diff options
author | Neels Hofmeyr <neels@hofmeyr.de> | 2017-10-10 02:25:00 +0200 |
---|---|---|
committer | Neels Hofmeyr <neels@hofmeyr.de> | 2017-10-11 22:32:19 +0200 |
commit | 1332a17a3db46bae7e2ee720baf21c594e1b2331 (patch) | |
tree | ccac4b3683ba3184f8ee4e06c03823a7782250d0 | |
parent | e50121ec96c2457c66501d7b6e1fcd539ee3c5e1 (diff) |
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
-rw-r--r-- | src/db.c | 8 | ||||
-rw-r--r-- | src/db.h | 29 | ||||
-rw-r--r-- | src/db_hlr.c | 208 | ||||
-rw-r--r-- | tests/db/db_test.c | 280 | ||||
-rw-r--r-- | tests/db/db_test.err | 480 |
5 files changed, 1003 insertions, 2 deletions
@@ -61,6 +61,14 @@ static const char *stmt_sql[] = { [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) @@ -18,6 +18,10 @@ enum stmt_idx { 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 @@ struct hlr_subscriber { 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_create(struct db_context *dbc, const char *imsi) 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 @@ int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id) ": SQL modified %d rows (expected 1)\n", subscr_id, rc); ret = -EIO; } + db_remove_reset(stmt); - /* FIXME: also remove authentication data from auc_2g and auc_3g */ + /* 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; - db_remove_reset(stmt); return ret; } @@ -154,6 +172,192 @@ out: } +/* 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) diff --git a/tests/db/db_test.c b/tests/db/db_test.c index 20553ef..0442dbe 100644 --- a/tests/db/db_test.c +++ b/tests/db/db_test.c @@ -66,9 +66,32 @@ fprintf(stderr, "\n"); \ } while (0) +/* Do db_get_auth_data() and verbosely assert that its return value is as expected. + * Print the subscriber struct to stderr to be validated by db_test.err. + * The results are then available in g_aud2g and g_aud3g. */ +#define ASSERT_SEL_AUD(imsi, expect_rc, expect_id) \ + do { \ + g_aud2g = (struct osmo_sub_auth_data){}; \ + g_aud3g = (struct osmo_sub_auth_data){}; \ + g_id = 0; \ + ASSERT_RC(db_get_auth_data(dbc, imsi, &g_aud2g, &g_aud3g, &g_id), expect_rc); \ + if (g_rc == 1) { \ + dump_aud("2G", &g_aud2g); \ + dump_aud("3G", &g_aud3g); \ + }\ + if (g_id != expect_id) {\ + fprintf(stderr, "MISMATCH: got subscriber id %"PRId64 \ + ", expected %"PRId64"\n", g_id, (int64_t)(expect_id)); \ + OSMO_ASSERT(g_id == expect_id); \ + } \ + fprintf(stderr, "\n"); \ + } while (0) + static struct db_context *dbc = NULL; static void *ctx = NULL; static struct hlr_subscriber g_subscr; +static struct osmo_sub_auth_data g_aud2g; +static struct osmo_sub_auth_data g_aud3g; static int g_rc; static int64_t g_id; @@ -385,6 +408,262 @@ static void test_subscr_create_update_sel_delete() comment_end(); } +static const struct sub_auth_data_str *mk_aud_2g(enum osmo_auth_algo algo, + const char *ki) +{ + static struct sub_auth_data_str aud; + aud = (struct sub_auth_data_str){ + .type = OSMO_AUTH_TYPE_GSM, + .algo = algo, + .u.gsm.ki = ki, + }; + return &aud; +} + +static const struct sub_auth_data_str *mk_aud_3g(enum osmo_auth_algo algo, + const char *opc, bool opc_is_op, + const char *k, unsigned int ind_bitlen) +{ + static struct sub_auth_data_str aud; + aud = (struct sub_auth_data_str){ + .type = OSMO_AUTH_TYPE_UMTS, + .algo = algo, + .u.umts.k = k, + .u.umts.opc = opc, + .u.umts.opc_is_op = opc_is_op ? 1 : 0, + .u.umts.ind_bitlen = ind_bitlen, + }; + return &aud; +} + +static void test_subscr_aud() +{ + int64_t id; + + comment_start(); + + comment("Get auth data for non-existent subscriber"); + ASSERT_SEL_AUD(unknown_imsi, 0, 0); + + comment("Create subscriber"); + + ASSERT_RC(db_subscr_create(dbc, imsi0), 0); + ASSERT_SEL(imsi, imsi0, 0); + + id = g_subscr.id; + ASSERT_SEL_AUD(imsi0, -1, id); + + + comment("Set auth data, 2G only"); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + /* same again */ + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_COMP128v2, "BeadedBeeAced1EbbedDefacedFacade")), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_COMP128v3, "DeafBeddedBabeAcceededFadedDecaf")), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + comment("Remove 2G auth data"); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)), + 0); + ASSERT_SEL_AUD(imsi0, -1, id); + + /* Removing nothing results in -ENOENT */ + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)), + -ENOENT); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_NONE, "f000000000000f00000000000f000000")), + 0); + ASSERT_SEL_AUD(imsi0, -1, id); + + + comment("Set auth data, 3G only"); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "BeefedCafeFaceAcedAddedDecadeFee", true, + "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + /* same again */ + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "BeefedCafeFaceAcedAddedDecadeFee", true, + "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "Deaf0ff1ceD0d0DabbedD1ced1ceF00d", true, + "F1bbed0afD0eF0bD0ffed0ddF1fe0b0e", 0)), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "BeefedCafeFaceAcedAddedDecadeFee", false, + "DeafBeddedBabeAcceededFadedDecaf", + OSMO_MILENAGE_IND_BITLEN_MAX)), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "CededEffacedAceFacedBadFadedBeef", false, + "BeefedCafeFaceAcedAddedDecadeFee", 5)), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + comment("Remove 3G auth data"); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)), + 0); + ASSERT_SEL_AUD(imsi0, -1, id); + + /* Removing nothing results in -ENOENT */ + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)), + -ENOENT); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "CededEffacedAceFacedBadFadedBeef", false, + "BeefedCafeFaceAcedAddedDecadeFee", 5)), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_NONE, + "asdfasdfasd", false, + "asdfasdfasdf", 99999)), + 0); + ASSERT_SEL_AUD(imsi0, -1, id); + + + comment("Set auth data, 2G and 3G"); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_COMP128v3, "CededEffacedAceFacedBadFadedBeef")), + 0); + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "BeefedCafeFaceAcedAddedDecadeFee", false, + "DeafBeddedBabeAcceededFadedDecaf", 5)), + 0); + ASSERT_SEL_AUD(imsi0, 1, id); + + + comment("Set invalid auth data"); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(99999, "f000000000000f00000000000f000000")), + -EINVAL); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_XOR, "f000000000000f00000000000f000000f00000000")), + -EINVAL); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_XOR, "f00")), + -EINVAL); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_2g(OSMO_AUTH_ALG_MILENAGE, "0123456789abcdef0123456789abcdef")), + -EINVAL); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "0f000000000000f00000000000f000000", false, + "f000000000000f00000000000f000000", 5)), + -EINVAL); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "f000000000000f00000000000f000000", false, + "000000000000f00000000000f000000", 5)), + -EINVAL); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "f000000000000f00000000000f000000", false, + "f000000000000f00000000000f000000", + OSMO_MILENAGE_IND_BITLEN_MAX + 1)), + -EINVAL); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "X000000000000f00000000000f000000", false, + "f000000000000f00000000000f000000", 5)), + -EINVAL); + ASSERT_SEL_AUD(imsi0, 1, id); + + ASSERT_RC(db_subscr_update_aud_by_id(dbc, id, + mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, + "f000000000000f00000000000f000000", false, + "f000000000000 f00000000000 f000000", 5)), + -EINVAL); + ASSERT_SEL_AUD(imsi0, 1, id); + + comment("Delete subscriber"); + + ASSERT_SEL(imsi, imsi0, 0); + ASSERT_RC(db_subscr_delete_by_id(dbc, id), 0); + ASSERT_SEL(imsi, imsi0, -ENOENT); + + comment("Re-add subscriber and verify auth data didn't come back"); + + ASSERT_RC(db_subscr_create(dbc, imsi0), 0); + ASSERT_SEL(imsi, imsi0, 0); + + /* For this test to work, we want to get the same subscriber ID back, + * and make sure there are no auth data leftovers for this ID. */ + OSMO_ASSERT(id == g_subscr.id); + ASSERT_SEL_AUD(imsi0, -1, id); + + ASSERT_RC(db_subscr_delete_by_id(dbc, id), 0); + ASSERT_SEL(imsi, imsi0, -ENOENT); + + comment_end(); +} + static struct { bool verbose; } cmdline_opts = { @@ -459,6 +738,7 @@ int main(int argc, char **argv) OSMO_ASSERT(dbc); test_subscr_create_update_sel_delete(); + test_subscr_aud(); printf("Done\n"); return 0; diff --git a/tests/db/db_test.err b/tests/db/db_test.err index 59b9ba1..5d3ab5f 100644 --- a/tests/db/db_test.err +++ b/tests/db/db_test.err @@ -711,3 +711,483 @@ DAUC Cannot read subscriber from db: IMSI='123456': No such subscriber ===== test_subscr_create_update_sel_delete: SUCCESS + +===== test_subscr_aud + +--- Get auth data for non-existent subscriber + +db_get_auth_data(dbc, unknown_imsi, &g_aud2g, &g_aud3g, &g_id) --> 0 +DAUC IMSI='999999999': No such subscriber + + + +--- Create subscriber + +db_subscr_create(dbc, imsi0) --> 0 + +db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0 +struct hlr_subscriber { + .id = 1, + .imsi = '123456789000000', +} + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -1 +DAUC IMSI='123456789000000': No 2G Auth Data +DAUC IMSI='123456789000000': No 3G Auth Data + + + +--- Set auth data, 2G only + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 3G Auth Data + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v1, + .u.gsm.ki = '0123456789abcdef0123456789abcdef', +} +3G: none + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_COMP128v1, "0123456789abcdef0123456789abcdef")) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 3G Auth Data + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v1, + .u.gsm.ki = '0123456789abcdef0123456789abcdef', +} +3G: none + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_COMP128v2, "BeadedBeeAced1EbbedDefacedFacade")) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 3G Auth Data + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v2, + .u.gsm.ki = 'beadedbeeaced1ebbeddefacedfacade', +} +3G: none + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_COMP128v3, "DeafBeddedBabeAcceededFadedDecaf")) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 3G Auth Data + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v3, + .u.gsm.ki = 'deafbeddedbabeacceededfadeddecaf', +} +3G: none + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 3G Auth Data + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = XOR, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: none + + +--- Remove 2G auth data + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -1 +DAUC IMSI='123456789000000': No 2G Auth Data +DAUC IMSI='123456789000000': No 3G Auth Data + + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_NONE, NULL)) --> -ENOENT + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR, "CededEffacedAceFacedBadFadedBeef")) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 3G Auth Data + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = XOR, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: none + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_NONE, "f000000000000f00000000000f000000")) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -1 +DAUC IMSI='123456789000000': No 2G Auth Data +DAUC IMSI='123456789000000': No 3G Auth Data + + + +--- Set auth data, 3G only + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "BeefedCafeFaceAcedAddedDecadeFee", true, "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 2G Auth Data + +2G: none +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 1, + .u.umts.k = 'c01ffedc1cadaeac1d1f1edacac1ab0a', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "BeefedCafeFaceAcedAddedDecadeFee", true, "C01ffedC1cadaeAc1d1f1edAcac1aB0a", 5)) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 2G Auth Data + +2G: none +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 1, + .u.umts.k = 'c01ffedc1cadaeac1d1f1edacac1ab0a', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "Deaf0ff1ceD0d0DabbedD1ced1ceF00d", true, "F1bbed0afD0eF0bD0ffed0ddF1fe0b0e", 0)) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 2G Auth Data + +2G: none +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'deaf0ff1ced0d0dabbedd1ced1cef00d', + .u.umts.opc_is_op = 1, + .u.umts.k = 'f1bbed0afd0ef0bd0ffed0ddf1fe0b0e', + .u.umts.amf = '0000', +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "BeefedCafeFaceAcedAddedDecadeFee", false, "DeafBeddedBabeAcceededFadedDecaf", OSMO_MILENAGE_IND_BITLEN_MAX)) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 2G Auth Data + +2G: none +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 0, + .u.umts.k = 'deafbeddedbabeacceededfadeddecaf', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 28, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "CededEffacedAceFacedBadFadedBeef", false, "BeefedCafeFaceAcedAddedDecadeFee", 5)) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 2G Auth Data + +2G: none +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'cededeffacedacefacedbadfadedbeef', + .u.umts.opc_is_op = 0, + .u.umts.k = 'beefedcafefaceacedaddeddecadefee', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + + +--- Remove 3G auth data + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -1 +DAUC IMSI='123456789000000': No 2G Auth Data +DAUC IMSI='123456789000000': No 3G Auth Data + + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_NONE, NULL, false, NULL, 0)) --> -ENOENT + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "CededEffacedAceFacedBadFadedBeef", false, "BeefedCafeFaceAcedAddedDecadeFee", 5)) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 +DAUC IMSI='123456789000000': No 2G Auth Data + +2G: none +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'cededeffacedacefacedbadfadedbeef', + .u.umts.opc_is_op = 0, + .u.umts.k = 'beefedcafefaceacedaddeddecadefee', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_NONE, "asdfasdfasd", false, "asdfasdfasdf", 99999)) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -1 +DAUC IMSI='123456789000000': No 2G Auth Data +DAUC IMSI='123456789000000': No 3G Auth Data + + + +--- Set auth data, 2G and 3G + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_COMP128v3, "CededEffacedAceFacedBadFadedBeef")) --> 0 + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "BeefedCafeFaceAcedAddedDecadeFee", false, "DeafBeddedBabeAcceededFadedDecaf", 5)) --> 0 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v3, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 0, + .u.umts.k = 'deafbeddedbabeacceededfadeddecaf', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + + +--- Set invalid auth data + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(99999, "f000000000000f00000000000f000000")) --> -EINVAL +DAUC Cannot update auth tokens: Unknown auth algo: 99999 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v3, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 0, + .u.umts.k = 'deafbeddedbabeacceededfadeddecaf', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR, "f000000000000f00000000000f000000f00000000")) --> -EINVAL +DAUC Cannot update auth tokens: Invalid KI: 'f000000000000f00000000000f000000f00000000' + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v3, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 0, + .u.umts.k = 'deafbeddedbabeacceededfadeddecaf', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_XOR, "f00")) --> -EINVAL +DAUC Cannot update auth tokens: Invalid KI: 'f00' + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v3, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 0, + .u.umts.k = 'deafbeddedbabeacceededfadeddecaf', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_2g(OSMO_AUTH_ALG_MILENAGE, "0123456789abcdef0123456789abcdef")) --> -EINVAL +DAUC Cannot update auth tokens: auth algo not suited for 2G: MILENAGE + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v3, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 0, + .u.umts.k = 'deafbeddedbabeacceededfadeddecaf', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "0f000000000000f00000000000f000000", false, "f000000000000f00000000000f000000", 5)) --> -EINVAL +DAUC Cannot update auth tokens: Invalid OP/OPC: '0f000000000000f00000000000f000000' + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v3, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 0, + .u.umts.k = 'deafbeddedbabeacceededfadeddecaf', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "f000000000000f00000000000f000000", false, "000000000000f00000000000f000000", 5)) --> -EINVAL +DAUC Cannot update auth tokens: Invalid K: '000000000000f00000000000f000000' + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v3, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 0, + .u.umts.k = 'deafbeddedbabeacceededfadeddecaf', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "f000000000000f00000000000f000000", false, "f000000000000f00000000000f000000", OSMO_MILENAGE_IND_BITLEN_MAX + 1)) --> -EINVAL +DAUC Cannot update auth tokens: Invalid ind_bitlen: 29 + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v3, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 0, + .u.umts.k = 'deafbeddedbabeacceededfadeddecaf', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "X000000000000f00000000000f000000", false, "f000000000000f00000000000f000000", 5)) --> -EINVAL +DAUC Cannot update auth tokens: Invalid OP/OPC: 'X000000000000f00000000000f000000' + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v3, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 0, + .u.umts.k = 'deafbeddedbabeacceededfadeddecaf', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + +db_subscr_update_aud_by_id(dbc, id, mk_aud_3g(OSMO_AUTH_ALG_MILENAGE, "f000000000000f00000000000f000000", false, "f000000000000 f00000000000 f000000", 5)) --> -EINVAL +DAUC Cannot update auth tokens: Invalid K: 'f000000000000 f00000000000 f000000' + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> 1 + +2G: struct osmo_sub_auth_data { + .type = GSM, + .algo = COMP128v3, + .u.gsm.ki = 'cededeffacedacefacedbadfadedbeef', +} +3G: struct osmo_sub_auth_data { + .type = UMTS, + .algo = MILENAGE, + .u.umts.opc = 'beefedcafefaceacedaddeddecadefee', + .u.umts.opc_is_op = 0, + .u.umts.k = 'deafbeddedbabeacceededfadeddecaf', + .u.umts.amf = '0000', + .u.umts.ind_bitlen = 5, +} + + +--- Delete subscriber + +db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0 +struct hlr_subscriber { + .id = 1, + .imsi = '123456789000000', +} + +db_subscr_delete_by_id(dbc, id) --> 0 + +db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT +DAUC Cannot read subscriber from db: IMSI='123456789000000': No such subscriber + + +--- Re-add subscriber and verify auth data didn't come back + +db_subscr_create(dbc, imsi0) --> 0 + +db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> 0 +struct hlr_subscriber { + .id = 1, + .imsi = '123456789000000', +} + +db_get_auth_data(dbc, imsi0, &g_aud2g, &g_aud3g, &g_id) --> -1 +DAUC IMSI='123456789000000': No 2G Auth Data +DAUC IMSI='123456789000000': No 3G Auth Data + + +db_subscr_delete_by_id(dbc, id) --> 0 + +db_subscr_get_by_imsi(dbc, imsi0, &g_subscr) --> -ENOENT +DAUC Cannot read subscriber from db: IMSI='123456789000000': No such subscriber + +===== test_subscr_aud: SUCCESS + |