[gvfs/wip/cosimoc/admin: 4/17] Introduce an admin gvfs backend
- From: Cosimo Cecchi <cosimoc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gvfs/wip/cosimoc/admin: 4/17] Introduce an admin gvfs backend
- Date: Mon, 11 Jul 2016 14:52:30 +0000 (UTC)
commit 794c49b544e9a93df8e4846f355c2098987cb73b
Author: Cosimo Cecchi <cosimoc gnome org>
Date: Sat Oct 10 15:03:02 2015 -0400
Introduce an admin gvfs backend
The new admin backend is activated through pkexec and allows
applications to promote their I/O operations to be privileged.
Privilege checking is achieved through polkit.
configure.ac | 18 +
daemon/Makefile.am | 35 +-
daemon/admin.mount.in | 4 +
daemon/daemon-main-generic.c | 3 +
daemon/gvfsbackendadmin.c | 1060 +++++++++++++++++++++++
daemon/gvfsbackendadmin.h | 61 ++
daemon/org.gtk.vfs.file-operations.policy.in.in | 32 +
daemon/org.gtk.vfs.file-operations.rules | 8 +
8 files changed, 1220 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 0c43829..f226712 100644
--- a/configure.ac
+++ b/configure.ac
@@ -145,6 +145,24 @@ fi
AC_SEARCH_LIBS([login_tty], [util], [AC_DEFINE([HAVE_LOGIN_TTY],[],[Whether login_tty is available])])
+dnl ***************************************************
+dnl *** Check if we should build with admin backend ***
+dnl ***************************************************
+AC_ARG_ENABLE([admin], [AS_HELP_STRING([--disable-admin],[build without admin backend])])
+msg_admin=no
+
+if test "x$enable_admin" != "xno"; then
+ PKG_CHECK_MODULES([ADMIN], [polkit-gobject-1], [msg_admin=yes])
+
+ if test "x$msg_admin" = "xyes"; then
+ AC_DEFINE([HAVE_ADMIN], 1, [Define to 1 if admin backend is going to be built])
+ fi
+fi
+
+AC_SUBST(ADMIN_CFLAGS)
+AC_SUBST(ADMIN_LIBS)
+AM_CONDITIONAL([HAVE_ADMIN], [test "$msg_admin" = "yes"])
+
dnl **************************************************
dnl *** Check if we should build with http backend ***
dnl **************************************************
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 08ddec7..22f0929 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -55,6 +55,12 @@ libexec_PROGRAMS=gvfsd gvfsd-sftp gvfsd-trash gvfsd-computer gvfsd-burn gvfsd-lo
mount_in_files = sftp.mount.in ftp.mount.in ftps.mount.in trash.mount.in computer.mount.in burn.mount.in
localtest.mount.in network.mount.in
mount_DATA = sftp.mount ftp.mount ftps.mount trash.mount computer.mount burn.mount localtest.mount
network.mount
+mount_in_files += admin.mount.in
+if HAVE_ADMIN
+mount_DATA += admin.mount
+libexec_PROGRAMS += gvfsd-admin
+endif
+
mount_in_files +=google.mount.in
if USE_GOOGLE
mount_DATA += google.mount
@@ -147,6 +153,7 @@ EXTRA_DIST = \
$(gvfs_gschemas_dist) \
$(gvfs_gschemas_convert_dist) \
$(gsettings_ENUM_FILES) \
+ org.gtk.vfs.file-operations.policy.in.in \
$(NULL)
DISTCLEANFILES = $(mount_DATA) $(noinst_DATA)
@@ -155,6 +162,8 @@ CLEANFILES = \
$(gsettings__enum_file) \
$(service_DATA) \
$(systemd_user_DATA) \
+ $(gvfs_polkit_actions_DATA) \
+ $(gvfs_polkit_actions_in_files) \
*.gschema.valid
noinst_PROGRAMS = \
@@ -449,6 +458,20 @@ gvfsd_cdda_LDADD = $(libraries) $(CDDA_LIBS) $(HAL_LIBS) \
$(top_builddir)/common/libgvfscommon-hal.la
endif
+gvfsd_admin_SOURCES = \
+ gvfsbackendadmin.c gvfsbackendadmin.h \
+ daemon-main.c daemon-main.h \
+ daemon-main-generic.c
+
+gvfsd_admin_CPPFLAGS = \
+ $(flags) \
+ -DBACKEND_HEADER=gvfsbackendadmin.h \
+ -DDEFAULT_BACKEND_TYPE=admin \
+ -DBACKEND_TYPES='"admin", G_VFS_TYPE_BACKEND_ADMIN,' \
+ $(ADMIN_CFLAGS)
+
+gvfsd_admin_LDADD = $(libraries) $(ADMIN_LIBS)
+
gvfsd_google_SOURCES = \
gvfsbackendgoogle.c gvfsbackendgoogle.h \
daemon-main.c daemon-main.h \
@@ -628,7 +651,6 @@ gvfsd_nfs_CPPFLAGS = \
gvfsd_nfs_LDADD = $(libraries) $(NFS_LIBS)
-
# GSettings stuff
gsettings_ENUM_NAMESPACE = org.gnome.system.gvfs
gsettings_ENUM_FILES = $(top_srcdir)/daemon/gvfs-enums.h
@@ -639,3 +661,14 @@ gsettings_SCHEMAS = $(gvfs_gschemas)
gvfs_gschemas_convertdir = $(datadir)/GConf/gsettings
gvfs_gschemas_convert_DATA = $(gvfs_gschemas_convert)
+
+org.gtk.vfs.file-operations.policy.in: org.gtk.vfs.file-operations.policy.in.in Makefile
+ $(AM_V_GEN) sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
+
+@INTLTOOL_POLICY_RULE@
+gvfs_polkit_actionsdir = $(datadir)/polkit-1/actions
+gvfs_polkit_actions_in_files = org.gtk.vfs.file-operations.policy.in
+gvfs_polkit_actions_DATA = org.gtk.vfs.file-operations.policy
+
+gvfs_polkit_rulesdir = $(datadir)/polkit-1/rules.d
+dist_gvfs_polkit_rules_DATA = org.gtk.vfs.file-operations.rules
diff --git a/daemon/admin.mount.in b/daemon/admin.mount.in
new file mode 100644
index 0000000..0996a9e
--- /dev/null
+++ b/daemon/admin.mount.in
@@ -0,0 +1,4 @@
+[Mount]
+Type=admin
+Exec=/bin/sh -c 'pkexec @libexecdir@/gvfsd-admin --address "$@" $DBUS_SESSION_BUS_ADDRESS'
+AutoMount=true
diff --git a/daemon/daemon-main-generic.c b/daemon/daemon-main-generic.c
index dddfcff..80dbfcb 100644
--- a/daemon/daemon-main-generic.c
+++ b/daemon/daemon-main-generic.c
@@ -33,6 +33,9 @@ main (int argc, char *argv[])
#ifndef BACKEND_USES_GVFS
g_setenv ("GIO_USE_VFS", "local", TRUE);
#endif
+#ifdef BACKEND_PRE_SETUP_FUNC
+ BACKEND_PRE_SETUP_FUNC (&argc, &argv);
+#endif
daemon_init ();
#ifdef BACKEND_SETUP_FUNC
BACKEND_SETUP_FUNC ();
diff --git a/daemon/gvfsbackendadmin.c b/daemon/gvfsbackendadmin.c
new file mode 100644
index 0000000..c37316d
--- /dev/null
+++ b/daemon/gvfsbackendadmin.c
@@ -0,0 +1,1060 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2015 Cosimo Cecchi <cosimoc gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/fsuid.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <polkit/polkit.h>
+
+#include "gvfsbackendadmin.h"
+#include "gvfsjobcreatemonitor.h"
+#include "gvfsjobenumerate.h"
+#include "gvfsjobopenforread.h"
+#include "gvfsjobopenforwrite.h"
+#include "gvfsjobqueryattributes.h"
+#include "gvfsjobqueryinfo.h"
+#include "gvfsjobread.h"
+#include "gvfsjobseekread.h"
+#include "gvfsjobseekwrite.h"
+#include "gvfsjobsetdisplayname.h"
+#include "gvfsjobwrite.h"
+#include "gvfsmonitor.h"
+
+struct _GVfsBackendAdmin
+{
+ GVfsBackend parent_instance;
+
+ GMutex polkit_mutex;
+ PolkitAuthority *authority;
+};
+
+struct _GVfsBackendAdminClass
+{
+ GVfsBackendClass parent_class;
+};
+
+G_DEFINE_TYPE(GVfsBackendAdmin, g_vfs_backend_admin, G_VFS_TYPE_BACKEND)
+
+static void
+do_finalize (GObject *object)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (object);
+
+ g_clear_object (&self->authority);
+ g_mutex_clear (&self->polkit_mutex);
+
+ G_OBJECT_CLASS (g_vfs_backend_admin_parent_class)->finalize (object);
+}
+
+static gboolean
+check_permission (GVfsBackendAdmin *self,
+ GVfsJob *job)
+{
+ GVfsJobDBus *dbus_job = G_VFS_JOB_DBUS (job);
+ GError *error = NULL;
+
+ GDBusMethodInvocation *invocation = dbus_job->invocation;
+ GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation);
+ GCredentials *credentials = g_dbus_connection_get_peer_credentials (connection);
+
+ pid_t pid = g_credentials_get_unix_pid (credentials, &error);
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ uid_t uid = g_credentials_get_unix_user (credentials, &error);
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ /* Only one polkit dialog at a time */
+ g_mutex_lock (&self->polkit_mutex);
+
+ PolkitSubject *subject =
+ polkit_unix_process_new_for_owner (pid, 0, uid);
+ PolkitAuthorizationResult *result = polkit_authority_check_authorization_sync
+ (self->authority, subject,
+ "org.gtk.vfs.file-operations",
+ NULL, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
+ NULL, &error);
+ g_object_unref (subject);
+
+ g_mutex_unlock (&self->polkit_mutex);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ gboolean is_authorized =
+ polkit_authorization_result_get_is_authorized (result) ||
+ polkit_authorization_result_get_is_challenge (result);
+
+ g_object_unref (result);
+
+ if (!is_authorized)
+ g_vfs_job_failed_literal (job, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+ _("Permission denied"));
+
+ return is_authorized;
+}
+
+static void
+do_query_info (GVfsBackend *backend,
+ GVfsJobQueryInfo *query_info_job,
+ const char *filename,
+ GFileQueryInfoFlags flags,
+ GFileInfo *info,
+ GFileAttributeMatcher *matcher)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (query_info_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GError *error = NULL;
+ GFile *file = g_file_new_for_path (filename);
+ GFileInfo *real_info = g_file_query_info (file, query_info_job->attributes,
+ flags, job->cancellable,
+ &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ /* 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_copy_into (real_info, info);
+ g_object_unref (real_info);
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_close_write (GVfsBackend *backend,
+ GVfsJobCloseWrite *close_write_job,
+ GVfsBackendHandle handle)
+{
+ GVfsJob *job = G_VFS_JOB (close_write_job);
+ GOutputStream *stream = handle;
+
+ GError *error = NULL;
+ g_output_stream_close (stream, job->cancellable, &error);
+ g_object_unref (stream);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_write (GVfsBackend *backend,
+ GVfsJobWrite *write_job,
+ GVfsBackendHandle handle,
+ char *buffer,
+ gsize buffer_size)
+{
+ GVfsJob *job = G_VFS_JOB (write_job);
+ GOutputStream *stream = handle;
+
+ GError *error = NULL;
+ gssize bytes_written = g_output_stream_write (stream, buffer, buffer_size,
+ job->cancellable, &error);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_write_set_written_size (write_job, bytes_written);
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_append_to (GVfsBackend *backend,
+ GVfsJobOpenForWrite *open_write_job,
+ const char *filename,
+ GFileCreateFlags flags)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (open_write_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GError *error = NULL;
+ GFile *file = g_file_new_for_path (filename);
+ GFileOutputStream *stream = g_file_append_to (file, flags,
+ job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ GSeekable *seekable = G_SEEKABLE (stream);
+
+ /* Seek to the end of the file */
+ g_seekable_seek (seekable, 0, G_SEEK_END,
+ job->cancellable, &error);
+ if (error != NULL)
+ {
+ g_object_unref (stream);
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_open_for_write_set_handle (open_write_job, stream);
+ g_vfs_job_open_for_write_set_can_seek
+ (open_write_job, g_seekable_can_seek (seekable));
+ g_vfs_job_open_for_write_set_can_truncate
+ (open_write_job, g_seekable_can_truncate (seekable));
+ g_vfs_job_open_for_write_set_initial_offset
+ (open_write_job, g_seekable_tell (seekable));
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_create (GVfsBackend *backend,
+ GVfsJobOpenForWrite *open_write_job,
+ const char *filename,
+ GFileCreateFlags flags)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (open_write_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GError *error = NULL;
+ GFile *file = g_file_new_for_path (filename);
+ GFileOutputStream *stream = g_file_create (file, flags,
+ job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ GSeekable *seekable = G_SEEKABLE (stream);
+
+ g_vfs_job_open_for_write_set_handle (open_write_job, stream);
+ g_vfs_job_open_for_write_set_can_seek
+ (open_write_job, g_seekable_can_seek (seekable));
+ g_vfs_job_open_for_write_set_can_truncate
+ (open_write_job, g_seekable_can_truncate (seekable));
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_replace (GVfsBackend *backend,
+ GVfsJobOpenForWrite *open_write_job,
+ const char *filename,
+ const char *etag,
+ gboolean make_backup,
+ GFileCreateFlags flags)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (open_write_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GError *error = NULL;
+ GFile *file = g_file_new_for_path (filename);
+ GFileOutputStream *stream = g_file_replace (file, etag, make_backup, flags,
+ job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ GSeekable *seekable = G_SEEKABLE (stream);
+
+ g_vfs_job_open_for_write_set_handle (open_write_job, stream);
+ g_vfs_job_open_for_write_set_can_seek
+ (open_write_job, g_seekable_can_seek (seekable));
+ g_vfs_job_open_for_write_set_can_truncate
+ (open_write_job, g_seekable_can_truncate (seekable));
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_close_read (GVfsBackend *backend,
+ GVfsJobCloseRead *close_read_job,
+ GVfsBackendHandle handle)
+{
+ GVfsJob *job = G_VFS_JOB (close_read_job);
+ GInputStream *stream = handle;
+ GError *error = NULL;
+
+ g_input_stream_close (stream, job->cancellable, &error);
+ g_object_unref (stream);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_read (GVfsBackend *backend,
+ GVfsJobRead *read_job,
+ GVfsBackendHandle handle,
+ char *buffer,
+ gsize bytes_requested)
+{
+ GVfsJob *job = G_VFS_JOB (read_job);
+ GError *error = NULL;
+ GInputStream *stream = handle;
+
+ gssize bytes = g_input_stream_read (stream, buffer, bytes_requested,
+ job->cancellable, &error);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_read_set_size (read_job, bytes);
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_open_for_read (GVfsBackend *backend,
+ GVfsJobOpenForRead *open_read_job,
+ const char *filename)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (open_read_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GFile *file = g_file_new_for_path (filename);
+ GError *error = NULL;
+ GFileInputStream *stream = g_file_read (file, job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_open_for_read_set_handle (open_read_job, stream);
+ g_vfs_job_open_for_read_set_can_seek
+ (open_read_job,
+ g_seekable_can_seek (G_SEEKABLE (stream)));
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_truncate (GVfsBackend *backend,
+ GVfsJobTruncate *truncate_job,
+ GVfsBackendHandle handle,
+ goffset size)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (truncate_job);
+ GSeekable *seekable = handle;
+
+ GError *error = NULL;
+ g_seekable_truncate (seekable, size, job->cancellable, &error);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_seek_on_read (GVfsBackend *backend,
+ GVfsJobSeekRead *seek_read_job,
+ GVfsBackendHandle handle,
+ goffset offset,
+ GSeekType type)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (seek_read_job);
+ GSeekable *seekable = handle;
+
+ GError *error = NULL;
+ g_seekable_seek (seekable, offset, type,
+ job->cancellable, &error);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_seek_read_set_offset (seek_read_job, g_seekable_tell (seekable));
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_seek_on_write (GVfsBackend *backend,
+ GVfsJobSeekWrite *seek_write_job,
+ GVfsBackendHandle handle,
+ goffset offset,
+ GSeekType type)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (seek_write_job);
+ GSeekable *seekable = handle;
+
+ GError *error = NULL;
+ g_seekable_seek (seekable, offset, type,
+ job->cancellable, &error);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_seek_write_set_offset (seek_write_job, g_seekable_tell (seekable));
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_enumerate (GVfsBackend *backend,
+ GVfsJobEnumerate *enumerate_job,
+ const char *filename,
+ GFileAttributeMatcher *attribute_matcher,
+ GFileQueryInfoFlags flags)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (enumerate_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GFile *file = g_file_new_for_path (filename);
+ GError *error = NULL;
+ GFileEnumerator *enumerator =
+ g_file_enumerate_children (file, enumerate_job->attributes, flags,
+ job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ while (TRUE)
+ {
+ GFileInfo *info;
+ if (!g_file_enumerator_iterate (enumerator, &info, NULL,
+ job->cancellable, &error))
+ {
+ g_object_unref (enumerator);
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ if (!info)
+ break;
+
+ g_vfs_job_enumerate_add_info (enumerate_job, g_object_ref (info));
+ }
+
+ g_file_enumerator_close (enumerator, job->cancellable, &error);
+ g_object_unref (enumerator);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_succeeded (job);
+ g_vfs_job_enumerate_done (enumerate_job);
+}
+
+static void
+do_make_directory (GVfsBackend *backend,
+ GVfsJobMakeDirectory *mkdir_job,
+ const char *filename)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (mkdir_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GError *error = NULL;
+ GFile *file = g_file_new_for_path (filename);
+
+ g_file_make_directory (file, job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_make_symlink (GVfsBackend *backend,
+ GVfsJobMakeSymlink *symlink_job,
+ const char *filename,
+ const char *symlink_value)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (symlink_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GError *error = NULL;
+ GFile *file = g_file_new_for_path (filename);
+ g_file_make_symbolic_link (file, symlink_value, job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_query_fs_info (GVfsBackend *backend,
+ GVfsJobQueryFsInfo *query_info_job,
+ const char *filename,
+ GFileInfo *info,
+ GFileAttributeMatcher *attribute_matcher)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (query_info_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GError *error = NULL;
+ GFile *file = g_file_new_for_path (filename);
+ char *attributes = g_file_attribute_matcher_to_string (attribute_matcher);
+ GFileInfo *real_info = g_file_query_filesystem_info
+ (file, attributes,
+ job->cancellable, &error);
+ g_object_unref (file);
+ g_free (attributes);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_file_info_copy_into (real_info, info);
+ g_object_unref (real_info);
+ g_vfs_job_succeeded (job);
+}
+
+static void
+monitor_changed (GFileMonitor* monitor,
+ GFile* file,
+ GFile* other_file,
+ GFileMonitorEvent event_type,
+ GVfsMonitor *vfs_monitor)
+{
+ char *file_path;
+ char *other_file_path;
+
+ file_path = g_file_get_path (file);
+ if (other_file)
+ other_file_path = g_file_get_path (other_file);
+ else
+ other_file_path = NULL;
+
+ g_vfs_monitor_emit_event (vfs_monitor,
+ event_type,
+ file_path,
+ other_file_path);
+
+ g_free (file_path);
+ g_free (other_file_path);
+}
+
+static void
+create_dir_file_monitor (GVfsBackend *backend,
+ GVfsJobCreateMonitor *monitor_job,
+ const char *filename,
+ GFileMonitorFlags flags,
+ gboolean is_dir_monitor)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (monitor_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GFile *file = g_file_new_for_path (filename);
+ GError *error = NULL;
+ GFileMonitor *monitor;
+
+ if (is_dir_monitor)
+ monitor = g_file_monitor_directory (file, flags,
+ job->cancellable, &error);
+ else
+ monitor = g_file_monitor_file (file, flags,
+ job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ GVfsMonitor *vfs_monitor = g_vfs_monitor_new (backend);
+ g_signal_connect (monitor, "changed",
+ G_CALLBACK (monitor_changed), vfs_monitor);
+
+ g_object_set_data_full (G_OBJECT (vfs_monitor),
+ "real-monitor", monitor,
+ (GDestroyNotify) g_object_unref);
+
+ g_vfs_job_create_monitor_set_monitor (monitor_job, vfs_monitor);
+ g_object_unref (vfs_monitor);
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_create_dir_monitor (GVfsBackend *backend,
+ GVfsJobCreateMonitor *job,
+ const char *filename,
+ GFileMonitorFlags flags)
+{
+ create_dir_file_monitor (backend, job, filename, flags, TRUE);
+}
+
+
+static void
+do_create_file_monitor (GVfsBackend *backend,
+ GVfsJobCreateMonitor *job,
+ const char *filename,
+ GFileMonitorFlags flags)
+{
+ create_dir_file_monitor (backend, job, filename, flags, FALSE);
+}
+
+static void
+do_set_display_name (GVfsBackend *backend,
+ GVfsJobSetDisplayName *display_name_job,
+ const char *filename,
+ const char *display_name)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (display_name_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GFile *file = g_file_new_for_path (filename);
+ GError *error = NULL;
+ g_file_set_display_name (file, display_name,
+ job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ char *dirname, *new_path;
+ dirname = g_path_get_dirname (filename);
+ new_path = g_build_filename (dirname, display_name, NULL);
+
+ g_vfs_job_set_display_name_set_new_path (display_name_job, new_path);
+ g_vfs_job_succeeded (job);
+ g_free (dirname);
+ g_free (new_path);
+}
+
+static void
+do_set_attribute (GVfsBackend *backend,
+ GVfsJobSetAttribute *set_attribute_job,
+ const char *filename,
+ const char *attribute,
+ GFileAttributeType type,
+ gpointer value_p,
+ GFileQueryInfoFlags flags)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (set_attribute_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GFile *file = g_file_new_for_path (filename);
+ GError *error = NULL;
+ g_file_set_attribute (file, attribute, type, value_p, flags,
+ job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_delete (GVfsBackend *backend,
+ GVfsJobDelete *delete_job,
+ const char *filename)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (delete_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GFile *file = g_file_new_for_path (filename);
+ GError *error = NULL;
+ g_file_delete (file, job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_move (GVfsBackend *backend,
+ GVfsJobMove *move_job,
+ const char *source,
+ const char *destination,
+ GFileCopyFlags flags,
+ GFileProgressCallback progress_callback,
+ gpointer progress_callback_data)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (move_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GFile *src_file = g_file_new_for_path (source);
+ GFile *dst_file = g_file_new_for_path (destination);
+ GError *error = NULL;
+ g_file_move (src_file, dst_file, flags,
+ job->cancellable,
+ progress_callback, progress_callback_data,
+ &error);
+
+ g_object_unref (src_file);
+ g_object_unref (dst_file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_query_settable_attributes (GVfsBackend *backend,
+ GVfsJobQueryAttributes *query_job,
+ const char *filename)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (query_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GFile *file = g_file_new_for_path (filename);
+ GError *error = NULL;
+ GFileAttributeInfoList *attr_list =
+ g_file_query_settable_attributes (file, job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_query_attributes_set_list (query_job, attr_list);
+ g_file_attribute_info_list_unref (attr_list);
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_query_writable_namespaces (GVfsBackend *backend,
+ GVfsJobQueryAttributes *query_job,
+ const char *filename)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (query_job);
+
+ if (!check_permission (self, job))
+ return;
+
+ GFile *file = g_file_new_for_path (filename);
+ GError *error = NULL;
+ GFileAttributeInfoList *attr_list =
+ g_file_query_writable_namespaces (file, job->cancellable, &error);
+ g_object_unref (file);
+
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_query_attributes_set_list (query_job, attr_list);
+ g_file_attribute_info_list_unref (attr_list);
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+do_mount (GVfsBackend *backend,
+ GVfsJobMount *mount_job,
+ GMountSpec *mount_spec,
+ GMountSource *mount_source,
+ gboolean is_automount)
+{
+ GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
+ GVfsJob *job = G_VFS_JOB (mount_job);
+
+ GError *error = NULL;
+ self->authority = polkit_authority_get_sync (NULL, &error);
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_succeeded (job);
+}
+
+static void
+g_vfs_backend_admin_class_init (GVfsBackendAdminClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
+
+ object_class->finalize = do_finalize;
+
+ backend_class->mount = do_mount;
+ backend_class->open_for_read = do_open_for_read;
+ backend_class->query_info = do_query_info;
+ backend_class->read = do_read;
+ backend_class->create = do_create;
+ backend_class->append_to = do_append_to;
+ backend_class->replace = do_replace;
+ backend_class->write = do_write;
+ backend_class->close_read = do_close_read;
+ backend_class->close_write = do_close_write;
+ backend_class->seek_on_read = do_seek_on_read;
+ backend_class->seek_on_write = do_seek_on_write;
+ backend_class->enumerate = do_enumerate;
+ backend_class->truncate = do_truncate;
+ backend_class->make_directory = do_make_directory;
+ backend_class->make_symlink = do_make_symlink;
+ backend_class->query_fs_info = do_query_fs_info;
+ backend_class->create_dir_monitor = do_create_dir_monitor;
+ backend_class->set_display_name = do_set_display_name;
+ backend_class->set_attribute = do_set_attribute;
+ backend_class->delete = do_delete;
+ backend_class->move = do_move;
+ backend_class->query_settable_attributes = do_query_settable_attributes;
+ backend_class->query_writable_namespaces = do_query_writable_namespaces;
+}
+
+static void
+g_vfs_backend_admin_init (GVfsBackendAdmin *self)
+{
+ GVfsBackend *backend = G_VFS_BACKEND (self);
+ GMountSpec *mount_spec;
+
+ g_mutex_init (&self->polkit_mutex);
+ g_vfs_backend_set_user_visible (backend, FALSE);
+
+ mount_spec = g_mount_spec_new ("admin");
+ g_vfs_backend_set_mount_spec (backend, mount_spec);
+ g_mount_spec_unref (mount_spec);
+
+ g_vfs_backend_set_icon_name (backend, "folder");
+ g_vfs_backend_set_symbolic_icon_name (backend, "folder-symbolic");
+}
+
+#define REQUIRED_CAPS (CAP_TO_MASK(CAP_FOWNER) | \
+ CAP_TO_MASK(CAP_DAC_OVERRIDE) | \
+ CAP_TO_MASK(CAP_DAC_READ_SEARCH))
+
+static void
+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");
+
+ memset (&hdr, 0, sizeof(hdr));
+ hdr.version = _LINUX_CAPABILITY_VERSION;
+
+ /* Drop all non-require capabilities */
+ data.effective = REQUIRED_CAPS;
+ data.permitted = REQUIRED_CAPS;
+ data.inheritable = 0;
+ if (capset (&hdr, &data) < 0)
+ g_error ("capset failed");
+}
+
+static char *session_address = NULL;
+static GOptionEntry entries[] = {
+ { "address", 0, 0, G_OPTION_ARG_STRING, &session_address, "DBus session address", NULL },
+ { NULL }
+};
+
+void
+g_vfs_backend_admin_pre_setup (int *argc,
+ char **argv[])
+{
+ const char *pkexec_uid = g_getenv ("PKEXEC_UID");
+ if (pkexec_uid == NULL)
+ g_error ("gvfsd-admin must be executed under pkexec");
+
+ uid_t uid = strtol (pkexec_uid, NULL, 10);
+ if ((errno == ERANGE && (uid == LONG_MAX || uid == LONG_MIN))
+ || (errno != 0 && uid == 0))
+ g_error ("Unable to convert PKEXEC_UID string to uid_t");
+
+ GError *error = NULL;
+ GOptionContext *context = g_option_context_new (NULL);
+ g_option_context_set_ignore_unknown_options (context, TRUE);
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_parse (context, argc, argv, &error);
+
+ if (error != NULL)
+ g_error ("Can't parse arguments: %s", error->message);
+
+ acquire_caps (uid);
+ g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_address, TRUE);
+}
diff --git a/daemon/gvfsbackendadmin.h b/daemon/gvfsbackendadmin.h
new file mode 100644
index 0000000..9a70d30
--- /dev/null
+++ b/daemon/gvfsbackendadmin.h
@@ -0,0 +1,61 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2015 Cosimo Cecchi <cosimoc gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __G_VFS_BACKEND_ADMIN_H__
+#define __G_VFS_BACKEND_ADMIN_H__
+
+#include <gvfsbackend.h>
+
+G_BEGIN_DECLS
+
+#define BACKEND_PRE_SETUP_FUNC g_vfs_backend_admin_pre_setup
+#define G_VFS_TYPE_BACKEND_ADMIN (g_vfs_backend_admin_get_type())
+
+#define G_VFS_BACKEND_ADMIN(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST((o), \
+ G_VFS_TYPE_BACKEND_ADMIN, GVfsBackendAdmin))
+
+#define G_VFS_BACKEND_ADMIN_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST((k), \
+ G_VFS_TYPE_BACKEND_ADMIN, GVfsBackendAdminClass))
+
+#define G_VFS_IS_BACKEND_ADMIN(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((o), \
+ G_VFS_TYPE_BACKEND_ADMIN))
+
+#define G_VFS_IS_BACKEND_ADMIN_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE((k), \
+ G_VFS_TYPE_BACKEND_ADMIN))
+
+#define G_VFS_BACKEND_ADMIN_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS((o), \
+ G_VFS_TYPE_BACKEND_ADMIN, GVfsBackendAdminClass))
+
+typedef struct _GVfsBackendAdmin GVfsBackendAdmin;
+typedef struct _GVfsBackendAdminClass GVfsBackendAdminClass;
+
+GType g_vfs_backend_admin_get_type (void) G_GNUC_CONST;
+
+void g_vfs_backend_admin_pre_setup (int *argc, char **argv[]);
+
+G_END_DECLS
+
+#endif /* __G_VFS_BACKEND_ADMIN_H__ */
diff --git a/daemon/org.gtk.vfs.file-operations.policy.in.in b/daemon/org.gtk.vfs.file-operations.policy.in.in
new file mode 100644
index 0000000..a371a0d
--- /dev/null
+++ b/daemon/org.gtk.vfs.file-operations.policy.in.in
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+<policyconfig>
+
+ <vendor>GVfs</vendor>
+ <vendor_url>http://git.gnome.org/browse/gvfs</vendor_url>
+
+ <action id="org.gtk.vfs.file-operations-helper">
+ <_description>Perform file operations</_description>
+ <_message>Authentication is required to perform file operations</_message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.exec.path">@libexecdir@/gvfsd-admin</annotate>
+ </action>
+
+ <action id="org.gtk.vfs.file-operations">
+ <_description>Perform file operations</_description>
+ <_message>Authentication is required to perform file operations</_message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+</policyconfig>
diff --git a/daemon/org.gtk.vfs.file-operations.rules b/daemon/org.gtk.vfs.file-operations.rules
new file mode 100644
index 0000000..fb8d54a
--- /dev/null
+++ b/daemon/org.gtk.vfs.file-operations.rules
@@ -0,0 +1,8 @@
+polkit.addRule(function(action, subject) {
+ if ((action.id == "org.gtk.vfs.file-operations-helper") &&
+ subject.local &&
+ subject.active &&
+ subject.isInGroup ("wheel")) {
+ return polkit.Result.YES;
+ }
+});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]