$search
00001 /* 00002 * hostapd / EAP-SIM database/authenticator gateway 00003 * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License version 2 as 00007 * published by the Free Software Foundation. 00008 * 00009 * Alternatively, this software may be distributed under the terms of BSD 00010 * license. 00011 * 00012 * See README and COPYING for more details. 00013 * 00014 * This is an example implementation of the EAP-SIM/AKA database/authentication 00015 * gateway interface that is using an external program as an SS7 gateway to 00016 * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example 00017 * implementation of such a gateway program. This eap_sim_db.c takes care of 00018 * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different 00019 * gateway implementations for HLR/AuC access. Alternatively, it can also be 00020 * completely replaced if the in-memory database of pseudonyms/re-auth 00021 * identities is not suitable for some cases. 00022 */ 00023 00024 #include "includes.h" 00025 #include <sys/un.h> 00026 00027 #include "common.h" 00028 #include "eap_common/eap_sim_common.h" 00029 #include "eap_server/eap_sim_db.h" 00030 #include "eloop.h" 00031 00032 struct eap_sim_pseudonym { 00033 struct eap_sim_pseudonym *next; 00034 u8 *identity; 00035 size_t identity_len; 00036 char *pseudonym; 00037 }; 00038 00039 struct eap_sim_db_pending { 00040 struct eap_sim_db_pending *next; 00041 u8 imsi[20]; 00042 size_t imsi_len; 00043 enum { PENDING, SUCCESS, FAILURE } state; 00044 void *cb_session_ctx; 00045 struct os_time timestamp; 00046 int aka; 00047 union { 00048 struct { 00049 u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; 00050 u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; 00051 u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; 00052 int num_chal; 00053 } sim; 00054 struct { 00055 u8 rand[EAP_AKA_RAND_LEN]; 00056 u8 autn[EAP_AKA_AUTN_LEN]; 00057 u8 ik[EAP_AKA_IK_LEN]; 00058 u8 ck[EAP_AKA_CK_LEN]; 00059 u8 res[EAP_AKA_RES_MAX_LEN]; 00060 size_t res_len; 00061 } aka; 00062 } u; 00063 }; 00064 00065 struct eap_sim_db_data { 00066 int sock; 00067 char *fname; 00068 char *local_sock; 00069 void (*get_complete_cb)(void *ctx, void *session_ctx); 00070 void *ctx; 00071 struct eap_sim_pseudonym *pseudonyms; 00072 struct eap_sim_reauth *reauths; 00073 struct eap_sim_db_pending *pending; 00074 }; 00075 00076 00077 static struct eap_sim_db_pending * 00078 eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi, 00079 size_t imsi_len, int aka) 00080 { 00081 struct eap_sim_db_pending *entry, *prev = NULL; 00082 00083 entry = data->pending; 00084 while (entry) { 00085 if (entry->aka == aka && entry->imsi_len == imsi_len && 00086 os_memcmp(entry->imsi, imsi, imsi_len) == 0) { 00087 if (prev) 00088 prev->next = entry->next; 00089 else 00090 data->pending = entry->next; 00091 break; 00092 } 00093 prev = entry; 00094 entry = entry->next; 00095 } 00096 return entry; 00097 } 00098 00099 00100 static void eap_sim_db_add_pending(struct eap_sim_db_data *data, 00101 struct eap_sim_db_pending *entry) 00102 { 00103 entry->next = data->pending; 00104 data->pending = entry; 00105 } 00106 00107 00108 static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, 00109 const char *imsi, char *buf) 00110 { 00111 char *start, *end, *pos; 00112 struct eap_sim_db_pending *entry; 00113 int num_chal; 00114 00115 /* 00116 * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ... 00117 * SIM-RESP-AUTH <IMSI> FAILURE 00118 * (IMSI = ASCII string, Kc/SRES/RAND = hex string) 00119 */ 00120 00121 entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0); 00122 if (entry == NULL) { 00123 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " 00124 "received message found"); 00125 return; 00126 } 00127 00128 start = buf; 00129 if (os_strncmp(start, "FAILURE", 7) == 0) { 00130 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " 00131 "failure"); 00132 entry->state = FAILURE; 00133 eap_sim_db_add_pending(data, entry); 00134 data->get_complete_cb(data->ctx, entry->cb_session_ctx); 00135 return; 00136 } 00137 00138 num_chal = 0; 00139 while (num_chal < EAP_SIM_MAX_CHAL) { 00140 end = os_strchr(start, ' '); 00141 if (end) 00142 *end = '\0'; 00143 00144 pos = os_strchr(start, ':'); 00145 if (pos == NULL) 00146 goto parse_fail; 00147 *pos = '\0'; 00148 if (hexstr2bin(start, entry->u.sim.kc[num_chal], 00149 EAP_SIM_KC_LEN)) 00150 goto parse_fail; 00151 00152 start = pos + 1; 00153 pos = os_strchr(start, ':'); 00154 if (pos == NULL) 00155 goto parse_fail; 00156 *pos = '\0'; 00157 if (hexstr2bin(start, entry->u.sim.sres[num_chal], 00158 EAP_SIM_SRES_LEN)) 00159 goto parse_fail; 00160 00161 start = pos + 1; 00162 if (hexstr2bin(start, entry->u.sim.rand[num_chal], 00163 GSM_RAND_LEN)) 00164 goto parse_fail; 00165 00166 num_chal++; 00167 if (end == NULL) 00168 break; 00169 else 00170 start = end + 1; 00171 } 00172 entry->u.sim.num_chal = num_chal; 00173 00174 entry->state = SUCCESS; 00175 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " 00176 "successfully - callback"); 00177 eap_sim_db_add_pending(data, entry); 00178 data->get_complete_cb(data->ctx, entry->cb_session_ctx); 00179 return; 00180 00181 parse_fail: 00182 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); 00183 os_free(entry); 00184 } 00185 00186 00187 static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, 00188 const char *imsi, char *buf) 00189 { 00190 char *start, *end; 00191 struct eap_sim_db_pending *entry; 00192 00193 /* 00194 * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> 00195 * AKA-RESP-AUTH <IMSI> FAILURE 00196 * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string) 00197 */ 00198 00199 entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1); 00200 if (entry == NULL) { 00201 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " 00202 "received message found"); 00203 return; 00204 } 00205 00206 start = buf; 00207 if (os_strncmp(start, "FAILURE", 7) == 0) { 00208 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " 00209 "failure"); 00210 entry->state = FAILURE; 00211 eap_sim_db_add_pending(data, entry); 00212 data->get_complete_cb(data->ctx, entry->cb_session_ctx); 00213 return; 00214 } 00215 00216 end = os_strchr(start, ' '); 00217 if (end == NULL) 00218 goto parse_fail; 00219 *end = '\0'; 00220 if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN)) 00221 goto parse_fail; 00222 00223 start = end + 1; 00224 end = os_strchr(start, ' '); 00225 if (end == NULL) 00226 goto parse_fail; 00227 *end = '\0'; 00228 if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN)) 00229 goto parse_fail; 00230 00231 start = end + 1; 00232 end = os_strchr(start, ' '); 00233 if (end == NULL) 00234 goto parse_fail; 00235 *end = '\0'; 00236 if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN)) 00237 goto parse_fail; 00238 00239 start = end + 1; 00240 end = os_strchr(start, ' '); 00241 if (end == NULL) 00242 goto parse_fail; 00243 *end = '\0'; 00244 if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN)) 00245 goto parse_fail; 00246 00247 start = end + 1; 00248 end = os_strchr(start, ' '); 00249 if (end) 00250 *end = '\0'; 00251 else { 00252 end = start; 00253 while (*end) 00254 end++; 00255 } 00256 entry->u.aka.res_len = (end - start) / 2; 00257 if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) { 00258 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES"); 00259 entry->u.aka.res_len = 0; 00260 goto parse_fail; 00261 } 00262 if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len)) 00263 goto parse_fail; 00264 00265 entry->state = SUCCESS; 00266 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " 00267 "successfully - callback"); 00268 eap_sim_db_add_pending(data, entry); 00269 data->get_complete_cb(data->ctx, entry->cb_session_ctx); 00270 return; 00271 00272 parse_fail: 00273 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); 00274 os_free(entry); 00275 } 00276 00277 00278 static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx) 00279 { 00280 struct eap_sim_db_data *data = eloop_ctx; 00281 char buf[1000], *pos, *cmd, *imsi; 00282 int res; 00283 00284 res = recv(sock, buf, sizeof(buf), 0); 00285 if (res < 0) 00286 return; 00287 wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an " 00288 "external source", (u8 *) buf, res); 00289 if (res == 0) 00290 return; 00291 if (res >= (int) sizeof(buf)) 00292 res = sizeof(buf) - 1; 00293 buf[res] = '\0'; 00294 00295 if (data->get_complete_cb == NULL) { 00296 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb " 00297 "registered"); 00298 return; 00299 } 00300 00301 /* <cmd> <IMSI> ... */ 00302 00303 cmd = buf; 00304 pos = os_strchr(cmd, ' '); 00305 if (pos == NULL) 00306 goto parse_fail; 00307 *pos = '\0'; 00308 imsi = pos + 1; 00309 pos = os_strchr(imsi, ' '); 00310 if (pos == NULL) 00311 goto parse_fail; 00312 *pos = '\0'; 00313 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s", 00314 cmd, imsi); 00315 00316 if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0) 00317 eap_sim_db_sim_resp_auth(data, imsi, pos + 1); 00318 else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0) 00319 eap_sim_db_aka_resp_auth(data, imsi, pos + 1); 00320 else 00321 wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response " 00322 "'%s'", cmd); 00323 return; 00324 00325 parse_fail: 00326 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); 00327 } 00328 00329 00330 static int eap_sim_db_open_socket(struct eap_sim_db_data *data) 00331 { 00332 struct sockaddr_un addr; 00333 static int counter = 0; 00334 00335 if (os_strncmp(data->fname, "unix:", 5) != 0) 00336 return -1; 00337 00338 data->sock = socket(PF_UNIX, SOCK_DGRAM, 0); 00339 if (data->sock < 0) { 00340 perror("socket(eap_sim_db)"); 00341 return -1; 00342 } 00343 00344 os_memset(&addr, 0, sizeof(addr)); 00345 addr.sun_family = AF_UNIX; 00346 os_snprintf(addr.sun_path, sizeof(addr.sun_path), 00347 "/tmp/eap_sim_db_%d-%d", getpid(), counter++); 00348 data->local_sock = os_strdup(addr.sun_path); 00349 if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 00350 perror("bind(eap_sim_db)"); 00351 close(data->sock); 00352 data->sock = -1; 00353 return -1; 00354 } 00355 00356 os_memset(&addr, 0, sizeof(addr)); 00357 addr.sun_family = AF_UNIX; 00358 os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path)); 00359 if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 00360 perror("connect(eap_sim_db)"); 00361 wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket", 00362 (u8 *) addr.sun_path, 00363 os_strlen(addr.sun_path)); 00364 close(data->sock); 00365 data->sock = -1; 00366 return -1; 00367 } 00368 00369 eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL); 00370 00371 return 0; 00372 } 00373 00374 00375 static void eap_sim_db_close_socket(struct eap_sim_db_data *data) 00376 { 00377 if (data->sock >= 0) { 00378 eloop_unregister_read_sock(data->sock); 00379 close(data->sock); 00380 data->sock = -1; 00381 } 00382 if (data->local_sock) { 00383 unlink(data->local_sock); 00384 os_free(data->local_sock); 00385 data->local_sock = NULL; 00386 } 00387 } 00388 00389 00397 void * eap_sim_db_init(const char *config, 00398 void (*get_complete_cb)(void *ctx, void *session_ctx), 00399 void *ctx) 00400 { 00401 struct eap_sim_db_data *data; 00402 00403 data = os_zalloc(sizeof(*data)); 00404 if (data == NULL) 00405 return NULL; 00406 00407 data->sock = -1; 00408 data->get_complete_cb = get_complete_cb; 00409 data->ctx = ctx; 00410 data->fname = os_strdup(config); 00411 if (data->fname == NULL) 00412 goto fail; 00413 00414 if (os_strncmp(data->fname, "unix:", 5) == 0) { 00415 if (eap_sim_db_open_socket(data)) 00416 goto fail; 00417 } 00418 00419 return data; 00420 00421 fail: 00422 eap_sim_db_close_socket(data); 00423 os_free(data->fname); 00424 os_free(data); 00425 return NULL; 00426 } 00427 00428 00429 static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) 00430 { 00431 os_free(p->identity); 00432 os_free(p->pseudonym); 00433 os_free(p); 00434 } 00435 00436 00437 static void eap_sim_db_free_reauth(struct eap_sim_reauth *r) 00438 { 00439 os_free(r->identity); 00440 os_free(r->reauth_id); 00441 os_free(r); 00442 } 00443 00444 00449 void eap_sim_db_deinit(void *priv) 00450 { 00451 struct eap_sim_db_data *data = priv; 00452 struct eap_sim_pseudonym *p, *prev; 00453 struct eap_sim_reauth *r, *prevr; 00454 struct eap_sim_db_pending *pending, *prev_pending; 00455 00456 eap_sim_db_close_socket(data); 00457 os_free(data->fname); 00458 00459 p = data->pseudonyms; 00460 while (p) { 00461 prev = p; 00462 p = p->next; 00463 eap_sim_db_free_pseudonym(prev); 00464 } 00465 00466 r = data->reauths; 00467 while (r) { 00468 prevr = r; 00469 r = r->next; 00470 eap_sim_db_free_reauth(prevr); 00471 } 00472 00473 pending = data->pending; 00474 while (pending) { 00475 prev_pending = pending; 00476 pending = pending->next; 00477 os_free(prev_pending); 00478 } 00479 00480 os_free(data); 00481 } 00482 00483 00484 static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, 00485 size_t len) 00486 { 00487 int _errno = 0; 00488 00489 if (send(data->sock, msg, len, 0) < 0) { 00490 _errno = errno; 00491 perror("send[EAP-SIM DB UNIX]"); 00492 } 00493 00494 if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || 00495 _errno == ECONNREFUSED) { 00496 /* Try to reconnect */ 00497 eap_sim_db_close_socket(data); 00498 if (eap_sim_db_open_socket(data) < 0) 00499 return -1; 00500 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the " 00501 "external server"); 00502 if (send(data->sock, msg, len, 0) < 0) { 00503 perror("send[EAP-SIM DB UNIX]"); 00504 return -1; 00505 } 00506 } 00507 00508 return 0; 00509 } 00510 00511 00512 static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) 00513 { 00514 /* TODO: add limit for maximum length for pending list; remove latest 00515 * (i.e., last) entry from the list if the limit is reached; could also 00516 * use timeout to expire pending entries */ 00517 } 00518 00519 00547 int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, 00548 size_t identity_len, int max_chal, 00549 u8 *_rand, u8 *kc, u8 *sres, 00550 void *cb_session_ctx) 00551 { 00552 struct eap_sim_db_data *data = priv; 00553 struct eap_sim_db_pending *entry; 00554 int len, ret; 00555 size_t i; 00556 char msg[40]; 00557 00558 if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) { 00559 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", 00560 identity, identity_len); 00561 return EAP_SIM_DB_FAILURE; 00562 } 00563 identity++; 00564 identity_len--; 00565 for (i = 0; i < identity_len; i++) { 00566 if (identity[i] == '@') { 00567 identity_len = i; 00568 break; 00569 } 00570 } 00571 if (identity_len + 1 > sizeof(entry->imsi)) { 00572 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", 00573 identity, identity_len); 00574 return EAP_SIM_DB_FAILURE; 00575 } 00576 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI", 00577 identity, identity_len); 00578 00579 entry = eap_sim_db_get_pending(data, identity, identity_len, 0); 00580 if (entry) { 00581 int num_chal; 00582 if (entry->state == FAILURE) { 00583 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " 00584 "failure"); 00585 os_free(entry); 00586 return EAP_SIM_DB_FAILURE; 00587 } 00588 00589 if (entry->state == PENDING) { 00590 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " 00591 "still pending"); 00592 eap_sim_db_add_pending(data, entry); 00593 return EAP_SIM_DB_PENDING; 00594 } 00595 00596 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " 00597 "%d challenges", entry->u.sim.num_chal); 00598 num_chal = entry->u.sim.num_chal; 00599 if (num_chal > max_chal) 00600 num_chal = max_chal; 00601 os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN); 00602 os_memcpy(sres, entry->u.sim.sres, 00603 num_chal * EAP_SIM_SRES_LEN); 00604 os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN); 00605 os_free(entry); 00606 return num_chal; 00607 } 00608 00609 if (data->sock < 0) { 00610 if (eap_sim_db_open_socket(data) < 0) 00611 return EAP_SIM_DB_FAILURE; 00612 } 00613 00614 len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); 00615 if (len < 0 || len + identity_len >= sizeof(msg)) 00616 return EAP_SIM_DB_FAILURE; 00617 os_memcpy(msg + len, identity, identity_len); 00618 len += identity_len; 00619 ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); 00620 if (ret < 0 || (size_t) ret >= sizeof(msg) - len) 00621 return EAP_SIM_DB_FAILURE; 00622 len += ret; 00623 00624 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " 00625 "data for IMSI", identity, identity_len); 00626 if (eap_sim_db_send(data, msg, len) < 0) 00627 return EAP_SIM_DB_FAILURE; 00628 00629 entry = os_zalloc(sizeof(*entry)); 00630 if (entry == NULL) 00631 return EAP_SIM_DB_FAILURE; 00632 00633 os_get_time(&entry->timestamp); 00634 os_memcpy(entry->imsi, identity, identity_len); 00635 entry->imsi_len = identity_len; 00636 entry->cb_session_ctx = cb_session_ctx; 00637 entry->state = PENDING; 00638 eap_sim_db_add_pending(data, entry); 00639 eap_sim_db_expire_pending(data); 00640 00641 return EAP_SIM_DB_PENDING; 00642 } 00643 00644 00645 static struct eap_sim_pseudonym * 00646 eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity, 00647 size_t identity_len) 00648 { 00649 char *pseudonym; 00650 size_t len; 00651 struct eap_sim_pseudonym *p; 00652 00653 if (identity_len == 0 || 00654 (identity[0] != EAP_SIM_PSEUDONYM_PREFIX && 00655 identity[0] != EAP_AKA_PSEUDONYM_PREFIX)) 00656 return NULL; 00657 00658 /* Remove possible realm from identity */ 00659 len = 0; 00660 while (len < identity_len) { 00661 if (identity[len] == '@') 00662 break; 00663 len++; 00664 } 00665 00666 pseudonym = os_malloc(len + 1); 00667 if (pseudonym == NULL) 00668 return NULL; 00669 os_memcpy(pseudonym, identity, len); 00670 pseudonym[len] = '\0'; 00671 00672 p = data->pseudonyms; 00673 while (p) { 00674 if (os_strcmp(p->pseudonym, pseudonym) == 0) 00675 break; 00676 p = p->next; 00677 } 00678 00679 os_free(pseudonym); 00680 00681 return p; 00682 } 00683 00684 00685 static struct eap_sim_pseudonym * 00686 eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity, 00687 size_t identity_len) 00688 { 00689 struct eap_sim_pseudonym *p; 00690 00691 if (identity_len == 0 || 00692 (identity[0] != EAP_SIM_PERMANENT_PREFIX && 00693 identity[0] != EAP_AKA_PERMANENT_PREFIX)) 00694 return NULL; 00695 00696 p = data->pseudonyms; 00697 while (p) { 00698 if (identity_len == p->identity_len && 00699 os_memcmp(p->identity, identity, identity_len) == 0) 00700 break; 00701 p = p->next; 00702 } 00703 00704 return p; 00705 } 00706 00707 00708 static struct eap_sim_reauth * 00709 eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity, 00710 size_t identity_len) 00711 { 00712 char *reauth_id; 00713 size_t len; 00714 struct eap_sim_reauth *r; 00715 00716 if (identity_len == 0 || 00717 (identity[0] != EAP_SIM_REAUTH_ID_PREFIX && 00718 identity[0] != EAP_AKA_REAUTH_ID_PREFIX)) 00719 return NULL; 00720 00721 /* Remove possible realm from identity */ 00722 len = 0; 00723 while (len < identity_len) { 00724 if (identity[len] == '@') 00725 break; 00726 len++; 00727 } 00728 00729 reauth_id = os_malloc(len + 1); 00730 if (reauth_id == NULL) 00731 return NULL; 00732 os_memcpy(reauth_id, identity, len); 00733 reauth_id[len] = '\0'; 00734 00735 r = data->reauths; 00736 while (r) { 00737 if (os_strcmp(r->reauth_id, reauth_id) == 0) 00738 break; 00739 r = r->next; 00740 } 00741 00742 os_free(reauth_id); 00743 00744 return r; 00745 } 00746 00747 00748 static struct eap_sim_reauth * 00749 eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity, 00750 size_t identity_len) 00751 { 00752 struct eap_sim_pseudonym *p; 00753 struct eap_sim_reauth *r; 00754 00755 if (identity_len == 0) 00756 return NULL; 00757 00758 p = eap_sim_db_get_pseudonym(data, identity, identity_len); 00759 if (p == NULL) 00760 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); 00761 if (p) { 00762 identity = p->identity; 00763 identity_len = p->identity_len; 00764 } 00765 00766 r = data->reauths; 00767 while (r) { 00768 if (identity_len == r->identity_len && 00769 os_memcmp(r->identity, identity, identity_len) == 0) 00770 break; 00771 r = r->next; 00772 } 00773 00774 return r; 00775 } 00776 00777 00788 int eap_sim_db_identity_known(void *priv, const u8 *identity, 00789 size_t identity_len) 00790 { 00791 struct eap_sim_db_data *data = priv; 00792 00793 if (identity == NULL || identity_len < 2) 00794 return -1; 00795 00796 if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX || 00797 identity[0] == EAP_AKA_PSEUDONYM_PREFIX) { 00798 struct eap_sim_pseudonym *p = 00799 eap_sim_db_get_pseudonym(data, identity, identity_len); 00800 return p ? 0 : -1; 00801 } 00802 00803 if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX || 00804 identity[0] == EAP_AKA_REAUTH_ID_PREFIX) { 00805 struct eap_sim_reauth *r = 00806 eap_sim_db_get_reauth(data, identity, identity_len); 00807 return r ? 0 : -1; 00808 } 00809 00810 if (identity[0] != EAP_SIM_PERMANENT_PREFIX && 00811 identity[0] != EAP_AKA_PERMANENT_PREFIX) { 00812 /* Unknown identity prefix */ 00813 return -1; 00814 } 00815 00816 /* TODO: Should consider asking HLR/AuC gateway whether this permanent 00817 * identity is known. If it is, EAP-SIM/AKA can skip identity request. 00818 * In case of EAP-AKA, this would reduce number of needed round-trips. 00819 * Ideally, this would be done with one wait, i.e., just request 00820 * authentication data and store it for the next use. This would then 00821 * need to use similar pending-request functionality as the normal 00822 * request for authentication data at later phase. 00823 */ 00824 return -1; 00825 } 00826 00827 00828 static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) 00829 { 00830 char *id, *pos, *end; 00831 u8 buf[10]; 00832 00833 if (os_get_random(buf, sizeof(buf))) 00834 return NULL; 00835 id = os_malloc(sizeof(buf) * 2 + 2); 00836 if (id == NULL) 00837 return NULL; 00838 00839 pos = id; 00840 end = id + sizeof(buf) * 2 + 2; 00841 *pos++ = prefix; 00842 pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf)); 00843 00844 return id; 00845 } 00846 00847 00859 char * eap_sim_db_get_next_pseudonym(void *priv, int aka) 00860 { 00861 struct eap_sim_db_data *data = priv; 00862 return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX : 00863 EAP_SIM_PSEUDONYM_PREFIX); 00864 } 00865 00866 00879 char * eap_sim_db_get_next_reauth_id(void *priv, int aka) 00880 { 00881 struct eap_sim_db_data *data = priv; 00882 return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX : 00883 EAP_SIM_REAUTH_ID_PREFIX); 00884 } 00885 00886 00900 int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, 00901 size_t identity_len, char *pseudonym) 00902 { 00903 struct eap_sim_db_data *data = priv; 00904 struct eap_sim_pseudonym *p; 00905 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity", 00906 identity, identity_len); 00907 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym); 00908 00909 /* TODO: could store last two pseudonyms */ 00910 p = eap_sim_db_get_pseudonym(data, identity, identity_len); 00911 if (p == NULL) 00912 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); 00913 00914 if (p) { 00915 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " 00916 "pseudonym: %s", p->pseudonym); 00917 os_free(p->pseudonym); 00918 p->pseudonym = pseudonym; 00919 return 0; 00920 } 00921 00922 p = os_zalloc(sizeof(*p)); 00923 if (p == NULL) { 00924 os_free(pseudonym); 00925 return -1; 00926 } 00927 00928 p->next = data->pseudonyms; 00929 p->identity = os_malloc(identity_len); 00930 if (p->identity == NULL) { 00931 os_free(p); 00932 os_free(pseudonym); 00933 return -1; 00934 } 00935 os_memcpy(p->identity, identity, identity_len); 00936 p->identity_len = identity_len; 00937 p->pseudonym = pseudonym; 00938 data->pseudonyms = p; 00939 00940 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry"); 00941 return 0; 00942 } 00943 00944 00945 static struct eap_sim_reauth * 00946 eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity, 00947 size_t identity_len, char *reauth_id, u16 counter) 00948 { 00949 struct eap_sim_reauth *r; 00950 00951 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity", 00952 identity, identity_len); 00953 wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id); 00954 00955 r = eap_sim_db_get_reauth(data, identity, identity_len); 00956 if (r == NULL) 00957 r = eap_sim_db_get_reauth_id(data, identity, identity_len); 00958 00959 if (r) { 00960 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " 00961 "reauth_id: %s", r->reauth_id); 00962 os_free(r->reauth_id); 00963 r->reauth_id = reauth_id; 00964 } else { 00965 r = os_zalloc(sizeof(*r)); 00966 if (r == NULL) { 00967 os_free(reauth_id); 00968 return NULL; 00969 } 00970 00971 r->next = data->reauths; 00972 r->identity = os_malloc(identity_len); 00973 if (r->identity == NULL) { 00974 os_free(r); 00975 os_free(reauth_id); 00976 return NULL; 00977 } 00978 os_memcpy(r->identity, identity, identity_len); 00979 r->identity_len = identity_len; 00980 r->reauth_id = reauth_id; 00981 data->reauths = r; 00982 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry"); 00983 } 00984 00985 r->counter = counter; 00986 00987 return r; 00988 } 00989 00990 01007 int eap_sim_db_add_reauth(void *priv, const u8 *identity, 01008 size_t identity_len, char *reauth_id, u16 counter, 01009 const u8 *mk) 01010 { 01011 struct eap_sim_db_data *data = priv; 01012 struct eap_sim_reauth *r; 01013 01014 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id, 01015 counter); 01016 if (r == NULL) 01017 return -1; 01018 01019 os_memcpy(r->mk, mk, EAP_SIM_MK_LEN); 01020 r->aka_prime = 0; 01021 01022 return 0; 01023 } 01024 01025 01026 #ifdef EAP_SERVER_AKA_PRIME 01027 01045 int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity, 01046 size_t identity_len, char *reauth_id, 01047 u16 counter, const u8 *k_encr, const u8 *k_aut, 01048 const u8 *k_re) 01049 { 01050 struct eap_sim_db_data *data = priv; 01051 struct eap_sim_reauth *r; 01052 01053 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id, 01054 counter); 01055 if (r == NULL) 01056 return -1; 01057 01058 r->aka_prime = 1; 01059 os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN); 01060 os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN); 01061 os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN); 01062 01063 return 0; 01064 } 01065 #endif /* EAP_SERVER_AKA_PRIME */ 01066 01067 01076 const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity, 01077 size_t identity_len, size_t *len) 01078 { 01079 struct eap_sim_db_data *data = priv; 01080 struct eap_sim_pseudonym *p; 01081 01082 if (identity == NULL) 01083 return NULL; 01084 01085 p = eap_sim_db_get_pseudonym(data, identity, identity_len); 01086 if (p == NULL) 01087 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); 01088 if (p == NULL) 01089 return NULL; 01090 01091 *len = p->identity_len; 01092 return p->identity; 01093 } 01094 01095 01104 struct eap_sim_reauth * 01105 eap_sim_db_get_reauth_entry(void *priv, const u8 *identity, 01106 size_t identity_len) 01107 { 01108 struct eap_sim_db_data *data = priv; 01109 struct eap_sim_reauth *r; 01110 01111 if (identity == NULL) 01112 return NULL; 01113 r = eap_sim_db_get_reauth(data, identity, identity_len); 01114 if (r == NULL) 01115 r = eap_sim_db_get_reauth_id(data, identity, identity_len); 01116 return r; 01117 } 01118 01119 01126 void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) 01127 { 01128 struct eap_sim_db_data *data = priv; 01129 struct eap_sim_reauth *r, *prev = NULL; 01130 r = data->reauths; 01131 while (r) { 01132 if (r == reauth) { 01133 if (prev) 01134 prev->next = r->next; 01135 else 01136 data->reauths = r->next; 01137 eap_sim_db_free_reauth(r); 01138 return; 01139 } 01140 prev = r; 01141 r = r->next; 01142 } 01143 } 01144 01145 01174 int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, 01175 size_t identity_len, u8 *_rand, u8 *autn, u8 *ik, 01176 u8 *ck, u8 *res, size_t *res_len, 01177 void *cb_session_ctx) 01178 { 01179 struct eap_sim_db_data *data = priv; 01180 struct eap_sim_db_pending *entry; 01181 int len; 01182 size_t i; 01183 char msg[40]; 01184 01185 if (identity_len < 2 || identity == NULL || 01186 identity[0] != EAP_AKA_PERMANENT_PREFIX) { 01187 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", 01188 identity, identity_len); 01189 return EAP_SIM_DB_FAILURE; 01190 } 01191 identity++; 01192 identity_len--; 01193 for (i = 0; i < identity_len; i++) { 01194 if (identity[i] == '@') { 01195 identity_len = i; 01196 break; 01197 } 01198 } 01199 if (identity_len + 1 > sizeof(entry->imsi)) { 01200 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", 01201 identity, identity_len); 01202 return EAP_SIM_DB_FAILURE; 01203 } 01204 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI", 01205 identity, identity_len); 01206 01207 entry = eap_sim_db_get_pending(data, identity, identity_len, 1); 01208 if (entry) { 01209 if (entry->state == FAILURE) { 01210 os_free(entry); 01211 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); 01212 return EAP_SIM_DB_FAILURE; 01213 } 01214 01215 if (entry->state == PENDING) { 01216 eap_sim_db_add_pending(data, entry); 01217 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending"); 01218 return EAP_SIM_DB_PENDING; 01219 } 01220 01221 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully " 01222 "received authentication data"); 01223 os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN); 01224 os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN); 01225 os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN); 01226 os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); 01227 os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); 01228 *res_len = entry->u.aka.res_len; 01229 os_free(entry); 01230 return 0; 01231 } 01232 01233 if (data->sock < 0) { 01234 if (eap_sim_db_open_socket(data) < 0) 01235 return EAP_SIM_DB_FAILURE; 01236 } 01237 01238 len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); 01239 if (len < 0 || len + identity_len >= sizeof(msg)) 01240 return EAP_SIM_DB_FAILURE; 01241 os_memcpy(msg + len, identity, identity_len); 01242 len += identity_len; 01243 01244 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " 01245 "data for IMSI", identity, identity_len); 01246 if (eap_sim_db_send(data, msg, len) < 0) 01247 return EAP_SIM_DB_FAILURE; 01248 01249 entry = os_zalloc(sizeof(*entry)); 01250 if (entry == NULL) 01251 return EAP_SIM_DB_FAILURE; 01252 01253 os_get_time(&entry->timestamp); 01254 entry->aka = 1; 01255 os_memcpy(entry->imsi, identity, identity_len); 01256 entry->imsi_len = identity_len; 01257 entry->cb_session_ctx = cb_session_ctx; 01258 entry->state = PENDING; 01259 eap_sim_db_add_pending(data, entry); 01260 eap_sim_db_expire_pending(data); 01261 01262 return EAP_SIM_DB_PENDING; 01263 } 01264 01265 01281 int eap_sim_db_resynchronize(void *priv, const u8 *identity, 01282 size_t identity_len, const u8 *auts, 01283 const u8 *_rand) 01284 { 01285 struct eap_sim_db_data *data = priv; 01286 size_t i; 01287 01288 if (identity_len < 2 || identity == NULL || 01289 identity[0] != EAP_AKA_PERMANENT_PREFIX) { 01290 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", 01291 identity, identity_len); 01292 return -1; 01293 } 01294 identity++; 01295 identity_len--; 01296 for (i = 0; i < identity_len; i++) { 01297 if (identity[i] == '@') { 01298 identity_len = i; 01299 break; 01300 } 01301 } 01302 if (identity_len > 20) { 01303 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", 01304 identity, identity_len); 01305 return -1; 01306 } 01307 01308 if (data->sock >= 0) { 01309 char msg[100]; 01310 int len, ret; 01311 01312 len = os_snprintf(msg, sizeof(msg), "AKA-AUTS "); 01313 if (len < 0 || len + identity_len >= sizeof(msg)) 01314 return -1; 01315 os_memcpy(msg + len, identity, identity_len); 01316 len += identity_len; 01317 01318 ret = os_snprintf(msg + len, sizeof(msg) - len, " "); 01319 if (ret < 0 || (size_t) ret >= sizeof(msg) - len) 01320 return -1; 01321 len += ret; 01322 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, 01323 auts, EAP_AKA_AUTS_LEN); 01324 ret = os_snprintf(msg + len, sizeof(msg) - len, " "); 01325 if (ret < 0 || (size_t) ret >= sizeof(msg) - len) 01326 return -1; 01327 len += ret; 01328 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, 01329 _rand, EAP_AKA_RAND_LEN); 01330 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for " 01331 "IMSI", identity, identity_len); 01332 if (eap_sim_db_send(data, msg, len) < 0) 01333 return -1; 01334 } 01335 01336 return 0; 01337 }