[gnome-boxes/gnome-3-20] unattended-setup-box: Don't cache passwords in plain text



commit 3f71ec5cda4e3ad810c1cc50a1fd3e66324b0e17
Author: Debarshi Ray <debarshir gnome org>
Date:   Tue Jan 24 17:51:44 2017 +0100

    unattended-setup-box: Don't cache passwords in plain text
    
    The user password specified during an express installation is cached
    in plain text in ~/.config/gnome-boxes/unattended/setup-data.conf.
    Instead, we should use the keyring - it's meant for this. As long as
    the host user account has a password, the keyring will be encrypted
    and protected in case the disk is stolen.
    
    We store the password as a serialized GVariant dictionary because it
    is more extensible than a simple string. In future we might want to
    store multiple secrets for a particular media (eg., registration
    passwords, product keys, etc.), and those can be accommodated by
    specifying a different key in the dictionary.
    
    Fallout from ae21e562515ab60d3279f182313e9a6c7be53abd
    
    https://bugzilla.gnome.org/show_bug.cgi?id=777788

 data/ui/unattended-setup-box.ui |    1 +
 src/unattended-setup-box.vala   |   84 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 82 insertions(+), 3 deletions(-)
---
diff --git a/data/ui/unattended-setup-box.ui b/data/ui/unattended-setup-box.ui
index 3f12160..3d11722 100644
--- a/data/ui/unattended-setup-box.ui
+++ b/data/ui/unattended-setup-box.ui
@@ -189,6 +189,7 @@
                     <property name="text"></property>
                     <property name="visible">True</property>
                     <property name="visibility">False</property>
+                    <signal name="changed" handler="on_password_entry_changed"/>
                     <signal name="focus-out-event" handler="on_password_entry_focus_out"/>
                     <signal name="activate" handler="on_password_entry_activated"/>
                   </object>
diff --git a/src/unattended-setup-box.vala b/src/unattended-setup-box.vala
index b5d6967..61d70a8 100644
--- a/src/unattended-setup-box.vala
+++ b/src/unattended-setup-box.vala
@@ -97,7 +97,12 @@ private class Boxes.UnattendedSetupBox : Gtk.Box {
 
     private string? product_key_format;
     private string media_path;
+    private Cancellable cancellable = new Cancellable ();
     private GLib.KeyFile keyfile;
+    private Secret.Schema secret_password_schema
+            = new Secret.Schema ("org.gnome.Boxes",
+                                 Secret.SchemaFlags.NONE,
+                                 "gnome-boxes-media-path", Secret.SchemaAttributeType.STRING);
 
     public UnattendedSetupBox (InstallerMedia media, string? product_key_format, bool needs_internet) {
         this.product_key_format = product_key_format;
@@ -114,9 +119,45 @@ private class Boxes.UnattendedSetupBox : Gtk.Box {
 
             set_entry_text_from_key (username_entry, USERNAME_KEY, Environment.get_user_name ());
             set_entry_text_from_key (password_entry, PASSWORD_KEY);
-            if (password != "")
-                password_notebook.next_page ();
             set_entry_text_from_key (product_key_entry, PRODUCTKEY_KEY);
+
+            if (password != "") {
+                password_notebook.next_page ();
+            } else {
+                Secret.password_lookup.begin (secret_password_schema, cancellable, (obj, res) => {
+                    try {
+                        var credentials_str = Secret.password_lookup.end (res);
+                        if (credentials_str == null || credentials_str == "")
+                            return;
+
+                        try {
+                            var credentials_variant = GLib.Variant.parse (null, credentials_str, null, null);
+                            string password_str;
+                            if (!credentials_variant.lookup ("password", "s", out password_str))
+                                throw new Boxes.Error.INVALID ("couldn't unpack a string for the 'password' 
key");
+
+                            if (password_str != null && password_str != "") {
+                                password_entry.text = password_str;
+                                password_notebook.next_page ();
+                            }
+                        } catch (GLib.Error error) {
+                            warning ("Failed to parse password from the keyring: %s", error.message);
+                        }
+                    } catch (GLib.IOError.CANCELLED error) {
+                        return;
+                    } catch (GLib.Error error) {
+                        warning ("Failed to lookup password for '%s' from the keyring: %s",
+                                 media_path,
+                                 error.message);
+                    }
+                }, "gnome-boxes-media-path", media_path);
+            }
+
+            try {
+                keyfile.remove_key (media_path, PASSWORD_KEY);
+            } catch (GLib.Error error) {
+                debug ("Failed to remove key '%s' under '%s': %s", PASSWORD_KEY, media_path, error.message);
+            }
         } catch (GLib.Error error) {
             debug ("%s either doesn't already exist or we failed to load it: %s", KEY_FILE, error.message);
         }
@@ -135,6 +176,15 @@ private class Boxes.UnattendedSetupBox : Gtk.Box {
                 express_toggle.bind_property ("active", child, "sensitive", BindingFlags.SYNC_CREATE);
     }
 
+    public override void dispose () {
+        if (cancellable != null) {
+            cancellable.cancel ();
+            cancellable = null;
+        }
+
+        base.dispose ();
+    }
+
     public void clean_up () {
         NetworkMonitor.get_default ().network_changed.disconnect (update_express_toggle);
     }
@@ -142,7 +192,6 @@ private class Boxes.UnattendedSetupBox : Gtk.Box {
     public void save_settings () {
         keyfile.set_boolean (media_path, EXPRESS_KEY, express_install);
         keyfile.set_string (media_path, USERNAME_KEY, username);
-        keyfile.set_string (media_path, PASSWORD_KEY, password);
         keyfile.set_string (media_path, PRODUCTKEY_KEY, product_key);
 
         var filename = get_user_unattended (KEY_FILE);
@@ -151,6 +200,29 @@ private class Boxes.UnattendedSetupBox : Gtk.Box {
         } catch (GLib.Error error) {
             debug ("Error saving settings for '%s': %s", media_path, error.message);
         }
+
+        if (password != null && password != "") {
+            var variant_builder = new GLib.VariantBuilder (GLib.VariantType.VARDICT);
+            var password_variant = new GLib.Variant ("s", password);
+            variant_builder.add ("{sv}", "password", password_variant);
+
+            var credentials_variant = variant_builder.end ();
+            var credentials_str = credentials_variant.print (true);
+
+            var label = _("GNOME Boxes credentials for '%s'").printf (media_path);
+            Secret.password_store.begin (secret_password_schema,
+                                         Secret.COLLECTION_DEFAULT,
+                                         label,
+                                         credentials_str,
+                                         null,
+                                         (obj, res) => {
+                try {
+                    Secret.password_store.end (res);
+                } catch (GLib.Error error) {
+                    warning ("Failed to store password for '%s' in the keyring: %s", media_path, 
error.message);
+                }
+            }, "gnome-boxes-media-path", media_path);
+        }
     }
 
     private void setup_express_toggle (bool live, bool needs_internet) {
@@ -212,6 +284,12 @@ private class Boxes.UnattendedSetupBox : Gtk.Box {
     }
 
     [GtkCallback]
+    private void on_password_entry_changed () {
+        cancellable.cancel ();
+        cancellable = new Cancellable ();
+    }
+
+    [GtkCallback]
     private bool on_password_entry_focus_out () {
         if (password_entry.text_length == 0)
             password_notebook.prev_page ();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]