GERAN: allow GSM SRES on UMTS AKA challenge

Store the established security context type (GSM or UMTS) instead of the
boolean flag is_authenticated. Provide the previous boolean query with thin
sgsn_mm_ctx_is_authenticated() function.

Knowing which security context was established will be necessary for OS#3224,
i.e. using the proper ciphering key, which is not yet tested properly, and
probably not correct at this stage.

This change will make new SGSN_Tests.TC_attach_umts_aka_gsm_sres pass.

Related: OS#3193 OS#3224
Change-Id: I36807bad3bc55c0030d4f09cb2c369714f24bec7
diff --git a/src/gprs/gprs_gmm.c b/src/gprs/gprs_gmm.c
index 642c738..28fba71 100644
--- a/src/gprs/gprs_gmm.c
+++ b/src/gprs/gprs_gmm.c
@@ -662,24 +662,24 @@
 }
 
 /* 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)
+static enum osmo_sub_auth_type 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. */
+	 * supports UMTS AKA, otherwise we expect GSM AKA.
+	 * However, on GERAN, even if we sent a UMTS AKA Authentication Request, the MS may decide to
+	 * instead reply with a GSM AKA SRES response. */
 	if (is_utran
-	    || (mmctx_is_r99(ctx) && (vec->auth_types & OSMO_AUTH_TYPE_UMTS))) {
+	    || (mmctx_is_r99(ctx) && (vec->auth_types & OSMO_AUTH_TYPE_UMTS)
+		&& (res_len > sizeof(vec->sres)))) {
 		expect_type = OSMO_AUTH_TYPE_UMTS;
 		expect_str = "UMTS RES";
 		expect_res = vec->res;
@@ -696,7 +696,7 @@
 			  " 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;
+		return OSMO_AUTH_TYPE_NONE;
 	}
 
 	if (!res)
@@ -709,12 +709,12 @@
 		goto auth_mismatch;
 
 	/* Authorized! */
-	return true;
+	return expect_type;
 
 auth_mismatch:
 	LOGMMCTXP(LOGL_ERROR, ctx, "Auth mismatch: expected %s = %s\n",
 		  expect_str, osmo_hexdump_nospc(expect_res, expect_res_len));
-	return false;
+	return OSMO_AUTH_TYPE_NONE;
 }
 
 /* Section 9.4.10: Authentication and Ciphering Response */
@@ -778,15 +778,13 @@
 
 	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) {
+	ctx->sec_ctx = check_auth_resp(ctx, false, &at->vec, res, res_len);
+	if (!sgsn_mm_ctx_is_authenticated(ctx)) {
 		rc = gsm48_tx_gmm_auth_ciph_rej(ctx);
 		mm_ctx_cleanup_free(ctx, "GPRS AUTH AND CIPH REJECT");
 		return rc;
 	}
 
-	ctx->is_authenticated = 1;
-
 	if (ctx->ran_type == MM_CTX_T_UTRAN_Iu)
 		ctx->iu.new_key = 1;
 
@@ -1026,7 +1024,8 @@
 		return 0;
 	}
 
-	if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && !ctx->is_authenticated) {
+	if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE
+	    && !sgsn_mm_ctx_is_authenticated(ctx)) {
 		struct gsm_auth_tuple *at = &ctx->auth_triplet;
 
 		mmctx_timer_start(ctx, 3360, sgsn->cfg.timers.T3360);
@@ -1034,7 +1033,7 @@
 						  false);
 	}
 
-	if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && ctx->is_authenticated &&
+	if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && sgsn_mm_ctx_is_authenticated(ctx) &&
 	    ctx->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) {
 		/* Check again for authorization */
 		sgsn_auth_request(ctx);
@@ -1106,7 +1105,7 @@
 
 void gsm0408_gprs_authenticate(struct sgsn_mm_ctx *ctx)
 {
-	ctx->is_authenticated = 0;
+	ctx->sec_ctx = OSMO_AUTH_TYPE_NONE;
 
 	gsm48_gmm_authorize(ctx);
 }
@@ -1420,7 +1419,7 @@
 		ctx->gb.tlli_new = gprs_tmsi2tlli(ctx->p_tmsi, TLLI_LOCAL);
 
 		/* Inform LLC layer about new TLLI but keep old active */
-		if (ctx->is_authenticated)
+		if (sgsn_mm_ctx_is_authenticated(ctx))
 			gprs_llme_copy_key(ctx, ctx->gb.llme);
 
 		gprs_llgmm_assign(ctx->gb.llme, ctx->gb.tlli, ctx->gb.tlli_new);
diff --git a/src/gprs/sgsn_auth.c b/src/gprs/sgsn_auth.c
index 6fb32b7..694bece 100644
--- a/src/gprs/sgsn_auth.c
+++ b/src/gprs/sgsn_auth.c
@@ -114,7 +114,7 @@
 			return mmctx->auth_state;
 
 		if (sgsn->cfg.require_authentication &&
-		    (!mmctx->is_authenticated ||
+		    (!sgsn_mm_ctx_is_authenticated(mmctx) ||
 		     mmctx->subscr->sgsn_data->auth_triplets_updated))
 			return SGSN_AUTH_AUTHENTICATE;
 
@@ -175,7 +175,7 @@
 
 	OSMO_ASSERT(mmctx->subscr != NULL);
 
-	if (sgsn->cfg.require_authentication && !mmctx->is_authenticated) {
+	if (sgsn->cfg.require_authentication && !sgsn_mm_ctx_is_authenticated(mmctx)) {
 		/* Find next tuple */
 		at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq);