diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt index 0f4445c353..7aafc588e2 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(fgs_5gmm_lib OBJECT FGSUplinkNasTransport.c FGSDeregistrationRequestUEOriginating.c fgmm_authentication_failure.c + fgmm_authentication_reject.c ) target_link_libraries(fgs_5gmm_lib PUBLIC fgs_5gmm_ies_lib) diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_authentication_reject.c b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_authentication_reject.c new file mode 100644 index 0000000000..193df8f7dd --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_authentication_reject.c @@ -0,0 +1,69 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "fgmm_authentication_reject.h" +#include +#include // For htons and ntohs +#include // For malloc and free +#include "fgmm_lib.h" + +#define MIN_AUTH_REJECT_LEN 3 + +/** @brief Encode Authentication Reject (8.2.5 of 3GPP TS 24.501) */ +int encode_fgmm_auth_reject(byte_array_t *buffer, const fgmm_auth_reject_msg_t *msg) +{ + uint32_t encoded = 0; + + const byte_array_t *eap_msg = &msg->eap_msg; + if (eap_msg->len > 0) { + return encode_eap_msg_ie(buffer, eap_msg); + } + return encoded; +} + +/** @brief Decode Authentication Reject (8.2.5 of 3GPP TS 24.501) + * the message contains only optional IEIs (EAP MSG) */ +int decode_fgmm_auth_reject(fgmm_auth_reject_msg_t *msg, const byte_array_t *buffer) +{ + if (buffer->len < MIN_AUTH_REJECT_LEN) { + // No optional IEIs present + return 0; + } + byte_array_t ba = *buffer; // Local copy of buffer + uint32_t decoded = 0; + // Decode the IEI + uint8_t iei = buffer->buf[decoded++]; + UPDATE_BYTE_ARRAY(ba, decoded); + if (iei == IEI_EAPMSG) { + return (decoded + decode_eap_msg_ie(&msg->eap_msg, &ba)); + } + PRINT_NAS_ERROR("Expected EAP MSG but it is not present"); + return -1; +} + +bool eq_auth_reject(fgmm_auth_reject_msg_t *a, fgmm_auth_reject_msg_t *b) +{ + _NAS_EQ_CHECK_LONG(a->eap_msg.len, b->eap_msg.len); + if (a->eap_msg.len > 0) { + return !memcmp(a->eap_msg.buf, b->eap_msg.buf, a->eap_msg.len); + } + return true; +} diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_authentication_reject.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_authentication_reject.h new file mode 100644 index 0000000000..33ae7a5181 --- /dev/null +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_authentication_reject.h @@ -0,0 +1,38 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef FGMM_AUTH_REJECT_H +#define FGMM_AUTH_REJECT_H + +#include +#include "fgmm_lib.h" +#include "common/utils/ds/byte_array.h" + +typedef struct { + // EAP Message (Optional) + byte_array_t eap_msg; +} fgmm_auth_reject_msg_t; + +int decode_fgmm_auth_reject(fgmm_auth_reject_msg_t *msg, const byte_array_t *buffer); +int encode_fgmm_auth_reject(byte_array_t *buffer, const fgmm_auth_reject_msg_t *msg); +bool eq_auth_reject(fgmm_auth_reject_msg_t *a, fgmm_auth_reject_msg_t *b); + +#endif /* FGMM_AUTH_REJECT_H */ diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c index 1b607589d9..63c6ec0d32 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.c @@ -25,8 +25,11 @@ #include // For malloc and free #include "common/utils/ds/byte_array.h" +#define IEI_LEN 1 #define GPRS_TIMER_LENGTH 3 // octets #define FGS_NAS_CAUSE_LENGTH 1 +#define MIN_EAP_MSG_LEN 7 +#define MIN_EAP_CONTENTS_LEN 4 /** * Encode PDU Session Status and PDU session reactivation result (optional IEs) @@ -152,12 +155,72 @@ bool eq_gprs_timer(const gprs_timer_t *a, const gprs_timer_t *b) return true; } -/* Decode EAP Message (todo) */ -int decode_eap_msg_ie(const byte_array_t *buffer) +/** @brief Encode EAP message IE (3GPP TS 24.501 9.11.2.2) */ +int encode_eap_msg_ie(byte_array_t *buffer, const byte_array_t *msg) { - int eap_msg_len = (buffer->buf[0] << 8) | buffer->buf[1]; - PRINT_NAS_ERROR("EAP message IE in NAS Service Reject is not handled\n"); - return eap_msg_len + 2; // content length + 2 octets for the length IE + int encoded = 0; + + // Sanity check on buffer length + if (buffer->len < MIN_EAP_MSG_LEN) { + PRINT_NAS_ERROR("Invalid buffer length %ld", buffer->len); + return -1; + } + + // Sanity check on input length + if (msg->len < MIN_EAP_CONTENTS_LEN || msg->len > MAX_EAP_CONTENTS_LEN) { + PRINT_NAS_ERROR("Invalid EAP message length %ld", msg->len); + return -1; + } + + // Encode IEI + buffer->buf[encoded++] = IEI_EAPMSG; + + // Encode length of EAP message content + uint16_t len = htons(msg->len); + memcpy(&buffer->buf[encoded], &len, sizeof(len)); + encoded += sizeof(len); + + // Encode EAP message content + memcpy(&buffer->buf[encoded], msg->buf, msg->len); + encoded += msg->len; + + return encoded; +} + +/** @brief Decode EAP message IE (3GPP TS 24.501 9.11.2.2): + length of contents and message payload */ +int decode_eap_msg_ie(byte_array_t *eap_msg, const byte_array_t *buffer) +{ + int decoded = 0; + int min_len_to_decode = MIN_EAP_MSG_LEN - IEI_LEN; + + // Check buffer length validity + if (buffer->len < min_len_to_decode) { + PRINT_NAS_ERROR("Invalid buffer for decoding EAP message IE"); + return -1; + } + + // Decode length IE + eap_msg->len = (buffer->buf[decoded] << 8) | buffer->buf[decoded + 1]; + decoded += 2; + + // Check payload length validity + if (eap_msg->len < MIN_EAP_CONTENTS_LEN || eap_msg->len > MAX_EAP_CONTENTS_LEN) { + PRINT_NAS_ERROR("Invalid EAP message length %ld", eap_msg->len); + return -1; + } + + // Check for buffer overflow + if (buffer->len < decoded + eap_msg->len) { + PRINT_NAS_ERROR("Buffer too short for EAP message: %ld < %ld", buffer->len, decoded + eap_msg->len); + return -1; + } + + // Copy EAP payload + memcpy(eap_msg->buf, &buffer->buf[decoded], eap_msg->len); + decoded += eap_msg->len; + + return decoded; } int encode_fgs_nas_cause(byte_array_t *buffer, const cause_id_t *cause) diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h index 25118f2742..8d833e74aa 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_lib.h @@ -39,6 +39,7 @@ both PDU Session Status and PDU session Reactivation Result IEs */ #define MAX_PDU_SESSION_CONTENTS_LEN 32 #define MAX_NUM_PSI 16 #define MAX_NUM_PDU_ERRORS 256 +#define MAX_EAP_CONTENTS_LEN 1500 /* This macro updates the local copy of a byte_array_t pointer used for enc/dec */ @@ -164,7 +165,8 @@ int decode_pdu_session_ie(uint8_t *psi, const byte_array_t *buffer); int encode_gprs_timer_ie(byte_array_t *buffer, nas_service_IEI_t iei, const gprs_timer_t *timer); int decode_gprs_timer_ie(gprs_timer_t *timer, const byte_array_t *buffer); bool eq_gprs_timer(const gprs_timer_t *a, const gprs_timer_t *b); -int decode_eap_msg_ie(const byte_array_t *buffer); +int decode_eap_msg_ie(byte_array_t *eap_msg, const byte_array_t *buffer); +int encode_eap_msg_ie(byte_array_t *buffer, const byte_array_t *msg); int encode_fgs_nas_cause(byte_array_t *buffer, const cause_id_t *cause); int decode_fgs_nas_cause(cause_id_t *cause, const byte_array_t *buffer); diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c index 797deede84..63e2901f61 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.c @@ -133,7 +133,7 @@ int decode_fgs_service_accept(fgs_service_accept_msg_t *msg, const byte_array_t break; case IEI_EAPMSG: - DECODE_NAS_IE(ba, decode_eap_msg_ie(&ba), decoded); + DECODE_NAS_IE(ba, decode_eap_msg_ie(&msg->eap_msg, &ba), decoded); break; default: diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.h index 9f82f47eb1..c5d042b674 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.h +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_accept.h @@ -47,6 +47,9 @@ typedef struct { // T3448 Value (Optional) gprs_timer_t *t3448; + + // EAP Message (Optional) + byte_array_t eap_msg; } fgs_service_accept_msg_t; int decode_fgs_service_accept(fgs_service_accept_msg_t *msg, const byte_array_t *buffer); diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.c b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.c index 74175fda36..14078637a3 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.c +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.c @@ -59,6 +59,11 @@ int encode_fgs_service_reject(byte_array_t *buffer, const fgs_service_reject_msg ENCODE_NAS_IE(ba, encode_gprs_timer_ie(&ba, IEI_T3448_VALUE, msg->t3448), encoded); } + // EAP message (Optional) + if (msg->eap_msg) { + ENCODE_NAS_IE(ba, encode_eap_msg_ie(&ba, msg->eap_msg), encoded); + } + return encoded; } @@ -99,7 +104,7 @@ int decode_fgs_service_reject(fgs_service_reject_msg_t *msg, const byte_array_t break; case IEI_EAPMSG: - DECODE_NAS_IE(ba, decode_eap_msg_ie((const byte_array_t *)&ba), decoded); + DECODE_NAS_IE(ba, decode_eap_msg_ie(msg->eap_msg, &ba), decoded); break; case IEI_CAG_INFO_LIST: { diff --git a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.h b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.h index 61337d8525..29c8ab9b72 100644 --- a/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.h +++ b/openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_service_reject.h @@ -35,6 +35,8 @@ typedef struct { bool has_psi_status; // T3448 Value (Optional) gprs_timer_t *t3448; + // EAP message (optional) + byte_array_t *eap_msg; } fgs_service_reject_msg_t; int decode_fgs_service_reject(fgs_service_reject_msg_t *msg, const byte_array_t *buffer); diff --git a/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c b/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c index f733bdd3a5..d9f837d6b0 100644 --- a/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c +++ b/openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c @@ -9,6 +9,7 @@ #include "fgmm_service_reject.h" #include "fgmm_authentication_failure.h" #include "FGSNASSecurityModeReject.h" +#include "fgmm_authentication_reject.h" #include "nr_nas_msg.h" void exit_function(const char *file, const char *function, const int line, const char *s, const int assert) @@ -298,6 +299,44 @@ static void test_security_mode_reject(void) AssertFatal(eq_sec_mode_reject(&orig, &dec), "test_sec_mode_reject() failed: original and decoded messages do not match\n"); } +/** + * @brief Test NAS Authentication Reject enc/dec + */ +static void test_auth_reject(void) +{ + // Dummy NAS Authentication Reject message + uint8_t dummy_eap_msg[] = {0x12, 0x34, 0x56, 0x78, 0x91, 0x01, 0x23}; // Example EAP message + fgmm_auth_reject_msg_t orig = { + .eap_msg.len = sizeof(dummy_eap_msg), + }; + orig.eap_msg.buf = dummy_eap_msg; + + // Expected encoded data + uint8_t expected_enc[] = {0x78, 0x00, 0x07, 0x12, 0x34, 0x56, 0x78, 0x91, 0x01, 0x23}; + + // Buffer + uint8_t buf[10] = {0}; + byte_array_t buffer = {.buf = buf, .len = sizeof(buf)}; + + // Encode + int encoded_length = encode_fgmm_auth_reject(&buffer, &orig); + AssertFatal(encoded_length >= 0, "encode_fgmm_auth_reject() failed\n"); + + // Compare the raw encoded buffer with expected encoded data + AssertFatal(encoded_length == sizeof(expected_enc), "Encoded length mismatch %d != %ld \n", encoded_length, sizeof(expected_enc)); + AssertFatal(memcmp(buffer.buf, expected_enc, encoded_length) == 0, "Encoding mismatch!\n"); + + // Decode + fgmm_auth_reject_msg_t dec = {0}; + uint8_t eap_msg[1500] = {0}; + dec.eap_msg.buf = eap_msg; + int decoded_length = decode_fgmm_auth_reject(&dec, &buffer); + AssertFatal(decoded_length >= 0, "decode_fgmm_auth_reject() failed\n"); + + // Compare original and decoded messages + AssertFatal(eq_auth_reject(&orig, &dec), "test_auth_reject() failed: original and decoded messages do not match\n"); +} + int main() { test_regression_registration_accept(); @@ -305,6 +344,7 @@ int main() test_service_accept(); test_service_reject(); test_auth_failure(); + test_auth_reject(); test_security_mode_reject(); return 0; } diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index d7fb313fa8..69a910a0e7 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -71,6 +71,7 @@ #include "fgs_nas_utils.h" #include "fgmm_service_accept.h" #include "fgmm_service_reject.h" +#include "fgmm_authentication_reject.h" #include "ds/byte_array.h" #include "key_nas_deriver.h" @@ -1063,6 +1064,33 @@ static void handle_fgmm_authentication_request(nr_ue_nas_t *nas, as_nas_info_t * generateAuthenticationResp(nas, initialNasMsg, buffer->buf); } +/** @brief Handle authentication not accepted by the network + * This function assumes the message is not integrity protected, processes the received + * NAS message and logs whether a EAP-failure is enclosed. The UE enters state: 5GMM-DEREGISTERED. + * @todo The UE shall performs actions as per 5.4.1.3.5 of 3GPP TS 24.501, including + * (1) Abort any ongoing 5GMM procedure (2) Stop all active timers: T3510, T3516, T3517, + * T3519, T3520, T3521 (3) Delete stored SUCI. (4) handle EAP-failure message. */ +static void handle_authentication_reject(nr_ue_nas_t *nas, as_nas_info_t *initialNasMsg, uint8_t *pdu, int pdu_length) +{ + LOG_E(NAS, "Received Authentication Reject message from the network\n"); + uint8_t eap_msg[MAX_EAP_CONTENTS_LEN] = {0}; + fgmm_auth_reject_msg_t msg = {.eap_msg.buf = eap_msg}; + + byte_array_t ba = {.buf = pdu + 3 /* skip header */, .len = pdu_length}; + if (decode_fgmm_auth_reject(&msg, &ba) < 0) { + LOG_E(NAS, "Could not decode Authentication Reject\n"); + return; + } + + if (msg.eap_msg.len > 0) { + /** @todo UE handling EAP-failure message (5.4.1.2.2.11 of 3GPP TS 24.501) */ + LOG_W(NAS, "NAS Authentication Reject contains an EAP message: handling is not implemented\n"); + log_hex_buffer("EAP-Failure", msg.eap_msg.buf, msg.eap_msg.len); + } + + nas->fiveGMM_state = FGS_DEREGISTERED; +} + int nas_itti_kgnb_refresh_req(instance_t instance, const uint8_t kgnb[32]) { MessageDef *message_p; @@ -2064,6 +2092,9 @@ void *nas_nrue(void *args_p) case FGS_AUTHENTICATION_REQUEST: handle_fgmm_authentication_request(nas, &initialNasMsg, &buffer); break; + case FGS_AUTHENTICATION_REJECT: + handle_authentication_reject(nas, &initialNasMsg, pdu_buffer, pdu_length); + break; case FGS_SECURITY_MODE_COMMAND: handle_security_mode_command(nas, &initialNasMsg, pdu_buffer, pdu_length); break; diff --git a/openair3/SECU/key_nas_deriver.c b/openair3/SECU/key_nas_deriver.c index a7cf27a1ff..3fb35adee2 100644 --- a/openair3/SECU/key_nas_deriver.c +++ b/openair3/SECU/key_nas_deriver.c @@ -57,7 +57,7 @@ #define NR_FC_ALG_KEY_DER (0x69) #define NR_FC_ALG_KEY_NG_RAN_STAR_DER (0x70) -static void log_hex_buffer(const char *label, const uint8_t *buf, const int len) +void log_hex_buffer(const char *label, const uint8_t *buf, const int len) { char hex_str[len * 2 + 1]; for (int i = 0; i < len; i++) { diff --git a/openair3/SECU/key_nas_deriver.h b/openair3/SECU/key_nas_deriver.h index 0ab14ad51a..eff72c68bb 100644 --- a/openair3/SECU/key_nas_deriver.h +++ b/openair3/SECU/key_nas_deriver.h @@ -36,4 +36,6 @@ void nr_derive_key_ng_ran_star(uint16_t pci, uint64_t nr_arfcn_dl, const uint8_t void nr_derive_nh(const uint8_t k_amf[SECURITY_KEY_LEN], const uint8_t *sync_input, uint8_t *nh); +void log_hex_buffer(const char *label, const uint8_t *buf, const int len); + #endif