]> git.ipfire.org Git - thirdparty/hostap.git/blame - src/eap_server/eap_sim_db.c
EAP-SIM server: Require SIM/Start response to include identity
[thirdparty/hostap.git] / src / eap_server / eap_sim_db.c
CommitLineData
6fc6879b
JM
1/*
2 * hostapd / EAP-SIM database/authenticator gateway
762e4ce6 3 * Copyright (c) 2005-2010, 2012, Jouni Malinen <j@w1.fi>
6fc6879b 4 *
0f3d578e
JM
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
6fc6879b
JM
7 *
8 * This is an example implementation of the EAP-SIM/AKA database/authentication
9 * gateway interface that is using an external program as an SS7 gateway to
10 * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example
11 * implementation of such a gateway program. This eap_sim_db.c takes care of
12 * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different
13 * gateway implementations for HLR/AuC access. Alternatively, it can also be
14 * completely replaced if the in-memory database of pseudonyms/re-auth
15 * identities is not suitable for some cases.
16 */
17
18#include "includes.h"
19#include <sys/un.h>
66979bb8
JM
20#ifdef CONFIG_SQLITE
21#include <sqlite3.h>
22#endif /* CONFIG_SQLITE */
6fc6879b
JM
23
24#include "common.h"
3642c431 25#include "crypto/random.h"
6fc6879b
JM
26#include "eap_common/eap_sim_common.h"
27#include "eap_server/eap_sim_db.h"
28#include "eloop.h"
29
30struct eap_sim_pseudonym {
31 struct eap_sim_pseudonym *next;
32 u8 *identity;
33 size_t identity_len;
34 char *pseudonym;
35};
36
37struct eap_sim_db_pending {
38 struct eap_sim_db_pending *next;
39 u8 imsi[20];
40 size_t imsi_len;
41 enum { PENDING, SUCCESS, FAILURE } state;
42 void *cb_session_ctx;
43 struct os_time timestamp;
44 int aka;
45 union {
46 struct {
47 u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
48 u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
49 u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
50 int num_chal;
51 } sim;
52 struct {
53 u8 rand[EAP_AKA_RAND_LEN];
54 u8 autn[EAP_AKA_AUTN_LEN];
55 u8 ik[EAP_AKA_IK_LEN];
56 u8 ck[EAP_AKA_CK_LEN];
57 u8 res[EAP_AKA_RES_MAX_LEN];
58 size_t res_len;
59 } aka;
60 } u;
61};
62
63struct eap_sim_db_data {
64 int sock;
65 char *fname;
66 char *local_sock;
67 void (*get_complete_cb)(void *ctx, void *session_ctx);
68 void *ctx;
69 struct eap_sim_pseudonym *pseudonyms;
70 struct eap_sim_reauth *reauths;
71 struct eap_sim_db_pending *pending;
66979bb8
JM
72#ifdef CONFIG_SQLITE
73 sqlite3 *sqlite_db;
74 u8 db_tmp_identity[100];
75 char db_tmp_pseudonym_str[100];
76 struct eap_sim_pseudonym db_tmp_pseudonym;
29813cfd 77 struct eap_sim_reauth db_tmp_reauth;
66979bb8 78#endif /* CONFIG_SQLITE */
6fc6879b
JM
79};
80
81
66979bb8
JM
82#ifdef CONFIG_SQLITE
83
84static int db_table_exists(sqlite3 *db, const char *name)
85{
86 char cmd[128];
87 os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
88 return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
89}
90
91
92static int db_table_create_pseudonym(sqlite3 *db)
93{
94 char *err = NULL;
95 const char *sql =
96 "CREATE TABLE pseudonyms("
97 " imsi INTEGER PRIMARY KEY NOT NULL,"
98 " pseudonym CHAR(21) NOT NULL"
99 ");";
100
101 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
102 "pseudonym information");
103 if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
104 wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
105 sqlite3_free(err);
106 return -1;
107 }
108
109 return 0;
110}
111
112
29813cfd
JM
113static int db_table_create_reauth(sqlite3 *db)
114{
115 char *err = NULL;
116 const char *sql =
117 "CREATE TABLE reauth("
118 " imsi INTEGER PRIMARY KEY NOT NULL,"
119 " reauth_id CHAR(21) NOT NULL,"
120 " counter INTEGER,"
29813cfd
JM
121 " mk CHAR(40),"
122 " k_encr CHAR(32),"
123 " k_aut CHAR(64),"
124 " k_re CHAR(64)"
125 ");";
126
127 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
128 "reauth information");
129 if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
130 wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
131 sqlite3_free(err);
132 return -1;
133 }
134
135 return 0;
136}
137
138
66979bb8
JM
139static sqlite3 * db_open(const char *db_file)
140{
141 sqlite3 *db;
142
143 if (sqlite3_open(db_file, &db)) {
144 wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database "
145 "%s: %s", db_file, sqlite3_errmsg(db));
146 sqlite3_close(db);
147 return NULL;
148 }
149
150 if (!db_table_exists(db, "pseudonyms") &&
151 db_table_create_pseudonym(db) < 0) {
152 sqlite3_close(db);
153 return NULL;
154 }
155
29813cfd
JM
156 if (!db_table_exists(db, "reauth") &&
157 db_table_create_reauth(db) < 0) {
158 sqlite3_close(db);
159 return NULL;
160 }
161
66979bb8
JM
162 return db;
163}
164
165
166static int valid_pseudonym_string(const char *pseudonym)
167{
168 const char *pos = pseudonym;
169 while (*pos) {
170 if ((*pos < '0' || *pos > '9') &&
171 (*pos < 'a' || *pos > 'f'))
172 return 0;
173 pos++;
174 }
175 return 1;
176}
177
178
179static int db_add_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
180 size_t identity_len, char *pseudonym)
181{
182 char cmd[128];
183 unsigned long long imsi;
29813cfd 184 char *err = NULL;
66979bb8
JM
185
186 if (!valid_pseudonym_string(pseudonym) || identity_len >= sizeof(cmd))
187 {
188 os_free(pseudonym);
189 return -1;
190 }
191 os_memcpy(cmd, identity, identity_len);
192 cmd[identity_len] = '\0';
193 imsi = atoll(cmd);
194
195 os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms "
196 "(imsi, pseudonym) VALUES (%llu , '%s');",
197 imsi, pseudonym);
198 os_free(pseudonym);
29813cfd
JM
199 if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
200 {
201 wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
202 sqlite3_free(err);
66979bb8 203 return -1;
29813cfd 204 }
66979bb8
JM
205
206 return 0;
207}
208
209
210static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[])
211{
212 struct eap_sim_db_data *data = ctx;
213 int i;
214 size_t len;
215
216 for (i = 0; i < argc; i++) {
217 if (os_strcmp(col[i], "imsi") == 0 && argv[i]) {
218 len = os_strlen(argv[i]);
219 if (len > sizeof(data->db_tmp_identity))
220 continue;
221 os_memcpy(data->db_tmp_identity, argv[i], len);
222 data->db_tmp_pseudonym.identity =
223 data->db_tmp_identity;
224 data->db_tmp_pseudonym.identity_len = len;
225 } else if (os_strcmp(col[i], "pseudonym") == 0 && argv[i]) {
226 len = os_strlen(argv[i]);
227 if (len >= sizeof(data->db_tmp_pseudonym_str))
228 continue;
229 os_memcpy(data->db_tmp_pseudonym_str, argv[i], len);
230 data->db_tmp_pseudonym_str[len] = '\0';
231 data->db_tmp_pseudonym.pseudonym =
232 data->db_tmp_pseudonym_str;
233 }
234 }
235
236 return 0;
237}
238
239
240static struct eap_sim_pseudonym *
241db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym)
242{
243 char cmd[128];
244
245 if (!valid_pseudonym_string(pseudonym))
246 return NULL;
247 os_memset(&data->db_tmp_pseudonym, 0, sizeof(data->db_tmp_pseudonym));
248 os_strlcpy(data->db_tmp_pseudonym_str, pseudonym,
249 sizeof(data->db_tmp_pseudonym_str));
250 data->db_tmp_pseudonym.pseudonym = data->db_tmp_pseudonym_str;
251 os_snprintf(cmd, sizeof(cmd),
252 "SELECT imsi FROM pseudonyms WHERE pseudonym='%s';",
253 pseudonym);
254 if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) !=
255 SQLITE_OK)
256 return NULL;
257 if (data->db_tmp_pseudonym.identity == NULL)
258 return NULL;
259 return &data->db_tmp_pseudonym;
260}
261
262
263static struct eap_sim_pseudonym *
264db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
265 size_t identity_len)
266{
267 char cmd[128];
268 unsigned long long imsi;
269
270 if (identity_len >= sizeof(cmd))
271 return NULL;
272 os_memcpy(cmd, identity, identity_len);
273 cmd[identity_len] = '\0';
274 imsi = atoll(cmd);
275
276 os_memset(&data->db_tmp_pseudonym, 0, sizeof(data->db_tmp_pseudonym));
277 if (identity_len > sizeof(data->db_tmp_identity))
278 return NULL;
279 os_memcpy(data->db_tmp_identity, identity, identity_len);
280 data->db_tmp_pseudonym.identity = data->db_tmp_identity;
281 data->db_tmp_pseudonym.identity_len = identity_len;
282 os_snprintf(cmd, sizeof(cmd),
283 "SELECT pseudonym FROM pseudonyms WHERE imsi=%llu;", imsi);
284 if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) !=
285 SQLITE_OK)
286 return NULL;
287 if (data->db_tmp_pseudonym.pseudonym == NULL)
288 return NULL;
289 return &data->db_tmp_pseudonym;
290}
291
29813cfd
JM
292
293static int db_add_reauth(struct eap_sim_db_data *data, const u8 *identity,
294 size_t identity_len, char *reauth_id, u16 counter,
e87982ea 295 const u8 *mk, const u8 *k_encr,
29813cfd
JM
296 const u8 *k_aut, const u8 *k_re)
297{
298 char cmd[2000], *pos, *end;
299 unsigned long long imsi;
300 char *err = NULL;
301
302 if (!valid_pseudonym_string(reauth_id) || identity_len >= sizeof(cmd))
303 {
304 os_free(reauth_id);
305 return -1;
306 }
307 os_memcpy(cmd, identity, identity_len);
308 cmd[identity_len] = '\0';
309 imsi = atoll(cmd);
310
311 pos = cmd;
312 end = pos + sizeof(cmd);
313 pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth "
e87982ea
JM
314 "(imsi, reauth_id, counter%s%s%s%s) "
315 "VALUES (%llu, '%s', %u",
29813cfd
JM
316 mk ? ", mk" : "",
317 k_encr ? ", k_encr" : "",
318 k_aut ? ", k_aut" : "",
319 k_re ? ", k_re" : "",
e87982ea 320 imsi, reauth_id, counter);
29813cfd
JM
321 os_free(reauth_id);
322
323 if (mk) {
324 pos += os_snprintf(pos, end - pos, ", '");
325 pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN);
326 pos += os_snprintf(pos, end - pos, "'");
327 }
328
329 if (k_encr) {
330 pos += os_snprintf(pos, end - pos, ", '");
331 pos += wpa_snprintf_hex(pos, end - pos, k_encr,
332 EAP_SIM_K_ENCR_LEN);
333 pos += os_snprintf(pos, end - pos, "'");
334 }
335
336 if (k_aut) {
337 pos += os_snprintf(pos, end - pos, ", '");
338 pos += wpa_snprintf_hex(pos, end - pos, k_aut,
339 EAP_AKA_PRIME_K_AUT_LEN);
340 pos += os_snprintf(pos, end - pos, "'");
341 }
342
343 if (k_re) {
344 pos += os_snprintf(pos, end - pos, ", '");
345 pos += wpa_snprintf_hex(pos, end - pos, k_re,
346 EAP_AKA_PRIME_K_RE_LEN);
347 pos += os_snprintf(pos, end - pos, "'");
348 }
349
350 os_snprintf(pos, end - pos, ");");
351
352 if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
353 {
354 wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
355 sqlite3_free(err);
356 return -1;
357 }
358
359 return 0;
360}
361
362
363static int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[])
364{
365 struct eap_sim_db_data *data = ctx;
366 int i;
367 size_t len;
368 struct eap_sim_reauth *reauth = &data->db_tmp_reauth;
369
370 for (i = 0; i < argc; i++) {
371 if (os_strcmp(col[i], "imsi") == 0 && argv[i]) {
372 len = os_strlen(argv[i]);
373 if (len > sizeof(data->db_tmp_identity))
374 continue;
375 os_memcpy(data->db_tmp_identity, argv[i], len);
376 reauth->identity = data->db_tmp_identity;
377 reauth->identity_len = len;
378 } else if (os_strcmp(col[i], "reauth_id") == 0 && argv[i]) {
379 len = os_strlen(argv[i]);
380 if (len >= sizeof(data->db_tmp_pseudonym_str))
381 continue;
382 os_memcpy(data->db_tmp_pseudonym_str, argv[i], len);
383 data->db_tmp_pseudonym_str[len] = '\0';
384 reauth->reauth_id = data->db_tmp_pseudonym_str;
385 } else if (os_strcmp(col[i], "counter") == 0 && argv[i]) {
386 reauth->counter = atoi(argv[i]);
29813cfd
JM
387 } else if (os_strcmp(col[i], "mk") == 0 && argv[i]) {
388 hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk));
389 } else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) {
390 hexstr2bin(argv[i], reauth->k_encr,
391 sizeof(reauth->k_encr));
392 } else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) {
393 hexstr2bin(argv[i], reauth->k_aut,
394 sizeof(reauth->k_aut));
395 } else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) {
396 hexstr2bin(argv[i], reauth->k_re,
397 sizeof(reauth->k_re));
398 }
399 }
400
401 return 0;
402}
403
404
405static struct eap_sim_reauth *
406db_get_reauth(struct eap_sim_db_data *data, const char *reauth_id)
407{
408 char cmd[256];
409
410 if (!valid_pseudonym_string(reauth_id))
411 return NULL;
412 os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth));
413 os_strlcpy(data->db_tmp_pseudonym_str, reauth_id,
414 sizeof(data->db_tmp_pseudonym_str));
415 data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str;
416 os_snprintf(cmd, sizeof(cmd),
417 "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id);
418 if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) !=
419 SQLITE_OK)
420 return NULL;
421 if (data->db_tmp_reauth.identity == NULL)
422 return NULL;
423 return &data->db_tmp_reauth;
424}
425
426
427static struct eap_sim_reauth *
428db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
429 size_t identity_len)
430{
431 char cmd[256];
432 unsigned long long imsi;
433
434 if (identity_len >= sizeof(cmd))
435 return NULL;
436 os_memcpy(cmd, identity, identity_len);
437 cmd[identity_len] = '\0';
438 imsi = atoll(cmd);
439
440 os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth));
441 if (identity_len > sizeof(data->db_tmp_identity))
442 return NULL;
443 os_memcpy(data->db_tmp_identity, identity, identity_len);
444 data->db_tmp_reauth.identity = data->db_tmp_identity;
445 data->db_tmp_reauth.identity_len = identity_len;
446 os_snprintf(cmd, sizeof(cmd),
447 "SELECT * FROM reauth WHERE imsi=%llu;", imsi);
448 if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) !=
449 SQLITE_OK)
450 return NULL;
451 if (data->db_tmp_reauth.reauth_id == NULL)
452 return NULL;
453 return &data->db_tmp_reauth;
454}
455
456
457static void db_remove_reauth(struct eap_sim_db_data *data,
458 struct eap_sim_reauth *reauth)
459{
460 char cmd[256];
461 unsigned long long imsi;
462
463 if (reauth->identity_len >= sizeof(cmd))
464 return;
465 os_memcpy(cmd, reauth->identity, reauth->identity_len);
466 cmd[reauth->identity_len] = '\0';
467 imsi = atoll(cmd);
468 os_snprintf(cmd, sizeof(cmd),
469 "DELETE FROM reauth WHERE imsi=%llu;", imsi);
470 sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL);
471}
472
66979bb8
JM
473#endif /* CONFIG_SQLITE */
474
29813cfd 475
6fc6879b
JM
476static struct eap_sim_db_pending *
477eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi,
478 size_t imsi_len, int aka)
479{
480 struct eap_sim_db_pending *entry, *prev = NULL;
481
482 entry = data->pending;
483 while (entry) {
484 if (entry->aka == aka && entry->imsi_len == imsi_len &&
485 os_memcmp(entry->imsi, imsi, imsi_len) == 0) {
486 if (prev)
487 prev->next = entry->next;
488 else
489 data->pending = entry->next;
490 break;
491 }
492 prev = entry;
493 entry = entry->next;
494 }
495 return entry;
496}
497
498
499static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
500 struct eap_sim_db_pending *entry)
501{
502 entry->next = data->pending;
503 data->pending = entry;
504}
505
506
507static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
508 const char *imsi, char *buf)
509{
510 char *start, *end, *pos;
511 struct eap_sim_db_pending *entry;
512 int num_chal;
513
514 /*
515 * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ...
516 * SIM-RESP-AUTH <IMSI> FAILURE
517 * (IMSI = ASCII string, Kc/SRES/RAND = hex string)
518 */
519
520 entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0);
521 if (entry == NULL) {
522 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
523 "received message found");
524 return;
525 }
526
527 start = buf;
528 if (os_strncmp(start, "FAILURE", 7) == 0) {
529 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
530 "failure");
531 entry->state = FAILURE;
532 eap_sim_db_add_pending(data, entry);
533 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
534 return;
535 }
536
537 num_chal = 0;
538 while (num_chal < EAP_SIM_MAX_CHAL) {
539 end = os_strchr(start, ' ');
540 if (end)
541 *end = '\0';
542
543 pos = os_strchr(start, ':');
544 if (pos == NULL)
545 goto parse_fail;
546 *pos = '\0';
547 if (hexstr2bin(start, entry->u.sim.kc[num_chal],
548 EAP_SIM_KC_LEN))
549 goto parse_fail;
550
551 start = pos + 1;
552 pos = os_strchr(start, ':');
553 if (pos == NULL)
554 goto parse_fail;
555 *pos = '\0';
556 if (hexstr2bin(start, entry->u.sim.sres[num_chal],
557 EAP_SIM_SRES_LEN))
558 goto parse_fail;
559
560 start = pos + 1;
561 if (hexstr2bin(start, entry->u.sim.rand[num_chal],
562 GSM_RAND_LEN))
563 goto parse_fail;
564
565 num_chal++;
566 if (end == NULL)
567 break;
568 else
569 start = end + 1;
570 }
571 entry->u.sim.num_chal = num_chal;
572
573 entry->state = SUCCESS;
574 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
575 "successfully - callback");
576 eap_sim_db_add_pending(data, entry);
577 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
578 return;
579
580parse_fail:
581 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
582 os_free(entry);
583}
584
585
586static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
587 const char *imsi, char *buf)
588{
589 char *start, *end;
590 struct eap_sim_db_pending *entry;
591
592 /*
593 * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
594 * AKA-RESP-AUTH <IMSI> FAILURE
595 * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string)
596 */
597
598 entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1);
599 if (entry == NULL) {
600 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
601 "received message found");
602 return;
603 }
604
605 start = buf;
606 if (os_strncmp(start, "FAILURE", 7) == 0) {
607 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
608 "failure");
609 entry->state = FAILURE;
610 eap_sim_db_add_pending(data, entry);
611 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
612 return;
613 }
614
615 end = os_strchr(start, ' ');
616 if (end == NULL)
617 goto parse_fail;
618 *end = '\0';
619 if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN))
620 goto parse_fail;
621
622 start = end + 1;
623 end = os_strchr(start, ' ');
624 if (end == NULL)
625 goto parse_fail;
626 *end = '\0';
627 if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN))
628 goto parse_fail;
629
630 start = end + 1;
631 end = os_strchr(start, ' ');
632 if (end == NULL)
633 goto parse_fail;
634 *end = '\0';
635 if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN))
636 goto parse_fail;
637
638 start = end + 1;
639 end = os_strchr(start, ' ');
640 if (end == NULL)
641 goto parse_fail;
642 *end = '\0';
643 if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN))
644 goto parse_fail;
645
646 start = end + 1;
647 end = os_strchr(start, ' ');
648 if (end)
649 *end = '\0';
650 else {
651 end = start;
652 while (*end)
653 end++;
654 }
655 entry->u.aka.res_len = (end - start) / 2;
656 if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) {
657 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES");
658 entry->u.aka.res_len = 0;
659 goto parse_fail;
660 }
661 if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
662 goto parse_fail;
663
664 entry->state = SUCCESS;
665 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
666 "successfully - callback");
667 eap_sim_db_add_pending(data, entry);
668 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
669 return;
670
671parse_fail:
672 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
673 os_free(entry);
674}
675
676
677static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
678{
679 struct eap_sim_db_data *data = eloop_ctx;
680 char buf[1000], *pos, *cmd, *imsi;
681 int res;
682
683 res = recv(sock, buf, sizeof(buf), 0);
684 if (res < 0)
685 return;
686 wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
687 "external source", (u8 *) buf, res);
688 if (res == 0)
689 return;
690 if (res >= (int) sizeof(buf))
691 res = sizeof(buf) - 1;
692 buf[res] = '\0';
693
694 if (data->get_complete_cb == NULL) {
695 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
696 "registered");
697 return;
698 }
699
700 /* <cmd> <IMSI> ... */
701
702 cmd = buf;
703 pos = os_strchr(cmd, ' ');
704 if (pos == NULL)
705 goto parse_fail;
706 *pos = '\0';
707 imsi = pos + 1;
708 pos = os_strchr(imsi, ' ');
709 if (pos == NULL)
710 goto parse_fail;
711 *pos = '\0';
712 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s",
713 cmd, imsi);
714
715 if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0)
716 eap_sim_db_sim_resp_auth(data, imsi, pos + 1);
717 else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0)
718 eap_sim_db_aka_resp_auth(data, imsi, pos + 1);
719 else
720 wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response "
721 "'%s'", cmd);
722 return;
723
724parse_fail:
725 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
726}
727
728
729static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
730{
731 struct sockaddr_un addr;
732 static int counter = 0;
733
734 if (os_strncmp(data->fname, "unix:", 5) != 0)
735 return -1;
736
737 data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
738 if (data->sock < 0) {
739 perror("socket(eap_sim_db)");
740 return -1;
741 }
742
743 os_memset(&addr, 0, sizeof(addr));
744 addr.sun_family = AF_UNIX;
745 os_snprintf(addr.sun_path, sizeof(addr.sun_path),
746 "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
c13f0a3e 747 os_free(data->local_sock);
6fc6879b
JM
748 data->local_sock = os_strdup(addr.sun_path);
749 if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
750 perror("bind(eap_sim_db)");
751 close(data->sock);
752 data->sock = -1;
753 return -1;
754 }
755
756 os_memset(&addr, 0, sizeof(addr));
757 addr.sun_family = AF_UNIX;
758 os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path));
759 if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
760 perror("connect(eap_sim_db)");
761 wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket",
762 (u8 *) addr.sun_path,
763 os_strlen(addr.sun_path));
764 close(data->sock);
765 data->sock = -1;
766 return -1;
767 }
768
769 eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
770
771 return 0;
772}
773
774
775static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
776{
777 if (data->sock >= 0) {
778 eloop_unregister_read_sock(data->sock);
779 close(data->sock);
780 data->sock = -1;
781 }
782 if (data->local_sock) {
783 unlink(data->local_sock);
784 os_free(data->local_sock);
785 data->local_sock = NULL;
786 }
787}
788
789
790/**
791 * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
792 * @config: Configuration data (e.g., file name)
793 * @get_complete_cb: Callback function for reporting availability of triplets
794 * @ctx: Context pointer for get_complete_cb
795 * Returns: Pointer to a private data structure or %NULL on failure
796 */
797void * eap_sim_db_init(const char *config,
798 void (*get_complete_cb)(void *ctx, void *session_ctx),
799 void *ctx)
800{
801 struct eap_sim_db_data *data;
66979bb8 802 char *pos;
6fc6879b
JM
803
804 data = os_zalloc(sizeof(*data));
805 if (data == NULL)
806 return NULL;
807
808 data->sock = -1;
809 data->get_complete_cb = get_complete_cb;
810 data->ctx = ctx;
811 data->fname = os_strdup(config);
812 if (data->fname == NULL)
813 goto fail;
66979bb8
JM
814 pos = os_strstr(data->fname, " db=");
815 if (pos) {
816 *pos = '\0';
817#ifdef CONFIG_SQLITE
818 pos += 4;
819 data->sqlite_db = db_open(pos);
820 if (data->sqlite_db == NULL)
821 goto fail;
822#endif /* CONFIG_SQLITE */
823 }
6fc6879b
JM
824
825 if (os_strncmp(data->fname, "unix:", 5) == 0) {
704b8762
JM
826 if (eap_sim_db_open_socket(data)) {
827 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database "
828 "connection not available - will retry "
829 "later");
830 }
6fc6879b
JM
831 }
832
833 return data;
834
835fail:
836 eap_sim_db_close_socket(data);
837 os_free(data->fname);
838 os_free(data);
839 return NULL;
840}
841
842
843static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
844{
845 os_free(p->identity);
846 os_free(p->pseudonym);
847 os_free(p);
848}
849
850
851static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
852{
853 os_free(r->identity);
854 os_free(r->reauth_id);
855 os_free(r);
856}
857
858
859/**
860 * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
861 * @priv: Private data pointer from eap_sim_db_init()
862 */
863void eap_sim_db_deinit(void *priv)
864{
865 struct eap_sim_db_data *data = priv;
866 struct eap_sim_pseudonym *p, *prev;
867 struct eap_sim_reauth *r, *prevr;
868 struct eap_sim_db_pending *pending, *prev_pending;
869
66979bb8
JM
870#ifdef CONFIG_SQLITE
871 if (data->sqlite_db) {
872 sqlite3_close(data->sqlite_db);
873 data->sqlite_db = NULL;
874 }
875#endif /* CONFIG_SQLITE */
876
6fc6879b
JM
877 eap_sim_db_close_socket(data);
878 os_free(data->fname);
879
880 p = data->pseudonyms;
881 while (p) {
882 prev = p;
883 p = p->next;
884 eap_sim_db_free_pseudonym(prev);
885 }
886
887 r = data->reauths;
888 while (r) {
889 prevr = r;
890 r = r->next;
891 eap_sim_db_free_reauth(prevr);
892 }
893
894 pending = data->pending;
895 while (pending) {
896 prev_pending = pending;
897 pending = pending->next;
898 os_free(prev_pending);
899 }
900
901 os_free(data);
902}
903
904
905static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
906 size_t len)
907{
908 int _errno = 0;
909
910 if (send(data->sock, msg, len, 0) < 0) {
911 _errno = errno;
912 perror("send[EAP-SIM DB UNIX]");
913 }
914
915 if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
916 _errno == ECONNREFUSED) {
917 /* Try to reconnect */
918 eap_sim_db_close_socket(data);
919 if (eap_sim_db_open_socket(data) < 0)
920 return -1;
921 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
922 "external server");
923 if (send(data->sock, msg, len, 0) < 0) {
924 perror("send[EAP-SIM DB UNIX]");
925 return -1;
926 }
927 }
928
929 return 0;
930}
931
932
933static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
934{
935 /* TODO: add limit for maximum length for pending list; remove latest
936 * (i.e., last) entry from the list if the limit is reached; could also
937 * use timeout to expire pending entries */
938}
939
940
941/**
942 * eap_sim_db_get_gsm_triplets - Get GSM triplets
943 * @priv: Private data pointer from eap_sim_db_init()
944 * @identity: User name identity
945 * @identity_len: Length of identity in bytes
946 * @max_chal: Maximum number of triplets
947 * @_rand: Buffer for RAND values
948 * @kc: Buffer for Kc values
949 * @sres: Buffer for SRES values
950 * @cb_session_ctx: Session callback context for get_complete_cb()
951 * Returns: Number of triplets received (has to be less than or equal to
952 * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
953 * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
954 * callback function registered with eap_sim_db_init() will be called once the
955 * results become available.
956 *
957 * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
958 * ASCII format.
959 *
960 * When using an external server for GSM triplets, this function can always
961 * start a request and return EAP_SIM_DB_PENDING immediately if authentication
962 * triplets are not available. Once the triplets are received, callback
963 * function registered with eap_sim_db_init() is called to notify EAP state
964 * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
965 * function will then be called again and the newly received triplets will then
966 * be given to the caller.
967 */
968int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
969 size_t identity_len, int max_chal,
970 u8 *_rand, u8 *kc, u8 *sres,
971 void *cb_session_ctx)
972{
973 struct eap_sim_db_data *data = priv;
974 struct eap_sim_db_pending *entry;
975 int len, ret;
976 size_t i;
977 char msg[40];
978
979 if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
980 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
981 identity, identity_len);
982 return EAP_SIM_DB_FAILURE;
983 }
984 identity++;
985 identity_len--;
986 for (i = 0; i < identity_len; i++) {
987 if (identity[i] == '@') {
988 identity_len = i;
989 break;
990 }
991 }
992 if (identity_len + 1 > sizeof(entry->imsi)) {
993 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
994 identity, identity_len);
995 return EAP_SIM_DB_FAILURE;
996 }
997 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
998 identity, identity_len);
999
1000 entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
1001 if (entry) {
1002 int num_chal;
1003 if (entry->state == FAILURE) {
1004 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
1005 "failure");
1006 os_free(entry);
1007 return EAP_SIM_DB_FAILURE;
1008 }
1009
1010 if (entry->state == PENDING) {
1011 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
1012 "still pending");
1013 eap_sim_db_add_pending(data, entry);
1014 return EAP_SIM_DB_PENDING;
1015 }
1016
1017 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
1018 "%d challenges", entry->u.sim.num_chal);
1019 num_chal = entry->u.sim.num_chal;
1020 if (num_chal > max_chal)
1021 num_chal = max_chal;
1022 os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
1023 os_memcpy(sres, entry->u.sim.sres,
1024 num_chal * EAP_SIM_SRES_LEN);
1025 os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
1026 os_free(entry);
1027 return num_chal;
1028 }
1029
1030 if (data->sock < 0) {
1031 if (eap_sim_db_open_socket(data) < 0)
1032 return EAP_SIM_DB_FAILURE;
1033 }
1034
1035 len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
1036 if (len < 0 || len + identity_len >= sizeof(msg))
1037 return EAP_SIM_DB_FAILURE;
1038 os_memcpy(msg + len, identity, identity_len);
1039 len += identity_len;
1040 ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
1041 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1042 return EAP_SIM_DB_FAILURE;
1043 len += ret;
1044
1045 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
1046 "data for IMSI", identity, identity_len);
1047 if (eap_sim_db_send(data, msg, len) < 0)
1048 return EAP_SIM_DB_FAILURE;
1049
1050 entry = os_zalloc(sizeof(*entry));
1051 if (entry == NULL)
1052 return EAP_SIM_DB_FAILURE;
1053
1054 os_get_time(&entry->timestamp);
1055 os_memcpy(entry->imsi, identity, identity_len);
1056 entry->imsi_len = identity_len;
1057 entry->cb_session_ctx = cb_session_ctx;
1058 entry->state = PENDING;
1059 eap_sim_db_add_pending(data, entry);
1060 eap_sim_db_expire_pending(data);
1061
1062 return EAP_SIM_DB_PENDING;
1063}
1064
1065
1066static struct eap_sim_pseudonym *
1067eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
1068 size_t identity_len)
1069{
1070 char *pseudonym;
1071 size_t len;
1072 struct eap_sim_pseudonym *p;
1073
1074 if (identity_len == 0 ||
1075 (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
762e4ce6
JM
1076 identity[0] != EAP_AKA_PSEUDONYM_PREFIX &&
1077 identity[0] != EAP_AKA_PRIME_PSEUDONYM_PREFIX))
6fc6879b
JM
1078 return NULL;
1079
1080 /* Remove possible realm from identity */
1081 len = 0;
1082 while (len < identity_len) {
1083 if (identity[len] == '@')
1084 break;
1085 len++;
1086 }
1087
1088 pseudonym = os_malloc(len + 1);
1089 if (pseudonym == NULL)
1090 return NULL;
1091 os_memcpy(pseudonym, identity, len);
1092 pseudonym[len] = '\0';
1093
66979bb8
JM
1094#ifdef CONFIG_SQLITE
1095 if (data->sqlite_db) {
1096 p = db_get_pseudonym(data, pseudonym);
1097 os_free(pseudonym);
1098 return p;
1099 }
1100#endif /* CONFIG_SQLITE */
1101
6fc6879b
JM
1102 p = data->pseudonyms;
1103 while (p) {
1104 if (os_strcmp(p->pseudonym, pseudonym) == 0)
1105 break;
1106 p = p->next;
1107 }
1108
1109 os_free(pseudonym);
1110
1111 return p;
1112}
1113
1114
1115static struct eap_sim_pseudonym *
1116eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
1117 size_t identity_len)
1118{
1119 struct eap_sim_pseudonym *p;
1120
1121 if (identity_len == 0 ||
1122 (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
762e4ce6
JM
1123 identity[0] != EAP_AKA_PERMANENT_PREFIX &&
1124 identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX))
6fc6879b
JM
1125 return NULL;
1126
66979bb8
JM
1127#ifdef CONFIG_SQLITE
1128 if (data->sqlite_db)
1129 return db_get_pseudonym_id(data, identity, identity_len);
1130#endif /* CONFIG_SQLITE */
1131
6fc6879b
JM
1132 p = data->pseudonyms;
1133 while (p) {
1134 if (identity_len == p->identity_len &&
1135 os_memcmp(p->identity, identity, identity_len) == 0)
1136 break;
1137 p = p->next;
1138 }
1139
1140 return p;
1141}
1142
1143
1144static struct eap_sim_reauth *
1145eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
1146 size_t identity_len)
1147{
1148 char *reauth_id;
1149 size_t len;
1150 struct eap_sim_reauth *r;
1151
1152 if (identity_len == 0 ||
1153 (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
762e4ce6
JM
1154 identity[0] != EAP_AKA_REAUTH_ID_PREFIX &&
1155 identity[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX))
6fc6879b
JM
1156 return NULL;
1157
1158 /* Remove possible realm from identity */
1159 len = 0;
1160 while (len < identity_len) {
1161 if (identity[len] == '@')
1162 break;
1163 len++;
1164 }
1165
1166 reauth_id = os_malloc(len + 1);
1167 if (reauth_id == NULL)
1168 return NULL;
1169 os_memcpy(reauth_id, identity, len);
1170 reauth_id[len] = '\0';
1171
29813cfd
JM
1172#ifdef CONFIG_SQLITE
1173 if (data->sqlite_db) {
1174 r = db_get_reauth(data, reauth_id);
1175 os_free(reauth_id);
1176 return r;
1177 }
1178#endif /* CONFIG_SQLITE */
1179
6fc6879b
JM
1180 r = data->reauths;
1181 while (r) {
1182 if (os_strcmp(r->reauth_id, reauth_id) == 0)
1183 break;
1184 r = r->next;
1185 }
1186
1187 os_free(reauth_id);
1188
1189 return r;
1190}
1191
1192
1193static struct eap_sim_reauth *
1194eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
1195 size_t identity_len)
1196{
1197 struct eap_sim_pseudonym *p;
1198 struct eap_sim_reauth *r;
1199
1200 if (identity_len == 0)
1201 return NULL;
1202
1203 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1204 if (p == NULL)
1205 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1206 if (p) {
1207 identity = p->identity;
1208 identity_len = p->identity_len;
1209 }
1210
29813cfd
JM
1211#ifdef CONFIG_SQLITE
1212 if (data->sqlite_db)
1213 return db_get_reauth_id(data, identity, identity_len);
1214#endif /* CONFIG_SQLITE */
1215
6fc6879b
JM
1216 r = data->reauths;
1217 while (r) {
1218 if (identity_len == r->identity_len &&
1219 os_memcmp(r->identity, identity, identity_len) == 0)
1220 break;
1221 r = r->next;
1222 }
1223
1224 return r;
1225}
1226
1227
1228/**
1229 * eap_sim_db_identity_known - Verify whether the given identity is known
1230 * @priv: Private data pointer from eap_sim_db_init()
1231 * @identity: User name identity
1232 * @identity_len: Length of identity in bytes
1233 * Returns: 0 if the user is found or -1 on failure
1234 *
762e4ce6
JM
1235 * In most cases, the user name is ['0','1','6'] | IMSI, i.e., 1 followed by
1236 * the IMSI in ASCII format for EAP-SIM, ['2','3','7'] | pseudonym, or
1237 * ['4','5','7'] | reauth_id.
6fc6879b
JM
1238 */
1239int eap_sim_db_identity_known(void *priv, const u8 *identity,
1240 size_t identity_len)
1241{
1242 struct eap_sim_db_data *data = priv;
1243
1244 if (identity == NULL || identity_len < 2)
1245 return -1;
1246
1247 if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
762e4ce6
JM
1248 identity[0] == EAP_AKA_PSEUDONYM_PREFIX ||
1249 identity[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) {
6fc6879b
JM
1250 struct eap_sim_pseudonym *p =
1251 eap_sim_db_get_pseudonym(data, identity, identity_len);
1252 return p ? 0 : -1;
1253 }
1254
1255 if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
762e4ce6
JM
1256 identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
1257 identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) {
6fc6879b
JM
1258 struct eap_sim_reauth *r =
1259 eap_sim_db_get_reauth(data, identity, identity_len);
1260 return r ? 0 : -1;
1261 }
1262
1263 if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
762e4ce6
JM
1264 identity[0] != EAP_AKA_PERMANENT_PREFIX &&
1265 identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) {
6fc6879b
JM
1266 /* Unknown identity prefix */
1267 return -1;
1268 }
1269
1270 /* TODO: Should consider asking HLR/AuC gateway whether this permanent
1271 * identity is known. If it is, EAP-SIM/AKA can skip identity request.
1272 * In case of EAP-AKA, this would reduce number of needed round-trips.
1273 * Ideally, this would be done with one wait, i.e., just request
1274 * authentication data and store it for the next use. This would then
1275 * need to use similar pending-request functionality as the normal
1276 * request for authentication data at later phase.
1277 */
1278 return -1;
1279}
1280
1281
1282static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
1283{
1284 char *id, *pos, *end;
1285 u8 buf[10];
1286
3642c431 1287 if (random_get_bytes(buf, sizeof(buf)))
6fc6879b
JM
1288 return NULL;
1289 id = os_malloc(sizeof(buf) * 2 + 2);
1290 if (id == NULL)
1291 return NULL;
1292
1293 pos = id;
1294 end = id + sizeof(buf) * 2 + 2;
1295 *pos++ = prefix;
1296 pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
1297
1298 return id;
1299}
1300
1301
1302/**
1303 * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
1304 * @priv: Private data pointer from eap_sim_db_init()
762e4ce6 1305 * @method: EAP method (SIM/AKA/AKA')
6fc6879b
JM
1306 * Returns: Next pseudonym (allocated string) or %NULL on failure
1307 *
1308 * This function is used to generate a pseudonym for EAP-SIM. The returned
1309 * pseudonym is not added to database at this point; it will need to be added
1310 * with eap_sim_db_add_pseudonym() once the authentication has been completed
1311 * successfully. Caller is responsible for freeing the returned buffer.
1312 */
762e4ce6 1313char * eap_sim_db_get_next_pseudonym(void *priv, enum eap_sim_db_method method)
6fc6879b
JM
1314{
1315 struct eap_sim_db_data *data = priv;
762e4ce6
JM
1316 char prefix = EAP_SIM_REAUTH_ID_PREFIX;
1317
1318 switch (method) {
1319 case EAP_SIM_DB_SIM:
1320 prefix = EAP_SIM_PSEUDONYM_PREFIX;
1321 break;
1322 case EAP_SIM_DB_AKA:
1323 prefix = EAP_AKA_PSEUDONYM_PREFIX;
1324 break;
1325 case EAP_SIM_DB_AKA_PRIME:
1326 prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX;
1327 break;
1328 }
1329
1330 return eap_sim_db_get_next(data, prefix);
6fc6879b
JM
1331}
1332
1333
1334/**
1335 * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
1336 * @priv: Private data pointer from eap_sim_db_init()
762e4ce6 1337 * @method: EAP method (SIM/AKA/AKA')
6fc6879b
JM
1338 * Returns: Next reauth_id (allocated string) or %NULL on failure
1339 *
1340 * This function is used to generate a fast re-authentication identity for
1341 * EAP-SIM. The returned reauth_id is not added to database at this point; it
1342 * will need to be added with eap_sim_db_add_reauth() once the authentication
1343 * has been completed successfully. Caller is responsible for freeing the
1344 * returned buffer.
1345 */
762e4ce6 1346char * eap_sim_db_get_next_reauth_id(void *priv, enum eap_sim_db_method method)
6fc6879b
JM
1347{
1348 struct eap_sim_db_data *data = priv;
762e4ce6
JM
1349 char prefix = EAP_SIM_REAUTH_ID_PREFIX;
1350
1351 switch (method) {
1352 case EAP_SIM_DB_SIM:
1353 prefix = EAP_SIM_REAUTH_ID_PREFIX;
1354 break;
1355 case EAP_SIM_DB_AKA:
1356 prefix = EAP_AKA_REAUTH_ID_PREFIX;
1357 break;
1358 case EAP_SIM_DB_AKA_PRIME:
1359 prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX;
1360 break;
1361 }
1362
1363 return eap_sim_db_get_next(data, prefix);
6fc6879b
JM
1364}
1365
1366
1367/**
1368 * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
1369 * @priv: Private data pointer from eap_sim_db_init()
1370 * @identity: Identity of the user (may be permanent identity or pseudonym)
1371 * @identity_len: Length of identity
1372 * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
1373 * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
1374 * free it.
1375 * Returns: 0 on success, -1 on failure
1376 *
1377 * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
1378 * responsible of freeing pseudonym buffer once it is not needed anymore.
1379 */
1380int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
1381 size_t identity_len, char *pseudonym)
1382{
1383 struct eap_sim_db_data *data = priv;
1384 struct eap_sim_pseudonym *p;
1385 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
1386 identity, identity_len);
1387 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
1388
1389 /* TODO: could store last two pseudonyms */
66979bb8
JM
1390#ifdef CONFIG_SQLITE
1391 if (data->sqlite_db)
1392 return db_add_pseudonym(data, identity, identity_len,
1393 pseudonym);
1394#endif /* CONFIG_SQLITE */
6fc6879b
JM
1395 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1396 if (p == NULL)
1397 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1398
1399 if (p) {
1400 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
1401 "pseudonym: %s", p->pseudonym);
1402 os_free(p->pseudonym);
1403 p->pseudonym = pseudonym;
1404 return 0;
1405 }
1406
1407 p = os_zalloc(sizeof(*p));
1408 if (p == NULL) {
1409 os_free(pseudonym);
1410 return -1;
1411 }
1412
1413 p->next = data->pseudonyms;
1414 p->identity = os_malloc(identity_len);
1415 if (p->identity == NULL) {
1416 os_free(p);
1417 os_free(pseudonym);
1418 return -1;
1419 }
1420 os_memcpy(p->identity, identity, identity_len);
1421 p->identity_len = identity_len;
1422 p->pseudonym = pseudonym;
1423 data->pseudonyms = p;
1424
1425 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
1426 return 0;
1427}
1428
1429
9881795e
JM
1430static struct eap_sim_reauth *
1431eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
1432 size_t identity_len, char *reauth_id, u16 counter)
6fc6879b 1433{
6fc6879b 1434 struct eap_sim_reauth *r;
9881795e 1435
6fc6879b
JM
1436 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
1437 identity, identity_len);
1438 wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
1439
1440 r = eap_sim_db_get_reauth(data, identity, identity_len);
1441 if (r == NULL)
1442 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1443
1444 if (r) {
1445 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
1446 "reauth_id: %s", r->reauth_id);
1447 os_free(r->reauth_id);
1448 r->reauth_id = reauth_id;
1449 } else {
1450 r = os_zalloc(sizeof(*r));
1451 if (r == NULL) {
1452 os_free(reauth_id);
9881795e 1453 return NULL;
6fc6879b
JM
1454 }
1455
1456 r->next = data->reauths;
1457 r->identity = os_malloc(identity_len);
1458 if (r->identity == NULL) {
1459 os_free(r);
1460 os_free(reauth_id);
9881795e 1461 return NULL;
6fc6879b
JM
1462 }
1463 os_memcpy(r->identity, identity, identity_len);
1464 r->identity_len = identity_len;
1465 r->reauth_id = reauth_id;
1466 data->reauths = r;
1467 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
1468 }
1469
1470 r->counter = counter;
9881795e
JM
1471
1472 return r;
1473}
1474
1475
1476/**
1477 * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
1478 * @priv: Private data pointer from eap_sim_db_init()
1479 * @identity: Identity of the user (may be permanent identity or pseudonym)
1480 * @identity_len: Length of identity
1481 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
1482 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1483 * free it.
a17df5fb 1484 * @counter: AT_COUNTER value for fast re-authentication
9881795e
JM
1485 * @mk: 16-byte MK from the previous full authentication or %NULL
1486 * Returns: 0 on success, -1 on failure
1487 *
1488 * This function adds a new re-authentication entry for an EAP-SIM user.
1489 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1490 * anymore.
1491 */
1492int eap_sim_db_add_reauth(void *priv, const u8 *identity,
1493 size_t identity_len, char *reauth_id, u16 counter,
1494 const u8 *mk)
1495{
1496 struct eap_sim_db_data *data = priv;
1497 struct eap_sim_reauth *r;
1498
29813cfd
JM
1499#ifdef CONFIG_SQLITE
1500 if (data->sqlite_db)
1501 return db_add_reauth(data, identity, identity_len,
e87982ea 1502 reauth_id, counter, mk, NULL, NULL,
29813cfd
JM
1503 NULL);
1504#endif /* CONFIG_SQLITE */
9881795e
JM
1505 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1506 counter);
1507 if (r == NULL)
1508 return -1;
1509
6fc6879b 1510 os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
9881795e
JM
1511
1512 return 0;
1513}
1514
1515
1e5839e0 1516#ifdef EAP_SERVER_AKA_PRIME
9881795e
JM
1517/**
1518 * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
1519 * @priv: Private data pointer from eap_sim_db_init()
1520 * @identity: Identity of the user (may be permanent identity or pseudonym)
1521 * @identity_len: Length of identity
1522 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
1523 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1524 * free it.
a17df5fb
JM
1525 * @counter: AT_COUNTER value for fast re-authentication
1526 * @k_encr: K_encr from the previous full authentication
1527 * @k_aut: K_aut from the previous full authentication
1528 * @k_re: 32-byte K_re from the previous full authentication
9881795e
JM
1529 * Returns: 0 on success, -1 on failure
1530 *
1531 * This function adds a new re-authentication entry for an EAP-AKA' user.
1532 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1533 * anymore.
1534 */
1535int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
1536 size_t identity_len, char *reauth_id,
1537 u16 counter, const u8 *k_encr, const u8 *k_aut,
1538 const u8 *k_re)
1539{
1540 struct eap_sim_db_data *data = priv;
1541 struct eap_sim_reauth *r;
1542
29813cfd
JM
1543#ifdef CONFIG_SQLITE
1544 if (data->sqlite_db)
1545 return db_add_reauth(data, identity, identity_len,
e87982ea 1546 reauth_id, counter, NULL,
29813cfd
JM
1547 k_encr, k_aut, k_re);
1548#endif /* CONFIG_SQLITE */
9881795e
JM
1549 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1550 counter);
1551 if (r == NULL)
1552 return -1;
1553
9881795e
JM
1554 os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
1555 os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
1556 os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
6fc6879b
JM
1557
1558 return 0;
1559}
1e5839e0 1560#endif /* EAP_SERVER_AKA_PRIME */
6fc6879b
JM
1561
1562
1563/**
1564 * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
1565 * @priv: Private data pointer from eap_sim_db_init()
1566 * @identity: Identity of the user (may be permanent identity or pseudonym)
1567 * @identity_len: Length of identity
1568 * @len: Buffer for length of the returned permanent identity
1569 * Returns: Pointer to the permanent identity, or %NULL if not found
1570 */
1571const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
1572 size_t identity_len, size_t *len)
1573{
1574 struct eap_sim_db_data *data = priv;
1575 struct eap_sim_pseudonym *p;
1576
1577 if (identity == NULL)
1578 return NULL;
1579
1580 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1581 if (p == NULL)
1582 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1583 if (p == NULL)
1584 return NULL;
1585
1586 *len = p->identity_len;
1587 return p->identity;
1588}
1589
1590
1591/**
1592 * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
1593 * @priv: Private data pointer from eap_sim_db_init()
1594 * @identity: Identity of the user (may be permanent identity, pseudonym, or
1595 * reauth_id)
1596 * @identity_len: Length of identity
6fc6879b
JM
1597 * Returns: Pointer to the re-auth entry, or %NULL if not found
1598 */
1599struct eap_sim_reauth *
1600eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
1601 size_t identity_len)
1602{
1603 struct eap_sim_db_data *data = priv;
1604 struct eap_sim_reauth *r;
1605
1606 if (identity == NULL)
1607 return NULL;
1608 r = eap_sim_db_get_reauth(data, identity, identity_len);
1609 if (r == NULL)
1610 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1611 return r;
1612}
1613
1614
1615/**
1616 * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
1617 * @priv: Private data pointer from eap_sim_db_init()
1618 * @reauth: Pointer to re-authentication entry from
1619 * eap_sim_db_get_reauth_entry()
1620 */
1621void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
1622{
1623 struct eap_sim_db_data *data = priv;
1624 struct eap_sim_reauth *r, *prev = NULL;
29813cfd
JM
1625#ifdef CONFIG_SQLITE
1626 if (data->sqlite_db) {
1627 db_remove_reauth(data, reauth);
1628 return;
1629 }
1630#endif /* CONFIG_SQLITE */
6fc6879b
JM
1631 r = data->reauths;
1632 while (r) {
1633 if (r == reauth) {
1634 if (prev)
1635 prev->next = r->next;
1636 else
1637 data->reauths = r->next;
1638 eap_sim_db_free_reauth(r);
1639 return;
1640 }
1641 prev = r;
1642 r = r->next;
1643 }
1644}
1645
1646
1647/**
1648 * eap_sim_db_get_aka_auth - Get AKA authentication values
1649 * @priv: Private data pointer from eap_sim_db_init()
1650 * @identity: User name identity
1651 * @identity_len: Length of identity in bytes
1652 * @_rand: Buffer for RAND value
1653 * @autn: Buffer for AUTN value
1654 * @ik: Buffer for IK value
1655 * @ck: Buffer for CK value
1656 * @res: Buffer for RES value
1657 * @res_len: Buffer for RES length
1658 * @cb_session_ctx: Session callback context for get_complete_cb()
1659 * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
1660 * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
1661 * case, the callback function registered with eap_sim_db_init() will be
1662 * called once the results become available.
1663 *
1664 * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
762e4ce6 1665 * ASCII format for EAP-AKA and '6' | IMSI for EAP-AKA'.
6fc6879b
JM
1666 *
1667 * When using an external server for AKA authentication, this function can
1668 * always start a request and return EAP_SIM_DB_PENDING immediately if
1669 * authentication triplets are not available. Once the authentication data are
1670 * received, callback function registered with eap_sim_db_init() is called to
1671 * notify EAP state machine to reprocess the message. This
1672 * eap_sim_db_get_aka_auth() function will then be called again and the newly
1673 * received triplets will then be given to the caller.
1674 */
1675int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
1676 size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
1677 u8 *ck, u8 *res, size_t *res_len,
1678 void *cb_session_ctx)
1679{
1680 struct eap_sim_db_data *data = priv;
1681 struct eap_sim_db_pending *entry;
1682 int len;
1683 size_t i;
1684 char msg[40];
1685
1686 if (identity_len < 2 || identity == NULL ||
762e4ce6
JM
1687 (identity[0] != EAP_AKA_PERMANENT_PREFIX &&
1688 identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) {
6fc6879b
JM
1689 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1690 identity, identity_len);
1691 return EAP_SIM_DB_FAILURE;
1692 }
1693 identity++;
1694 identity_len--;
1695 for (i = 0; i < identity_len; i++) {
1696 if (identity[i] == '@') {
1697 identity_len = i;
1698 break;
1699 }
1700 }
1701 if (identity_len + 1 > sizeof(entry->imsi)) {
1702 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1703 identity, identity_len);
1704 return EAP_SIM_DB_FAILURE;
1705 }
1706 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
1707 identity, identity_len);
1708
1709 entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
1710 if (entry) {
1711 if (entry->state == FAILURE) {
1712 os_free(entry);
1713 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
1714 return EAP_SIM_DB_FAILURE;
1715 }
1716
1717 if (entry->state == PENDING) {
1718 eap_sim_db_add_pending(data, entry);
1719 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
1720 return EAP_SIM_DB_PENDING;
1721 }
1722
1723 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
1724 "received authentication data");
1725 os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
1726 os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
1727 os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
1728 os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
1729 os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
1730 *res_len = entry->u.aka.res_len;
1731 os_free(entry);
1732 return 0;
1733 }
1734
1735 if (data->sock < 0) {
1736 if (eap_sim_db_open_socket(data) < 0)
1737 return EAP_SIM_DB_FAILURE;
1738 }
1739
1740 len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
1741 if (len < 0 || len + identity_len >= sizeof(msg))
1742 return EAP_SIM_DB_FAILURE;
1743 os_memcpy(msg + len, identity, identity_len);
1744 len += identity_len;
1745
1746 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
1747 "data for IMSI", identity, identity_len);
1748 if (eap_sim_db_send(data, msg, len) < 0)
1749 return EAP_SIM_DB_FAILURE;
1750
1751 entry = os_zalloc(sizeof(*entry));
1752 if (entry == NULL)
1753 return EAP_SIM_DB_FAILURE;
1754
1755 os_get_time(&entry->timestamp);
1756 entry->aka = 1;
1757 os_memcpy(entry->imsi, identity, identity_len);
1758 entry->imsi_len = identity_len;
1759 entry->cb_session_ctx = cb_session_ctx;
1760 entry->state = PENDING;
1761 eap_sim_db_add_pending(data, entry);
1762 eap_sim_db_expire_pending(data);
1763
1764 return EAP_SIM_DB_PENDING;
1765}
1766
1767
1768/**
1769 * eap_sim_db_resynchronize - Resynchronize AKA AUTN
1770 * @priv: Private data pointer from eap_sim_db_init()
1771 * @identity: User name identity
1772 * @identity_len: Length of identity in bytes
1773 * @auts: AUTS value from the peer
1774 * @_rand: RAND value used in the rejected message
1775 * Returns: 0 on success, -1 on failure
1776 *
1777 * This function is called when the peer reports synchronization failure in the
1778 * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
1779 * HLR/AuC to allow it to resynchronize with the peer. After this,
1780 * eap_sim_db_get_aka_auth() will be called again to to fetch updated
1781 * RAND/AUTN values for the next challenge.
1782 */
1783int eap_sim_db_resynchronize(void *priv, const u8 *identity,
1784 size_t identity_len, const u8 *auts,
1785 const u8 *_rand)
1786{
1787 struct eap_sim_db_data *data = priv;
1788 size_t i;
1789
1790 if (identity_len < 2 || identity == NULL ||
762e4ce6
JM
1791 (identity[0] != EAP_AKA_PERMANENT_PREFIX &&
1792 identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) {
6fc6879b
JM
1793 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1794 identity, identity_len);
1795 return -1;
1796 }
1797 identity++;
1798 identity_len--;
1799 for (i = 0; i < identity_len; i++) {
1800 if (identity[i] == '@') {
1801 identity_len = i;
1802 break;
1803 }
1804 }
1805 if (identity_len > 20) {
1806 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1807 identity, identity_len);
1808 return -1;
1809 }
1810
1811 if (data->sock >= 0) {
1812 char msg[100];
1813 int len, ret;
1814
1815 len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
1816 if (len < 0 || len + identity_len >= sizeof(msg))
1817 return -1;
1818 os_memcpy(msg + len, identity, identity_len);
1819 len += identity_len;
1820
1821 ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1822 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1823 return -1;
1824 len += ret;
1825 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1826 auts, EAP_AKA_AUTS_LEN);
1827 ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1828 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1829 return -1;
1830 len += ret;
1831 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1832 _rand, EAP_AKA_RAND_LEN);
1833 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
1834 "IMSI", identity, identity_len);
1835 if (eap_sim_db_send(data, msg, len) < 0)
1836 return -1;
1837 }
1838
1839 return 0;
1840}