From 83abcd8873006fcccb341ae3b8c49cefea57fddf Mon Sep 17 00:00:00 2001 From: Mike Fleetwood Date: Fri, 26 Mar 2021 21:16:06 +0000 Subject: [PATCH] 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