diff options
author | David Zeuthen <davidz@redhat.com> | 2011-08-01 14:56:18 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2011-08-01 14:56:18 -0400 |
commit | f7002aa892dd2c2ee40518b6666db0d3cfabf23e (patch) | |
tree | 70e842b6409904a822f3de423a4eca440af2bdd0 | |
parent | d65be9ca5209dfbf4cbf6e25eb82918ef0549a63 (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.xml | 23 | ||||
-rw-r--r-- | src/tests/test.c | 30 | ||||
-rw-r--r-- | src/udiskscleanup.c | 3 | ||||
-rw-r--r-- | src/udiskscleanup.h | 4 | ||||
-rw-r--r-- | src/udisksdaemon.c | 7 | ||||
-rw-r--r-- | src/udisksdaemon.h | 6 | ||||
-rw-r--r-- | src/udiskslinuxencrypted.c | 2 | ||||
-rw-r--r-- | src/udiskslinuxfilesystem.c | 234 | ||||
-rw-r--r-- | src/udiskslinuxloop.c | 1 | ||||
-rw-r--r-- | src/udiskslinuxswapspace.c | 2 | ||||
-rw-r--r-- | src/udisksspawnedjob.c | 85 | ||||
-rw-r--r-- | src/udisksspawnedjob.h | 1 |
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); |