diff options
author | David Zeuthen <davidz@redhat.com> | 2011-08-12 12:40:17 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2011-08-12 12:40:17 -0400 |
commit | 8d432a303d277b4a7b469a9fc4b822be6b6d2778 (patch) | |
tree | 42a6115f910c0ce2d9bfd1cf4e53436f879df991 | |
parent | 2f7455e32bb90dc31bbeaaa01e64a9767c2a16ae (diff) |
Allow mounting/unmounting fstab devices without the 'user' or 'users' option
... by introducing a new org.freedesktop.udisks2.filesystem-nonuser-fstab
polkit action.
http://people.freedesktop.org/~david/palimpsest-nonuser-fstab-devices.png
Signed-off-by: David Zeuthen <davidz@redhat.com>
-rw-r--r-- | data/org.freedesktop.UDisks2.xml | 16 | ||||
-rw-r--r-- | policy/org.freedesktop.udisks2.policy.in | 10 | ||||
-rw-r--r-- | src/tests/test.c | 30 | ||||
-rw-r--r-- | src/udiskscleanup.c | 18 | ||||
-rw-r--r-- | src/udisksdaemon.c | 53 | ||||
-rw-r--r-- | src/udisksdaemon.h | 11 | ||||
-rw-r--r-- | src/udiskslinuxdrive.c | 6 | ||||
-rw-r--r-- | src/udiskslinuxencrypted.c | 12 | ||||
-rw-r--r-- | src/udiskslinuxfilesystem.c | 74 | ||||
-rw-r--r-- | src/udiskslinuxloop.c | 6 | ||||
-rw-r--r-- | src/udiskslinuxswapspace.c | 6 | ||||
-rw-r--r-- | src/udisksspawnedjob.c | 75 | ||||
-rw-r--r-- | src/udisksspawnedjob.h | 3 |
13 files changed, 240 insertions, 80 deletions
diff --git a/data/org.freedesktop.UDisks2.xml b/data/org.freedesktop.UDisks2.xml index 4a5b21c..012791a 100644 --- a/data/org.freedesktop.UDisks2.xml +++ b/data/org.freedesktop.UDisks2.xml @@ -674,8 +674,14 @@ 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 + command is called directly as the calling user. + If the calling user does not have sufficient permissions to + mount the device (it could be the <literal>user</literal> or + <literal>users</literal> option isn't specificed), then + additional authorization is requested and, if obtained, the + <citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry> + command is invoked as root. + 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 @@ -702,6 +708,12 @@ then the <citerefentry><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry> command is called directly as the calling user. + If the calling user does not have sufficient permissions to + unmount the device (it could be the <literal>user</literal> or + <literal>users</literal> option isn't specificed), then + additional authorization is requested and, if obtained, the + <citerefentry><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry> + command is invoked as root. --> <method name="Unmount"> <arg name="options" direction="in" type="a{sv}"/> diff --git a/policy/org.freedesktop.udisks2.policy.in b/policy/org.freedesktop.udisks2.policy.in index 8e61608..a27b6b2 100644 --- a/policy/org.freedesktop.udisks2.policy.in +++ b/policy/org.freedesktop.udisks2.policy.in @@ -30,6 +30,16 @@ </defaults> </action> + <action id="org.freedesktop.udisks2.filesystem-nonuser-fstab"> + <_description>Mount/unmount non-user filesystems defined in the fstab file</_description> + <_message>Authentication is required to mount/unmount the filesystem</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + <action id="org.freedesktop.udisks2.filesystem-unmount-others"> <_description>Unmount a device mounted by another user</_description> <_message>Authentication is required to unmount a filesystem mounted by another user</_message> diff --git a/src/tests/test.c b/src/tests/test.c index 0964e12..1855deb 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, getuid (), NULL); + job = udisks_spawned_job_new ("/bin/true", NULL, getuid (), geteuid (), 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, getuid (), NULL); + job = udisks_spawned_job_new ("/bin/false", NULL, getuid (), geteuid (), 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, getuid (), NULL); + job = udisks_spawned_job_new ("/path/to/unknown/file", NULL, getuid (), geteuid (), 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, getuid (), cancellable); + job = udisks_spawned_job_new ("/bin/true", NULL, getuid (), geteuid (), 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, getuid (), cancellable); + job = udisks_spawned_job_new ("/bin/sleep 0.5", NULL, getuid (), geteuid (), 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, getuid (), NULL /* GCancellable */); + job = udisks_spawned_job_new ("/path/to/unknown/file", NULL, getuid (), geteuid (), 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, getuid (), NULL /* GCancellable */); + job = udisks_spawned_job_new ("/bin/sleep 1000", NULL, getuid (), geteuid (), 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, getuid (), NULL); + job = udisks_spawned_job_new (s, NULL, getuid (), geteuid (), 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, getuid (), NULL); + job = udisks_spawned_job_new (s, NULL, getuid (), geteuid (), 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, getuid (), NULL); + job = udisks_spawned_job_new (s, NULL, getuid (), geteuid (), 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, getuid (), NULL); + job = udisks_spawned_job_new (s, NULL, getuid (), geteuid (), 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, getuid (), NULL); + job = udisks_spawned_job_new (s, NULL, getuid (), geteuid (), 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, getuid (), NULL); + job = udisks_spawned_job_new (s, NULL, getuid (), geteuid (), 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, getuid (), NULL); + job = udisks_spawned_job_new (s, NULL, getuid (), geteuid (), 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", getuid (), NULL); + job = udisks_spawned_job_new (s, "foobar", getuid (), geteuid (), 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 dccb057..6084340 100644 --- a/src/udiskscleanup.c +++ b/src/udiskscleanup.c @@ -621,8 +621,10 @@ udisks_cleanup_check_mounted_fs_entry (UDisksCleanup *cleanup, escaped_mount_point = g_strescape (mount_point, NULL); /* 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 */ + NULL, /* GCancellable */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ + NULL, /* gint *out_status */ &error_message, NULL, /* input_string */ "umount -l \"%s\"", @@ -1290,8 +1292,10 @@ udisks_cleanup_check_unlocked_luks_entry (UDisksCleanup *cleanup, error_message = NULL; 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 */ + NULL, /* GCancellable */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ + NULL, /* gint *out_status */ &error_message, NULL, /* input_string */ "cryptsetup luksClose \"%s\"", @@ -1959,8 +1963,10 @@ udisks_cleanup_check_loop_entry (UDisksCleanup *cleanup, error_message = NULL; 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 */ + NULL, /* GCancellable */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ + NULL, /* gint *out_status */ &error_message, NULL, /* input_string */ "losetup -d \"%s\"", diff --git a/src/udisksdaemon.c b/src/udisksdaemon.c index 4532746..47c48c2 100644 --- a/src/udisksdaemon.c +++ b/src/udisksdaemon.c @@ -598,7 +598,8 @@ 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. + * @run_as_uid: The #uid_t to run the command as. + * @run_as_euid: The effective #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. @@ -618,7 +619,8 @@ udisks_daemon_launch_threaded_job (UDisksDaemon *daemon, UDisksBaseJob * udisks_daemon_launch_spawned_job (UDisksDaemon *daemon, GCancellable *cancellable, - uid_t run_as, + uid_t run_as_uid, + uid_t run_as_euid, const gchar *input_string, const gchar *command_line_format, ...) @@ -636,7 +638,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, run_as, cancellable); + job = udisks_spawned_job_new (command_line, input_string, run_as_uid, run_as_euid, cancellable); g_free (command_line); /* TODO: protect job_id by a mutex */ @@ -661,14 +663,28 @@ typedef struct GMainContext *context; GMainLoop *loop; gboolean success; + gint status; gchar *message; } SpawnedJobSyncData; +static gboolean +spawned_job_sync_on_spawned_job_completed (UDisksSpawnedJob *job, + GError *error, + gint status, + GString *standard_output, + GString *standard_error, + gpointer user_data) +{ + SpawnedJobSyncData *data = user_data; + data->status = status; + return FALSE; /* let other handlers run */ +} + static void -spawned_job_sync_on_job_completed (UDisksJob *job, - gboolean success, - const gchar *message, - gpointer user_data) +spawned_job_sync_on_completed (UDisksJob *job, + gboolean success, + const gchar *message, + gpointer user_data) { SpawnedJobSyncData *data = user_data; data->success = success; @@ -680,8 +696,10 @@ 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. + * @run_as_uid: The #uid_t to run the command as. + * @run_as_euid: The effective #uid_t to run the command as. * @input_string: A string to write to stdin of the spawned program or %NULL. + * @out_status: Return location for the @status parameter of the #UDisksSpawnedJob::spawned-job-completed signal. * @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. * @...: Arguments for @command_line_format. @@ -689,12 +707,14 @@ spawned_job_sync_on_job_completed (UDisksJob *job, * Like udisks_daemon_launch_spawned_job() but blocks the calling * thread until the job completes. * - * Returns: The @success parameter of the #UDisksJob::completed. + * Returns: The @success parameter of the #UDisksJob::completed signal. */ gboolean udisks_daemon_launch_spawned_job_sync (UDisksDaemon *daemon, GCancellable *cancellable, - uid_t run_as, + uid_t run_as_uid, + uid_t run_as_euid, + gint *out_status, gchar **out_message, const gchar *input_string, const gchar *command_line_format, @@ -713,6 +733,7 @@ udisks_daemon_launch_spawned_job_sync (UDisksDaemon *daemon, g_main_context_push_thread_default (data.context); data.loop = g_main_loop_new (data.context, FALSE); data.success = FALSE; + data.status = 0; data.message = NULL; va_start (var_args, command_line_format); @@ -720,17 +741,25 @@ udisks_daemon_launch_spawned_job_sync (UDisksDaemon *daemon, va_end (var_args); job = udisks_daemon_launch_spawned_job (daemon, cancellable, - run_as, + run_as_uid, + run_as_euid, input_string, "%s", command_line); + g_signal_connect (job, + "spawned-job-completed", + G_CALLBACK (spawned_job_sync_on_spawned_job_completed), + &data); g_signal_connect_after (job, "completed", - G_CALLBACK (spawned_job_sync_on_job_completed), + G_CALLBACK (spawned_job_sync_on_completed), &data); g_main_loop_run (data.loop); + if (out_status != NULL) + *out_status = data.status; + if (out_message != NULL) *out_message = data.message; else diff --git a/src/udisksdaemon.h b/src/udisksdaemon.h index 56bbdaf..80805f1 100644 --- a/src/udisksdaemon.h +++ b/src/udisksdaemon.h @@ -72,17 +72,20 @@ UDisksBaseJob *udisks_daemon_launch_simple_job (UDisksDaemon * GCancellable *cancellable); UDisksBaseJob *udisks_daemon_launch_spawned_job (UDisksDaemon *daemon, GCancellable *cancellable, - uid_t run_as, + uid_t run_as_uid, + uid_t run_as_euid, const gchar *input_string, const gchar *command_line_format, - ...) G_GNUC_PRINTF (5, 6); + ...) G_GNUC_PRINTF (6, 7); gboolean udisks_daemon_launch_spawned_job_sync (UDisksDaemon *daemon, GCancellable *cancellable, - uid_t run_as, + uid_t run_as_uid, + uid_t run_as_euid, + gint *out_status, gchar **out_message, const gchar *input_string, const gchar *command_line_format, - ...) G_GNUC_PRINTF (6, 7); + ...) G_GNUC_PRINTF (8, 9); UDisksBaseJob *udisks_daemon_launch_threaded_job (UDisksDaemon *daemon, UDisksThreadedJobFunc job_func, gpointer user_data, diff --git a/src/udiskslinuxdrive.c b/src/udiskslinuxdrive.c index 16c54d3..9fd1b5e 100644 --- a/src/udiskslinuxdrive.c +++ b/src/udiskslinuxdrive.c @@ -686,8 +686,10 @@ on_eject (UDisksDrive *drive_iface, goto out; if (!udisks_daemon_launch_spawned_job_sync (daemon, - NULL, /* GCancellable */ - 0, /* uid_t run_as */ + NULL, /* GCancellable */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ + NULL, /* gint *out_status */ &error_message, NULL, /* input_string */ "eject \"%s\"", diff --git a/src/udiskslinuxencrypted.c b/src/udiskslinuxencrypted.c index f6cfdff..1d2fed8 100644 --- a/src/udiskslinuxencrypted.c +++ b/src/udiskslinuxencrypted.c @@ -221,8 +221,10 @@ handle_unlock (UDisksEncrypted *encrypted, /* TODO: support a 'readonly' option */ if (!udisks_daemon_launch_spawned_job_sync (daemon, - NULL, /* GCancellable */ - 0, /* uid_t run_as */ + NULL, /* GCancellable */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ + NULL, /* gint *out_status */ &error_message, passphrase, /* input_string */ "cryptsetup luksOpen \"%s\" \"%s\"", @@ -427,8 +429,10 @@ handle_lock (UDisksEncrypted *encrypted, } if (!udisks_daemon_launch_spawned_job_sync (daemon, - NULL, /* GCancellable */ - 0, /* uid_t run_as */ + NULL, /* GCancellable */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ + NULL, /* gint *out_status */ &error_message, NULL, /* input_string */ "cryptsetup luksClose \"%s\"", diff --git a/src/udiskslinuxfilesystem.c b/src/udiskslinuxfilesystem.c index f152f1d..72d0a7a 100644 --- a/src/udiskslinuxfilesystem.c +++ b/src/udiskslinuxfilesystem.c @@ -829,6 +829,9 @@ handle_mount (UDisksFilesystem *filesystem, */ if (system_managed) { + gint status; + gboolean mount_fstab_as_root; + if (!g_file_test (mount_point_to_use, G_FILE_TEST_IS_DIR)) { if (g_mkdir_with_parents (mount_point_to_use, 0755) != 0) @@ -844,14 +847,33 @@ handle_mount (UDisksFilesystem *filesystem, } escaped_mount_point_to_use = g_strescape (mount_point_to_use, NULL); + mount_fstab_as_root = FALSE; + mount_fstab_again: if (!udisks_daemon_launch_spawned_job_sync (daemon, NULL, /* GCancellable */ - caller_uid, /* uid_t run_as */ + mount_fstab_as_root ? 0 : caller_uid, /* uid_t run_as_uid */ + mount_fstab_as_root ? 0 : caller_uid, /* uid_t run_as_euid */ + &status, &error_message, NULL, /* input_string */ "mount \"%s\"", escaped_mount_point_to_use)) { + /* mount(8) exits with status 1 on "incorrect invocation or permissions" - if this is + * is so, try as as root */ + if (!mount_fstab_as_root && WIFEXITED (status) && WEXITSTATUS (status) == 1) + { + if (!udisks_daemon_util_check_authorization_sync (daemon, + object, + "org.freedesktop.udisks2.filesystem-nonuser-fstab", + options, + N_("Authentication is required to mount the non-user fstab device $(udisks2.device)"), + invocation)) + goto out; + mount_fstab_as_root = TRUE; + goto mount_fstab_again; + } + g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, @@ -988,8 +1010,10 @@ handle_mount (UDisksFilesystem *filesystem, /* run mount(8) */ if (!udisks_daemon_launch_spawned_job_sync (daemon, - NULL, /* GCancellable */ - 0, /* uid_t run_as */ + NULL, /* GCancellable */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ + NULL, /* gint *out_status */ &error_message, NULL, /* input_string */ "mount -t \"%s\" -o \"%s\" \"%s\" \"%s\"", @@ -1122,17 +1146,42 @@ handle_unmount (UDisksFilesystem *filesystem, */ if (system_managed) { + gint status; + gboolean unmount_fstab_as_root; + + unmount_fstab_as_root = FALSE; + unmount_fstab_again: escaped_mount_point = g_strescape (mount_point, NULL); /* right now -l is the only way to "force unmount" file systems... */ if (!udisks_daemon_launch_spawned_job_sync (daemon, - NULL, /* GCancellable */ - caller_uid, /* uid_t run_as */ + NULL, /* GCancellable */ + unmount_fstab_as_root ? 0 : caller_uid, /* uid_t run_as_uid */ + unmount_fstab_as_root ? 0 : caller_uid, /* uid_t run_as_euid */ + &status, &error_message, NULL, /* input_string */ "umount %s \"%s\"", opt_force ? "-l" : "", escaped_mount_point)) { + /* umount(8) does not (yet) have a specific exits status for + * "insufficient permissions" so just try again as root + * + * TODO: file bug asking for such an exit status + */ + if (!unmount_fstab_as_root && WIFEXITED (status) && WEXITSTATUS (status) != 0) + { + if (!udisks_daemon_util_check_authorization_sync (daemon, + object, + "org.freedesktop.udisks2.filesystem-nonuser-fstab", + options, + N_("Authentication is required to unmount the non-user fstab device $(udisks2.device)"), + invocation)) + goto out; + unmount_fstab_as_root = TRUE; + goto unmount_fstab_again; + } + g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, @@ -1202,8 +1251,10 @@ handle_unmount (UDisksFilesystem *filesystem, } escaped_mount_point = g_strescape (mount_point, NULL); rc = udisks_daemon_launch_spawned_job_sync (daemon, - NULL, /* GCancellable */ - 0, /* uid_t run_as */ + NULL, /* GCancellable */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ + NULL, /* gint *out_status */ &error_message, NULL, /* input_string */ "umount %s \"%s\"", @@ -1214,8 +1265,10 @@ handle_unmount (UDisksFilesystem *filesystem, { /* mount_point == NULL */ rc = udisks_daemon_launch_spawned_job_sync (daemon, - NULL, /* GCancellable */ - 0, /* uid_t run_as */ + NULL, /* GCancellable */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ + NULL, /* gint *out_status */ &error_message, NULL, /* input_string */ "umount %s \"%s\"", @@ -1400,7 +1453,8 @@ 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 */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ NULL, /* input_string */ "e2label %s %s", udisks_block_device_get_device (block), diff --git a/src/udiskslinuxloop.c b/src/udiskslinuxloop.c index 27f243b..61d42f6 100644 --- a/src/udiskslinuxloop.c +++ b/src/udiskslinuxloop.c @@ -178,8 +178,10 @@ handle_delete (UDisksLoop *loop, } if (!udisks_daemon_launch_spawned_job_sync (daemon, - NULL, /* GCancellable */ - 0, /* uid_t run_as */ + NULL, /* GCancellable */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ + NULL, /* gint *out_status */ &error_message, NULL, /* input_string */ "losetup -d \"%s\"", diff --git a/src/udiskslinuxswapspace.c b/src/udiskslinuxswapspace.c index 43bda2f..32ae054 100644 --- a/src/udiskslinuxswapspace.c +++ b/src/udiskslinuxswapspace.c @@ -142,7 +142,8 @@ handle_start (UDisksSwapspace *swapspace, job = udisks_daemon_launch_spawned_job (daemon, NULL, /* cancellable */ - 0, /* uid_t run_as */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ NULL, /* input_string */ "swapon %s", udisks_block_device_get_device (block)); @@ -203,7 +204,8 @@ handle_stop (UDisksSwapspace *swapspace, job = udisks_daemon_launch_spawned_job (daemon, NULL, /* cancellable */ - 0, /* uid_t run_as */ + 0, /* uid_t run_as_uid */ + 0, /* uid_t run_as_euid */ NULL, /* input_string */ "swapoff %s", udisks_block_device_get_device (block)); diff --git a/src/udisksspawnedjob.c b/src/udisksspawnedjob.c index a790cd0..41cbc6c 100644 --- a/src/udisksspawnedjob.c +++ b/src/udisksspawnedjob.c @@ -21,6 +21,7 @@ #include "config.h" #include <glib/gi18n-lib.h> +#include <stdio.h> #include <sys/types.h> #include <sys/wait.h> #include <string.h> @@ -60,7 +61,8 @@ struct _UDisksSpawnedJob GMainContext *main_context; gchar *input_string; - uid_t run_as; + uid_t run_as_uid; + uid_t run_as_euid; const gchar *input_string_cursor; GPid child_pid; @@ -99,7 +101,8 @@ enum PROP_0, PROP_COMMAND_LINE, PROP_INPUT_STRING, - PROP_RUN_AS + PROP_RUN_AS_UID, + PROP_RUN_AS_EUID }; enum @@ -184,8 +187,12 @@ 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); + case PROP_RUN_AS_UID: + job->run_as_uid = g_value_get_uint (value); + break; + + case PROP_RUN_AS_EUID: + job->run_as_euid = g_value_get_uint (value); break; default: @@ -358,22 +365,29 @@ 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; + gid_t egid; - if (job->run_as == getuid ()) + if (job->run_as_uid == getuid () && job->run_as_euid == geteuid ()) goto out; - pw = getpwuid (job->run_as); + pw = getpwuid (job->run_as_euid); + if (pw == NULL) + { + g_printerr ("No password record for uid %d: %m\n", (gint) job->run_as_euid); + abort (); + } + egid = pw->pw_gid; + + pw = getpwuid (job->run_as_uid); if (pw == NULL) { - g_printerr ("No password record for uid %d: %m\n", (gint) job->run_as); + g_printerr ("No password record for uid %d: %m\n", (gint) job->run_as_uid); abort (); } @@ -392,17 +406,20 @@ child_setup (gpointer user_data) } if (initgroups (pw->pw_name, pw->pw_gid) != 0) { - g_printerr ("Error initializing groups for uid %d: %m\n", (gint) job->run_as); + g_printerr ("Error initializing groups for user %s and group %d: %m\n", + pw->pw_name, (gint) pw->pw_gid); abort (); } - if (setregid (pw->pw_gid, pw->pw_gid) != 0) + if (setregid (pw->pw_gid, egid) != 0) { - g_printerr ("Error setting real+effective gid for uid %d: %m\n", (gint) job->run_as); + g_printerr ("Error setting real+effective gid %d and %d: %m\n", + (gint) pw->pw_gid, (gint) egid); abort (); } - if (setreuid (pw->pw_uid, pw->pw_uid) != 0) + if (setreuid (pw->pw_uid, job->run_as_euid) != 0) { - g_printerr ("Error setting real+effective uid for uid %d: %m\n", (gint) job->run_as); + g_printerr ("Error setting real+effective uid %d and %d: %m\n", + (gint) pw->pw_uid, (gint) job->run_as_euid); abort (); } @@ -565,13 +582,13 @@ udisks_spawned_job_class_init (UDisksSpawnedJobClass *klass) G_PARAM_STATIC_STRINGS)); /** - * UDisksSpawnedJob:run-as: + * UDisksSpawnedJob:run-as-uid: * * The #uid_t to run the program as. */ g_object_class_install_property (gobject_class, - PROP_RUN_AS, - g_param_spec_uint ("run-as", + PROP_RUN_AS_UID, + g_param_spec_uint ("run-as-uid", "Run As", "The uid_t to run the program as", 0, G_MAXUINT, 0, @@ -580,6 +597,21 @@ udisks_spawned_job_class_init (UDisksSpawnedJobClass *klass) G_PARAM_STATIC_STRINGS)); /** + * UDisksSpawnedJob:run-as-euid: + * + * The effective #uid_t to run the program as. + */ + g_object_class_install_property (gobject_class, + PROP_RUN_AS_EUID, + g_param_spec_uint ("run-as-euid", + "Run As (effective)", + "The effective 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. @@ -628,7 +660,8 @@ 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. + * @run_as_uid: The #uid_t to run the program as. + * @run_as_euid: The effective #uid_t to run the program as. * @cancellable: A #GCancellable or %NULL. * * Creates a new #UDisksSpawnedJob instance. @@ -642,7 +675,8 @@ udisks_spawned_job_class_init (UDisksSpawnedJobClass *klass) UDisksSpawnedJob * udisks_spawned_job_new (const gchar *command_line, const gchar *input_string, - uid_t run_as, + uid_t run_as_uid, + uid_t run_as_euid, GCancellable *cancellable) { g_return_val_if_fail (command_line != NULL, NULL); @@ -650,7 +684,8 @@ 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, + "run-as-uid", run_as_uid, + "run-as-euid", run_as_euid, "cancellable", cancellable, NULL)); } diff --git a/src/udisksspawnedjob.h b/src/udisksspawnedjob.h index 547ee26..f14e876 100644 --- a/src/udisksspawnedjob.h +++ b/src/udisksspawnedjob.h @@ -32,7 +32,8 @@ 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, + uid_t run_as_uid, + uid_t run_as_euid, GCancellable *cancellable); const gchar *udisks_spawned_job_get_command_line (UDisksSpawnedJob *job); |