aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openbsc/src/gprs/gprs_gmm.c108
1 files changed, 89 insertions, 19 deletions
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
index 363b45766..e1e15276b 100644
--- a/openbsc/src/gprs/gprs_gmm.c
+++ b/openbsc/src/gprs/gprs_gmm.c
@@ -526,8 +526,20 @@ 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;
+ else
+ 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");
@@ -535,8 +547,13 @@ 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);
@@ -560,13 +577,24 @@ 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 */
@@ -591,6 +619,37 @@ 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(bool is_r99, bool is_utran,
+ const struct osmo_auth_vector *vec,
+ const uint8_t *res, uint8_t res_len)
+{
+ if (!vec)
+ return true;
+
+ /* RES must be present and at least 32bit */
+ if (!res || res_len < 4)
+ return false;
+
+ if (is_r99 && vec->auth_types & OSMO_AUTH_TYPE_UMTS) {
+ /* We have a R99 capable UE and have a UMTS AKA
+ * capable USIM. However, the ME may still chose to only
+ * perform GSM AKA, as long as the bearer is GERAN */
+ if (is_utran && res_len != vec->res_len)
+ return false;
+ }
+
+ if (res_len == vec->res_len && !memcmp(res, vec->res, res_len)) {
+ /* We have established a UMTS Security Context */
+ return true;
+ } else if (res_len == 4 && !memcmp(res, vec->sres, 4)) {
+ /* We have establieshed a GSM Security Context */
+ return true;
+ }
+
+ 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)
@@ -599,6 +658,8 @@ 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;
+ uint8_t res[16];
+ uint8_t res_len;
int rc;
LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS AUTH AND CIPH RESPONSE\n");
@@ -623,24 +684,33 @@ 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;
- 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;
+ }
- 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\n");
+ LOGMMCTXP(LOGL_DEBUG, ctx, "checking received auth info, RES = %s\n",
+ osmo_hexdump(res, res_len));
+ rc = check_auth_resp(mmctx_is_r99(ctx), false, &at->vec, res, res_len);
+ if (!rc) {
+ LOGMMCTXP(LOGL_NOTICE, ctx, "Received RES doesn't match\n");
rc = gsm48_tx_gmm_auth_ciph_rej(ctx);
mm_ctx_cleanup_free(ctx, "GPRS AUTH AND CIPH REJECT");
return rc;
@@ -836,8 +906,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 &&
@@ -1948,7 +2018,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 */