Blame SOURCES/flatpak-0.8.8-cve-2018-6560.patch

7e13fc
From e00ded769dcdddea0169dd813c5878c915a63f6a Mon Sep 17 00:00:00 2001
7e13fc
From: Alexander Larsson <alexl@redhat.com>
7e13fc
Date: Sun, 28 Jan 2018 20:51:54 +0100
7e13fc
Subject: [PATCH] Fix vulnerability in dbus proxy
7e13fc
7e13fc
During the authentication all client data is directly forwarded
7e13fc
to the dbus daemon as is, until we detect the BEGIN command after
7e13fc
which we start filtering the binary dbus protocol.
7e13fc
7e13fc
Unfortunately the detection of the BEGIN command in the proxy
7e13fc
did not exactly match the detection in the dbus daemon. A BEGIN
7e13fc
followed by a space or tab was considered ok in the daemon but
7e13fc
not by the proxy. This could be exploited to send arbitrary
7e13fc
dbus messages to the host, which can be used to break out of
7e13fc
the sandbox.
7e13fc
7e13fc
This was noticed by Gabriel Campana of The Google Security Team.
7e13fc
7e13fc
This fix makes the detection of the authentication phase end
7e13fc
match the dbus code. In addition we duplicate the authentication
7e13fc
line validation from dbus, which includes ensuring all data is
7e13fc
ASCII, and limiting the size of a line to 16k. In fact, we add
7e13fc
some extra stringent checks, disallowing ASCII control chars and
7e13fc
requiring that auth lines start with a capital letter.
7e13fc
---
7e13fc
 dbus-proxy/flatpak-proxy.c | 127 ++++++++++++++++++++++++++-----------
7e13fc
 1 file changed, 89 insertions(+), 38 deletions(-)
7e13fc
7e13fc
diff --git a/dbus-proxy/flatpak-proxy.c b/dbus-proxy/flatpak-proxy.c
7e13fc
index aee73993..ec90cba7 100644
7e13fc
--- a/dbus-proxy/flatpak-proxy.c
7e13fc
+++ b/dbus-proxy/flatpak-proxy.c
7e13fc
@@ -173,10 +173,11 @@ typedef struct FlatpakProxyClient FlatpakProxyClient;
7e13fc
 FlatpakPolicy flatpak_proxy_get_policy (FlatpakProxy *proxy,
7e13fc
                                         const char   *name);
7e13fc
 
7e13fc
-/* We start looking ignoring the first cr-lf
7e13fc
-   since there is no previous line initially */
7e13fc
-#define AUTH_END_INIT_OFFSET 2
7e13fc
-#define AUTH_END_STRING "\r\nBEGIN\r\n"
7e13fc
+#define FIND_AUTH_END_CONTINUE -1
7e13fc
+#define FIND_AUTH_END_ABORT -2
7e13fc
+
7e13fc
+#define AUTH_LINE_SENTINEL "\r\n"
7e13fc
+#define AUTH_BEGIN "BEGIN"
7e13fc
 
7e13fc
 typedef enum {
7e13fc
   EXPECTED_REPLY_NONE,
7e13fc
@@ -251,7 +252,7 @@ struct FlatpakProxyClient
7e13fc
   FlatpakProxy *proxy;
7e13fc
 
7e13fc
   gboolean      authenticated;
7e13fc
-  int           auth_end_offset;
7e13fc
+  GByteArray   *auth_buffer;
7e13fc
 
7e13fc
   ProxySide     client_side;
7e13fc
   ProxySide     bus_side;
7e13fc
@@ -363,6 +364,7 @@ flatpak_proxy_client_finalize (GObject *object)
7e13fc
   client->proxy->clients = g_list_remove (client->proxy->clients, client);
7e13fc
   g_clear_object (&client->proxy);
7e13fc
 
7e13fc
+  g_byte_array_free (client->auth_buffer, TRUE);
7e13fc
   g_hash_table_destroy (client->rewrite_reply);
7e13fc
   g_hash_table_destroy (client->get_owner_reply);
7e13fc
   g_hash_table_destroy (client->unique_id_policy);
7e13fc
@@ -398,7 +400,7 @@ flatpak_proxy_client_init (FlatpakProxyClient *client)
7e13fc
   init_side (client, &client->client_side);
7e13fc
   init_side (client, &client->bus_side);
7e13fc
 
7e13fc
-  client->auth_end_offset = AUTH_END_INIT_OFFSET;
7e13fc
+  client->auth_buffer = g_byte_array_new ();
7e13fc
   client->rewrite_reply = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
7e13fc
   client->get_owner_reply = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
7e13fc
   client->unique_id_policy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
7e13fc
@@ -2203,51 +2205,92 @@ got_buffer_from_side (ProxySide *side, Buffer *buffer)
7e13fc
     got_buffer_from_bus (client, side, buffer);
7e13fc
 }
