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