Merge remote-tracking branch 'origin/bugfix-nas-security-msg-type' into integration_2025_w27 (!3494)

bugfix: read msg_type only after deciphering

There was a problem with accessing msg_type from the input buffer before
deciphering, giving obviously wrong value.

CI team: we should add a test in the CI with NAS ciphering + integrity
(connection to the core) set to not null (I suggest nea2/nia2). With
openair's core, this is done by setting this in the amf config file
(nia1 and nea2 at the top of the list):

    supported_integrity_algorithms:
      - "NIA2"
      - "NIA1"
      - "NIA0"
    supported_encryption_algorithms:
      - "NEA2"
      - "NEA1"
      - "NEA0"

And also put full security in the AS. This is done with this in the gnb
config file:

    security = {
      ciphering_algorithms = ( "nea2", "nea0" );
      integrity_algorithms = ( "nia2", "nia1", "nia0" );
      drb_ciphering = "yes";
      drb_integrity = "yes"
    };

Ideally we should test this with both openair UE and cots UE. But as a
strict minimum do for openair UE (this is where the bug was).

No need to do much traffic, a few ping is enough. But if you want to
check throughput with full security, why not.
This commit is contained in:
Robert Schmidt
2025-07-02 18:42:36 +02:00
4 changed files with 54 additions and 52 deletions
+5 -5
View File
@@ -65,9 +65,9 @@ oc-cn5g:
Host: avra
NetworkScript: echo "inet 172.21.6.102"
RunIperf3Server: False
Deploy: "! scripts/oc-cn5g-deploy.sh /opt/oai-cn5g-fed-develop-2025-jan oaicicd-core-for-ci-ran"
Undeploy: "! scripts/oc-cn5g-undeploy.sh /opt/oai-cn5g-fed-develop-2025-jan oaicicd-core-for-ci-ran"
LogCollect: "! scripts/oc-cn5g-logcollect.sh /opt/oai-cn5g-fed-develop-2025-jan oaicicd-core-for-ci-ran %%log_dir%%"
Deploy: "! scripts/oc-cn5g-deploy.sh /opt/oai-cn5g-fed-develop-2025-jun oaicicd-core-for-ci-ran"
Undeploy: "! scripts/oc-cn5g-undeploy.sh /opt/oai-cn5g-fed-develop-2025-jun oaicicd-core-for-ci-ran"
LogCollect: "! scripts/oc-cn5g-logcollect.sh /opt/oai-cn5g-fed-develop-2025-jun oaicicd-core-for-ci-ran %%log_dir%%"
oc-cn5g-20897:
Host: cacofonix
@@ -89,8 +89,8 @@ matix-cn5g:
Host: matix
NetworkScript: docker exec prod-trf-gen ip a show dev eth0
RunIperf3Server: False
Deploy: "cd /opt/oai-cn5g-fed-v2/docker-compose && python3 ./core-network.py --type start-basic"
Undeploy: "cd /opt/oai-cn5g-fed-v2/docker-compose && python3 ./core-network.py --type stop-basic"
Deploy: "cd /opt/oai-cn5g-fed-v2-jun-2025/docker-compose && python3 ./core-network.py --type start-basic"
Undeploy: "cd /opt/oai-cn5g-fed-v2-jun-2025/docker-compose && python3 ./core-network.py --type stop-basic"
LogCollect:
- "docker logs oai-smf > %%log_dir%%/oai-smf.log"
- "docker logs oai-amf > %%log_dir%%/oai-amf.log"
@@ -213,7 +213,7 @@ security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
ciphering_algorithms = ( "nea2", "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
@@ -223,7 +223,7 @@ security = {
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
drb_integrity = "yes";
};
log_config :
@@ -212,7 +212,7 @@ security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
ciphering_algorithms = ( "nea2", "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
@@ -222,7 +222,7 @@ security = {
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
drb_integrity = "yes";
};
log_config :
+45 -43
View File
@@ -301,7 +301,44 @@ static void nas_security_decrypt_payload(nr_ue_nas_t *nas, byte_array_t buffer,
free(decrypted);
}
static security_state_t nas_security_rx_process(nr_ue_nas_t *nas, byte_array_t buffer, fgs_nas_msg_t msg_type)
static fgs_nas_msg_t get_msg_type(uint8_t *pdu_buffer, uint32_t length)
{
if (pdu_buffer == NULL)
goto error;
/* get security header type */
if (length < 2)
goto error;
int security_header_type = pdu_buffer[1];
if (security_header_type == 0) {
/* plain NAS message */
if (length < 3)
goto error;
return pdu_buffer[2];
}
if (length < 10)
goto error;
int msg_type = pdu_buffer[9];
if (msg_type == FGS_DOWNLINK_NAS_TRANSPORT) {
if (length < 17)
goto error;
msg_type = pdu_buffer[16];
}
return msg_type;
error:
LOG_E(NAS, "[UE] Received invalid downlink message\n");
return 0;
}
static security_state_t nas_security_rx_process(nr_ue_nas_t *nas, byte_array_t buffer)
{
if (nas->security_container == NULL)
return NAS_SECURITY_NO_SECURITY_CONTEXT;
@@ -314,9 +351,11 @@ static security_state_t nas_security_rx_process(nr_ue_nas_t *nas, byte_array_t b
LOG_D(NAS, "Security type is: %s\n", print_info(security_type, security_header_type_s, sizeofArray(security_header_type_s)));
switch (security_type) {
case PLAIN_5GS_MSG:
case PLAIN_5GS_MSG: {
fgs_nas_msg_t msg_type = get_msg_type(buffer.buf, buffer.len);
return unprotected_allowed(buffer, msg_type) ? NAS_SECURITY_UNPROTECTED : NAS_SECURITY_BAD_INPUT;
break;
}
case INTEGRITY_PROTECTED_WITH_NEW_SECU_CTX:
stream_security_container_delete(nas->security_container);
nas->security_container = NULL;
@@ -1707,43 +1746,6 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t *
}
}
static fgs_nas_msg_t get_msg_type(uint8_t *pdu_buffer, uint32_t length)
{
if (pdu_buffer == NULL)
goto error;
/* get security header type */
if (length < 2)
goto error;
int security_header_type = pdu_buffer[1];
if (security_header_type == 0) {
/* plain NAS message */
if (length < 3)
goto error;
return pdu_buffer[2];
}
if (length < 10)
goto error;
int msg_type = pdu_buffer[9];
if (msg_type == FGS_DOWNLINK_NAS_TRANSPORT) {
if (length < 17)
goto error;
msg_type = pdu_buffer[16];
}
return msg_type;
error:
LOG_E(NAS, "[UE] Received invalid downlink message\n");
return 0;
}
static void send_nas_uplink_data_req(nr_ue_nas_t *nas, const as_nas_info_t *initial_nas_msg)
{
MessageDef *msg = itti_alloc_new_message(TASK_NAS_NRUE, nas->UE_id, NAS_UPLINK_DATA_REQ);
@@ -2003,13 +2005,13 @@ void *nas_nrue(void *args_p)
NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length);
byte_array_t ba = {.buf = NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.nas_data, .len = NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.length};
fgs_nas_msg_t msg_type = get_msg_type(ba.buf, ba.len);
security_state_t security_state = nas_security_rx_process(nas, ba, msg_type);
security_state_t security_state = nas_security_rx_process(nas, ba);
if (security_state > NAS_SECURITY_INTEGRITY_PASSED) {
LOG_E(NAS, "NAS integrity failed, discard incoming message: security state is %s\n", security_state_info[security_state].text);
break;
}
fgs_nas_msg_t msg_type = get_msg_type(ba.buf, ba.len);
if (msg_type == FGS_REGISTRATION_ACCEPT) {
handle_registration_accept(nas, ba.buf, ba.len);
} else if (msg_type == FGS_PDU_SESSION_ESTABLISHMENT_ACC) {
@@ -2071,13 +2073,13 @@ void *nas_nrue(void *args_p)
uint8_t *pdu_buffer = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.nas_data;
int pdu_length = NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length;
byte_array_t buffer = {.buf = pdu_buffer, .len = pdu_length};
fgs_nas_msg_t msg_type = get_msg_type(pdu_buffer, pdu_length);
security_state_t security_state = nas_security_rx_process(nas, buffer, msg_type);
security_state_t security_state = nas_security_rx_process(nas, buffer);
if (security_state > NAS_SECURITY_INTEGRITY_PASSED) {
LOG_E(NAS, "NAS integrity failed, discard incoming message\n");
break;
}
fgs_nas_msg_t msg_type = get_msg_type(pdu_buffer, pdu_length);
LOG_I(NAS,
"[UE %ld] Received %s type %s with length %u\n",
nas->UE_id,