c506382
From 79f0ea7c0d5cb39cf1ab40afaea0e485aeb4bc49 Mon Sep 17 00:00:00 2001
c506382
From: David Zeuthen <davidz@redhat.com>
c506382
Date: Thu, 26 Apr 2012 14:35:37 -0400
c506382
Subject: [PATCH 7/9] udisks2: Support getting/storing LUKS encryption passphrase from keyring
c506382
c506382
Also use a nicer message when asking for the passphrase, e.g. use strings like
c506382
c506382
 'SD04G (SD Card Reader)'
c506382
 'WD 2500JB External (250 GB Hard Disk)'
c506382
c506382
instead of /dev/mmcblk0p1 or /dev/sdb1. If stored in the keyring, we
c506382
also use the following display name
c506382
c506382
 'Encryption passphrase for $DRIVE'
c506382
c506382
where $DRIVE is of the above form. This makes it easy for the user to
c506382
manage (for example, delete) pass-phrases using seahorse(1).
c506382
c506382
This was discussed in bug 674161.
c506382
c506382
 https://bugzilla.gnome.org/show_bug.cgi?id=674161
c506382
c506382
This commit adds/changes translatable strings but Tomas said he would
c506382
soon branch gvfs for gnome-3-at a point before this patch.
c506382
c506382
Signed-off-by: David Zeuthen <davidz@redhat.com>
c506382
---
c506382
 monitor/udisks2/Makefile.am         |    2 +
c506382
 monitor/udisks2/gvfsudisks2volume.c |  225 ++++++++++++++++++++++++++++++++++-
c506382
 2 files changed, 221 insertions(+), 6 deletions(-)
c506382
c506382
diff --git a/monitor/udisks2/Makefile.am b/monitor/udisks2/Makefile.am
c506382
index 776f670..c5ddd7c 100644
c506382
--- a/monitor/udisks2/Makefile.am
c506382
+++ b/monitor/udisks2/Makefile.am
c506382
@@ -20,6 +20,7 @@ gvfs_udisks2_volume_monitor_CFLAGS =		\
c506382
 	$(UDISKS2_CFLAGS)                       \
c506382
 	$(GUDEV_CFLAGS)                         \
c506382
 	$(LIBSYSTEMD_LOGIN_CFLAGS)		\
c506382
+	$(KEYRING_CFLAGS)			\
c506382
 	-DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\"	\
c506382
 	-DGVFS_LOCALEDIR=\""$(localedir)"\"	\
c506382
 	-DG_DISABLE_DEPRECATED			\
c506382
@@ -34,6 +35,7 @@ gvfs_udisks2_volume_monitor_LDADD  =		     			      	\
c506382
 	$(UDISKS2_LIBS)                                  			\
c506382
 	$(GUDEV_LIBS)                                  			      	\
c506382
 	$(LIBSYSTEMD_LOGIN_LIBS)						\
c506382
+	$(KEYRING_LIBS)								\
c506382
 	$(top_builddir)/common/libgvfscommon.la 			      	\
c506382
 	$(top_builddir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la 	\
c506382
 	$(NULL)
c506382
diff --git a/monitor/udisks2/gvfsudisks2volume.c b/monitor/udisks2/gvfsudisks2volume.c
c506382
index 3ef5895..bb5c687 100644
c506382
--- a/monitor/udisks2/gvfsudisks2volume.c
c506382
+++ b/monitor/udisks2/gvfsudisks2volume.c
c506382
@@ -31,6 +31,10 @@
c506382
 #include <glib/gi18n-lib.h>
c506382
 #include <gio/gio.h>
c506382
 
c506382
+#ifdef HAVE_KEYRING
c506382
+#include <gnome-keyring.h>
c506382
+#endif
c506382
+
c506382
 #include "gvfsudisks2drive.h"
c506382
 #include "gvfsudisks2volume.h"
c506382
 #include "gvfsudisks2mount.h"
c506382
@@ -756,6 +760,17 @@ gvfs_udisks2_volume_get_activation_root (GVolume *_volume)
c506382
 
c506382
 /* ---------------------------------------------------------------------------------------------------- */
c506382
 
c506382
+#ifdef HAVE_KEYRING
c506382
+static GnomeKeyringPasswordSchema luks_passphrase_schema =
c506382
+{
c506382
+  GNOME_KEYRING_ITEM_GENERIC_SECRET,
c506382
+  {
c506382
+    {"gvfs-luks-uuid", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING},
c506382
+    {NULL, 0}
c506382
+  }
c506382
+};
c506382
+#endif
c506382
+
c506382
 struct MountData
c506382
 {
c506382
   GSimpleAsyncResult *simple;
c506382
@@ -769,9 +784,15 @@ struct MountData
c506382
 
c506382
   gchar *passphrase;
c506382
 
c506382
+  gchar *passphrase_from_keyring;
c506382
+  GPasswordSave password_save;
c506382
+
c506382
+  gchar *uuid_of_encrypted_to_unlock;
c506382
+  gchar *desc_of_encrypted_to_unlock;
c506382
   UDisksEncrypted *encrypted_to_unlock;
c506382
   UDisksFilesystem *filesystem_to_mount;
c506382
 
c506382
+  gboolean checked_keyring;
c506382
 };
c506382
 
c506382
 static void
c506382
@@ -795,7 +816,10 @@ mount_data_free (MountData *data)
c506382
     }
