1e28a4053SRui Paulo /*
2e28a4053SRui Paulo * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
3*85732ac8SCy Schubert * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
7e28a4053SRui Paulo *
8e28a4053SRui Paulo * This is an example implementation of the EAP-SIM/AKA database/authentication
9e28a4053SRui Paulo * gateway interface to HLR/AuC. It is expected to be replaced with an
10e28a4053SRui Paulo * implementation of SS7 gateway to GSM/UMTS authentication center (HLR/AuC) or
11e28a4053SRui Paulo * a local implementation of SIM triplet and AKA authentication data generator.
12e28a4053SRui Paulo *
13e28a4053SRui Paulo * hostapd will send SIM/AKA authentication queries over a UNIX domain socket
14e28a4053SRui Paulo * to and external program, e.g., this hlr_auc_gw. This interface uses simple
15e28a4053SRui Paulo * text-based format:
16e28a4053SRui Paulo *
17e28a4053SRui Paulo * EAP-SIM / GSM triplet query/response:
18e28a4053SRui Paulo * SIM-REQ-AUTH <IMSI> <max_chal>
19e28a4053SRui Paulo * SIM-RESP-AUTH <IMSI> Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3]
20e28a4053SRui Paulo * SIM-RESP-AUTH <IMSI> FAILURE
215b9c547cSRui Paulo * GSM-AUTH-REQ <IMSI> RAND1:RAND2[:RAND3]
225b9c547cSRui Paulo * GSM-AUTH-RESP <IMSI> Kc1:SRES1:Kc2:SRES2[:Kc3:SRES3]
235b9c547cSRui Paulo * GSM-AUTH-RESP <IMSI> FAILURE
24e28a4053SRui Paulo *
25e28a4053SRui Paulo * EAP-AKA / UMTS query/response:
26e28a4053SRui Paulo * AKA-REQ-AUTH <IMSI>
27e28a4053SRui Paulo * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
28e28a4053SRui Paulo * AKA-RESP-AUTH <IMSI> FAILURE
29e28a4053SRui Paulo *
30e28a4053SRui Paulo * EAP-AKA / UMTS AUTS (re-synchronization):
31e28a4053SRui Paulo * AKA-AUTS <IMSI> <AUTS> <RAND>
32e28a4053SRui Paulo *
33e28a4053SRui Paulo * IMSI and max_chal are sent as an ASCII string,
34e28a4053SRui Paulo * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings.
35e28a4053SRui Paulo *
365b9c547cSRui Paulo * An example implementation here reads GSM authentication triplets from a
37e28a4053SRui Paulo * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex
38e28a4053SRui Paulo * strings. This is used to simulate an HLR/AuC. As such, it is not very useful
39e28a4053SRui Paulo * for real life authentication, but it is useful both as an example
40f05cddf9SRui Paulo * implementation and for EAP-SIM/AKA/AKA' testing.
41f05cddf9SRui Paulo *
425b9c547cSRui Paulo * For a stronger example design, Milenage and GSM-Milenage algorithms can be
435b9c547cSRui Paulo * used to dynamically generate authenticatipn information for EAP-AKA/AKA' and
445b9c547cSRui Paulo * EAP-SIM, respectively, if Ki is known.
455b9c547cSRui Paulo *
46f05cddf9SRui Paulo * SQN generation follows the not time-based Profile 2 described in
47f05cddf9SRui Paulo * 3GPP TS 33.102 Annex C.3.2. The length of IND is 5 bits by default, but this
48f05cddf9SRui Paulo * can be changed with a command line options if needed.
49e28a4053SRui Paulo */
50e28a4053SRui Paulo
51e28a4053SRui Paulo #include "includes.h"
52e28a4053SRui Paulo #include <sys/un.h>
53f05cddf9SRui Paulo #ifdef CONFIG_SQLITE
54f05cddf9SRui Paulo #include <sqlite3.h>
55f05cddf9SRui Paulo #endif /* CONFIG_SQLITE */
56e28a4053SRui Paulo
57e28a4053SRui Paulo #include "common.h"
58e28a4053SRui Paulo #include "crypto/milenage.h"
59f05cddf9SRui Paulo #include "crypto/random.h"
60e28a4053SRui Paulo
61e28a4053SRui Paulo static const char *default_socket_path = "/tmp/hlr_auc_gw.sock";
62e28a4053SRui Paulo static const char *socket_path;
63e28a4053SRui Paulo static int serv_sock = -1;
64f05cddf9SRui Paulo static char *milenage_file = NULL;
65f05cddf9SRui Paulo static int update_milenage = 0;
66f05cddf9SRui Paulo static int sqn_changes = 0;
67f05cddf9SRui Paulo static int ind_len = 5;
685b9c547cSRui Paulo static int stdout_debug = 1;
69e28a4053SRui Paulo
70e28a4053SRui Paulo /* GSM triplets */
71e28a4053SRui Paulo struct gsm_triplet {
72e28a4053SRui Paulo struct gsm_triplet *next;
73e28a4053SRui Paulo char imsi[20];
74e28a4053SRui Paulo u8 kc[8];
75e28a4053SRui Paulo u8 sres[4];
76e28a4053SRui Paulo u8 _rand[16];
77e28a4053SRui Paulo };
78e28a4053SRui Paulo
79e28a4053SRui Paulo static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL;
80e28a4053SRui Paulo
81e28a4053SRui Paulo /* OPc and AMF parameters for Milenage (Example algorithms for AKA). */
82e28a4053SRui Paulo struct milenage_parameters {
83e28a4053SRui Paulo struct milenage_parameters *next;
84e28a4053SRui Paulo char imsi[20];
85e28a4053SRui Paulo u8 ki[16];
86e28a4053SRui Paulo u8 opc[16];
87e28a4053SRui Paulo u8 amf[2];
88e28a4053SRui Paulo u8 sqn[6];
89f05cddf9SRui Paulo int set;
90325151a3SRui Paulo size_t res_len;
91e28a4053SRui Paulo };
92e28a4053SRui Paulo
93e28a4053SRui Paulo static struct milenage_parameters *milenage_db = NULL;
94e28a4053SRui Paulo
95e28a4053SRui Paulo #define EAP_SIM_MAX_CHAL 3
96e28a4053SRui Paulo
97e28a4053SRui Paulo #define EAP_AKA_RAND_LEN 16
98e28a4053SRui Paulo #define EAP_AKA_AUTN_LEN 16
99e28a4053SRui Paulo #define EAP_AKA_AUTS_LEN 14
100325151a3SRui Paulo #define EAP_AKA_RES_MIN_LEN 4
101e28a4053SRui Paulo #define EAP_AKA_RES_MAX_LEN 16
102e28a4053SRui Paulo #define EAP_AKA_IK_LEN 16
103e28a4053SRui Paulo #define EAP_AKA_CK_LEN 16
104e28a4053SRui Paulo
105e28a4053SRui Paulo
106f05cddf9SRui Paulo #ifdef CONFIG_SQLITE
107f05cddf9SRui Paulo
108f05cddf9SRui Paulo static sqlite3 *sqlite_db = NULL;
109f05cddf9SRui Paulo static struct milenage_parameters db_tmp_milenage;
110f05cddf9SRui Paulo
111f05cddf9SRui Paulo
db_table_exists(sqlite3 * db,const char * name)112f05cddf9SRui Paulo static int db_table_exists(sqlite3 *db, const char *name)
113f05cddf9SRui Paulo {
114f05cddf9SRui Paulo char cmd[128];
115f05cddf9SRui Paulo os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
116f05cddf9SRui Paulo return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
117f05cddf9SRui Paulo }
118f05cddf9SRui Paulo
119f05cddf9SRui Paulo
db_table_create_milenage(sqlite3 * db)120f05cddf9SRui Paulo static int db_table_create_milenage(sqlite3 *db)
121f05cddf9SRui Paulo {
122f05cddf9SRui Paulo char *err = NULL;
123f05cddf9SRui Paulo const char *sql =
124f05cddf9SRui Paulo "CREATE TABLE milenage("
125f05cddf9SRui Paulo " imsi INTEGER PRIMARY KEY NOT NULL,"
126f05cddf9SRui Paulo " ki CHAR(32) NOT NULL,"
127f05cddf9SRui Paulo " opc CHAR(32) NOT NULL,"
128f05cddf9SRui Paulo " amf CHAR(4) NOT NULL,"
129325151a3SRui Paulo " sqn CHAR(12) NOT NULL,"
130325151a3SRui Paulo " res_len INTEGER"
131f05cddf9SRui Paulo ");";
132f05cddf9SRui Paulo
133f05cddf9SRui Paulo printf("Adding database table for milenage information\n");
134f05cddf9SRui Paulo if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
135f05cddf9SRui Paulo printf("SQLite error: %s\n", err);
136f05cddf9SRui Paulo sqlite3_free(err);
137f05cddf9SRui Paulo return -1;
138f05cddf9SRui Paulo }
139f05cddf9SRui Paulo
140f05cddf9SRui Paulo return 0;
141f05cddf9SRui Paulo }
142f05cddf9SRui Paulo
143f05cddf9SRui Paulo
db_open(const char * db_file)144f05cddf9SRui Paulo static sqlite3 * db_open(const char *db_file)
145f05cddf9SRui Paulo {
146f05cddf9SRui Paulo sqlite3 *db;
147f05cddf9SRui Paulo
148f05cddf9SRui Paulo if (sqlite3_open(db_file, &db)) {
149f05cddf9SRui Paulo printf("Failed to open database %s: %s\n",
150f05cddf9SRui Paulo db_file, sqlite3_errmsg(db));
151f05cddf9SRui Paulo sqlite3_close(db);
152f05cddf9SRui Paulo return NULL;
153f05cddf9SRui Paulo }
154f05cddf9SRui Paulo
155f05cddf9SRui Paulo if (!db_table_exists(db, "milenage") &&
156f05cddf9SRui Paulo db_table_create_milenage(db) < 0) {
157f05cddf9SRui Paulo sqlite3_close(db);
158f05cddf9SRui Paulo return NULL;
159f05cddf9SRui Paulo }
160f05cddf9SRui Paulo
161f05cddf9SRui Paulo return db;
162f05cddf9SRui Paulo }
163f05cddf9SRui Paulo
164f05cddf9SRui Paulo
get_milenage_cb(void * ctx,int argc,char * argv[],char * col[])165f05cddf9SRui Paulo static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[])
166f05cddf9SRui Paulo {
167f05cddf9SRui Paulo struct milenage_parameters *m = ctx;
168f05cddf9SRui Paulo int i;
169f05cddf9SRui Paulo
170f05cddf9SRui Paulo m->set = 1;
171f05cddf9SRui Paulo
172f05cddf9SRui Paulo for (i = 0; i < argc; i++) {
173f05cddf9SRui Paulo if (os_strcmp(col[i], "ki") == 0 && argv[i] &&
174f05cddf9SRui Paulo hexstr2bin(argv[i], m->ki, sizeof(m->ki))) {
175f05cddf9SRui Paulo printf("Invalid ki value in database\n");
176f05cddf9SRui Paulo return -1;
177f05cddf9SRui Paulo }
178f05cddf9SRui Paulo
179f05cddf9SRui Paulo if (os_strcmp(col[i], "opc") == 0 && argv[i] &&
180f05cddf9SRui Paulo hexstr2bin(argv[i], m->opc, sizeof(m->opc))) {
181f05cddf9SRui Paulo printf("Invalid opcvalue in database\n");
182f05cddf9SRui Paulo return -1;
183f05cddf9SRui Paulo }
184f05cddf9SRui Paulo
185f05cddf9SRui Paulo if (os_strcmp(col[i], "amf") == 0 && argv[i] &&
186f05cddf9SRui Paulo hexstr2bin(argv[i], m->amf, sizeof(m->amf))) {
187f05cddf9SRui Paulo printf("Invalid amf value in database\n");
188f05cddf9SRui Paulo return -1;
189f05cddf9SRui Paulo }
190f05cddf9SRui Paulo
191f05cddf9SRui Paulo if (os_strcmp(col[i], "sqn") == 0 && argv[i] &&
192f05cddf9SRui Paulo hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) {
193f05cddf9SRui Paulo printf("Invalid sqn value in database\n");
194f05cddf9SRui Paulo return -1;
195f05cddf9SRui Paulo }
196325151a3SRui Paulo
197325151a3SRui Paulo if (os_strcmp(col[i], "res_len") == 0 && argv[i]) {
198325151a3SRui Paulo m->res_len = atoi(argv[i]);
199325151a3SRui Paulo }
200f05cddf9SRui Paulo }
201f05cddf9SRui Paulo
202f05cddf9SRui Paulo return 0;
203f05cddf9SRui Paulo }
204f05cddf9SRui Paulo
205f05cddf9SRui Paulo
db_get_milenage(const char * imsi_txt)206f05cddf9SRui Paulo static struct milenage_parameters * db_get_milenage(const char *imsi_txt)
207f05cddf9SRui Paulo {
208f05cddf9SRui Paulo char cmd[128];
209f05cddf9SRui Paulo unsigned long long imsi;
210f05cddf9SRui Paulo
211f05cddf9SRui Paulo os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage));
212f05cddf9SRui Paulo imsi = atoll(imsi_txt);
213f05cddf9SRui Paulo os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi),
214f05cddf9SRui Paulo "%llu", imsi);
215f05cddf9SRui Paulo os_snprintf(cmd, sizeof(cmd),
216325151a3SRui Paulo "SELECT * FROM milenage WHERE imsi=%llu;", imsi);
217f05cddf9SRui Paulo if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage,
218f05cddf9SRui Paulo NULL) != SQLITE_OK)
219f05cddf9SRui Paulo return NULL;
220f05cddf9SRui Paulo
221f05cddf9SRui Paulo if (!db_tmp_milenage.set)
222f05cddf9SRui Paulo return NULL;
223f05cddf9SRui Paulo return &db_tmp_milenage;
224f05cddf9SRui Paulo }
225f05cddf9SRui Paulo
226f05cddf9SRui Paulo
db_update_milenage_sqn(struct milenage_parameters * m)227f05cddf9SRui Paulo static int db_update_milenage_sqn(struct milenage_parameters *m)
228f05cddf9SRui Paulo {
229f05cddf9SRui Paulo char cmd[128], val[13], *pos;
230f05cddf9SRui Paulo
2315b9c547cSRui Paulo if (sqlite_db == NULL)
2325b9c547cSRui Paulo return 0;
2335b9c547cSRui Paulo
234f05cddf9SRui Paulo pos = val;
235f05cddf9SRui Paulo pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6);
236f05cddf9SRui Paulo *pos = '\0';
237f05cddf9SRui Paulo os_snprintf(cmd, sizeof(cmd),
238f05cddf9SRui Paulo "UPDATE milenage SET sqn='%s' WHERE imsi=%s;",
239f05cddf9SRui Paulo val, m->imsi);
240f05cddf9SRui Paulo if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
241f05cddf9SRui Paulo printf("Failed to update SQN in database for IMSI %s\n",
242f05cddf9SRui Paulo m->imsi);
243f05cddf9SRui Paulo return -1;
244f05cddf9SRui Paulo }
245f05cddf9SRui Paulo return 0;
246f05cddf9SRui Paulo }
247f05cddf9SRui Paulo
248f05cddf9SRui Paulo #endif /* CONFIG_SQLITE */
249f05cddf9SRui Paulo
250f05cddf9SRui Paulo
open_socket(const char * path)251e28a4053SRui Paulo static int open_socket(const char *path)
252e28a4053SRui Paulo {
253e28a4053SRui Paulo struct sockaddr_un addr;
254e28a4053SRui Paulo int s;
255e28a4053SRui Paulo
256e28a4053SRui Paulo s = socket(PF_UNIX, SOCK_DGRAM, 0);
257e28a4053SRui Paulo if (s < 0) {
258e28a4053SRui Paulo perror("socket(PF_UNIX)");
259e28a4053SRui Paulo return -1;
260e28a4053SRui Paulo }
261e28a4053SRui Paulo
262e28a4053SRui Paulo memset(&addr, 0, sizeof(addr));
263e28a4053SRui Paulo addr.sun_family = AF_UNIX;
264e28a4053SRui Paulo os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
265e28a4053SRui Paulo if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
266f05cddf9SRui Paulo perror("hlr-auc-gw: bind(PF_UNIX)");
267e28a4053SRui Paulo close(s);
268e28a4053SRui Paulo return -1;
269e28a4053SRui Paulo }
270e28a4053SRui Paulo
271e28a4053SRui Paulo return s;
272e28a4053SRui Paulo }
273e28a4053SRui Paulo
274e28a4053SRui Paulo
read_gsm_triplets(const char * fname)275e28a4053SRui Paulo static int read_gsm_triplets(const char *fname)
276e28a4053SRui Paulo {
277e28a4053SRui Paulo FILE *f;
278e28a4053SRui Paulo char buf[200], *pos, *pos2;
279e28a4053SRui Paulo struct gsm_triplet *g = NULL;
280e28a4053SRui Paulo int line, ret = 0;
281e28a4053SRui Paulo
282e28a4053SRui Paulo if (fname == NULL)
283e28a4053SRui Paulo return -1;
284e28a4053SRui Paulo
285e28a4053SRui Paulo f = fopen(fname, "r");
286e28a4053SRui Paulo if (f == NULL) {
287780fb4a2SCy Schubert printf("Could not open GSM triplet data file '%s'\n", fname);
288e28a4053SRui Paulo return -1;
289e28a4053SRui Paulo }
290e28a4053SRui Paulo
291e28a4053SRui Paulo line = 0;
292e28a4053SRui Paulo while (fgets(buf, sizeof(buf), f)) {
293e28a4053SRui Paulo line++;
294e28a4053SRui Paulo
295e28a4053SRui Paulo /* Parse IMSI:Kc:SRES:RAND */
296e28a4053SRui Paulo buf[sizeof(buf) - 1] = '\0';
297e28a4053SRui Paulo if (buf[0] == '#')
298e28a4053SRui Paulo continue;
299e28a4053SRui Paulo pos = buf;
300e28a4053SRui Paulo while (*pos != '\0' && *pos != '\n')
301e28a4053SRui Paulo pos++;
302e28a4053SRui Paulo if (*pos == '\n')
303e28a4053SRui Paulo *pos = '\0';
304e28a4053SRui Paulo pos = buf;
305e28a4053SRui Paulo if (*pos == '\0')
306e28a4053SRui Paulo continue;
307e28a4053SRui Paulo
308e28a4053SRui Paulo g = os_zalloc(sizeof(*g));
309e28a4053SRui Paulo if (g == NULL) {
310e28a4053SRui Paulo ret = -1;
311e28a4053SRui Paulo break;
312e28a4053SRui Paulo }
313e28a4053SRui Paulo
314e28a4053SRui Paulo /* IMSI */
315780fb4a2SCy Schubert pos2 = NULL;
316780fb4a2SCy Schubert pos = str_token(buf, ":", &pos2);
317780fb4a2SCy Schubert if (!pos || os_strlen(pos) >= sizeof(g->imsi)) {
318780fb4a2SCy Schubert printf("%s:%d - Invalid IMSI\n", fname, line);
319e28a4053SRui Paulo ret = -1;
320e28a4053SRui Paulo break;
321e28a4053SRui Paulo }
322e28a4053SRui Paulo os_strlcpy(g->imsi, pos, sizeof(g->imsi));
323e28a4053SRui Paulo
324e28a4053SRui Paulo /* Kc */
325780fb4a2SCy Schubert pos = str_token(buf, ":", &pos2);
326780fb4a2SCy Schubert if (!pos || os_strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) {
327780fb4a2SCy Schubert printf("%s:%d - Invalid Kc\n", fname, line);
328e28a4053SRui Paulo ret = -1;
329e28a4053SRui Paulo break;
330e28a4053SRui Paulo }
331e28a4053SRui Paulo
332e28a4053SRui Paulo /* SRES */
333780fb4a2SCy Schubert pos = str_token(buf, ":", &pos2);
334780fb4a2SCy Schubert if (!pos || os_strlen(pos) != 8 ||
335780fb4a2SCy Schubert hexstr2bin(pos, g->sres, 4)) {
336780fb4a2SCy Schubert printf("%s:%d - Invalid SRES\n", fname, line);
337e28a4053SRui Paulo ret = -1;
338e28a4053SRui Paulo break;
339e28a4053SRui Paulo }
340e28a4053SRui Paulo
341e28a4053SRui Paulo /* RAND */
342780fb4a2SCy Schubert pos = str_token(buf, ":", &pos2);
343780fb4a2SCy Schubert if (!pos || os_strlen(pos) != 32 ||
344780fb4a2SCy Schubert hexstr2bin(pos, g->_rand, 16)) {
345780fb4a2SCy Schubert printf("%s:%d - Invalid RAND\n", fname, line);
346e28a4053SRui Paulo ret = -1;
347e28a4053SRui Paulo break;
348e28a4053SRui Paulo }
349e28a4053SRui Paulo
350e28a4053SRui Paulo g->next = gsm_db;
351e28a4053SRui Paulo gsm_db = g;
352e28a4053SRui Paulo g = NULL;
353e28a4053SRui Paulo }
354f05cddf9SRui Paulo os_free(g);
355e28a4053SRui Paulo
356e28a4053SRui Paulo fclose(f);
357e28a4053SRui Paulo
358e28a4053SRui Paulo return ret;
359e28a4053SRui Paulo }
360e28a4053SRui Paulo
361e28a4053SRui Paulo
get_gsm_triplet(const char * imsi)362e28a4053SRui Paulo static struct gsm_triplet * get_gsm_triplet(const char *imsi)
363e28a4053SRui Paulo {
364e28a4053SRui Paulo struct gsm_triplet *g = gsm_db_pos;
365e28a4053SRui Paulo
366e28a4053SRui Paulo while (g) {
367e28a4053SRui Paulo if (strcmp(g->imsi, imsi) == 0) {
368e28a4053SRui Paulo gsm_db_pos = g->next;
369e28a4053SRui Paulo return g;
370e28a4053SRui Paulo }
371e28a4053SRui Paulo g = g->next;
372e28a4053SRui Paulo }
373e28a4053SRui Paulo
374e28a4053SRui Paulo g = gsm_db;
375e28a4053SRui Paulo while (g && g != gsm_db_pos) {
376e28a4053SRui Paulo if (strcmp(g->imsi, imsi) == 0) {
377e28a4053SRui Paulo gsm_db_pos = g->next;
378e28a4053SRui Paulo return g;
379e28a4053SRui Paulo }
380e28a4053SRui Paulo g = g->next;
381e28a4053SRui Paulo }
382e28a4053SRui Paulo
383e28a4053SRui Paulo return NULL;
384e28a4053SRui Paulo }
385e28a4053SRui Paulo
386e28a4053SRui Paulo
read_milenage(const char * fname)387e28a4053SRui Paulo static int read_milenage(const char *fname)
388e28a4053SRui Paulo {
389e28a4053SRui Paulo FILE *f;
390e28a4053SRui Paulo char buf[200], *pos, *pos2;
391e28a4053SRui Paulo struct milenage_parameters *m = NULL;
392e28a4053SRui Paulo int line, ret = 0;
393e28a4053SRui Paulo
394e28a4053SRui Paulo if (fname == NULL)
395e28a4053SRui Paulo return -1;
396e28a4053SRui Paulo
397e28a4053SRui Paulo f = fopen(fname, "r");
398e28a4053SRui Paulo if (f == NULL) {
399e28a4053SRui Paulo printf("Could not open Milenage data file '%s'\n", fname);
400e28a4053SRui Paulo return -1;
401e28a4053SRui Paulo }
402e28a4053SRui Paulo
403e28a4053SRui Paulo line = 0;
404e28a4053SRui Paulo while (fgets(buf, sizeof(buf), f)) {
405e28a4053SRui Paulo line++;
406e28a4053SRui Paulo
407325151a3SRui Paulo /* Parse IMSI Ki OPc AMF SQN [RES_len] */
408e28a4053SRui Paulo buf[sizeof(buf) - 1] = '\0';
409e28a4053SRui Paulo if (buf[0] == '#')
410e28a4053SRui Paulo continue;
411e28a4053SRui Paulo pos = buf;
412e28a4053SRui Paulo while (*pos != '\0' && *pos != '\n')
413e28a4053SRui Paulo pos++;
414e28a4053SRui Paulo if (*pos == '\n')
415e28a4053SRui Paulo *pos = '\0';
416e28a4053SRui Paulo pos = buf;
417e28a4053SRui Paulo if (*pos == '\0')
418e28a4053SRui Paulo continue;
419e28a4053SRui Paulo
420e28a4053SRui Paulo m = os_zalloc(sizeof(*m));
421e28a4053SRui Paulo if (m == NULL) {
422e28a4053SRui Paulo ret = -1;
423e28a4053SRui Paulo break;
424e28a4053SRui Paulo }
425e28a4053SRui Paulo
426e28a4053SRui Paulo /* IMSI */
427780fb4a2SCy Schubert pos2 = NULL;
428780fb4a2SCy Schubert pos = str_token(buf, " ", &pos2);
429780fb4a2SCy Schubert if (!pos || os_strlen(pos) >= sizeof(m->imsi)) {
430780fb4a2SCy Schubert printf("%s:%d - Invalid IMSI\n", fname, line);
431e28a4053SRui Paulo ret = -1;
432e28a4053SRui Paulo break;
433e28a4053SRui Paulo }
434e28a4053SRui Paulo os_strlcpy(m->imsi, pos, sizeof(m->imsi));
435e28a4053SRui Paulo
436e28a4053SRui Paulo /* Ki */
437780fb4a2SCy Schubert pos = str_token(buf, " ", &pos2);
438780fb4a2SCy Schubert if (!pos || os_strlen(pos) != 32 ||
439780fb4a2SCy Schubert hexstr2bin(pos, m->ki, 16)) {
440780fb4a2SCy Schubert printf("%s:%d - Invalid Ki\n", fname, line);
441e28a4053SRui Paulo ret = -1;
442e28a4053SRui Paulo break;
443e28a4053SRui Paulo }
444e28a4053SRui Paulo
445e28a4053SRui Paulo /* OPc */
446780fb4a2SCy Schubert pos = str_token(buf, " ", &pos2);
447780fb4a2SCy Schubert if (!pos || os_strlen(pos) != 32 ||
448780fb4a2SCy Schubert hexstr2bin(pos, m->opc, 16)) {
449780fb4a2SCy Schubert printf("%s:%d - Invalid OPc\n", fname, line);
450e28a4053SRui Paulo ret = -1;
451e28a4053SRui Paulo break;
452e28a4053SRui Paulo }
453e28a4053SRui Paulo
454e28a4053SRui Paulo /* AMF */
455780fb4a2SCy Schubert pos = str_token(buf, " ", &pos2);
456780fb4a2SCy Schubert if (!pos || os_strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) {
457780fb4a2SCy Schubert printf("%s:%d - Invalid AMF\n", fname, line);
458e28a4053SRui Paulo ret = -1;
459e28a4053SRui Paulo break;
460e28a4053SRui Paulo }
461e28a4053SRui Paulo
462e28a4053SRui Paulo /* SQN */
463780fb4a2SCy Schubert pos = str_token(buf, " ", &pos2);
464780fb4a2SCy Schubert if (!pos || os_strlen(pos) != 12 ||
465780fb4a2SCy Schubert hexstr2bin(pos, m->sqn, 6)) {
466780fb4a2SCy Schubert printf("%s:%d - Invalid SEQ\n", fname, line);
467e28a4053SRui Paulo ret = -1;
468e28a4053SRui Paulo break;
469e28a4053SRui Paulo }
470325151a3SRui Paulo
471780fb4a2SCy Schubert pos = str_token(buf, " ", &pos2);
472780fb4a2SCy Schubert if (pos) {
473325151a3SRui Paulo m->res_len = atoi(pos);
474325151a3SRui Paulo if (m->res_len &&
475325151a3SRui Paulo (m->res_len < EAP_AKA_RES_MIN_LEN ||
476325151a3SRui Paulo m->res_len > EAP_AKA_RES_MAX_LEN)) {
477780fb4a2SCy Schubert printf("%s:%d - Invalid RES_len\n",
478780fb4a2SCy Schubert fname, line);
479325151a3SRui Paulo ret = -1;
480325151a3SRui Paulo break;
481325151a3SRui Paulo }
482325151a3SRui Paulo }
483e28a4053SRui Paulo
484e28a4053SRui Paulo m->next = milenage_db;
485e28a4053SRui Paulo milenage_db = m;
486e28a4053SRui Paulo m = NULL;
487e28a4053SRui Paulo }
488f05cddf9SRui Paulo os_free(m);
489e28a4053SRui Paulo
490e28a4053SRui Paulo fclose(f);
491e28a4053SRui Paulo
492e28a4053SRui Paulo return ret;
493e28a4053SRui Paulo }
494e28a4053SRui Paulo
495e28a4053SRui Paulo
update_milenage_file(const char * fname)496f05cddf9SRui Paulo static void update_milenage_file(const char *fname)
497f05cddf9SRui Paulo {
498f05cddf9SRui Paulo FILE *f, *f2;
499325151a3SRui Paulo char name[500], buf[500], *pos;
500f05cddf9SRui Paulo char *end = buf + sizeof(buf);
501f05cddf9SRui Paulo struct milenage_parameters *m;
502f05cddf9SRui Paulo size_t imsi_len;
503f05cddf9SRui Paulo
504f05cddf9SRui Paulo f = fopen(fname, "r");
505f05cddf9SRui Paulo if (f == NULL) {
506f05cddf9SRui Paulo printf("Could not open Milenage data file '%s'\n", fname);
507f05cddf9SRui Paulo return;
508f05cddf9SRui Paulo }
509f05cddf9SRui Paulo
510325151a3SRui Paulo snprintf(name, sizeof(name), "%s.new", fname);
511325151a3SRui Paulo f2 = fopen(name, "w");
512f05cddf9SRui Paulo if (f2 == NULL) {
513325151a3SRui Paulo printf("Could not write Milenage data file '%s'\n", name);
514f05cddf9SRui Paulo fclose(f);
515f05cddf9SRui Paulo return;
516f05cddf9SRui Paulo }
517f05cddf9SRui Paulo
518f05cddf9SRui Paulo while (fgets(buf, sizeof(buf), f)) {
519f05cddf9SRui Paulo /* IMSI Ki OPc AMF SQN */
520f05cddf9SRui Paulo buf[sizeof(buf) - 1] = '\0';
521f05cddf9SRui Paulo
522f05cddf9SRui Paulo pos = strchr(buf, ' ');
523f05cddf9SRui Paulo if (buf[0] == '#' || pos == NULL || pos - buf >= 20)
524f05cddf9SRui Paulo goto no_update;
525f05cddf9SRui Paulo
526f05cddf9SRui Paulo imsi_len = pos - buf;
527f05cddf9SRui Paulo
528f05cddf9SRui Paulo for (m = milenage_db; m; m = m->next) {
529f05cddf9SRui Paulo if (strncmp(buf, m->imsi, imsi_len) == 0 &&
530f05cddf9SRui Paulo m->imsi[imsi_len] == '\0')
531f05cddf9SRui Paulo break;
532f05cddf9SRui Paulo }
533f05cddf9SRui Paulo
534f05cddf9SRui Paulo if (!m)
535f05cddf9SRui Paulo goto no_update;
536f05cddf9SRui Paulo
537f05cddf9SRui Paulo pos = buf;
538f05cddf9SRui Paulo pos += snprintf(pos, end - pos, "%s ", m->imsi);
539f05cddf9SRui Paulo pos += wpa_snprintf_hex(pos, end - pos, m->ki, 16);
540f05cddf9SRui Paulo *pos++ = ' ';
541f05cddf9SRui Paulo pos += wpa_snprintf_hex(pos, end - pos, m->opc, 16);
542f05cddf9SRui Paulo *pos++ = ' ';
543f05cddf9SRui Paulo pos += wpa_snprintf_hex(pos, end - pos, m->amf, 2);
544f05cddf9SRui Paulo *pos++ = ' ';
545f05cddf9SRui Paulo pos += wpa_snprintf_hex(pos, end - pos, m->sqn, 6);
546f05cddf9SRui Paulo *pos++ = '\n';
547f05cddf9SRui Paulo
548f05cddf9SRui Paulo no_update:
549f05cddf9SRui Paulo fprintf(f2, "%s", buf);
550f05cddf9SRui Paulo }
551f05cddf9SRui Paulo
552f05cddf9SRui Paulo fclose(f2);
553f05cddf9SRui Paulo fclose(f);
554f05cddf9SRui Paulo
555325151a3SRui Paulo snprintf(name, sizeof(name), "%s.bak", fname);
556325151a3SRui Paulo if (rename(fname, name) < 0) {
557f05cddf9SRui Paulo perror("rename");
558f05cddf9SRui Paulo return;
559f05cddf9SRui Paulo }
560f05cddf9SRui Paulo
561325151a3SRui Paulo snprintf(name, sizeof(name), "%s.new", fname);
562325151a3SRui Paulo if (rename(name, fname) < 0) {
563f05cddf9SRui Paulo perror("rename");
564f05cddf9SRui Paulo return;
565f05cddf9SRui Paulo }
566f05cddf9SRui Paulo
567f05cddf9SRui Paulo }
568f05cddf9SRui Paulo
569f05cddf9SRui Paulo
get_milenage(const char * imsi)570e28a4053SRui Paulo static struct milenage_parameters * get_milenage(const char *imsi)
571e28a4053SRui Paulo {
572e28a4053SRui Paulo struct milenage_parameters *m = milenage_db;
573e28a4053SRui Paulo
574e28a4053SRui Paulo while (m) {
575e28a4053SRui Paulo if (strcmp(m->imsi, imsi) == 0)
576e28a4053SRui Paulo break;
577e28a4053SRui Paulo m = m->next;
578e28a4053SRui Paulo }
579e28a4053SRui Paulo
580f05cddf9SRui Paulo #ifdef CONFIG_SQLITE
581f05cddf9SRui Paulo if (!m)
582f05cddf9SRui Paulo m = db_get_milenage(imsi);
583f05cddf9SRui Paulo #endif /* CONFIG_SQLITE */
584f05cddf9SRui Paulo
585e28a4053SRui Paulo return m;
586e28a4053SRui Paulo }
587e28a4053SRui Paulo
588e28a4053SRui Paulo
sim_req_auth(char * imsi,char * resp,size_t resp_len)5895b9c547cSRui Paulo static int sim_req_auth(char *imsi, char *resp, size_t resp_len)
590e28a4053SRui Paulo {
591e28a4053SRui Paulo int count, max_chal, ret;
592e28a4053SRui Paulo char *pos;
5935b9c547cSRui Paulo char *rpos, *rend;
594e28a4053SRui Paulo struct milenage_parameters *m;
595e28a4053SRui Paulo struct gsm_triplet *g;
596e28a4053SRui Paulo
5975b9c547cSRui Paulo resp[0] = '\0';
598e28a4053SRui Paulo
599e28a4053SRui Paulo pos = strchr(imsi, ' ');
600e28a4053SRui Paulo if (pos) {
601e28a4053SRui Paulo *pos++ = '\0';
602e28a4053SRui Paulo max_chal = atoi(pos);
6035b9c547cSRui Paulo if (max_chal < 1 || max_chal > EAP_SIM_MAX_CHAL)
604e28a4053SRui Paulo max_chal = EAP_SIM_MAX_CHAL;
605e28a4053SRui Paulo } else
606e28a4053SRui Paulo max_chal = EAP_SIM_MAX_CHAL;
607e28a4053SRui Paulo
6085b9c547cSRui Paulo rend = resp + resp_len;
6095b9c547cSRui Paulo rpos = resp;
610e28a4053SRui Paulo ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi);
611e28a4053SRui Paulo if (ret < 0 || ret >= rend - rpos)
6125b9c547cSRui Paulo return -1;
613e28a4053SRui Paulo rpos += ret;
614e28a4053SRui Paulo
615e28a4053SRui Paulo m = get_milenage(imsi);
616e28a4053SRui Paulo if (m) {
617e28a4053SRui Paulo u8 _rand[16], sres[4], kc[8];
618e28a4053SRui Paulo for (count = 0; count < max_chal; count++) {
619f05cddf9SRui Paulo if (random_get_bytes(_rand, 16) < 0)
6205b9c547cSRui Paulo return -1;
621e28a4053SRui Paulo gsm_milenage(m->opc, m->ki, _rand, sres, kc);
622e28a4053SRui Paulo *rpos++ = ' ';
623e28a4053SRui Paulo rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
624e28a4053SRui Paulo *rpos++ = ':';
625e28a4053SRui Paulo rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4);
626e28a4053SRui Paulo *rpos++ = ':';
627e28a4053SRui Paulo rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16);
628e28a4053SRui Paulo }
629e28a4053SRui Paulo *rpos = '\0';
6305b9c547cSRui Paulo return 0;
631e28a4053SRui Paulo }
632e28a4053SRui Paulo
633e28a4053SRui Paulo count = 0;
634e28a4053SRui Paulo while (count < max_chal && (g = get_gsm_triplet(imsi))) {
635e28a4053SRui Paulo if (strcmp(g->imsi, imsi) != 0)
636e28a4053SRui Paulo continue;
637e28a4053SRui Paulo
638e28a4053SRui Paulo if (rpos < rend)
639e28a4053SRui Paulo *rpos++ = ' ';
640e28a4053SRui Paulo rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8);
641e28a4053SRui Paulo if (rpos < rend)
642e28a4053SRui Paulo *rpos++ = ':';
643e28a4053SRui Paulo rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4);
644e28a4053SRui Paulo if (rpos < rend)
645e28a4053SRui Paulo *rpos++ = ':';
646e28a4053SRui Paulo rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16);
647e28a4053SRui Paulo count++;
648e28a4053SRui Paulo }
649e28a4053SRui Paulo
650e28a4053SRui Paulo if (count == 0) {
651e28a4053SRui Paulo printf("No GSM triplets found for %s\n", imsi);
652e28a4053SRui Paulo ret = snprintf(rpos, rend - rpos, " FAILURE");
653e28a4053SRui Paulo if (ret < 0 || ret >= rend - rpos)
6545b9c547cSRui Paulo return -1;
655e28a4053SRui Paulo rpos += ret;
656e28a4053SRui Paulo }
657e28a4053SRui Paulo
6585b9c547cSRui Paulo return 0;
6595b9c547cSRui Paulo }
6605b9c547cSRui Paulo
6615b9c547cSRui Paulo
gsm_auth_req(char * imsi,char * resp,size_t resp_len)6625b9c547cSRui Paulo static int gsm_auth_req(char *imsi, char *resp, size_t resp_len)
6635b9c547cSRui Paulo {
6645b9c547cSRui Paulo int count, ret;
6655b9c547cSRui Paulo char *pos, *rpos, *rend;
6665b9c547cSRui Paulo struct milenage_parameters *m;
6675b9c547cSRui Paulo
6685b9c547cSRui Paulo resp[0] = '\0';
6695b9c547cSRui Paulo
6705b9c547cSRui Paulo pos = os_strchr(imsi, ' ');
6715b9c547cSRui Paulo if (!pos)
6725b9c547cSRui Paulo return -1;
6735b9c547cSRui Paulo *pos++ = '\0';
6745b9c547cSRui Paulo
6755b9c547cSRui Paulo rend = resp + resp_len;
6765b9c547cSRui Paulo rpos = resp;
6775b9c547cSRui Paulo ret = os_snprintf(rpos, rend - rpos, "GSM-AUTH-RESP %s", imsi);
6785b9c547cSRui Paulo if (os_snprintf_error(rend - rpos, ret))
6795b9c547cSRui Paulo return -1;
6805b9c547cSRui Paulo rpos += ret;
6815b9c547cSRui Paulo
6825b9c547cSRui Paulo m = get_milenage(imsi);
6835b9c547cSRui Paulo if (m) {
6845b9c547cSRui Paulo u8 _rand[16], sres[4], kc[8];
6855b9c547cSRui Paulo for (count = 0; count < EAP_SIM_MAX_CHAL; count++) {
6865b9c547cSRui Paulo if (hexstr2bin(pos, _rand, 16) != 0)
6875b9c547cSRui Paulo return -1;
6885b9c547cSRui Paulo gsm_milenage(m->opc, m->ki, _rand, sres, kc);
6895b9c547cSRui Paulo *rpos++ = count == 0 ? ' ' : ':';
6905b9c547cSRui Paulo rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
6915b9c547cSRui Paulo *rpos++ = ':';
6925b9c547cSRui Paulo rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4);
6935b9c547cSRui Paulo pos += 16 * 2;
6945b9c547cSRui Paulo if (*pos != ':')
6955b9c547cSRui Paulo break;
6965b9c547cSRui Paulo pos++;
6975b9c547cSRui Paulo }
6985b9c547cSRui Paulo *rpos = '\0';
6995b9c547cSRui Paulo return 0;
7005b9c547cSRui Paulo }
7015b9c547cSRui Paulo
7025b9c547cSRui Paulo printf("No GSM triplets found for %s\n", imsi);
7035b9c547cSRui Paulo ret = os_snprintf(rpos, rend - rpos, " FAILURE");
7045b9c547cSRui Paulo if (os_snprintf_error(rend - rpos, ret))
7055b9c547cSRui Paulo return -1;
7065b9c547cSRui Paulo rpos += ret;
7075b9c547cSRui Paulo
7085b9c547cSRui Paulo return 0;
709e28a4053SRui Paulo }
710e28a4053SRui Paulo
711e28a4053SRui Paulo
inc_sqn(u8 * sqn)712f05cddf9SRui Paulo static void inc_sqn(u8 *sqn)
713f05cddf9SRui Paulo {
714f05cddf9SRui Paulo u64 val, seq, ind;
715f05cddf9SRui Paulo
716f05cddf9SRui Paulo /*
717f05cddf9SRui Paulo * SQN = SEQ | IND = SEQ1 | SEQ2 | IND
718f05cddf9SRui Paulo *
719f05cddf9SRui Paulo * The mechanism used here is not time-based, so SEQ2 is void and
720f05cddf9SRui Paulo * SQN = SEQ1 | IND. The length of IND is ind_len bits and the length
721f05cddf9SRui Paulo * of SEQ1 is 48 - ind_len bits.
722f05cddf9SRui Paulo */
723f05cddf9SRui Paulo
724f05cddf9SRui Paulo /* Increment both SEQ and IND by one */
725f05cddf9SRui Paulo val = ((u64) WPA_GET_BE32(sqn) << 16) | ((u64) WPA_GET_BE16(sqn + 4));
726f05cddf9SRui Paulo seq = (val >> ind_len) + 1;
727f05cddf9SRui Paulo ind = (val + 1) & ((1 << ind_len) - 1);
728f05cddf9SRui Paulo val = (seq << ind_len) | ind;
729f05cddf9SRui Paulo WPA_PUT_BE32(sqn, val >> 16);
730f05cddf9SRui Paulo WPA_PUT_BE16(sqn + 4, val & 0xffff);
731f05cddf9SRui Paulo }
732f05cddf9SRui Paulo
733f05cddf9SRui Paulo
aka_req_auth(char * imsi,char * resp,size_t resp_len)7345b9c547cSRui Paulo static int aka_req_auth(char *imsi, char *resp, size_t resp_len)
735e28a4053SRui Paulo {
736e28a4053SRui Paulo /* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */
7375b9c547cSRui Paulo char *pos, *end;
738e28a4053SRui Paulo u8 _rand[EAP_AKA_RAND_LEN];
739e28a4053SRui Paulo u8 autn[EAP_AKA_AUTN_LEN];
740e28a4053SRui Paulo u8 ik[EAP_AKA_IK_LEN];
741e28a4053SRui Paulo u8 ck[EAP_AKA_CK_LEN];
742e28a4053SRui Paulo u8 res[EAP_AKA_RES_MAX_LEN];
743e28a4053SRui Paulo size_t res_len;
744e28a4053SRui Paulo int ret;
745e28a4053SRui Paulo struct milenage_parameters *m;
746f05cddf9SRui Paulo int failed = 0;
747e28a4053SRui Paulo
748e28a4053SRui Paulo m = get_milenage(imsi);
749e28a4053SRui Paulo if (m) {
750f05cddf9SRui Paulo if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0)
7515b9c547cSRui Paulo return -1;
752e28a4053SRui Paulo res_len = EAP_AKA_RES_MAX_LEN;
753f05cddf9SRui Paulo inc_sqn(m->sqn);
754f05cddf9SRui Paulo #ifdef CONFIG_SQLITE
755f05cddf9SRui Paulo db_update_milenage_sqn(m);
756f05cddf9SRui Paulo #endif /* CONFIG_SQLITE */
757f05cddf9SRui Paulo sqn_changes = 1;
7585b9c547cSRui Paulo if (stdout_debug) {
759e28a4053SRui Paulo printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
760e28a4053SRui Paulo m->sqn[0], m->sqn[1], m->sqn[2],
761e28a4053SRui Paulo m->sqn[3], m->sqn[4], m->sqn[5]);
7625b9c547cSRui Paulo }
763e28a4053SRui Paulo milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand,
764e28a4053SRui Paulo autn, ik, ck, res, &res_len);
765325151a3SRui Paulo if (m->res_len >= EAP_AKA_RES_MIN_LEN &&
766325151a3SRui Paulo m->res_len <= EAP_AKA_RES_MAX_LEN &&
767325151a3SRui Paulo m->res_len < res_len)
768325151a3SRui Paulo res_len = m->res_len;
769e28a4053SRui Paulo } else {
770e28a4053SRui Paulo printf("Unknown IMSI: %s\n", imsi);
771e28a4053SRui Paulo #ifdef AKA_USE_FIXED_TEST_VALUES
772e28a4053SRui Paulo printf("Using fixed test values for AKA\n");
773e28a4053SRui Paulo memset(_rand, '0', EAP_AKA_RAND_LEN);
774e28a4053SRui Paulo memset(autn, '1', EAP_AKA_AUTN_LEN);
775e28a4053SRui Paulo memset(ik, '3', EAP_AKA_IK_LEN);
776e28a4053SRui Paulo memset(ck, '4', EAP_AKA_CK_LEN);
777e28a4053SRui Paulo memset(res, '2', EAP_AKA_RES_MAX_LEN);
778e28a4053SRui Paulo res_len = EAP_AKA_RES_MAX_LEN;
779e28a4053SRui Paulo #else /* AKA_USE_FIXED_TEST_VALUES */
780f05cddf9SRui Paulo failed = 1;
781e28a4053SRui Paulo #endif /* AKA_USE_FIXED_TEST_VALUES */
782e28a4053SRui Paulo }
783e28a4053SRui Paulo
7845b9c547cSRui Paulo pos = resp;
7855b9c547cSRui Paulo end = resp + resp_len;
786e28a4053SRui Paulo ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi);
787e28a4053SRui Paulo if (ret < 0 || ret >= end - pos)
7885b9c547cSRui Paulo return -1;
789e28a4053SRui Paulo pos += ret;
790f05cddf9SRui Paulo if (failed) {
791f05cddf9SRui Paulo ret = snprintf(pos, end - pos, "FAILURE");
792f05cddf9SRui Paulo if (ret < 0 || ret >= end - pos)
7935b9c547cSRui Paulo return -1;
794f05cddf9SRui Paulo pos += ret;
7955b9c547cSRui Paulo return 0;
796f05cddf9SRui Paulo }
797e28a4053SRui Paulo pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
798e28a4053SRui Paulo *pos++ = ' ';
799e28a4053SRui Paulo pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN);
800e28a4053SRui Paulo *pos++ = ' ';
801e28a4053SRui Paulo pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN);
802e28a4053SRui Paulo *pos++ = ' ';
803e28a4053SRui Paulo pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN);
804e28a4053SRui Paulo *pos++ = ' ';
805e28a4053SRui Paulo pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
806e28a4053SRui Paulo
8075b9c547cSRui Paulo return 0;
808e28a4053SRui Paulo }
809e28a4053SRui Paulo
810e28a4053SRui Paulo
aka_auts(char * imsi,char * resp,size_t resp_len)8115b9c547cSRui Paulo static int aka_auts(char *imsi, char *resp, size_t resp_len)
812e28a4053SRui Paulo {
813e28a4053SRui Paulo char *auts, *__rand;
814e28a4053SRui Paulo u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6];
815e28a4053SRui Paulo struct milenage_parameters *m;
816e28a4053SRui Paulo
8175b9c547cSRui Paulo resp[0] = '\0';
8185b9c547cSRui Paulo
819e28a4053SRui Paulo /* AKA-AUTS <IMSI> <AUTS> <RAND> */
820e28a4053SRui Paulo
821e28a4053SRui Paulo auts = strchr(imsi, ' ');
822e28a4053SRui Paulo if (auts == NULL)
8235b9c547cSRui Paulo return -1;
824e28a4053SRui Paulo *auts++ = '\0';
825e28a4053SRui Paulo
826e28a4053SRui Paulo __rand = strchr(auts, ' ');
827e28a4053SRui Paulo if (__rand == NULL)
8285b9c547cSRui Paulo return -1;
829e28a4053SRui Paulo *__rand++ = '\0';
830e28a4053SRui Paulo
8315b9c547cSRui Paulo if (stdout_debug) {
8325b9c547cSRui Paulo printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n",
8335b9c547cSRui Paulo imsi, auts, __rand);
8345b9c547cSRui Paulo }
835e28a4053SRui Paulo if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) ||
836e28a4053SRui Paulo hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) {
837e28a4053SRui Paulo printf("Could not parse AUTS/RAND\n");
8385b9c547cSRui Paulo return -1;
839e28a4053SRui Paulo }
840e28a4053SRui Paulo
841e28a4053SRui Paulo m = get_milenage(imsi);
842e28a4053SRui Paulo if (m == NULL) {
843e28a4053SRui Paulo printf("Unknown IMSI: %s\n", imsi);
8445b9c547cSRui Paulo return -1;
845e28a4053SRui Paulo }
846e28a4053SRui Paulo
847e28a4053SRui Paulo if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) {
848e28a4053SRui Paulo printf("AKA-AUTS: Incorrect MAC-S\n");
849e28a4053SRui Paulo } else {
850e28a4053SRui Paulo memcpy(m->sqn, sqn, 6);
8515b9c547cSRui Paulo if (stdout_debug) {
852e28a4053SRui Paulo printf("AKA-AUTS: Re-synchronized: "
853e28a4053SRui Paulo "SQN=%02x%02x%02x%02x%02x%02x\n",
854e28a4053SRui Paulo sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
8555b9c547cSRui Paulo }
856f05cddf9SRui Paulo #ifdef CONFIG_SQLITE
857f05cddf9SRui Paulo db_update_milenage_sqn(m);
858f05cddf9SRui Paulo #endif /* CONFIG_SQLITE */
859f05cddf9SRui Paulo sqn_changes = 1;
860e28a4053SRui Paulo }
8615b9c547cSRui Paulo
8625b9c547cSRui Paulo return 0;
8635b9c547cSRui Paulo }
8645b9c547cSRui Paulo
8655b9c547cSRui Paulo
process_cmd(char * cmd,char * resp,size_t resp_len)8665b9c547cSRui Paulo static int process_cmd(char *cmd, char *resp, size_t resp_len)
8675b9c547cSRui Paulo {
8685b9c547cSRui Paulo if (os_strncmp(cmd, "SIM-REQ-AUTH ", 13) == 0)
8695b9c547cSRui Paulo return sim_req_auth(cmd + 13, resp, resp_len);
8705b9c547cSRui Paulo
8715b9c547cSRui Paulo if (os_strncmp(cmd, "GSM-AUTH-REQ ", 13) == 0)
8725b9c547cSRui Paulo return gsm_auth_req(cmd + 13, resp, resp_len);
8735b9c547cSRui Paulo
8745b9c547cSRui Paulo if (os_strncmp(cmd, "AKA-REQ-AUTH ", 13) == 0)
8755b9c547cSRui Paulo return aka_req_auth(cmd + 13, resp, resp_len);
8765b9c547cSRui Paulo
8775b9c547cSRui Paulo if (os_strncmp(cmd, "AKA-AUTS ", 9) == 0)
8785b9c547cSRui Paulo return aka_auts(cmd + 9, resp, resp_len);
8795b9c547cSRui Paulo
8805b9c547cSRui Paulo printf("Unknown request: %s\n", cmd);
8815b9c547cSRui Paulo return -1;
882e28a4053SRui Paulo }
883e28a4053SRui Paulo
884e28a4053SRui Paulo
process(int s)885e28a4053SRui Paulo static int process(int s)
886e28a4053SRui Paulo {
8875b9c547cSRui Paulo char buf[1000], resp[1000];
888e28a4053SRui Paulo struct sockaddr_un from;
889e28a4053SRui Paulo socklen_t fromlen;
890e28a4053SRui Paulo ssize_t res;
891e28a4053SRui Paulo
892e28a4053SRui Paulo fromlen = sizeof(from);
893e28a4053SRui Paulo res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from,
894e28a4053SRui Paulo &fromlen);
895e28a4053SRui Paulo if (res < 0) {
896e28a4053SRui Paulo perror("recvfrom");
897e28a4053SRui Paulo return -1;
898e28a4053SRui Paulo }
899e28a4053SRui Paulo
900e28a4053SRui Paulo if (res == 0)
901e28a4053SRui Paulo return 0;
902e28a4053SRui Paulo
903e28a4053SRui Paulo if ((size_t) res >= sizeof(buf))
904e28a4053SRui Paulo res = sizeof(buf) - 1;
905e28a4053SRui Paulo buf[res] = '\0';
906e28a4053SRui Paulo
907e28a4053SRui Paulo printf("Received: %s\n", buf);
908e28a4053SRui Paulo
9095b9c547cSRui Paulo if (process_cmd(buf, resp, sizeof(resp)) < 0) {
9105b9c547cSRui Paulo printf("Failed to process request\n");
9115b9c547cSRui Paulo return -1;
9125b9c547cSRui Paulo }
9135b9c547cSRui Paulo
9145b9c547cSRui Paulo if (resp[0] == '\0') {
9155b9c547cSRui Paulo printf("No response\n");
9165b9c547cSRui Paulo return 0;
9175b9c547cSRui Paulo }
9185b9c547cSRui Paulo
9195b9c547cSRui Paulo printf("Send: %s\n", resp);
9205b9c547cSRui Paulo
9215b9c547cSRui Paulo if (sendto(s, resp, os_strlen(resp), 0, (struct sockaddr *) &from,
9225b9c547cSRui Paulo fromlen) < 0)
9235b9c547cSRui Paulo perror("send");
924e28a4053SRui Paulo
925e28a4053SRui Paulo return 0;
926e28a4053SRui Paulo }
927e28a4053SRui Paulo
928e28a4053SRui Paulo
cleanup(void)929e28a4053SRui Paulo static void cleanup(void)
930e28a4053SRui Paulo {
931e28a4053SRui Paulo struct gsm_triplet *g, *gprev;
932e28a4053SRui Paulo struct milenage_parameters *m, *prev;
933e28a4053SRui Paulo
934f05cddf9SRui Paulo if (update_milenage && milenage_file && sqn_changes)
935f05cddf9SRui Paulo update_milenage_file(milenage_file);
936f05cddf9SRui Paulo
937e28a4053SRui Paulo g = gsm_db;
938e28a4053SRui Paulo while (g) {
939e28a4053SRui Paulo gprev = g;
940e28a4053SRui Paulo g = g->next;
941f05cddf9SRui Paulo os_free(gprev);
942e28a4053SRui Paulo }
943e28a4053SRui Paulo
944e28a4053SRui Paulo m = milenage_db;
945e28a4053SRui Paulo while (m) {
946e28a4053SRui Paulo prev = m;
947e28a4053SRui Paulo m = m->next;
948f05cddf9SRui Paulo os_free(prev);
949e28a4053SRui Paulo }
950e28a4053SRui Paulo
9515b9c547cSRui Paulo if (serv_sock >= 0)
952e28a4053SRui Paulo close(serv_sock);
9535b9c547cSRui Paulo if (socket_path)
954e28a4053SRui Paulo unlink(socket_path);
955f05cddf9SRui Paulo
956f05cddf9SRui Paulo #ifdef CONFIG_SQLITE
957f05cddf9SRui Paulo if (sqlite_db) {
958f05cddf9SRui Paulo sqlite3_close(sqlite_db);
959f05cddf9SRui Paulo sqlite_db = NULL;
960f05cddf9SRui Paulo }
961f05cddf9SRui Paulo #endif /* CONFIG_SQLITE */
962e28a4053SRui Paulo }
963e28a4053SRui Paulo
964e28a4053SRui Paulo
handle_term(int sig)965e28a4053SRui Paulo static void handle_term(int sig)
966e28a4053SRui Paulo {
967e28a4053SRui Paulo printf("Signal %d - terminate\n", sig);
968e28a4053SRui Paulo exit(0);
969e28a4053SRui Paulo }
970e28a4053SRui Paulo
971e28a4053SRui Paulo
usage(void)972e28a4053SRui Paulo static void usage(void)
973e28a4053SRui Paulo {
974e28a4053SRui Paulo printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
975e28a4053SRui Paulo "database/authenticator\n"
976*85732ac8SCy Schubert "Copyright (c) 2005-2017, Jouni Malinen <j@w1.fi>\n"
977e28a4053SRui Paulo "\n"
978e28a4053SRui Paulo "usage:\n"
979f05cddf9SRui Paulo "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
980f05cddf9SRui Paulo "[-m<milenage file>] \\\n"
9815b9c547cSRui Paulo " [-D<DB file>] [-i<IND len in bits>] [command]\n"
982e28a4053SRui Paulo "\n"
983e28a4053SRui Paulo "options:\n"
984e28a4053SRui Paulo " -h = show this usage help\n"
985f05cddf9SRui Paulo " -u = update SQN in Milenage file on exit\n"
986e28a4053SRui Paulo " -s<socket path> = path for UNIX domain socket\n"
987e28a4053SRui Paulo " (default: %s)\n"
988e28a4053SRui Paulo " -g<triplet file> = path for GSM authentication triplets\n"
989f05cddf9SRui Paulo " -m<milenage file> = path for Milenage keys\n"
990f05cddf9SRui Paulo " -D<DB file> = path to SQLite database\n"
9915b9c547cSRui Paulo " -i<IND len in bits> = IND length for SQN (default: 5)\n"
9925b9c547cSRui Paulo "\n"
9935b9c547cSRui Paulo "If the optional command argument, like "
9945b9c547cSRui Paulo "\"AKA-REQ-AUTH <IMSI>\" is used, a single\n"
9955b9c547cSRui Paulo "command is processed with response sent to stdout. Otherwise, "
9965b9c547cSRui Paulo "hlr_auc_gw opens\n"
9975b9c547cSRui Paulo "a control interface and processes commands sent through it "
9985b9c547cSRui Paulo "(e.g., by EAP server\n"
9995b9c547cSRui Paulo "in hostapd).\n",
1000e28a4053SRui Paulo default_socket_path);
1001e28a4053SRui Paulo }
1002e28a4053SRui Paulo
1003e28a4053SRui Paulo
main(int argc,char * argv[])1004e28a4053SRui Paulo int main(int argc, char *argv[])
1005e28a4053SRui Paulo {
1006e28a4053SRui Paulo int c;
1007e28a4053SRui Paulo char *gsm_triplet_file = NULL;
1008f05cddf9SRui Paulo char *sqlite_db_file = NULL;
10095b9c547cSRui Paulo int ret = 0;
1010f05cddf9SRui Paulo
1011f05cddf9SRui Paulo if (os_program_init())
1012f05cddf9SRui Paulo return -1;
1013e28a4053SRui Paulo
1014e28a4053SRui Paulo socket_path = default_socket_path;
1015e28a4053SRui Paulo
1016e28a4053SRui Paulo for (;;) {
1017f05cddf9SRui Paulo c = getopt(argc, argv, "D:g:hi:m:s:u");
1018e28a4053SRui Paulo if (c < 0)
1019e28a4053SRui Paulo break;
1020e28a4053SRui Paulo switch (c) {
1021f05cddf9SRui Paulo case 'D':
1022f05cddf9SRui Paulo #ifdef CONFIG_SQLITE
1023f05cddf9SRui Paulo sqlite_db_file = optarg;
1024f05cddf9SRui Paulo break;
1025f05cddf9SRui Paulo #else /* CONFIG_SQLITE */
1026f05cddf9SRui Paulo printf("No SQLite support included in the build\n");
1027f05cddf9SRui Paulo return -1;
1028f05cddf9SRui Paulo #endif /* CONFIG_SQLITE */
1029e28a4053SRui Paulo case 'g':
1030e28a4053SRui Paulo gsm_triplet_file = optarg;
1031e28a4053SRui Paulo break;
1032e28a4053SRui Paulo case 'h':
1033e28a4053SRui Paulo usage();
1034e28a4053SRui Paulo return 0;
1035f05cddf9SRui Paulo case 'i':
1036f05cddf9SRui Paulo ind_len = atoi(optarg);
1037f05cddf9SRui Paulo if (ind_len < 0 || ind_len > 32) {
1038f05cddf9SRui Paulo printf("Invalid IND length\n");
1039f05cddf9SRui Paulo return -1;
1040f05cddf9SRui Paulo }
1041f05cddf9SRui Paulo break;
1042e28a4053SRui Paulo case 'm':
1043e28a4053SRui Paulo milenage_file = optarg;
1044e28a4053SRui Paulo break;
1045e28a4053SRui Paulo case 's':
1046e28a4053SRui Paulo socket_path = optarg;
1047e28a4053SRui Paulo break;
1048f05cddf9SRui Paulo case 'u':
1049f05cddf9SRui Paulo update_milenage = 1;
1050f05cddf9SRui Paulo break;
1051e28a4053SRui Paulo default:
1052e28a4053SRui Paulo usage();
1053e28a4053SRui Paulo return -1;
1054e28a4053SRui Paulo }
1055e28a4053SRui Paulo }
1056e28a4053SRui Paulo
1057f05cddf9SRui Paulo if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) {
1058f05cddf9SRui Paulo usage();
1059f05cddf9SRui Paulo return -1;
1060f05cddf9SRui Paulo }
1061f05cddf9SRui Paulo
1062f05cddf9SRui Paulo #ifdef CONFIG_SQLITE
1063f05cddf9SRui Paulo if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL)
1064f05cddf9SRui Paulo return -1;
1065f05cddf9SRui Paulo #endif /* CONFIG_SQLITE */
1066f05cddf9SRui Paulo
1067e28a4053SRui Paulo if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0)
1068e28a4053SRui Paulo return -1;
1069e28a4053SRui Paulo
1070e28a4053SRui Paulo if (milenage_file && read_milenage(milenage_file) < 0)
1071e28a4053SRui Paulo return -1;
1072e28a4053SRui Paulo
10735b9c547cSRui Paulo if (optind == argc) {
1074e28a4053SRui Paulo serv_sock = open_socket(socket_path);
1075e28a4053SRui Paulo if (serv_sock < 0)
1076e28a4053SRui Paulo return -1;
1077e28a4053SRui Paulo
1078e28a4053SRui Paulo printf("Listening for requests on %s\n", socket_path);
1079e28a4053SRui Paulo
1080e28a4053SRui Paulo atexit(cleanup);
1081e28a4053SRui Paulo signal(SIGTERM, handle_term);
1082e28a4053SRui Paulo signal(SIGINT, handle_term);
1083e28a4053SRui Paulo
1084e28a4053SRui Paulo for (;;)
1085e28a4053SRui Paulo process(serv_sock);
10865b9c547cSRui Paulo } else {
10875b9c547cSRui Paulo char buf[1000];
10885b9c547cSRui Paulo socket_path = NULL;
10895b9c547cSRui Paulo stdout_debug = 0;
10905b9c547cSRui Paulo if (process_cmd(argv[optind], buf, sizeof(buf)) < 0) {
10915b9c547cSRui Paulo printf("FAIL\n");
10925b9c547cSRui Paulo ret = -1;
10935b9c547cSRui Paulo } else {
10945b9c547cSRui Paulo printf("%s\n", buf);
10955b9c547cSRui Paulo }
10965b9c547cSRui Paulo cleanup();
10975b9c547cSRui Paulo }
1098e28a4053SRui Paulo
1099f05cddf9SRui Paulo #ifdef CONFIG_SQLITE
1100f05cddf9SRui Paulo if (sqlite_db) {
1101f05cddf9SRui Paulo sqlite3_close(sqlite_db);
1102f05cddf9SRui Paulo sqlite_db = NULL;
1103f05cddf9SRui Paulo }
1104f05cddf9SRui Paulo #endif /* CONFIG_SQLITE */
1105f05cddf9SRui Paulo
1106f05cddf9SRui Paulo os_program_deinit();
1107f05cddf9SRui Paulo
11085b9c547cSRui Paulo return ret;
1109e28a4053SRui Paulo }
1110