7e13fc
 
7e13fc
+#define _DBUS_ISASCII(c) ((c) != '\0' && (((c) & ~0x7f) == 0))
7e13fc
+
7e13fc
+static gboolean
7e13fc
+auth_line_is_valid (guint8 *line, guint8 *line_end)
7e13fc
+{
7e13fc
+  guint8 *p;
7e13fc
+
7e13fc
+  for (p = line; p < line_end; p++)
7e13fc
+    {
7e13fc
+      if (!_DBUS_ISASCII(*p))
7e13fc
+        return FALSE;
7e13fc
+
7e13fc
+      /* Technically, the dbus spec allows all ASCII characters, but for robustness we also
7e13fc
+         fail if we see any control characters. Such low values will appear in  potential attacks,
7e13fc
+         but will never happen in real sasl (where all binary data is hex encoded). */
7e13fc
+      if (*p < ' ')
7e13fc
+        return FALSE;
7e13fc
+    }
7e13fc
+
7e13fc
+  /* For robustness we require the first char of the line to be an upper case letter.
7e13fc
+     This is not technically required by the dbus spec, but all commands are upper
7e13fc
+     case, and there is no provisioning for whitespace before the command, so in practice
7e13fc
+     this is true, and this means we're not confused by e.g. initial whitespace. */
7e13fc
+  if (line[0] < 'A' || line[0] > 'Z')
7e13fc
+    return FALSE;
7e13fc
+
7e13fc
+  return TRUE;
7e13fc
+}
7e13fc
+
7e13fc
+static gboolean
7e13fc
+auth_line_is_begin (guint8 *line)
7e13fc
+{
7e13fc
+  guint8 next_char;
7e13fc
+
7e13fc
+  if (!g_str_has_prefix ((char *)line, AUTH_BEGIN))
7e13fc
+    return FALSE;
7e13fc
+
7e13fc
+  /* dbus-daemon accepts either nothing, or a whitespace followed by anything as end of auth */
7e13fc
+  next_char = line[strlen (AUTH_BEGIN)];
7e13fc
+  return (next_char == 0 ||
7e13fc
+          next_char == ' ' ||
7e13fc
+          next_char == '\t');
7e13fc
+}
7e13fc
+
7e13fc
 static gssize
7e13fc
 find_auth_end (FlatpakProxyClient *client, Buffer *buffer)