c506382
 
c506382
   g_free (data->passphrase);
c506382
+  g_free (data->passphrase_from_keyring);
c506382
 
c506382
+  g_free (data->uuid_of_encrypted_to_unlock);
c506382
+  g_free (data->desc_of_encrypted_to_unlock);
c506382
   g_clear_object (&data->encrypted_to_unlock);
c506382
   g_clear_object (&data->filesystem_to_mount);
c506382
   g_free (data);
c506382
@@ -912,6 +936,62 @@ do_mount (MountData *data)
c506382
 
c506382
 /* ------------------------------ */
c506382
 
c506382
+#ifdef HAVE_KEYRING
c506382
+static void
c506382
+luks_store_passphrase_cb (GnomeKeyringResult result,
c506382
+                          gpointer           user_data)
c506382
+{
c506382
+  MountData *data = user_data;
c506382
+  if (result == GNOME_KEYRING_RESULT_OK)
c506382
+    {
c506382
+      /* everything is good */
c506382
+      do_mount (data);
c506382
+    }
c506382
+  else
c506382
+    {
c506382
+      /* report failure */
c506382
+      g_simple_async_result_set_error (data->simple,
c506382
+                                       G_IO_ERROR,
c506382
+                                       G_IO_ERROR_FAILED,
c506382
+                                       _("Error storing passphrase in keyring (error code %d)"),
c506382
+                                       result);
c506382
+      g_simple_async_result_complete (data->simple);
c506382
+      mount_data_free (data);
c506382
+    }
c506382
+}
c506382
+#endif
c506382
+
c506382
+
c506382
+static void do_unlock (MountData *data);
c506382
+
c506382
+
c506382
+#ifdef HAVE_KEYRING
c506382
+static void
c506382
+luks_delete_passphrase_cb (GnomeKeyringResult result,
c506382
+                           gpointer           user_data)
c506382
+{
c506382
+  MountData *data = user_data;
c506382
+  if (result == GNOME_KEYRING_RESULT_OK)
c506382
+    {
c506382
+      /* with the bad passphrase out of the way, try again */
c506382
+      g_free (data->passphrase);
c506382
+      data->passphrase = NULL;
c506382
+      do_unlock (data);
c506382
+    }
c506382
+  else
c506382
+    {
c506382
+      /* report failure */
c506382
+      g_simple_async_result_set_error (data->simple,
c506382
+                                       G_IO_ERROR,
c506382
+                                       G_IO_ERROR_FAILED,
c506382
+                                       _("Error deleting invalid passphrase from keyring (error code %d)"),
c506382
+                                       result);
c506382
+      g_simple_async_result_complete (data->simple);
c506382
+      mount_data_free (data);
c506382
+    }
c506382
+}
c506382
+#endif
c506382
+
c506382
 static void
c506382
 unlock_cb (GObject       *source_object,
c506382
            GAsyncResult  *res,
c506382
@@ -927,6 +1007,26 @@ unlock_cb (GObject       *source_object,
c506382
                                             res,
c506382
                                             &error))
