add db_subscr_update_aud_by_id(), complete db_subscr_delete_by_id()
Add ability to add and remove auc_2g and auc_3g table rows with
db_subscr_update_aud_by_id().
In db_subscr_delete_by_id(), make sure that when deleting a subscriber, also
all auth data associated with that user ID is removed as well. A newly created
subscriber must not obtain the same auth tokens just by getting the same id.
Depends: libosmocore Idf75946eb0a84e145adad13fc7c78bb7a267aa0a
Change-Id: Icb11b5e059fb920447a9aa414db1819a0c020529
diff --git a/src/db.c b/src/db.c
index 2b2c2c4..4136e39 100644
--- a/src/db.c
+++ b/src/db.c
@@ -61,6 +61,14 @@
[DB_STMT_SUBSCR_CREATE] = "INSERT INTO subscriber (imsi) VALUES ($imsi)",
[DB_STMT_DEL_BY_ID] = "DELETE FROM subscriber WHERE id = $subscriber_id",
[DB_STMT_SET_MSISDN_BY_IMSI] = "UPDATE subscriber SET msisdn = $msisdn WHERE imsi = $imsi",
+ [DB_STMT_AUC_2G_INSERT] =
+ "INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki)"
+ " VALUES($subscriber_id, $algo_id_2g, $ki)",
+ [DB_STMT_AUC_2G_DELETE] = "DELETE FROM auc_2g WHERE subscriber_id = $subscriber_id",
+ [DB_STMT_AUC_3G_INSERT] =
+ "INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)"
+ " VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)",
+ [DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id",
};
static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
diff --git a/src/db.h b/src/db.h
index 2e6cc9b..f6aaa58 100644
--- a/src/db.h
+++ b/src/db.h
@@ -18,6 +18,10 @@
DB_STMT_SUBSCR_CREATE,
DB_STMT_DEL_BY_ID,
DB_STMT_SET_MSISDN_BY_IMSI,
+ DB_STMT_AUC_2G_INSERT,
+ DB_STMT_AUC_2G_DELETE,
+ DB_STMT_AUC_3G_INSERT,
+ DB_STMT_AUC_3G_DELETE,
_NUM_DB_STMT
};
@@ -78,11 +82,36 @@
bool ms_purged_ps;
};
+/* Like struct osmo_sub_auth_data, but the keys are in hexdump representation.
+ * This is useful because SQLite requires them in hexdump format, and callers
+ * like the VTY and CTRL interface also have them available as hexdump to begin
+ * with. In the binary format, a VTY command would first need to hexparse,
+ * after which the db function would again hexdump, copying to separate
+ * buffers. The roundtrip can be saved by providing char* to begin with. */
+struct sub_auth_data_str {
+ enum osmo_sub_auth_type type;
+ enum osmo_auth_algo algo;
+ union {
+ struct {
+ const char *opc;
+ const char *k;
+ uint64_t sqn;
+ int opc_is_op;
+ unsigned int ind_bitlen;
+ } umts;
+ struct {
+ const char *ki;
+ } gsm;
+ } u;
+};
+
int db_subscr_create(struct db_context *dbc, const char *imsi);
int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id);
int db_subscr_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi,
const char *msisdn);
+int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
+ const struct sub_auth_data_str *aud);
int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
struct hlr_subscriber *subscr);
diff --git a/src/db_hlr.c b/src/db_hlr.c
index b6d224b..71f682d 100644
--- a/src/db_hlr.c
+++ b/src/db_hlr.c
@@ -71,6 +71,7 @@
int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
{
int rc;
+ struct sub_auth_data_str aud;
int ret = 0;
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_DEL_BY_ID];
@@ -99,10 +100,27 @@
": SQL modified %d rows (expected 1)\n", subscr_id, rc);
ret = -EIO;
}
-
- /* FIXME: also remove authentication data from auc_2g and auc_3g */
-
db_remove_reset(stmt);
+
+ /* make sure to remove authentication data for this subscriber id, for
+ * both 2G and 3G. */
+
+ aud = (struct sub_auth_data_str){
+ .type = OSMO_AUTH_TYPE_GSM,
+ .algo = OSMO_AUTH_ALG_NONE,
+ };
+ rc = db_subscr_update_aud_by_id(dbc, subscr_id, &aud);
+ if (ret == -ENOENT && !rc)
+ ret = 0;
+
+ aud = (struct sub_auth_data_str){
+ .type = OSMO_AUTH_TYPE_UMTS,
+ .algo = OSMO_AUTH_ALG_NONE,
+ };
+ rc = db_subscr_update_aud_by_id(dbc, subscr_id, &aud);
+ if (ret == -ENOENT && !rc)
+ ret = 0;
+
return ret;
}
@@ -154,6 +172,192 @@
}
+/* Insert or update 2G or 3G authentication tokens in the database.
+ * If aud->type is OSMO_AUTH_TYPE_GSM, the auc_2g table entry for the
+ * subscriber will be added or modified; if aud->algo is OSMO_AUTH_ALG_NONE,
+ * however, the auc_2g entry for the subscriber is deleted. If aud->type is
+ * OSMO_AUTH_TYPE_UMTS, the auc_3g table is updated; again, if aud->algo is
+ * OSMO_AUTH_ALG_NONE, the auc_3g entry is deleted.
+ * Returns 0 if successful, -EINVAL for unknown aud->type, -ENOENT for unknown
+ * subscr_id, -EIO for SQL errors.
+ */
+int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
+ const struct sub_auth_data_str *aud)
+{
+ sqlite3_stmt *stmt_del;
+ sqlite3_stmt *stmt_ins;
+ sqlite3_stmt *stmt;
+ const char *label;
+ int rc;
+ int ret = 0;
+
+ switch (aud->type) {
+ case OSMO_AUTH_TYPE_GSM:
+ label = "auc_2g";
+ stmt_del = dbc->stmt[DB_STMT_AUC_2G_DELETE];
+ stmt_ins = dbc->stmt[DB_STMT_AUC_2G_INSERT];
+
+ switch (aud->algo) {
+ case OSMO_AUTH_ALG_NONE:
+ case OSMO_AUTH_ALG_COMP128v1:
+ case OSMO_AUTH_ALG_COMP128v2:
+ case OSMO_AUTH_ALG_COMP128v3:
+ case OSMO_AUTH_ALG_XOR:
+ break;
+ case OSMO_AUTH_ALG_MILENAGE:
+ LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
+ " auth algo not suited for 2G: %s\n",
+ osmo_auth_alg_name(aud->algo));
+ return -EINVAL;
+ default:
+ LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
+ " Unknown auth algo: %d\n", aud->algo);
+ return -EINVAL;
+ }
+
+ if (aud->algo == OSMO_AUTH_ALG_NONE)
+ break;
+ if (!osmo_is_hexstr(aud->u.gsm.ki, 32, 32, true)) {
+ LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
+ " Invalid KI: '%s'\n", aud->u.gsm.ki);
+ return -EINVAL;
+ }
+ break;
+
+ case OSMO_AUTH_TYPE_UMTS:
+ label = "auc_3g";
+ stmt_del = dbc->stmt[DB_STMT_AUC_3G_DELETE];
+ stmt_ins = dbc->stmt[DB_STMT_AUC_3G_INSERT];
+ switch (aud->algo) {
+ case OSMO_AUTH_ALG_NONE:
+ case OSMO_AUTH_ALG_MILENAGE:
+ break;
+ case OSMO_AUTH_ALG_COMP128v1:
+ case OSMO_AUTH_ALG_COMP128v2:
+ case OSMO_AUTH_ALG_COMP128v3:
+ case OSMO_AUTH_ALG_XOR:
+ LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
+ " auth algo not suited for 3G: %s\n",
+ osmo_auth_alg_name(aud->algo));
+ return -EINVAL;
+ default:
+ LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
+ " Unknown auth algo: %d\n", aud->algo);
+ return -EINVAL;
+ }
+
+ if (aud->algo == OSMO_AUTH_ALG_NONE)
+ break;
+ if (!osmo_is_hexstr(aud->u.umts.k, 32, 32, true)) {
+ LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
+ " Invalid K: '%s'\n", aud->u.umts.k);
+ return -EINVAL;
+ }
+ if (!osmo_is_hexstr(aud->u.umts.opc, 32, 32, true)) {
+ LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
+ " Invalid OP/OPC: '%s'\n", aud->u.umts.opc);
+ return -EINVAL;
+ }
+ if (aud->u.umts.ind_bitlen > OSMO_MILENAGE_IND_BITLEN_MAX) {
+ LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
+ " Invalid ind_bitlen: %d\n", aud->u.umts.ind_bitlen);
+ return -EINVAL;
+ }
+ break;
+ default:
+ LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
+ " unknown auth type: %d\n", aud->type);
+ return -EINVAL;
+ }
+
+ stmt = stmt_del;
+
+ 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 delete %s row: SQL error: (%d) %s\n",
+ label, rc, sqlite3_errmsg(dbc->db));
+ ret = -EIO;
+ goto out;
+ }
+
+ /* verify execution result */
+ rc = sqlite3_changes(dbc->db);
+ if (!rc)
+ /* Leave "no such entry" logging to the caller -- during
+ * db_subscr_delete_by_id(), we call this to make sure it is
+ * empty, and no entry is not an error then.*/
+ ret = -ENOENT;
+ else if (rc != 1) {
+ LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%"PRId64
+ " from %s: SQL modified %d rows (expected 1)\n",
+ subscr_id, label, rc);
+ ret = -EIO;
+ }
+
+ db_remove_reset(stmt);
+
+ /* Error situation? Return now. */
+ if (ret && ret != -ENOENT)
+ return ret;
+
+ /* Just delete requested? */
+ if (aud->algo == OSMO_AUTH_ALG_NONE)
+ return ret;
+
+ /* Don't return -ENOENT if inserting new data. */
+ ret = 0;
+
+ /* Insert new row */
+ stmt = stmt_ins;
+
+ if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
+ return -EIO;
+
+ switch (aud->type) {
+ case OSMO_AUTH_TYPE_GSM:
+ if (!db_bind_int(stmt, "$algo_id_2g", aud->algo))
+ return -EIO;
+ if (!db_bind_text(stmt, "$ki", aud->u.gsm.ki))
+ return -EIO;
+ break;
+ case OSMO_AUTH_TYPE_UMTS:
+ if (!db_bind_int(stmt, "$algo_id_3g", aud->algo))
+ return -EIO;
+ if (!db_bind_text(stmt, "$k", aud->u.umts.k))
+ return -EIO;
+ if (!db_bind_text(stmt, "$op",
+ aud->u.umts.opc_is_op ? aud->u.umts.opc : NULL))
+ return -EIO;
+ if (!db_bind_text(stmt, "$opc",
+ aud->u.umts.opc_is_op ? NULL : aud->u.umts.opc))
+ return -EIO;
+ if (!db_bind_int(stmt, "$ind_bitlen", aud->u.umts.ind_bitlen))
+ return -EIO;
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+
+ /* execute the statement */
+ rc = sqlite3_step(stmt);
+ if (rc != SQLITE_DONE) {
+ LOGP(DAUC, LOGL_ERROR,
+ "Cannot insert %s row: SQL error: (%d) %s\n",
+ label, rc, sqlite3_errmsg(dbc->db));
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ db_remove_reset(stmt);
+ return ret;
+}
+
/* Common code for db_subscr_get_by_*() functions. */
static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscriber *subscr,
const char **err)