voice: validate outgoing and deflect call numbers

Validate the number parameter in Voice.CreateCall and Call.Deflect
D-Bus methods to ensure they do not contain control characters or
semicolons. This prevents AT command injection when the number is
later spliced into ATD or +CTFR commands.

Introduce a common helper mm_utils_is_valid_dial_number() to validate
dial numbers, and add unit tests for it.

https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/work_items/1067

Signed-off-by: Aleksander Morgado <aleksandermj@google.com>
This commit is contained in:
Aleksander Morgado
2026-07-10 13:43:48 +02:00
committed by Aleksander Morgado
parent 3291e7dd1c
commit 6b3682ed65
5 changed files with 92 additions and 7 deletions
+7
View File
@@ -440,6 +440,13 @@ handle_deflect_auth_ready (MMAuthProvider *authp,
return;
}
/* Validate input number */
if (!mm_utils_is_valid_dial_number (ctx->number, &error)) {
mm_dbus_method_invocation_take_error (ctx->invocation, error);
handle_deflect_context_free (ctx);
return;
}
state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self));
/* We can only deflect incoming call in ringing or waiting state */
+3 -7
View File
@@ -23,6 +23,7 @@
#include "mm-call-list.h"
#include "mm-error-helpers.h"
#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#define CALL_LIST_POLLING_CONTEXT_TAG "voice-call-list-polling-context-tag"
#define IN_CALL_EVENT_CONTEXT_TAG "voice-in-call-event-context-tag"
@@ -176,14 +177,9 @@ create_outgoing_call_from_properties (MMIfaceModemVoice *self,
guint dtmf_tone_duration;
/* Don't create CALL from properties if either number is missing */
number = mm_call_properties_get_number (properties) ;
if (!number) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Cannot create call: mandatory parameter 'number' is missing");
number = mm_call_properties_get_number (properties);
if (!mm_utils_is_valid_dial_number (number, error))
return NULL;
}
dtmf_tone_duration = mm_call_properties_get_dtmf_tone_duration (properties) ;
+22
View File
@@ -5992,3 +5992,25 @@ mm_utils_is_numeric (const gchar *str)
}
return TRUE;
}
gboolean
mm_utils_is_valid_dial_number (const gchar *str,
GError **error)
{
const gchar *c;
if (!str || !str[0]) {
g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
"missing number");
return FALSE;
}
for (c = str; *c; c++) {
if (((guint8)*c) < 0x20 || *c == ';') {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
"invalid character '0x%02x' in number", (guint8)*c);
return FALSE;
}
}
return TRUE;
}
+2
View File
@@ -574,6 +574,8 @@ gboolean mm_parse_supl_address (const gchar *supl,
void mm_utils_remove_control_characters (gchar *str);
gboolean mm_utils_is_valid_fqdn (const gchar *fqdn);
gboolean mm_utils_is_numeric (const gchar *str);
gboolean mm_utils_is_valid_dial_number (const gchar *str,
GError **error);
gboolean mm_validate_cbs_channels (GArray *channels,
GError **error);
+58
View File
@@ -4923,6 +4923,63 @@ test_is_numeric (void *f, gpointer d)
g_assert (!mm_utils_is_numeric ("+123"));
}
static void
test_is_valid_dial_number (void *f, gpointer d)
{
GError *error = NULL;
gboolean r;
r = mm_utils_is_valid_dial_number ("123456", &error);
g_assert (r);
g_assert_no_error (error);
r = mm_utils_is_valid_dial_number ("+123456", &error);
g_assert (r);
g_assert_no_error (error);
r = mm_utils_is_valid_dial_number ("*123#", &error);
g_assert (r);
g_assert_no_error (error);
r = mm_utils_is_valid_dial_number ("123,456", &error);
g_assert (r);
g_assert_no_error (error);
r = mm_utils_is_valid_dial_number ("123W456", &error);
g_assert (r);
g_assert_no_error (error);
r = mm_utils_is_valid_dial_number (NULL, &error);
g_assert (!r);
g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS);
g_clear_error (&error);
r = mm_utils_is_valid_dial_number ("", &error);
g_assert (!r);
g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS);
g_clear_error (&error);
r = mm_utils_is_valid_dial_number ("123;456", &error);
g_assert (!r);
g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS);
g_clear_error (&error);
r = mm_utils_is_valid_dial_number ("123\r456", &error);
g_assert (!r);
g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS);
g_clear_error (&error);
r = mm_utils_is_valid_dial_number ("123\n456", &error);
g_assert (!r);
g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS);
g_clear_error (&error);
r = mm_utils_is_valid_dial_number ("123\x01" "456", &error);
g_assert (!r);
g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS);
g_clear_error (&error);
}
/*****************************************************************************/
typedef struct {
@@ -5472,6 +5529,7 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_remove_control_characters, NULL));
g_test_suite_add (suite, TESTCASE (test_is_valid_fqdn, NULL));
g_test_suite_add (suite, TESTCASE (test_is_numeric, NULL));
g_test_suite_add (suite, TESTCASE (test_is_valid_dial_number, NULL));
g_test_suite_add (suite, TESTCASE (test_cpol_response, NULL));