aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
};