00001
00047 #include <stdlib.h>
00048 #include <stdio.h>
00049 #include <unistd.h>
00050 #include <signal.h>
00051 #include <string.h>
00052 #include <sys/types.h>
00053 #include <sys/socket.h>
00054 #include <sys/un.h>
00055
00056 #include "common.h"
00057 #include "milenage.h"
00058
00059 static const char *default_socket_path = "/tmp/hlr_auc_gw.sock";
00060 static const char *socket_path;
00061 static const char *default_gsm_triplet_file = "hostapd.sim_db";
00062 static const char *gsm_triplet_file;
00063 static int serv_sock = -1;
00064
00065
00066 struct milenage_parameters {
00067 struct milenage_parameters *next;
00068 char imsi[20];
00069 u8 ki[16];
00070 u8 opc[16];
00071 u8 amf[2];
00072 u8 sqn[6];
00073 };
00074
00075 static struct milenage_parameters *milenage_db = NULL;
00076
00077 #define EAP_SIM_MAX_CHAL 3
00078
00079 #define EAP_AKA_RAND_LEN 16
00080 #define EAP_AKA_AUTN_LEN 16
00081 #define EAP_AKA_AUTS_LEN 14
00082 #define EAP_AKA_RES_MAX_LEN 16
00083 #define EAP_AKA_IK_LEN 16
00084 #define EAP_AKA_CK_LEN 16
00085
00086
00087 static int open_socket(const char *path)
00088 {
00089 struct sockaddr_un addr;
00090 int s;
00091
00092 s = socket(PF_UNIX, SOCK_DGRAM, 0);
00093 if (s < 0) {
00094 perror("socket(PF_UNIX)");
00095 return -1;
00096 }
00097
00098 memset(&addr, 0, sizeof(addr));
00099 addr.sun_family = AF_UNIX;
00100 strncpy(addr.sun_path, path, sizeof(addr.sun_path));
00101 if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
00102 perror("bind(PF_UNIX)");
00103 close(s);
00104 return -1;
00105 }
00106
00107 return s;
00108 }
00109
00110
00111 static int read_milenage(const char *fname)
00112 {
00113 FILE *f;
00114 char buf[200], *pos, *pos2;
00115 struct milenage_parameters *m = NULL;
00116 int line, ret = 0;
00117
00118 if (fname == NULL)
00119 return -1;
00120
00121 f = fopen(fname, "r");
00122 if (f == NULL) {
00123 printf("Could not open Milenage data file '%s'\n", fname);
00124 return -1;
00125 }
00126
00127 line = 0;
00128 while (fgets(buf, sizeof(buf), f)) {
00129 line++;
00130
00131
00132 buf[sizeof(buf) - 1] = '\0';
00133 if (buf[0] == '#')
00134 continue;
00135 pos = buf;
00136 while (*pos != '\0' && *pos != '\n')
00137 pos++;
00138 if (*pos == '\n')
00139 *pos = '\0';
00140 pos = buf;
00141 if (*pos == '\0')
00142 continue;
00143
00144 m = os_zalloc(sizeof(*m));
00145 if (m == NULL) {
00146 ret = -1;
00147 break;
00148 }
00149
00150
00151 pos2 = strchr(pos, ' ');
00152 if (pos2 == NULL) {
00153 printf("%s:%d - Invalid IMSI (%s)\n",
00154 fname, line, pos);
00155 ret = -1;
00156 break;
00157 }
00158 *pos2 = '\0';
00159 if (strlen(pos) >= sizeof(m->imsi)) {
00160 printf("%s:%d - Too long IMSI (%s)\n",
00161 fname, line, pos);
00162 ret = -1;
00163 break;
00164 }
00165 strncpy(m->imsi, pos, sizeof(m->imsi));
00166 pos = pos2 + 1;
00167
00168
00169 pos2 = strchr(pos, ' ');
00170 if (pos2 == NULL) {
00171 printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
00172 ret = -1;
00173 break;
00174 }
00175 *pos2 = '\0';
00176 if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) {
00177 printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
00178 ret = -1;
00179 break;
00180 }
00181 pos = pos2 + 1;
00182
00183
00184 pos2 = strchr(pos, ' ');
00185 if (pos2 == NULL) {
00186 printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
00187 ret = -1;
00188 break;
00189 }
00190 *pos2 = '\0';
00191 if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) {
00192 printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
00193 ret = -1;
00194 break;
00195 }
00196 pos = pos2 + 1;
00197
00198
00199 pos2 = strchr(pos, ' ');
00200 if (pos2 == NULL) {
00201 printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
00202 ret = -1;
00203 break;
00204 }
00205 *pos2 = '\0';
00206 if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) {
00207 printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
00208 ret = -1;
00209 break;
00210 }
00211 pos = pos2 + 1;
00212
00213
00214 pos2 = strchr(pos, ' ');
00215 if (pos2)
00216 *pos2 = '\0';
00217 if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) {
00218 printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos);
00219 ret = -1;
00220 break;
00221 }
00222 pos = pos2 + 1;
00223
00224 m->next = milenage_db;
00225 milenage_db = m;
00226 m = NULL;
00227 }
00228 free(m);
00229
00230 fclose(f);
00231
00232 return ret;
00233 }
00234
00235
00236 static struct milenage_parameters * get_milenage(const char *imsi)
00237 {
00238 struct milenage_parameters *m = milenage_db;
00239
00240 while (m) {
00241 if (strcmp(m->imsi, imsi) == 0)
00242 break;
00243 m = m->next;
00244 }
00245
00246 return m;
00247 }
00248
00249
00250 static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
00251 char *imsi)
00252 {
00253 FILE *f;
00254 int count, max_chal, ret;
00255 char buf[80], *pos;
00256 char reply[1000], *rpos, *rend;
00257 struct milenage_parameters *m;
00258
00259 reply[0] = '\0';
00260
00261 pos = strchr(imsi, ' ');
00262 if (pos) {
00263 *pos++ = '\0';
00264 max_chal = atoi(pos);
00265 if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL)
00266 max_chal = EAP_SIM_MAX_CHAL;
00267 } else
00268 max_chal = EAP_SIM_MAX_CHAL;
00269
00270 rend = &reply[sizeof(reply)];
00271 rpos = reply;
00272 ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi);
00273 if (ret < 0 || ret >= rend - rpos)
00274 return;
00275 rpos += ret;
00276
00277 m = get_milenage(imsi);
00278 if (m) {
00279 u8 _rand[16], sres[4], kc[8];
00280 for (count = 0; count < max_chal; count++) {
00281 os_get_random(_rand, 16);
00282 gsm_milenage(m->opc, m->ki, _rand, sres, kc);
00283 *rpos++ = ' ';
00284 rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
00285 *rpos++ = ':';
00286 rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4);
00287 *rpos++ = ':';
00288 rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16);
00289 }
00290 *rpos = '\0';
00291 goto send;
00292 }
00293
00294
00295
00296
00297 f = fopen(gsm_triplet_file, "r");
00298 if (f == NULL) {
00299 printf("Could not open GSM triplet file '%s'\n",
00300 gsm_triplet_file);
00301 ret = snprintf(rpos, rend - rpos, " FAILURE");
00302 if (ret < 0 || ret >= rend - rpos)
00303 return;
00304 rpos += ret;
00305 goto send;
00306 }
00307
00308 count = 0;
00309 while (count < max_chal && fgets(buf, sizeof(buf), f)) {
00310
00311 buf[sizeof(buf) - 1] = '\0';
00312 pos = buf;
00313 while (*pos != '\0' && *pos != '\n')
00314 pos++;
00315 if (*pos == '\n')
00316 *pos = '\0';
00317 if (pos - buf < 60 || pos[0] == '#')
00318 continue;
00319
00320 pos = strchr(buf, ':');
00321 if (pos == NULL)
00322 continue;
00323 *pos++ = '\0';
00324 if (strcmp(buf, imsi) != 0)
00325 continue;
00326
00327 ret = snprintf(rpos, rend - rpos, " %s", pos);
00328 if (ret < 0 || ret >= rend - rpos) {
00329 fclose(f);
00330 return;
00331 }
00332 rpos += ret;
00333 count++;
00334 }
00335
00336 fclose(f);
00337
00338 if (count == 0) {
00339 printf("No GSM triplets found for %s\n", imsi);
00340 ret = snprintf(rpos, rend - rpos, " FAILURE");
00341 if (ret < 0 || ret >= rend - rpos)
00342 return;
00343 rpos += ret;
00344 }
00345
00346 send:
00347 printf("Send: %s\n", reply);
00348 if (sendto(s, reply, rpos - reply, 0,
00349 (struct sockaddr *) from, fromlen) < 0)
00350 perror("send");
00351 }
00352
00353
00354 static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
00355 char *imsi)
00356 {
00357
00358 char reply[1000], *pos, *end;
00359 u8 _rand[EAP_AKA_RAND_LEN];
00360 u8 autn[EAP_AKA_AUTN_LEN];
00361 u8 ik[EAP_AKA_IK_LEN];
00362 u8 ck[EAP_AKA_CK_LEN];
00363 u8 res[EAP_AKA_RES_MAX_LEN];
00364 size_t res_len;
00365 int ret;
00366 struct milenage_parameters *m;
00367
00368 m = get_milenage(imsi);
00369 if (m) {
00370 os_get_random(_rand, EAP_AKA_RAND_LEN);
00371 res_len = EAP_AKA_RES_MAX_LEN;
00372 inc_byte_array(m->sqn, 6);
00373 printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
00374 m->sqn[0], m->sqn[1], m->sqn[2],
00375 m->sqn[3], m->sqn[4], m->sqn[5]);
00376 milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand,
00377 autn, ik, ck, res, &res_len);
00378 } else {
00379 printf("Unknown IMSI: %s\n", imsi);
00380 #ifdef AKA_USE_FIXED_TEST_VALUES
00381 printf("Using fixed test values for AKA\n");
00382 memset(_rand, '0', EAP_AKA_RAND_LEN);
00383 memset(autn, '1', EAP_AKA_AUTN_LEN);
00384 memset(ik, '3', EAP_AKA_IK_LEN);
00385 memset(ck, '4', EAP_AKA_CK_LEN);
00386 memset(res, '2', EAP_AKA_RES_MAX_LEN);
00387 res_len = EAP_AKA_RES_MAX_LEN;
00388 #else
00389 return;
00390 #endif
00391 }
00392
00393 pos = reply;
00394 end = &reply[sizeof(reply)];
00395 ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi);
00396 if (ret < 0 || ret >= end - pos)
00397 return;
00398 pos += ret;
00399 pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
00400 *pos++ = ' ';
00401 pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN);
00402 *pos++ = ' ';
00403 pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN);
00404 *pos++ = ' ';
00405 pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN);
00406 *pos++ = ' ';
00407 pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
00408
00409 printf("Send: %s\n", reply);
00410
00411 if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from,
00412 fromlen) < 0)
00413 perror("send");
00414 }
00415
00416
00417 static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen,
00418 char *imsi)
00419 {
00420 char *auts, *rand;
00421 u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6];
00422 struct milenage_parameters *m;
00423
00424
00425
00426 auts = strchr(imsi, ' ');
00427 if (auts == NULL)
00428 return;
00429 *auts++ = '\0';
00430
00431 rand = strchr(auts, ' ');
00432 if (rand == NULL)
00433 return;
00434 *rand++ = '\0';
00435
00436 printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, rand);
00437 if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) ||
00438 hexstr2bin(rand, _rand, EAP_AKA_RAND_LEN)) {
00439 printf("Could not parse AUTS/RAND\n");
00440 return;
00441 }
00442
00443 m = get_milenage(imsi);
00444 if (m == NULL) {
00445 printf("Unknown IMSI: %s\n", imsi);
00446 return;
00447 }
00448
00449 if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) {
00450 printf("AKA-AUTS: Incorrect MAC-S\n");
00451 } else {
00452 memcpy(m->sqn, sqn, 6);
00453 printf("AKA-AUTS: Re-synchronized: "
00454 "SQN=%02x%02x%02x%02x%02x%02x\n",
00455 sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
00456 }
00457 }
00458
00459
00460 static int process(int s)
00461 {
00462 char buf[1000];
00463 struct sockaddr_un from;
00464 socklen_t fromlen;
00465 ssize_t res;
00466
00467 fromlen = sizeof(from);
00468 res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from,
00469 &fromlen);
00470 if (res < 0) {
00471 perror("recvfrom");
00472 return -1;
00473 }
00474
00475 if (res == 0)
00476 return 0;
00477
00478 if ((size_t) res >= sizeof(buf))
00479 res = sizeof(buf) - 1;
00480 buf[res] = '\0';
00481
00482 printf("Received: %s\n", buf);
00483
00484 if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0)
00485 sim_req_auth(s, &from, fromlen, buf + 13);
00486 else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0)
00487 aka_req_auth(s, &from, fromlen, buf + 13);
00488 else if (strncmp(buf, "AKA-AUTS ", 9) == 0)
00489 aka_auts(s, &from, fromlen, buf + 9);
00490 else
00491 printf("Unknown request: %s\n", buf);
00492
00493 return 0;
00494 }
00495
00496
00497 static void cleanup(void)
00498 {
00499 struct milenage_parameters *m, *prev;
00500
00501 m = milenage_db;
00502 while (m) {
00503 prev = m;
00504 m = m->next;
00505 free(prev);
00506 }
00507
00508 close(serv_sock);
00509 unlink(socket_path);
00510 }
00511
00512
00513 static void handle_term(int sig)
00514 {
00515 printf("Signal %d - terminate\n", sig);
00516 exit(0);
00517 }
00518
00519
00520 static void usage(void)
00521 {
00522 printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
00523 "database/authenticator\n"
00524 "Copyright (c) 2005-2006, Jouni Malinen <[email protected]>\n"
00525 "\n"
00526 "usage:\n"
00527 "hlr_auc_gw [-h] [-s<socket path>] [-g<triplet file>] "
00528 "[-m<milenage file>]\n"
00529 "\n"
00530 "options:\n"
00531 " -h = show this usage help\n"
00532 " -s<socket path> = path for UNIX domain socket\n"
00533 " (default: %s)\n"
00534 " -g<triplet file> = path for GSM authentication triplets\n"
00535 " (default: %s)\n"
00536 " -m<milenage file> = path for Milenage keys\n",
00537 default_socket_path, default_gsm_triplet_file);
00538 }
00539
00540
00541 int main(int argc, char *argv[])
00542 {
00543 int c;
00544 char *milenage_file = NULL;
00545
00546 socket_path = default_socket_path;
00547 gsm_triplet_file = default_gsm_triplet_file;
00548
00549 for (;;) {
00550 c = getopt(argc, argv, "g:hm:s:");
00551 if (c < 0)
00552 break;
00553 switch (c) {
00554 case 'g':
00555 gsm_triplet_file = optarg;
00556 break;
00557 case 'h':
00558 usage();
00559 return 0;
00560 case 'm':
00561 milenage_file = optarg;
00562 break;
00563 case 's':
00564 socket_path = optarg;
00565 break;
00566 default:
00567 usage();
00568 return -1;
00569 }
00570 }
00571
00572 if (milenage_file && read_milenage(milenage_file) < 0)
00573 return -1;
00574
00575 serv_sock = open_socket(socket_path);
00576 if (serv_sock < 0)
00577 return -1;
00578
00579 printf("Listening for requests on %s\n", socket_path);
00580
00581 atexit(cleanup);
00582 signal(SIGTERM, handle_term);
00583 signal(SIGINT, handle_term);
00584
00585 for (;;)
00586 process(serv_sock);
00587
00588 return 0;
00589 }
00590