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
This commit is contained in:
Neels Hofmeyr 2022-10-05 01:15:54 +02:00 committed by neels
parent 262584764f
commit e860fc49d9
2 changed files with 409 additions and 32 deletions

View File

@ -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 {

View File

@ -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() );
}