/* (C) 2015 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include "logging.h" #include "db.h" #include "auc.h" #include "rand.h" #define LOGAUC(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args) /* update the SQN for a given subscriber ID */ int db_update_sqn(struct db_context *dbc, int64_t subscr_id, uint64_t new_sqn) { sqlite3_stmt *stmt = dbc->stmt[DB_STMT_AUC_UPD_SQN]; int rc; int ret = 0; if (!db_bind_int64(stmt, "$sqn", new_sqn)) return -EIO; if (!db_bind_int64(stmt, "$subscriber_id", subscr_id)) return -EIO; /* execute the statement */ rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) { LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%"PRId64 ": SQL error: (%d) %s\n", subscr_id, rc, sqlite3_errmsg(dbc->db)); ret = -EIO; goto out; } /* verify execution result */ rc = sqlite3_changes(dbc->db); if (!rc) { LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%"PRId64 ": no auc_3g entry for such subscriber\n", subscr_id); ret = -ENOENT; } else if (rc != 1) { LOGP(DAUC, LOGL_ERROR, "Update SQN for subscriber ID=%"PRId64 ": SQL modified %d rows (expected 1)\n", subscr_id, rc); ret = -EIO; } out: db_remove_reset(stmt); return ret; } /* obtain the authentication data for a given imsi * returns -1 in case of error, 0 for unknown IMSI, 1 for success */ int db_get_auth_data(struct db_context *dbc, const char *imsi, struct osmo_sub_auth_data *aud2g, struct osmo_sub_auth_data *aud3g, int64_t *subscr_id) { sqlite3_stmt *stmt = dbc->stmt[DB_STMT_AUC_BY_IMSI]; int ret = 0; int rc; memset(aud2g, 0, sizeof(*aud2g)); memset(aud3g, 0, sizeof(*aud3g)); if (!db_bind_text(stmt, "$imsi", imsi)) return -EIO; /* execute the statement */ rc = sqlite3_step(stmt); if (rc == SQLITE_DONE) { LOGAUC(imsi, LOGL_INFO, "No such subscriber\n"); ret = -ENOENT; goto out; } else if (rc != SQLITE_ROW) { LOGAUC(imsi, LOGL_ERROR, "Error executing SQL: %d\n", rc); ret = -EIO; goto out; } /* as an optimization, we retrieve the subscriber ID, to ensure we can * update the SQN later without having to go back via a JOIN with the * subscriber table. */ if (subscr_id) *subscr_id = sqlite3_column_int64(stmt, 0); /* obtain result values using sqlite3_column_*() */ if (sqlite3_column_type(stmt, 1) == SQLITE_INTEGER) { /* we do have some 2G authentication data */ const uint8_t *ki; aud2g->algo = sqlite3_column_int(stmt, 1); ki = sqlite3_column_text(stmt, 2); #if 0 if (sqlite3_column_bytes(stmt, 2) != sizeof(aud2g->u.gsm.ki)) { LOGAUC(imsi, LOGL_ERROR, "Error reading Ki: %d\n", rc); goto end_2g; } #endif osmo_hexparse((void*)ki, (void*)&aud2g->u.gsm.ki, sizeof(aud2g->u.gsm.ki)); aud2g->type = OSMO_AUTH_TYPE_GSM; } else LOGAUC(imsi, LOGL_DEBUG, "No 2G Auth Data\n"); //end_2g: if (sqlite3_column_type(stmt, 3) == SQLITE_INTEGER) { /* we do have some 3G authentication data */ const uint8_t *k, *op, *opc; aud3g->algo = sqlite3_column_int(stmt, 3); k = sqlite3_column_text(stmt, 4); if (!k) { LOGAUC(imsi, LOGL_ERROR, "Error reading K: %d\n", rc); ret = -EIO; goto out; } osmo_hexparse((void*)k, (void*)&aud3g->u.umts.k, sizeof(aud3g->u.umts.k)); /* UMTS Subscribers can have either OP or OPC */ op = sqlite3_column_text(stmt, 5); if (!op) { opc = sqlite3_column_text(stmt, 6); if (!opc) { LOGAUC(imsi, LOGL_ERROR, "Error reading OPC: %d\n", rc); ret = -EIO; goto out; } osmo_hexparse((void*)opc, (void*)&aud3g->u.umts.opc, sizeof(aud3g->u.umts.opc)); aud3g->u.umts.opc_is_op = 0; } else { osmo_hexparse((void*)op, (void*)&aud3g->u.umts.opc, sizeof(aud3g->u.umts.opc)); aud3g->u.umts.opc_is_op = 1; } aud3g->u.umts.sqn = sqlite3_column_int64(stmt, 7); aud3g->u.umts.ind_bitlen = sqlite3_column_int(stmt, 8); /* FIXME: amf? */ aud3g->type = OSMO_AUTH_TYPE_UMTS; } else LOGAUC(imsi, LOGL_DEBUG, "No 3G Auth Data\n"); if (aud2g->type == 0 && aud3g->type == 0) ret = -ENOENT; out: db_remove_reset(stmt); return ret; } /* return -1 in case of error, 0 for unknown imsi, positive for number * of vectors generated */ int db_get_auc(struct db_context *dbc, const char *imsi, unsigned int auc_3g_ind, struct osmo_auth_vector *vec, unsigned int num_vec, const uint8_t *rand_auts, const uint8_t *auts) { struct osmo_sub_auth_data aud2g, aud3g; int64_t subscr_id; int ret = 0; int rc; rc = db_get_auth_data(dbc, imsi, &aud2g, &aud3g, &subscr_id); if (rc) return rc; aud3g.u.umts.ind = auc_3g_ind; if (aud3g.type == OSMO_AUTH_TYPE_UMTS && aud3g.u.umts.ind >= (1U << aud3g.u.umts.ind_bitlen)) { LOGAUC(imsi, LOGL_NOTICE, "3G auth: SQN's IND bitlen %u is" " too small to hold an index of %u. Truncating. This" " may cause numerous additional AUTS resyncing.\n", aud3g.u.umts.ind_bitlen, aud3g.u.umts.ind); aud3g.u.umts.ind &= (1U << aud3g.u.umts.ind_bitlen) - 1; } LOGAUC(imsi, LOGL_DEBUG, "Calling to generate %u vectors\n", num_vec); rc = auc_compute_vectors(vec, num_vec, &aud2g, &aud3g, rand_auts, auts); if (rc < 0) { num_vec = 0; ret = -1; } else { num_vec = rc; ret = num_vec; } LOGAUC(imsi, LOGL_INFO, "Generated %u vectors\n", num_vec); /* Update SQN in database, as needed */ if (aud3g.algo) { LOGAUC(imsi, LOGL_DEBUG, "Updating SQN=%" PRIu64 " in DB\n", aud3g.u.umts.sqn); rc = db_update_sqn(dbc, subscr_id, aud3g.u.umts.sqn); /* don't tell caller we generated any triplets in case of * update error */ if (rc < 0) { LOGAUC(imsi, LOGL_ERROR, "Error updating SQN: %d\n", rc); num_vec = 0; ret = -1; } } return ret; }