c506382
     {
c506382
+#ifdef HAVE_KEYRING
c506382
+      /* If this failed with a passphrase read from the keyring, try again
c506382
+       * this time prompting the user...
c506382
+       *
c506382
+       * TODO: ideally check against something like UDISKS_ERROR_PASSPHRASE_INVALID
c506382
+       * when such a thing is available in udisks
c506382
+       */
c506382
+      if (data->passphrase_from_keyring != NULL &&
c506382
+          g_strcmp0 (data->passphrase, data->passphrase_from_keyring) == 0)
c506382
+        {
c506382
+          /* nuke the invalid passphrase from keyring... */
c506382
+          gnome_keyring_delete_password (&luks_passphrase_schema,
c506382
+                                         luks_delete_passphrase_cb,
c506382
+                                         data,
c506382
+                                         NULL, /* GDestroyNotify */
c506382
+                                         "gvfs-luks-uuid", data->uuid_of_encrypted_to_unlock,
c506382
+                                         NULL); /* sentinel */
c506382
+          goto out;
c506382
+        }
c506382
+#endif
c506382
       gvfs_udisks2_utils_udisks_error_to_gio_error (error);
c506382
       g_simple_async_result_take_error (data->simple, error);
c506382
       g_simple_async_result_complete (data->simple);
c506382
@@ -953,6 +1053,42 @@ unlock_cb (GObject       *source_object,
c506382
           goto out;
c506382
         }
c506382
 
c506382
+#ifdef HAVE_KEYRING
c506382
+      /* passphrase worked - save it in the keyring if requested */
c506382
+      if (data->password_save != G_PASSWORD_SAVE_NEVER)
c506382
+        {
c506382
+          const gchar *keyring;
c506382
+          gchar *display_name;
c506382
+
c506382
+          switch (data->password_save)
c506382
+            {
c506382
+            case G_PASSWORD_SAVE_NEVER:
c506382
+              g_assert_not_reached ();
c506382
+              break;
c506382
+            case G_PASSWORD_SAVE_FOR_SESSION:
c506382
+              keyring = GNOME_KEYRING_SESSION;
c506382
+              break;
c506382
+            case G_PASSWORD_SAVE_PERMANENTLY:
c506382
+              keyring = GNOME_KEYRING_DEFAULT;
c506382
+              break;
c506382
+            }
c506382
+
c506382
+          display_name = g_strdup_printf (_("Encryption passphrase for %s"),
c506382
+                                          data->desc_of_encrypted_to_unlock);
c506382
+
c506382
+          gnome_keyring_store_password (&luks_passphrase_schema,
c506382
+                                        keyring,
c506382
+                                        display_name,
c506382
+                                        data->passphrase,
c506382
+                                        luks_store_passphrase_cb,
c506382
+                                        data,
c506382
+                                        NULL, /* GDestroyNotify */
c506382
+                                        "gvfs-luks-uuid", data->uuid_of_encrypted_to_unlock,
c506382
+                                        NULL); /* sentinel */
c506382
+          goto out;
c506382
+        }
c506382
+#endif
c506382
+
c506382
       /* OK, ready to rock */
c506382
       do_mount (data);
c506382
     }
c506382
@@ -961,8 +1097,6 @@ unlock_cb (GObject       *source_object,
c506382
   g_free (cleartext_device);
c506382
 }
c506382
 
c506382
-static void do_unlock (MountData *data);
c506382
-
c506382
 static void
c506382
 on_mount_operation_reply (GMountOperation       *mount_operation,
c506382
                           GMountOperationResult result,
c506382
@@ -1005,6 +1139,9 @@ on_mount_operation_reply (GMountOperation       *mount_operation,
c506382
     }
c506382
 
c506382
   data->passphrase = g_strdup (g_mount_operation_get_password (mount_operation));
c506382
+  data->password_save = g_mount_operation_get_password_save (mount_operation);
c506382
+
c506382
+  /* Don't save password in keyring just yet - check if it works first */
c506382
 
c506382
   do_unlock (data);
c506382
 
c506382
@@ -1049,6 +1186,27 @@ has_crypttab_passphrase (MountData *data)
c506382
   return ret;
c506382
 }
c506382
 
c506382
+#ifdef HAVE_KEYRING
c506382
+static void
c506382
+luks_find_passphrase_cb (GnomeKeyringResult result,
c506382
+                         const gchar       *string,
c506382
+                         gpointer           user_data)
c506382
+{
c506382
+  MountData *data = user_data;
c506382
+
c506382
+  /* Don't fail if a keyring error occured - just continue and request
c506382
+   * the passphrase from the user...
c506382
+   */
c506382
+  if (result == GNOME_KEYRING_RESULT_OK)
c506382
+    {
c506382
+      data->passphrase = g_strdup (string);
c506382
+      data->passphrase_from_keyring = g_strdup (string);
c506382
+    }
c506382
+  /* try again */
c506382
+  do_unlock (data);
c506382
+}
c506382
+#endif
c506382
+
c506382
 static void
c506382
 do_unlock (MountData *data)
