diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 66da3bdf5..adddb3154 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -1308,67 +1308,99 @@ validate_headers (GDBusMessage *message, break; case G_DBUS_MESSAGE_TYPE_METHOD_CALL: - if (g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH) == NULL || - g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER) == NULL) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("METHOD_CALL message: PATH or MEMBER header field is missing")); - goto out; - } + { + GVariant *path_variant = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH); + GVariant *member_variant = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER); + + if (path_variant == NULL || + !g_variant_is_of_type (path_variant, G_VARIANT_TYPE_OBJECT_PATH) || + member_variant == NULL || + !g_variant_is_of_type (member_variant, G_VARIANT_TYPE_STRING) || + !g_dbus_is_member_name (g_variant_get_string (member_variant, NULL))) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("METHOD_CALL message: PATH or MEMBER header field is missing or invalid")); + goto out; + } + } break; case G_DBUS_MESSAGE_TYPE_METHOD_RETURN: - if (g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL) == NULL) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("METHOD_RETURN message: REPLY_SERIAL header field is missing")); - goto out; - } + { + GVariant *reply_serial_variant = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL); + + if (reply_serial_variant == NULL || + !g_variant_is_of_type (reply_serial_variant, G_VARIANT_TYPE_UINT32)) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("METHOD_RETURN message: REPLY_SERIAL header field is missing or invalid")); + goto out; + } + } break; case G_DBUS_MESSAGE_TYPE_ERROR: - if (g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME) == NULL || - g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL) == NULL) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing")); - goto out; - } + { + GVariant *error_name_variant = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME); + GVariant *reply_serial_variant = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL); + + if (error_name_variant == NULL || + !g_variant_is_of_type (error_name_variant, G_VARIANT_TYPE_STRING) || + !g_dbus_is_error_name (g_variant_get_string (error_name_variant, NULL)) || + reply_serial_variant == NULL || + !g_variant_is_of_type (reply_serial_variant, G_VARIANT_TYPE_UINT32)) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing or invalid")); + goto out; + } + } break; case G_DBUS_MESSAGE_TYPE_SIGNAL: - if (g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH) == NULL || - g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE) == NULL || - g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER) == NULL) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("SIGNAL message: PATH, INTERFACE or MEMBER header field is missing")); - goto out; - } - if (g_strcmp0 (g_dbus_message_get_path (message), "/org/freedesktop/DBus/Local") == 0) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("SIGNAL message: The PATH header field is using the reserved value /org/freedesktop/DBus/Local")); - goto out; - } - if (g_strcmp0 (g_dbus_message_get_interface (message), "org.freedesktop.DBus.Local") == 0) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("SIGNAL message: The INTERFACE header field is using the reserved value org.freedesktop.DBus.Local")); - goto out; - } + { + GVariant *path_variant = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH); + GVariant *interface_variant = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE); + GVariant *member_variant = g_dbus_message_get_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER); + + if (path_variant == NULL || + !g_variant_is_of_type (path_variant, G_VARIANT_TYPE_OBJECT_PATH) || + interface_variant == NULL || + !g_variant_is_of_type (interface_variant, G_VARIANT_TYPE_STRING) || + !g_dbus_is_interface_name (g_variant_get_string (interface_variant, NULL)) || + member_variant == NULL || + !g_variant_is_of_type (member_variant, G_VARIANT_TYPE_STRING) || + !g_dbus_is_member_name (g_variant_get_string (member_variant, NULL))) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("SIGNAL message: PATH, INTERFACE or MEMBER header field is missing or invalid")); + goto out; + } + if (g_strcmp0 (g_dbus_message_get_path (message), "/org/freedesktop/DBus/Local") == 0) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("SIGNAL message: The PATH header field is using the reserved value /org/freedesktop/DBus/Local")); + goto out; + } + if (g_strcmp0 (g_dbus_message_get_interface (message), "org.freedesktop.DBus.Local") == 0) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("SIGNAL message: The INTERFACE header field is using the reserved value org.freedesktop.DBus.Local")); + goto out; + } + } break; default: diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c index 8285d1111..9ad540956 100644 --- a/gio/tests/gdbus-serialization.c +++ b/gio/tests/gdbus-serialization.c @@ -936,7 +936,7 @@ test_message_serialize_header_checks (void) g_dbus_message_set_interface (message, NULL); blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); - g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); /* interface reserved value => error */ @@ -953,7 +953,7 @@ test_message_serialize_header_checks (void) g_dbus_message_set_path (message, NULL); blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); - g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); /* path reserved value => error */ @@ -970,7 +970,7 @@ test_message_serialize_header_checks (void) g_dbus_message_set_member (message, NULL); blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); - g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); /* reset member */ @@ -988,7 +988,7 @@ test_message_serialize_header_checks (void) g_dbus_message_set_path (message, NULL); blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); - g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); /* reset path */ @@ -998,7 +998,7 @@ test_message_serialize_header_checks (void) g_dbus_message_set_member (message, NULL); blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); - g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); /* reset member */ @@ -1018,7 +1018,7 @@ test_message_serialize_header_checks (void) g_dbus_message_set_header (reply, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, NULL); blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); - g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_RETURN message: REPLY_SERIAL header field is missing"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_RETURN message: REPLY_SERIAL header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); g_object_unref (reply); @@ -1029,7 +1029,7 @@ test_message_serialize_header_checks (void) g_dbus_message_set_error_name (reply, NULL); blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); - g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); /* reset ERROR_NAME */ @@ -1038,7 +1038,7 @@ test_message_serialize_header_checks (void) g_dbus_message_set_header (reply, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, NULL); blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); - g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); g_object_unref (reply); @@ -1615,6 +1615,127 @@ test_message_serialize_empty_structure (void) g_clear_object (&message); } +static void +test_message_parse_missing_header (void) +{ + const guint8 data[] = { + 'l', /* little-endian byte order */ + 0x01, /* message type (method call) */ + 0x00, /* message flags (none) */ + 0x01, /* major protocol version */ + 0x12, 0x00, 0x00, 0x00, /* body length (in bytes) */ + 0x20, 0x20, 0x20, 0x20, /* message serial */ + /* a{yv} of header fields: */ + 0x24, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */ + 0x01, /* array key (PATH, required for method call messages) */ + /* Variant array value: */ + 0x01, /* signature length */ + 'o', /* one complete type */ + 0x00, /* nul terminator */ + /* (Variant array value payload) */ + 0x01, 0x00, 0x00, 0x00, + '/', 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x30, /* array key (MEMBER, required for method call messages; CORRUPTED from 0x03) */ + /* Variant array value: */ + 0x01, /* signature length */ + 's', /* one complete type */ + 0x00, /* nul terminator */ + /* (Variant array value payload) */ + 0x03, 0x00, 0x00, 0x00, + 'H', 'e', 'y', 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, /* array key (SIGNATURE) */ + /* Variant array value: */ + 0x01, /* signature length */ + 'g', /* one complete type */ + 0x00, /* nul terminator */ + /* (Variant array value payload) */ + 0x02, 's', 's', 0x00, + /* Some arbitrary valid content inside the message body: */ + 0x03, 0x00, 0x00, 0x00, + 'h', 'e', 'y', 0x00, + 0x05, 0x00, 0x00, 0x00, + 't', 'h', 'e', 'r', 'e', 0x00 + }; + + gsize size = sizeof (data); + GDBusMessage *message = NULL; + GError *local_error = NULL; + + g_test_summary ("Test that missing (required) headers prompt an error."); + g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3061"); + + message = g_dbus_message_new_from_blob ((guchar *) data, size, + G_DBUS_CAPABILITY_FLAGS_NONE, + &local_error); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_assert_null (message); + + g_clear_error (&local_error); +} + +static void +test_message_parse_invalid_header_type (void) +{ + const guint8 data[] = { + 'l', /* little-endian byte order */ + 0x01, /* message type (method call) */ + 0x00, /* message flags (none) */ + 0x01, /* major protocol version */ + 0x12, 0x00, 0x00, 0x00, /* body length (in bytes) */ + 0x20, 0x20, 0x20, 0x20, /* message serial */ + /* a{yv} of header fields: */ + 0x24, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */ + 0x01, /* array key (PATH, required for method call messages) */ + /* Variant array value: */ + 0x01, /* signature length */ + 'o', /* one complete type */ + 0x00, /* nul terminator */ + /* (Variant array value payload) */ + 0x01, 0x00, 0x00, 0x00, + '/', 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x03, /* array key (MEMBER, required for method call messages) */ + /* Variant array value: */ + 0x01, /* signature length */ + 't', /* one complete type; CORRUPTED, MEMBER should be 's' */ + 0x00, /* nul terminator */ + /* (Padding to 64-bit alignment of 't)' */ + 0x00, 0x00, 0x00, 0x00, + /* (Variant array value payload) */ + 'H', 'e', 'y', 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x08, /* array key (SIGNATURE) */ + /* Variant array value: */ + 0x01, /* signature length */ + 'g', /* one complete type */ + 0x00, /* nul terminator */ + /* (Variant array value payload) */ + 0x02, 's', 's', 0x00, + /* Some arbitrary valid content inside the message body: */ + 0x03, 0x00, 0x00, 0x00, + 'h', 'e', 'y', 0x00, + 0x05, 0x00, 0x00, 0x00, + 't', 'h', 'e', 'r', 'e', 0x00 + }; + + gsize size = sizeof (data); + GDBusMessage *message = NULL; + GError *local_error = NULL; + + g_test_summary ("Test that the type of well-known headers is checked."); + g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3061"); + + message = g_dbus_message_new_from_blob ((guchar *) data, size, + G_DBUS_CAPABILITY_FLAGS_NONE, + &local_error); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_assert_null (message); + + g_clear_error (&local_error); +} + /* ---------------------------------------------------------------------------------------------------- */ int @@ -1657,6 +1778,10 @@ main (int argc, test_message_parse_truncated); g_test_add_func ("/gdbus/message-parse/empty-structure", test_message_parse_empty_structure); + g_test_add_func ("/gdbus/message-parse/missing-header", + test_message_parse_missing_header); + g_test_add_func ("/gdbus/message-parse/invalid-header-type", + test_message_parse_invalid_header_type); return g_test_run(); }