hostapd: remove version tag from directory
[dragonfly.git] / contrib / hostapd / eap_sim.c
blob8c3c82870036a03c3d05600fcccde5bd7bb73ee5
1 /*
2 * hostapd / EAP-SIM (RFC 4186)
3 * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
12 * See README and COPYING for more details.
15 #include "includes.h"
17 #include "hostapd.h"
18 #include "common.h"
19 #include "crypto.h"
20 #include "eap_i.h"
21 #include "eap_sim_common.h"
22 #include "eap_sim_db.h"
25 struct eap_sim_data {
26 u8 mk[EAP_SIM_MK_LEN];
27 u8 nonce_mt[EAP_SIM_NONCE_MT_LEN];
28 u8 nonce_s[EAP_SIM_NONCE_S_LEN];
29 u8 k_aut[EAP_SIM_K_AUT_LEN];
30 u8 k_encr[EAP_SIM_K_ENCR_LEN];
31 u8 msk[EAP_SIM_KEYING_DATA_LEN];
32 u8 emsk[EAP_EMSK_LEN];
33 u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
34 u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
35 u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
36 int num_chal;
37 enum { START, CHALLENGE, REAUTH, SUCCESS, FAILURE } state;
38 char *next_pseudonym;
39 char *next_reauth_id;
40 u16 counter;
41 struct eap_sim_reauth *reauth;
45 static const char * eap_sim_state_txt(int state)
47 switch (state) {
48 case START:
49 return "START";
50 case CHALLENGE:
51 return "CHALLENGE";
52 case REAUTH:
53 return "REAUTH";
54 case SUCCESS:
55 return "SUCCESS";
56 case FAILURE:
57 return "FAILURE";
58 default:
59 return "Unknown?!";
64 static void eap_sim_state(struct eap_sim_data *data, int state)
66 wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
67 eap_sim_state_txt(data->state),
68 eap_sim_state_txt(state));
69 data->state = state;
73 static void * eap_sim_init(struct eap_sm *sm)
75 struct eap_sim_data *data;
77 if (sm->eap_sim_db_priv == NULL) {
78 wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured");
79 return NULL;
82 data = wpa_zalloc(sizeof(*data));
83 if (data == NULL)
84 return NULL;
85 data->state = START;
87 return data;
91 static void eap_sim_reset(struct eap_sm *sm, void *priv)
93 struct eap_sim_data *data = priv;
94 free(data->next_pseudonym);
95 free(data->next_reauth_id);
96 free(data);
100 static u8 * eap_sim_build_start(struct eap_sm *sm, struct eap_sim_data *data,
101 int id, size_t *reqDataLen)
103 struct eap_sim_msg *msg;
104 u8 ver[2];
106 wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
107 msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
108 EAP_SIM_SUBTYPE_START);
109 if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
110 sm->identity_len)) {
111 wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ");
112 eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
114 wpa_printf(MSG_DEBUG, " AT_VERSION_LIST");
115 ver[0] = 0;
116 ver[1] = EAP_SIM_VERSION;
117 eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver),
118 ver, sizeof(ver));
119 return eap_sim_msg_finish(msg, reqDataLen, NULL, NULL, 0);
123 static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data,
124 struct eap_sim_msg *msg, u16 counter,
125 const u8 *nonce_s)
127 free(data->next_pseudonym);
128 data->next_pseudonym =
129 eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
130 free(data->next_reauth_id);
131 if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
132 data->next_reauth_id =
133 eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 0);
134 } else {
135 wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
136 "count exceeded - force full authentication");
137 data->next_reauth_id = NULL;
140 if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
141 counter == 0 && nonce_s == NULL)
142 return 0;
144 wpa_printf(MSG_DEBUG, " AT_IV");
145 wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
146 eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
148 if (counter > 0) {
149 wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter);
150 eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
153 if (nonce_s) {
154 wpa_printf(MSG_DEBUG, " *AT_NONCE_S");
155 eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
156 EAP_SIM_NONCE_S_LEN);
159 if (data->next_pseudonym) {
160 wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)",
161 data->next_pseudonym);
162 eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
163 strlen(data->next_pseudonym),
164 (u8 *) data->next_pseudonym,
165 strlen(data->next_pseudonym));
168 if (data->next_reauth_id) {
169 wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)",
170 data->next_reauth_id);
171 eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
172 strlen(data->next_reauth_id),
173 (u8 *) data->next_reauth_id,
174 strlen(data->next_reauth_id));
177 if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
178 wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
179 "AT_ENCR_DATA");
180 return -1;
183 return 0;
187 static u8 * eap_sim_build_challenge(struct eap_sm *sm,
188 struct eap_sim_data *data,
189 int id, size_t *reqDataLen)
191 struct eap_sim_msg *msg;
193 wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge");
194 msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
195 EAP_SIM_SUBTYPE_CHALLENGE);
196 wpa_printf(MSG_DEBUG, " AT_RAND");
197 eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand,
198 data->num_chal * GSM_RAND_LEN);
200 if (eap_sim_build_encr(sm, data, msg, 0, NULL)) {
201 eap_sim_msg_free(msg);
202 return NULL;
205 wpa_printf(MSG_DEBUG, " AT_MAC");
206 eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
207 return eap_sim_msg_finish(msg, reqDataLen, data->k_aut, data->nonce_mt,
208 EAP_SIM_NONCE_MT_LEN);
212 static u8 * eap_sim_build_reauth(struct eap_sm *sm,
213 struct eap_sim_data *data,
214 int id, size_t *reqDataLen)
216 struct eap_sim_msg *msg;
218 wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
220 if (hostapd_get_rand(data->nonce_s, EAP_SIM_NONCE_S_LEN))
221 return NULL;
222 wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S",
223 data->nonce_s, EAP_SIM_NONCE_S_LEN);
225 eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
226 data->emsk);
227 eap_sim_derive_keys_reauth(data->counter, sm->identity,
228 sm->identity_len, data->nonce_s, data->mk,
229 data->msk, data->emsk);
231 msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
232 EAP_SIM_SUBTYPE_REAUTHENTICATION);
234 if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) {
235 eap_sim_msg_free(msg);
236 return NULL;
239 wpa_printf(MSG_DEBUG, " AT_MAC");
240 eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
241 return eap_sim_msg_finish(msg, reqDataLen, data->k_aut, NULL, 0);
245 static u8 * eap_sim_buildReq(struct eap_sm *sm, void *priv, int id,
246 size_t *reqDataLen)
248 struct eap_sim_data *data = priv;
250 switch (data->state) {
251 case START:
252 return eap_sim_build_start(sm, data, id, reqDataLen);
253 case CHALLENGE:
254 return eap_sim_build_challenge(sm, data, id, reqDataLen);
255 case REAUTH:
256 return eap_sim_build_reauth(sm, data, id, reqDataLen);
257 default:
258 wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
259 "buildReq", data->state);
260 break;
262 return NULL;
266 static Boolean eap_sim_check(struct eap_sm *sm, void *priv,
267 u8 *respData, size_t respDataLen)
269 struct eap_sim_data *data = priv;
270 struct eap_hdr *resp;
271 u8 *pos, subtype;
273 resp = (struct eap_hdr *) respData;
274 pos = (u8 *) (resp + 1);
275 if (respDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_SIM ||
276 (ntohs(resp->length)) > respDataLen) {
277 wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
278 return TRUE;
280 subtype = pos[1];
282 if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
283 return FALSE;
285 switch (data->state) {
286 case START:
287 if (subtype != EAP_SIM_SUBTYPE_START) {
288 wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
289 "subtype %d", subtype);
290 return TRUE;
292 break;
293 case CHALLENGE:
294 if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) {
295 wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
296 "subtype %d", subtype);
297 return TRUE;
299 break;
300 case REAUTH:
301 if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) {
302 wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
303 "subtype %d", subtype);
304 return TRUE;
306 break;
307 default:
308 wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for "
309 "processing a response", data->state);
310 return TRUE;
313 return FALSE;
317 static int eap_sim_supported_ver(struct eap_sim_data *data, int version)
319 return version == EAP_SIM_VERSION;
323 static void eap_sim_process_start(struct eap_sm *sm,
324 struct eap_sim_data *data,
325 u8 *respData, size_t respDataLen,
326 struct eap_sim_attrs *attr)
328 const u8 *identity;
329 size_t identity_len;
330 u8 ver_list[2];
332 wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
334 if (attr->nonce_mt == NULL || attr->selected_version < 0) {
335 wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
336 "required attributes");
337 eap_sim_state(data, FAILURE);
338 return;
341 if (!eap_sim_supported_ver(data, attr->selected_version)) {
342 wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
343 "version %d", attr->selected_version);
344 eap_sim_state(data, FAILURE);
345 return;
348 if (attr->identity) {
349 free(sm->identity);
350 sm->identity = malloc(attr->identity_len);
351 if (sm->identity) {
352 memcpy(sm->identity, attr->identity,
353 attr->identity_len);
354 sm->identity_len = attr->identity_len;
358 identity = NULL;
359 identity_len = 0;
361 if (sm->identity && sm->identity_len > 0 &&
362 sm->identity[0] == EAP_SIM_PERMANENT_PREFIX) {
363 identity = sm->identity;
364 identity_len = sm->identity_len;
365 } else {
366 identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
367 sm->identity,
368 sm->identity_len,
369 &identity_len);
370 if (identity == NULL) {
371 data->reauth = eap_sim_db_get_reauth_entry(
372 sm->eap_sim_db_priv, sm->identity,
373 sm->identity_len);
374 if (data->reauth) {
375 wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast "
376 "re-authentication");
377 identity = data->reauth->identity;
378 identity_len = data->reauth->identity_len;
379 data->counter = data->reauth->counter;
380 memcpy(data->mk, data->reauth->mk,
381 EAP_SIM_MK_LEN);
386 if (identity == NULL) {
387 wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent"
388 " user name");
389 eap_sim_state(data, FAILURE);
390 return;
393 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
394 identity, identity_len);
396 if (data->reauth) {
397 eap_sim_state(data, REAUTH);
398 return;
401 data->counter = 0; /* reset re-auth counter since this is full auth */
402 data->reauth = NULL;
404 data->num_chal = eap_sim_db_get_gsm_triplets(
405 sm->eap_sim_db_priv, identity, identity_len,
406 EAP_SIM_MAX_CHAL,
407 (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
408 if (data->num_chal == EAP_SIM_DB_PENDING) {
409 wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
410 "not yet available - pending request");
411 sm->method_pending = METHOD_PENDING_WAIT;
412 return;
414 if (data->num_chal < 2) {
415 wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
416 "authentication triplets for the peer");
417 eap_sim_state(data, FAILURE);
418 return;
421 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation",
422 sm->identity, sm->identity_len);
424 memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN);
425 WPA_PUT_BE16(ver_list, EAP_SIM_VERSION);
426 eap_sim_derive_mk(sm->identity, sm->identity_len, attr->nonce_mt,
427 attr->selected_version, ver_list, sizeof(ver_list),
428 data->num_chal, (const u8 *) data->kc, data->mk);
429 eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
430 data->emsk);
432 eap_sim_state(data, CHALLENGE);
436 static void eap_sim_process_challenge(struct eap_sm *sm,
437 struct eap_sim_data *data,
438 u8 *respData, size_t respDataLen,
439 struct eap_sim_attrs *attr)
441 const u8 *identity;
442 size_t identity_len;
444 if (attr->mac == NULL ||
445 eap_sim_verify_mac(data->k_aut, respData, respDataLen, attr->mac,
446 (u8 *) data->sres,
447 data->num_chal * EAP_SIM_SRES_LEN)) {
448 wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
449 "did not include valid AT_MAC");
450 eap_sim_state(data, FAILURE);
451 return;
454 wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the "
455 "correct AT_MAC");
456 eap_sim_state(data, SUCCESS);
458 identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
459 sm->identity_len, &identity_len);
460 if (identity == NULL) {
461 identity = sm->identity;
462 identity_len = sm->identity_len;
465 if (data->next_pseudonym) {
466 eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
467 identity_len,
468 data->next_pseudonym);
469 data->next_pseudonym = NULL;
471 if (data->next_reauth_id) {
472 eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
473 identity_len,
474 data->next_reauth_id, data->counter + 1,
475 data->mk);
476 data->next_reauth_id = NULL;
481 static void eap_sim_process_reauth(struct eap_sm *sm,
482 struct eap_sim_data *data,
483 u8 *respData, size_t respDataLen,
484 struct eap_sim_attrs *attr)
486 struct eap_sim_attrs eattr;
487 u8 *decrypted = NULL;
488 const u8 *identity, *id2;
489 size_t identity_len, id2_len;
491 if (attr->mac == NULL ||
492 eap_sim_verify_mac(data->k_aut, respData, respDataLen, attr->mac,
493 data->nonce_s, EAP_SIM_NONCE_S_LEN)) {
494 wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
495 "did not include valid AT_MAC");
496 goto fail;
499 if (attr->encr_data == NULL || attr->iv == NULL) {
500 wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
501 "message did not include encrypted data");
502 goto fail;
505 decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
506 attr->encr_data_len, attr->iv, &eattr,
508 if (decrypted == NULL) {
509 wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
510 "data from reauthentication message");
511 goto fail;
514 if (eattr.counter != data->counter) {
515 wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
516 "used incorrect counter %u, expected %u",
517 eattr.counter, data->counter);
518 goto fail;
520 free(decrypted);
521 decrypted = NULL;
523 wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes "
524 "the correct AT_MAC");
525 eap_sim_state(data, SUCCESS);
527 if (data->reauth) {
528 identity = data->reauth->identity;
529 identity_len = data->reauth->identity_len;
530 } else {
531 identity = sm->identity;
532 identity_len = sm->identity_len;
535 id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
536 identity_len, &id2_len);
537 if (id2) {
538 identity = id2;
539 identity_len = id2_len;
542 if (data->next_pseudonym) {
543 eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
544 identity_len, data->next_pseudonym);
545 data->next_pseudonym = NULL;
547 if (data->next_reauth_id) {
548 eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
549 identity_len, data->next_reauth_id,
550 data->counter + 1, data->mk);
551 data->next_reauth_id = NULL;
552 } else {
553 eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
554 data->reauth = NULL;
557 return;
559 fail:
560 eap_sim_state(data, FAILURE);
561 eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
562 data->reauth = NULL;
563 free(decrypted);
567 static void eap_sim_process_client_error(struct eap_sm *sm,
568 struct eap_sim_data *data,
569 u8 *respData, size_t respDataLen,
570 struct eap_sim_attrs *attr)
572 wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d",
573 attr->client_error_code);
574 eap_sim_state(data, FAILURE);
578 static void eap_sim_process(struct eap_sm *sm, void *priv,
579 u8 *respData, size_t respDataLen)
581 struct eap_sim_data *data = priv;
582 struct eap_hdr *resp;
583 u8 *pos, subtype;
584 size_t len;
585 struct eap_sim_attrs attr;
587 resp = (struct eap_hdr *) respData;
588 pos = (u8 *) (resp + 1);
589 subtype = pos[1];
590 len = ntohs(resp->length);
591 pos += 4;
593 if (eap_sim_parse_attr(pos, respData + len, &attr, 0, 0)) {
594 wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
595 eap_sim_state(data, FAILURE);
596 return;
599 if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) {
600 eap_sim_process_client_error(sm, data, respData, len, &attr);
601 return;
604 switch (data->state) {
605 case START:
606 eap_sim_process_start(sm, data, respData, len, &attr);
607 break;
608 case CHALLENGE:
609 eap_sim_process_challenge(sm, data, respData, len, &attr);
610 break;
611 case REAUTH:
612 eap_sim_process_reauth(sm, data, respData, len, &attr);
613 break;
614 default:
615 wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
616 "process", data->state);
617 break;
622 static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv)
624 struct eap_sim_data *data = priv;
625 return data->state == SUCCESS || data->state == FAILURE;
629 static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
631 struct eap_sim_data *data = priv;
632 u8 *key;
634 if (data->state != SUCCESS)
635 return NULL;
637 key = malloc(EAP_SIM_KEYING_DATA_LEN);
638 if (key == NULL)
639 return NULL;
640 memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
641 *len = EAP_SIM_KEYING_DATA_LEN;
642 return key;
646 static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
648 struct eap_sim_data *data = priv;
649 u8 *key;
651 if (data->state != SUCCESS)
652 return NULL;
654 key = malloc(EAP_EMSK_LEN);
655 if (key == NULL)
656 return NULL;
657 memcpy(key, data->emsk, EAP_EMSK_LEN);
658 *len = EAP_EMSK_LEN;
659 return key;
663 static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv)
665 struct eap_sim_data *data = priv;
666 return data->state == SUCCESS;
670 int eap_server_sim_register(void)
672 struct eap_method *eap;
673 int ret;
675 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
676 EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
677 if (eap == NULL)
678 return -1;
680 eap->init = eap_sim_init;
681 eap->reset = eap_sim_reset;
682 eap->buildReq = eap_sim_buildReq;
683 eap->check = eap_sim_check;
684 eap->process = eap_sim_process;
685 eap->isDone = eap_sim_isDone;
686 eap->getKey = eap_sim_getKey;
687 eap->isSuccess = eap_sim_isSuccess;
688 eap->get_emsk = eap_sim_get_emsk;
690 ret = eap_server_method_register(eap);
691 if (ret)
692 eap_server_method_free(eap);
693 return ret;