summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2011-08-01 14:56:18 -0400
committerDavid Zeuthen <davidz@redhat.com>2011-08-01 14:56:18 -0400
commitf7002aa892dd2c2ee40518b6666db0d3cfabf23e (patch)
tree70e842b6409904a822f3de423a4eca440af2bdd0
parentd65be9ca5209dfbf4cbf6e25eb82918ef0549a63 (diff)
If a device is referenced in fstab, invoke mount/umount as the calling user
Signed-off-by: David Zeuthen <davidz@redhat.com>
-rw-r--r--data/org.freedesktop.UDisks2.xml23
-rw-r--r--src/tests/test.c30
-rw-r--r--src/udiskscleanup.c3
-rw-r--r--src/udiskscleanup.h4
-rw-r--r--src/udisksdaemon.c7
-rw-r--r--src/udisksdaemon.h6
-rw-r--r--src/udiskslinuxencrypted.c2
-rw-r--r--src/udiskslinuxfilesystem.c234
-rw-r--r--src/udiskslinuxloop.c1
-rw-r--r--src/udiskslinuxswapspace.c2
-rw-r--r--src/udisksspawnedjob.c85
-rw-r--r--src/udisksspawnedjob.h1
12 files changed, 352 insertions, 46 deletions
diff --git a/data/org.freedesktop.UDisks2.xml b/data/org.freedesktop.UDisks2.xml
index 476e605..3d3514b 100644
--- a/data/org.freedesktop.UDisks2.xml
+++ b/data/org.freedesktop.UDisks2.xml
@@ -393,7 +393,7 @@
@options: Options - known options (in addition to <link linkend="udisks-std-options">standard options</link>) includes <parameter>fstype</parameter> (of type 's') and <parameter>options</parameter> (of type 's').
@mount_path: The filesystem path where the device was mounted.
- Mount the filesystem.
+ Mount a filesystem on the device.
The directory the device will be mounted in is determined by
looking at data related to the device (such the filesystem
@@ -420,6 +420,19 @@
<citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>
command. Note that the mount options are validated - only a
small subset per filesystem type is allowed.
+
+ If the device in question is referenced in a system-wide
+ configuration file (such as the
+ <citerefentry><refentrytitle>/etc/fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ file)
+ then the
+ <citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ command is called directly as the calling user and no clean-up
+ is performed when the device is removed. Additionally, the
+ given <parameter>options</parameter> and
+ <parameter>fstype</parameter> options are not used as they are
+ instead read from the system-wide configuration file in
+ question.
-->
<method name="Mount">
<arg name="options" direction="in" type="a{sv}"/>
@@ -434,6 +447,14 @@
If the filesystem is busy, this operation fails unless the
option <parameter>force</parameter> is given.
+
+ If the device in question is referenced in a system-wide
+ configuration file (such as the
+ <citerefentry><refentrytitle>/etc/fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ file)
+ then the
+ <citerefentry><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ command is called directly as the calling user.
-->
<method name="Unmount">
<arg name="options" direction="in" type="a{sv}"/>
diff --git a/src/tests/test.c b/src/tests/test.c
index eda10a0..0964e12 100644
--- a/src/tests/test.c
+++ b/src/tests/test.c
@@ -69,7 +69,7 @@ test_spawned_job_successful (void)
{
UDisksSpawnedJob *job;
- job = udisks_spawned_job_new ("/bin/true", NULL, NULL);
+ job = udisks_spawned_job_new ("/bin/true", NULL, getuid (), NULL);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_success), NULL);
g_object_unref (job);
}
@@ -81,7 +81,7 @@ test_spawned_job_failure (void)
{
UDisksSpawnedJob *job;
- job = udisks_spawned_job_new ("/bin/false", NULL, NULL);
+ job = udisks_spawned_job_new ("/bin/false", NULL, getuid (), NULL);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
"Command-line `/bin/false' exited with non-zero exit status 1: ");
g_object_unref (job);
@@ -94,7 +94,7 @@ test_spawned_job_missing_program (void)
{
UDisksSpawnedJob *job;
- job = udisks_spawned_job_new ("/path/to/unknown/file", NULL, NULL);
+ job = udisks_spawned_job_new ("/path/to/unknown/file", NULL, getuid (), NULL);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
"Error spawning command-line `/path/to/unknown/file': Failed to execute child process \"/path/to/unknown/file\" (No such file or directory) (g-exec-error-quark, 8)");
g_object_unref (job);
@@ -110,7 +110,7 @@ test_spawned_job_cancelled_at_start (void)
cancellable = g_cancellable_new ();
g_cancellable_cancel (cancellable);
- job = udisks_spawned_job_new ("/bin/true", NULL, cancellable);
+ job = udisks_spawned_job_new ("/bin/true", NULL, getuid (), cancellable);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
"Operation was cancelled (g-io-error-quark, 19)");
g_object_unref (job);
@@ -135,7 +135,7 @@ test_spawned_job_cancelled_midway (void)
GCancellable *cancellable;
cancellable = g_cancellable_new ();
- job = udisks_spawned_job_new ("/bin/sleep 0.5", NULL, cancellable);
+ job = udisks_spawned_job_new ("/bin/sleep 0.5", NULL, getuid (), cancellable);
g_timeout_add (10, on_timeout, cancellable); /* 10 msec */
g_main_loop_run (loop);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
@@ -167,7 +167,7 @@ test_spawned_job_override_signal_handler (void)
UDisksSpawnedJob *job;
gboolean handler_ran;
- job = udisks_spawned_job_new ("/path/to/unknown/file", NULL, NULL /* GCancellable */);
+ job = udisks_spawned_job_new ("/path/to/unknown/file", NULL, getuid (), NULL /* GCancellable */);
handler_ran = FALSE;
g_signal_connect (job, "spawned-job-completed", G_CALLBACK (on_spawned_job_completed), &handler_ran);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
@@ -183,7 +183,7 @@ test_spawned_job_premature_termination (void)
{
UDisksSpawnedJob *job;
- job = udisks_spawned_job_new ("/bin/sleep 1000", NULL, NULL /* GCancellable */);
+ job = udisks_spawned_job_new ("/bin/sleep 1000", NULL, getuid (), NULL /* GCancellable */);
g_object_unref (job);
}
@@ -214,7 +214,7 @@ test_spawned_job_read_stdout (void)
gchar *s;
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 0");
- job = udisks_spawned_job_new (s, NULL, NULL);
+ job = udisks_spawned_job_new (s, NULL, getuid (), NULL);
_g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (read_stdout_on_spawned_job_completed), NULL);
g_object_unref (job);
g_free (s);
@@ -247,7 +247,7 @@ test_spawned_job_read_stderr (void)
gchar *s;
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 1");
- job = udisks_spawned_job_new (s, NULL, NULL);
+ job = udisks_spawned_job_new (s, NULL, getuid (), NULL);
_g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (read_stderr_on_spawned_job_completed), NULL);
g_object_unref (job);
g_free (s);
@@ -278,14 +278,14 @@ test_spawned_job_exit_status (void)
gchar *s;
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 2");
- job = udisks_spawned_job_new (s, NULL, NULL);
+ job = udisks_spawned_job_new (s, NULL, getuid (), NULL);
_g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (exit_status_on_spawned_job_completed),
GINT_TO_POINTER (1));
g_object_unref (job);
g_free (s);
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 3");
- job = udisks_spawned_job_new (s, NULL, NULL);
+ job = udisks_spawned_job_new (s, NULL, getuid (), NULL);
_g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (exit_status_on_spawned_job_completed),
GINT_TO_POINTER (2));
g_object_unref (job);
@@ -301,7 +301,7 @@ test_spawned_job_abnormal_termination (void)
gchar *s;
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 4");
- job = udisks_spawned_job_new (s, NULL, NULL);
+ job = udisks_spawned_job_new (s, NULL, getuid (), NULL);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
"Command-line `./udisks-test-helper 4' was signaled with signal SIGSEGV (11): "
"OK, deliberately causing a segfault\n");
@@ -309,7 +309,7 @@ test_spawned_job_abnormal_termination (void)
g_free (s);
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 5");
- job = udisks_spawned_job_new (s, NULL, NULL);
+ job = udisks_spawned_job_new (s, NULL, getuid (), NULL);
_g_assert_signal_received (job, "completed", G_CALLBACK (on_completed_expect_failure),
"Command-line `./udisks-test-helper 5' was signaled with signal SIGABRT (6): "
"OK, deliberately abort()'ing\n");
@@ -350,7 +350,7 @@ test_spawned_job_binary_output (void)
gchar *s;
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 6");
- job = udisks_spawned_job_new (s, NULL, NULL);
+ job = udisks_spawned_job_new (s, NULL, getuid (), NULL);
_g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (binary_output_on_spawned_job_completed), NULL);
g_object_unref (job);
g_free (s);
@@ -381,7 +381,7 @@ test_spawned_job_input_string (void)
gchar *s;
s = g_strdup_printf (UDISKS_TEST_DIR "/udisks-test-helper 7");
- job = udisks_spawned_job_new (s, "foobar", NULL);
+ job = udisks_spawned_job_new (s, "foobar", getuid (), NULL);
_g_assert_signal_received (job, "spawned-job-completed", G_CALLBACK (input_string_on_spawned_job_completed), NULL);
g_object_unref (job);
g_free (s);
diff --git a/src/udiskscleanup.c b/src/udiskscleanup.c
index 6747f08..2ecce6d 100644
--- a/src/udiskscleanup.c
+++ b/src/udiskscleanup.c
@@ -604,6 +604,7 @@ udisks_cleanup_check_mounted_fs_entry (UDisksCleanup *cleanup,
/* right now -l is the only way to "force unmount" file systems... */
if (!udisks_daemon_launch_spawned_job_sync (cleanup->daemon,
NULL, /* GCancellable */
+ 0, /* uid_t run_as */
&error_message,
NULL, /* input_string */
"umount -l \"%s\"",
@@ -1248,6 +1249,7 @@ udisks_cleanup_check_unlocked_luks_entry (UDisksCleanup *cleanup,
escaped_device_file = g_strescape (device_file_cleartext, NULL);
if (!udisks_daemon_launch_spawned_job_sync (cleanup->daemon,
NULL, /* GCancellable */
+ 0, /* uid_t run_as */
&error_message,
NULL, /* input_string */
"cryptsetup luksClose \"%s\"",
@@ -1916,6 +1918,7 @@ udisks_cleanup_check_loop_entry (UDisksCleanup *cleanup,
escaped_loop_device_file = g_strescape (loop_device, NULL);
if (!udisks_daemon_launch_spawned_job_sync (cleanup->daemon,
NULL, /* GCancellable */
+ 0, /* uid_t run_as */
&error_message,
NULL, /* input_string */
"losetup -d \"%s\"",
diff --git a/src/udiskscleanup.h b/src/udiskscleanup.h
index 0fe710a..e29b432 100644
--- a/src/udiskscleanup.h
+++ b/src/udiskscleanup.h
@@ -92,9 +92,9 @@ gboolean udisks_cleanup_has_loop (UDisksCleanup *cleanup,
uid_t *out_uid,
GError **error);
gboolean udisks_cleanup_ignore_loop (UDisksCleanup *cleanup,
- const gchar *device);
+ const gchar *device_file);
void udisks_cleanup_unignore_loop (UDisksCleanup *cleanup,
- const gchar *device);
+ const gchar *device_file);
G_END_DECLS
diff --git a/src/udisksdaemon.c b/src/udisksdaemon.c
index 0ecbc79..2b048e0 100644
--- a/src/udisksdaemon.c
+++ b/src/udisksdaemon.c
@@ -545,6 +545,7 @@ udisks_daemon_launch_threaded_job (UDisksDaemon *daemon,
* udisks_daemon_launch_spawned_job:
* @daemon: A #UDisksDaemon.
* @cancellable: A #GCancellable or %NULL.
+ * @run_as: The #uid_t to run the command as.
* @input_string: A string to write to stdin of the spawned program or %NULL.
* @command_line_format: printf()-style format for the command line to spawn.
* @...: Arguments for @command_line_format.
@@ -564,6 +565,7 @@ udisks_daemon_launch_threaded_job (UDisksDaemon *daemon,
UDisksBaseJob *
udisks_daemon_launch_spawned_job (UDisksDaemon *daemon,
GCancellable *cancellable,
+ uid_t run_as,
const gchar *input_string,
const gchar *command_line_format,
...)
@@ -581,7 +583,7 @@ udisks_daemon_launch_spawned_job (UDisksDaemon *daemon,
va_start (var_args, command_line_format);
command_line = g_strdup_vprintf (command_line_format, var_args);
va_end (var_args);
- job = udisks_spawned_job_new (command_line, input_string, cancellable);
+ job = udisks_spawned_job_new (command_line, input_string, run_as, cancellable);
g_free (command_line);
/* TODO: protect job_id by a mutex */
@@ -625,6 +627,7 @@ spawned_job_sync_on_job_completed (UDisksJob *job,
* udisks_daemon_launch_spawned_job_sync:
* @daemon: A #UDisksDaemon.
* @cancellable: A #GCancellable or %NULL.
+ * @run_as: The #uid_t to run the command as.
* @input_string: A string to write to stdin of the spawned program or %NULL.
* @out_message: Return location for the @message parameter of the #UDisksJob::completed signal.
* @command_line_format: printf()-style format for the command line to spawn.
@@ -638,6 +641,7 @@ spawned_job_sync_on_job_completed (UDisksJob *job,
gboolean
udisks_daemon_launch_spawned_job_sync (UDisksDaemon *daemon,
GCancellable *cancellable,
+ uid_t run_as,
gchar **out_message,
const gchar *input_string,
const gchar *command_line_format,
@@ -663,6 +667,7 @@ udisks_daemon_launch_spawned_job_sync (UDisksDaemon *daemon,
va_end (var_args);
job = udisks_daemon_launch_spawned_job (daemon,
cancellable,
+ run_as,
input_string,
"%s",
command_line);
diff --git a/src/udisksdaemon.h b/src/udisksdaemon.h
index 4939c8b..305b79a 100644
--- a/src/udisksdaemon.h
+++ b/src/udisksdaemon.h
@@ -70,15 +70,17 @@ UDisksBaseJob *udisks_daemon_launch_simple_job (UDisksDaemon *
GCancellable *cancellable);
UDisksBaseJob *udisks_daemon_launch_spawned_job (UDisksDaemon *daemon,
GCancellable *cancellable,
+ uid_t run_as,
const gchar *input_string,
const gchar *command_line_format,
- ...) G_GNUC_PRINTF (4, 5);
+ ...) G_GNUC_PRINTF (5, 6);
gboolean udisks_daemon_launch_spawned_job_sync (UDisksDaemon *daemon,
GCancellable *cancellable,
+ uid_t run_as,
gchar **out_message,
const gchar *input_string,
const gchar *command_line_format,
- ...) G_GNUC_PRINTF (5, 6);
+ ...) G_GNUC_PRINTF (6, 7);
UDisksBaseJob *udisks_daemon_launch_threaded_job (UDisksDaemon *daemon,
UDisksThreadedJobFunc job_func,
gpointer user_data,
diff --git a/src/udiskslinuxencrypted.c b/src/udiskslinuxencrypted.c
index 41c4edd..aa631da 100644
--- a/src/udiskslinuxencrypted.c
+++ b/src/udiskslinuxencrypted.c
@@ -222,6 +222,7 @@ handle_unlock (UDisksEncrypted *encrypted,
/* TODO: support a 'readonly' option */
if (!udisks_daemon_launch_spawned_job_sync (daemon,
NULL, /* GCancellable */
+ 0, /* uid_t run_as */
&error_message,
passphrase, /* input_string */
"cryptsetup luksOpen \"%s\" \"%s\"",
@@ -429,6 +430,7 @@ handle_lock (UDisksEncrypted *encrypted,
if (!udisks_daemon_launch_spawned_job_sync (daemon,
NULL, /* GCancellable */
+ 0, /* uid_t run_as */
&error_message,
NULL, /* input_string */
"cryptsetup luksClose \"%s\"",
diff --git a/src/udiskslinuxfilesystem.c b/src/udiskslinuxfilesystem.c
index 553bd6a..c54178b 100644
--- a/src/udiskslinuxfilesystem.c
+++ b/src/udiskslinuxfilesystem.c
@@ -26,6 +26,8 @@
#include <grp.h>
#include <string.h>
#include <stdlib.h>
+#include <stdio.h>
+#include <mntent.h>
#include <glib/gstdio.h>
@@ -647,6 +649,105 @@ calculate_mount_point (UDisksBlockDevice *block,
/* ---------------------------------------------------------------------------------------------------- */
+static gboolean
+is_in_fstab (UDisksBlockDevice *block,
+ const gchar *fstab_path,
+ gchar **out_mount_point)
+{
+ gboolean ret;
+ FILE *f;
+ char buf[8192];
+ struct mntent mbuf;
+ struct mntent *m;
+
+ ret = FALSE;
+ f = fopen (fstab_path, "r");
+ if (f == NULL)
+ {
+ udisks_warning ("Error opening fstab file %s: %m", fstab_path);
+ goto out;
+ }
+
+ while ((m = getmntent_r (f, &mbuf, buf, sizeof (buf))) != NULL && !ret)
+ {
+ gchar *device;
+ struct stat sb;
+
+ device = NULL;
+ if (g_str_has_prefix (m->mnt_fsname, "UUID="))
+ {
+ device = g_strdup_printf ("/dev/disk/by-uuid/%s", m->mnt_fsname + 5);
+ }
+ else if (g_str_has_prefix (m->mnt_fsname, "LABEL="))
+ {
+ device = g_strdup_printf ("/dev/disk/by-label/%s", m->mnt_fsname + 6);
+ }
+ else if (g_str_has_prefix (m->mnt_fsname, "/dev"))
+ {
+ device = g_strdup (m->mnt_fsname);
+ }
+ else
+ {
+ /* ignore non-device entries */
+ goto continue_loop;
+ }
+
+ if (stat (device, &sb) != 0)
+ {
+ udisks_debug ("Error statting %s (for entry %s): %m", device, m->mnt_fsname);
+ goto continue_loop;
+ }
+ if (!S_ISBLK (sb.st_mode))
+ {
+ udisks_debug ("Device %s (for entry %s) is not a block device", device, m->mnt_fsname);
+ goto continue_loop;
+ }
+
+ /* udisks_debug ("device %d:%d for entry %s", major (sb.st_rdev), minor (sb.st_rdev), m->mnt_fsname); */
+
+ if (makedev (udisks_block_device_get_major (block),
+ udisks_block_device_get_minor (block)) == sb.st_rdev)
+ {
+ ret = TRUE;
+ if (out_mount_point != NULL)
+ *out_mount_point = g_strdup (m->mnt_dir);
+ }
+
+ continue_loop:
+ g_free (device);
+ }
+
+ out:
+ if (f != NULL)
+ fclose (f);
+ return ret;
+}
+
+/* returns TRUE if, and only if, device is referenced in e.g. /etc/fstab
+ *
+ * TODO: check all files in /etc/fstab.d (it's a non-standard Linux extension)
+ * TODO: check if systemd has a specific "unit" for the device
+ */
+static gboolean
+is_system_managed (UDisksBlockDevice *block,
+ gchar **out_mount_point)
+{
+ gboolean ret;
+
+ ret = TRUE;
+
+ /* First, check /etc/fstab */
+ if (is_in_fstab (block, "/etc/fstab", out_mount_point))
+ goto out;
+
+ ret = FALSE;
+
+ out:
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
/* runs in thread dedicated to handling @invocation */
static gboolean
handle_mount (UDisksFilesystem *filesystem,
@@ -669,6 +770,7 @@ handle_mount (UDisksFilesystem *filesystem,
gchar *error_message;
GError *error;
const gchar *action_id;
+ gboolean system_managed;
object = NULL;
error_message = NULL;
@@ -678,16 +780,18 @@ handle_mount (UDisksFilesystem *filesystem,
escaped_fs_type_to_use = NULL;
escaped_mount_options_to_use = NULL;
escaped_mount_point_to_use = NULL;
+ system_managed = FALSE;
object = UDISKS_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (filesystem)));
block = udisks_object_peek_block_device (object);
daemon = udisks_linux_block_get_daemon (UDISKS_LINUX_BLOCK (object));
cleanup = udisks_daemon_get_cleanup (daemon);
- /* TODO: check if mount point is managed by e.g. /etc/fstab or
- * similar - if so, use that instead of managing mount points
- * in /media
- */
+ /* check if mount point is managed by e.g. /etc/fstab or similar */
+ if (is_system_managed (block, &mount_point_to_use))
+ {
+ system_managed = TRUE;
+ }
/* First, fail if the device is already mounted */
existing_mount_points = udisks_filesystem_get_mount_points (filesystem);
@@ -712,7 +816,45 @@ handle_mount (UDisksFilesystem *filesystem,
goto out;
}
- /* Them fail if the device is not mountable - we actually allow mounting
+ error = NULL;
+ if (!udisks_daemon_util_get_caller_uid_sync (daemon, invocation, NULL /* GCancellable */, &caller_uid, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* if system-managed (e.g. referenced in /etc/fstab or similar), just
+ * run mount(8) as the calling user
+ */
+ if (system_managed)
+ {
+ escaped_mount_point_to_use = g_strescape (mount_point_to_use, NULL);
+ if (!udisks_daemon_launch_spawned_job_sync (daemon,
+ NULL, /* GCancellable */
+ caller_uid, /* uid_t run_as */
+ &error_message,
+ NULL, /* input_string */
+ "mount \"%s\"",
+ escaped_mount_point_to_use))
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "Error mounting system-managed device %s: %s",
+ udisks_block_device_get_device (block),
+ error_message);
+ goto out;
+ }
+ udisks_notice ("Mounted %s (system) at %s on behalf of uid %d",
+ udisks_block_device_get_device (block),
+ mount_point_to_use,
+ caller_uid);
+ udisks_filesystem_complete_mount (filesystem, invocation, mount_point_to_use);
+ goto out;
+ }
+
+ /* Then fail if the device is not mountable - we actually allow mounting
* devices that are not probed since since it could be that we just
* don't have the data in the udev database but the device has a
* filesystem *anyway*...
@@ -736,15 +878,6 @@ handle_mount (UDisksFilesystem *filesystem,
goto out;
}
- /* we need the uid of the caller to check mount options */
- error = NULL;
- if (!udisks_daemon_util_get_caller_uid_sync (daemon, invocation, NULL /* GCancellable */, &caller_uid, &error))
- {
- g_dbus_method_invocation_return_gerror (invocation, error);
- g_error_free (error);
- goto out;
- }
-
/* calculate filesystem type (guaranteed to be valid UTF-8) */
error = NULL;
fs_type_to_use = calculate_fs_type (block,
@@ -825,6 +958,7 @@ handle_mount (UDisksFilesystem *filesystem,
/* run mount(8) */
if (!udisks_daemon_launch_spawned_job_sync (daemon,
NULL, /* GCancellable */
+ 0, /* uid_t run_as */
&error_message,
NULL, /* input_string */
"mount -t \"%s\" -o \"%s\" \"%s\" \"%s\"",
@@ -905,6 +1039,7 @@ handle_unmount (UDisksFilesystem *filesystem,
const gchar *const *mount_points;
gboolean opt_force;
gboolean rc;
+ gboolean system_managed;
mount_point = NULL;
escaped_mount_point = NULL;
@@ -915,6 +1050,7 @@ handle_unmount (UDisksFilesystem *filesystem,
block = udisks_object_peek_block_device (object);
daemon = udisks_linux_block_get_daemon (UDISKS_LINUX_BLOCK (object));
cleanup = udisks_daemon_get_cleanup (daemon);
+ system_managed = FALSE;
if (options != NULL)
{
@@ -936,6 +1072,65 @@ handle_unmount (UDisksFilesystem *filesystem,
}
error = NULL;
+ if (!udisks_daemon_util_get_caller_uid_sync (daemon, invocation, NULL, &caller_uid, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* check if mount point is managed by e.g. /etc/fstab or similar */
+ if (is_system_managed (block, &mount_point))
+ {
+ system_managed = TRUE;
+ }
+
+ /* if system-managed (e.g. referenced in /etc/fstab or similar), just
+ * run mount(8) as the calling user
+ */
+ if (system_managed)
+ {
+ escaped_mount_point = g_strescape (mount_point, NULL);
+ /* right now -l is the only way to "force unmount" file systems... */
+ if (opt_force)
+ {
+ rc = udisks_daemon_launch_spawned_job_sync (daemon,
+ NULL, /* GCancellable */
+ caller_uid, /* uid_t run_as */
+ &error_message,
+ NULL, /* input_string */
+ "umount -l \"%s\"",
+ escaped_mount_point);
+ }
+ else
+ {
+ rc = udisks_daemon_launch_spawned_job_sync (daemon,
+ NULL, /* GCancellable */
+ caller_uid, /* uid_t run_as */
+ &error_message,
+ NULL, /* input_string */
+ "umount \"%s\"",
+ escaped_mount_point);
+ }
+ if (!rc)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "Error unmounting system-managed device %s: %s",
+ udisks_block_device_get_device (block),
+ error_message);
+ goto out;
+ }
+ udisks_notice ("Unmounted %s (system) from %s on behalf of uid %d",
+ udisks_block_device_get_device (block),
+ mount_point,
+ caller_uid);
+ udisks_filesystem_complete_unmount (filesystem, invocation);
+ goto out;
+ }
+
+ error = NULL;
mount_point = udisks_cleanup_find_mounted_fs (cleanup,
makedev (udisks_block_device_get_major (block), udisks_block_device_get_minor (block)),
&mounted_by_uid,
@@ -965,14 +1160,6 @@ handle_unmount (UDisksFilesystem *filesystem,
/* TODO: allow unmounting stuff not in the mounted-fs file? */
- error = NULL;
- if (!udisks_daemon_util_get_caller_uid_sync (daemon, invocation, NULL, &caller_uid, &error))
- {
- g_dbus_method_invocation_return_gerror (invocation, error);
- g_error_free (error);
- goto out;
- }
-
if (caller_uid != 0 && (caller_uid != mounted_by_uid))
{
if (!udisks_daemon_util_check_authorization_sync (daemon,
@@ -1002,6 +1189,7 @@ handle_unmount (UDisksFilesystem *filesystem,
/* right now -l is the only way to "force unmount" file systems... */
rc = udisks_daemon_launch_spawned_job_sync (daemon,
NULL, /* GCancellable */
+ 0, /* uid_t run_as */
&error_message,
NULL, /* input_string */
"umount -l \"%s\"",
@@ -1011,6 +1199,7 @@ handle_unmount (UDisksFilesystem *filesystem,
{
rc = udisks_daemon_launch_spawned_job_sync (daemon,
NULL, /* GCancellable */
+ 0, /* uid_t run_as */
&error_message,
NULL, /* input_string */
"umount \"%s\"",
@@ -1192,6 +1381,7 @@ handle_set_label (UDisksFilesystem *filesystem,
escaped_label = g_shell_quote (label);
job = udisks_daemon_launch_spawned_job (daemon,
NULL, /* cancellable */
+ 0, /* uid_t run_as */
NULL, /* input_string */
"e2label %s %s",
udisks_block_device_get_device (block),
diff --git a/src/udiskslinuxloop.c b/src/udiskslinuxloop.c
index 57150df..27f243b 100644
--- a/src/udiskslinuxloop.c
+++ b/src/udiskslinuxloop.c
@@ -179,6 +179,7 @@ handle_delete (UDisksLoop *loop,
if (!udisks_daemon_launch_spawned_job_sync (daemon,
NULL, /* GCancellable */
+ 0, /* uid_t run_as */
&error_message,
NULL, /* input_string */
"losetup -d \"%s\"",
diff --git a/src/udiskslinuxswapspace.c b/src/udiskslinuxswapspace.c
index 5cf5e97..43bda2f 100644
--- a/src/udiskslinuxswapspace.c
+++ b/src/udiskslinuxswapspace.c
@@ -142,6 +142,7 @@ handle_start (UDisksSwapspace *swapspace,
job = udisks_daemon_launch_spawned_job (daemon,
NULL, /* cancellable */
+ 0, /* uid_t run_as */
NULL, /* input_string */
"swapon %s",
udisks_block_device_get_device (block));
@@ -202,6 +203,7 @@ handle_stop (UDisksSwapspace *swapspace,
job = udisks_daemon_launch_spawned_job (daemon,
NULL, /* cancellable */
+ 0, /* uid_t run_as */
NULL, /* input_string */
"swapoff %s",
udisks_block_device_get_device (block));
diff --git a/src/udisksspawnedjob.c b/src/udisksspawnedjob.c
index a3b612d..a790cd0 100644
--- a/src/udisksspawnedjob.c
+++ b/src/udisksspawnedjob.c
@@ -24,6 +24,10 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdlib.h>
#include "udisksbasejob.h"
#include "udisksspawnedjob.h"
@@ -56,6 +60,7 @@ struct _UDisksSpawnedJob
GMainContext *main_context;
gchar *input_string;
+ uid_t run_as;
const gchar *input_string_cursor;
GPid child_pid;
@@ -93,7 +98,8 @@ enum
{
PROP_0,
PROP_COMMAND_LINE,
- PROP_INPUT_STRING
+ PROP_INPUT_STRING,
+ PROP_RUN_AS
};
enum
@@ -178,6 +184,10 @@ udisks_spawned_job_set_property (GObject *object,
job->input_string = g_value_dup_string (value);
break;
+ case PROP_RUN_AS:
+ job->run_as = g_value_get_uint (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -348,6 +358,57 @@ child_watch_cb (GPid pid,
g_object_unref (job);
}
+#include <stdio.h>
+
+/* careful, this is in the fork()'ed child so all utility threads etc are not available */
+static void
+child_setup (gpointer user_data)
+{
+ UDisksSpawnedJob *job = UDISKS_SPAWNED_JOB (user_data);
+ struct passwd *pw;
+
+ if (job->run_as == getuid ())
+ goto out;
+
+ pw = getpwuid (job->run_as);
+ if (pw == NULL)
+ {
+ g_printerr ("No password record for uid %d: %m\n", (gint) job->run_as);
+ abort ();
+ }
+
+ /* become the user...
+ *
+ * TODO: this might need to involve running the whole PAM 'session'
+ * stack as done by e.g. pkexec(1) and various login managers
+ * otherwise things like the SELinux context might not be entirely
+ * right. What we really need is some library function to
+ * impersonate a pid or uid. What a mess.
+ */
+ if (setgroups (0, NULL) != 0)
+ {
+ g_printerr ("Error resetting groups: %m\n");
+ abort ();
+ }
+ if (initgroups (pw->pw_name, pw->pw_gid) != 0)
+ {
+ g_printerr ("Error initializing groups for uid %d: %m\n", (gint) job->run_as);
+ abort ();
+ }
+ if (setregid (pw->pw_gid, pw->pw_gid) != 0)
+ {
+ g_printerr ("Error setting real+effective gid for uid %d: %m\n", (gint) job->run_as);
+ abort ();
+ }
+ if (setreuid (pw->pw_uid, pw->pw_uid) != 0)
+ {
+ g_printerr ("Error setting real+effective uid for uid %d: %m\n", (gint) job->run_as);
+ abort ();
+ }
+
+ out:
+ ;
+}
static void
udisks_spawned_job_constructed (GObject *object)
@@ -397,8 +458,8 @@ udisks_spawned_job_constructed (GObject *object)
child_argv,
NULL, /* envp */
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
- NULL, /* child_setup */
- NULL, /* child_setup's user_data */
+ child_setup, /* child_setup */
+ job, /* child_setup's user_data */
&(job->child_pid),
job->input_string != NULL ? &(job->child_stdin_fd) : NULL,
&(job->child_stdout_fd),
@@ -504,6 +565,21 @@ udisks_spawned_job_class_init (UDisksSpawnedJobClass *klass)
G_PARAM_STATIC_STRINGS));
/**
+ * UDisksSpawnedJob:run-as:
+ *
+ * The #uid_t to run the program as.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_RUN_AS,
+ g_param_spec_uint ("run-as",
+ "Run As",
+ "The uid_t to run the program as",
+ 0, G_MAXUINT, 0,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
* UDisksSpawnedJob::spawned-job-completed:
* @job: The #UDisksSpawnedJob emitting the signal.
* @error: %NULL if running the whole command line succeeded, otherwise a #GError that is set.
@@ -552,6 +628,7 @@ udisks_spawned_job_class_init (UDisksSpawnedJobClass *klass)
* udisks_spawned_job_new:
* @command_line: The command line to run.
* @input_string: A string to write to stdin of the spawned program or %NULL.
+ * @run_as: The #uid_t to run the program as.
* @cancellable: A #GCancellable or %NULL.
*
* Creates a new #UDisksSpawnedJob instance.
@@ -565,6 +642,7 @@ udisks_spawned_job_class_init (UDisksSpawnedJobClass *klass)
UDisksSpawnedJob *
udisks_spawned_job_new (const gchar *command_line,
const gchar *input_string,
+ uid_t run_as,
GCancellable *cancellable)
{
g_return_val_if_fail (command_line != NULL, NULL);
@@ -572,6 +650,7 @@ udisks_spawned_job_new (const gchar *command_line,
return UDISKS_SPAWNED_JOB (g_object_new (UDISKS_TYPE_SPAWNED_JOB,
"command-line", command_line,
"input-string", input_string,
+ "run-as", run_as,
"cancellable", cancellable,
NULL));
}
diff --git a/src/udisksspawnedjob.h b/src/udisksspawnedjob.h
index ad62418..547ee26 100644
--- a/src/udisksspawnedjob.h
+++ b/src/udisksspawnedjob.h
@@ -32,6 +32,7 @@ G_BEGIN_DECLS
GType udisks_spawned_job_get_type (void) G_GNUC_CONST;
UDisksSpawnedJob *udisks_spawned_job_new (const gchar *command_line,
const gchar *input_string,
+ uid_t run_as,
GCancellable *cancellable);
const gchar *udisks_spawned_job_get_command_line (UDisksSpawnedJob *job);