From 38160d6c3f8f9be8b88b06e9f8a22e58d36ab6db Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 30 May 2023 18:28:15 +0200 Subject: [PATCH] db: extend database schema to support 256bit K and/or OP[c] values Other UMTS AKA algorithms than MILENAGE (notably TUAK) support K sizes of up to 256bit, or mandate a OP/OPc size of 256 bit. Let's extend our database schema to accommodate such larger sizes. Change-Id: Ibbde68484c904507a15c35cbfdf88cd47d0c7039 --- sql/hlr.sql | 8 ++--- src/ctrl.c | 12 ++++---- src/db.c | 45 +++++++++++++++++++++++++++-- src/hlr_vty_subscr.c | 32 +++++++++++++------- tests/db_upgrade/db_upgrade_test.ok | 9 +++--- tests/test_subscriber.vty | 4 +-- 6 files changed, 81 insertions(+), 29 deletions(-) diff --git a/sql/hlr.sql b/sql/hlr.sql index e855a6c7..4c87f43a 100644 --- a/sql/hlr.sql +++ b/sql/hlr.sql @@ -71,9 +71,9 @@ CREATE TABLE auc_2g ( CREATE TABLE auc_3g ( subscriber_id INTEGER PRIMARY KEY, -- subscriber.id algo_id_3g INTEGER NOT NULL, -- enum osmo_auth_algo value - k VARCHAR(32) NOT NULL, -- hex string: subscriber's secret key (128bit) - op VARCHAR(32), -- hex string: operator's secret key (128bit) - opc VARCHAR(32), -- hex string: derived from OP and K (128bit) + k VARCHAR(64) NOT NULL, -- hex string: subscriber's secret key (128/256bit) + op VARCHAR(64), -- hex string: operator's secret key (128/256bit) + opc VARCHAR(64), -- hex string: derived from OP and K (128/256bit) sqn INTEGER NOT NULL DEFAULT 0, -- sequence number of key usage -- nr of index bits at lower SQN end ind_bitlen INTEGER NOT NULL DEFAULT 5 @@ -91,4 +91,4 @@ CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi); -- Set HLR database schema version number -- Note: This constant is currently duplicated in src/db.c and must be kept in sync! -PRAGMA user_version = 6; +PRAGMA user_version = 7; diff --git a/src/ctrl.c b/src/ctrl.c index f0f2ee66..e04195d3 100644 --- a/src/ctrl.c +++ b/src/ctrl.c @@ -39,7 +39,7 @@ #define SEL_BY_ID SEL_BY "id-" extern bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo, - int *minlen, int *maxlen); + int *minlen, int *maxlen, int *minlen_opc, int *maxlen_opc); #define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf)) @@ -550,7 +550,7 @@ static int set_subscr_aud2g(struct ctrl_cmd *cmd, void *data) } if (strcmp(tok, "none") == 0) { aud2g.algo = OSMO_AUTH_ALG_NONE; - } else if (!auth_algo_parse(tok, &aud2g.algo, &minlen, &maxlen)) { + } else if (!auth_algo_parse(tok, &aud2g.algo, &minlen, &maxlen, NULL, NULL)) { cmd->reply = "Unknown auth algorithm."; return CTRL_CMD_ERROR; } @@ -630,8 +630,8 @@ static int set_subscr_aud3g(struct ctrl_cmd *cmd, void *data) struct hlr *hlr = data; const char *by_selector = cmd->node; char *tmp = NULL, *tok, *saveptr; - int minlen = 0; - int maxlen = 0; + int minlen = 0, minlen_opc = 0; + int maxlen = 0, maxlen_opc = 0; struct sub_auth_data_str aud3g = { .type = OSMO_AUTH_TYPE_UMTS, .u.umts = { @@ -657,7 +657,7 @@ static int set_subscr_aud3g(struct ctrl_cmd *cmd, void *data) } if (strcmp(tok, "none") == 0) { aud3g.algo = OSMO_AUTH_ALG_NONE; - } else if (!auth_algo_parse(tok, &aud3g.algo, &minlen, &maxlen)) { + } else if (!auth_algo_parse(tok, &aud3g.algo, &minlen, &maxlen, &minlen_opc, &maxlen_opc)) { cmd->reply = "Unknown auth algorithm."; return CTRL_CMD_ERROR; } @@ -699,7 +699,7 @@ static int set_subscr_aud3g(struct ctrl_cmd *cmd, void *data) } aud3g.u.umts.opc = tok; - if (!osmo_is_hexstr(aud3g.u.umts.opc, MILENAGE_KEY_LEN * 2, MILENAGE_KEY_LEN * 2, true)) { + if (!osmo_is_hexstr(aud3g.u.umts.opc, minlen_opc * 2, maxlen_opc * 2, true)) { cmd->reply = talloc_asprintf(cmd, "Invalid OP/OPC."); return CTRL_CMD_ERROR; } diff --git a/src/db.c b/src/db.c index 23dcbbcd..19dfcee4 100644 --- a/src/db.c +++ b/src/db.c @@ -1,4 +1,4 @@ -/* (C) 2015 by Harald Welte +/* (C) 2015-2023 by Harald Welte * * All Rights Reserved * @@ -28,7 +28,7 @@ #include "db_bootstrap.h" /* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */ -#define CURRENT_SCHEMA_VERSION 6 +#define CURRENT_SCHEMA_VERSION 7 #define SEL_COLUMNS \ "id," \ @@ -513,6 +513,46 @@ static int db_upgrade_v6(struct db_context *dbc) return rc; } +static int db_upgrade_v7(struct db_context *dbc) +{ + int rc; + /* SQLite doesn't allow us to change the column type in-place, so we + * first rename the old table, create a new table and then copy + * the data over before deleting the old table */ +#define CREATE_AUC_3G_V7 \ +"CREATE TABLE auc_3g (\n" \ +" subscriber_id INTEGER PRIMARY KEY, -- subscriber.id\n" \ +" algo_id_3g INTEGER NOT NULL, -- enum osmo_auth_algo value\n" \ +" k VARCHAR(64) NOT NULL, -- hex string: subscriber's secret key (128/256bit)\n" \ +" op VARCHAR(64), -- hex string: operator's secret key (128/256bit)\n" \ +" opc VARCHAR(64), -- hex string: derived from OP and K (128/256bit)\n" \ +" sqn INTEGER NOT NULL DEFAULT 0, -- sequence number of key usage\n" \ +" -- nr of index bits at lower SQN end\n" \ +" ind_bitlen INTEGER NOT NULL DEFAULT 5\n" \ +");" + static const char *statements[] = { + "BEGIN TRANSACTION", + /* rename old table */ + "ALTER TABLE auc_3g RENAME TO old_auc_3g", + /* create new table */ + CREATE_AUC_3G_V7, + /* copy over old data */ + "INSERT INTO auc_3g SELECT subscriber_id, algo_id_3g, k, op, opc,sqn, ind_bitlen FROM old_auc_3g", + /* delete old table */ + "DROP TABLE old_auc_3g", + /* update user_version */ + "PRAGMA user_version = 7", + "COMMIT", + }; + + rc = db_run_statements(dbc, statements, ARRAY_SIZE(statements)); + if (rc != SQLITE_DONE) { + LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 6\n"); + return rc; + } + return rc; +} + typedef int (*db_upgrade_func_t)(struct db_context *dbc); static db_upgrade_func_t db_upgrade_path[] = { db_upgrade_v1, @@ -521,6 +561,7 @@ static db_upgrade_func_t db_upgrade_path[] = { db_upgrade_v4, db_upgrade_v5, db_upgrade_v6, + db_upgrade_v7, }; static int db_get_user_version(struct db_context *dbc) diff --git a/src/hlr_vty_subscr.c b/src/hlr_vty_subscr.c index 191a87d1..708359a6 100644 --- a/src/hlr_vty_subscr.c +++ b/src/hlr_vty_subscr.c @@ -473,8 +473,15 @@ static bool is_hexkey_valid(struct vty *vty, const char *label, "Use Milenage algorithm\n" bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo, - int *minlen, int *maxlen) + int *minlen, int *maxlen, int *minlen_opc, int *maxlen_opc) { + /* Default: no OP[c]. True for all 2G algorithms, and 3G-XOR. Overridden + * below for real 3G AKA algorithms. */ + if (minlen_opc) + *minlen_opc = 0; + if (maxlen_opc) + *maxlen_opc = 0; + if (!strcasecmp(alg_str, "none")) { *algo = OSMO_AUTH_ALG_NONE; *minlen = *maxlen = 0; @@ -497,6 +504,10 @@ bool auth_algo_parse(const char *alg_str, enum osmo_auth_algo *algo, } else if (!strcasecmp(alg_str, "milenage")) { *algo = OSMO_AUTH_ALG_MILENAGE; *minlen = *maxlen = MILENAGE_KEY_LEN; + if (minlen_opc) + *minlen_opc = MILENAGE_KEY_LEN; + if (maxlen_opc) + *maxlen_opc = MILENAGE_KEY_LEN; } else return false; return true; @@ -552,7 +563,7 @@ DEFUN(subscriber_aud2g, .u.gsm.ki = ki, }; - if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen)) { + if (!auth_algo_parse(alg_type, &aud2g.algo, &minlen, &maxlen, NULL, NULL)) { vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE); return CMD_WARNING; } @@ -611,13 +622,13 @@ DEFUN(subscriber_aud3g, SUBSCR_UPDATE_HELP "Set UMTS authentication data (3G, and 2G with UMTS AKA)\n" AUTH_ALG_TYPES_3G_HELP - "Set Encryption Key K\n" "K as 32 hexadecimal characters\n" - "Set OP key\n" "Set OPC key\n" "OP or OPC as 32 hexadecimal characters\n" + "Set Encryption Key K\n" "K as 32/64 hexadecimal characters\n" + "Set OP key\n" "Set OPC key\n" "OP or OPC as 32/64 hexadecimal characters\n" "Set IND bit length\n" "IND bit length value (default: 5)\n") { struct hlr_subscriber subscr; - int minlen = 0; - int maxlen = 0; + int minlen = 0, minlen_opc = 0; + int maxlen = 0, maxlen_opc = 0; int rc; const char *id_type = argv[0]; const char *id = argv[1]; @@ -636,7 +647,7 @@ DEFUN(subscriber_aud3g, }, }; - if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen)) { + if (!auth_algo_parse(alg_type, &aud3g.algo, &minlen, &maxlen, &minlen_opc, &maxlen_opc)) { vty_out(vty, "%% Unknown auth algorithm: '%s'%s", alg_type, VTY_NEWLINE); return CMD_WARNING; } @@ -644,8 +655,7 @@ DEFUN(subscriber_aud3g, if (!is_hexkey_valid(vty, "K", aud3g.u.umts.k, minlen, maxlen)) return CMD_WARNING; - if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc, - MILENAGE_KEY_LEN, MILENAGE_KEY_LEN)) + if (!is_hexkey_valid(vty, opc_is_op ? "OP" : "OPC", aud3g.u.umts.opc, minlen_opc, maxlen_opc)) return CMD_WARNING; if (get_subscr_by_argv(vty, id_type, id, &subscr)) @@ -689,8 +699,8 @@ DEFUN(subscriber_aud3g_xor, }, }; - if (!auth_algo_parse("xor-3g", &aud3g.algo, &minlen, &maxlen)) { - vty_out(vty, "%% Unknown auth algorithm: '%s'%s", "xor-3g", VTY_NEWLINE); + if (!auth_algo_parse("xor-3g", &aud3g.algo, &minlen, &maxlen, NULL, NULL)) { + vty_out(vty, "%% Unknown auth algorithm: '%s'%s", "xor", VTY_NEWLINE); return CMD_WARNING; } diff --git a/tests/db_upgrade/db_upgrade_test.ok b/tests/db_upgrade/db_upgrade_test.ok index ce5b17c8..c7194982 100644 --- a/tests/db_upgrade/db_upgrade_test.ok +++ b/tests/db_upgrade/db_upgrade_test.ok @@ -86,6 +86,7 @@ DDB Database test.db' has been upgraded to HLR DB schema version 3 DDB Database test.db' has been upgraded to HLR DB schema version 4 DDB Database test.db' has been upgraded to HLR DB schema version 5 DDB Database test.db' has been upgraded to HLR DB schema version 6 +DDB Database test.db' has been upgraded to HLR DB schema version 7 DMAIN Cmdline option --db-check: Database was opened successfully, quitting. Resulting db: @@ -106,9 +107,9 @@ Table: auc_3g name|type|notnull|dflt_value|pk algo_id_3g|INTEGER|1||0 ind_bitlen|INTEGER|1|5|0 -k|VARCHAR(32)|1||0 -op|VARCHAR(32)|0||0 -opc|VARCHAR(32)|0||0 +k|VARCHAR(64)|1||0 +op|VARCHAR(64)|0||0 +opc|VARCHAR(64)|0||0 sqn|INTEGER|1|0|0 subscriber_id|INTEGER|0||1 @@ -179,5 +180,5 @@ osmo-hlr --database $db --db-check --config-file $srcdir/osmo-hlr.cfg rc = 0 DMAIN hlr starting DDB using database: test.db -DDB Database test.db' has HLR DB schema version 6 +DDB Database test.db' has HLR DB schema version 7 DMAIN Cmdline option --db-check: Database was opened successfully, quitting. diff --git a/tests/test_subscriber.vty b/tests/test_subscriber.vty index dbe93272..9036fb8d 100644 --- a/tests/test_subscriber.vty +++ b/tests/test_subscriber.vty @@ -275,14 +275,14 @@ OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage ? k Set Encryption Key K OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k ? - K K as 32 hexadecimal characters + K K as 32/64 hexadecimal characters OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d ? op Set OP key opc Set OPC key OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc ? - OP_C OP or OPC as 32 hexadecimal characters + OP_C OP or OPC as 32/64 hexadecimal characters OsmoHLR# subscriber imsi 123456789023000 update aud3g milenage k Deaf0ff1ceD0d0DabbedD1ced1ceF00d opc CededEffacedAceFacedBadFadedBeef ? [ind-bitlen] Set IND bit length