summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2017-02-24 06:24:45 +0100
committerNeels Hofmeyr <nhofmeyr@sysmocom.de>2017-02-27 14:29:41 +0000
commit058cd573d8c5554eba31cd4cc31625630ceeca1a (patch)
tree9997f08c3ddd370455708686d5bf318fa0287015
parent44468ad531b7baa1c5692d224b2f11695af47970 (diff)
SGSN: Integrate support for UMTS AKA
The general infrastructure for UMTS AKA is already in place: * GSUP with capability to send us auth_vectors that contain either triplets or quintuples * mm_context that holds such auth_vectors Add: * capability to send UMTS AUTN in GMM AUTH REQ * parse extended UMTS RES * on auth response, validate expected AKA with vector and received res/sres * add Auth Failure message to receive resync AUTS token and * send to HLR * clear out-of-sync auth tuple * enter new state for when we're waiting for HLR to resync and send new tuples so that the next Auth Request will be handled Original first half of this patch by: Harald Welte <laforge@gnumonks.org> Full UMTS AKA procedure including AUTS resync tested to work against OsmoHLR with R99 USIM and Milenage algorithm. The sgsn_test.c needs adjustment because we're checking the vector's auth_types now. Depends: libosmocore change-ids I277fb3d407396dffa5c07a9c5454d87a415d393f If943731a78089f0aac3d55245de80596d01314a4 Related: OS#1956 Change-Id: Ie6a0cefba5e4e7f02cc2eaf6ec006ac07d5c1816
-rw-r--r--openbsc/include/openbsc/gprs_sgsn.h7
-rw-r--r--openbsc/src/gprs/gprs_gmm.c198
-rw-r--r--openbsc/src/gprs/gprs_subscriber.c26
-rw-r--r--openbsc/src/gprs/sgsn_auth.c3
-rw-r--r--openbsc/tests/sgsn/sgsn_test.c1
5 files changed, 205 insertions, 30 deletions
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index c3cac7f2a..fd86174be 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -73,6 +73,7 @@ enum gprs_t3350_mode {
enum sgsn_auth_state {
SGSN_AUTH_UNKNOWN,
SGSN_AUTH_AUTHENTICATE,
+ SGSN_AUTH_UMTS_RESYNC,
SGSN_AUTH_ACCEPTED,
SGSN_AUTH_REJECTED
};
@@ -446,7 +447,11 @@ struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx,
int gprs_subscr_init(struct sgsn_instance *sgi);
int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx);
-int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx);
+int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx,
+ const uint8_t *auts,
+ const uint8_t *auts_rand);
+int gprs_subscr_auth_sync(struct gprs_subscr *subscr,
+ const uint8_t *auts, const uint8_t *auts_rand);
void gprs_subscr_cleanup(struct gprs_subscr *subscr);
struct gprs_subscr *gprs_subscr_get_or_create(const char *imsi);
struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx( struct sgsn_mm_ctx *mmctx);
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index 9efe4026c..3f59a2b64 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -83,6 +83,8 @@ static const struct tlv_definition gsm48_gmm_att_tlvdef = {
[GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 },
[GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 },
[GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 },
+ [GSM48_IE_GMM_AUTH_RES_EXT] = { TLV_TYPE_TLV, 0 },
+ [GSM48_IE_GMM_AUTH_FAIL_PAR] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 },
[GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 },
[GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 },
@@ -554,8 +556,19 @@ static int gsm48_tx_gmm_id_req(struct sgsn_mm_ctx *mm, uint8_t id_type)
return gsm48_gmm_sendmsg(msg, 1, mm, false);
}
+/* determine if the MS/UE supports R99 or later */
+static bool mmctx_is_r99(const struct sgsn_mm_ctx *mm)
+{
+ if (mm->ms_network_capa.len < 1)
+ return false;
+ if (mm->ms_network_capa.buf[0] & 0x01)
+ return true;
+ return false;
+}
+
/* 3GPP TS 24.008 Section 9.4.9: Authentication and Ciphering Request */
-static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, uint8_t *rnd,
+static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm,
+ const struct osmo_auth_vector *vec,
uint8_t key_seq, bool force_standby)
{
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 AUTH CIPH REQ");
@@ -563,8 +576,14 @@ static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, uint8_t *rnd,
struct gsm48_auth_ciph_req *acreq;
uint8_t *m_rand, *m_cksn, rbyte;
- LOGMMCTXP(LOGL_INFO, mm, "<- GPRS AUTH AND CIPHERING REQ (rand = %s)\n",
- osmo_hexdump(rnd, 16));
+ LOGMMCTXP(LOGL_INFO, mm, "<- GPRS AUTH AND CIPHERING REQ (rand = %s",
+ osmo_hexdump(vec->rand, sizeof(vec->rand)));
+ if (mmctx_is_r99(mm) && vec
+ && (vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
+ LOGPC(DMM, LOGL_INFO, ", autn = %s)\n",
+ osmo_hexdump(vec->autn, sizeof(vec->autn)));
+ } else
+ LOGPC(DMM, LOGL_INFO, ")\n");
mmctx2msgid(msg, mm);
@@ -588,16 +607,25 @@ static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, uint8_t *rnd,
mm->ac_ref_nr_used = acreq->ac_ref_nr;
/* Only if authentication is requested we need to set RAND + CKSN */
- if (rnd) {
- m_rand = msgb_put(msg, 16+1);
+ if (vec) {
+ m_rand = msgb_put(msg, sizeof(vec->rand) + 1);
m_rand[0] = GSM48_IE_GMM_AUTH_RAND;
- memcpy(m_rand + 1, rnd, 16);
+ memcpy(m_rand + 1, vec->rand, sizeof(vec->rand));
+
/* § 10.5.1.2: */
m_cksn = msgb_put(msg, 1);
m_cksn[0] = (GSM48_IE_GMM_CIPH_CKSN << 4) | (key_seq & 0x07);
+
+ /* A Release99 or higher MS/UE must be able to handle
+ * the optional AUTN IE. If a classic GSM SIM is
+ * inserted, it will simply ignore AUTN and just use
+ * RAND */
+ if (mmctx_is_r99(mm) &&
+ (vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
+ msgb_tlv_put(msg, GSM48_IE_GMM_AUTN,
+ sizeof(vec->autn), vec->autn);
+ }
}
- /* FIXME: add AUTN for 3g auth according to 3GPP TS 24.008 § 10.5.3.1.1 */
- /* FIXME: make sure we don't send any other messages to the MS */
return gsm48_gmm_sendmsg(msg, 1, mm, false);
}
@@ -619,6 +647,62 @@ static int gsm48_tx_gmm_auth_ciph_rej(struct sgsn_mm_ctx *mm)
return gsm48_gmm_sendmsg(msg, 0, mm, false);
}
+/* check if the received authentication response matches */
+static bool check_auth_resp(struct sgsn_mm_ctx *ctx,
+ bool is_utran,
+ const struct osmo_auth_vector *vec,
+ const uint8_t *res, uint8_t res_len)
+{
+ const uint8_t *expect_res;
+ uint8_t expect_res_len;
+ enum osmo_sub_auth_type expect_type;
+ const char *expect_str;
+
+ if (!vec)
+ return true; /* really!? */
+
+ /* On UTRAN (3G) we always expect UMTS AKA. On GERAN (2G) we sent AUTN
+ * and expect UMTS AKA if there is R99 capability and our vector
+ * supports UMTS AKA, otherwise we expect GSM AKA. */
+ if (is_utran
+ || (mmctx_is_r99(ctx) && (vec->auth_types & OSMO_AUTH_TYPE_UMTS))) {
+ expect_type = OSMO_AUTH_TYPE_UMTS;
+ expect_str = "UMTS RES";
+ expect_res = vec->res;
+ expect_res_len = vec->res_len;
+ } else {
+ expect_type = OSMO_AUTH_TYPE_GSM;
+ expect_str = "GSM SRES";
+ expect_res = vec->sres;
+ expect_res_len = sizeof(vec->sres);
+ }
+
+ if (!(vec->auth_types & expect_type)) {
+ LOGMMCTXP(LOGL_ERROR, ctx, "Auth error: auth vector does"
+ " not provide the expected auth type:"
+ " expected %s = 0x%x, auth_types are 0x%x\n",
+ expect_str, expect_type, vec->auth_types);
+ return false;
+ }
+
+ if (!res)
+ goto auth_mismatch;
+
+ if (res_len != expect_res_len)
+ goto auth_mismatch;
+
+ if (memcmp(res, expect_res, res_len) != 0)
+ goto auth_mismatch;
+
+ /* Authorized! */
+ return true;
+
+auth_mismatch:
+ LOGMMCTXP(LOGL_ERROR, ctx, "Auth mismatch: expected %s = %s\n",
+ expect_str, osmo_hexdump_nospc(expect_res, expect_res_len));
+ return false;
+}
+
/* Section 9.4.10: Authentication and Ciphering Response */
static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx,
struct msgb *msg)
@@ -627,6 +711,9 @@ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx,
struct gsm48_auth_ciph_resp *acr = (struct gsm48_auth_ciph_resp *)gh->data;
struct tlv_parsed tp;
struct gsm_auth_tuple *at;
+ const char *res_name = "(no response)";
+ uint8_t res[16];
+ uint8_t res_len;
int rc;
LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS AUTH AND CIPH RESPONSE\n");
@@ -651,26 +738,34 @@ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx,
(msg->data + msg->len) - acr->data, 0, 0);
if (!TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_SRES) ||
- !TLVP_PRESENT(&tp, GSM48_IE_GMM_IMEISV)) {
+ !TLVP_PRESENT(&tp, GSM48_IE_GMM_IMEISV) ||
+ TLVP_LEN(&tp,GSM48_IE_GMM_AUTH_SRES) != 4) {
/* TODO: missing mandatory IE, return STATUS or REJ? */
LOGMMCTXP(LOGL_ERROR, ctx, "Missing mandantory IE\n");
return -EINVAL;
}
- /* Compare SRES with what we expected */
- LOGMMCTXP(LOGL_DEBUG, ctx, "checking received auth info, SRES = %s\n",
- osmo_hexdump(TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_SRES),
- TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_SRES)));
+ /* Start with the good old 4-byte SRES */
+ memcpy(res, TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_SRES), 4);
+ res_len = 4;
+ res_name = "GSM SRES";
- at = &ctx->auth_triplet;
+ /* Append extended RES as part of UMTS AKA, if any */
+ if (TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_RES_EXT)) {
+ unsigned int l = TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_RES_EXT);
+ if (l > sizeof(res)-4)
+ l = sizeof(res)-4;
+ memcpy(res+4, TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_RES_EXT), l);
+ res_len += l;
+ res_name = "UMTS RES";
+ }
- if (TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_SRES) != sizeof(at->vec.sres) ||
- memcmp(TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_SRES), at->vec.sres,
- sizeof(at->vec.sres)) != 0) {
+ at = &ctx->auth_triplet;
- LOGMMCTXP(LOGL_NOTICE, ctx, "Received SRES doesn't match "
- "expected RES %s\n", osmo_hexdump(at->vec.sres,
- sizeof(at->vec.sres)));
+ LOGMMCTXP(LOGL_DEBUG, ctx, "checking auth: received %s = %s\n",
+ res_name, osmo_hexdump(res, res_len));
+ rc = check_auth_resp(ctx, false, &at->vec, res, res_len);
+ if (!rc) {
rc = gsm48_tx_gmm_auth_ciph_rej(ctx);
mm_ctx_cleanup_free(ctx, "GPRS AUTH AND CIPH REJECT");
return rc;
@@ -687,6 +782,60 @@ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx,
return gsm48_gmm_authorize(ctx);
}
+/* Section 9.4.10: Authentication and Ciphering Failure */
+static int gsm48_rx_gmm_auth_ciph_fail(struct sgsn_mm_ctx *ctx,
+ struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
+ struct tlv_parsed tp;
+ const uint8_t gmm_cause = gh->data[0];
+ const uint8_t *auts;
+ int rc;
+
+ LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS AUTH AND CIPH FAILURE (cause = %s)\n",
+ get_value_string(gsm48_gmm_cause_names, gmm_cause));
+
+ tlv_parse(&tp, &gsm48_gmm_att_tlvdef, gh->data+1, msg->len - 1, 0, 0);
+
+ /* Only if GMM cause is present and the AUTS is provided, we can
+ * start re-sync procedure */
+ if (gmm_cause == GMM_CAUSE_SYNC_FAIL &&
+ TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR)) {
+ if (TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR) != 14) {
+ LOGMMCTXP(LOGL_ERROR, ctx, "AUTS IE has wrong size:"
+ " expected %d, got %u\n", 14,
+ TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR));
+ return -EINVAL;
+ }
+ auts = TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR);
+
+ LOGMMCTXP(LOGL_INFO, ctx,
+ "R99 AUTHENTICATION SYNCH (AUTS = %s)\n",
+ osmo_hexdump_nospc(auts, 14));
+
+ /* make sure we'll refresh the auth_triplet in
+ * sgsn_auth_update() */
+ ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
+
+ /* make sure we'll retry authentication after the resync */
+ ctx->auth_state = SGSN_AUTH_UMTS_RESYNC;
+
+ /* Send AUTS to HLR and wait for new Auth Info Result */
+ rc = gprs_subscr_request_auth_info(ctx, auts,
+ ctx->auth_triplet.vec.rand);
+ if (!rc)
+ return 0;
+ /* on error, fall through to send a reject */
+ LOGMMCTXP(LOGL_ERROR, ctx,
+ "Sending AUTS to HLR failed (rc = %d)\n", rc);
+ }
+
+ LOGMMCTXP(LOGL_NOTICE, ctx, "Authentication failed\n");
+ rc = gsm48_tx_gmm_auth_ciph_rej(ctx);
+ mm_ctx_cleanup_free(ctx, "GPRS AUTH FAILURE");
+ return rc;
+}
+
static void extract_subscr_msisdn(struct sgsn_mm_ctx *ctx)
{
struct gsm_mncc_number called;
@@ -865,8 +1014,8 @@ static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx)
struct gsm_auth_tuple *at = &ctx->auth_triplet;
mmctx_timer_start(ctx, 3360, sgsn->cfg.timers.T3360);
- return gsm48_tx_gmm_auth_ciph_req(ctx, at->vec.rand,
- at->key_seq, false);
+ return gsm48_tx_gmm_auth_ciph_req(ctx, &at->vec, at->key_seq,
+ false);
}
if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && ctx->is_authenticated &&
@@ -1911,6 +2060,9 @@ static int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
goto null_mmctx;
rc = gsm48_rx_gmm_auth_ciph_resp(mmctx, msg);
break;
+ case GSM48_MT_GMM_AUTH_CIPH_FAIL:
+ rc = gsm48_rx_gmm_auth_ciph_fail(mmctx, msg);
+ break;
default:
LOGMMCTXP(LOGL_NOTICE, mmctx, "Unknown GSM 04.08 GMM msg type 0x%02x\n",
gh->msg_type);
@@ -1979,7 +2131,7 @@ static void mmctx_timer_cb(void *_mm)
}
at = &mm->auth_triplet;
- gsm48_tx_gmm_auth_ciph_req(mm, at->vec.rand, at->key_seq, false);
+ gsm48_tx_gmm_auth_ciph_req(mm, &at->vec, at->key_seq, false);
osmo_timer_schedule(&mm->timer, sgsn->cfg.timers.T3360, 0);
break;
case 3370: /* waiting for IDENTITY RESPONSE */
diff --git a/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c
index 5f426f80c..2042ec6eb 100644
--- a/openbsc/src/gprs/gprs_subscriber.c
+++ b/openbsc/src/gprs/gprs_subscriber.c
@@ -766,14 +766,21 @@ int gprs_subscr_purge(struct gprs_subscr *subscr)
return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
}
-int gprs_subscr_query_auth_info(struct gprs_subscr *subscr)
+static int gprs_subscr_query_auth_info(struct gprs_subscr *subscr,
+ const uint8_t *auts,
+ const uint8_t *auts_rand)
{
struct osmo_gsup_message gsup_msg = {0};
- LOGGSUBSCRP(LOGL_INFO, subscr,
- "subscriber auth info is not available\n");
+ /* Make sure we have a complete resync or clearly no resync. */
+ OSMO_ASSERT((auts != NULL) == (auts_rand != NULL));
+
+ LOGGSUBSCRP(LOGL_INFO, subscr, "requesting auth info%s\n",
+ auts ? " with AUTS (UMTS Resynch)" : "");
gsup_msg.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST;
+ gsup_msg.auts = auts;
+ gsup_msg.rand = auts_rand;
return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
}
@@ -854,7 +861,16 @@ int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx)
return rc;
}
-int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx)
+/*! \brief Send Update Auth Info request via GSUP, with or without resync.
+ * \param[in] mmctx MM context to request authentication tuples for.
+ * \param[in] auts 14 octet AUTS token for UMTS resync, or NULL.
+ * \param[in] auts_rand 16 octet Random token for UMTS resync, or NULL.
+ * In case of normal Authentication Info request, both \a auts and \a auts_rand
+ * must be NULL. For resync, both must be non-NULL.
+ */
+int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx,
+ const uint8_t *auts,
+ const uint8_t *auts_rand)
{
struct gprs_subscr *subscr = NULL;
int rc;
@@ -865,7 +881,7 @@ int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx)
subscr->flags |= GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING;
- rc = gprs_subscr_query_auth_info(subscr);
+ rc = gprs_subscr_query_auth_info(subscr, auts, auts_rand);
gprs_subscr_put(subscr);
return rc;
}
diff --git a/openbsc/src/gprs/sgsn_auth.c b/openbsc/src/gprs/sgsn_auth.c
index df7ee37b9..a64339c3e 100644
--- a/openbsc/src/gprs/sgsn_auth.c
+++ b/openbsc/src/gprs/sgsn_auth.c
@@ -32,6 +32,7 @@ const struct value_string auth_state_names[] = {
{ SGSN_AUTH_REJECTED, "rejected"},
{ SGSN_AUTH_UNKNOWN, "unknown"},
{ SGSN_AUTH_AUTHENTICATE, "authenticate" },
+ { SGSN_AUTH_UMTS_RESYNC, "UMTS-resync" },
{ 0, NULL }
};
@@ -182,7 +183,7 @@ int sgsn_auth_request(struct sgsn_mm_ctx *mmctx)
mmctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
LOGMMCTXP(LOGL_INFO, mmctx,
"Requesting authentication tuples\n");
- rc = gprs_subscr_request_auth_info(mmctx);
+ rc = gprs_subscr_request_auth_info(mmctx, NULL, NULL);
if (rc >= 0)
return 0;
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index 6eabddefd..2f1513a29 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -1164,6 +1164,7 @@ int my_subscr_request_auth_info_real_auth(struct sgsn_mm_ctx *mmctx)
{
struct gsm_auth_tuple at = {
.vec.sres = {0x51, 0xe5, 0x51, 0xe5},
+ .vec.auth_types = OSMO_AUTH_TYPE_GSM,
.key_seq = 0
};