diff --git a/msc/BSC_ConnectionHandler.ttcn b/msc/BSC_ConnectionHandler.ttcn index 4e736b8bb..fe9187c82 100644 --- a/msc/BSC_ConnectionHandler.ttcn +++ b/msc/BSC_ConnectionHandler.ttcn @@ -78,8 +78,24 @@ type component BSC_ConnHdlr extends RAN_ConnHdlr, MNCC_ConnHdlr, GSUP_ConnHdlr, } 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 @@ runs on BSC_ConnHdlr { } 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 @@ private function f_build_lu(MobileIdentityLV mi) runs on BSC_ConnHdlr return PDU 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 @@ function f_mm_common() runs on BSC_ConnHdlr f_mm_ciph_utran(); } + if (not g_pars.net.expect_attach_success) { + return; + } f_expect_common_id(); } @@ -810,15 +842,18 @@ runs on BSC_ConnHdlr { 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 @@ function f_msc_lu_hlr() runs on BSC_ConnHdlr 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 @@ function f_accept_reject_lu() runs on BSC_ConnHdlr { 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 e6d270d0a..36fe1eb6b 100644 --- a/msc/MSC_Tests.ttcn +++ b/msc/MSC_Tests.ttcn @@ -371,7 +371,11 @@ function f_init_pars(integer imsi_suffix, boolean sgsap := false, boolean gsup : 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 @@ private function f_tc_cipher_complete_without_alg(charstring id, BSC_ConnHdlrPar /* 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 @@ testcase TC_cm_serv_wrong_mi() runs on MTC_CT { 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 @@ control { 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() ); }