Author: renodr Date: Sat Jun 29 12:32:58 2019 New Revision: 3945 Log: Add GVFS Security Patch
Added: trunk/gvfs/gvfs-1.40.1-fix_security_vulnerabilities-1.patch Added: trunk/gvfs/gvfs-1.40.1-fix_security_vulnerabilities-1.patch ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ trunk/gvfs/gvfs-1.40.1-fix_security_vulnerabilities-1.patch Sat Jun 29 12:32:58 2019 (r3945) @@ -0,0 +1,511 @@ +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); -- http://lists.linuxfromscratch.org/listinfo/patches FAQ: http://www.linuxfromscratch.org/blfs/faq.html Unsubscribe: See the above information page