aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/osmo-bsc_nat/bsc_nat.c
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src/osmo-bsc_nat/bsc_nat.c')
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_nat.c156
1 files changed, 129 insertions, 27 deletions
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c
index 4357485ff..581193e5a 100644
--- a/openbsc/src/osmo-bsc_nat/bsc_nat.c
+++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c
@@ -21,6 +21,8 @@
*
*/
#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
@@ -31,6 +33,7 @@
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
+#include <fcntl.h>
#define _GNU_SOURCE
#include <getopt.h>
@@ -48,6 +51,8 @@
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/ctrl/control_if.h>
+#include <osmocom/crypt/auth.h>
+
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
@@ -185,9 +190,9 @@ static void send_id_ack(struct bsc_connection *bsc)
bsc_send_data(bsc, id_ack, sizeof(id_ack), IPAC_PROTO_IPACCESS);
}
-static void send_id_req(struct bsc_connection *bsc)
+static void send_id_req(struct bsc_nat *nat, struct bsc_connection *bsc)
{
- static const uint8_t id_req[] = {
+ static const uint8_t s_id_req[] = {
IPAC_MSGT_ID_GET,
0x01, IPAC_IDTAG_UNIT,
0x01, IPAC_IDTAG_MACADDR,
@@ -199,7 +204,41 @@ static void send_id_req(struct bsc_connection *bsc)
0x01, IPAC_IDTAG_SERNR,
};
+ int toread, rounds;
+ uint8_t *mrand, *randoff;
+ uint8_t id_req[sizeof(s_id_req) + (2+16)];
+ uint8_t *buf = &id_req[sizeof(s_id_req)];
+
+ /* copy the static data */
+ memcpy(id_req, s_id_req, sizeof(s_id_req));
+
+ /* put the RAND with length, tag, value */
+ buf = v_put(buf, 0x11);
+ buf = v_put(buf, 0x23);
+ mrand = bsc->last_rand;
+ randoff = mrand;
+ memset(randoff, 0, 16);
+
+ for (toread = 16, rounds = 0; rounds < 5 && toread > 0; ++rounds) {
+ int rc = read(nat->random_fd, randoff, toread);
+ if (rc <= 0)
+ goto failed_random;
+ toread -= rc;
+ randoff += rc;
+ }
+
+ if (toread != 0)
+ goto failed_random;
+ memcpy(buf, mrand, 16);
+ buf += 16;
+
bsc_send_data(bsc, id_req, sizeof(id_req), IPAC_PROTO_IPACCESS);
+ return;
+
+failed_random:
+ /* the timeout will trigger and close this connection */
+ LOGP(DNAT, LOGL_ERROR, "Failed to read from urandom.\n");
+ return;
}
static struct msgb *nat_create_rlsd(struct nat_sccp_connection *conn)
@@ -357,7 +396,7 @@ static void initialize_msc_if_needed(struct bsc_msc_connection *msc_con)
static void send_id_get_response(struct bsc_msc_connection *msc_con)
{
- struct msgb *msg = bsc_msc_id_get_resp(nat->token);
+ struct msgb *msg = bsc_msc_id_get_resp(0, nat->token, NULL, 0);
if (!msg)
return;
@@ -956,11 +995,57 @@ static void ipaccess_close_bsc(void *data)
bsc_close_connection(conn);
}
+/* Wishful thinking to generate a constant time compare */
+static int constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count)
+{
+ int x = 0, i;
+
+ for (i = 0; i < count; ++i)
+ x |= exp[i] ^ rel[i];
+
+ return x != 0;
+}
+
+static int verify_key(struct bsc_connection *conn, struct bsc_config *conf, const uint8_t *key, const int keylen)
+{
+ struct osmo_auth_vector vec;
+
+ struct osmo_sub_auth_data auth = {
+ .type = OSMO_AUTH_TYPE_GSM,
+ .algo = OSMO_AUTH_ALG_MILENAGE,
+ };
+
+ /* expect a specific keylen */
+ if (keylen != 8) {
+ LOGP(DNAT, LOGL_ERROR, "Key length is wrong: %d for bsc nr %d\n",
+ keylen, conf->nr);
+ return 0;
+ }
+
+ memcpy(auth.u.umts.opc, conf->key, 16);
+ memcpy(auth.u.umts.k, conf->key, 16);
+ memset(auth.u.umts.amf, 0, 2);
+ auth.u.umts.sqn = 0;
+
+ memset(&vec, 0, sizeof(vec));
+ osmo_auth_gen_vec(&vec, &auth, conn->last_rand);
+
+ if (vec.res_len != 8) {
+ LOGP(DNAT, LOGL_ERROR, "Res length is wrong: %d for bsc nr %d\n",
+ keylen, conf->nr);
+ return 0;
+ }
+
+ return constant_time_cmp(vec.res, key, 8) == 0;
+}
+
static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc)
{
struct bsc_config *conf;
const char *token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME);
- const int len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME);
+ int len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME);
+ const uint8_t *xres = TLVP_VAL(tvp, 0x24);
+ const int xlen = TLVP_LEN(tvp, 0x24);
if (bsc->cfg) {
LOGP(DNAT, LOGL_ERROR, "Reauth on fd %d bsc nr %d\n",
@@ -980,27 +1065,38 @@ static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc
return;
}
- llist_for_each_entry(conf, &bsc->nat->bsc_configs, entry) {
- /*
- * Add the '\0' of the token for the memcmp, the IPA messages
- * for some reason added null termination.
- */
- const int token_len = strlen(conf->token) + 1;
-
- if (token_len == len && memcmp(conf->token, token, token_len) == 0) {
- rate_ctr_inc(&conf->stats.ctrg->ctr[BCFG_CTR_NET_RECONN]);
- bsc->authenticated = 1;
- bsc->cfg = conf;
- osmo_timer_del(&bsc->id_timeout);
- LOGP(DNAT, LOGL_NOTICE, "Authenticated bsc nr: %d on fd %d\n",
- conf->nr, bsc->write_queue.bfd.fd);
- start_ping_pong(bsc);
- return;
- }
+ /*
+ * New systems have fixed the structure of the message but
+ * we need to support old ones too.
+ */
+ if (len >= 2 && token[len - 2] == '\0')
+ len -= 1;
+
+ conf = bsc_config_by_token(bsc->nat, token, len);
+ if (!conf) {
+ LOGP(DNAT, LOGL_ERROR,
+ "No bsc found for token '%s' len %d on fd: %d.\n", token,
+ bsc->write_queue.bfd.fd, len);
+ bsc_close_connection(bsc);
+ return;
}
- LOGP(DNAT, LOGL_ERROR, "No bsc found for token '%s' on fd: %d.\n", token,
- bsc->write_queue.bfd.fd);
+ /* We have set a key and expect it to be present */
+ if (conf->key_present && !verify_key(bsc, conf, xres, xlen - 1)) {
+ LOGP(DNAT, LOGL_ERROR,
+ "Wrong key for bsc nr %d fd: %d.\n", conf->nr,
+ bsc->write_queue.bfd.fd);
+ bsc_close_connection(bsc);
+ return;
+ }
+
+ rate_ctr_inc(&conf->stats.ctrg->ctr[BCFG_CTR_NET_RECONN]);
+ bsc->authenticated = 1;
+ bsc->cfg = conf;
+ osmo_timer_del(&bsc->id_timeout);
+ LOGP(DNAT, LOGL_NOTICE, "Authenticated bsc nr: %d on fd %d\n",
+ conf->nr, bsc->write_queue.bfd.fd);
+ start_ping_pong(bsc);
}
static void handle_con_stats(struct nat_sccp_connection *con)
@@ -1185,12 +1281,12 @@ exit:
send_reset_ack(bsc);
} else if (parsed->ipa_proto == IPAC_PROTO_IPACCESS) {
/* do we know who is handling this? */
- if (msg->l2h[0] == IPAC_MSGT_ID_RESP) {
+ if (msg->l2h[0] == IPAC_MSGT_ID_RESP && msgb_l2len(msg) > 2) {
struct tlv_parsed tvp;
int ret;
- ret = ipa_ccm_idtag_parse(&tvp,
+ ret = ipa_ccm_idtag_parse_off(&tvp,
(unsigned char *) msg->l2h + 2,
- msgb_l2len(msg) - 2);
+ msgb_l2len(msg) - 2, 0);
if (ret < 0) {
LOGP(DNAT, LOGL_ERROR, "ignoring IPA response "
"message with malformed TLVs\n");
@@ -1357,7 +1453,7 @@ static int ipaccess_listen_bsc_cb(struct osmo_fd *bfd, unsigned int what)
bsc->last_id = 0;
send_id_ack(bsc);
- send_id_req(bsc);
+ send_id_req(nat, bsc);
send_mgcp_reset(bsc);
/*
@@ -1532,6 +1628,12 @@ int main(int argc, char **argv)
/* We need to add mode-set for amr codecs */
nat->sdp_ensure_amr_mode_set = 1;
+ nat->random_fd = open("/dev/random", O_RDONLY);
+ if (nat->random_fd < 0) {
+ fprintf(stderr, "Failed to open /dev/urandom.\n");
+ return -5;
+ }
+
vty_info.copyright = openbsc_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(&log_info);