From fb60ebb0df16808753a3b65c568a2b33513881e5 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Wed, 6 Dec 2023 22:47:14 +0000 Subject: [PATCH 1/2] gdbusmessage: Validate the types of all known message headers Previously, the code only validated the types of *required* message headers, and did not validate optional ones. Now, the headers are validated in one step, and a subsequent step checks whether the required ones are present. The existing tests have been updated to match the new error message wording. More tests will be added to test the new behaviour in a subsequent commit. Signed-off-by: Philip Withnall Fixes: #3093 --- gio/gdbusmessage.c | 305 +++++++++++++++++++++++--------- gio/tests/gdbus-serialization.c | 20 +-- 2 files changed, 230 insertions(+), 95 deletions(-) diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c index 4e5e3d24e..5a5fec880 100644 --- a/gio/gdbusmessage.c +++ b/gio/gdbusmessage.c @@ -1283,17 +1283,225 @@ get_type_fixed_size (const GVariantType *type) } } +static const char * +message_type_to_string (GDBusMessageType message_type) +{ + switch (message_type) + { + case G_DBUS_MESSAGE_TYPE_INVALID: + return "INVALID"; + case G_DBUS_MESSAGE_TYPE_METHOD_CALL: + return "METHOD_CALL"; + case G_DBUS_MESSAGE_TYPE_METHOD_RETURN: + return "METHOD_RETURN"; + case G_DBUS_MESSAGE_TYPE_ERROR: + return "ERROR"; + case G_DBUS_MESSAGE_TYPE_SIGNAL: + return "SIGNAL"; + default: + return "unknown-type"; + } +} + +static const char * +message_header_field_to_string (GDBusMessageHeaderField field) +{ + switch (field) + { + case G_DBUS_MESSAGE_HEADER_FIELD_INVALID: + return "INVALID"; + case G_DBUS_MESSAGE_HEADER_FIELD_PATH: + return "PATH"; + case G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE: + return "INTERFACE"; + case G_DBUS_MESSAGE_HEADER_FIELD_MEMBER: + return "MEMBER"; + case G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME: + return "ERROR_NAME"; + case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL: + return "REPLY_SERIAL"; + case G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION: + return "DESTINATION"; + case G_DBUS_MESSAGE_HEADER_FIELD_SENDER: + return "SENDER"; + case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE: + return "SIGNATURE"; + case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS: + return "NUM_UNIX_FDS"; + default: + return "unknown-field"; + } +} + +static gboolean +validate_header (GDBusMessage *message, + GDBusMessageHeaderField field, + GVariant *header_value, + const GVariantType *expected_type, + GError **error) +{ + g_assert (header_value != NULL); + + if (!g_variant_is_of_type (header_value, expected_type)) + { + char *expected_type_string = g_variant_type_dup_string (expected_type); + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("%s message: %s header field is invalid; expected a value of type ‘%s’"), + message_type_to_string (message->type), + message_header_field_to_string (field), + expected_type_string); + g_free (expected_type_string); + return FALSE; + } + + return TRUE; +} + +static gboolean +require_header (GDBusMessage *message, + GDBusMessageHeaderField field, + GError **error) +{ + GVariant *header_value = g_dbus_message_get_header (message, field); + + if (header_value == NULL) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("%s message: %s header field is missing or invalid"), + message_type_to_string (message->type), + message_header_field_to_string (field)); + return FALSE; + } + + return TRUE; +} + +/* Implement the validation rules given in + * https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-header-fields */ static gboolean validate_headers (GDBusMessage *message, GError **error) { gboolean ret; + GHashTableIter headers_iter; + gpointer key; + GVariant *header_value; g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ret = FALSE; + /* Validate the types of all known headers. */ + g_hash_table_iter_init (&headers_iter, message->headers); + while (g_hash_table_iter_next (&headers_iter, &key, (gpointer) &header_value)) + { + GDBusMessageHeaderField field_type = GPOINTER_TO_INT (key); + + switch (field_type) + { + case G_DBUS_MESSAGE_HEADER_FIELD_INVALID: + /* The invalid header must be rejected as per + * https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-header-fields */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("%s message: INVALID header field supplied"), + message_type_to_string (message->type)); + goto out; + case G_DBUS_MESSAGE_HEADER_FIELD_PATH: + if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_OBJECT_PATH, error)) + goto out; + if (g_strcmp0 (g_variant_get_string (header_value, NULL), "/org/freedesktop/DBus/Local") == 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("%s message: PATH header field is using the reserved value /org/freedesktop/DBus/Local"), + message_type_to_string (message->type)); + goto out; + } + break; + case G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE: + if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_STRING, error)) + goto out; + if (!g_dbus_is_interface_name (g_variant_get_string (header_value, NULL))) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("%s message: INTERFACE header field does not contain a valid interface name"), + message_type_to_string (message->type)); + goto out; + } + if (g_strcmp0 (g_variant_get_string (header_value, NULL), "org.freedesktop.DBus.Local") == 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("%s message: INTERFACE header field is using the reserved value org.freedesktop.DBus.Local"), + message_type_to_string (message->type)); + goto out; + } + break; + case G_DBUS_MESSAGE_HEADER_FIELD_MEMBER: + if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_STRING, error)) + goto out; + if (!g_dbus_is_member_name (g_variant_get_string (header_value, NULL))) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("%s message: MEMBER header field does not contain a valid member name"), + message_type_to_string (message->type)); + goto out; + } + break; + case G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME: + if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_STRING, error)) + goto out; + if (!g_dbus_is_error_name (g_variant_get_string (header_value, NULL))) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + _("%s message: ERROR_NAME header field does not contain a valid error name"), + message_type_to_string (message->type)); + goto out; + } + break; + case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL: + if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_UINT32, error)) + goto out; + break; + case G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION: + if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_STRING, error)) + goto out; + break; + case G_DBUS_MESSAGE_HEADER_FIELD_SENDER: + if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_STRING, error)) + goto out; + break; + case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE: + if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_SIGNATURE, error)) + goto out; + break; + case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS: + if (!validate_header (message, field_type, header_value, G_VARIANT_TYPE_UINT32, error)) + goto out; + break; + default: + /* Ignore unknown fields as per + * https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-header-fields. */ + continue; + } + } + + /* Check for message-type-specific required headers. */ switch (message->type) { case G_DBUS_MESSAGE_TYPE_INVALID: @@ -1302,102 +1510,29 @@ validate_headers (GDBusMessage *message, G_IO_ERROR_INVALID_ARGUMENT, _("type is INVALID")); goto out; - break; case G_DBUS_MESSAGE_TYPE_METHOD_CALL: - { - 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; - } - } + if (!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, error) || + !require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, error)) + goto out; break; case G_DBUS_MESSAGE_TYPE_METHOD_RETURN: - { - 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; - } - } + if (!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, error)) + goto out; break; case G_DBUS_MESSAGE_TYPE_ERROR: - { - 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; - } - } + if (!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME, error) || + !require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, error)) + goto out; break; case G_DBUS_MESSAGE_TYPE_SIGNAL: - { - 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; - } - } + if (!require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, error) || + !require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE, error) || + !require_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, error)) + goto out; break; default: diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c index 9ad540956..b500d72df 100644 --- a/gio/tests/gdbus-serialization.c +++ b/gio/tests/gdbus-serialization.c @@ -921,7 +921,7 @@ test_message_serialize_header_checks (void) 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, ==, "Signature header found but is not of type signature"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: SIGNATURE header field is invalid; expected a value of type ‘g’"); g_assert_null (blob); g_clear_error (&error); @@ -936,14 +936,14 @@ 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 or invalid"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: INTERFACE header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); /* interface reserved value => error */ g_dbus_message_set_interface (message, "org.freedesktop.DBus.Local"); 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: The INTERFACE header field is using the reserved value org.freedesktop.DBus.Local"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: INTERFACE header field is using the reserved value org.freedesktop.DBus.Local"); g_clear_error (&error); g_assert_null (blob); /* reset interface */ @@ -953,14 +953,14 @@ 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 or invalid"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); /* path reserved value => error */ g_dbus_message_set_path (message, "/org/freedesktop/DBus/Local"); 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: The PATH header field is using the reserved value /org/freedesktop/DBus/Local"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH header field is using the reserved value /org/freedesktop/DBus/Local"); g_clear_error (&error); g_assert_null (blob); /* reset path */ @@ -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 or invalid"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: 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 or invalid"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH 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 or invalid"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: MEMBER header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); /* reset member */ @@ -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 or invalid"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: 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 or invalid"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL header field is missing or invalid"); g_clear_error (&error); g_assert_null (blob); g_object_unref (reply); From cecc5ef778dca4e15b7459ac9895fd8ba11b245b Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Wed, 6 Dec 2023 23:40:49 +0000 Subject: [PATCH 2/2] tests: Add more tests of header validation in D-Bus message serialisation See the previous commit. These additions were kept in a separate commit to make the changes to the existing tests in the previous commit clearer. Signed-off-by: Philip Withnall Helps: #3093 --- gio/tests/gdbus-serialization.c | 159 +++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 3 deletions(-) diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c index b500d72df..16bf35788 100644 --- a/gio/tests/gdbus-serialization.c +++ b/gio/tests/gdbus-serialization.c @@ -914,19 +914,106 @@ test_message_serialize_header_checks (void) g_object_unref (message); /* - * check that we can't serialize messages with SIGNATURE set to a non-signature-typed value + * check we can't serialize messages with an INVALID header */ message = g_dbus_message_new_signal ("/the/path", "The.Interface", "TheMember"); - g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_boolean (FALSE)); + g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_INVALID, g_variant_new_boolean (FALSE)); 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: SIGNATURE header field is invalid; expected a value of type ‘g’"); + g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: INVALID header field supplied"); g_assert_null (blob); g_clear_error (&error); g_clear_object (&message); + /* + * check that we can't serialize messages with various fields set to incorrectly typed values + */ + const struct + { + GDBusMessageHeaderField field; + const char *invalid_value; /* as a GVariant in text form */ + const char *expected_error_message; + } + field_type_tests[] = + { + { + G_DBUS_MESSAGE_HEADER_FIELD_PATH, + "'/correct/value/but/wrong/type'", + "Cannot serialize message: SIGNAL message: PATH header field is invalid; expected a value of type ‘o’" + }, + { + G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE, + "@u 5", + "Cannot serialize message: SIGNAL message: INTERFACE header field is invalid; expected a value of type ‘s’" + }, + { + G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE, + "'valid type, but not an interface name'", + "Cannot serialize message: SIGNAL message: INTERFACE header field does not contain a valid interface name" + }, + { + G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, + "@u 5", + "Cannot serialize message: SIGNAL message: MEMBER header field is invalid; expected a value of type ‘s’" + }, + { + G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, + "'valid type, but not a member name'", + "Cannot serialize message: SIGNAL message: MEMBER header field does not contain a valid member name" + }, + { + G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME, + "@u 5", + "Cannot serialize message: SIGNAL message: ERROR_NAME header field is invalid; expected a value of type ‘s’" + }, + { + G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME, + "'valid type, but not an error name'", + "Cannot serialize message: SIGNAL message: ERROR_NAME header field does not contain a valid error name" + }, + { + G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, + "'oops'", + "Cannot serialize message: SIGNAL message: REPLY_SERIAL header field is invalid; expected a value of type ‘u’" + }, + { + G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION, + "@u 5", + "Cannot serialize message: SIGNAL message: DESTINATION header field is invalid; expected a value of type ‘s’" + }, + { + G_DBUS_MESSAGE_HEADER_FIELD_SENDER, + "@u 5", + "Cannot serialize message: SIGNAL message: SENDER header field is invalid; expected a value of type ‘s’" + }, + { + G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, + "false", + "Cannot serialize message: SIGNAL message: SIGNATURE header field is invalid; expected a value of type ‘g’" + }, + { + G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS, + "'five'", + "Cannot serialize message: SIGNAL message: NUM_UNIX_FDS header field is invalid; expected a value of type ‘u’" + }, + }; + + for (size_t i = 0; i < G_N_ELEMENTS (field_type_tests); i++) + { + message = g_dbus_message_new_signal ("/the/path", "The.Interface", "TheMember"); + g_dbus_message_set_header (message, field_type_tests[i].field, g_variant_new_parsed (field_type_tests[i].invalid_value)); + 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, ==, field_type_tests[i].expected_error_message); + g_assert_null (blob); + + g_clear_error (&error); + g_clear_object (&message); + } + /* * check we can't serialize signal messages with INTERFACE, PATH or MEMBER unset / set to reserved value */ @@ -1045,6 +1132,70 @@ test_message_serialize_header_checks (void) g_object_unref (message); } +static void +test_message_serialize_header_checks_valid (void) +{ + GDBusMessage *message = NULL, *reply = NULL; + GError *local_error = NULL; + guchar *blob; + gsize blob_size; + + g_test_summary ("Test that validation allows well-formed messages of all the different types"); + + /* Method call */ + message = g_dbus_message_new_method_call ("Some.Name", "/the/path", "org.some.Interface", "TheMethod"); + g_dbus_message_set_serial (message, 666); + blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error); + g_assert_no_error (local_error); + g_assert_nonnull (blob); + g_free (blob); + + /* Method return */ + reply = g_dbus_message_new_method_reply (message); + blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error); + g_assert_no_error (local_error); + g_assert_nonnull (blob); + g_free (blob); + g_clear_object (&reply); + + /* Error */ + reply = g_dbus_message_new_method_error (message, "Error.Name", "Some error message"); + blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error); + g_assert_no_error (local_error); + g_assert_nonnull (blob); + g_free (blob); + + g_clear_object (&reply); + g_clear_object (&message); + + /* Signal */ + message = g_dbus_message_new_signal ("/the/path", "org.some.Interface", "SignalName"); + blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error); + g_assert_no_error (local_error); + g_assert_nonnull (blob); + g_free (blob); + g_clear_object (&message); + + /* Also check that an unknown message type is allowed */ + message = g_dbus_message_new (); + g_dbus_message_set_message_type (message, 123); + blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error); + g_assert_no_error (local_error); + g_assert_nonnull (blob); + g_free (blob); + g_clear_object (&message); + + /* Even one with a well-defined field on it */ + message = g_dbus_message_new (); + g_dbus_message_set_message_type (message, 123); + g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS, g_variant_new_uint32 (0)); + blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error); + g_assert_no_error (local_error); + g_assert_nonnull (blob); + g_free (blob); + g_clear_object (&message); +} + /* ---------------------------------------------------------------------------------------------------- */ static void @@ -1755,6 +1906,8 @@ main (int argc, test_message_serialize_invalid); g_test_add_func ("/gdbus/message-serialize/header-checks", test_message_serialize_header_checks); + g_test_add_func ("/gdbus/message-serialize/header-checks/valid", + test_message_serialize_header_checks_valid); g_test_add_func ("/gdbus/message-serialize/double-array", test_message_serialize_double_array); g_test_add_func ("/gdbus/message-serialize/empty-structure",