1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator 3e5b75505Sopenharmony_ci * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen <j@w1.fi> 4e5b75505Sopenharmony_ci * 5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 6e5b75505Sopenharmony_ci * See README for more details. 7e5b75505Sopenharmony_ci * 8e5b75505Sopenharmony_ci * This is an example implementation of the EAP-SIM/AKA database/authentication 9e5b75505Sopenharmony_ci * gateway interface to HLR/AuC. It is expected to be replaced with an 10e5b75505Sopenharmony_ci * implementation of SS7 gateway to GSM/UMTS authentication center (HLR/AuC) or 11e5b75505Sopenharmony_ci * a local implementation of SIM triplet and AKA authentication data generator. 12e5b75505Sopenharmony_ci * 13e5b75505Sopenharmony_ci * hostapd will send SIM/AKA authentication queries over a UNIX domain socket 14e5b75505Sopenharmony_ci * to and external program, e.g., this hlr_auc_gw. This interface uses simple 15e5b75505Sopenharmony_ci * text-based format: 16e5b75505Sopenharmony_ci * 17e5b75505Sopenharmony_ci * EAP-SIM / GSM triplet query/response: 18e5b75505Sopenharmony_ci * SIM-REQ-AUTH <IMSI> <max_chal> 19e5b75505Sopenharmony_ci * SIM-RESP-AUTH <IMSI> Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3] 20e5b75505Sopenharmony_ci * SIM-RESP-AUTH <IMSI> FAILURE 21e5b75505Sopenharmony_ci * GSM-AUTH-REQ <IMSI> RAND1:RAND2[:RAND3] 22e5b75505Sopenharmony_ci * GSM-AUTH-RESP <IMSI> Kc1:SRES1:Kc2:SRES2[:Kc3:SRES3] 23e5b75505Sopenharmony_ci * GSM-AUTH-RESP <IMSI> FAILURE 24e5b75505Sopenharmony_ci * 25e5b75505Sopenharmony_ci * EAP-AKA / UMTS query/response: 26e5b75505Sopenharmony_ci * AKA-REQ-AUTH <IMSI> 27e5b75505Sopenharmony_ci * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> 28e5b75505Sopenharmony_ci * AKA-RESP-AUTH <IMSI> FAILURE 29e5b75505Sopenharmony_ci * 30e5b75505Sopenharmony_ci * EAP-AKA / UMTS AUTS (re-synchronization): 31e5b75505Sopenharmony_ci * AKA-AUTS <IMSI> <AUTS> <RAND> 32e5b75505Sopenharmony_ci * 33e5b75505Sopenharmony_ci * IMSI and max_chal are sent as an ASCII string, 34e5b75505Sopenharmony_ci * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings. 35e5b75505Sopenharmony_ci * 36e5b75505Sopenharmony_ci * An example implementation here reads GSM authentication triplets from a 37e5b75505Sopenharmony_ci * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex 38e5b75505Sopenharmony_ci * strings. This is used to simulate an HLR/AuC. As such, it is not very useful 39e5b75505Sopenharmony_ci * for real life authentication, but it is useful both as an example 40e5b75505Sopenharmony_ci * implementation and for EAP-SIM/AKA/AKA' testing. 41e5b75505Sopenharmony_ci * 42e5b75505Sopenharmony_ci * For a stronger example design, Milenage and GSM-Milenage algorithms can be 43e5b75505Sopenharmony_ci * used to dynamically generate authenticatipn information for EAP-AKA/AKA' and 44e5b75505Sopenharmony_ci * EAP-SIM, respectively, if Ki is known. 45e5b75505Sopenharmony_ci * 46e5b75505Sopenharmony_ci * SQN generation follows the not time-based Profile 2 described in 47e5b75505Sopenharmony_ci * 3GPP TS 33.102 Annex C.3.2. The length of IND is 5 bits by default, but this 48e5b75505Sopenharmony_ci * can be changed with a command line options if needed. 49e5b75505Sopenharmony_ci */ 50e5b75505Sopenharmony_ci 51e5b75505Sopenharmony_ci#include "includes.h" 52e5b75505Sopenharmony_ci#include <sys/un.h> 53e5b75505Sopenharmony_ci#ifdef CONFIG_SQLITE 54e5b75505Sopenharmony_ci#include <sqlite3.h> 55e5b75505Sopenharmony_ci#endif /* CONFIG_SQLITE */ 56e5b75505Sopenharmony_ci 57e5b75505Sopenharmony_ci#include "common.h" 58e5b75505Sopenharmony_ci#include "crypto/milenage.h" 59e5b75505Sopenharmony_ci#include "crypto/random.h" 60e5b75505Sopenharmony_ci 61e5b75505Sopenharmony_cistatic const char *default_socket_path = "/tmp/hlr_auc_gw.sock"; 62e5b75505Sopenharmony_cistatic const char *socket_path; 63e5b75505Sopenharmony_cistatic int serv_sock = -1; 64e5b75505Sopenharmony_cistatic char *milenage_file = NULL; 65e5b75505Sopenharmony_cistatic int update_milenage = 0; 66e5b75505Sopenharmony_cistatic int sqn_changes = 0; 67e5b75505Sopenharmony_cistatic int ind_len = 5; 68e5b75505Sopenharmony_cistatic int stdout_debug = 1; 69e5b75505Sopenharmony_ci 70e5b75505Sopenharmony_ci/* GSM triplets */ 71e5b75505Sopenharmony_cistruct gsm_triplet { 72e5b75505Sopenharmony_ci struct gsm_triplet *next; 73e5b75505Sopenharmony_ci char imsi[20]; 74e5b75505Sopenharmony_ci u8 kc[8]; 75e5b75505Sopenharmony_ci u8 sres[4]; 76e5b75505Sopenharmony_ci u8 _rand[16]; 77e5b75505Sopenharmony_ci}; 78e5b75505Sopenharmony_ci 79e5b75505Sopenharmony_cistatic struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL; 80e5b75505Sopenharmony_ci 81e5b75505Sopenharmony_ci/* OPc and AMF parameters for Milenage (Example algorithms for AKA). */ 82e5b75505Sopenharmony_cistruct milenage_parameters { 83e5b75505Sopenharmony_ci struct milenage_parameters *next; 84e5b75505Sopenharmony_ci char imsi[20]; 85e5b75505Sopenharmony_ci u8 ki[16]; 86e5b75505Sopenharmony_ci u8 opc[16]; 87e5b75505Sopenharmony_ci u8 amf[2]; 88e5b75505Sopenharmony_ci u8 sqn[6]; 89e5b75505Sopenharmony_ci int set; 90e5b75505Sopenharmony_ci size_t res_len; 91e5b75505Sopenharmony_ci}; 92e5b75505Sopenharmony_ci 93e5b75505Sopenharmony_cistatic struct milenage_parameters *milenage_db = NULL; 94e5b75505Sopenharmony_ci 95e5b75505Sopenharmony_ci#define EAP_SIM_MAX_CHAL 3 96e5b75505Sopenharmony_ci 97e5b75505Sopenharmony_ci#define EAP_AKA_RAND_LEN 16 98e5b75505Sopenharmony_ci#define EAP_AKA_AUTN_LEN 16 99e5b75505Sopenharmony_ci#define EAP_AKA_AUTS_LEN 14 100e5b75505Sopenharmony_ci#define EAP_AKA_RES_MIN_LEN 4 101e5b75505Sopenharmony_ci#define EAP_AKA_RES_MAX_LEN 16 102e5b75505Sopenharmony_ci#define EAP_AKA_IK_LEN 16 103e5b75505Sopenharmony_ci#define EAP_AKA_CK_LEN 16 104e5b75505Sopenharmony_ci 105e5b75505Sopenharmony_ci 106e5b75505Sopenharmony_ci#ifdef CONFIG_SQLITE 107e5b75505Sopenharmony_ci 108e5b75505Sopenharmony_cistatic sqlite3 *sqlite_db = NULL; 109e5b75505Sopenharmony_cistatic struct milenage_parameters db_tmp_milenage; 110e5b75505Sopenharmony_ci 111e5b75505Sopenharmony_ci 112e5b75505Sopenharmony_cistatic int db_table_exists(sqlite3 *db, const char *name) 113e5b75505Sopenharmony_ci{ 114e5b75505Sopenharmony_ci char cmd[128]; 115e5b75505Sopenharmony_ci os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); 116e5b75505Sopenharmony_ci return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; 117e5b75505Sopenharmony_ci} 118e5b75505Sopenharmony_ci 119e5b75505Sopenharmony_ci 120e5b75505Sopenharmony_cistatic int db_table_create_milenage(sqlite3 *db) 121e5b75505Sopenharmony_ci{ 122e5b75505Sopenharmony_ci char *err = NULL; 123e5b75505Sopenharmony_ci const char *sql = 124e5b75505Sopenharmony_ci "CREATE TABLE milenage(" 125e5b75505Sopenharmony_ci " imsi INTEGER PRIMARY KEY NOT NULL," 126e5b75505Sopenharmony_ci " ki CHAR(32) NOT NULL," 127e5b75505Sopenharmony_ci " opc CHAR(32) NOT NULL," 128e5b75505Sopenharmony_ci " amf CHAR(4) NOT NULL," 129e5b75505Sopenharmony_ci " sqn CHAR(12) NOT NULL," 130e5b75505Sopenharmony_ci " res_len INTEGER" 131e5b75505Sopenharmony_ci ");"; 132e5b75505Sopenharmony_ci 133e5b75505Sopenharmony_ci printf("Adding database table for milenage information\n"); 134e5b75505Sopenharmony_ci if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { 135e5b75505Sopenharmony_ci printf("SQLite error: %s\n", err); 136e5b75505Sopenharmony_ci sqlite3_free(err); 137e5b75505Sopenharmony_ci return -1; 138e5b75505Sopenharmony_ci } 139e5b75505Sopenharmony_ci 140e5b75505Sopenharmony_ci return 0; 141e5b75505Sopenharmony_ci} 142e5b75505Sopenharmony_ci 143e5b75505Sopenharmony_ci 144e5b75505Sopenharmony_cistatic sqlite3 * db_open(const char *db_file) 145e5b75505Sopenharmony_ci{ 146e5b75505Sopenharmony_ci sqlite3 *db; 147e5b75505Sopenharmony_ci 148e5b75505Sopenharmony_ci if (sqlite3_open(db_file, &db)) { 149e5b75505Sopenharmony_ci printf("Failed to open database %s: %s\n", 150e5b75505Sopenharmony_ci db_file, sqlite3_errmsg(db)); 151e5b75505Sopenharmony_ci sqlite3_close(db); 152e5b75505Sopenharmony_ci return NULL; 153e5b75505Sopenharmony_ci } 154e5b75505Sopenharmony_ci 155e5b75505Sopenharmony_ci if (!db_table_exists(db, "milenage") && 156e5b75505Sopenharmony_ci db_table_create_milenage(db) < 0) { 157e5b75505Sopenharmony_ci sqlite3_close(db); 158e5b75505Sopenharmony_ci return NULL; 159e5b75505Sopenharmony_ci } 160e5b75505Sopenharmony_ci 161e5b75505Sopenharmony_ci return db; 162e5b75505Sopenharmony_ci} 163e5b75505Sopenharmony_ci 164e5b75505Sopenharmony_ci 165e5b75505Sopenharmony_cistatic int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[]) 166e5b75505Sopenharmony_ci{ 167e5b75505Sopenharmony_ci struct milenage_parameters *m = ctx; 168e5b75505Sopenharmony_ci int i; 169e5b75505Sopenharmony_ci 170e5b75505Sopenharmony_ci m->set = 1; 171e5b75505Sopenharmony_ci 172e5b75505Sopenharmony_ci for (i = 0; i < argc; i++) { 173e5b75505Sopenharmony_ci if (os_strcmp(col[i], "ki") == 0 && argv[i] && 174e5b75505Sopenharmony_ci hexstr2bin(argv[i], m->ki, sizeof(m->ki))) { 175e5b75505Sopenharmony_ci printf("Invalid ki value in database\n"); 176e5b75505Sopenharmony_ci return -1; 177e5b75505Sopenharmony_ci } 178e5b75505Sopenharmony_ci 179e5b75505Sopenharmony_ci if (os_strcmp(col[i], "opc") == 0 && argv[i] && 180e5b75505Sopenharmony_ci hexstr2bin(argv[i], m->opc, sizeof(m->opc))) { 181e5b75505Sopenharmony_ci printf("Invalid opcvalue in database\n"); 182e5b75505Sopenharmony_ci return -1; 183e5b75505Sopenharmony_ci } 184e5b75505Sopenharmony_ci 185e5b75505Sopenharmony_ci if (os_strcmp(col[i], "amf") == 0 && argv[i] && 186e5b75505Sopenharmony_ci hexstr2bin(argv[i], m->amf, sizeof(m->amf))) { 187e5b75505Sopenharmony_ci printf("Invalid amf value in database\n"); 188e5b75505Sopenharmony_ci return -1; 189e5b75505Sopenharmony_ci } 190e5b75505Sopenharmony_ci 191e5b75505Sopenharmony_ci if (os_strcmp(col[i], "sqn") == 0 && argv[i] && 192e5b75505Sopenharmony_ci hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) { 193e5b75505Sopenharmony_ci printf("Invalid sqn value in database\n"); 194e5b75505Sopenharmony_ci return -1; 195e5b75505Sopenharmony_ci } 196e5b75505Sopenharmony_ci 197e5b75505Sopenharmony_ci if (os_strcmp(col[i], "res_len") == 0 && argv[i]) { 198e5b75505Sopenharmony_ci m->res_len = atoi(argv[i]); 199e5b75505Sopenharmony_ci } 200e5b75505Sopenharmony_ci } 201e5b75505Sopenharmony_ci 202e5b75505Sopenharmony_ci return 0; 203e5b75505Sopenharmony_ci} 204e5b75505Sopenharmony_ci 205e5b75505Sopenharmony_ci 206e5b75505Sopenharmony_cistatic struct milenage_parameters * db_get_milenage(const char *imsi_txt) 207e5b75505Sopenharmony_ci{ 208e5b75505Sopenharmony_ci char cmd[128]; 209e5b75505Sopenharmony_ci unsigned long long imsi; 210e5b75505Sopenharmony_ci 211e5b75505Sopenharmony_ci os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage)); 212e5b75505Sopenharmony_ci imsi = atoll(imsi_txt); 213e5b75505Sopenharmony_ci os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi), 214e5b75505Sopenharmony_ci "%llu", imsi); 215e5b75505Sopenharmony_ci os_snprintf(cmd, sizeof(cmd), 216e5b75505Sopenharmony_ci "SELECT * FROM milenage WHERE imsi=%llu;", imsi); 217e5b75505Sopenharmony_ci if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage, 218e5b75505Sopenharmony_ci NULL) != SQLITE_OK) 219e5b75505Sopenharmony_ci return NULL; 220e5b75505Sopenharmony_ci 221e5b75505Sopenharmony_ci if (!db_tmp_milenage.set) 222e5b75505Sopenharmony_ci return NULL; 223e5b75505Sopenharmony_ci return &db_tmp_milenage; 224e5b75505Sopenharmony_ci} 225e5b75505Sopenharmony_ci 226e5b75505Sopenharmony_ci 227e5b75505Sopenharmony_cistatic int db_update_milenage_sqn(struct milenage_parameters *m) 228e5b75505Sopenharmony_ci{ 229e5b75505Sopenharmony_ci char cmd[128], val[13], *pos; 230e5b75505Sopenharmony_ci 231e5b75505Sopenharmony_ci if (sqlite_db == NULL) 232e5b75505Sopenharmony_ci return 0; 233e5b75505Sopenharmony_ci 234e5b75505Sopenharmony_ci pos = val; 235e5b75505Sopenharmony_ci pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6); 236e5b75505Sopenharmony_ci *pos = '\0'; 237e5b75505Sopenharmony_ci os_snprintf(cmd, sizeof(cmd), 238e5b75505Sopenharmony_ci "UPDATE milenage SET sqn='%s' WHERE imsi=%s;", 239e5b75505Sopenharmony_ci val, m->imsi); 240e5b75505Sopenharmony_ci if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) { 241e5b75505Sopenharmony_ci printf("Failed to update SQN in database for IMSI %s\n", 242e5b75505Sopenharmony_ci m->imsi); 243e5b75505Sopenharmony_ci return -1; 244e5b75505Sopenharmony_ci } 245e5b75505Sopenharmony_ci return 0; 246e5b75505Sopenharmony_ci} 247e5b75505Sopenharmony_ci 248e5b75505Sopenharmony_ci#endif /* CONFIG_SQLITE */ 249e5b75505Sopenharmony_ci 250e5b75505Sopenharmony_ci 251e5b75505Sopenharmony_cistatic int open_socket(const char *path) 252e5b75505Sopenharmony_ci{ 253e5b75505Sopenharmony_ci struct sockaddr_un addr; 254e5b75505Sopenharmony_ci int s; 255e5b75505Sopenharmony_ci 256e5b75505Sopenharmony_ci s = socket(PF_UNIX, SOCK_DGRAM, 0); 257e5b75505Sopenharmony_ci if (s < 0) { 258e5b75505Sopenharmony_ci perror("socket(PF_UNIX)"); 259e5b75505Sopenharmony_ci return -1; 260e5b75505Sopenharmony_ci } 261e5b75505Sopenharmony_ci 262e5b75505Sopenharmony_ci memset(&addr, 0, sizeof(addr)); 263e5b75505Sopenharmony_ci addr.sun_family = AF_UNIX; 264e5b75505Sopenharmony_ci os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); 265e5b75505Sopenharmony_ci if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 266e5b75505Sopenharmony_ci perror("hlr-auc-gw: bind(PF_UNIX)"); 267e5b75505Sopenharmony_ci close(s); 268e5b75505Sopenharmony_ci return -1; 269e5b75505Sopenharmony_ci } 270e5b75505Sopenharmony_ci 271e5b75505Sopenharmony_ci return s; 272e5b75505Sopenharmony_ci} 273e5b75505Sopenharmony_ci 274e5b75505Sopenharmony_ci 275e5b75505Sopenharmony_cistatic int read_gsm_triplets(const char *fname) 276e5b75505Sopenharmony_ci{ 277e5b75505Sopenharmony_ci FILE *f; 278e5b75505Sopenharmony_ci char buf[200], *pos, *pos2; 279e5b75505Sopenharmony_ci struct gsm_triplet *g = NULL; 280e5b75505Sopenharmony_ci int line, ret = 0; 281e5b75505Sopenharmony_ci 282e5b75505Sopenharmony_ci if (fname == NULL) 283e5b75505Sopenharmony_ci return -1; 284e5b75505Sopenharmony_ci 285e5b75505Sopenharmony_ci f = fopen(fname, "r"); 286e5b75505Sopenharmony_ci if (f == NULL) { 287e5b75505Sopenharmony_ci printf("Could not open GSM triplet data file '%s'\n", fname); 288e5b75505Sopenharmony_ci return -1; 289e5b75505Sopenharmony_ci } 290e5b75505Sopenharmony_ci 291e5b75505Sopenharmony_ci line = 0; 292e5b75505Sopenharmony_ci while (fgets(buf, sizeof(buf), f)) { 293e5b75505Sopenharmony_ci line++; 294e5b75505Sopenharmony_ci 295e5b75505Sopenharmony_ci /* Parse IMSI:Kc:SRES:RAND */ 296e5b75505Sopenharmony_ci buf[sizeof(buf) - 1] = '\0'; 297e5b75505Sopenharmony_ci if (buf[0] == '#') 298e5b75505Sopenharmony_ci continue; 299e5b75505Sopenharmony_ci pos = buf; 300e5b75505Sopenharmony_ci while (*pos != '\0' && *pos != '\n') 301e5b75505Sopenharmony_ci pos++; 302e5b75505Sopenharmony_ci if (*pos == '\n') 303e5b75505Sopenharmony_ci *pos = '\0'; 304e5b75505Sopenharmony_ci pos = buf; 305e5b75505Sopenharmony_ci if (*pos == '\0') 306e5b75505Sopenharmony_ci continue; 307e5b75505Sopenharmony_ci 308e5b75505Sopenharmony_ci g = os_zalloc(sizeof(*g)); 309e5b75505Sopenharmony_ci if (g == NULL) { 310e5b75505Sopenharmony_ci ret = -1; 311e5b75505Sopenharmony_ci break; 312e5b75505Sopenharmony_ci } 313e5b75505Sopenharmony_ci 314e5b75505Sopenharmony_ci /* IMSI */ 315e5b75505Sopenharmony_ci pos2 = NULL; 316e5b75505Sopenharmony_ci pos = str_token(buf, ":", &pos2); 317e5b75505Sopenharmony_ci if (!pos || os_strlen(pos) >= sizeof(g->imsi)) { 318e5b75505Sopenharmony_ci printf("%s:%d - Invalid IMSI\n", fname, line); 319e5b75505Sopenharmony_ci ret = -1; 320e5b75505Sopenharmony_ci break; 321e5b75505Sopenharmony_ci } 322e5b75505Sopenharmony_ci os_strlcpy(g->imsi, pos, sizeof(g->imsi)); 323e5b75505Sopenharmony_ci 324e5b75505Sopenharmony_ci /* Kc */ 325e5b75505Sopenharmony_ci pos = str_token(buf, ":", &pos2); 326e5b75505Sopenharmony_ci if (!pos || os_strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { 327e5b75505Sopenharmony_ci printf("%s:%d - Invalid Kc\n", fname, line); 328e5b75505Sopenharmony_ci ret = -1; 329e5b75505Sopenharmony_ci break; 330e5b75505Sopenharmony_ci } 331e5b75505Sopenharmony_ci 332e5b75505Sopenharmony_ci /* SRES */ 333e5b75505Sopenharmony_ci pos = str_token(buf, ":", &pos2); 334e5b75505Sopenharmony_ci if (!pos || os_strlen(pos) != 8 || 335e5b75505Sopenharmony_ci hexstr2bin(pos, g->sres, 4)) { 336e5b75505Sopenharmony_ci printf("%s:%d - Invalid SRES\n", fname, line); 337e5b75505Sopenharmony_ci ret = -1; 338e5b75505Sopenharmony_ci break; 339e5b75505Sopenharmony_ci } 340e5b75505Sopenharmony_ci 341e5b75505Sopenharmony_ci /* RAND */ 342e5b75505Sopenharmony_ci pos = str_token(buf, ":", &pos2); 343e5b75505Sopenharmony_ci if (!pos || os_strlen(pos) != 32 || 344e5b75505Sopenharmony_ci hexstr2bin(pos, g->_rand, 16)) { 345e5b75505Sopenharmony_ci printf("%s:%d - Invalid RAND\n", fname, line); 346e5b75505Sopenharmony_ci ret = -1; 347e5b75505Sopenharmony_ci break; 348e5b75505Sopenharmony_ci } 349e5b75505Sopenharmony_ci 350e5b75505Sopenharmony_ci g->next = gsm_db; 351e5b75505Sopenharmony_ci gsm_db = g; 352e5b75505Sopenharmony_ci g = NULL; 353e5b75505Sopenharmony_ci } 354e5b75505Sopenharmony_ci os_free(g); 355e5b75505Sopenharmony_ci 356e5b75505Sopenharmony_ci fclose(f); 357e5b75505Sopenharmony_ci 358e5b75505Sopenharmony_ci return ret; 359e5b75505Sopenharmony_ci} 360e5b75505Sopenharmony_ci 361e5b75505Sopenharmony_ci 362e5b75505Sopenharmony_cistatic struct gsm_triplet * get_gsm_triplet(const char *imsi) 363e5b75505Sopenharmony_ci{ 364e5b75505Sopenharmony_ci struct gsm_triplet *g = gsm_db_pos; 365e5b75505Sopenharmony_ci 366e5b75505Sopenharmony_ci while (g) { 367e5b75505Sopenharmony_ci if (strcmp(g->imsi, imsi) == 0) { 368e5b75505Sopenharmony_ci gsm_db_pos = g->next; 369e5b75505Sopenharmony_ci return g; 370e5b75505Sopenharmony_ci } 371e5b75505Sopenharmony_ci g = g->next; 372e5b75505Sopenharmony_ci } 373e5b75505Sopenharmony_ci 374e5b75505Sopenharmony_ci g = gsm_db; 375e5b75505Sopenharmony_ci while (g && g != gsm_db_pos) { 376e5b75505Sopenharmony_ci if (strcmp(g->imsi, imsi) == 0) { 377e5b75505Sopenharmony_ci gsm_db_pos = g->next; 378e5b75505Sopenharmony_ci return g; 379e5b75505Sopenharmony_ci } 380e5b75505Sopenharmony_ci g = g->next; 381e5b75505Sopenharmony_ci } 382e5b75505Sopenharmony_ci 383e5b75505Sopenharmony_ci return NULL; 384e5b75505Sopenharmony_ci} 385e5b75505Sopenharmony_ci 386e5b75505Sopenharmony_ci 387e5b75505Sopenharmony_cistatic int read_milenage(const char *fname) 388e5b75505Sopenharmony_ci{ 389e5b75505Sopenharmony_ci FILE *f; 390e5b75505Sopenharmony_ci char buf[200], *pos, *pos2; 391e5b75505Sopenharmony_ci struct milenage_parameters *m = NULL; 392e5b75505Sopenharmony_ci int line, ret = 0; 393e5b75505Sopenharmony_ci 394e5b75505Sopenharmony_ci if (fname == NULL) 395e5b75505Sopenharmony_ci return -1; 396e5b75505Sopenharmony_ci 397e5b75505Sopenharmony_ci f = fopen(fname, "r"); 398e5b75505Sopenharmony_ci if (f == NULL) { 399e5b75505Sopenharmony_ci printf("Could not open Milenage data file '%s'\n", fname); 400e5b75505Sopenharmony_ci return -1; 401e5b75505Sopenharmony_ci } 402e5b75505Sopenharmony_ci 403e5b75505Sopenharmony_ci line = 0; 404e5b75505Sopenharmony_ci while (fgets(buf, sizeof(buf), f)) { 405e5b75505Sopenharmony_ci line++; 406e5b75505Sopenharmony_ci 407e5b75505Sopenharmony_ci /* Parse IMSI Ki OPc AMF SQN [RES_len] */ 408e5b75505Sopenharmony_ci buf[sizeof(buf) - 1] = '\0'; 409e5b75505Sopenharmony_ci if (buf[0] == '#') 410e5b75505Sopenharmony_ci continue; 411e5b75505Sopenharmony_ci pos = buf; 412e5b75505Sopenharmony_ci while (*pos != '\0' && *pos != '\n') 413e5b75505Sopenharmony_ci pos++; 414e5b75505Sopenharmony_ci if (*pos == '\n') 415e5b75505Sopenharmony_ci *pos = '\0'; 416e5b75505Sopenharmony_ci pos = buf; 417e5b75505Sopenharmony_ci if (*pos == '\0') 418e5b75505Sopenharmony_ci continue; 419e5b75505Sopenharmony_ci 420e5b75505Sopenharmony_ci m = os_zalloc(sizeof(*m)); 421e5b75505Sopenharmony_ci if (m == NULL) { 422e5b75505Sopenharmony_ci ret = -1; 423e5b75505Sopenharmony_ci break; 424e5b75505Sopenharmony_ci } 425e5b75505Sopenharmony_ci 426e5b75505Sopenharmony_ci /* IMSI */ 427e5b75505Sopenharmony_ci pos2 = NULL; 428e5b75505Sopenharmony_ci pos = str_token(buf, " ", &pos2); 429e5b75505Sopenharmony_ci if (!pos || os_strlen(pos) >= sizeof(m->imsi)) { 430e5b75505Sopenharmony_ci printf("%s:%d - Invalid IMSI\n", fname, line); 431e5b75505Sopenharmony_ci ret = -1; 432e5b75505Sopenharmony_ci break; 433e5b75505Sopenharmony_ci } 434e5b75505Sopenharmony_ci os_strlcpy(m->imsi, pos, sizeof(m->imsi)); 435e5b75505Sopenharmony_ci 436e5b75505Sopenharmony_ci /* Ki */ 437e5b75505Sopenharmony_ci pos = str_token(buf, " ", &pos2); 438e5b75505Sopenharmony_ci if (!pos || os_strlen(pos) != 32 || 439e5b75505Sopenharmony_ci hexstr2bin(pos, m->ki, 16)) { 440e5b75505Sopenharmony_ci printf("%s:%d - Invalid Ki\n", fname, line); 441e5b75505Sopenharmony_ci ret = -1; 442e5b75505Sopenharmony_ci break; 443e5b75505Sopenharmony_ci } 444e5b75505Sopenharmony_ci 445e5b75505Sopenharmony_ci /* OPc */ 446e5b75505Sopenharmony_ci pos = str_token(buf, " ", &pos2); 447e5b75505Sopenharmony_ci if (!pos || os_strlen(pos) != 32 || 448e5b75505Sopenharmony_ci hexstr2bin(pos, m->opc, 16)) { 449e5b75505Sopenharmony_ci printf("%s:%d - Invalid OPc\n", fname, line); 450e5b75505Sopenharmony_ci ret = -1; 451e5b75505Sopenharmony_ci break; 452e5b75505Sopenharmony_ci } 453e5b75505Sopenharmony_ci 454e5b75505Sopenharmony_ci /* AMF */ 455e5b75505Sopenharmony_ci pos = str_token(buf, " ", &pos2); 456e5b75505Sopenharmony_ci if (!pos || os_strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { 457e5b75505Sopenharmony_ci printf("%s:%d - Invalid AMF\n", fname, line); 458e5b75505Sopenharmony_ci ret = -1; 459e5b75505Sopenharmony_ci break; 460e5b75505Sopenharmony_ci } 461e5b75505Sopenharmony_ci 462e5b75505Sopenharmony_ci /* SQN */ 463e5b75505Sopenharmony_ci pos = str_token(buf, " ", &pos2); 464e5b75505Sopenharmony_ci if (!pos || os_strlen(pos) != 12 || 465e5b75505Sopenharmony_ci hexstr2bin(pos, m->sqn, 6)) { 466e5b75505Sopenharmony_ci printf("%s:%d - Invalid SEQ\n", fname, line); 467e5b75505Sopenharmony_ci ret = -1; 468e5b75505Sopenharmony_ci break; 469e5b75505Sopenharmony_ci } 470e5b75505Sopenharmony_ci 471e5b75505Sopenharmony_ci pos = str_token(buf, " ", &pos2); 472e5b75505Sopenharmony_ci if (pos) { 473e5b75505Sopenharmony_ci m->res_len = atoi(pos); 474e5b75505Sopenharmony_ci if (m->res_len && 475e5b75505Sopenharmony_ci (m->res_len < EAP_AKA_RES_MIN_LEN || 476e5b75505Sopenharmony_ci m->res_len > EAP_AKA_RES_MAX_LEN)) { 477e5b75505Sopenharmony_ci printf("%s:%d - Invalid RES_len\n", 478e5b75505Sopenharmony_ci fname, line); 479e5b75505Sopenharmony_ci ret = -1; 480e5b75505Sopenharmony_ci break; 481e5b75505Sopenharmony_ci } 482e5b75505Sopenharmony_ci } 483e5b75505Sopenharmony_ci 484e5b75505Sopenharmony_ci m->next = milenage_db; 485e5b75505Sopenharmony_ci milenage_db = m; 486e5b75505Sopenharmony_ci m = NULL; 487e5b75505Sopenharmony_ci } 488e5b75505Sopenharmony_ci os_free(m); 489e5b75505Sopenharmony_ci 490e5b75505Sopenharmony_ci fclose(f); 491e5b75505Sopenharmony_ci 492e5b75505Sopenharmony_ci return ret; 493e5b75505Sopenharmony_ci} 494e5b75505Sopenharmony_ci 495e5b75505Sopenharmony_ci 496e5b75505Sopenharmony_cistatic void update_milenage_file(const char *fname) 497e5b75505Sopenharmony_ci{ 498e5b75505Sopenharmony_ci FILE *f, *f2; 499e5b75505Sopenharmony_ci char name[500], buf[500], *pos; 500e5b75505Sopenharmony_ci char *end = buf + sizeof(buf); 501e5b75505Sopenharmony_ci struct milenage_parameters *m; 502e5b75505Sopenharmony_ci size_t imsi_len; 503e5b75505Sopenharmony_ci 504e5b75505Sopenharmony_ci f = fopen(fname, "r"); 505e5b75505Sopenharmony_ci if (f == NULL) { 506e5b75505Sopenharmony_ci printf("Could not open Milenage data file '%s'\n", fname); 507e5b75505Sopenharmony_ci return; 508e5b75505Sopenharmony_ci } 509e5b75505Sopenharmony_ci 510e5b75505Sopenharmony_ci snprintf(name, sizeof(name), "%s.new", fname); 511e5b75505Sopenharmony_ci f2 = fopen(name, "w"); 512e5b75505Sopenharmony_ci if (f2 == NULL) { 513e5b75505Sopenharmony_ci printf("Could not write Milenage data file '%s'\n", name); 514e5b75505Sopenharmony_ci fclose(f); 515e5b75505Sopenharmony_ci return; 516e5b75505Sopenharmony_ci } 517e5b75505Sopenharmony_ci 518e5b75505Sopenharmony_ci while (fgets(buf, sizeof(buf), f)) { 519e5b75505Sopenharmony_ci /* IMSI Ki OPc AMF SQN */ 520e5b75505Sopenharmony_ci buf[sizeof(buf) - 1] = '\0'; 521e5b75505Sopenharmony_ci 522e5b75505Sopenharmony_ci pos = strchr(buf, ' '); 523e5b75505Sopenharmony_ci if (buf[0] == '#' || pos == NULL || pos - buf >= 20) 524e5b75505Sopenharmony_ci goto no_update; 525e5b75505Sopenharmony_ci 526e5b75505Sopenharmony_ci imsi_len = pos - buf; 527e5b75505Sopenharmony_ci 528e5b75505Sopenharmony_ci for (m = milenage_db; m; m = m->next) { 529e5b75505Sopenharmony_ci if (strncmp(buf, m->imsi, imsi_len) == 0 && 530e5b75505Sopenharmony_ci m->imsi[imsi_len] == '\0') 531e5b75505Sopenharmony_ci break; 532e5b75505Sopenharmony_ci } 533e5b75505Sopenharmony_ci 534e5b75505Sopenharmony_ci if (!m) 535e5b75505Sopenharmony_ci goto no_update; 536e5b75505Sopenharmony_ci 537e5b75505Sopenharmony_ci pos = buf; 538e5b75505Sopenharmony_ci pos += snprintf(pos, end - pos, "%s ", m->imsi); 539e5b75505Sopenharmony_ci pos += wpa_snprintf_hex(pos, end - pos, m->ki, 16); 540e5b75505Sopenharmony_ci *pos++ = ' '; 541e5b75505Sopenharmony_ci pos += wpa_snprintf_hex(pos, end - pos, m->opc, 16); 542e5b75505Sopenharmony_ci *pos++ = ' '; 543e5b75505Sopenharmony_ci pos += wpa_snprintf_hex(pos, end - pos, m->amf, 2); 544e5b75505Sopenharmony_ci *pos++ = ' '; 545e5b75505Sopenharmony_ci pos += wpa_snprintf_hex(pos, end - pos, m->sqn, 6); 546e5b75505Sopenharmony_ci *pos++ = '\n'; 547e5b75505Sopenharmony_ci 548e5b75505Sopenharmony_ci no_update: 549e5b75505Sopenharmony_ci fprintf(f2, "%s", buf); 550e5b75505Sopenharmony_ci } 551e5b75505Sopenharmony_ci 552e5b75505Sopenharmony_ci fclose(f2); 553e5b75505Sopenharmony_ci fclose(f); 554e5b75505Sopenharmony_ci 555e5b75505Sopenharmony_ci snprintf(name, sizeof(name), "%s.bak", fname); 556e5b75505Sopenharmony_ci if (rename(fname, name) < 0) { 557e5b75505Sopenharmony_ci perror("rename"); 558e5b75505Sopenharmony_ci return; 559e5b75505Sopenharmony_ci } 560e5b75505Sopenharmony_ci 561e5b75505Sopenharmony_ci snprintf(name, sizeof(name), "%s.new", fname); 562e5b75505Sopenharmony_ci if (rename(name, fname) < 0) { 563e5b75505Sopenharmony_ci perror("rename"); 564e5b75505Sopenharmony_ci return; 565e5b75505Sopenharmony_ci } 566e5b75505Sopenharmony_ci 567e5b75505Sopenharmony_ci} 568e5b75505Sopenharmony_ci 569e5b75505Sopenharmony_ci 570e5b75505Sopenharmony_cistatic struct milenage_parameters * get_milenage(const char *imsi) 571e5b75505Sopenharmony_ci{ 572e5b75505Sopenharmony_ci struct milenage_parameters *m = milenage_db; 573e5b75505Sopenharmony_ci 574e5b75505Sopenharmony_ci while (m) { 575e5b75505Sopenharmony_ci if (strcmp(m->imsi, imsi) == 0) 576e5b75505Sopenharmony_ci break; 577e5b75505Sopenharmony_ci m = m->next; 578e5b75505Sopenharmony_ci } 579e5b75505Sopenharmony_ci 580e5b75505Sopenharmony_ci#ifdef CONFIG_SQLITE 581e5b75505Sopenharmony_ci if (!m) 582e5b75505Sopenharmony_ci m = db_get_milenage(imsi); 583e5b75505Sopenharmony_ci#endif /* CONFIG_SQLITE */ 584e5b75505Sopenharmony_ci 585e5b75505Sopenharmony_ci return m; 586e5b75505Sopenharmony_ci} 587e5b75505Sopenharmony_ci 588e5b75505Sopenharmony_ci 589e5b75505Sopenharmony_cistatic int sim_req_auth(char *imsi, char *resp, size_t resp_len) 590e5b75505Sopenharmony_ci{ 591e5b75505Sopenharmony_ci int count, max_chal, ret; 592e5b75505Sopenharmony_ci char *pos; 593e5b75505Sopenharmony_ci char *rpos, *rend; 594e5b75505Sopenharmony_ci struct milenage_parameters *m; 595e5b75505Sopenharmony_ci struct gsm_triplet *g; 596e5b75505Sopenharmony_ci 597e5b75505Sopenharmony_ci resp[0] = '\0'; 598e5b75505Sopenharmony_ci 599e5b75505Sopenharmony_ci pos = strchr(imsi, ' '); 600e5b75505Sopenharmony_ci if (pos) { 601e5b75505Sopenharmony_ci *pos++ = '\0'; 602e5b75505Sopenharmony_ci max_chal = atoi(pos); 603e5b75505Sopenharmony_ci if (max_chal < 1 || max_chal > EAP_SIM_MAX_CHAL) 604e5b75505Sopenharmony_ci max_chal = EAP_SIM_MAX_CHAL; 605e5b75505Sopenharmony_ci } else 606e5b75505Sopenharmony_ci max_chal = EAP_SIM_MAX_CHAL; 607e5b75505Sopenharmony_ci 608e5b75505Sopenharmony_ci rend = resp + resp_len; 609e5b75505Sopenharmony_ci rpos = resp; 610e5b75505Sopenharmony_ci ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi); 611e5b75505Sopenharmony_ci if (ret < 0 || ret >= rend - rpos) 612e5b75505Sopenharmony_ci return -1; 613e5b75505Sopenharmony_ci rpos += ret; 614e5b75505Sopenharmony_ci 615e5b75505Sopenharmony_ci m = get_milenage(imsi); 616e5b75505Sopenharmony_ci if (m) { 617e5b75505Sopenharmony_ci u8 _rand[16], sres[4], kc[8]; 618e5b75505Sopenharmony_ci for (count = 0; count < max_chal; count++) { 619e5b75505Sopenharmony_ci if (random_get_bytes(_rand, 16) < 0) 620e5b75505Sopenharmony_ci return -1; 621e5b75505Sopenharmony_ci gsm_milenage(m->opc, m->ki, _rand, sres, kc); 622e5b75505Sopenharmony_ci *rpos++ = ' '; 623e5b75505Sopenharmony_ci rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); 624e5b75505Sopenharmony_ci *rpos++ = ':'; 625e5b75505Sopenharmony_ci rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4); 626e5b75505Sopenharmony_ci *rpos++ = ':'; 627e5b75505Sopenharmony_ci rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16); 628e5b75505Sopenharmony_ci } 629e5b75505Sopenharmony_ci *rpos = '\0'; 630e5b75505Sopenharmony_ci return 0; 631e5b75505Sopenharmony_ci } 632e5b75505Sopenharmony_ci 633e5b75505Sopenharmony_ci count = 0; 634e5b75505Sopenharmony_ci while (count < max_chal && (g = get_gsm_triplet(imsi))) { 635e5b75505Sopenharmony_ci if (strcmp(g->imsi, imsi) != 0) 636e5b75505Sopenharmony_ci continue; 637e5b75505Sopenharmony_ci 638e5b75505Sopenharmony_ci if (rpos < rend) 639e5b75505Sopenharmony_ci *rpos++ = ' '; 640e5b75505Sopenharmony_ci rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8); 641e5b75505Sopenharmony_ci if (rpos < rend) 642e5b75505Sopenharmony_ci *rpos++ = ':'; 643e5b75505Sopenharmony_ci rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4); 644e5b75505Sopenharmony_ci if (rpos < rend) 645e5b75505Sopenharmony_ci *rpos++ = ':'; 646e5b75505Sopenharmony_ci rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16); 647e5b75505Sopenharmony_ci count++; 648e5b75505Sopenharmony_ci } 649e5b75505Sopenharmony_ci 650e5b75505Sopenharmony_ci if (count == 0) { 651e5b75505Sopenharmony_ci printf("No GSM triplets found for %s\n", imsi); 652e5b75505Sopenharmony_ci ret = snprintf(rpos, rend - rpos, " FAILURE"); 653e5b75505Sopenharmony_ci if (ret < 0 || ret >= rend - rpos) 654e5b75505Sopenharmony_ci return -1; 655e5b75505Sopenharmony_ci rpos += ret; 656e5b75505Sopenharmony_ci } 657e5b75505Sopenharmony_ci 658e5b75505Sopenharmony_ci return 0; 659e5b75505Sopenharmony_ci} 660e5b75505Sopenharmony_ci 661e5b75505Sopenharmony_ci 662e5b75505Sopenharmony_cistatic int gsm_auth_req(char *imsi, char *resp, size_t resp_len) 663e5b75505Sopenharmony_ci{ 664e5b75505Sopenharmony_ci int count, ret; 665e5b75505Sopenharmony_ci char *pos, *rpos, *rend; 666e5b75505Sopenharmony_ci struct milenage_parameters *m; 667e5b75505Sopenharmony_ci 668e5b75505Sopenharmony_ci resp[0] = '\0'; 669e5b75505Sopenharmony_ci 670e5b75505Sopenharmony_ci pos = os_strchr(imsi, ' '); 671e5b75505Sopenharmony_ci if (!pos) 672e5b75505Sopenharmony_ci return -1; 673e5b75505Sopenharmony_ci *pos++ = '\0'; 674e5b75505Sopenharmony_ci 675e5b75505Sopenharmony_ci rend = resp + resp_len; 676e5b75505Sopenharmony_ci rpos = resp; 677e5b75505Sopenharmony_ci ret = os_snprintf(rpos, rend - rpos, "GSM-AUTH-RESP %s", imsi); 678e5b75505Sopenharmony_ci if (os_snprintf_error(rend - rpos, ret)) 679e5b75505Sopenharmony_ci return -1; 680e5b75505Sopenharmony_ci rpos += ret; 681e5b75505Sopenharmony_ci 682e5b75505Sopenharmony_ci m = get_milenage(imsi); 683e5b75505Sopenharmony_ci if (m) { 684e5b75505Sopenharmony_ci u8 _rand[16], sres[4], kc[8]; 685e5b75505Sopenharmony_ci for (count = 0; count < EAP_SIM_MAX_CHAL; count++) { 686e5b75505Sopenharmony_ci if (hexstr2bin(pos, _rand, 16) != 0) 687e5b75505Sopenharmony_ci return -1; 688e5b75505Sopenharmony_ci gsm_milenage(m->opc, m->ki, _rand, sres, kc); 689e5b75505Sopenharmony_ci *rpos++ = count == 0 ? ' ' : ':'; 690e5b75505Sopenharmony_ci rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); 691e5b75505Sopenharmony_ci *rpos++ = ':'; 692e5b75505Sopenharmony_ci rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4); 693e5b75505Sopenharmony_ci pos += 16 * 2; 694e5b75505Sopenharmony_ci if (*pos != ':') 695e5b75505Sopenharmony_ci break; 696e5b75505Sopenharmony_ci pos++; 697e5b75505Sopenharmony_ci } 698e5b75505Sopenharmony_ci *rpos = '\0'; 699e5b75505Sopenharmony_ci return 0; 700e5b75505Sopenharmony_ci } 701e5b75505Sopenharmony_ci 702e5b75505Sopenharmony_ci printf("No GSM triplets found for %s\n", imsi); 703e5b75505Sopenharmony_ci ret = os_snprintf(rpos, rend - rpos, " FAILURE"); 704e5b75505Sopenharmony_ci if (os_snprintf_error(rend - rpos, ret)) 705e5b75505Sopenharmony_ci return -1; 706e5b75505Sopenharmony_ci rpos += ret; 707e5b75505Sopenharmony_ci 708e5b75505Sopenharmony_ci return 0; 709e5b75505Sopenharmony_ci} 710e5b75505Sopenharmony_ci 711e5b75505Sopenharmony_ci 712e5b75505Sopenharmony_cistatic void inc_sqn(u8 *sqn) 713e5b75505Sopenharmony_ci{ 714e5b75505Sopenharmony_ci u64 val, seq, ind; 715e5b75505Sopenharmony_ci 716e5b75505Sopenharmony_ci /* 717e5b75505Sopenharmony_ci * SQN = SEQ | IND = SEQ1 | SEQ2 | IND 718e5b75505Sopenharmony_ci * 719e5b75505Sopenharmony_ci * The mechanism used here is not time-based, so SEQ2 is void and 720e5b75505Sopenharmony_ci * SQN = SEQ1 | IND. The length of IND is ind_len bits and the length 721e5b75505Sopenharmony_ci * of SEQ1 is 48 - ind_len bits. 722e5b75505Sopenharmony_ci */ 723e5b75505Sopenharmony_ci 724e5b75505Sopenharmony_ci /* Increment both SEQ and IND by one */ 725e5b75505Sopenharmony_ci val = ((u64) WPA_GET_BE32(sqn) << 16) | ((u64) WPA_GET_BE16(sqn + 4)); 726e5b75505Sopenharmony_ci seq = (val >> ind_len) + 1; 727e5b75505Sopenharmony_ci ind = (val + 1) & ((1 << ind_len) - 1); 728e5b75505Sopenharmony_ci val = (seq << ind_len) | ind; 729e5b75505Sopenharmony_ci WPA_PUT_BE32(sqn, val >> 16); 730e5b75505Sopenharmony_ci WPA_PUT_BE16(sqn + 4, val & 0xffff); 731e5b75505Sopenharmony_ci} 732e5b75505Sopenharmony_ci 733e5b75505Sopenharmony_ci 734e5b75505Sopenharmony_cistatic int aka_req_auth(char *imsi, char *resp, size_t resp_len) 735e5b75505Sopenharmony_ci{ 736e5b75505Sopenharmony_ci /* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */ 737e5b75505Sopenharmony_ci char *pos, *end; 738e5b75505Sopenharmony_ci u8 _rand[EAP_AKA_RAND_LEN]; 739e5b75505Sopenharmony_ci u8 autn[EAP_AKA_AUTN_LEN]; 740e5b75505Sopenharmony_ci u8 ik[EAP_AKA_IK_LEN]; 741e5b75505Sopenharmony_ci u8 ck[EAP_AKA_CK_LEN]; 742e5b75505Sopenharmony_ci u8 res[EAP_AKA_RES_MAX_LEN]; 743e5b75505Sopenharmony_ci size_t res_len; 744e5b75505Sopenharmony_ci int ret; 745e5b75505Sopenharmony_ci struct milenage_parameters *m; 746e5b75505Sopenharmony_ci int failed = 0; 747e5b75505Sopenharmony_ci 748e5b75505Sopenharmony_ci m = get_milenage(imsi); 749e5b75505Sopenharmony_ci if (m) { 750e5b75505Sopenharmony_ci if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0) 751e5b75505Sopenharmony_ci return -1; 752e5b75505Sopenharmony_ci res_len = EAP_AKA_RES_MAX_LEN; 753e5b75505Sopenharmony_ci inc_sqn(m->sqn); 754e5b75505Sopenharmony_ci#ifdef CONFIG_SQLITE 755e5b75505Sopenharmony_ci db_update_milenage_sqn(m); 756e5b75505Sopenharmony_ci#endif /* CONFIG_SQLITE */ 757e5b75505Sopenharmony_ci sqn_changes = 1; 758e5b75505Sopenharmony_ci if (stdout_debug) { 759e5b75505Sopenharmony_ci printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n", 760e5b75505Sopenharmony_ci m->sqn[0], m->sqn[1], m->sqn[2], 761e5b75505Sopenharmony_ci m->sqn[3], m->sqn[4], m->sqn[5]); 762e5b75505Sopenharmony_ci } 763e5b75505Sopenharmony_ci milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand, 764e5b75505Sopenharmony_ci autn, ik, ck, res, &res_len); 765e5b75505Sopenharmony_ci if (m->res_len >= EAP_AKA_RES_MIN_LEN && 766e5b75505Sopenharmony_ci m->res_len <= EAP_AKA_RES_MAX_LEN && 767e5b75505Sopenharmony_ci m->res_len < res_len) 768e5b75505Sopenharmony_ci res_len = m->res_len; 769e5b75505Sopenharmony_ci } else { 770e5b75505Sopenharmony_ci printf("Unknown IMSI: %s\n", imsi); 771e5b75505Sopenharmony_ci#ifdef AKA_USE_FIXED_TEST_VALUES 772e5b75505Sopenharmony_ci printf("Using fixed test values for AKA\n"); 773e5b75505Sopenharmony_ci memset(_rand, '0', EAP_AKA_RAND_LEN); 774e5b75505Sopenharmony_ci memset(autn, '1', EAP_AKA_AUTN_LEN); 775e5b75505Sopenharmony_ci memset(ik, '3', EAP_AKA_IK_LEN); 776e5b75505Sopenharmony_ci memset(ck, '4', EAP_AKA_CK_LEN); 777e5b75505Sopenharmony_ci memset(res, '2', EAP_AKA_RES_MAX_LEN); 778e5b75505Sopenharmony_ci res_len = EAP_AKA_RES_MAX_LEN; 779e5b75505Sopenharmony_ci#else /* AKA_USE_FIXED_TEST_VALUES */ 780e5b75505Sopenharmony_ci failed = 1; 781e5b75505Sopenharmony_ci#endif /* AKA_USE_FIXED_TEST_VALUES */ 782e5b75505Sopenharmony_ci } 783e5b75505Sopenharmony_ci 784e5b75505Sopenharmony_ci pos = resp; 785e5b75505Sopenharmony_ci end = resp + resp_len; 786e5b75505Sopenharmony_ci ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi); 787e5b75505Sopenharmony_ci if (ret < 0 || ret >= end - pos) 788e5b75505Sopenharmony_ci return -1; 789e5b75505Sopenharmony_ci pos += ret; 790e5b75505Sopenharmony_ci if (failed) { 791e5b75505Sopenharmony_ci ret = snprintf(pos, end - pos, "FAILURE"); 792e5b75505Sopenharmony_ci if (ret < 0 || ret >= end - pos) 793e5b75505Sopenharmony_ci return -1; 794e5b75505Sopenharmony_ci pos += ret; 795e5b75505Sopenharmony_ci return 0; 796e5b75505Sopenharmony_ci } 797e5b75505Sopenharmony_ci pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN); 798e5b75505Sopenharmony_ci *pos++ = ' '; 799e5b75505Sopenharmony_ci pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN); 800e5b75505Sopenharmony_ci *pos++ = ' '; 801e5b75505Sopenharmony_ci pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN); 802e5b75505Sopenharmony_ci *pos++ = ' '; 803e5b75505Sopenharmony_ci pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN); 804e5b75505Sopenharmony_ci *pos++ = ' '; 805e5b75505Sopenharmony_ci pos += wpa_snprintf_hex(pos, end - pos, res, res_len); 806e5b75505Sopenharmony_ci 807e5b75505Sopenharmony_ci return 0; 808e5b75505Sopenharmony_ci} 809e5b75505Sopenharmony_ci 810e5b75505Sopenharmony_ci 811e5b75505Sopenharmony_cistatic int aka_auts(char *imsi, char *resp, size_t resp_len) 812e5b75505Sopenharmony_ci{ 813e5b75505Sopenharmony_ci char *auts, *__rand; 814e5b75505Sopenharmony_ci u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6]; 815e5b75505Sopenharmony_ci struct milenage_parameters *m; 816e5b75505Sopenharmony_ci 817e5b75505Sopenharmony_ci resp[0] = '\0'; 818e5b75505Sopenharmony_ci 819e5b75505Sopenharmony_ci /* AKA-AUTS <IMSI> <AUTS> <RAND> */ 820e5b75505Sopenharmony_ci 821e5b75505Sopenharmony_ci auts = strchr(imsi, ' '); 822e5b75505Sopenharmony_ci if (auts == NULL) 823e5b75505Sopenharmony_ci return -1; 824e5b75505Sopenharmony_ci *auts++ = '\0'; 825e5b75505Sopenharmony_ci 826e5b75505Sopenharmony_ci __rand = strchr(auts, ' '); 827e5b75505Sopenharmony_ci if (__rand == NULL) 828e5b75505Sopenharmony_ci return -1; 829e5b75505Sopenharmony_ci *__rand++ = '\0'; 830e5b75505Sopenharmony_ci 831e5b75505Sopenharmony_ci if (stdout_debug) { 832e5b75505Sopenharmony_ci printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", 833e5b75505Sopenharmony_ci imsi, auts, __rand); 834e5b75505Sopenharmony_ci } 835e5b75505Sopenharmony_ci if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) || 836e5b75505Sopenharmony_ci hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) { 837e5b75505Sopenharmony_ci printf("Could not parse AUTS/RAND\n"); 838e5b75505Sopenharmony_ci return -1; 839e5b75505Sopenharmony_ci } 840e5b75505Sopenharmony_ci 841e5b75505Sopenharmony_ci m = get_milenage(imsi); 842e5b75505Sopenharmony_ci if (m == NULL) { 843e5b75505Sopenharmony_ci printf("Unknown IMSI: %s\n", imsi); 844e5b75505Sopenharmony_ci return -1; 845e5b75505Sopenharmony_ci } 846e5b75505Sopenharmony_ci 847e5b75505Sopenharmony_ci if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) { 848e5b75505Sopenharmony_ci printf("AKA-AUTS: Incorrect MAC-S\n"); 849e5b75505Sopenharmony_ci } else { 850e5b75505Sopenharmony_ci memcpy(m->sqn, sqn, 6); 851e5b75505Sopenharmony_ci if (stdout_debug) { 852e5b75505Sopenharmony_ci printf("AKA-AUTS: Re-synchronized: " 853e5b75505Sopenharmony_ci "SQN=%02x%02x%02x%02x%02x%02x\n", 854e5b75505Sopenharmony_ci sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]); 855e5b75505Sopenharmony_ci } 856e5b75505Sopenharmony_ci#ifdef CONFIG_SQLITE 857e5b75505Sopenharmony_ci db_update_milenage_sqn(m); 858e5b75505Sopenharmony_ci#endif /* CONFIG_SQLITE */ 859e5b75505Sopenharmony_ci sqn_changes = 1; 860e5b75505Sopenharmony_ci } 861e5b75505Sopenharmony_ci 862e5b75505Sopenharmony_ci return 0; 863e5b75505Sopenharmony_ci} 864e5b75505Sopenharmony_ci 865e5b75505Sopenharmony_ci 866e5b75505Sopenharmony_cistatic int process_cmd(char *cmd, char *resp, size_t resp_len) 867e5b75505Sopenharmony_ci{ 868e5b75505Sopenharmony_ci if (os_strncmp(cmd, "SIM-REQ-AUTH ", 13) == 0) 869e5b75505Sopenharmony_ci return sim_req_auth(cmd + 13, resp, resp_len); 870e5b75505Sopenharmony_ci 871e5b75505Sopenharmony_ci if (os_strncmp(cmd, "GSM-AUTH-REQ ", 13) == 0) 872e5b75505Sopenharmony_ci return gsm_auth_req(cmd + 13, resp, resp_len); 873e5b75505Sopenharmony_ci 874e5b75505Sopenharmony_ci if (os_strncmp(cmd, "AKA-REQ-AUTH ", 13) == 0) 875e5b75505Sopenharmony_ci return aka_req_auth(cmd + 13, resp, resp_len); 876e5b75505Sopenharmony_ci 877e5b75505Sopenharmony_ci if (os_strncmp(cmd, "AKA-AUTS ", 9) == 0) 878e5b75505Sopenharmony_ci return aka_auts(cmd + 9, resp, resp_len); 879e5b75505Sopenharmony_ci 880e5b75505Sopenharmony_ci printf("Unknown request: %s\n", cmd); 881e5b75505Sopenharmony_ci return -1; 882e5b75505Sopenharmony_ci} 883e5b75505Sopenharmony_ci 884e5b75505Sopenharmony_ci 885e5b75505Sopenharmony_cistatic int process(int s) 886e5b75505Sopenharmony_ci{ 887e5b75505Sopenharmony_ci char buf[1000], resp[1000]; 888e5b75505Sopenharmony_ci struct sockaddr_un from; 889e5b75505Sopenharmony_ci socklen_t fromlen; 890e5b75505Sopenharmony_ci ssize_t res; 891e5b75505Sopenharmony_ci 892e5b75505Sopenharmony_ci fromlen = sizeof(from); 893e5b75505Sopenharmony_ci res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from, 894e5b75505Sopenharmony_ci &fromlen); 895e5b75505Sopenharmony_ci if (res < 0) { 896e5b75505Sopenharmony_ci perror("recvfrom"); 897e5b75505Sopenharmony_ci return -1; 898e5b75505Sopenharmony_ci } 899e5b75505Sopenharmony_ci 900e5b75505Sopenharmony_ci if (res == 0) 901e5b75505Sopenharmony_ci return 0; 902e5b75505Sopenharmony_ci 903e5b75505Sopenharmony_ci if ((size_t) res >= sizeof(buf)) 904e5b75505Sopenharmony_ci res = sizeof(buf) - 1; 905e5b75505Sopenharmony_ci buf[res] = '\0'; 906e5b75505Sopenharmony_ci 907e5b75505Sopenharmony_ci printf("Received: %s\n", buf); 908e5b75505Sopenharmony_ci 909e5b75505Sopenharmony_ci if (process_cmd(buf, resp, sizeof(resp)) < 0) { 910e5b75505Sopenharmony_ci printf("Failed to process request\n"); 911e5b75505Sopenharmony_ci return -1; 912e5b75505Sopenharmony_ci } 913e5b75505Sopenharmony_ci 914e5b75505Sopenharmony_ci if (resp[0] == '\0') { 915e5b75505Sopenharmony_ci printf("No response\n"); 916e5b75505Sopenharmony_ci return 0; 917e5b75505Sopenharmony_ci } 918e5b75505Sopenharmony_ci 919e5b75505Sopenharmony_ci printf("Send: %s\n", resp); 920e5b75505Sopenharmony_ci 921e5b75505Sopenharmony_ci if (sendto(s, resp, os_strlen(resp), 0, (struct sockaddr *) &from, 922e5b75505Sopenharmony_ci fromlen) < 0) 923e5b75505Sopenharmony_ci perror("send"); 924e5b75505Sopenharmony_ci 925e5b75505Sopenharmony_ci return 0; 926e5b75505Sopenharmony_ci} 927e5b75505Sopenharmony_ci 928e5b75505Sopenharmony_ci 929e5b75505Sopenharmony_cistatic void cleanup(void) 930e5b75505Sopenharmony_ci{ 931e5b75505Sopenharmony_ci struct gsm_triplet *g, *gprev; 932e5b75505Sopenharmony_ci struct milenage_parameters *m, *prev; 933e5b75505Sopenharmony_ci 934e5b75505Sopenharmony_ci if (update_milenage && milenage_file && sqn_changes) 935e5b75505Sopenharmony_ci update_milenage_file(milenage_file); 936e5b75505Sopenharmony_ci 937e5b75505Sopenharmony_ci g = gsm_db; 938e5b75505Sopenharmony_ci while (g) { 939e5b75505Sopenharmony_ci gprev = g; 940e5b75505Sopenharmony_ci g = g->next; 941e5b75505Sopenharmony_ci os_free(gprev); 942e5b75505Sopenharmony_ci } 943e5b75505Sopenharmony_ci 944e5b75505Sopenharmony_ci m = milenage_db; 945e5b75505Sopenharmony_ci while (m) { 946e5b75505Sopenharmony_ci prev = m; 947e5b75505Sopenharmony_ci m = m->next; 948e5b75505Sopenharmony_ci os_free(prev); 949e5b75505Sopenharmony_ci } 950e5b75505Sopenharmony_ci 951e5b75505Sopenharmony_ci if (serv_sock >= 0) 952e5b75505Sopenharmony_ci close(serv_sock); 953e5b75505Sopenharmony_ci if (socket_path) 954e5b75505Sopenharmony_ci unlink(socket_path); 955e5b75505Sopenharmony_ci 956e5b75505Sopenharmony_ci#ifdef CONFIG_SQLITE 957e5b75505Sopenharmony_ci if (sqlite_db) { 958e5b75505Sopenharmony_ci sqlite3_close(sqlite_db); 959e5b75505Sopenharmony_ci sqlite_db = NULL; 960e5b75505Sopenharmony_ci } 961e5b75505Sopenharmony_ci#endif /* CONFIG_SQLITE */ 962e5b75505Sopenharmony_ci} 963e5b75505Sopenharmony_ci 964e5b75505Sopenharmony_ci 965e5b75505Sopenharmony_cistatic void handle_term(int sig) 966e5b75505Sopenharmony_ci{ 967e5b75505Sopenharmony_ci printf("Signal %d - terminate\n", sig); 968e5b75505Sopenharmony_ci exit(0); 969e5b75505Sopenharmony_ci} 970e5b75505Sopenharmony_ci 971e5b75505Sopenharmony_ci 972e5b75505Sopenharmony_cistatic void usage(void) 973e5b75505Sopenharmony_ci{ 974e5b75505Sopenharmony_ci printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " 975e5b75505Sopenharmony_ci "database/authenticator\n" 976e5b75505Sopenharmony_ci "Copyright (c) 2005-2017, Jouni Malinen <j@w1.fi>\n" 977e5b75505Sopenharmony_ci "\n" 978e5b75505Sopenharmony_ci "usage:\n" 979e5b75505Sopenharmony_ci "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] " 980e5b75505Sopenharmony_ci "[-m<milenage file>] \\\n" 981e5b75505Sopenharmony_ci " [-D<DB file>] [-i<IND len in bits>] [command]\n" 982e5b75505Sopenharmony_ci "\n" 983e5b75505Sopenharmony_ci "options:\n" 984e5b75505Sopenharmony_ci " -h = show this usage help\n" 985e5b75505Sopenharmony_ci " -u = update SQN in Milenage file on exit\n" 986e5b75505Sopenharmony_ci " -s<socket path> = path for UNIX domain socket\n" 987e5b75505Sopenharmony_ci " (default: %s)\n" 988e5b75505Sopenharmony_ci " -g<triplet file> = path for GSM authentication triplets\n" 989e5b75505Sopenharmony_ci " -m<milenage file> = path for Milenage keys\n" 990e5b75505Sopenharmony_ci " -D<DB file> = path to SQLite database\n" 991e5b75505Sopenharmony_ci " -i<IND len in bits> = IND length for SQN (default: 5)\n" 992e5b75505Sopenharmony_ci "\n" 993e5b75505Sopenharmony_ci "If the optional command argument, like " 994e5b75505Sopenharmony_ci "\"AKA-REQ-AUTH <IMSI>\" is used, a single\n" 995e5b75505Sopenharmony_ci "command is processed with response sent to stdout. Otherwise, " 996e5b75505Sopenharmony_ci "hlr_auc_gw opens\n" 997e5b75505Sopenharmony_ci "a control interface and processes commands sent through it " 998e5b75505Sopenharmony_ci "(e.g., by EAP server\n" 999e5b75505Sopenharmony_ci "in hostapd).\n", 1000e5b75505Sopenharmony_ci default_socket_path); 1001e5b75505Sopenharmony_ci} 1002e5b75505Sopenharmony_ci 1003e5b75505Sopenharmony_ci 1004e5b75505Sopenharmony_ciint main(int argc, char *argv[]) 1005e5b75505Sopenharmony_ci{ 1006e5b75505Sopenharmony_ci int c; 1007e5b75505Sopenharmony_ci char *gsm_triplet_file = NULL; 1008e5b75505Sopenharmony_ci char *sqlite_db_file = NULL; 1009e5b75505Sopenharmony_ci int ret = 0; 1010e5b75505Sopenharmony_ci 1011e5b75505Sopenharmony_ci if (os_program_init()) 1012e5b75505Sopenharmony_ci return -1; 1013e5b75505Sopenharmony_ci 1014e5b75505Sopenharmony_ci socket_path = default_socket_path; 1015e5b75505Sopenharmony_ci 1016e5b75505Sopenharmony_ci for (;;) { 1017e5b75505Sopenharmony_ci c = getopt(argc, argv, "D:g:hi:m:s:u"); 1018e5b75505Sopenharmony_ci if (c < 0) 1019e5b75505Sopenharmony_ci break; 1020e5b75505Sopenharmony_ci switch (c) { 1021e5b75505Sopenharmony_ci case 'D': 1022e5b75505Sopenharmony_ci#ifdef CONFIG_SQLITE 1023e5b75505Sopenharmony_ci sqlite_db_file = optarg; 1024e5b75505Sopenharmony_ci break; 1025e5b75505Sopenharmony_ci#else /* CONFIG_SQLITE */ 1026e5b75505Sopenharmony_ci printf("No SQLite support included in the build\n"); 1027e5b75505Sopenharmony_ci return -1; 1028e5b75505Sopenharmony_ci#endif /* CONFIG_SQLITE */ 1029e5b75505Sopenharmony_ci case 'g': 1030e5b75505Sopenharmony_ci gsm_triplet_file = optarg; 1031e5b75505Sopenharmony_ci break; 1032e5b75505Sopenharmony_ci case 'h': 1033e5b75505Sopenharmony_ci usage(); 1034e5b75505Sopenharmony_ci return 0; 1035e5b75505Sopenharmony_ci case 'i': 1036e5b75505Sopenharmony_ci ind_len = atoi(optarg); 1037e5b75505Sopenharmony_ci if (ind_len < 0 || ind_len > 32) { 1038e5b75505Sopenharmony_ci printf("Invalid IND length\n"); 1039e5b75505Sopenharmony_ci return -1; 1040e5b75505Sopenharmony_ci } 1041e5b75505Sopenharmony_ci break; 1042e5b75505Sopenharmony_ci case 'm': 1043e5b75505Sopenharmony_ci milenage_file = optarg; 1044e5b75505Sopenharmony_ci break; 1045e5b75505Sopenharmony_ci case 's': 1046e5b75505Sopenharmony_ci socket_path = optarg; 1047e5b75505Sopenharmony_ci break; 1048e5b75505Sopenharmony_ci case 'u': 1049e5b75505Sopenharmony_ci update_milenage = 1; 1050e5b75505Sopenharmony_ci break; 1051e5b75505Sopenharmony_ci default: 1052e5b75505Sopenharmony_ci usage(); 1053e5b75505Sopenharmony_ci return -1; 1054e5b75505Sopenharmony_ci } 1055e5b75505Sopenharmony_ci } 1056e5b75505Sopenharmony_ci 1057e5b75505Sopenharmony_ci if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) { 1058e5b75505Sopenharmony_ci usage(); 1059e5b75505Sopenharmony_ci return -1; 1060e5b75505Sopenharmony_ci } 1061e5b75505Sopenharmony_ci 1062e5b75505Sopenharmony_ci#ifdef CONFIG_SQLITE 1063e5b75505Sopenharmony_ci if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL) 1064e5b75505Sopenharmony_ci return -1; 1065e5b75505Sopenharmony_ci#endif /* CONFIG_SQLITE */ 1066e5b75505Sopenharmony_ci 1067e5b75505Sopenharmony_ci if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0) 1068e5b75505Sopenharmony_ci return -1; 1069e5b75505Sopenharmony_ci 1070e5b75505Sopenharmony_ci if (milenage_file && read_milenage(milenage_file) < 0) 1071e5b75505Sopenharmony_ci return -1; 1072e5b75505Sopenharmony_ci 1073e5b75505Sopenharmony_ci if (optind == argc) { 1074e5b75505Sopenharmony_ci serv_sock = open_socket(socket_path); 1075e5b75505Sopenharmony_ci if (serv_sock < 0) 1076e5b75505Sopenharmony_ci return -1; 1077e5b75505Sopenharmony_ci 1078e5b75505Sopenharmony_ci printf("Listening for requests on %s\n", socket_path); 1079e5b75505Sopenharmony_ci 1080e5b75505Sopenharmony_ci atexit(cleanup); 1081e5b75505Sopenharmony_ci signal(SIGTERM, handle_term); 1082e5b75505Sopenharmony_ci signal(SIGINT, handle_term); 1083e5b75505Sopenharmony_ci 1084e5b75505Sopenharmony_ci for (;;) 1085e5b75505Sopenharmony_ci process(serv_sock); 1086e5b75505Sopenharmony_ci } else { 1087e5b75505Sopenharmony_ci char buf[1000]; 1088e5b75505Sopenharmony_ci socket_path = NULL; 1089e5b75505Sopenharmony_ci stdout_debug = 0; 1090e5b75505Sopenharmony_ci if (process_cmd(argv[optind], buf, sizeof(buf)) < 0) { 1091e5b75505Sopenharmony_ci printf("FAIL\n"); 1092e5b75505Sopenharmony_ci ret = -1; 1093e5b75505Sopenharmony_ci } else { 1094e5b75505Sopenharmony_ci printf("%s\n", buf); 1095e5b75505Sopenharmony_ci } 1096e5b75505Sopenharmony_ci cleanup(); 1097e5b75505Sopenharmony_ci } 1098e5b75505Sopenharmony_ci 1099e5b75505Sopenharmony_ci#ifdef CONFIG_SQLITE 1100e5b75505Sopenharmony_ci if (sqlite_db) { 1101e5b75505Sopenharmony_ci sqlite3_close(sqlite_db); 1102e5b75505Sopenharmony_ci sqlite_db = NULL; 1103e5b75505Sopenharmony_ci } 1104e5b75505Sopenharmony_ci#endif /* CONFIG_SQLITE */ 1105e5b75505Sopenharmony_ci 1106e5b75505Sopenharmony_ci os_program_deinit(); 1107e5b75505Sopenharmony_ci 1108e5b75505Sopenharmony_ci return ret; 1109e5b75505Sopenharmony_ci} 1110