aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/libmsc
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src/libmsc')
-rw-r--r--openbsc/src/libmsc/auth.c8
-rw-r--r--openbsc/src/libmsc/ctrl_commands.c62
-rw-r--r--openbsc/src/libmsc/db.c40
-rw-r--r--openbsc/src/libmsc/gsm_04_08.c87
-rw-r--r--openbsc/src/libmsc/mncc_builtin.c3
-rw-r--r--openbsc/src/libmsc/vty_interface_layer3.c22
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. */