Fix CVE-2026-15588: limit D-Bus auth line read length
Backport upstream fix from GNOME/glib MR !5240 (commit 407349aa) for CVE-2026-15588. The patch adds a line length limit in GDBusAuth to prevent an untrusted D-Bus client from exhausting server memory by sending arbitrarily long authentication lines. A corresponding unit test is included. CVE: CVE-2026-15588 Upstream patches: - https://gitlab.gnome.org/GNOME/glib/-/merge_requests/5240.patch Resolves: RHEL-219128 This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent. Assisted-by: Ymir
This commit is contained in:
parent
27fb374f16
commit
a06b1ba6d5
263
CVE-2026-15588.patch
Normal file
263
CVE-2026-15588.patch
Normal file
@ -0,0 +1,263 @@
|
||||
From 9bbe8d82a27f24ef371718bae8815cd7fca92def Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@gnome.org>
|
||||
Date: Sat, 4 Jul 2026 18:13:08 +0100
|
||||
Subject: [PATCH] gdbusauth: Limit length of lines read from client
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The client isn’t trusted at this point, and there was previously nothing
|
||||
limiting how long a line `GDBusAuth` would read. So an untrusted client
|
||||
could exhaust the server’s memory by sending anything except `\r\n`.
|
||||
|
||||
Fix that by applying a reasonably length limit when reading a line, and
|
||||
add a unit test.
|
||||
|
||||
Spotted by Gitee Codepecker Lab.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
|
||||
Fixes: #3985
|
||||
---
|
||||
gio/gdbusauth.c | 44 +++++++++++++++
|
||||
gio/tests/gdbus-auth.c | 119 +++++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 163 insertions(+)
|
||||
|
||||
diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c
|
||||
index cb84d0d64..72ffd0b4d 100644
|
||||
--- a/gio/gdbusauth.c
|
||||
+++ b/gio/gdbusauth.c
|
||||
@@ -262,6 +262,21 @@ find_mech_by_name (GDBusAuth *auth,
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static size_t
|
||||
+get_longest_mechanism_name_length (GDBusAuth *auth)
|
||||
+{
|
||||
+ size_t len = 0;
|
||||
+
|
||||
+ for (GList *l = auth->priv->available_mechanisms; l != NULL; l = l->next)
|
||||
+ {
|
||||
+ Mechanism *m = l->data;
|
||||
+
|
||||
+ len = MAX (len, strlen (m->name));
|
||||
+ }
|
||||
+
|
||||
+ return len;
|
||||
+}
|
||||
+
|
||||
GDBusAuth *
|
||||
_g_dbus_auth_new (GIOStream *stream)
|
||||
{
|
||||
@@ -270,6 +285,20 @@ _g_dbus_auth_new (GIOStream *stream)
|
||||
NULL);
|
||||
}
|
||||
|
||||
+/* Arbitrarily chosen limit on the length of a DATA command payload, to prevent
|
||||
+ * unbounded reads from malicious clients.
|
||||
+ *
|
||||
+ * - The ANONYMOUS mechanism doesn’t use DATA.
|
||||
+ * - The EXTERNAL mechanism just uses it to transfer a decimal-encoded UID.
|
||||
+ * - The DBUS_COOKIE_SHA1 mechanism transfers a challenge and a SHA1 hash. The
|
||||
+ * hash is bounded in length, but the challenge is not, so could potentially
|
||||
+ * hit this limit. It doesn’t seem unreasonable to bound the challenge to
|
||||
+ * ~4KB though. GDBus itself generates a 16 byte challenge.
|
||||
+ *
|
||||
+ * See https://dbus.freedesktop.org/doc/dbus-specification.html#auth-command-data
|
||||
+ */
|
||||
+#define MAX_DATA_PAYLOAD_LENGTH_BYTES 4096
|
||||
+
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
/* like g_data_input_stream_read_line() but sets error if there's no content to read */
|
||||
static gchar *
|
||||
@@ -307,6 +336,7 @@ _my_g_data_input_stream_read_line (GDataInputStream *dis,
|
||||
*/
|
||||
static gchar *
|
||||
_my_g_input_stream_read_line_safe (GInputStream *i,
|
||||
+ size_t max_line_length,
|
||||
gsize *out_line_length,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
@@ -316,11 +346,22 @@ _my_g_input_stream_read_line_safe (GInputStream *i,
|
||||
gssize num_read;
|
||||
gboolean last_was_cr;
|
||||
|
||||
+ g_assert (max_line_length <= SIZE_MAX - 2);
|
||||
+
|
||||
str = g_string_new (NULL);
|
||||
|
||||
last_was_cr = FALSE;
|
||||
while (TRUE)
|
||||
{
|
||||
+ if (str->len >= max_line_length + 2 /* allow for \r\n */)
|
||||
+ {
|
||||
+ g_set_error_literal (error,
|
||||
+ G_IO_ERROR,
|
||||
+ G_IO_ERROR_FAILED,
|
||||
+ _("Malformed D-Bus authentication line"));
|
||||
+ goto fail;
|
||||
+ }
|
||||
+
|
||||
num_read = g_input_stream_read (i,
|
||||
&c,
|
||||
1,
|
||||
@@ -1075,6 +1116,7 @@ _g_dbus_auth_run_server (GDBusAuth *auth,
|
||||
case SERVER_STATE_WAITING_FOR_AUTH:
|
||||
debug_print ("SERVER: WaitingForAuth");
|
||||
line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream),
|
||||
+ strlen ("AUTH ") + get_longest_mechanism_name_length (auth) + strlen (" ") + MAX_DATA_PAYLOAD_LENGTH_BYTES,
|
||||
&line_length,
|
||||
cancellable,
|
||||
error);
|
||||
@@ -1296,6 +1338,7 @@ _g_dbus_auth_run_server (GDBusAuth *auth,
|
||||
case SERVER_STATE_WAITING_FOR_DATA:
|
||||
debug_print ("SERVER: WaitingForData");
|
||||
line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream),
|
||||
+ strlen ("DATA ") + MAX_DATA_PAYLOAD_LENGTH_BYTES,
|
||||
&line_length,
|
||||
cancellable,
|
||||
error);
|
||||
@@ -1338,6 +1381,7 @@ _g_dbus_auth_run_server (GDBusAuth *auth,
|
||||
case SERVER_STATE_WAITING_FOR_BEGIN:
|
||||
debug_print ("SERVER: WaitingForBegin");
|
||||
line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream),
|
||||
+ MAX (strlen ("BEGIN"), strlen ("NEGOTIATE_UNIX_FD")),
|
||||
&line_length,
|
||||
cancellable,
|
||||
error);
|
||||
diff --git a/gio/tests/gdbus-auth.c b/gio/tests/gdbus-auth.c
|
||||
index 18288f36d..ed1894969 100644
|
||||
--- a/gio/tests/gdbus-auth.c
|
||||
+++ b/gio/tests/gdbus-auth.c
|
||||
@@ -278,6 +278,124 @@ temp_dbus_keyrings_teardown (void)
|
||||
g_unsetenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION");
|
||||
}
|
||||
|
||||
+static void
|
||||
+async_result_cb (GObject *obj,
|
||||
+ GAsyncResult *result,
|
||||
+ void *user_data)
|
||||
+{
|
||||
+ GAsyncResult **result_out = user_data;
|
||||
+
|
||||
+ g_assert (result_out != NULL);
|
||||
+ g_assert (*result_out == NULL);
|
||||
+
|
||||
+ *result_out = g_object_ref (result);
|
||||
+ g_main_context_wakeup (g_main_context_get_thread_default ());
|
||||
+}
|
||||
+
|
||||
+static gboolean
|
||||
+server_new_connection_unexpected_cb (GDBusServer *server,
|
||||
+ GDBusConnection *connection,
|
||||
+ void *user_data)
|
||||
+{
|
||||
+ g_assert_not_reached ();
|
||||
+ return FALSE;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+test_auth_server_read_limit (void)
|
||||
+{
|
||||
+ GDBusServer *server = NULL;
|
||||
+ unsigned long new_connection_id = 0;
|
||||
+ const char *server_address;
|
||||
+ GIOStream *client_stream = NULL;
|
||||
+ GOutputStream *client_output_stream;
|
||||
+ GInputStream *client_input_stream;
|
||||
+ GAsyncResult *result = NULL;
|
||||
+ char *write_buffer = NULL;
|
||||
+ char read_buffer[100];
|
||||
+ ssize_t read_len;
|
||||
+ size_t bytes_written;
|
||||
+ GError *local_error = NULL;
|
||||
+
|
||||
+ g_test_summary ("Test that GDBusServer limits the lengths of reads it does during auth from a client");
|
||||
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3985");
|
||||
+
|
||||
+ server = server_new_for_mechanism (NULL);
|
||||
+
|
||||
+ new_connection_id = g_signal_connect (server,
|
||||
+ "new-connection",
|
||||
+ G_CALLBACK (server_new_connection_unexpected_cb),
|
||||
+ NULL);
|
||||
+ server_address = g_dbus_server_get_client_address (server);
|
||||
+ g_dbus_server_start (server);
|
||||
+
|
||||
+ /* Start connecting as a client */
|
||||
+ g_dbus_address_get_stream (server_address, NULL, async_result_cb, &result);
|
||||
+
|
||||
+ while (result == NULL)
|
||||
+ g_main_context_iteration (NULL, TRUE);
|
||||
+
|
||||
+ client_stream = g_dbus_address_get_stream_finish (result, NULL, &local_error);
|
||||
+ g_assert_no_error (local_error);
|
||||
+ g_clear_object (&result);
|
||||
+
|
||||
+ /* Send an over-long AUTH line, maliciously */
|
||||
+ client_output_stream = g_io_stream_get_output_stream (client_stream);
|
||||
+ client_input_stream = g_io_stream_get_input_stream (client_stream);
|
||||
+
|
||||
+ write_buffer = g_strdup_printf ("AUTH DBUS_COOKIE_SHA1 context%0*d 123 456\r\n", 5000, 0);
|
||||
+
|
||||
+ g_output_stream_write_all_async (client_output_stream,
|
||||
+ write_buffer,
|
||||
+ strlen (write_buffer),
|
||||
+ G_PRIORITY_DEFAULT,
|
||||
+ NULL,
|
||||
+ async_result_cb,
|
||||
+ &result);
|
||||
+
|
||||
+ while (result == NULL)
|
||||
+ g_main_context_iteration (NULL, TRUE);
|
||||
+
|
||||
+ g_output_stream_write_all_finish (client_output_stream, result, &bytes_written, &local_error);
|
||||
+ g_assert_no_error (local_error);
|
||||
+ g_assert_cmpuint (bytes_written, ==, strlen (write_buffer));
|
||||
+ g_clear_object (&result);
|
||||
+
|
||||
+ g_clear_pointer (&write_buffer, g_free);
|
||||
+
|
||||
+ /* Authentication should have been rejected, so reading or writing the stream
|
||||
+ * should now fail. */
|
||||
+ read_len = g_input_stream_read (client_input_stream,
|
||||
+ read_buffer,
|
||||
+ sizeof (read_buffer),
|
||||
+ NULL,
|
||||
+ &local_error);
|
||||
+ g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED);
|
||||
+ g_assert_cmpint (read_len, <, 0);
|
||||
+ g_clear_error (&local_error);
|
||||
+
|
||||
+ write_buffer = g_strdup_printf ("AUTH\r\n");
|
||||
+
|
||||
+ g_output_stream_write_all (client_output_stream,
|
||||
+ write_buffer,
|
||||
+ strlen (write_buffer),
|
||||
+ &bytes_written,
|
||||
+ NULL,
|
||||
+ &local_error);
|
||||
+ g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED);
|
||||
+ g_assert_cmpuint (bytes_written, ==, 0);
|
||||
+ g_clear_error (&local_error);
|
||||
+
|
||||
+ g_clear_pointer (&write_buffer, g_free);
|
||||
+
|
||||
+ /* Cleanup */
|
||||
+ g_clear_object (&client_stream);
|
||||
+ g_dbus_server_stop (server);
|
||||
+
|
||||
+ g_clear_signal_handler (&new_connection_id, server);
|
||||
+ g_clear_object (&server);
|
||||
+}
|
||||
+
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
int
|
||||
@@ -297,6 +415,7 @@ main (int argc,
|
||||
g_test_add_func ("/gdbus/auth/server/ANONYMOUS", auth_server_anonymous);
|
||||
g_test_add_func ("/gdbus/auth/server/EXTERNAL", auth_server_external);
|
||||
g_test_add_func ("/gdbus/auth/server/DBUS_COOKIE_SHA1", auth_server_dbus_cookie_sha1);
|
||||
+ g_test_add_func ("/gdbus/auth/server/read-limit", test_auth_server_read_limit);
|
||||
|
||||
/* TODO: we currently don't have tests for
|
||||
*
|
||||
@ -1,6 +1,6 @@
|
||||
Name: glib2
|
||||
Version: 2.68.4
|
||||
Release: 26%{?dist}
|
||||
Release: 27%{?dist}
|
||||
Summary: A library of handy utility functions
|
||||
|
||||
License: LGPLv2+
|
||||
@ -110,6 +110,9 @@ Patch: CVE-2026-58013.patch
|
||||
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/5131
|
||||
Patch: CVE-2026-58011.patch
|
||||
|
||||
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/5240
|
||||
Patch: CVE-2026-15588.patch
|
||||
|
||||
BuildRequires: chrpath
|
||||
BuildRequires: gcc
|
||||
BuildRequires: gcc-c++
|
||||
@ -331,6 +334,10 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
|
||||
%{_datadir}/installed-tests
|
||||
|
||||
%changelog
|
||||
* Wed Aug 19 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.68.4-27
|
||||
- Fix CVE-2026-15588: limit D-Bus auth line read length
|
||||
Resolves: RHEL-219128
|
||||
|
||||
* Wed Aug 19 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.68.4-26
|
||||
- Fix CVE-2026-58011: range validation in g_date_time_add_full()
|
||||
Resolves: RHEL-212196
|
||||
|
||||
Loading…
Reference in New Issue
Block a user