From 106a9accd457887ef551e476b0f14a939b174e2e Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Sun, 16 Mar 2025 11:54:45 +0900 Subject: [PATCH] [AMF/MME] Fix security context restoration and state transition cleanup (#3756) - Backup sensitive security context fields (e.g. xres, kasme, rand, autn, keys, counters) when transitioning from REGISTERED state. - Set the can_restore_security_context flag in common_register_state() based on whether the transition originates from a REGISTERED or de-registered state. - In emm_state_authentication(), restore the security context and revert to the REGISTERED state on authentication failure only if restoration is allowed; otherwise, transition to an exception state. - Remove the redundant unconditional state transition in the cleanup block to prevent overriding a valid restoration. --- src/amf/context.c | 58 ++++++- src/amf/context.h | 86 +++++++++- src/amf/gmm-sm.c | 56 +++++-- src/mme/emm-handler.c | 44 +++++ src/mme/emm-handler.h | 4 + src/mme/emm-sm.c | 108 ++++++------ src/mme/mme-context.c | 78 ++++++++- src/mme/mme-context.h | 91 ++++++++++- tests/attach/auth-test.c | 42 +++-- tests/registration/auth-test.c | 282 +++++++++++++++++++++++++++----- tests/registration/reset-test.c | 155 +++++++++++++++++- 11 files changed, 858 insertions(+), 146 deletions(-) diff --git a/src/amf/context.c b/src/amf/context.c index 6880c9604..854dc6d91 100644 --- a/src/amf/context.c +++ b/src/amf/context.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -2653,6 +2653,62 @@ uint8_t amf_selected_enc_algorithm(amf_ue_t *amf_ue) return 0; } +/* Backup the sensitive security context fields from the UE context */ +void amf_backup_security_context(amf_ue_t *amf_ue, + amf_security_context_t *backup) +{ + ogs_assert(amf_ue); + ogs_assert(backup); + + memcpy(&backup->ue_security_capability, &amf_ue->ue_security_capability, + sizeof(backup->ue_security_capability)); + memcpy(&backup->ue_network_capability, &amf_ue->ue_network_capability, + sizeof(backup->ue_network_capability)); + memcpy(backup->rand, amf_ue->rand, OGS_RAND_LEN); + memcpy(backup->autn, amf_ue->autn, OGS_AUTN_LEN); + memcpy(backup->xres_star, amf_ue->xres_star, OGS_MAX_RES_LEN); + memcpy(backup->abba, amf_ue->abba, OGS_NAS_MAX_ABBA_LEN); + backup->abba_len = amf_ue->abba_len; + memcpy(backup->hxres_star, amf_ue->hxres_star, OGS_MAX_RES_LEN); + memcpy(backup->kamf, amf_ue->kamf, OGS_SHA256_DIGEST_SIZE); + memcpy(backup->knas_int, amf_ue->knas_int, OGS_SHA256_DIGEST_SIZE/2); + memcpy(backup->knas_enc, amf_ue->knas_enc, OGS_SHA256_DIGEST_SIZE/2); + backup->dl_count = amf_ue->dl_count; + backup->ul_count = amf_ue->ul_count.i32; + memcpy(backup->kgnb, amf_ue->kgnb, OGS_SHA256_DIGEST_SIZE); + memcpy(backup->nh, amf_ue->nh, OGS_SHA256_DIGEST_SIZE); + backup->selected_enc_algorithm = amf_ue->selected_enc_algorithm; + backup->selected_int_algorithm = amf_ue->selected_int_algorithm; +} + +/* Restore the sensitive security context fields into the UE context */ +void amf_restore_security_context(amf_ue_t *amf_ue, + const amf_security_context_t *backup) +{ + ogs_assert(amf_ue); + ogs_assert(backup); + + memcpy(&amf_ue->ue_security_capability, &backup->ue_security_capability, + sizeof(amf_ue->ue_security_capability)); + memcpy(&amf_ue->ue_network_capability, &backup->ue_network_capability, + sizeof(amf_ue->ue_network_capability)); + memcpy(amf_ue->rand, backup->rand, OGS_RAND_LEN); + memcpy(amf_ue->autn, backup->autn, OGS_AUTN_LEN); + memcpy(amf_ue->xres_star, backup->xres_star, OGS_MAX_RES_LEN); + memcpy(amf_ue->abba, backup->abba, OGS_NAS_MAX_ABBA_LEN); + amf_ue->abba_len = backup->abba_len; + memcpy(amf_ue->hxres_star, backup->hxres_star, OGS_MAX_RES_LEN); + memcpy(amf_ue->kamf, backup->kamf, OGS_SHA256_DIGEST_SIZE); + memcpy(amf_ue->knas_int, backup->knas_int, OGS_SHA256_DIGEST_SIZE/2); + memcpy(amf_ue->knas_enc, backup->knas_enc, OGS_SHA256_DIGEST_SIZE/2); + amf_ue->dl_count = backup->dl_count; + amf_ue->ul_count.i32 = backup->ul_count; + memcpy(amf_ue->kgnb, backup->kgnb, OGS_SHA256_DIGEST_SIZE); + memcpy(amf_ue->nh, backup->nh, OGS_SHA256_DIGEST_SIZE); + amf_ue->selected_enc_algorithm = backup->selected_enc_algorithm; + amf_ue->selected_int_algorithm = backup->selected_int_algorithm; +} + void amf_clear_subscribed_info(amf_ue_t *amf_ue) { int i, j; diff --git a/src/amf/context.h b/src/amf/context.h index 92732dd1d..68b17df7c 100644 --- a/src/amf/context.h +++ b/src/amf/context.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -237,6 +237,67 @@ struct ran_ue_s { ogs_pool_id_t amf_ue_id; }; +typedef struct amf_security_context_s { + /* UE security capability info: supported security features. */ + ogs_nas_ue_security_capability_t ue_security_capability; + /* UE network capability info: supported network features. */ + ogs_nas_ue_network_capability_t ue_network_capability; + + /* Random challenge value */ + uint8_t rand[OGS_RAND_LEN]; + /* Authentication token */ + uint8_t autn[OGS_AUTN_LEN]; + /* Expected auth response. */ + uint8_t xres_star[OGS_MAX_RES_LEN]; + + /* NAS backoff parameter value. */ + uint8_t abba[OGS_NAS_MAX_ABBA_LEN]; + uint8_t abba_len; + + /* Hash of XRES*. */ + uint8_t hxres_star[OGS_MAX_RES_LEN]; + /* Key for AMF derived from NAS key. */ + uint8_t kamf[OGS_SHA256_DIGEST_SIZE]; + + /* Integrity and ciphering keys */ + uint8_t knas_int[OGS_SHA256_DIGEST_SIZE/2]; + uint8_t knas_enc[OGS_SHA256_DIGEST_SIZE/2]; + /* Downlink counter */ + uint32_t dl_count; + /* Uplink counter (24-bit stored in uint32_t) */ + uint32_t ul_count; + /* gNB key derived from kasme */ + uint8_t kgnb[OGS_SHA256_DIGEST_SIZE]; + + /* + * Next Hop Channing Counter + * + * Note that the "nhcc" field is not included in the backup + * because it is a transient counter used only during next-hop key + * derivation. In our design, only the persistent keying material + * and related values that are required to recreate the security context + * are backed up. The nhcc value is recalculated or updated dynamically + * when the next hop key is derived (e.g. via ogs_kdf_nh_enb()), + * so it is not necessary to store it in the backup. + * + * If there is a requirement to preserve the exact nhcc value across state + * transitions, you could add it to the backup structure, but typically + * it is treated as a computed, temporary value that can be reinitialized + * safely without compromising the security context. + * struct { + * ED2(uint8_t nhcc_spare:5;, + * uint8_t nhcc:3;) + * }; + */ + + /* Next hop key */ + uint8_t nh[OGS_SHA256_DIGEST_SIZE]; + + /* Selected algorithms (set by UDM/subscription) */ + uint8_t selected_enc_algorithm; + uint8_t selected_int_algorithm; +} amf_security_context_t; + struct amf_ue_s { ogs_sbi_object_t sbi; ogs_pool_id_t id; @@ -367,6 +428,12 @@ struct amf_ue_s { int security_context_available; int mac_failed; + /* flag: 1 = allow restoration of security context, 0 = disallow */ + bool can_restore_security_context; + + /* Backup of security context fields */ + amf_security_context_t sec_backup; + /* Security Context */ ogs_nas_ue_security_capability_t ue_security_capability; ogs_nas_ue_network_capability_t ue_network_capability; @@ -391,20 +458,29 @@ struct amf_ue_s { char *resource_uri; ogs_sbi_client_t *client; } confirmation_for_5g_aka; + /* Random challenge value */ uint8_t rand[OGS_RAND_LEN]; + /* Authentication token */ uint8_t autn[OGS_AUTN_LEN]; + /* Expected auth response. */ uint8_t xres_star[OGS_MAX_RES_LEN]; + /* NAS backoff parameter value. */ uint8_t abba[OGS_NAS_MAX_ABBA_LEN]; uint8_t abba_len; + /* Hash of XRES*. */ uint8_t hxres_star[OGS_MAX_RES_LEN]; + /* Key for AMF derived from NAS key. */ uint8_t kamf[OGS_SHA256_DIGEST_SIZE]; OpenAPI_auth_result_e auth_result; + /* Integrity and ciphering keys */ uint8_t knas_int[OGS_SHA256_DIGEST_SIZE/2]; uint8_t knas_enc[OGS_SHA256_DIGEST_SIZE/2]; + /* Downlink counter */ uint32_t dl_count; + /* Uplink counter (24-bit stored in uint32_t) */ union { struct { ED3(uint8_t spare;, @@ -413,14 +489,17 @@ struct amf_ue_s { } __attribute__ ((packed)); uint32_t i32; } ul_count; + /* gNB key derived from kasme */ uint8_t kgnb[OGS_SHA256_DIGEST_SIZE]; struct { ED2(uint8_t nhcc_spare:5;, uint8_t nhcc:3;) /* Next Hop Channing Counter */ }; + /* Next hop key */ uint8_t nh[OGS_SHA256_DIGEST_SIZE]; /* NH Security Key */ + /* Selected algorithms (set by UDM/subscription) */ /* defined in 'lib/nas/common/types.h' * #define OGS_NAS_SECURITY_ALGORITHMS_NEA0 0 * #define OGS_NAS_SECURITY_ALGORITHMS_128_NEA1 1 @@ -1006,6 +1085,11 @@ int amf_m_tmsi_free(amf_m_tmsi_t *tmsi); uint8_t amf_selected_int_algorithm(amf_ue_t *amf_ue); uint8_t amf_selected_enc_algorithm(amf_ue_t *amf_ue); +void amf_backup_security_context( + amf_ue_t *amf_ue, amf_security_context_t *backup); +void amf_restore_security_context( + amf_ue_t *amf_ue, const amf_security_context_t *backup); + void amf_clear_subscribed_info(amf_ue_t *amf_ue); bool amf_update_allowed_nssai(amf_ue_t *amf_ue); diff --git a/src/amf/gmm-sm.c b/src/amf/gmm-sm.c index 78b79d0db..71a43bf54 100644 --- a/src/amf/gmm-sm.c +++ b/src/amf/gmm-sm.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019,2020 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -1279,6 +1279,16 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, ogs_assert(amf_ue); } + /* If transition is from REGISTERED, allow restoration */ + if (state == GMM_COMMON_STATE_REGISTERED) { + amf_ue->can_restore_security_context = 1; + amf_backup_security_context(amf_ue, &amf_ue->sec_backup); + } else if (state == GMM_COMMON_STATE_DEREGISTERED) { + /* Transition from de-registered: do not restore */ + amf_ue->can_restore_security_context = 0; + } else + ogs_assert_if_reached(); + switch (e->h.id) { case AMF_EVENT_5GMM_MESSAGE: nas_message = e->nas.message; @@ -1726,10 +1736,11 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e) amf_ue, &nas_message->gmm.authentication_response); if (rv != OGS_OK) { + ogs_error("gmm_handle_authentication_response() failed"); r = nas_5gs_send_authentication_reject(amf_ue); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception); + goto cleanup; } break; @@ -1740,7 +1751,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e) authentication_failure_parameter; ogs_assert(authentication_failure_parameter); - ogs_debug("[%s] Authentication failure [%d]", amf_ue->suci, + ogs_warn("[%s] Authentication failure [%d]", amf_ue->suci, authentication_failure->gmm_cause); amf_metrics_inst_by_cause_add(authentication_failure->gmm_cause, @@ -1793,9 +1804,8 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e) r = nas_5gs_send_authentication_reject(amf_ue); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception); + goto cleanup; - break; case OGS_NAS_5GS_REGISTRATION_REQUEST: ogs_warn("Registration request"); gmm_cause = gmm_handle_registration_request( @@ -1807,8 +1817,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e) r = nas_5gs_send_registration_reject(ran_ue, amf_ue, gmm_cause); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, gmm_state_exception); - break; + goto cleanup; } r = amf_ue_sbi_discover_and_send( @@ -1846,7 +1855,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e) r = nas_5gs_send_authentication_reject(amf_ue); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception); + goto cleanup; } else { amf_ue->t3560.retry_count++; r = nas_5gs_send_authentication_request(amf_ue); @@ -1879,12 +1888,12 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e) ogs_error("[%s] HTTP response error [%d]", amf_ue->suci, sbi_message->res_status); } + r = nas_5gs_send_gmm_reject_from_sbi( amf_ue, sbi_message->res_status); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception); - break; + goto cleanup; } SWITCH(sbi_message->h.method) @@ -1897,7 +1906,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e) r = nas_5gs_send_authentication_reject(amf_ue); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception); + goto cleanup; } break; CASE(OGS_SBI_HTTP_METHOD_PUT) @@ -1909,7 +1918,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e) r = nas_5gs_send_authentication_reject(amf_ue); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception); + goto cleanup; } else { amf_ue->selected_int_algorithm = amf_selected_int_algorithm(amf_ue); @@ -1923,8 +1932,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e) "bypassed with NIA0", amf_ue->selected_enc_algorithm, amf_ue->selected_int_algorithm); - OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception); - break; + goto cleanup; } OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_security_mode); @@ -2035,6 +2043,26 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e) ogs_error("Unknown event[%s]", amf_event_get_name(e)); break; } + + return; + +cleanup: + if (amf_ue->can_restore_security_context) { + /* If allowed, restore the security context */ + amf_restore_security_context(amf_ue, &amf_ue->sec_backup); + + amf_ue->security_context_available = 1; + amf_ue->mac_failed = 0; + + OGS_FSM_TRAN(s, &gmm_state_registered); + ogs_warn("[%s] Auth failure in registered trans; " + "restoring context and going to REGISTERED.", amf_ue->supi); + } else { + /* Do not restore; transition to exception state */ + OGS_FSM_TRAN(s, &gmm_state_exception); + ogs_warn("[%s] Auth failure in de-registered trans; " + "no context restoration.", amf_ue->supi); + } } void gmm_state_security_mode(ogs_fsm_t *s, amf_event_t *e) diff --git a/src/mme/emm-handler.c b/src/mme/emm-handler.c index 07339fde7..f1c227211 100644 --- a/src/mme/emm-handler.c +++ b/src/mme/emm-handler.c @@ -379,6 +379,50 @@ int emm_handle_attach_complete( return r; } +int emm_handle_authentication_response( + enb_ue_t *enb_ue, mme_ue_t *mme_ue, + ogs_nas_eps_authentication_response_t *authentication_response) +{ + ogs_nas_authentication_response_parameter_t + *authentication_response_parameter = + &authentication_response->authentication_response_parameter; + + ogs_assert(authentication_response); + + ogs_assert(mme_ue); + ogs_assert(enb_ue); + + ogs_debug("Authentication response"); + ogs_debug(" IMSI[%s]", mme_ue->imsi_bcd); + + CLEAR_MME_UE_TIMER(mme_ue->t3460); + + if (authentication_response_parameter->length == 0 || + memcmp(authentication_response_parameter->res, mme_ue->xres, + authentication_response_parameter->length) != 0) { + ogs_log_hexdump(OGS_LOG_WARN, + authentication_response_parameter->res, + authentication_response_parameter->length); + ogs_log_hexdump(OGS_LOG_WARN, + mme_ue->xres, OGS_MAX_RES_LEN); + return OGS_ERROR; + } else { + mme_ue->selected_int_algorithm = mme_selected_int_algorithm(mme_ue); + mme_ue->selected_enc_algorithm = mme_selected_enc_algorithm(mme_ue); + + if (mme_ue->selected_int_algorithm == + OGS_NAS_SECURITY_ALGORITHMS_EIA0) { + ogs_error("Encrypt[0x%x] can be skipped with EEA0, " + "but Integrity[0x%x] cannot be bypassed with EIA0", + mme_ue->selected_enc_algorithm, + mme_ue->selected_int_algorithm); + return OGS_ERROR; + } + } + + return OGS_OK; +} + int emm_handle_identity_response( enb_ue_t *enb_ue, mme_ue_t *mme_ue, ogs_nas_eps_identity_response_t *identity_response) diff --git a/src/mme/emm-handler.h b/src/mme/emm-handler.h index 0c5af4d07..f80da9cd2 100644 --- a/src/mme/emm-handler.h +++ b/src/mme/emm-handler.h @@ -33,6 +33,10 @@ int emm_handle_attach_complete( enb_ue_t *enb_ue, mme_ue_t *mme_ue, ogs_nas_eps_attach_complete_t *attach_complete); +int emm_handle_authentication_response( + enb_ue_t *enb_ue, mme_ue_t *mme_ue, + ogs_nas_eps_authentication_response_t *authentication_response); + int emm_handle_identity_response( enb_ue_t *enb_ue, mme_ue_t *mme_ue, ogs_nas_eps_identity_response_t *identity_response); diff --git a/src/mme/emm-sm.c b/src/mme/emm-sm.c index 20643b71d..84268fb93 100644 --- a/src/mme/emm-sm.c +++ b/src/mme/emm-sm.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2024 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -304,6 +304,15 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, mme_ue = mme_ue_find_by_id(e->mme_ue_id); ogs_assert(mme_ue); + /* If transition is from REGISTERED, allow restoration */ + if (state == EMM_COMMON_STATE_REGISTERED) { + mme_ue->can_restore_security_context = 1; + mme_backup_security_context(mme_ue, &mme_ue->sec_backup); + } else if (state == EMM_COMMON_STATE_DEREGISTERED) { + /* Transition from de-registered: do not restore */ + mme_ue->can_restore_security_context = 0; + } + switch (e->id) { case MME_EVENT_EMM_MESSAGE: message = e->nas_message; @@ -1060,6 +1069,8 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e) enb_ue_t *enb_ue = NULL; ogs_nas_eps_message_t *message = NULL; + ogs_nas_eps_authentication_failure_t *authentication_failure = NULL; + ogs_assert(s); ogs_assert(e); @@ -1082,64 +1093,27 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e) switch (message->emm.h.message_type) { case OGS_NAS_EPS_AUTHENTICATION_RESPONSE: - { - ogs_nas_eps_authentication_response_t *authentication_response = - &message->emm.authentication_response; - ogs_nas_authentication_response_parameter_t - *authentication_response_parameter = - &authentication_response-> - authentication_response_parameter; - - ogs_debug("Authentication response"); - ogs_debug(" IMSI[%s]", mme_ue->imsi_bcd); - - CLEAR_MME_UE_TIMER(mme_ue->t3460); - - if (authentication_response_parameter->length == 0 || - memcmp(authentication_response_parameter->res, - mme_ue->xres, - authentication_response_parameter->length) != 0) { - ogs_log_hexdump(OGS_LOG_WARN, - authentication_response_parameter->res, - authentication_response_parameter->length); - ogs_log_hexdump(OGS_LOG_WARN, - mme_ue->xres, OGS_MAX_RES_LEN); + rv = emm_handle_authentication_response(enb_ue, mme_ue, + &message->emm.authentication_response); + if (rv != OGS_OK) { + ogs_error("emm_handle_authentication_response() failed"); r = nas_eps_send_authentication_reject(mme_ue); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(&mme_ue->sm, &emm_state_exception); - } else { - mme_ue->selected_int_algorithm = - mme_selected_int_algorithm(mme_ue); - mme_ue->selected_enc_algorithm = - mme_selected_enc_algorithm(mme_ue); - - if (mme_ue->selected_int_algorithm == - OGS_NAS_SECURITY_ALGORITHMS_EIA0) { - ogs_error("Encrypt[0x%x] can be skipped with EEA0, " - "but Integrity[0x%x] cannot be bypassed with EIA0", - mme_ue->selected_enc_algorithm, - mme_ue->selected_int_algorithm); - OGS_FSM_TRAN(&mme_ue->sm, &emm_state_exception); - break; - } - - OGS_FSM_TRAN(&mme_ue->sm, &emm_state_security_mode); + goto cleanup; } + OGS_FSM_TRAN(&mme_ue->sm, &emm_state_security_mode); break; - } case OGS_NAS_EPS_AUTHENTICATION_FAILURE: - { - ogs_nas_eps_authentication_failure_t *authentication_failure = - &message->emm.authentication_failure; + authentication_failure = &message->emm.authentication_failure; ogs_nas_authentication_failure_parameter_t *authentication_failure_parameter = &authentication_failure-> authentication_failure_parameter; - ogs_debug("Authentication failure"); - ogs_debug(" IMSI[%s] OGS_NAS_EMM_CAUSE[%d]", mme_ue->imsi_bcd, + ogs_warn("Authentication failure"); + ogs_warn(" IMSI[%s] OGS_NAS_EMM_CAUSE[%d]", mme_ue->imsi_bcd, authentication_failure->emm_cause); CLEAR_MME_UE_TIMER(mme_ue->t3460); @@ -1167,18 +1141,15 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e) r = nas_eps_send_authentication_reject(mme_ue); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(&mme_ue->sm, &emm_state_exception); + goto cleanup; - break; - } case OGS_NAS_EPS_ATTACH_REQUEST: ogs_warn("[%s] Attach request", mme_ue->imsi_bcd); rv = emm_handle_attach_request( enb_ue, mme_ue, &message->emm.attach_request, e->pkbuf); if (rv != OGS_OK) { ogs_error("emm_handle_attach_request() failed"); - OGS_FSM_TRAN(s, emm_state_exception); - break; + goto cleanup; } mme_s6a_send_air(enb_ue, mme_ue, NULL); @@ -1194,8 +1165,7 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e) enb_ue, mme_ue, &message->emm.detach_request_from_ue); if (rv != OGS_OK) { ogs_error("emm_handle_detach_request() failed"); - OGS_FSM_TRAN(s, emm_state_exception); - break; + goto cleanup; } if (!MME_UE_HAVE_IMSI(mme_ue)) { @@ -1203,8 +1173,7 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e) ogs_assert(OGS_OK == nas_eps_send_service_reject(enb_ue, mme_ue, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK)); - OGS_FSM_TRAN(s, &emm_state_exception); - break; + goto cleanup; } if (!SECURITY_CONTEXT_IS_VALID(mme_ue)) { @@ -1212,8 +1181,7 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e) ogs_assert(OGS_OK == nas_eps_send_service_reject(enb_ue, mme_ue, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK)); - OGS_FSM_TRAN(s, &emm_state_exception); - break; + goto cleanup; } /* @@ -1243,11 +1211,10 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e) mme_timer_cfg(MME_TIMER_T3460)->max_count) { ogs_warn("Retransmission of IMSI[%s] failed. " "Stop retransmission", mme_ue->imsi_bcd); - OGS_FSM_TRAN(&mme_ue->sm, &emm_state_exception); - r = nas_eps_send_authentication_reject(mme_ue); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); + goto cleanup; } else { mme_ue->t3460.retry_count++; r = nas_eps_send_authentication_request(mme_ue); @@ -1265,6 +1232,27 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e) ogs_error("Unknown event[%s]", mme_event_get_name(e)); break; } + + return; + +cleanup: + if (mme_ue->can_restore_security_context) { + /* If allowed, restore the security context */ + mme_restore_security_context(mme_ue, &mme_ue->sec_backup); + + mme_ue->security_context_available = 1; + mme_ue->mac_failed = 0; + + OGS_FSM_TRAN(s, &emm_state_registered); + ogs_warn("[%s] Auth failure in registered trans; " + "restoring context and going to REGISTERED.", + mme_ue->imsi_bcd); + } else { + /* Do not restore; transition to exception state */ + OGS_FSM_TRAN(s, &emm_state_exception); + ogs_warn("[%s] Auth failure in de-registered trans; " + "no context restoration.", mme_ue->imsi_bcd); + } } void emm_state_security_mode(ogs_fsm_t *s, mme_event_t *e) diff --git a/src/mme/mme-context.c b/src/mme/mme-context.c index 413715ca6..77bb2408f 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -5030,6 +5030,82 @@ uint8_t mme_selected_enc_algorithm(mme_ue_t *mme_ue) return 0; } +/* Backup the sensitive security context fields from the UE context */ +void mme_backup_security_context( + mme_ue_t *mme_ue, mme_security_context_t *backup) +{ + ogs_assert(mme_ue); + ogs_assert(backup); + + memcpy(&backup->ue_network_capability, + &mme_ue->ue_network_capability, + sizeof(backup->ue_network_capability)); + memcpy(&backup->ms_network_capability, + &mme_ue->ms_network_capability, + sizeof(backup->ms_network_capability)); + memcpy(&backup->ue_additional_security_capability, + &mme_ue->ue_additional_security_capability, + sizeof(backup->ue_additional_security_capability)); + memcpy(backup->xres, mme_ue->xres, OGS_MAX_RES_LEN); + backup->xres_len = mme_ue->xres_len; + memcpy(backup->kasme, mme_ue->kasme, OGS_SHA256_DIGEST_SIZE); + memcpy(backup->rand, mme_ue->rand, OGS_RAND_LEN); + memcpy(backup->autn, mme_ue->autn, OGS_AUTN_LEN); + memcpy(backup->knas_int, mme_ue->knas_int, + OGS_SHA256_DIGEST_SIZE / 2); + memcpy(backup->knas_enc, mme_ue->knas_enc, + OGS_SHA256_DIGEST_SIZE / 2); + backup->dl_count = mme_ue->dl_count; + backup->ul_count = mme_ue->ul_count.i32; + memcpy(backup->kenb, mme_ue->kenb, OGS_SHA256_DIGEST_SIZE); + memcpy(backup->hash_mme, mme_ue->hash_mme, OGS_HASH_MME_LEN); + backup->nonceue = mme_ue->nonceue; + backup->noncemme = mme_ue->noncemme; + backup->gprs_ciphering_key_sequence_number = + mme_ue->gprs_ciphering_key_sequence_number; + memcpy(backup->nh, mme_ue->nh, OGS_SHA256_DIGEST_SIZE); + backup->selected_enc_algorithm = mme_ue->selected_enc_algorithm; + backup->selected_int_algorithm = mme_ue->selected_int_algorithm; +} + +/* Restore the sensitive security context fields into the UE context */ +void mme_restore_security_context( + mme_ue_t *mme_ue, const mme_security_context_t *backup) +{ + ogs_assert(mme_ue); + ogs_assert(backup); + + memcpy(&mme_ue->ue_network_capability, + &backup->ue_network_capability, + sizeof(mme_ue->ue_network_capability)); + memcpy(&mme_ue->ms_network_capability, + &backup->ms_network_capability, + sizeof(mme_ue->ms_network_capability)); + memcpy(&mme_ue->ue_additional_security_capability, + &backup->ue_additional_security_capability, + sizeof(mme_ue->ue_additional_security_capability)); + memcpy(mme_ue->xres, backup->xres, OGS_MAX_RES_LEN); + mme_ue->xres_len = backup->xres_len; + memcpy(mme_ue->kasme, backup->kasme, OGS_SHA256_DIGEST_SIZE); + memcpy(mme_ue->rand, backup->rand, OGS_RAND_LEN); + memcpy(mme_ue->autn, backup->autn, OGS_AUTN_LEN); + memcpy(mme_ue->knas_int, backup->knas_int, + OGS_SHA256_DIGEST_SIZE / 2); + memcpy(mme_ue->knas_enc, backup->knas_enc, + OGS_SHA256_DIGEST_SIZE / 2); + mme_ue->dl_count = backup->dl_count; + mme_ue->ul_count.i32 = backup->ul_count; + memcpy(mme_ue->kenb, backup->kenb, OGS_SHA256_DIGEST_SIZE); + memcpy(mme_ue->hash_mme, backup->hash_mme, OGS_HASH_MME_LEN); + mme_ue->nonceue = backup->nonceue; + mme_ue->noncemme = backup->noncemme; + mme_ue->gprs_ciphering_key_sequence_number = + backup->gprs_ciphering_key_sequence_number; + memcpy(mme_ue->nh, backup->nh, OGS_SHA256_DIGEST_SIZE); + mme_ue->selected_enc_algorithm = backup->selected_enc_algorithm; + mme_ue->selected_int_algorithm = backup->selected_int_algorithm; +} + static void stats_add_enb_ue(void) { mme_metrics_inst_global_inc(MME_METR_GLOB_GAUGE_ENB_UE); diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index 7721a5c4c..45fd3659c 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -342,6 +342,69 @@ struct sgw_ue_s { ogs_pool_id_t mme_ue_id; }; +typedef struct mme_security_context_s { + /* UE network capability info: supported network features. */ + ogs_nas_ue_network_capability_t ue_network_capability; + /* MS network capability info: supported network features. */ + ogs_nas_ms_network_capability_t ms_network_capability; + /* UE additional security capability: extra security features. */ + ogs_nas_ue_additional_security_capability_t + ue_additional_security_capability; + + /* Expected response and its length */ + uint8_t xres[OGS_MAX_RES_LEN]; + uint8_t xres_len; + /* Derived key from HSS */ + uint8_t kasme[OGS_SHA256_DIGEST_SIZE]; + /* Random challenge value */ + uint8_t rand[OGS_RAND_LEN]; + /* Authentication token */ + uint8_t autn[OGS_AUTN_LEN]; + /* Integrity and ciphering keys */ + uint8_t knas_int[OGS_SHA256_DIGEST_SIZE/2]; + uint8_t knas_enc[OGS_SHA256_DIGEST_SIZE/2]; + /* Downlink counter */ + uint32_t dl_count; + /* Uplink counter (24-bit stored in uint32_t) */ + uint32_t ul_count; + /* eNB key derived from kasme */ + uint8_t kenb[OGS_SHA256_DIGEST_SIZE]; + /* Hash used for NAS message integrity */ + uint8_t hash_mme[OGS_HASH_MME_LEN]; + /* Nonces for resynchronization */ + uint32_t nonceue; + uint32_t noncemme; + /* GPRS ciphering key sequence number */ + uint8_t gprs_ciphering_key_sequence_number; + + /* + * Next Hop Channing Counter + * + * Note that the "nhcc" field is not included in the backup + * because it is a transient counter used only during next-hop key + * derivation. In our design, only the persistent keying material + * and related values that are required to recreate the security context + * are backed up. The nhcc value is recalculated or updated dynamically + * when the next hop key is derived (e.g. via ogs_kdf_nh_enb()), + * so it is not necessary to store it in the backup. + * + * If there is a requirement to preserve the exact nhcc value across state + * transitions, you could add it to the backup structure, but typically + * it is treated as a computed, temporary value that can be reinitialized + * safely without compromising the security context. + * struct { + * ED2(uint8_t nhcc_spare:5;, + * uint8_t nhcc:3;) + * }; + */ + + /* Next hop key */ + uint8_t nh[OGS_SHA256_DIGEST_SIZE]; + /* Selected algorithms (set by HSS/subscription) */ + uint8_t selected_enc_algorithm; + uint8_t selected_int_algorithm; +} mme_security_context_t; + struct mme_ue_s { ogs_lnode_t lnode; ogs_pool_id_t id; @@ -462,19 +525,32 @@ struct mme_ue_s { int security_context_available; int mac_failed; + /* flag: 1 = allow restoration of security context, 0 = disallow */ + bool can_restore_security_context; + + /* Backup of security context fields */ + mme_security_context_t sec_backup; + /* Security Context */ ogs_nas_ue_network_capability_t ue_network_capability; ogs_nas_ms_network_capability_t ms_network_capability; ogs_nas_ue_additional_security_capability_t ue_additional_security_capability; + /* Expected response and its length */ uint8_t xres[OGS_MAX_RES_LEN]; uint8_t xres_len; + /* Derived key from HSS */ uint8_t kasme[OGS_SHA256_DIGEST_SIZE]; + /* Random challenge value */ uint8_t rand[OGS_RAND_LEN]; + /* Authentication token */ uint8_t autn[OGS_AUTN_LEN]; + /* Integrity and ciphering keys */ uint8_t knas_int[OGS_SHA256_DIGEST_SIZE/2]; uint8_t knas_enc[OGS_SHA256_DIGEST_SIZE/2]; + /* Downlink counter */ uint32_t dl_count; + /* Uplink counter (24-bit stored in i32) */ union { struct { ED3(uint8_t spare;, @@ -483,17 +559,23 @@ struct mme_ue_s { } __attribute__ ((packed)); uint32_t i32; } ul_count; + /* eNB key derived from kasme */ uint8_t kenb[OGS_SHA256_DIGEST_SIZE]; + /* Hash used for NAS message integrity */ uint8_t hash_mme[OGS_HASH_MME_LEN]; + /* Nonces for resynchronization */ uint32_t nonceue, noncemme; + /* GPRS ciphering key sequence number */ uint8_t gprs_ciphering_key_sequence_number; struct { ED2(uint8_t nhcc_spare:5;, uint8_t nhcc:3;) /* Next Hop Channing Counter */ }; - uint8_t nh[OGS_SHA256_DIGEST_SIZE]; /* NH Security Key */ + /* Next hop key */ + uint8_t nh[OGS_SHA256_DIGEST_SIZE]; + /* Selected algorithms (set by HSS/subscription) */ /* defined in 'nas_ies.h' * #define NAS_SECURITY_ALGORITHMS_EIA0 0 * #define NAS_SECURITY_ALGORITHMS_128_EEA1 1 @@ -1133,6 +1215,11 @@ void mme_ebi_pool_clear(mme_ue_t *mme_ue); uint8_t mme_selected_int_algorithm(mme_ue_t *mme_ue); uint8_t mme_selected_enc_algorithm(mme_ue_t *mme_ue); +void mme_backup_security_context( + mme_ue_t *mme_ue, mme_security_context_t *backup); +void mme_restore_security_context( + mme_ue_t *mme_ue, const mme_security_context_t *backup); + #ifdef __cplusplus } #endif diff --git a/tests/attach/auth-test.c b/tests/attach/auth-test.c index 622750b46..9a41434c6 100644 --- a/tests/attach/auth-test.c +++ b/tests/attach/auth-test.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -345,11 +345,6 @@ static void test1_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); tests1ap_recv(test_ue, recvbuf); - /* Receive UE Context Release Command */ - recvbuf = testenb_s1ap_read(s1ap); - ABTS_PTR_NOTNULL(tc, recvbuf); - tests1ap_recv(test_ue, recvbuf); - /* Send Attach Request - No Integrity */ sess->pdn_connectivity_param.eit = 1; sess->pdn_connectivity_param.pco = 1; @@ -407,17 +402,6 @@ static void test1_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); tests1ap_recv(test_ue, recvbuf); - /* Receive UE Context Release Command */ - recvbuf = testenb_s1ap_read(s1ap); - ABTS_PTR_NOTNULL(tc, recvbuf); - tests1ap_recv(test_ue, recvbuf); - - /* Send UE Context Release Complete */ - sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); - ABTS_PTR_NOTNULL(tc, sendbuf); - rv = testenb_s1ap_send(s1ap, sendbuf); - ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* * --- Immediately Re-attach Test --- * @@ -489,6 +473,30 @@ static void test1_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); tests1ap_recv(test_ue, recvbuf); + /* Send Detach Request */ + emmbuf = testemm_build_detach_request(test_ue, 1, true, false); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_initial_ue_message( + test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Signalling, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive OLD UE Context Release Command */ + enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; + + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send OLD UE Context Release Complete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; + /* Receive UE Context Release Command */ recvbuf = testenb_s1ap_read(s1ap); ABTS_PTR_NOTNULL(tc, recvbuf); diff --git a/tests/registration/auth-test.c b/tests/registration/auth-test.c index b359baa6e..d739f2bbd 100644 --- a/tests/registration/auth-test.c +++ b/tests/registration/auth-test.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019,2020 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -34,6 +34,7 @@ static void test1_func(abts_case *tc, void *data) ogs_nas_5gs_mobile_identity_suci_t mobile_identity_suci; test_ue_t *test_ue = NULL; + test_sess_t *sess = NULL; test_bearer_t *qos_flow = NULL; bson_t *doc = NULL; @@ -72,7 +73,7 @@ static void test1_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, gtpu); /* Send NG-Setup Reqeust */ - sendbuf = testngap_build_ng_setup_request(0x4000, 27); + sendbuf = testngap_build_ng_setup_request(0x4000, 22); ABTS_PTR_NOTNULL(tc, sendbuf); rv = testgnb_ngap_send(ngap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -88,6 +89,237 @@ static void test1_func(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, test_db_insert_ue(test_ue, doc)); /* Send Registration request */ + test_ue->registration_request_param.guti = 1; + gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false); + ABTS_PTR_NOTNULL(tc, gmmbuf); + + test_ue->registration_request_param.gmm_capability = 1; + test_ue->registration_request_param.s1_ue_network_capability = 1; + test_ue->registration_request_param.requested_nssai = 1; + test_ue->registration_request_param.last_visited_registered_tai = 1; + test_ue->registration_request_param.ue_usage_setting = 1; + nasbuf = testgmm_build_registration_request(test_ue, NULL, false, false); + ABTS_PTR_NOTNULL(tc, nasbuf); + + sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf, + NGAP_RRCEstablishmentCause_mo_Signalling, false, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Identity request */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Identity response */ + gmmbuf = testgmm_build_identity_response(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication request */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Authentication response */ + gmmbuf = testgmm_build_authentication_response(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode command */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Security mode complete */ + gmmbuf = testgmm_build_security_mode_complete(test_ue, nasbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive InitialContextSetupRequest + + * Registration accept */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_InitialContextSetup, + test_ue->ngap_procedure_code); + + /* Send UERadioCapabilityInfoIndication */ + sendbuf = testngap_build_ue_radio_capability_info_indication(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send InitialContextSetupResponse */ + sendbuf = testngap_build_initial_context_setup_response(test_ue, false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Registration complete */ + gmmbuf = testgmm_build_registration_complete(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Configuration update command */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send PDU session establishment request */ + sess = test_sess_add_by_dnn_and_psi(test_ue, "internet", 5); + ogs_assert(sess); + + sess->ul_nas_transport_param.request_type = + OGS_NAS_5GS_REQUEST_TYPE_INITIAL; + sess->ul_nas_transport_param.dnn = 1; + sess->ul_nas_transport_param.s_nssai = 0; + + sess->pdu_session_establishment_param.ssc_mode = 1; + sess->pdu_session_establishment_param.epco = 1; + + gsmbuf = testgsm_build_pdu_session_establishment_request(sess); + ABTS_PTR_NOTNULL(tc, gsmbuf); + gmmbuf = testgmm_build_ul_nas_transport(sess, + OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDUSessionResourceSetupRequest + + * DL NAS transport + + * PDU session establishment accept */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_PDUSessionResourceSetup, + test_ue->ngap_procedure_code); + + /* Send PDUSessionResourceSetupResponse */ + sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send UEContextReleaseRequest */ + sendbuf = testngap_build_ue_context_release_request(test_ue, + NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_user_inactivity, + true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_UEContextRelease, + test_ue->ngap_procedure_code); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Registration request */ + memset(&test_ue->registration_request_param, 0, + sizeof(test_ue->registration_request_param)); + + gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false); + ABTS_PTR_NOTNULL(tc, gmmbuf); + + test_ue->registration_request_param.gmm_capability = 1; + test_ue->registration_request_param.s1_ue_network_capability = 1; + test_ue->registration_request_param.requested_nssai = 1; + test_ue->registration_request_param.last_visited_registered_tai = 1; + test_ue->registration_request_param.ue_usage_setting = 1; + nasbuf = testgmm_build_registration_request(test_ue, NULL, false, false); + ABTS_PTR_NOTNULL(tc, nasbuf); + + sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf, + NGAP_RRCEstablishmentCause_mo_Signalling, false, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication request */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Authentication failure - SYNCH failure */ + gmmbuf = testgmm_build_authentication_failure( + test_ue, OGS_5GMM_CAUSE_SYNCH_FAILURE, 0x11223344); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication request */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Authentication failure - MAC failure */ + gmmbuf = testgmm_build_authentication_failure( + test_ue, OGS_5GMM_CAUSE_MAC_FAILURE, 0); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication reject */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send De-registration request */ + gmmbuf = testgmm_build_de_registration_request(test_ue, 1, true, true); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_UEContextRelease, + test_ue->ngap_procedure_code); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Registration request */ + memset(&test_ue->registration_request_param, 0, + sizeof(test_ue->registration_request_param)); + gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false); ABTS_PTR_NOTNULL(tc, gmmbuf); sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf, @@ -137,50 +369,14 @@ static void test1_func(abts_case *tc, void *data) NGAP_ProcedureCode_id_UEContextRelease, test_ue->ngap_procedure_code); - /* Send Registration request */ - gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false); - ABTS_PTR_NOTNULL(tc, gmmbuf); - sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf, - NGAP_RRCEstablishmentCause_mo_Signalling, false, true); + /* Send UEContextReleaseRequest */ + sendbuf = testngap_build_ue_context_release_request(test_ue, + NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_user_inactivity, + true); ABTS_PTR_NOTNULL(tc, sendbuf); rv = testgnb_ngap_send(ngap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); -#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED - /* OLD Receive UEContextReleaseCommand */ - recvbuf = testgnb_ngap_read(ngap); - ABTS_PTR_NOTNULL(tc, recvbuf); - testngap_recv(test_ue, recvbuf); - ABTS_INT_EQUAL(tc, - NGAP_ProcedureCode_id_UEContextRelease, - test_ue->ngap_procedure_code); - - /* Send OLD UEContextReleaseComplete */ - sendbuf = testngap_build_ue_context_release_complete(test_ue); - ABTS_PTR_NOTNULL(tc, sendbuf); - rv = testgnb_ngap_send(ngap, sendbuf); - ABTS_INT_EQUAL(tc, OGS_OK, rv); -#endif - - /* Receive Authentication request */ - recvbuf = testgnb_ngap_read(ngap); - ABTS_PTR_NOTNULL(tc, recvbuf); - testngap_recv(test_ue, recvbuf); - - /* Send Authentication failure - MAC failure */ - gmmbuf = testgmm_build_authentication_failure( - test_ue, OGS_5GMM_CAUSE_MAC_FAILURE, 0); - ABTS_PTR_NOTNULL(tc, gmmbuf); - sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); - ABTS_PTR_NOTNULL(tc, sendbuf); - rv = testgnb_ngap_send(ngap, sendbuf); - ABTS_INT_EQUAL(tc, OGS_OK, rv); - - /* Receive Authentication reject */ - recvbuf = testgnb_ngap_read(ngap); - ABTS_PTR_NOTNULL(tc, recvbuf); - testngap_recv(test_ue, recvbuf); - /* Receive UEContextReleaseCommand */ recvbuf = testgnb_ngap_read(ngap); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -195,6 +391,8 @@ static void test1_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); + ogs_msleep(300); + /********** Remove Subscriber in Database */ ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue)); diff --git a/tests/registration/reset-test.c b/tests/registration/reset-test.c index aeab6ad5b..4b7321053 100644 --- a/tests/registration/reset-test.c +++ b/tests/registration/reset-test.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019,2020 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -650,17 +650,161 @@ static void test3_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); testngap_recv(test_ue, recvbuf); -#if 0 /* To reject UE registration */ /********** Insert Subscriber in Database */ doc = test_db_new_simple(test_ue); ABTS_PTR_NOTNULL(tc, doc); ABTS_INT_EQUAL(tc, OGS_OK, test_db_insert_ue(test_ue, doc)); -#endif /* Send Registration request */ gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false); ABTS_PTR_NOTNULL(tc, gmmbuf); + test_ue->registration_request_param.gmm_capability = 1; + test_ue->registration_request_param.s1_ue_network_capability = 1; + test_ue->registration_request_param.requested_nssai = 1; + test_ue->registration_request_param.last_visited_registered_tai = 1; + test_ue->registration_request_param.ue_usage_setting = 1; + nasbuf = testgmm_build_registration_request(test_ue, NULL, false, false); + ABTS_PTR_NOTNULL(tc, nasbuf); + + sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf, + NGAP_RRCEstablishmentCause_mo_Signalling, false, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication request */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Authentication response */ + gmmbuf = testgmm_build_authentication_response(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode command */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Security mode complete */ + gmmbuf = testgmm_build_security_mode_complete(test_ue, nasbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive InitialContextSetupRequest + + * Registration accept */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_InitialContextSetup, + test_ue->ngap_procedure_code); + + /* Send UERadioCapabilityInfoIndication */ + sendbuf = testngap_build_ue_radio_capability_info_indication(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send InitialContextSetupResponse */ + sendbuf = testngap_build_initial_context_setup_response(test_ue, false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Registration complete */ + gmmbuf = testgmm_build_registration_complete(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Configuration update command */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send PDU session establishment request */ + sess = test_sess_add_by_dnn_and_psi(test_ue, "internet", 5); + ogs_assert(sess); + + sess->ul_nas_transport_param.request_type = + OGS_NAS_5GS_REQUEST_TYPE_INITIAL; + sess->ul_nas_transport_param.dnn = 1; + sess->ul_nas_transport_param.s_nssai = 1; + + sess->pdu_session_establishment_param.ssc_mode = 1; + sess->pdu_session_establishment_param.epco = 1; + + gsmbuf = testgsm_build_pdu_session_establishment_request(sess); + ABTS_PTR_NOTNULL(tc, gsmbuf); + gmmbuf = testgmm_build_ul_nas_transport(sess, + OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDUSessionResourceSetupRequest + + * DL NAS transport + + * PDU session establishment accept */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_PDUSessionResourceSetup, + test_ue->ngap_procedure_code); + + /* Send PDUSessionResourceSetupResponse */ + sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send De-registration request */ + gmmbuf = testgmm_build_de_registration_request(test_ue, 1, true, true); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_UEContextRelease, + test_ue->ngap_procedure_code); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /********** Remove Subscriber in Database */ + ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue)); + + /* Wait for removing subscriber */ + ogs_msleep(100); + + /* Send Registration request */ + memset(&test_ue->registration_request_param, 0, + sizeof(test_ue->registration_request_param)); + gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf, NGAP_RRCEstablishmentCause_mo_Signalling, false, true); ABTS_PTR_NOTNULL(tc, sendbuf); @@ -709,11 +853,6 @@ static void test3_func(abts_case *tc, void *data) ogs_msleep(300); -#if 0 /* To reject UE registration */ - /********** Remove Subscriber in Database */ - ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue)); -#endif - /* gNB disonncect from UPF */ testgnb_gtpu_close(gtpu);