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