eap_sim_db.c
Go to the documentation of this file.
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 }


wpa_supplicant
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Jan 2 2014 11:26:37