Allow mounting/unmounting fstab devices without the 'user' or 'users' option
authorDavid Zeuthen <davidz@redhat.com>
Fri, 12 Aug 2011 16:40:17 +0000 (12:40 -0400)
committerDavid Zeuthen <davidz@redhat.com>
Fri, 12 Aug 2011 16:40:17 +0000 (12:40 -0400)
... 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>
13 files changed:
data/org.freedesktop.UDisks2.xml
policy/org.freedesktop.udisks2.policy.in
src/tests/test.c
src/udiskscleanup.c
src/udisksdaemon.c
src/udisksdaemon.h
src/udiskslinuxdrive.c
src/udiskslinuxencrypted.c
src/udiskslinuxfilesystem.c
src/udiskslinuxloop.c
src/udiskslinuxswapspace.c
src/udisksspawnedjob.c
src/udisksspawnedjob.h

index 4a5b21c..012791a 100644 (file)
         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
         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}"/>
index 8e61608..a27b6b2 100644 (file)
     </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>
index 0964e12..1855deb 100644 (file)
@@ -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);
index dccb057..6084340 100644 (file)
@@ -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\"",
index 4532746..47c48c2 100644 (file)
@@ -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
index 56bbdaf..80805f1 100644 (file)
@@ -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,
index 16c54d3..9fd1b5e 100644 (file)
@@ -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\"",
index f6cfdff..1d2fed8 100644 (file)
@@ -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\"",
index f152f1d..72d0a7a 100644 (file)
@@ -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),
index 27f243b..61d42f6 100644 (file)
@@ -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\"",
index 43bda2f..32ae054 100644 (file)
@@ -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));
index a790cd0..41cbc6c 100644 (file)
@@ -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));
 }
index 547ee26..f14e876 100644 (file)
@@ -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);