From e819125976a186529297af7691da7d3181cec0e2 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 5 May 2016 22:52:41 +0200 Subject: SGSN: Integrate basic support for UMTS AKA We already had almost all the parts in place before: * GSUP with capability to send us auth_vectors that contain either triplets or quintuples * mm_context that holds such auth_vectors All that we need to add in this patch is the capability to send the AUTN parameter in the GMM AUTH REQ and parse the extended RES in the GMM AUTH RESP. TODO: Implement SQN re-synchronoization mechanism (Auth Fail: Sync Err) --- openbsc/src/gprs/gprs_gmm.c | 108 ++++++++++++++++++++++++++++++++++++-------- 1 file 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 */ -- cgit v1.2.3