msc: test auth options, and fall-back to no-auth
Test 12 permutations of
(auth optional,required) x (a5 '0', '0 3', '3') x (hlr has auth info)
In TC_auth_options_2(), expect behavior after implementing OS#4830: if
the HLR fails to return auth info and auth + ciph are configured
optional, fall back to no authentication. This test will start
succeeding starting with commit
I5feda196fa481dd8a46b0e4721c64b7c6600f0d1 in osmo-msc.git.
All other tests yield the current behavior of osmo-msc.
Related: I5feda196fa481dd8a46b0e4721c64b7c6600f0d1 (osmo-msc)
Related: OS#4830
Change-Id: I8e3b02ca83e56ef5349d85f08407509e19fa353c
diff --git a/msc/BSC_ConnectionHandler.ttcn b/msc/BSC_ConnectionHandler.ttcn
index 4e736b8..fe9187c 100644
--- a/msc/BSC_ConnectionHandler.ttcn
+++ b/msc/BSC_ConnectionHandler.ttcn
@@ -78,8 +78,24 @@
}
type record BSC_ConnHdlrNetworkPars {
+ /* Bitmask of expected A5 levels; in Ciphering, will expect use of the highest A5 remaining after masking this
+ * with A5 supported by the MS */
OCT1 kc_support,
+ /* osmo-msc VTY cfg under 'network': If a test wants to temporarily modify auth and encr config, this is the
+ * original config to return to for this test (is *not* sent to vty before the test, but maybe it should).
+ * For example: { "authentication optional", "encryption a5 0 3" } */
+ rof_charstring net_config,
+ /* expect_attach_success == true: expect Location Updating / CM Service Request to succeed.
+ * expect_attach_success == false: expect the MSC to reject LU / CM Service. */
+ boolean expect_attach_success,
+ /* expect_tmsi == true: expect MSC to allocate a new TMSI.
+ * expect_tmsi == false: expect no TMSI to be assigned, operate with IMSI Mobile Identity. */
boolean expect_tmsi,
+ /* expect_auth == true overrides expect_auth_attempt == false */
+ boolean expect_auth_attempt,
+ /* hlr_has_auth_info has an effect only when (expect_auth_attempt or expect_auth) == true.
+ * hlr_has_auth_info == false means the HLR responds to Send Auth Info Request with a NACK. */
+ boolean hlr_has_auth_info,
boolean expect_auth,
boolean expect_ciph,
boolean expect_imei,
@@ -404,7 +420,11 @@
} else {
if (etype != EST_TYPE_PAG_RESP) {
/* explicit CM SERVICE ACCEPT */
- BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_ACC));
+ if (g_pars.net.expect_attach_success) {
+ BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_ACC));
+ } else {
+ BSSAP.receive(tr_PDU_DTAP_MT(tr_CM_SERV_REJ));
+ }
}
}
}
@@ -436,34 +456,43 @@
altstep as_GSUP_SAI() runs on BSC_ConnHdlr {
var GSUP_IE auth_tuple;
[] GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi)) {
- if (g_pars.use_umts_aka) {
- if (not g_pars.vec_keep) {
- g_pars.vec := f_gen_auth_vec_3g();
+ if (g_pars.net.hlr_has_auth_info) {
+ if (g_pars.use_umts_aka) {
+ if (not g_pars.vec_keep) {
+ g_pars.vec := f_gen_auth_vec_3g();
+ }
+ auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G3G(g_pars.vec.rand,
+ g_pars.vec.sres,
+ g_pars.vec.kc,
+ g_pars.vec.ik,
+ g_pars.vec.ck,
+ g_pars.vec.autn,
+ g_pars.vec.res));
+ GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple));
+ } else {
+ if (not g_pars.vec_keep) {
+ g_pars.vec := f_gen_auth_vec_2g();
+ }
+ auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G(g_pars.vec.rand,
+ g_pars.vec.sres,
+ g_pars.vec.kc));
+ GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple));
}
- auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G3G(g_pars.vec.rand,
- g_pars.vec.sres,
- g_pars.vec.kc,
- g_pars.vec.ik,
- g_pars.vec.ck,
- g_pars.vec.autn,
- g_pars.vec.res));
- GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple));
} else {
- if (not g_pars.vec_keep) {
- g_pars.vec := f_gen_auth_vec_2g();
- }
- auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G(g_pars.vec.rand,
- g_pars.vec.sres,
- g_pars.vec.kc));
- GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple));
+ log("XXX ts_GSUP_SAI_ERR");
+ /* HLR knows the IMSI but has no authentication info; osmo-hlr responds with GMM_CAUSE_IMSI_UNKNOWN=2 in
+ * this case, for SAI this merely means there is no auth entry for this IMSI. */
+ GSUP.send(ts_GSUP_SAI_ERR(g_pars.imsi, 2));
}
}
}
function f_mm_auth() runs on BSC_ConnHdlr
{
- if (g_pars.net.expect_auth) {
+ if (g_pars.net.expect_auth or g_pars.net.expect_auth_attempt) {
as_GSUP_SAI();
+ }
+ if (g_pars.net.expect_auth) {
if (g_pars.use_umts_aka) {
BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_MM_AUTH_REQ_3G(g_pars.vec.rand, g_pars.vec.autn)));
var OCT4 res := substr(g_pars.vec.res, 0, 4);
@@ -685,6 +714,9 @@
f_mm_ciph_utran();
}
+ if (not g_pars.net.expect_attach_success) {
+ return;
+ }
f_expect_common_id();
}
@@ -810,15 +842,18 @@
f_mm_common();
f_msc_lu_hlr();
f_mm_imei();
- f_accept_reject_lu();
+ as_accept_reject_lu(g_pars.net.expect_attach_success);
/* FIXME: there could be pending SMS or other common procedures by the MSC, let's ignore them */
- f_expect_clear();
+ f_expect_clear(verify_vlr_cell_id := g_pars.net.expect_attach_success);
setverdict(pass);
}
function f_msc_lu_hlr() runs on BSC_ConnHdlr
{
+ if (not g_pars.net.expect_attach_success) {
+ return;
+ }
/* Expect MSC to perform LU with HLR */
GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi));
GSUP.send(ts_GSUP_ISD_REQ(g_pars.imsi, g_pars.msisdn));
@@ -826,11 +861,10 @@
GSUP.send(ts_GSUP_UL_RES(g_pars.imsi));
}
-function f_accept_reject_lu() runs on BSC_ConnHdlr {
+altstep as_accept_reject_lu(boolean expect_accept := true) runs on BSC_ConnHdlr {
var PDU_DTAP_MT dtap_mt;
- alt {
- [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Acc)) -> value dtap_mt {
+ [expect_accept] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Acc)) -> value dtap_mt {
var PDU_ML3_LocationUpdateAccept lu_acc := dtap_mt.dtap.msgs.mm.locationUpdateAccept;
if (g_pars.net.expect_tmsi) {
if (not ispresent(lu_acc.mobileIdentityTLV) or
@@ -848,16 +882,23 @@
mtc.stop;
}
}
- }
- [] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)) {
+
+ /* Wait for MM-Information (if enabled) */
+ f_expect_mm_info();
+ setverdict(pass);
+ }
+ [expect_accept] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)) {
setverdict(fail, "Expected LU ACK, but received LU REJ");
mtc.stop;
- }
}
- /* Wait for MM-Information (if enabled) */
- f_expect_mm_info();
- setverdict(pass);
+ [not expect_accept] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Rej)) {
+ setverdict(pass);
+ }
+ [not expect_accept] BSSAP.receive(tr_PDU_DTAP_MT(tr_ML3_MT_LU_Acc)) -> value dtap_mt {
+ setverdict(fail, "Expected LU REJ, but received LU ACK");
+ mtc.stop;
+ }
}
function f_expect_lu_reject(template OCT1 cause := ?) runs on BSC_ConnHdlr {
diff --git a/msc/MSC_Tests.ttcn b/msc/MSC_Tests.ttcn
index e6d270d..36fe1eb 100644
--- a/msc/MSC_Tests.ttcn
+++ b/msc/MSC_Tests.ttcn
@@ -371,7 +371,11 @@
runs on MTC_CT return BSC_ConnHdlrPars {
var BSC_ConnHdlrNetworkPars net_pars := {
kc_support := '0A'O, /* A5/1 and A5/3 enabled */
+ net_config := { "authentication optional", "encryption a5 0" },
+ expect_attach_success := true,
expect_tmsi := true,
+ expect_auth_attempt := false,
+ hlr_has_auth_info := true,
expect_auth := false,
expect_ciph := false,
expect_imei := false,
@@ -4474,7 +4478,7 @@
/* TODO: Verify MSC is using the best cipher available! How? */
f_msc_lu_hlr();
- f_accept_reject_lu();
+ as_accept_reject_lu();
f_expect_clear();
setverdict(pass);
}
@@ -6848,6 +6852,325 @@
vc_conn.done;
}
+/* a5 0 a5 0 a5 0 3 a5 0 3 a5 3 a5 3
+ * HLR has auth info no yes no yes no yes
+ *
+ * test case index [0] [1] [2] [3] [4] [5]
+ * authentication optional No auth No auth attempt auth, auth reject auth
+ * (%) fall back to +ciph +ciph
+ * no-auth
+ *
+ * [6] [7] [8] [9] [10] [11]
+ * authentication mandatory reject auth reject auth reject auth
+ * only +ciph +ciph
+ *
+ * (%): Arguably, when HLR has auth info, the MSC should use it. Current behavior of osmo-msc is to not attempt auth at
+ * all. Related: OS#4830.
+ */
+type record of BSC_ConnHdlrNetworkPars rof_netpars;
+
+const rof_netpars auth_options_testcases := {
+ {
+ /* [0] auth optional, encr a5 0: no-auth" */
+ kc_support := '01'O,
+ net_config := { "authentication optional",
+ "encryption a5 0" },
+ expect_attach_success := true,
+ expect_tmsi := true,
+ expect_auth_attempt := false,
+ hlr_has_auth_info := false,
+ expect_auth := false,
+ expect_ciph := false,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ },
+ {
+ /* [1] auth optional, encr a5 0, HLR HAS auth info: no-auth */
+ kc_support := '01'O,
+ net_config := { "authentication optional",
+ "encryption a5 0" },
+ expect_attach_success := true,
+ expect_tmsi := true,
+ expect_auth_attempt := false,
+ hlr_has_auth_info := true,
+ expect_auth := false,
+ expect_ciph := false,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ },
+ {
+ /* [2] auth optional, encr a5 0 3, HLR has NO Auth Info: Fall back to no-auth" */
+ kc_support := '09'O,
+ net_config := { "authentication optional",
+ "encryption a5 0 3" },
+ expect_attach_success := true,
+ expect_tmsi := true,
+ expect_auth_attempt := true,
+ hlr_has_auth_info := false,
+ expect_auth := false,
+ expect_ciph := false,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ },
+ {
+ /* [3] auth optional, encr a5 0 3, HLR HAS Auth Info: Use A5/3 */
+ kc_support := '09'O,
+ net_config := { "authentication optional",
+ "encryption a5 0 3" },
+ expect_attach_success := true,
+ expect_tmsi := true,
+ expect_auth_attempt := true,
+ hlr_has_auth_info := true,
+ expect_auth := true,
+ expect_ciph := true,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ },
+ {
+ /* [4] auth optional, encr a5 3, HLR has NO Auth Info: reject.
+ * Auth is required implicitly because ciph is required. */
+ kc_support := '08'O,
+ net_config := { "authentication optional",
+ "encryption a5 3" },
+ expect_attach_success := false,
+ expect_tmsi := true,
+ expect_auth_attempt := true,
+ hlr_has_auth_info := false,
+ expect_auth := false,
+ expect_ciph := false,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ },
+ {
+ /* [5] auth optional, encr a5 3, HLR HAS Auth Info: auth + ciph.
+ * Auth is required implicitly because ciph is required. */
+ kc_support := '08'O,
+ net_config := { "authentication optional",
+ "encryption a5 3" },
+ expect_attach_success := true,
+ expect_tmsi := true,
+ expect_auth_attempt := true,
+ hlr_has_auth_info := true,
+ expect_auth := true,
+ expect_ciph := true,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ },
+
+ /* Same as above, but with 'authentication required' */
+
+ {
+ /* [6] auth required, encr a5 0, HLR has NO auth info: reject */
+ kc_support := '01'O,
+ net_config := { "authentication required",
+ "encryption a5 0" },
+ expect_attach_success := false,
+ expect_tmsi := true,
+ expect_auth_attempt := true,
+ hlr_has_auth_info := false,
+ expect_auth := false,
+ expect_ciph := false,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ },
+ {
+ /* [7] auth required, encr a5 0, HLR HAS auth info: do auth, no ciph" */
+ kc_support := '01'O,
+ net_config := { "authentication required",
+ "encryption a5 0" },
+ expect_attach_success := true,
+ expect_tmsi := true,
+ expect_auth_attempt := true,
+ hlr_has_auth_info := true,
+ expect_auth := true,
+ expect_ciph := false,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ },
+ {
+ /* [8] auth required, encr a5 0 3, HLR has NO Auth Info: reject */
+ kc_support := '09'O,
+ net_config := { "authentication required",
+ "encryption a5 0 3" },
+ expect_attach_success := false,
+ expect_tmsi := true,
+ expect_auth_attempt := true,
+ hlr_has_auth_info := false,
+ expect_auth := false,
+ expect_ciph := false,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ },
+ {
+ /* [9] auth required, encr a5 0 3, HLR HAS Auth Info: Use A5/3 */
+ kc_support := '09'O,
+ net_config := { "authentication required",
+ "encryption a5 0 3" },
+ expect_attach_success := true,
+ expect_tmsi := true,
+ expect_auth_attempt := true,
+ hlr_has_auth_info := true,
+ expect_auth := true,
+ expect_ciph := true,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ },
+ {
+ /* [10] auth required, encr a5 3, HLR has NO Auth Info: reject. */
+ kc_support := '08'O,
+ net_config := { "authentication required",
+ "encryption a5 3" },
+ expect_attach_success := false,
+ expect_tmsi := true,
+ expect_auth_attempt := true,
+ hlr_has_auth_info := false,
+ expect_auth := false,
+ expect_ciph := false,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ },
+ {
+ /* [11] auth required, encr a5 3, HLR HAS Auth Info: auth + ciph. */
+ kc_support := '08'O,
+ net_config := { "authentication required",
+ "encryption a5 3" },
+ expect_attach_success := true,
+ expect_tmsi := true,
+ expect_auth_attempt := true,
+ hlr_has_auth_info := true,
+ expect_auth := true,
+ expect_ciph := true,
+ expect_imei := false,
+ expect_imei_early := false,
+ check_imei_result := OSMO_GSUP_IMEI_RESULT_ACK,
+ check_imei_error := false
+ }
+};
+
+private function f_tc_auth_options(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr {
+ f_init_handler(pars);
+
+ /* Location Updating */
+ log(MSCVTY, "f_perform_lu() starting");
+ f_perform_lu();
+ log(MSCVTY, "f_perform_lu() done");
+
+ f_sleep(1.0);
+
+ if (not pars.net.expect_attach_success) {
+ /* Expected above LU to fail. In order to test CM Service Request below, a LU has to succeed first. So
+ * run another LU that will be successful. Careful not to load auth tokens into the VLR that may taint
+ * the test for CM Service Request below. */
+
+ log(MSCVTY, "Running a successful LU so that CM Service Request can be tested");
+ var BSC_ConnHdlrNetworkPars saved_net := g_pars.net;
+ g_pars.net.kc_support := '01'O;
+ g_pars.net.expect_attach_success := true;
+ g_pars.net.expect_auth_attempt := false;
+ g_pars.net.expect_auth := false;
+ g_pars.net.expect_ciph := false;
+ f_vty_config3(MSCVTY, {"network"}, {"authentication optional", "encryption a5 0"});
+ f_perform_lu();
+
+ /* Reconfigure like it was before */
+ g_pars.net := saved_net;
+ f_vty_config3(MSCVTY, {"network"}, g_pars.net.net_config);
+ log(MSCVTY, "Running a successful LU done");
+ }
+
+ /* CM Service Request */
+ log(MSCVTY, "f_establish_fully() starting");
+ f_establish_fully();
+ log(MSCVTY, "f_establish_fully() done");
+ BSSAP.send(ts_BSSMAP_ClearRequest(0));
+ f_expect_clear();
+}
+
+function f_TC_auth_options(integer tc_i) runs on MTC_CT {
+ f_init();
+
+ var BSC_ConnHdlrNetworkPars tc := auth_options_testcases[tc_i];
+
+ f_vty_config3(MSCVTY, {"network"}, tc.net_config);
+
+ var BSC_ConnHdlrPars pars := f_init_pars(42300 + tc_i);
+ pars.net := tc;
+
+ var BSC_ConnHdlr vc_conn;
+ vc_conn := f_start_handler_with_pars(refers(f_tc_auth_options), pars);
+ vc_conn.done;
+}
+
+testcase TC_auth_options_0() runs on MTC_CT {
+ f_TC_auth_options(0);
+}
+
+testcase TC_auth_options_1() runs on MTC_CT {
+ f_TC_auth_options(1);
+}
+
+testcase TC_auth_options_2() runs on MTC_CT {
+ f_TC_auth_options(2);
+}
+
+testcase TC_auth_options_3() runs on MTC_CT {
+ f_TC_auth_options(3);
+}
+
+testcase TC_auth_options_4() runs on MTC_CT {
+ f_TC_auth_options(4);
+}
+
+testcase TC_auth_options_5() runs on MTC_CT {
+ f_TC_auth_options(5);
+}
+
+testcase TC_auth_options_6() runs on MTC_CT {
+ f_TC_auth_options(6);
+}
+
+testcase TC_auth_options_7() runs on MTC_CT {
+ f_TC_auth_options(7);
+}
+
+testcase TC_auth_options_8() runs on MTC_CT {
+ f_TC_auth_options(8);
+}
+
+testcase TC_auth_options_9() runs on MTC_CT {
+ f_TC_auth_options(9);
+}
+
+testcase TC_auth_options_10() runs on MTC_CT {
+ f_TC_auth_options(10);
+}
+
+testcase TC_auth_options_11() runs on MTC_CT {
+ f_TC_auth_options(11);
+}
+
control {
execute( TC_cr_before_reset() );
execute( TC_lu_imsi_noauth_tmsi() );
@@ -7012,6 +7335,19 @@
execute( TC_call_re_establishment_ciph() );
execute( TC_cm_serv_wrong_mi() );
+
+ execute( TC_auth_options_0() );
+ execute( TC_auth_options_1() );
+ execute( TC_auth_options_2() );
+ execute( TC_auth_options_3() );
+ execute( TC_auth_options_4() );
+ execute( TC_auth_options_5() );
+ execute( TC_auth_options_6() );
+ execute( TC_auth_options_7() );
+ execute( TC_auth_options_8() );
+ execute( TC_auth_options_9() );
+ execute( TC_auth_options_10() );
+ execute( TC_auth_options_11() );
}