diff options
Diffstat (limited to 'openbsc/src/libmsc')
-rw-r--r-- | openbsc/src/libmsc/auth.c | 8 | ||||
-rw-r--r-- | openbsc/src/libmsc/ctrl_commands.c | 62 | ||||
-rw-r--r-- | openbsc/src/libmsc/db.c | 40 | ||||
-rw-r--r-- | openbsc/src/libmsc/gsm_04_08.c | 87 | ||||
-rw-r--r-- | openbsc/src/libmsc/mncc_builtin.c | 3 | ||||
-rw-r--r-- | openbsc/src/libmsc/vty_interface_layer3.c | 22 |
6 files changed, 168 insertions, 54 deletions
diff --git a/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c index 2d42c2dfe..cc96e8f28 100644 --- a/openbsc/src/libmsc/auth.c +++ b/openbsc/src/libmsc/auth.c @@ -54,9 +54,9 @@ _use_xor(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) } for (i=0; i<4; i++) - atuple->sres[i] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; + atuple->vec.sres[i] = atuple->vec.rand[i] ^ ainfo->a3a8_ki[i]; for (i=4; i<12; i++) - atuple->kc[i-4] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; + atuple->vec.kc[i-4] = atuple->vec.rand[i] ^ ainfo->a3a8_ki[i]; return 0; } @@ -71,7 +71,7 @@ _use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) return -1; } - comp128(ainfo->a3a8_ki, atuple->rand, atuple->sres, atuple->kc); + comp128(ainfo->a3a8_ki, atuple->vec.rand, atuple->vec.sres, atuple->vec.kc); return 0; } @@ -120,7 +120,7 @@ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, } atuple->use_count = 1; - if (RAND_bytes(atuple->rand, sizeof(atuple->rand)) != 1) { + if (RAND_bytes(atuple->vec.rand, sizeof(atuple->vec.rand)) != 1) { LOGP(DMM, LOGL_NOTICE, "RAND_bytes failed, can't generate new auth tuple\n"); return AUTH_ERROR; } diff --git a/openbsc/src/libmsc/ctrl_commands.c b/openbsc/src/libmsc/ctrl_commands.c index e48c6a3e1..0d6a37c96 100644 --- a/openbsc/src/libmsc/ctrl_commands.c +++ b/openbsc/src/libmsc/ctrl_commands.c @@ -24,9 +24,25 @@ #include <openbsc/db.h> #include <openbsc/debug.h> +static bool alg_supported(const char *alg) +{ + /* + * TODO: share this with the vty_interface and extend to all + * algorithms supported by libosmocore now. Make it table based + * as well. + */ + if (strcasecmp(alg, "none") == 0) + return true; + if (strcasecmp(alg, "xor") == 0) + return true; + if (strcasecmp(alg, "comp128v1") == 0) + return true; + return false; +} + static int verify_subscriber_modify(struct ctrl_cmd *cmd, const char *value, void *d) { - char *tmp, *imsi, *msisdn, *saveptr = NULL; + char *tmp, *imsi, *msisdn, *alg, *ki, *saveptr = NULL; int rc = 0; tmp = talloc_strdup(cmd, value); @@ -35,13 +51,21 @@ static int verify_subscriber_modify(struct ctrl_cmd *cmd, const char *value, voi imsi = strtok_r(tmp, ",", &saveptr); msisdn = strtok_r(NULL, ",", &saveptr); + alg = strtok_r(NULL, ",", &saveptr); + ki = strtok_r(NULL, ",", &saveptr); if (!imsi || !msisdn) rc = 1; - else if (strlen(imsi) >= GSM_IMSI_LENGTH) + else if (strlen(imsi) > GSM23003_IMSI_MAX_DIGITS) rc = 1; else if (strlen(msisdn) >= GSM_EXTENSION_LENGTH) rc = 1; + else if (alg) { + if (!alg_supported(alg)) + rc = 1; + else if (strcasecmp(alg, "none") != 0 && !ki) + rc = 1; + } talloc_free(tmp); return rc; @@ -56,7 +80,7 @@ static int get_subscriber_modify(struct ctrl_cmd *cmd, void *data) static int set_subscriber_modify(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net = cmd->node; - char *tmp, *imsi, *msisdn, *saveptr = NULL; + char *tmp, *imsi, *msisdn, *alg, *ki, *saveptr = NULL; struct gsm_subscriber* subscr; int rc; @@ -66,6 +90,8 @@ static int set_subscriber_modify(struct ctrl_cmd *cmd, void *data) imsi = strtok_r(tmp, ",", &saveptr); msisdn = strtok_r(NULL, ",", &saveptr); + alg = strtok_r(NULL, ",", &saveptr); + ki = strtok_r(NULL, ",", &saveptr); subscr = subscr_get_by_imsi(net->subscr_group, imsi); if (!subscr) @@ -80,6 +106,36 @@ static int set_subscriber_modify(struct ctrl_cmd *cmd, void *data) /* put it back to the db */ rc = db_sync_subscriber(subscr); db_subscriber_update(subscr); + + /* handle optional ciphering */ + if (alg) { + if (strcasecmp(alg, "none") == 0) + db_sync_authinfo_for_subscr(NULL, subscr); + else { + struct gsm_auth_info ainfo = { 0, }; + /* the verify should make sure that this is okay */ + OSMO_ASSERT(alg); + OSMO_ASSERT(ki); + + if (strcasecmp(alg, "xor") == 0) + ainfo.auth_algo = AUTH_ALGO_XOR; + else if (strcasecmp(alg, "comp128v1") == 0) + ainfo.auth_algo = AUTH_ALGO_COMP128v1; + + rc = osmo_hexparse(ki, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki)); + if (rc < 0) { + subscr_put(subscr); + talloc_free(tmp); + cmd->reply = "Failed to parse KI"; + return CTRL_CMD_ERROR; + } + + ainfo.a3a8_ki_len = rc; + db_sync_authinfo_for_subscr(&ainfo, subscr); + rc = 0; + } + db_sync_lastauthtuple_for_subscr(NULL, subscr); + } subscr_put(subscr); talloc_free(tmp); diff --git a/openbsc/src/libmsc/db.c b/openbsc/src/libmsc/db.c index 8d7d7dc19..b555905ed 100644 --- a/openbsc/src/libmsc/db.c +++ b/openbsc/src/libmsc/db.c @@ -34,6 +34,7 @@ #include <openbsc/db.h> #include <openbsc/debug.h> +#include <osmocom/gsm/protocol/gsm_23_003.h> #include <osmocom/core/talloc.h> #include <osmocom/core/statistics.h> #include <osmocom/core/rate_ctr.h> @@ -508,14 +509,8 @@ struct gsm_subscriber *db_create_subscriber(const char *imsi) /* Is this subscriber known in the db? */ subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi); if (subscr) { - result = dbi_conn_queryf(conn, - "UPDATE Subscriber set updated = datetime('now') " - "WHERE imsi = %s " , imsi); - if (!result) - LOGP(DDB, LOGL_ERROR, "failed to update timestamp\n"); - else - dbi_result_free(result); - return subscr; + subscr_put(subscr); + return NULL; } subscr = subscr_alloc(); @@ -529,10 +524,13 @@ struct gsm_subscriber *db_create_subscriber(const char *imsi) "(%s, datetime('now'), datetime('now')) ", imsi ); - if (!result) + if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to create Subscriber by IMSI.\n"); + subscr_put(subscr); + return NULL; + } subscr->id = dbi_conn_sequence_last(conn, NULL); - strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1); + strncpy(subscr->imsi, imsi, sizeof(subscr->imsi)-1); dbi_result_free(result); LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi); db_subscriber_alloc_exten(subscr); @@ -703,25 +701,25 @@ int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq"); len = dbi_result_get_field_length(result, "rand"); - if (len != sizeof(atuple->rand)) + if (len != sizeof(atuple->vec.rand)) goto err_size; blob = dbi_result_get_binary(result, "rand"); - memcpy(atuple->rand, blob, len); + memcpy(atuple->vec.rand, blob, len); len = dbi_result_get_field_length(result, "sres"); - if (len != sizeof(atuple->sres)) + if (len != sizeof(atuple->vec.sres)) goto err_size; blob = dbi_result_get_binary(result, "sres"); - memcpy(atuple->sres, blob, len); + memcpy(atuple->vec.sres, blob, len); len = dbi_result_get_field_length(result, "kc"); - if (len != sizeof(atuple->kc)) + if (len != sizeof(atuple->vec.kc)) goto err_size; blob = dbi_result_get_binary(result, "kc"); - memcpy(atuple->kc, blob, len); + memcpy(atuple->vec.kc, blob, len); dbi_result_free(result); @@ -762,11 +760,11 @@ int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, /* Update / Insert */ dbi_conn_quote_binary_copy(conn, - atuple->rand, sizeof(atuple->rand), &rand_str); + atuple->vec.rand, sizeof(atuple->vec.rand), &rand_str); dbi_conn_quote_binary_copy(conn, - atuple->sres, sizeof(atuple->sres), &sres_str); + atuple->vec.sres, sizeof(atuple->vec.sres), &sres_str); dbi_conn_quote_binary_copy(conn, - atuple->kc, sizeof(atuple->kc), &kc_str); + atuple->vec.kc, sizeof(atuple->vec.kc), &kc_str); if (!upd) { result = dbi_conn_queryf(conn, @@ -806,7 +804,7 @@ static void db_set_from_query(struct gsm_subscriber *subscr, dbi_conn result) const char *string; string = dbi_result_get_string(result, "imsi"); if (string) - strncpy(subscr->imsi, string, GSM_IMSI_LENGTH-1); + strncpy(subscr->imsi, string, sizeof(subscr->imsi)-1); string = dbi_result_get_string(result, "tmsi"); if (string) @@ -1320,7 +1318,7 @@ int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t *token return 0; } -int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IMEI_LENGTH]) +int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM23003_IMEISV_NUM_DIGITS]) { unsigned long long equipment_id, watch_id; dbi_result result; diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c index b2ac55c48..8c1cf9adb 100644 --- a/openbsc/src/libmsc/gsm_04_08.c +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -60,6 +60,7 @@ #include <osmocom/gsm/gsm48.h> #include <osmocom/gsm/gsm0480.h> #include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> #include <osmocom/gsm/tlv.h> @@ -147,7 +148,6 @@ void allocate_security_operation(struct gsm_subscriber_connection *conn) int iu_hack__get_hardcoded_auth_tuple(struct gsm_auth_tuple *atuple) { unsigned char tmp_rand[16]; - struct osmo_auth_vector vec; /* Ki 000102030405060708090a0b0c0d0e0f */ struct osmo_sub_auth_data auth = { .type = OSMO_AUTH_TYPE_GSM, @@ -161,13 +161,10 @@ int iu_hack__get_hardcoded_auth_tuple(struct gsm_auth_tuple *atuple) RAND_bytes(tmp_rand, sizeof(tmp_rand)); - memset(&vec, 0, sizeof(vec)); - osmo_auth_gen_vec(&vec, &auth, tmp_rand); + memset(&atuple->vec, 0, sizeof(atuple->vec)); + osmo_auth_gen_vec(&atuple->vec, &auth, tmp_rand); atuple->key_seq = 0; - memcpy(&atuple->rand, &tmp_rand, sizeof(atuple->rand)); - memcpy(&atuple->sres, &vec.sres, sizeof(atuple->sres)); - memcpy(&atuple->kc, &vec.kc, sizeof(atuple->kc)); return AUTH_DO_AUTH; } @@ -260,13 +257,13 @@ int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, /* Start authentication */ DEBUGP(DMM, "gsm48_secure_channel(%s) starting authentication\n", subscr_name(subscr)); - return gsm48_tx_mm_auth_req(conn, op->atuple.rand, op->atuple.key_seq); + return gsm48_tx_mm_auth_req(conn, op->atuple.vec.rand, op->atuple.key_seq); } else if (rc == AUTH_DO_CIPH) { /* Start ciphering directly */ DEBUGP(DMM, "gsm48_secure_channel(%s) starting ciphering\n", subscr_name(subscr)); return gsm0808_cipher_mode(conn, net->a5_encryption, - op->atuple.kc, 8, 0); + op->atuple.vec.kc, 8, 0); } return -EINVAL; /* not reached */ @@ -1137,10 +1134,10 @@ static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct cb = conn->sec_operation->cb; /* Validate SRES */ - if (memcmp(conn->sec_operation->atuple.sres, ar->sres,4)) { + if (memcmp(conn->sec_operation->atuple.vec.sres, ar->sres,4)) { int rc; DEBUGPC(DMM, "Invalid (expected %s)\n", - osmo_hexdump(conn->sec_operation->atuple.sres, 4)); + osmo_hexdump(conn->sec_operation->atuple.vec.sres, 4)); if (cb) cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, @@ -1161,7 +1158,7 @@ static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct * As soon as such a receiver exists, it must call * iu_tx_sec_mode_cmd() as below. */ return gsm0808_cipher_mode(conn, net->a5_encryption, - conn->sec_operation->atuple.kc, 8, 0); + conn->sec_operation->atuple.vec.kc, 8, 0); if (conn->via_iface == IFACE_IU && !conn->iu.integrity_protection) { @@ -1607,17 +1604,38 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan) { struct gsm_bts *bts = lchan->ts->trx->bts; struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts; + enum gsm_chan_t lt = lchan->type, rt = remote_lchan->type; + enum gsm48_chan_mode lm = lchan->tch_mode, rm = remote_lchan->tch_mode; int rc; - DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n", - bts->nr, lchan->ts->trx->nr, lchan->ts->nr, - remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr); + DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u,%s) and " + "(bts=%u,trx=%u,ts=%u,%s)\n", + bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + get_value_string(gsm_chan_t_names, lt), + remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr, + get_value_string(gsm_chan_t_names, rt)); if (bts->type != remote_bts->type) { LOGP(DCC, LOGL_ERROR, "Cannot switch calls between different BTS types yet\n"); return -EINVAL; } + if (lt != rt) { + LOGP(DCC, LOGL_ERROR, "Cannot patch through call with different" + " channel types: local = %s, remote = %s\n", + get_value_string(gsm_chan_t_names, lt), + get_value_string(gsm_chan_t_names, rt)); + return -EBADSLT; + } + + if (lm != rm) { + LOGP(DCC, LOGL_ERROR, "Cannot patch through call with different" + " channel modes: local = %s, remote = %s\n", + get_value_string(gsm48_chan_mode_names, lm), + get_value_string(gsm48_chan_mode_names, rm)); + return -EMEDIUMTYPE; + } + // todo: map between different bts types switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: @@ -1866,6 +1884,30 @@ static void gsm48_cc_timeout(void *arg) } +/* disconnect both calls from the bridge */ +static inline void disconnect_bridge(struct gsm_network *net, + struct gsm_mncc_bridge *bridge, int err) +{ + struct gsm_trans *trans0 = trans_find_by_callref(net, bridge->callref[0]); + struct gsm_trans *trans1 = trans_find_by_callref(net, bridge->callref[1]); + struct gsm_mncc mx_rel; + if (!trans0 || !trans1) + return; + + DEBUGP(DCC, "Failed to bridge TCH for calls %x <-> %x :: %s \n", + trans0->callref, trans1->callref, strerror(err)); + + memset(&mx_rel, 0, sizeof(struct gsm_mncc)); + mncc_set_cause(&mx_rel, GSM48_CAUSE_LOC_INN_NET, + GSM48_CC_CAUSE_CHAN_UNACCEPT); + + mx_rel.callref = trans0->callref; + gsm48_cc_tx_disconnect(trans0, &mx_rel); + + mx_rel.callref = trans1->callref; + gsm48_cc_tx_disconnect(trans1, &mx_rel); +} + static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, int sec, int micro) { @@ -3031,6 +3073,7 @@ static int tch_rtp_create(struct gsm_network *net, uint32_t callref) struct gsm_bts *bts; struct gsm_lchan *lchan; struct gsm_trans *trans; + enum gsm48_chan_mode m; /* Find callref */ trans = trans_find_by_callref(net, callref); @@ -3070,8 +3113,11 @@ static int tch_rtp_create(struct gsm_network *net, uint32_t callref) */ if (lchan->tch_mode == GSM48_CMODE_SIGN) { trans->conn->mncc_rtp_create_pending = 1; - return gsm0808_assign_req(trans->conn, - mncc_codec_for_mode(lchan->type), + m = mncc_codec_for_mode(lchan->type); + LOGP(DMNCC, LOGL_DEBUG, "RTP create: codec=%s, chan_type=%s\n", + get_value_string(gsm48_chan_mode_names, m), + get_value_string(gsm_chan_t_names, lchan->type)); + return gsm0808_assign_req(trans->conn, m, lchan->type != GSM_LCHAN_TCH_H); } @@ -3105,6 +3151,10 @@ static int tch_rtp_connect(struct gsm_network *net, void *arg) } lchan = trans->conn->lchan; + LOGP(DMNCC, LOGL_DEBUG, "RTP connect: codec=%s, chan_type=%s\n", + get_value_string(gsm48_chan_mode_names, + mncc_codec_for_mode(lchan->type)), + get_value_string(gsm_chan_t_names, lchan->type)); /* TODO: Check if payload_msg_type is compatible with what we have */ if (rtp->payload_type != lchan->abis_ip.rtp_payload) { @@ -3250,7 +3300,10 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) /* handle special messages */ switch(msg_type) { case MNCC_BRIDGE: - return tch_bridge(net, arg); + rc = tch_bridge(net, arg); + if (rc < 0) + disconnect_bridge(net, arg, -rc); + return rc; case MNCC_FRAME_DROP: return tch_recv_mncc(net, data->callref, 0); case MNCC_FRAME_RECV: diff --git a/openbsc/src/libmsc/mncc_builtin.c b/openbsc/src/libmsc/mncc_builtin.c index 46f86a15a..c670ed2e5 100644 --- a/openbsc/src/libmsc/mncc_builtin.c +++ b/openbsc/src/libmsc/mncc_builtin.c @@ -138,7 +138,8 @@ static int mncc_setup_ind(struct gsm_call *call, int msg_type, memset(&mncc, 0, sizeof(struct gsm_mncc)); mncc.callref = call->callref; mncc.lchan_mode = determine_lchan_mode(setup); - DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref); + DEBUGP(DMNCC, "(call %x) Modify channel mode: %s\n", call->callref, + get_value_string(gsm48_chan_mode_names, mncc.lchan_mode)); mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, &mncc); /* send setup to remote */ diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c index e4e01d27a..fdc7e8e6f 100644 --- a/openbsc/src/libmsc/vty_interface_layer3.c +++ b/openbsc/src/libmsc/vty_interface_layer3.c @@ -92,13 +92,13 @@ static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr) vty_out(vty, " seq # : %d%s", atuple.key_seq, VTY_NEWLINE); vty_out(vty, " RAND : %s%s", - osmo_hexdump(atuple.rand, sizeof(atuple.rand)), + osmo_hexdump(atuple.vec.rand, sizeof(atuple.vec.rand)), VTY_NEWLINE); vty_out(vty, " SRES : %s%s", - osmo_hexdump(atuple.sres, sizeof(atuple.sres)), + osmo_hexdump(atuple.vec.sres, sizeof(atuple.vec.sres)), VTY_NEWLINE); vty_out(vty, " Kc : %s%s", - osmo_hexdump(atuple.kc, sizeof(atuple.kc)), + osmo_hexdump(atuple.vec.kc, sizeof(atuple.vec.kc)), VTY_NEWLINE); } @@ -235,11 +235,17 @@ DEFUN(subscriber_create, struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr; - subscr = subscr_create_subscriber(gsmnet->subscr_group, argv[0]); - if (!subscr) { - vty_out(vty, "%% No subscriber created for IMSI %s%s", - argv[0], VTY_NEWLINE); - return CMD_WARNING; + subscr = subscr_get_by_imsi(gsmnet->subscr_group, argv[0]); + if (subscr) + db_sync_subscriber(subscr); + else { + subscr = subscr_create_subscriber(gsmnet->subscr_group, argv[0]); + + if (!subscr) { + vty_out(vty, "%% No subscriber created for IMSI %s%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } } /* Show info about the created subscriber. */ |