[gparted] Ask for LUKS passphrase for resize operation as required (#59)



commit 83abcd8873006fcccb341ae3b8c49cefea57fddf
Author: Mike Fleetwood <mike fleetwood googlemail com>
Date:   Fri Mar 26 21:16:06 2021 +0000

    Ask for LUKS passphrase for resize operation as required (#59)
    
    When composing a resize operation on an open encryption mapping, use the
    existing LUKS password dialog to prompt for the passphrase, if and only
    if 'cryptsetup resize' will prompt and GParted doesn't already have a
    password.  'cryptsetup resize' will prompt for a LUKS passphrase when
    the passphrase was stored in the kernel keyring service,
    key_loc == KEYLOC_KeyRing.  See the previous commit "Capture LUKS
    mapping master encryption key location (#59)" for more details.
    
    As commented in the code GParted specifically doesn't support the case
    where the LUKS passphrase is changed while GParted is running and it
    knew the old passphrase.  When resizing an open encryption mapping
    GParted will just pass the old out of date passphrase it knows and the
    resize will fail like this:
    
        # cryptsetup status sdb2_crypt | egrep 'type|key location'
          type:         LUKS2
          key location: keyring
        # dmsetup table --target crypt
        sdb2_crypt: 0 491520 crypt aes-xts-plain64 
:64:logon:cryptsetup:3d040240-97ba-4559-af98-72c3be500498-d0 0 8:18 32768
    
        # echo -n oldpassword | cryptsetup -v --size 491520 resize sdb2_crypt
        No key available with this passphrase.
        Command failed with code -2 (no permission or bad passphrase).
        # echo $?
        2
    
    To work around this either close and restart GParted or close and reopen
    the encryption mapping.  The former because GParted doesn't save
    passwords across a restart so will prompt and the latter because GParted
    will use the wrong old passphrase to try to open the mapping and then
    prompt for the correct passphrase until successfully opened.
    
    Closes #59 - Resize of LUKS2 encrypted file system fails with "Nothing
                 to read on input"

 include/Win_GParted.h |  1 +
 src/Win_GParted.cc    | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 80 insertions(+), 1 deletion(-)
---
diff --git a/include/Win_GParted.h b/include/Win_GParted.h
index 40a25599..9515357c 100644
--- a/include/Win_GParted.h
+++ b/include/Win_GParted.h
@@ -176,6 +176,7 @@ private:
        bool max_amount_prim_reached() ;
        
        void activate_resize(); 
+       bool ask_for_password_for_encrypted_resize_as_required(const Partition& partition);
        void activate_copy();
        void activate_paste();
        void activate_new();
diff --git a/src/Win_GParted.cc b/src/Win_GParted.cc
index dc7c895d..0789ead9 100644
--- a/src/Win_GParted.cc
+++ b/src/Win_GParted.cc
@@ -45,6 +45,7 @@
 #include "PartitionVector.h"
 #include "PasswordRAMStore.h"
 #include "LVM2_PV_Info.h"
+#include "LUKS_Info.h"
 #include "Utils.h"
 #include "../config.h"
 
@@ -2038,7 +2039,8 @@ void Win_GParted::activate_resize()
        delete working_ptn;
        working_ptn = NULL;
 
-       if ( dialog .run() == Gtk::RESPONSE_OK )
+       if (dialog.run() == Gtk::RESPONSE_OK                                           &&
+           ask_for_password_for_encrypted_resize_as_required(*selected_partition_ptr)   )
        {
                dialog .hide() ;
 
@@ -2107,6 +2109,82 @@ void Win_GParted::activate_resize()
        show_operationslist() ;
 }
 
+
+bool Win_GParted::ask_for_password_for_encrypted_resize_as_required(const Partition& partition)
+{
+       if (partition.fstype != FS_LUKS || ! partition.busy)
+               // Not active LUKS so won't need a password.
+               return true;
+
+       LUKS_Mapping mapping = LUKS_Info::get_cache_entry(partition.get_path());
+       if (mapping.name.empty() || mapping.key_loc == KEYLOC_DMCrypt)
+               // LUKS volume key stored in crypt Device-Mapper target so won't require a
+               // password for encryption mapping resize.
+               return true;
+
+       const char *pw = PasswordRAMStore::lookup(partition.uuid);
+       if (pw != NULL)
+               // GParted already has a password for this encryption mapping which was
+               // previously used successfully or tested for correctness.
+               //
+               // The password will still be correct, unless it was changed by someone
+               // outside GParted while running and since the last time the password was
+               // used.  Re-testing the password takes 2-3 seconds which would pause the
+               // UI after the [Resize/Move] button was pressed in the Resize/Move dialog
+               // but before the dialog closes.  With no trivial way to provide feedback
+               // that the password is being re-tested, don't spend coding effort to
+               // support this use case.  So just assume the known password is still
+               // correct and don't re-prompt when it will be correct 99.9% of the time.
+               return true;
+
+       DialogPasswordEntry dialog(partition);
+       dialog.set_transient_for(*this);
+       bool success = false;
+       do
+       {
+               if (dialog.run() != Gtk::RESPONSE_OK)
+                       // Password dialog cancelled or closed without having confirmed
+                       // the LUKS mapping passphrase.
+                       return false;
+
+               pw = dialog.get_password();
+               if (strlen(pw) == 0)
+               {
+                       // cryptsetup won't accept a zero length password.
+                       dialog.set_error_message("");
+                       continue;
+               }
+
+               // Create LUKS mapping name from partition name:
+               // "/dev/sdb1" -> "sdb1_crypt"
+               Glib::ustring mapping_name = selected_partition_ptr->get_path();
+               Glib::ustring::size_type last_slash = mapping_name.rfind("/");
+               if (last_slash != Glib::ustring::npos)
+                       mapping_name = mapping_name.substr(last_slash + 1);
+               mapping_name += "_crypt";
+
+               // Test the password can open the encryption mapping.
+               Glib::ustring cmd = "cryptsetup luksOpen --test-passphrase " +
+                                   Glib::shell_quote(partition.get_path()) + " " +
+                                   Glib::shell_quote(mapping_name);
+               Glib::ustring output;
+               Glib::ustring error;
+               success = ! Utils::execute_command(cmd, pw, output, error);
+
+               Glib::ustring message = (success) ? "" : _("LUKS encryption passphrase check failed");
+               dialog.set_error_message(message);
+       }
+       while (! success);
+
+       // Save the password just entered and successfully tested on the LUKS mapping.
+       PasswordRAMStore::store(partition.uuid, pw);
+
+       dialog.hide();
+
+       return true;
+}
+
+
 void Win_GParted::activate_copy()
 {
        g_assert( selected_partition_ptr != NULL );  // Bug: Partition callback without a selected partition


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