mirror of
https://github.com/open5gs/open5gs.git
synced 2026-10-19 09:44:04 +00:00
core/sbi: fix hex conversion UB and validate GUAMI AMF IDs
Fix the negative left shift reported by sbi_request_fuzz in ogs_ascii_to_hex(). Mask converted digits to a nibble and pass unsigned char values to ctype functions. Log missing buffers, invalid lengths and non-hex characters in the checked decoder. Delegate the string wrapper to it without duplicate logs or an additional header dependency. Require exactly six hex digits for AMF IDs and propagate conversion failures through GUAMI discovery, NF profiles and AMF/UDM/SMF/PCF handlers, with contextual error logs. Validate UDM registration GUAMI before replacing the stored callback URI. Include an INVALID_MANDATORY_INFORMATION N1 reject when SMF rejects an invalid GUAMI, matching the existing No Guami path.
This commit is contained in:
parent
f5c7025456
commit
db5935b334
11 changed files with 118 additions and 24 deletions
|
|
@ -35,9 +35,15 @@ int ogs_ascii_to_hex(char *in, int in_len, void *out, int out_len)
|
|||
uint8_t *out_p = out;
|
||||
|
||||
while(i < in_len && j < out_len) {
|
||||
if (!isspace(in[i])) {
|
||||
hex = isdigit(in[i]) ? in[i] - '0' :
|
||||
islower(in[i]) ? in[i] - 'a' + 10 : in[i] - 'A' + 10;
|
||||
if (!isspace((unsigned char)in[i])) {
|
||||
hex = isdigit((unsigned char)in[i]) ? in[i] - '0' :
|
||||
islower((unsigned char)in[i]) ?
|
||||
in[i] - 'a' + 10 : in[i] - 'A' + 10;
|
||||
/*
|
||||
* Non-hex input may produce a negative value. Mask it before
|
||||
* shifting; input validation remains the checked API's job.
|
||||
*/
|
||||
hex &= 0x0f;
|
||||
if ((k & 0x1) == 0) {
|
||||
out_p[j] = (hex << 4);
|
||||
} else {
|
||||
|
|
@ -57,18 +63,34 @@ int ogs_ascii_to_hex_checked(
|
|||
{
|
||||
int i;
|
||||
|
||||
if (!in || !out)
|
||||
if (!in) {
|
||||
ogs_error("Missing hex input");
|
||||
return OGS_ERROR;
|
||||
}
|
||||
if (!out) {
|
||||
ogs_error("Missing hex output buffer");
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
if (in_len < 0 || out_len < 0 || out_len > INT_MAX / 2 ||
|
||||
in_len != out_len * 2)
|
||||
if (in_len < 0 || out_len < 0 || out_len > INT_MAX / 2) {
|
||||
ogs_error("Invalid hex conversion length [input:%d output:%d]",
|
||||
in_len, out_len);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
if (in_len != out_len * 2) {
|
||||
ogs_error("Invalid hex string length [%d], expected [%d]",
|
||||
in_len, out_len * 2);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < in_len; i++) {
|
||||
if (!((in[i] >= '0' && in[i] <= '9') ||
|
||||
(in[i] >= 'a' && in[i] <= 'f') ||
|
||||
(in[i] >= 'A' && in[i] <= 'F')))
|
||||
(in[i] >= 'A' && in[i] <= 'F'))) {
|
||||
ogs_error("Invalid hex character [0x%02x] at offset [%d]",
|
||||
(unsigned char)in[i], i);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ogs_ascii_to_hex((char *)in, in_len, out, out_len);
|
||||
|
|
|
|||
|
|
@ -28,25 +28,41 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Skip whitespace and convert up to out_len bytes without validating hex.
|
||||
* Return the number of complete bytes. Short/odd input may leave partial
|
||||
* output; excess input is ignored. out_len is capacity, not an exact size.
|
||||
* Use ogs_ascii_to_hex_checked() for received, fixed-size hex fields.
|
||||
*/
|
||||
int ogs_ascii_to_hex(char *in, int in_len, void *out, int out_len);
|
||||
/*
|
||||
* Convert exactly out_len * 2 hexadecimal characters.
|
||||
* Return OGS_OK on success. On error, leave out unchanged.
|
||||
* Require exactly out_len * 2 hex digits (0-9, a-f, A-F), without whitespace.
|
||||
* Return OGS_OK, or log an error and return OGS_ERROR without changing out.
|
||||
* Check the result before using the output; out_len is the exact decoded size.
|
||||
*
|
||||
* Added for AUSF vector validation after issue #4734
|
||||
* (opened 2026-08-08): https://github.com/open5gs/open5gs/issues/4734
|
||||
*/
|
||||
int ogs_ascii_to_hex_checked(
|
||||
const char *in, int in_len, void *out, int out_len);
|
||||
/*
|
||||
* Permissive string wrapper; returns out without reporting invalid input.
|
||||
* Use ogs_hex_from_string_checked() for received, fixed-size hex fields.
|
||||
*/
|
||||
static ogs_inline void *ogs_hex_from_string(
|
||||
const char *str, void *out, int out_len)
|
||||
{
|
||||
ogs_ascii_to_hex((char*)str, strlen(str), out, out_len);
|
||||
return out;
|
||||
}
|
||||
/*
|
||||
* String wrapper for ogs_ascii_to_hex_checked(); check the return value.
|
||||
*/
|
||||
static ogs_inline int ogs_hex_from_string_checked(
|
||||
const char *str, void *out, int out_len)
|
||||
{
|
||||
if (!str)
|
||||
return OGS_ERROR;
|
||||
return ogs_ascii_to_hex_checked(str, strlen(str), out, out_len);
|
||||
return ogs_ascii_to_hex_checked(
|
||||
str, str ? strlen(str) : 0, out, out_len);
|
||||
}
|
||||
|
||||
void *ogs_hex_to_ascii(const void *in, int in_len, void *out, int out_len);
|
||||
|
|
|
|||
|
|
@ -246,7 +246,10 @@ ogs_amf_id_t *ogs_amf_id_from_string(ogs_amf_id_t *amf_id, const char *hex)
|
|||
ogs_assert(amf_id);
|
||||
ogs_assert(hex);
|
||||
|
||||
ogs_hex_from_string(hex, hexbuf, sizeof(hexbuf));
|
||||
if (ogs_hex_from_string_checked(hex, hexbuf, sizeof(hexbuf)) != OGS_OK) {
|
||||
ogs_error("Invalid AMF ID [%s]", hex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
amf_id->region = hexbuf[0];
|
||||
amf_id->set1 = hexbuf[1];
|
||||
|
|
|
|||
|
|
@ -287,6 +287,7 @@ typedef struct ogs_guami_s {
|
|||
|
||||
uint32_t ogs_amf_id_hexdump(const ogs_amf_id_t *amf_id);
|
||||
|
||||
/* Return NULL for malformed hex without changing amf_id. */
|
||||
ogs_amf_id_t *ogs_amf_id_from_string(ogs_amf_id_t *amf_id, const char *hex);
|
||||
char *ogs_amf_id_to_string(const ogs_amf_id_t *amf_id);
|
||||
|
||||
|
|
|
|||
|
|
@ -1345,7 +1345,10 @@ bool ogs_sbi_parse_guami(ogs_guami_t *guami, OpenAPI_guami_t *Guami)
|
|||
ogs_assert(Guami->amf_id);
|
||||
ogs_assert(Guami->plmn_id);
|
||||
|
||||
ogs_amf_id_from_string(&guami->amf_id, Guami->amf_id);
|
||||
if (!ogs_amf_id_from_string(&guami->amf_id, Guami->amf_id)) {
|
||||
ogs_error("Invalid GUAMI AMF ID [%s]", Guami->amf_id);
|
||||
return false;
|
||||
}
|
||||
ogs_sbi_parse_plmn_id_nid(&guami->plmn_id, Guami->plmn_id);
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -4158,7 +4158,13 @@ int ogs_sbi_discovery_option_parse_guami(
|
|||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
ogs_sbi_parse_guami(&parsed_guami, Guami);
|
||||
if (!ogs_sbi_parse_guami(&parsed_guami, Guami)) {
|
||||
ogs_error("Invalid GUAMI discovery parameter [%s]", guami);
|
||||
OpenAPI_guami_free(Guami);
|
||||
cJSON_Delete(guamItem);
|
||||
ogs_free(v);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
ogs_sbi_discovery_option_set_guami(discovery_option, &parsed_guami);
|
||||
OpenAPI_guami_free(Guami);
|
||||
cJSON_Delete(guamItem);
|
||||
|
|
|
|||
|
|
@ -860,9 +860,14 @@ static bool handle_amf_info(
|
|||
GuamiAmfInfoItem->plmn_id->mnc &&
|
||||
GuamiAmfInfoItem->plmn_id->mcc) {
|
||||
|
||||
ogs_sbi_parse_guami(
|
||||
if (!ogs_sbi_parse_guami(
|
||||
&nf_info->amf.guami[nf_info->amf.num_of_guami],
|
||||
GuamiAmfInfoItem);
|
||||
GuamiAmfInfoItem)) {
|
||||
ogs_error("[%s] Invalid GUAMI in AMF NF profile",
|
||||
nf_instance->id);
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
nf_info->amf.num_of_guami++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2258,7 +2258,11 @@ static bool amf_namf_comm_parse_guti(ogs_nas_5gs_guti_t *guti, char *ue_context_
|
|||
memset(&plmn_id, 0, sizeof(plmn_id));
|
||||
ogs_sbi_parse_plmn_id(&plmn_id, &Plmn_id);
|
||||
ogs_nas_from_plmn_id(&guti->nas_plmn_id, &plmn_id);
|
||||
ogs_amf_id_from_string(&guti->amf_id, amf_id_string);
|
||||
if (!ogs_amf_id_from_string(&guti->amf_id, amf_id_string)) {
|
||||
ogs_error("Invalid AMF ID [%s] in UE context ID [%s]",
|
||||
amf_id_string, ue_context_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
guti->m_tmsi = (u_int32_t)strtol(tmsi_string, NULL, 16);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -235,7 +235,14 @@ bool pcf_npcf_am_policy_control_handle_create(pcf_ue_am_t *pcf_ue_am,
|
|||
Guami = PolicyAssociationRequest->guami;
|
||||
if (Guami && Guami->amf_id &&
|
||||
Guami->plmn_id && Guami->plmn_id->mnc && Guami->plmn_id->mcc) {
|
||||
ogs_sbi_parse_guami(&pcf_ue_am->guami, PolicyAssociationRequest->guami);
|
||||
if (!ogs_sbi_parse_guami(
|
||||
&pcf_ue_am->guami, PolicyAssociationRequest->guami)) {
|
||||
ogs_error("[%s] Invalid GUAMI", pcf_ue_am->supi);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
message, "Invalid GUAMI", pcf_ue_am->supi, NULL));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
OpenAPI_list_for_each(PolicyAssociationRequest->allowed_snssais, node) {
|
||||
|
|
|
|||
|
|
@ -422,7 +422,15 @@ bool smf_nsmf_handle_create_sm_context(
|
|||
ogs_assert(sess->amf_nf_id);
|
||||
|
||||
ogs_assert(SmContextCreateData->guami);
|
||||
ogs_sbi_parse_guami(&sess->guami, SmContextCreateData->guami);
|
||||
if (!ogs_sbi_parse_guami(&sess->guami, SmContextCreateData->guami)) {
|
||||
ogs_error("[%s:%d] Invalid GUAMI", smf_ue->supi, sess->psi);
|
||||
n1smbuf = gsm_build_pdu_session_establishment_reject(sess,
|
||||
OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION);
|
||||
smf_sbi_send_sm_context_create_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST, OGS_SBI_APP_ERRNO_NULL,
|
||||
"Invalid GUAMI", smf_ue->supi, n1smbuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SmContextCreateData->pcf_id) {
|
||||
if (sess->pcf_id) ogs_free(sess->pcf_id);
|
||||
|
|
@ -1618,7 +1626,14 @@ bool smf_nsmf_handle_create_data_in_hsmf(
|
|||
sess->amf_nf_id = ogs_strdup(PduSessionCreateData->amf_nf_id);
|
||||
ogs_assert(sess->amf_nf_id);
|
||||
|
||||
ogs_sbi_parse_guami(&sess->guami, PduSessionCreateData->guami);
|
||||
if (!ogs_sbi_parse_guami(&sess->guami, PduSessionCreateData->guami)) {
|
||||
ogs_error("[%s:%d] Invalid GUAMI", smf_ue->supi, sess->psi);
|
||||
smf_sbi_send_pdu_session_create_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST, OGS_SBI_APP_ERRNO_NULL,
|
||||
OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION,
|
||||
"Invalid GUAMI", smf_ue->supi, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
sess->s_nssai.sst = sNssai->sst;
|
||||
sess->s_nssai.sd = ogs_s_nssai_sd_from_string(sNssai->sd);
|
||||
|
|
|
|||
|
|
@ -337,14 +337,20 @@ bool udm_nudm_uecm_handle_amf_registration(
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!ogs_sbi_parse_guami(&udm_ue->guami, Guami)) {
|
||||
ogs_error("[%s] Invalid GUAMI", udm_ue->supi);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
message, "Invalid GUAMI", udm_ue->supi, NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (udm_ue->dereg_callback_uri)
|
||||
ogs_free(udm_ue->dereg_callback_uri);
|
||||
udm_ue->dereg_callback_uri = ogs_strdup(
|
||||
Amf3GppAccessRegistration->dereg_callback_uri);
|
||||
ogs_assert(udm_ue->dereg_callback_uri);
|
||||
|
||||
ogs_sbi_parse_guami(&udm_ue->guami, Guami);
|
||||
|
||||
udm_ue->rat_type = Amf3GppAccessRegistration->rat_type;
|
||||
|
||||
udm_ue->amf_3gpp_access_registration =
|
||||
|
|
@ -441,7 +447,13 @@ bool udm_nudm_uecm_handle_amf_registration_update(
|
|||
* GUAMI. If so, the UDM shall set the PurgeFlag. The UDM responds with
|
||||
* "204 No Content".
|
||||
* 2b. Otherwise the UDM responds with "403 Forbidden". */
|
||||
ogs_sbi_parse_guami(&recv_guami, Guami);
|
||||
if (!ogs_sbi_parse_guami(&recv_guami, Guami)) {
|
||||
ogs_error("[%s] Invalid GUAMI", udm_ue->supi);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
message, "Invalid GUAMI", udm_ue->supi, NULL));
|
||||
return false;
|
||||
}
|
||||
if (memcmp(&recv_guami, &udm_ue->guami, sizeof(recv_guami)) != 0) {
|
||||
ogs_error("[%s] Guami mismatch", udm_ue->supi);
|
||||
/*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue