From: Holger Hans Peter Freyther holger@moiji-mobile.com
The msgb will always have these bytes but it is better practice to verify that the message really has space for the two bytes. --- openbsc/src/osmo-bsc_nat/bsc_nat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 4357485..537001e 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c @@ -1185,7 +1185,7 @@ 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,
From: Holger Hans Peter Freyther holger@moiji-mobile.com
In the upcoming authentication improvements it is nice to separate the finding of the config from the post-allow handling of it. --- openbsc/include/openbsc/bsc_nat.h | 1 + openbsc/src/osmo-bsc_nat/bsc_nat.c | 32 +++++++++++++------------------- openbsc/src/osmo-bsc_nat/bsc_nat_utils.c | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+), 19 deletions(-)
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index ae940b3..6921441 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -319,6 +319,7 @@ struct bsc_nat_ussd_con { /* create and init the structures */ struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token); struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num); +struct bsc_config *bsc_config_by_token(struct bsc_nat *nat, const char *token, int len); void bsc_config_free(struct bsc_config *); void bsc_config_add_lac(struct bsc_config *cfg, int lac); void bsc_config_del_lac(struct bsc_config *cfg, int lac); diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 537001e..2f186b2 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c @@ -980,27 +980,21 @@ 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; - } + conf = bsc_config_by_token(bsc->nat, token, len); + if (!conf) { + LOGP(DNAT, LOGL_ERROR, + "No bsc found for token '%s' on fd: %d.\n", token, + bsc->write_queue.bfd.fd); + return; }
- LOGP(DNAT, LOGL_ERROR, "No bsc found for token '%s' on fd: %d.\n", token, - bsc->write_queue.bfd.fd); + 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) diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c index d95227d..d7ec545 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c @@ -180,6 +180,24 @@ struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token) return conf; }
+struct bsc_config *bsc_config_by_token(struct bsc_nat *nat, const char *token, int len) +{ + struct bsc_config *conf; + + llist_for_each_entry(conf, &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) + return conf; + } + + return NULL; +} + void bsc_config_free(struct bsc_config *cfg) { llist_del(&cfg->entry);
From: Holger Hans Peter Freyther holger@moiji-mobile.com
In case the token was not correct, just close the connection. It is not clear that forcing a new TCP connection is going to give us any extra security here. But with the upcoming auth handling it does make sense to have both case look similar. --- openbsc/src/osmo-bsc_nat/bsc_nat.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 2f186b2..9216654 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c @@ -985,6 +985,7 @@ static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc LOGP(DNAT, LOGL_ERROR, "No bsc found for token '%s' on fd: %d.\n", token, bsc->write_queue.bfd.fd); + bsc_close_connection(bsc); return; }
From: Holger Hans Peter Freyther holger@moiji-mobile.com
Unfortunately the basic structure of the response is broken. There is a two byte length followed by data. The concept of a 'tag' happens to be the first byte of the data.
This means we want to write strlen of the token, then we want to write the NUL and then we need to account for the tag in front.
Introduce a flag if the new or old format should be used. This will allow to have new BSCs talk to old NATs without an additional change. In the long run we can clean that up. --- openbsc/include/openbsc/bsc_msc.h | 2 +- openbsc/src/libbsc/bsc_msc.c | 17 +++++++++++++++-- openbsc/src/osmo-bsc/osmo_bsc_msc.c | 2 +- openbsc/src/osmo-bsc_nat/bsc_nat.c | 15 +++++++++++---- 4 files changed, 28 insertions(+), 8 deletions(-)
diff --git a/openbsc/include/openbsc/bsc_msc.h b/openbsc/include/openbsc/bsc_msc.h index 763bae5..2eec163 100644 --- a/openbsc/include/openbsc/bsc_msc.h +++ b/openbsc/include/openbsc/bsc_msc.h @@ -60,6 +60,6 @@ void bsc_msc_schedule_connect(struct bsc_msc_connection *);
void bsc_msc_lost(struct bsc_msc_connection *);
-struct msgb *bsc_msc_id_get_resp(const char *token); +struct msgb *bsc_msc_id_get_resp(int fixed, const char *token);
#endif diff --git a/openbsc/src/libbsc/bsc_msc.c b/openbsc/src/libbsc/bsc_msc.c index a24efab..fc4530c 100644 --- a/openbsc/src/libbsc/bsc_msc.c +++ b/openbsc/src/libbsc/bsc_msc.c @@ -276,7 +276,7 @@ void bsc_msc_schedule_connect(struct bsc_msc_connection *con) osmo_timer_schedule(&con->reconnect_timer, 5, 0); }
-struct msgb *bsc_msc_id_get_resp(const char *token) +struct msgb *bsc_msc_id_get_resp(int fixed, const char *token) { struct msgb *msg;
@@ -291,8 +291,21 @@ struct msgb *bsc_msc_id_get_resp(const char *token) return NULL; }
+ /* + * The situation is bizarre. The encoding doesn't follow the + * TLV structure. It is more like a LV and old versions had + * it wrong but we want new versions to old servers so we + * introduce the quirk here. + */ msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP); - msgb_l16tv_put(msg, strlen(token) + 1, + if (fixed) { + msgb_put_u8(msg, 0); + msgb_put_u8(msg, strlen(token) + 2); + msgb_tv_fixed_put(msg, IPAC_IDTAG_UNITNAME, strlen(token) + 1, (uint8_t *) token); + } else { + msgb_l16tv_put(msg, strlen(token) + 1, IPAC_IDTAG_UNITNAME, (uint8_t *) token); + } + return msg; } diff --git a/openbsc/src/osmo-bsc/osmo_bsc_msc.c b/openbsc/src/osmo-bsc/osmo_bsc_msc.c index 129b23e..5127ca8 100644 --- a/openbsc/src/osmo-bsc/osmo_bsc_msc.c +++ b/openbsc/src/osmo-bsc/osmo_bsc_msc.c @@ -456,7 +456,7 @@ static void send_id_get_response(struct osmo_msc_data *data, int fd) struct msc_signal_data sig; struct msgb *msg;
- msg = bsc_msc_id_get_resp(data->bsc_token); + msg = bsc_msc_id_get_resp(0, data->bsc_token); if (!msg) return; msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 9216654..841262c 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c @@ -357,7 +357,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); if (!msg) return;
@@ -960,7 +960,7 @@ 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);
if (bsc->cfg) { LOGP(DNAT, LOGL_ERROR, "Reauth on fd %d bsc nr %d\n", @@ -980,11 +980,18 @@ static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *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' on fd: %d.\n", token, - bsc->write_queue.bfd.fd); + "No bsc found for token '%s' len %d on fd: %d.\n", token, + bsc->write_queue.bfd.fd, len); bsc_close_connection(bsc); return; }
From: Holger Hans Peter Freyther holger@moiji-mobile.com
Instead of doing open/read/close all the time, open the FD in the beginning and keep it open. To scare me even more I have seen /dev/urandom actually providing a short read and then blocking but it seems to be the best way to get the random byes we need for authentication.
So one should/could run the cheap random generator on the system (e.g. haveged) or deal with the NAT process to block. --- openbsc/include/openbsc/bsc_nat.h | 3 +++ openbsc/src/osmo-bsc_nat/bsc_nat.c | 9 +++++++++ 2 files changed, 12 insertions(+)
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index 6921441..1035937 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -304,6 +304,9 @@ struct bsc_nat {
/* control interface */ struct ctrl_handle *ctrl; + + /* for random values */ + int random_fd; };
struct bsc_nat_ussd_con { diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 841262c..82562ba 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> @@ -1534,6 +1537,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);
From: Holger Hans Peter Freyther holger@moiji-mobile.com
Generate 16 byte of random data to be used for A3A8 by the BSC in the response. We can't know which BSC it is at this point and I don't want to send another message once the token has been received so always send the data with an undefined code. The old BSCs don't parse the message and will happily ignore the RAND.
/dev/urandom can give short reads on Linux so loop around it until the bytes have been read from the kernel. --- openbsc/include/openbsc/bsc_nat.h | 1 + openbsc/src/osmo-bsc_nat/bsc_nat.c | 40 +++++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index 1035937..c313e52 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -84,6 +84,7 @@ struct bsc_connection {
/* do we know anything about this BSC? */ int authenticated; + uint8_t last_rand[16];
/* the fd we use to communicate */ struct osmo_wqueue write_queue; diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 82562ba..254ea42 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c @@ -188,9 +188,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, @@ -202,7 +202,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) @@ -1362,7 +1396,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);
/*
From: Holger Hans Peter Freyther holger@moiji-mobile.com
Check if the NAT has sent 16 bytes of RAND and if a key has been configured in the system and then generate a result using milenage. The milenage res will be sent and noth the four byte GSM SRES derivation. --- openbsc/include/openbsc/bsc_msc.h | 2 +- openbsc/include/openbsc/osmo_msc_data.h | 3 ++ openbsc/src/libbsc/bsc_msc.c | 7 +++- openbsc/src/osmo-bsc/osmo_bsc_msc.c | 58 ++++++++++++++++++++++++++++++--- openbsc/src/osmo-bsc/osmo_bsc_vty.c | 29 +++++++++++++++++ openbsc/src/osmo-bsc_nat/bsc_nat.c | 2 +- 6 files changed, 94 insertions(+), 7 deletions(-)
diff --git a/openbsc/include/openbsc/bsc_msc.h b/openbsc/include/openbsc/bsc_msc.h index 2eec163..39258d3 100644 --- a/openbsc/include/openbsc/bsc_msc.h +++ b/openbsc/include/openbsc/bsc_msc.h @@ -60,6 +60,6 @@ void bsc_msc_schedule_connect(struct bsc_msc_connection *);
void bsc_msc_lost(struct bsc_msc_connection *);
-struct msgb *bsc_msc_id_get_resp(int fixed, const char *token); +struct msgb *bsc_msc_id_get_resp(int fixed, const char *token, const uint8_t *res, int len);
#endif diff --git a/openbsc/include/openbsc/osmo_msc_data.h b/openbsc/include/openbsc/osmo_msc_data.h index 2d863aa..ed38187 100644 --- a/openbsc/include/openbsc/osmo_msc_data.h +++ b/openbsc/include/openbsc/osmo_msc_data.h @@ -59,6 +59,9 @@ struct osmo_msc_data {
/* Connection data */ char *bsc_token; + uint8_t bsc_key[16]; + uint8_t bsc_key_present; + int ping_timeout; int pong_timeout; struct osmo_timer_list ping_timer; diff --git a/openbsc/src/libbsc/bsc_msc.c b/openbsc/src/libbsc/bsc_msc.c index fc4530c..829ee2b 100644 --- a/openbsc/src/libbsc/bsc_msc.c +++ b/openbsc/src/libbsc/bsc_msc.c @@ -276,7 +276,7 @@ void bsc_msc_schedule_connect(struct bsc_msc_connection *con) osmo_timer_schedule(&con->reconnect_timer, 5, 0); }
-struct msgb *bsc_msc_id_get_resp(int fixed, const char *token) +struct msgb *bsc_msc_id_get_resp(int fixed, const char *token, const uint8_t *res, int len) { struct msgb *msg;
@@ -302,6 +302,11 @@ struct msgb *bsc_msc_id_get_resp(int fixed, const char *token) msgb_put_u8(msg, 0); msgb_put_u8(msg, strlen(token) + 2); msgb_tv_fixed_put(msg, IPAC_IDTAG_UNITNAME, strlen(token) + 1, (uint8_t *) token); + if (len > 0) { + msgb_put_u8(msg, 0); + msgb_put_u8(msg, len + 1); + msgb_tv_fixed_put(msg, 0x24, len, res); + } } else { msgb_l16tv_put(msg, strlen(token) + 1, IPAC_IDTAG_UNITNAME, (uint8_t *) token); diff --git a/openbsc/src/osmo-bsc/osmo_bsc_msc.c b/openbsc/src/osmo-bsc/osmo_bsc_msc.c index 5127ca8..773ee14 100644 --- a/openbsc/src/osmo-bsc/osmo_bsc_msc.c +++ b/openbsc/src/osmo-bsc/osmo_bsc_msc.c @@ -23,6 +23,7 @@ #include <openbsc/bsc_nat.h> #include <osmocom/ctrl/control_cmd.h> #include <osmocom/ctrl/control_if.h> +#include <osmocom/crypt/auth.h> #include <openbsc/debug.h> #include <openbsc/gsm_data.h> #include <openbsc/ipaccess.h> @@ -44,7 +45,7 @@
static void initialize_if_needed(struct bsc_msc_connection *conn); static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn); -static void send_id_get_response(struct osmo_msc_data *data, int fd); +static void send_id_get_response(struct osmo_msc_data *data, int fd, struct msgb *inp); static void send_ping(struct osmo_msc_data *data); static void schedule_ping_pong(struct osmo_msc_data *data);
@@ -302,7 +303,7 @@ static int ipaccess_a_fd_cb(struct osmo_fd *bfd) if (msg->l2h[0] == IPAC_MSGT_ID_ACK) initialize_if_needed(data->msc_con); else if (msg->l2h[0] == IPAC_MSGT_ID_GET) { - send_id_get_response(data, bfd->fd); + send_id_get_response(data, bfd->fd, msg); } else if (msg->l2h[0] == IPAC_MSGT_PONG) { osmo_timer_del(&data->pong_timer); } @@ -451,12 +452,61 @@ static void initialize_if_needed(struct bsc_msc_connection *conn) } }
-static void send_id_get_response(struct osmo_msc_data *data, int fd) +static int answer_challenge(struct osmo_msc_data *data, struct msgb *inp, struct osmo_auth_vector *vec) +{ + int ret; + struct tlv_parsed tvp; + const uint8_t *mrand; + uint8_t mrand_len; + struct osmo_sub_auth_data auth = { + .type = OSMO_AUTH_TYPE_GSM, + .algo = OSMO_AUTH_ALG_MILENAGE, + }; + + ret = ipa_ccm_idtag_parse_off(&tvp, + inp->l2h + 1, + msgb_l2len(inp) - 1, 1); + if (ret < 0) { + LOGP(DMSC, LOGL_ERROR, "ignoring IPA response " + "message with malformed TLVs: %s\n", osmo_hexdump(inp->l2h + 1, + msgb_l2len(inp) - 1)); + return 0; + } + + mrand = TLVP_VAL(&tvp, 0x23); + mrand_len = TLVP_LEN(&tvp, 0x23); + if (mrand_len != 16) { + LOGP(DMSC, LOGL_ERROR, + "RAND is not 16 bytes. Was %d\n", + mrand_len); + return 0; + } + + /* copy the key */ + memcpy(auth.u.umts.opc, data->bsc_key, 16); + memcpy(auth.u.umts.k, data->bsc_key, 16); + memset(auth.u.umts.amf, 0, 2); + auth.u.umts.sqn = 0; + + /* generate the result */ + memset(vec, 0, sizeof(*vec)); + osmo_auth_gen_vec(vec, &auth, mrand); + return 1; +} + + +static void send_id_get_response(struct osmo_msc_data *data, int fd, struct msgb *inp) { struct msc_signal_data sig; struct msgb *msg; + struct osmo_auth_vector vec; + int valid = 0; + + if (data->bsc_key_present) + valid = answer_challenge(data, inp, &vec);
- msg = bsc_msc_id_get_resp(0, data->bsc_token); + msg = bsc_msc_id_get_resp(valid, data->bsc_token, + vec.res, valid ? vec.res_len : 0); if (!msg) return; msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); diff --git a/openbsc/src/osmo-bsc/osmo_bsc_vty.c b/openbsc/src/osmo-bsc/osmo_bsc_vty.c index 06ad77d..9a17cd0 100644 --- a/openbsc/src/osmo-bsc/osmo_bsc_vty.c +++ b/openbsc/src/osmo-bsc/osmo_bsc_vty.c @@ -107,6 +107,9 @@ static void write_msc(struct vty *vty, struct osmo_msc_data *msc) vty_out(vty, "msc %d%s", msc->nr, VTY_NEWLINE); if (msc->bsc_token) vty_out(vty, " token %s%s", msc->bsc_token, VTY_NEWLINE); + if (msc->bsc_key_present) + vty_out(vty, " auth-key %s%s", + osmo_hexdump(msc->bsc_key, sizeof(msc->bsc_key)), VTY_NEWLINE); if (msc->core_ncc != -1) vty_out(vty, " core-mobile-network-code %d%s", msc->core_ncc, VTY_NEWLINE); @@ -231,6 +234,30 @@ DEFUN(cfg_net_bsc_token, return CMD_SUCCESS; }
+DEFUN(cfg_net_bsc_key, + cfg_net_bsc_key_cmd, + "auth-key KEY", + "Authentication (secret) key configuration\n" + "Security key\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + + osmo_hexparse(argv[0], data->bsc_key, sizeof(data->bsc_key)); + data->bsc_key_present = 1; + return CMD_SUCCESS; +} + +DEFUN(cfg_net_no_bsc_key, cfg_net_bsc_no_key_cmd, + "no auth-key", + NO_STR "Authentication (secret) key configuration\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + + memset(data->bsc_key, 0, sizeof(data->bsc_key)); + data->bsc_key_present = 0; + return CMD_SUCCESS; +} + DEFUN(cfg_net_bsc_ncc, cfg_net_bsc_ncc_cmd, "core-mobile-network-code <1-999>", @@ -871,6 +898,8 @@ int bsc_vty_init_extra(void) install_node(&msc_node, config_write_msc); vty_install_default(MSC_NODE); install_element(MSC_NODE, &cfg_net_bsc_token_cmd); + install_element(MSC_NODE, &cfg_net_bsc_key_cmd); + install_element(MSC_NODE, &cfg_net_bsc_no_key_cmd); install_element(MSC_NODE, &cfg_net_bsc_ncc_cmd); install_element(MSC_NODE, &cfg_net_bsc_mcc_cmd); install_element(MSC_NODE, &cfg_net_bsc_lac_cmd); diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 254ea42..9837709 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c @@ -394,7 +394,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(0, nat->token); + struct msgb *msg = bsc_msc_id_get_resp(0, nat->token, NULL, 0); if (!msg) return;
From: Holger Hans Peter Freyther holger@moiji-mobile.com
We are using the token to find the right bsc_config and then we can use the last_rand of the bsc_connection to calculate the expected result and try to compare it with a time constant(???) memcmp. --- openbsc/include/openbsc/bsc_nat.h | 2 ++ openbsc/src/osmo-bsc_nat/bsc_nat.c | 61 ++++++++++++++++++++++++++++++++-- openbsc/src/osmo-bsc_nat/bsc_nat_vty.c | 32 ++++++++++++++++-- 3 files changed, 91 insertions(+), 4 deletions(-)
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index c313e52..72773a9 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -148,6 +148,8 @@ enum bsc_cfg_ctr { struct bsc_config { struct llist_head entry;
+ uint8_t key[16]; + uint8_t key_present; char *token; int nr;
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 9837709..581193e 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c @@ -51,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>
@@ -993,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); 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", @@ -1033,6 +1081,15 @@ static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc return; }
+ /* 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; @@ -1227,9 +1284,9 @@ exit: 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"); diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c index 821e522..981168c 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c @@ -1,6 +1,6 @@ /* OpenBSC NAT interface to quagga VTY */ -/* (C) 2010-2013 by Holger Hans Peter Freyther - * (C) 2010-2013 by On-Waves +/* (C) 2010-2015 by Holger Hans Peter Freyther + * (C) 2010-2015 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify @@ -151,6 +151,8 @@ static void config_write_bsc_single(struct vty *vty, struct bsc_config *bsc) { vty_out(vty, " bsc %u%s", bsc->nr, VTY_NEWLINE); vty_out(vty, " token %s%s", bsc->token, VTY_NEWLINE); + if (bsc->key_present) + vty_out(vty, " auth-key %s%s", osmo_hexdump(bsc->key, 16), VTY_NEWLINE); dump_lac(vty, &bsc->lac_list); if (bsc->description) vty_out(vty, " description %s%s", bsc->description, VTY_NEWLINE); @@ -814,6 +816,30 @@ DEFUN(cfg_bsc_token, cfg_bsc_token_cmd, "token TOKEN", return CMD_SUCCESS; }
+DEFUN(cfg_bsc_auth_key, cfg_bsc_auth_key_cmd, + "auth-key KEY", + "Authentication (secret) key configuration\n" + "Non null security key\n") +{ + struct bsc_config *conf = vty->index; + + memset(conf->key, 0, sizeof(conf->key)); + osmo_hexparse(argv[0], conf->key, sizeof(conf->key)); + conf->key_present = 1; + return CMD_SUCCESS; +} + +DEFUN(cfg_bsc_no_auth_key, cfg_bsc_no_auth_key_cmd, + "no auth-key", + NO_STR "Authentication (secret) key configuration\n") +{ + struct bsc_config *conf = vty->index; + + memset(conf->key, 0, sizeof(conf->key)); + conf->key_present = 0; + return CMD_SUCCESS; +} + DEFUN(cfg_bsc_lac, cfg_bsc_lac_cmd, "location_area_code <0-65535>", "Add the Location Area Code (LAC) of this BSC\n" "LAC\n") { @@ -1202,6 +1228,8 @@ int bsc_nat_vty_init(struct bsc_nat *nat) install_node(&bsc_node, config_write_bsc); vty_install_default(NAT_BSC_NODE); install_element(NAT_BSC_NODE, &cfg_bsc_token_cmd); + install_element(NAT_BSC_NODE, &cfg_bsc_auth_key_cmd); + install_element(NAT_BSC_NODE, &cfg_bsc_no_auth_key_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_lac_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_no_lac_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_paging_cmd);