Submitted By:            Douglas R. Reno <renodr at linuxfromscratch dot org>
Date:                    2019-06-29
Initial Package Version: 1.40.1
Upstream Status:         Applied
Origin:                  Upstream
Description:             Fixes four security vulnerabilities in GVFS, two rated
                         as High and two as Critical. The IDs are CVE-2019-12795,
                         CVE-2019-12447, CVE-2019-12448, and CVE-2019-12449. In
                         addition, fix the AFP backend for anyone who uses it
                         since Apple iOS devices beyond 2017 use a UUID longer
                         than 40 characters.

diff -Naurp gvfs-1.40.1.orig/common/gmountsource.c gvfs-1.40.1/common/gmountsource.c
--- gvfs-1.40.1.orig/common/gmountsource.c	2019-04-09 01:36:07.000000000 -0500
+++ gvfs-1.40.1/common/gmountsource.c	2019-06-29 13:37:36.962027478 -0500
@@ -146,8 +146,8 @@ typedef struct AskSyncData AskSyncData;
 struct AskSyncData {
 
   /* For sync calls */
-  GMutex mutex;
-  GCond cond;
+  GMainContext *context;
+  GMainLoop *loop;
 
   /* results: */
   GAsyncResult *result;
@@ -375,10 +375,7 @@ ask_reply_sync  (GObject *source_object,
 
   data->result = g_object_ref (res);
 
-  /* Wake up sync call thread */
-  g_mutex_lock (&data->mutex);
-  g_cond_signal (&data->cond);
-  g_mutex_unlock (&data->mutex);
+  g_main_loop_quit (data->loop);
 }
 
 gboolean
@@ -397,11 +394,10 @@ g_mount_source_ask_password (GMountSourc
   gboolean handled;
   AskSyncData data;
 
-  memset (&data, 0, sizeof (data));
-  g_mutex_init (&data.mutex);
-  g_cond_init (&data.cond);
-  g_mutex_lock (&data.mutex);
+  data.context = g_main_context_new ();
+  data.loop = g_main_loop_new (data.context, FALSE);
 
+  g_main_context_push_thread_default (data.context);
 
   g_mount_source_ask_password_async (source,
                                      message_string,
@@ -411,13 +407,7 @@ g_mount_source_ask_password (GMountSourc
                                      ask_reply_sync,
                                      &data);
 
-  while (data.result == NULL)
-    g_cond_wait (&data.cond, &data.mutex);
-  g_mutex_unlock (&data.mutex);
-
-  g_cond_clear (&data.cond);
-  g_mutex_clear (&data.mutex);
-
+  g_main_loop_run (data.loop);
 
   handled = g_mount_source_ask_password_finish (source,
                                                 data.result,
@@ -427,6 +417,10 @@ g_mount_source_ask_password (GMountSourc
                                                 domain_out,
 						anonymous_out,
 						password_save_out);
+
+  g_main_context_pop_thread_default (data.context);
+  g_main_context_unref (data.context);
+  g_main_loop_unref (data.loop);
   g_object_unref (data.result);
 
   return handled;
@@ -563,10 +557,10 @@ g_mount_source_ask_question (GMountSourc
   gboolean handled, aborted;
   AskSyncData data;
   
-  memset (&data, 0, sizeof (data));
-  g_mutex_init (&data.mutex);
-  g_cond_init (&data.cond);
-  g_mutex_lock (&data.mutex);
+  data.context = g_main_context_new ();
+  data.loop = g_main_loop_new (data.context, FALSE);
+
+  g_main_context_push_thread_default (data.context);
 
   g_mount_source_ask_question_async (source,
                                      message,
@@ -574,18 +568,16 @@ g_mount_source_ask_question (GMountSourc
                                      ask_reply_sync,
                                      &data);
 
-  while (data.result == NULL)
-    g_cond_wait (&data.cond, &data.mutex);
-  g_mutex_unlock (&data.mutex);
-
-  g_cond_clear (&data.cond);
-  g_mutex_clear (&data.mutex);
+  g_main_loop_run (data.loop);
 
   handled = g_mount_source_ask_question_finish (source,
                                                 data.result,
                                                 &aborted,
                                                 &choice);
   
+  g_main_context_pop_thread_default (data.context);
+  g_main_context_unref (data.context);
+  g_main_loop_unref (data.loop);
   g_object_unref (data.result);
 
   if (aborted_out)
@@ -833,10 +825,10 @@ g_mount_source_show_processes (GMountSou
   gboolean handled, aborted;
   AskSyncData data;
 
-  memset (&data, 0, sizeof (data));
-  g_mutex_init (&data.mutex);
-  g_cond_init (&data.cond);
-  g_mutex_lock (&data.mutex);
+  data.context = g_main_context_new ();
+  data.loop = g_main_loop_new (data.context, FALSE);
+
+  g_main_context_push_thread_default (data.context);
 
   g_mount_source_show_processes_async (source,
                                        message,
@@ -845,18 +837,16 @@ g_mount_source_show_processes (GMountSou
                                        ask_reply_sync,
                                        &data);
 
-  while (data.result == NULL)
-    g_cond_wait (&data.cond, &data.mutex);
-  g_mutex_unlock (&data.mutex);
-
-  g_cond_clear (&data.cond);
-  g_mutex_clear (&data.mutex);
+  g_main_loop_run (data.loop);
 
   handled = g_mount_source_show_processes_finish (source,
                                                   data.result,
                                                   &aborted,
                                                   &choice);
 
+  g_main_context_pop_thread_default (data.context);
+  g_main_context_unref (data.context);
+  g_main_loop_unref (data.loop);
   g_object_unref (data.result);
 
   if (aborted_out)
diff -Naurp gvfs-1.40.1.orig/daemon/gvfsafpconnection.c gvfs-1.40.1/daemon/gvfsafpconnection.c
--- gvfs-1.40.1.orig/daemon/gvfsafpconnection.c	2019-04-09 01:36:07.000000000 -0500
+++ gvfs-1.40.1/daemon/gvfsafpconnection.c	2019-06-29 13:28:17.228547773 -0500
@@ -515,6 +515,12 @@ g_vfs_afp_command_put_pascal (GVfsAfpCom
 {
   size_t len;
 
+  if (str == NULL)
+  {
+     g_vfs_afp_command_put_byte (comm, 0);
+     return;
+  }
+
   len = MIN (strlen (str), 256);
 
   g_vfs_afp_command_put_byte (comm, len);
diff -Naurp gvfs-1.40.1.orig/daemon/gvfsbackendadmin.c gvfs-1.40.1/daemon/gvfsbackendadmin.c
--- gvfs-1.40.1.orig/daemon/gvfsbackendadmin.c	2019-04-09 01:36:07.000000000 -0500
+++ gvfs-1.40.1/daemon/gvfsbackendadmin.c	2019-06-29 14:15:39.601315566 -0500
@@ -42,6 +42,8 @@
 #include "gvfsjobopenforwrite.h"
 #include "gvfsjobqueryattributes.h"
 #include "gvfsjobqueryinfo.h"
+#include "gvfsjobqueryinforead.h"
+#include "gvfsjobqueryinfowrite.h"
 #include "gvfsjobread.h"
 #include "gvfsjobseekread.h"
 #include "gvfsjobseekwrite.h"
@@ -180,19 +182,6 @@ do_query_info (GVfsBackend *backend,
   if (error != NULL)
     goto out;
 
-  /* Override read/write flags, since the above call will use access()
-   * to determine permissions, which does not honor our privileged
-   * capabilities.
-   */
-  g_file_info_set_attribute_boolean (real_info,
-                                     G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
-  g_file_info_set_attribute_boolean (real_info,
-                                     G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, TRUE);
-  g_file_info_set_attribute_boolean (real_info,
-                                     G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, TRUE);
-  g_file_info_set_attribute_boolean (real_info,
-                                     G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, TRUE);
-
   g_file_info_copy_into (real_info, info);
   g_object_unref (real_info);
 
@@ -201,6 +190,55 @@ do_query_info (GVfsBackend *backend,
 }
 
 static void
+do_query_info_on_read (GVfsBackend *backend,
+                       GVfsJobQueryInfoRead *query_info_job,
+                       GVfsBackendHandle handle,
+                       GFileInfo *info,
+                       GFileAttributeMatcher *matcher)
+{
+   GVfsJob *job = G_VFS_JOB (query_info_job);
+   GFileInputStream *stream = handle;
+   GError *error = NULL;
+   GFileInfo *real_info;
+
+   real_info = g_file_input_stream_query_info (stream, query_info_job->attributes,
+                                               job->cancellable, &error);
+
+   if (error != NULL)
+      goto out;
+
+   g_file_info_copy_into (real_info, info);
+   g_object_unref (real_info);
+
+out:
+   complete_job (job, error);
+}
+static void
+do_query_info_on_write (GVfsBackend *backend,
+                        GVfsJobQueryInfoWrite *query_info_job,
+                        GVfsBackendHandle handle,
+                        GFileInfo *info,
+                        GFileAttributeMatcher *matcher)
+{
+   GVfsJob *job = G_VFS_JOB (query_info_job);
+   GFileOutputStream *stream = handle;
+   GError *error = NULL;
+   GFileInfo *real_info;
+
+   real_info = g_file_output_stream_query_info (stream, query_info_job->attributes,
+                                                job->cancellable, &error);
+
+   if (error != NULL)
+      goto out;
+
+   g_file_info_copy_into (real_info, info);
+   g_object_unref (real_info);
+
+out:
+   complete_job (job, error);
+}
+
+static void
 do_close_write (GVfsBackend *backend,
                 GVfsJobCloseWrite *close_write_job,
                 GVfsBackendHandle handle)
@@ -771,6 +809,51 @@ do_move (GVfsBackend *backend,
 }
 
 static void
+do_pull (GVfsBackend *backend,
+         GVfsJobPull *pull_job,
+         const char *source,
+         const char *local_path,
+         GFileCopyFlags flags,
+         gboolean remove_source,
+         GFileProgressCallback progress_callback,
+         gpointer progress_callback_data)
+{
+   GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+   GVfsJob *job = G_VFS_JOB (pull_job);
+   GError *error = NULL;
+   GFile *src_file, *dst_file;
+
+   /* Pull method is necessary when user/group needs to be restored, return
+    * G_IO_ERROR_NOT_SUPPORTED in other cases to proceed with the fallback code.
+    */
+   if (!(flags & G_FILE_COPY_ALL_METADATA))
+   {
+      g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR,
+                                G_IO_ERROR_NOT_SUPPORTED,
+                                _("Operation not supported"));
+      return;
+   }
+
+   if (!check_permission (self, job))
+      return;
+
+   src_file = g_file_new_for_path (source);
+   dst_file = g_file_new_for_path (local_path);
+
+   if (remove_source)
+      g_file_move (src_file, dst_file, flags, job->cancellable,
+                   progress_callback, progress_callback_data, &error);
+   else
+      g_file_copy (src_file, dst_file, flags, job->cancellable,
+                   progress_callback, progress_callback_data, &error);
+
+   g_object_unref (src_file);
+   g_object_unref (dst_file);
+
+   complete_job (job, error);
+}
+
+static void
 do_query_settable_attributes (GVfsBackend *backend,
                               GVfsJobQueryAttributes *query_job,
                               const char *filename)
@@ -868,6 +951,8 @@ g_vfs_backend_admin_class_init (GVfsBack
   backend_class->mount = do_mount;
   backend_class->open_for_read = do_open_for_read;
   backend_class->query_info = do_query_info;
+  backend_class->query_info_on_read = do_query_info_on_read;
+  backend_class->query_info_on_write = do_query_info_on_write;
   backend_class->read = do_read;
   backend_class->create = do_create;
   backend_class->append_to = do_append_to;
@@ -888,6 +973,7 @@ g_vfs_backend_admin_class_init (GVfsBack
   backend_class->set_attribute = do_set_attribute;
   backend_class->delete = do_delete;
   backend_class->move = do_move;
+  backend_class->pull = do_pull;
   backend_class->query_settable_attributes = do_query_settable_attributes;
   backend_class->query_writable_namespaces = do_query_writable_namespaces;
 }
@@ -913,7 +999,8 @@ g_vfs_backend_admin_init (GVfsBackendAdm
 
 #define REQUIRED_CAPS (CAP_TO_MASK(CAP_FOWNER) | \
                        CAP_TO_MASK(CAP_DAC_OVERRIDE) | \
-                       CAP_TO_MASK(CAP_DAC_READ_SEARCH))
+                       CAP_TO_MASK(CAP_DAC_READ_SEARCH) | \
+                       CAP_TO_MASK(CAP_CHOWN))
 
 static void
 acquire_caps (uid_t uid)
@@ -921,13 +1008,14 @@ acquire_caps (uid_t uid)
   struct __user_cap_header_struct hdr;
   struct __user_cap_data_struct data;
 
-  /* Tell kernel not clear capabilities when dropping root */
-  if (prctl (PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0)
-    g_error ("prctl(PR_SET_KEEPCAPS) failed");
-
-  /* Drop root uid, but retain the required permitted caps */
-  if (setuid (uid) < 0)
-    g_error ("unable to drop privs");
+  /* Set EUID to user to make dbus work */
+  if (seteuid (uid) < 0)
+     g_error ("Unable to drop privileges");
+
+  /* Set fsuid to still behave like root when working with files */
+  setfsuid (0);
+  if (setfsuid (-1) != 0)
+     g_error ("setfsuid failed");
 
   memset (&hdr, 0, sizeof(hdr));
   hdr.version = _LINUX_CAPABILITY_VERSION;
diff -Naurp gvfs-1.40.1.orig/daemon/gvfsbackendafc.c gvfs-1.40.1/daemon/gvfsbackendafc.c
--- gvfs-1.40.1.orig/daemon/gvfsbackendafc.c	2019-04-09 01:36:07.000000000 -0500
+++ gvfs-1.40.1/daemon/gvfsbackendafc.c	2019-06-29 13:40:12.774668154 -0500
@@ -9,7 +9,6 @@
 
 #include <limits.h>
 #include <stdint.h>
-#include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -73,7 +72,7 @@ typedef struct {
 struct _GVfsBackendAfc {
   GVfsBackend backend;
 
-  char uuid[41];
+  char *uuid;
   char *service;
   char *model;
   gboolean connected;
@@ -472,16 +471,11 @@ g_vfs_backend_afc_mount (GVfsBackend *ba
     {
       g_vfs_job_failed (G_VFS_JOB (job),
                         G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
-                        _("Invalid mount spec"));
-      return;
-    }
-  if (G_UNLIKELY(sscanf(str, "%40s", (char *) &self->uuid) < 1))
-    {
-      g_vfs_job_failed (G_VFS_JOB(job), G_IO_ERROR, G_IO_ERROR_FAILED,
                         _("Invalid AFC location: must be in the form of "
                           "afc://uuid:port-number"));
       return;
     }
+  self->uuid = g_strdup (str);
 
   str = g_mount_spec_get (spec, "port");
   if (str == NULL)
@@ -519,9 +513,7 @@ g_vfs_backend_afc_mount (GVfsBackend *ba
   display_name = NULL;
 
   real_spec = g_mount_spec_new ("afc");
-  tmp = g_strdup_printf ("%40s", (char *) &self->uuid);
-  g_mount_spec_set (real_spec, "host", tmp);
-  g_free (tmp);
+  g_mount_spec_set (real_spec, "host", self->uuid);
 
   /* INFO: Don't ever set the DefaultPort again or everything goes crazy */
   if (virtual_port != VIRTUAL_PORT_AFC)
@@ -2752,6 +2744,8 @@ g_vfs_backend_afc_finalize (GObject *obj
       self->force_umount_id = 0;
   }
 
+  g_free (self->uuid);
+
   if (G_OBJECT_CLASS(g_vfs_backend_afc_parent_class)->finalize)
     (*G_OBJECT_CLASS(g_vfs_backend_afc_parent_class)->finalize) (obj);
 }
diff -Naurp gvfs-1.40.1.orig/daemon/gvfsdaemon.c gvfs-1.40.1/daemon/gvfsdaemon.c
--- gvfs-1.40.1.orig/daemon/gvfsdaemon.c	2019-04-09 01:36:07.000000000 -0500
+++ gvfs-1.40.1/daemon/gvfsdaemon.c	2019-06-29 14:13:55.078149180 -0500
@@ -79,6 +79,7 @@ struct _GVfsDaemon
   
   gint mount_counter;
   
+  GDBusAuthObserver *auth_observer;
   GDBusConnection *conn;
   GVfsDBusDaemon *daemon_skeleton;
   GVfsDBusMountable *mountable_skeleton;
@@ -171,7 +172,9 @@ g_vfs_daemon_finalize (GObject *object)
     }
   if (daemon->conn != NULL)
     g_object_unref (daemon->conn);
-  
+  if (daemon->auth_observer != NULL)
+    g_object_unref (daemon->auth_observer);
+
   g_hash_table_destroy (daemon->registered_paths);
   g_hash_table_destroy (daemon->client_connections);
   g_mutex_clear (&daemon->lock);
@@ -236,6 +239,51 @@ name_vanished_handler (GDBusConnection *
   daemon->lost_main_daemon = TRUE;
 }
 
+/*
+ * Authentication observer signal handler that rejects all authentication
+ * mechanisms except for EXTERNAL (credentials-passing), which is the
+ * recommended authentication mechanism for all AF_UNIX sockets.
+ */
+static gboolean
+allow_mechanism_cb (GDBusAuthObserver *observer,
+                    const gchar *mechanism,
+                    G_GNUC_UNUSED gpointer user_data)
+{
+   if (g_strcmp0 (mechanism, "EXTERNAL") == 0)
+      return TRUE;
+
+   return FALSE;
+}
+
+/*
+ * Authentication observer signal handler that authorizes connections
+ * from the same uid as this process. This matches the behavior of a 
+ * libdbus DBusServer/DBusConnection when no DBusAllowUnixUserFunction
+ * has been set, but is not the default in GDBus.
+ */
+static gboolean
+authorize_authenticated_peer_cb (GDBusAuthObserver *observer,
+                                 G_GNUC_UNUSED GIOStream *stream,
+                                 GCredentials *credentials,
+                                 G_GNUC_UNUSED gpointer user_data)
+{
+   gboolean authorized = FALSE;
+
+   if (credentials != NULL)
+   {
+      GCredentials *own_credentials;
+
+      own_credentials = g_credentials_new () ;
+
+      if (g_credentials_is_same_user (credentials, own_credentials, NULL))
+         authorized = TRUE;
+
+      g_object_unref (own_credentials);
+   }
+
+   return authorized;
+}
+
 static void
 g_vfs_daemon_init (GVfsDaemon *daemon)
 {
@@ -265,6 +313,9 @@ g_vfs_daemon_init (GVfsDaemon *daemon)
 
   daemon->conn = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
   g_assert (daemon->conn != NULL);
+  daemon->auth_observer = g_dbus_auth_observer_new ();
+  g_signal_connect (daemon->auth_observer, "allow-mechanism", G_CALLBACK (allow_mechanism_cb), NULL);
+  g_signal_connect (daemon->auth_observer, "authorized-authenticated-peer", G_CALLBACK (authorize_authenticated_peer_cb), NULL);
 
   daemon->daemon_skeleton = gvfs_dbus_daemon_skeleton_new ();
   g_signal_connect (daemon->daemon_skeleton, "handle-get-connection", G_CALLBACK (handle_get_connection), daemon);
@@ -876,7 +927,7 @@ handle_get_connection (GVfsDBusDaemon *o
   server = g_dbus_server_new_sync (address1,
                                    G_DBUS_SERVER_FLAGS_NONE,
                                    guid,
-                                   NULL, /* GDBusAuthObserver */
+                                   daemon->auth_observer,
                                    NULL, /* GCancellable */
                                    &error);
   g_free (guid);