c506382
 {
c506382
@@ -1065,6 +1223,21 @@ do_unlock (MountData *data)
c506382
         {
c506382
           gchar *message;
c506382
 
c506382
+#ifdef HAVE_KEYRING
c506382
+          /* check if the passphrase is in the user's keyring */
c506382
+          if (!data->checked_keyring)
c506382
+            {
c506382
+              data->checked_keyring = TRUE;
c506382
+              gnome_keyring_find_password (&luks_passphrase_schema,
c506382
+                                           luks_find_passphrase_cb,
c506382
+                                           data,
c506382
+                                           NULL, /* GDestroyNotify */
c506382
+                                           "gvfs-luks-uuid", data->uuid_of_encrypted_to_unlock,
c506382
+                                           NULL); /* sentinel */
c506382
+              goto out;
c506382
+            }
c506382
+#endif
c506382
+
c506382
           if (data->mount_operation == NULL)
c506382
             {
c506382
               g_simple_async_result_set_error (data->simple,
c506382
@@ -1084,9 +1257,10 @@ do_unlock (MountData *data)
c506382
                                                                        "aborted",
c506382
                                                                        G_CALLBACK (on_mount_operation_aborted),
c506382
                                                                        data);
c506382
-          message = g_strdup_printf (_("Enter a password to unlock the volume\n"
c506382
-                                       "The device %s contains encrypted data."),
c506382
-                                     udisks_block_get_device (data->volume->block));
c506382
+          /* Translators: This is the message shown to users */
c506382
+          message = g_strdup_printf (_("Enter a passphrase to unlock the volume\n"
c506382
+                                       "The passphrase is needed to access encrypted data on %s."),
c506382
+                                     data->desc_of_encrypted_to_unlock);
c506382
 
c506382
           /* NOTE: We (currently) don't offer the user to save the
c506382
            * passphrase in the keyring or /etc/crypttab - compared to
c506382
@@ -1109,7 +1283,7 @@ do_unlock (MountData *data)
c506382
                                  NULL,
c506382
                                  NULL,
c506382
                                  G_ASK_PASSWORD_NEED_PASSWORD |
c506382
-                                 0/*G_ASK_PASSWORD_SAVING_SUPPORTED*/);
c506382
+                                 G_ASK_PASSWORD_SAVING_SUPPORTED);
c506382
           g_free (message);
c506382
           goto out;
c506382
         }
c506382
@@ -1209,6 +1383,45 @@ gvfs_udisks2_volume_mount (GVolume             *_volume,
c506382
       data->encrypted_to_unlock = udisks_object_get_encrypted (UDISKS_OBJECT (object));
c506382
       if (data->encrypted_to_unlock != NULL)
c506382
         {
c506382
+          UDisksDrive *udisks_drive;
c506382
+
c506382
+          /* This description is used in both the prompt and the display-name of
c506382
+           * the key stored in the user's keyring ...
c506382
+           *
c506382
+           * NOTE: we want a little bit more detail than what g_drive_get_name()
c506382
+           * gives us, since this is going to be used to refer to the device even
c506382
+           * when not plugged in
c506382
+           */
c506382
+          udisks_drive = udisks_client_get_drive_for_block (gvfs_udisks2_volume_monitor_get_udisks_client (volume->monitor),
c506382
+                                                            block);
c506382
+          if (udisks_drive != NULL)
c506382
+            {
c506382
+              gchar *drive_name;
c506382
+              gchar *drive_desc;
c506382
+              udisks_client_get_drive_info (gvfs_udisks2_volume_monitor_get_udisks_client (volume->monitor),
c506382
+                                            udisks_drive,
c506382
+                                            &drive_name,
c506382
+                                            &drive_desc,
c506382
+                                            NULL,  /* drive_icon */
c506382
+                                            NULL,  /* media_desc */
c506382
+                                            NULL); /* media_icon */
c506382
+              /* Translators: this is used to describe the drive the encrypted media
c506382
+               * is on - the first %s is the name (such as 'WD 2500JB External'), the
c506382
+               * second %s is the description ('250 GB Hard Disk').
c506382
+               */
c506382
+              data->desc_of_encrypted_to_unlock = g_strdup_printf (_("%s (%s)"),
c506382
+                                                                   drive_name,
c506382
+                                                                   drive_desc);
c506382
+              g_free (drive_desc);
c506382
+              g_free (drive_name);
c506382
+              g_object_unref (udisks_drive);
c506382
+            }
c506382
+          else
c506382
+            {
c506382
+              data->desc_of_encrypted_to_unlock = udisks_block_dup_preferred_device (block);
c506382
+            }
c506382
+          data->uuid_of_encrypted_to_unlock = udisks_block_dup_id_uuid (block);
c506382
+
c506382
           do_unlock (data);
c506382
           goto out;
c506382
         }
c506382
-- 
c506382
1.7.3.4
c506382