|
|
|
|
@@ -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";
|
|
|
|
|
|
|
|
|
|
/* 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";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
at = &ctx->auth_triplet;
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
|
|
|
|
|
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 */
|
|
|
|
|
|