7e13fc
 {
7e13fc
-  guchar *match;
7e13fc
-  int i;
7e13fc
+  goffset offset = 0;
7e13fc
+  gsize original_size = client->auth_buffer->len;
7e13fc
+
7e13fc
+  /* Add the new data to the remaining data from last iteration */
7e13fc
+  g_byte_array_append (client->auth_buffer, buffer->data, buffer->pos);
7e13fc
 
7e13fc
-  /* First try to match any leftover at the start */
7e13fc
-  if (client->auth_end_offset > 0)
7e13fc
+  while (TRUE)
7e13fc
     {
7e13fc
-      gsize left = strlen (AUTH_END_STRING) - client->auth_end_offset;
7e13fc
-      gsize to_match = MIN (left, buffer->pos);
7e13fc
-      /* Matched at least up to to_match */
7e13fc
-      if (memcmp (buffer->data, &AUTH_END_STRING[client->auth_end_offset], to_match) == 0)
7e13fc
+      guint8 *line_start = client->auth_buffer->data + offset;
7e13fc
+      gsize remaining_data = client->auth_buffer->len - offset;
7e13fc
+      guint8 *line_end;
7e13fc
+
7e13fc
+      line_end = memmem (line_start, remaining_data,
7e13fc
+                         AUTH_LINE_SENTINEL, strlen (AUTH_LINE_SENTINEL));
7e13fc
+      if (line_end) /* Found end of line */
7e13fc
         {
7e13fc
-          client->auth_end_offset += to_match;
7e13fc
+          offset = (line_end + strlen (AUTH_LINE_SENTINEL) - line_start);
7e13fc
 
7e13fc
-          /* Matched all */
7e13fc
-          if (client->auth_end_offset == strlen (AUTH_END_STRING))
7e13fc
-            return to_match;
7e13fc
+          if (!auth_line_is_valid (line_start, line_end))
7e13fc
+            return FIND_AUTH_END_ABORT;
7e13fc
 
7e13fc
-          /* Matched to end of buffer */
7e13fc
-          return -1;
7e13fc
-        }
7e13fc
+          *line_end = 0;
7e13fc
+          if (auth_line_is_begin (line_start))
7e13fc
+            return offset - original_size;
7e13fc
 
7e13fc
-      /* Did not actually match at start */
7e13fc
-      client->auth_end_offset = -1;
7e13fc
-    }
7e13fc
+          /* continue with next line */
7e13fc
+        }
7e13fc
+      else
7e13fc
+        {
7e13fc
+          /* No end-of-line in this buffer */
7e13fc
+          g_byte_array_remove_range (client->auth_buffer, 0, offset);
7e13fc
 
7e13fc
-  /* Look for whole match inside buffer */
7e13fc
-  match = memmem (buffer, buffer->pos,
7e13fc
-                  AUTH_END_STRING, strlen (AUTH_END_STRING));
7e13fc
-  if (match != NULL)
7e13fc
-    return match - buffer->data + strlen (AUTH_END_STRING);
7e13fc
+          /* Abort if more than 16k before newline, similar to what dbus-daemon does */
7e13fc
+          if (client->auth_buffer->len >= 16*1024)
7e13fc
+            return FIND_AUTH_END_ABORT;
7e13fc
 
7e13fc
-  /* Record longest prefix match at the end */
7e13fc
-  for (i = MIN (strlen (AUTH_END_STRING) - 1, buffer->pos); i > 0; i--)
7e13fc
-    {
7e13fc
-      if (memcmp (buffer->data + buffer->pos - i, AUTH_END_STRING, i) == 0)
7e13fc
-        {
7e13fc
-          client->auth_end_offset = i;
7e13fc
-          break;
7e13fc
+          return FIND_AUTH_END_CONTINUE;
7e13fc
         }
7e13fc
     }
7e13fc
-
7e13fc
-  return -1;
7e13fc
 }
7e13fc
 
7e13fc
 static gboolean
7e13fc
@@ -2306,6 +2349,14 @@ side_in_cb (GSocket *socket, GIOCondition condition, gpointer user_data)
7e13fc
                       if (extra_data > 0)
7e13fc
                         side->extra_input_data = g_bytes_new (buffer->data + buffer->size, extra_data);
7e13fc
                     }
7e13fc
+                  else if (auth_end == FIND_AUTH_END_ABORT)
7e13fc
+                    {
7e13fc
+                      buffer_unref (buffer);
7e13fc
+                      if (client->proxy->log_messages)
7e13fc
+                        g_print ("Invalid AUTH line, aborting\n");
7e13fc
+                      side_closed (side);
7e13fc
+                      break;
7e13fc
+                    }
7e13fc
                 }
7e13fc
 
7e13fc
               got_buffer_from_side (side, buffer);