• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * EAP peer method: EAP-SAKE (RFC 4763)
3  * Copyright (c) 2006, 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_i.h"
19 #include "config_ssid.h"
20 #include "eap_sake_common.h"
21 
22 struct eap_sake_data {
23 	enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
24 	u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN];
25 	u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN];
26 	u8 rand_s[EAP_SAKE_RAND_LEN];
27 	u8 rand_p[EAP_SAKE_RAND_LEN];
28 	struct {
29 		u8 auth[EAP_SAKE_TEK_AUTH_LEN];
30 		u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
31 	} tek;
32 	u8 msk[EAP_MSK_LEN];
33 	u8 emsk[EAP_EMSK_LEN];
34 	u8 session_id;
35 	int session_id_set;
36 	u8 *peerid;
37 	size_t peerid_len;
38 	u8 *serverid;
39 	size_t serverid_len;
40 };
41 
42 
eap_sake_state_txt(int state)43 static const char * eap_sake_state_txt(int state)
44 {
45 	switch (state) {
46 	case IDENTITY:
47 		return "IDENTITY";
48 	case CHALLENGE:
49 		return "CHALLENGE";
50 	case CONFIRM:
51 		return "CONFIRM";
52 	case SUCCESS:
53 		return "SUCCESS";
54 	case FAILURE:
55 		return "FAILURE";
56 	default:
57 		return "?";
58 	}
59 }
60 
61 
eap_sake_state(struct eap_sake_data * data,int state)62 static void eap_sake_state(struct eap_sake_data *data, int state)
63 {
64 	wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
65 		   eap_sake_state_txt(data->state),
66 		   eap_sake_state_txt(state));
67 	data->state = state;
68 }
69 
70 
71 static void eap_sake_deinit(struct eap_sm *sm, void *priv);
72 
73 
eap_sake_init(struct eap_sm * sm)74 static void * eap_sake_init(struct eap_sm *sm)
75 {
76 	struct wpa_ssid *config = eap_get_config(sm);
77 	struct eap_sake_data *data;
78 
79 	if (config == NULL) {
80 		wpa_printf(MSG_INFO, "EAP-SAKE: No configuration found");
81 		return NULL;
82 	}
83 
84 	if (!config->eappsk ||
85 	    config->eappsk_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
86 		wpa_printf(MSG_INFO, "EAP-SAKE: No key (eappsk) of correct "
87 			   "length configured");
88 		return NULL;
89 	}
90 
91 	data = os_zalloc(sizeof(*data));
92 	if (data == NULL)
93 		return NULL;
94 	data->state = IDENTITY;
95 
96 	if (config->nai) {
97 		data->peerid = os_malloc(config->nai_len);
98 		if (data->peerid == NULL) {
99 			eap_sake_deinit(sm, data);
100 			return NULL;
101 		}
102 		os_memcpy(data->peerid, config->nai, config->nai_len);
103 		data->peerid_len = config->nai_len;
104 	}
105 
106 	os_memcpy(data->root_secret_a, config->eappsk,
107 		  EAP_SAKE_ROOT_SECRET_LEN);
108 	os_memcpy(data->root_secret_b,
109 		  config->eappsk + EAP_SAKE_ROOT_SECRET_LEN,
110 		  EAP_SAKE_ROOT_SECRET_LEN);
111 
112 	return data;
113 }
114 
115 
eap_sake_deinit(struct eap_sm * sm,void * priv)116 static void eap_sake_deinit(struct eap_sm *sm, void *priv)
117 {
118 	struct eap_sake_data *data = priv;
119 	os_free(data->serverid);
120 	os_free(data->peerid);
121 	os_free(data);
122 }
123 
124 
eap_sake_build_msg(struct eap_sake_data * data,u8 ** payload,int id,size_t * length,u8 subtype)125 static u8 * eap_sake_build_msg(struct eap_sake_data *data, u8 **payload,
126 			       int id, size_t *length, u8 subtype)
127 {
128 	struct eap_sake_hdr *req;
129 	u8 *msg;
130 
131 	*length += sizeof(struct eap_sake_hdr);
132 
133 	msg = os_zalloc(*length);
134 	if (msg == NULL) {
135 		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
136 			   "request");
137 		return NULL;
138 	}
139 
140 	req = (struct eap_sake_hdr *) msg;
141 	req->code = EAP_CODE_RESPONSE;
142 	req->identifier = id;
143 	req->length = htons((u16) *length);
144 	req->type = EAP_TYPE_SAKE;
145 	req->version = EAP_SAKE_VERSION;
146 	req->session_id = data->session_id;
147 	req->subtype = subtype;
148 	*payload = (u8 *) (req + 1);
149 
150 	return msg;
151 }
152 
153 
eap_sake_process_identity(struct eap_sm * sm,struct eap_sake_data * data,struct eap_method_ret * ret,const u8 * reqData,size_t reqDataLen,const u8 * payload,size_t payload_len,size_t * respDataLen)154 static u8 * eap_sake_process_identity(struct eap_sm *sm,
155 				      struct eap_sake_data *data,
156 				      struct eap_method_ret *ret,
157 				      const u8 *reqData, size_t reqDataLen,
158 				      const u8 *payload, size_t payload_len,
159 				      size_t *respDataLen)
160 {
161 	struct eap_sake_parse_attr attr;
162 	u8 *resp, *rpos;
163 	const struct eap_hdr *hdr = (const struct eap_hdr *) reqData;
164 
165 	if (data->state != IDENTITY) {
166 		ret->ignore = TRUE;
167 		return NULL;
168 	}
169 
170 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity");
171 
172 	if (eap_sake_parse_attributes(payload, payload_len, &attr))
173 		return NULL;
174 
175 	if (!attr.perm_id_req && !attr.any_id_req) {
176 		wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or "
177 			   "AT_ANY_ID_REQ in Request/Identity");
178 		return NULL;
179 	}
180 
181 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity");
182 
183 	*respDataLen = 2 + data->peerid_len;
184 	resp = eap_sake_build_msg(data, &rpos, hdr->identifier, respDataLen,
185 				  EAP_SAKE_SUBTYPE_IDENTITY);
186 	if (resp == NULL)
187 		return NULL;
188 
189 	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
190 	*rpos++ = EAP_SAKE_AT_PEERID;
191 	*rpos++ = 2 + data->peerid_len;
192 	if (data->peerid)
193 		os_memcpy(rpos, data->peerid, data->peerid_len);
194 
195 	eap_sake_state(data, CHALLENGE);
196 
197 	return resp;
198 }
199 
200 
eap_sake_process_challenge(struct eap_sm * sm,struct eap_sake_data * data,struct eap_method_ret * ret,const u8 * reqData,size_t reqDataLen,const u8 * payload,size_t payload_len,size_t * respDataLen)201 static u8 * eap_sake_process_challenge(struct eap_sm *sm,
202 				       struct eap_sake_data *data,
203 				       struct eap_method_ret *ret,
204 				       const u8 *reqData, size_t reqDataLen,
205 				       const u8 *payload, size_t payload_len,
206 				       size_t *respDataLen)
207 {
208 	struct eap_sake_parse_attr attr;
209 	u8 *resp, *rpos;
210 	const struct eap_hdr *hdr = (const struct eap_hdr *) reqData;
211 
212 	if (data->state != IDENTITY && data->state != CHALLENGE) {
213 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received "
214 			   "in unexpected state (%d)", data->state);
215 		ret->ignore = TRUE;
216 		return NULL;
217 	}
218 	if (data->state == IDENTITY)
219 		eap_sake_state(data, CHALLENGE);
220 
221 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge");
222 
223 	if (eap_sake_parse_attributes(payload, payload_len, &attr))
224 		return NULL;
225 
226 	if (!attr.rand_s) {
227 		wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not "
228 			   "include AT_RAND_S");
229 		return NULL;
230 	}
231 
232 	os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN);
233 	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
234 		    data->rand_s, EAP_SAKE_RAND_LEN);
235 
236 	if (hostapd_get_rand(data->rand_p, EAP_SAKE_RAND_LEN)) {
237 		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
238 		return NULL;
239 	}
240 	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)",
241 		    data->rand_p, EAP_SAKE_RAND_LEN);
242 
243 	os_free(data->serverid);
244 	data->serverid = NULL;
245 	data->serverid_len = 0;
246 	if (attr.serverid) {
247 		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
248 				  attr.serverid, attr.serverid_len);
249 		data->serverid = os_malloc(attr.serverid_len);
250 		if (data->serverid == NULL)
251 			return NULL;
252 		os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
253 		data->serverid_len = attr.serverid_len;
254 	}
255 
256 	eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
257 			     data->rand_s, data->rand_p,
258 			     (u8 *) &data->tek, data->msk, data->emsk);
259 
260 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge");
261 
262 	*respDataLen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN;
263 	if (data->peerid)
264 		*respDataLen += 2 + data->peerid_len;
265 	resp = eap_sake_build_msg(data, &rpos, hdr->identifier, respDataLen,
266 				  EAP_SAKE_SUBTYPE_CHALLENGE);
267 	if (resp == NULL)
268 		return NULL;
269 
270 	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P");
271 	*rpos++ = EAP_SAKE_AT_RAND_P;
272 	*rpos++ = 2 + EAP_SAKE_RAND_LEN;
273 	os_memcpy(rpos, data->rand_p, EAP_SAKE_RAND_LEN);
274 	rpos += EAP_SAKE_RAND_LEN;
275 
276 	if (data->peerid) {
277 		wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
278 		*rpos++ = EAP_SAKE_AT_PEERID;
279 		*rpos++ = 2 + data->peerid_len;
280 		os_memcpy(rpos, data->peerid, data->peerid_len);
281 		rpos += data->peerid_len;
282 	}
283 
284 	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
285 	*rpos++ = EAP_SAKE_AT_MIC_P;
286 	*rpos++ = 2 + EAP_SAKE_MIC_LEN;
287 	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
288 				 data->serverid, data->serverid_len,
289 				 data->peerid, data->peerid_len, 1,
290 				 resp, *respDataLen, rpos, rpos)) {
291 		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
292 		os_free(resp);
293 		return NULL;
294 	}
295 
296 	eap_sake_state(data, CONFIRM);
297 
298 	return resp;
299 }
300 
301 
eap_sake_process_confirm(struct eap_sm * sm,struct eap_sake_data * data,struct eap_method_ret * ret,const u8 * reqData,size_t reqDataLen,const u8 * payload,size_t payload_len,size_t * respDataLen)302 static u8 * eap_sake_process_confirm(struct eap_sm *sm,
303 				     struct eap_sake_data *data,
304 				     struct eap_method_ret *ret,
305 				     const u8 *reqData, size_t reqDataLen,
306 				     const u8 *payload, size_t payload_len,
307 				     size_t *respDataLen)
308 {
309 	struct eap_sake_parse_attr attr;
310 	u8 mic_s[EAP_SAKE_MIC_LEN];
311 	u8 *resp, *rpos;
312 	const struct eap_hdr *hdr = (const struct eap_hdr *) reqData;
313 
314 	if (data->state != CONFIRM) {
315 		ret->ignore = TRUE;
316 		return NULL;
317 	}
318 
319 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm");
320 
321 	if (eap_sake_parse_attributes(payload, payload_len, &attr))
322 		return NULL;
323 
324 	if (!attr.mic_s) {
325 		wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not "
326 			   "include AT_MIC_S");
327 		return NULL;
328 	}
329 
330 	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
331 			     data->serverid, data->serverid_len,
332 			     data->peerid, data->peerid_len, 0,
333 			     reqData, reqDataLen, attr.mic_s, mic_s);
334 	if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
335 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S");
336 		eap_sake_state(data, FAILURE);
337 		ret->methodState = METHOD_DONE;
338 		ret->decision = DECISION_FAIL;
339 		ret->allowNotifications = FALSE;
340 		*respDataLen = 0;
341 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending "
342 			   "Response/Auth-Reject");
343 		return eap_sake_build_msg(data, &rpos, hdr->identifier,
344 					  respDataLen,
345 					  EAP_SAKE_SUBTYPE_AUTH_REJECT);
346 	}
347 
348 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm");
349 
350 	*respDataLen = 2 + EAP_SAKE_MIC_LEN;
351 	resp = eap_sake_build_msg(data, &rpos, hdr->identifier, respDataLen,
352 				  EAP_SAKE_SUBTYPE_CONFIRM);
353 	if (resp == NULL)
354 		return NULL;
355 
356 	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
357 	*rpos++ = EAP_SAKE_AT_MIC_P;
358 	*rpos++ = 2 + EAP_SAKE_MIC_LEN;
359 	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
360 				 data->serverid, data->serverid_len,
361 				 data->peerid, data->peerid_len, 1,
362 				 resp, *respDataLen, rpos, rpos)) {
363 		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
364 		os_free(resp);
365 		return NULL;
366 	}
367 
368 	eap_sake_state(data, SUCCESS);
369 	ret->methodState = METHOD_DONE;
370 	ret->decision = DECISION_UNCOND_SUCC;
371 	ret->allowNotifications = FALSE;
372 
373 	return resp;
374 }
375 
376 
eap_sake_process(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const u8 * reqData,size_t reqDataLen,size_t * respDataLen)377 static u8 * eap_sake_process(struct eap_sm *sm, void *priv,
378 			    struct eap_method_ret *ret,
379 			    const u8 *reqData, size_t reqDataLen,
380 			    size_t *respDataLen)
381 {
382 	struct eap_sake_data *data = priv;
383 	const struct eap_sake_hdr *req;
384 	u8 *resp;
385 	const u8 *pos, *end;
386 	size_t len;
387 	u8 subtype, session_id;
388 
389 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE,
390 			       reqData, reqDataLen, &len);
391 	if (pos == NULL || len < 3) {
392 		ret->ignore = TRUE;
393 		return NULL;
394 	}
395 
396 	req = (const struct eap_sake_hdr *) reqData;
397 	subtype = req->subtype;
398 	session_id = req->session_id;
399 	pos = (const u8 *) (req + 1);
400 	end = reqData + be_to_host16(req->length);
401 
402 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d "
403 		   "session_id %d", subtype, session_id);
404 	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
405 		    pos, end - pos);
406 
407 	if (data->session_id_set && data->session_id != session_id) {
408 		wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
409 			   session_id, data->session_id);
410 		ret->ignore = TRUE;
411 		return NULL;
412 	}
413 	data->session_id = session_id;
414 	data->session_id_set = 1;
415 
416 	ret->ignore = FALSE;
417 	ret->methodState = METHOD_MAY_CONT;
418 	ret->decision = DECISION_FAIL;
419 	ret->allowNotifications = TRUE;
420 
421 	switch (subtype) {
422 	case EAP_SAKE_SUBTYPE_IDENTITY:
423 		resp = eap_sake_process_identity(sm, data, ret, reqData,
424 						 reqDataLen, pos, end - pos,
425 						 respDataLen);
426 		break;
427 	case EAP_SAKE_SUBTYPE_CHALLENGE:
428 		resp = eap_sake_process_challenge(sm, data, ret, reqData,
429 						  reqDataLen, pos, end - pos,
430 						  respDataLen);
431 		break;
432 	case EAP_SAKE_SUBTYPE_CONFIRM:
433 		resp = eap_sake_process_confirm(sm, data, ret, reqData,
434 						reqDataLen, pos, end - pos,
435 						respDataLen);
436 		break;
437 	default:
438 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with "
439 			   "unknown subtype %d", subtype);
440 		ret->ignore = TRUE;
441 		return NULL;
442 	}
443 
444 	if (ret->methodState == METHOD_DONE)
445 		ret->allowNotifications = FALSE;
446 
447 	return resp;
448 }
449 
450 
eap_sake_isKeyAvailable(struct eap_sm * sm,void * priv)451 static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv)
452 {
453 	struct eap_sake_data *data = priv;
454 	return data->state == SUCCESS;
455 }
456 
457 
eap_sake_getKey(struct eap_sm * sm,void * priv,size_t * len)458 static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
459 {
460 	struct eap_sake_data *data = priv;
461 	u8 *key;
462 
463 	if (data->state != SUCCESS)
464 		return NULL;
465 
466 	key = os_malloc(EAP_MSK_LEN);
467 	if (key == NULL)
468 		return NULL;
469 	os_memcpy(key, data->msk, EAP_MSK_LEN);
470 	*len = EAP_MSK_LEN;
471 
472 	return key;
473 }
474 
475 
eap_sake_get_emsk(struct eap_sm * sm,void * priv,size_t * len)476 static u8 * eap_sake_get_emsk(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_EMSK_LEN);
485 	if (key == NULL)
486 		return NULL;
487 	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
488 	*len = EAP_EMSK_LEN;
489 
490 	return key;
491 }
492 
493 
eap_peer_sake_register(void)494 int eap_peer_sake_register(void)
495 {
496 	struct eap_method *eap;
497 	int ret;
498 
499 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
500 				    EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
501 	if (eap == NULL)
502 		return -1;
503 
504 	eap->init = eap_sake_init;
505 	eap->deinit = eap_sake_deinit;
506 	eap->process = eap_sake_process;
507 	eap->isKeyAvailable = eap_sake_isKeyAvailable;
508 	eap->getKey = eap_sake_getKey;
509 	eap->get_emsk = eap_sake_get_emsk;
510 
511 	ret = eap_peer_method_register(eap);
512 	if (ret)
513 		eap_peer_method_free(eap);
514 	return ret;
515 }
516