[Libguestfs] [libguestfs-common PATCH 1/2] options: extract & refactor decryption of mountables (partitions)
Laszlo Ersek
lersek at redhat.com
Thu Feb 24 11:44:13 UTC 2022
On 02/24/22 11:35, Richard W.M. Jones wrote:
> On Wed, Feb 23, 2022 at 05:19:14PM +0100, Laszlo Ersek wrote:
>> The inspect_do_decrypt() function interates over the list of partitions.
>> For each partition, it fetches the potentially matching keys, and tries
>> each key in turn. If no key unlocks the partition, the program exits with
>> an error message. If at least one partition is unlocked, then LVM is
>> rescanned.
>>
>> Decouple "partition" from the above logic, replacing it with "mountable".
>> Call the new function decrypt_mountables(). As a part of the extraction,
>> clean up the following readability warts:
>>
>> - CLEANUP_FREE* and other variable declarations intermixed with code,
>>
>> - a "goto" statement that is not used on an error path,
>>
>> - needless conditions on a one-shot "is_bitlocker" helper variable,
>>
>> - nondescript array indices,
>>
>> - guestfs_int_count_strings() called for counting the length of the full
>> string list, only to check if the string list is non-empty,
>>
>> - a comment about GUESTFS_CRYPTSETUP_OPEN_READONLY placed outside of
>> GUESTFS_HAVE_CRYPTSETUP_OPEN.
>>
>> Regression-checked with:
>> - libguestfs/tests/luks/test-key-option-inspect.sh
>> - guestfs-tools/inspector/test-virt-inspector-luks.sh
>>
>> Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1658126
>> Signed-off-by: Laszlo Ersek <lersek at redhat.com>
>> ---
>> options/decrypt.c | 128 +++++++++++++++++++++++++++-------------------
>> 1 file changed, 75 insertions(+), 53 deletions(-)
>>
>> diff --git a/options/decrypt.c b/options/decrypt.c
>> index 434b7d58c2a7..9141cf5193ad 100644
>> --- a/options/decrypt.c
>> +++ b/options/decrypt.c
>> @@ -65,6 +65,78 @@ make_mapname (const char *device, char *mapname, size_t len)
>> *mapname = '\0';
>> }
>>
>> +static bool
>> +decrypt_mountables (guestfs_h *g, const char * const *mountables,
>> + struct key_store *ks)
>> +{
>> + bool decrypted_some = false;
>> + const char * const *mnt_scan = mountables;
>> + const char *mountable;
>> +
>> + while ((mountable = *mnt_scan++) != NULL) {
>> + CLEANUP_FREE char *type = NULL;
>> + CLEANUP_FREE char *uuid = NULL;
>> + CLEANUP_FREE_STRING_LIST char **keys = NULL;
>> + char mapname[32];
>> + const char * const *key_scan;
>> + const char *key;
>> +
>> + type = guestfs_vfs_type (g, mountable);
>> + if (type == NULL)
>> + continue;
>> +
>> + /* "cryptsetup luksUUID" cannot read a UUID on Windows BitLocker disks
>> + * (unclear if this is a limitation of the format or cryptsetup).
>> + */
>> + if (STREQ (type, "crypto_LUKS")) {
>> +#ifdef GUESTFS_HAVE_LUKS_UUID
>> + uuid = guestfs_luks_uuid (g, mountable);
>> +#endif
>
> As a separate patch after this one, I don't mind getting rid of this
> #ifdef conditional, and also the GUESTFS_HAVE_CRYPTSETUP_OPEN
> conditional below.
>
> guestfs_luks_uuid was added in libguestfs 1.42 (released March 2020).
> guestfs_cryptsetup_open was added in libguestfs 1.44 (Jan 2021).
>
> guestfs-tools already requires libguestfs >= 1.44 (see
> guestfs-tools.git/m4/guestfs-libraries.m4). So that's OK.
>
> virt-v2v in theory requires only libguestfs >= 1.40, but I don't think
> there's any problem with requiring 1.44. It already requires a very
> recent libnbd. virt-v2v.git/m4/guestfs-libraries.m4 would need to be
> changed.
Thanks, those #ifdefs did look stale to me. The cleanups should go into
a separate patch; I'll look into that.
>
>> + } else if (STRNEQ (type, "BitLocker"))
>> + continue;
>> +
>> + /* Grab the keys that we should try with this device, based on device name,
>> + * or UUID (if any).
>> + */
>> + keys = get_keys (ks, mountable, uuid);
>> + assert (keys[0] != NULL);
>> +
>> + /* Generate a node name for the plaintext (decrypted) device node. */
>> + make_mapname (mountable, mapname, sizeof mapname);
>> +
>> + /* Try each key in turn. */
>> + key_scan = (const char * const *)keys;
>> + while ((key = *key_scan++) != NULL) {
>> + int r;
>> +
>> + guestfs_push_error_handler (g, NULL, NULL);
>> +#ifdef GUESTFS_HAVE_CRYPTSETUP_OPEN
>> + /* XXX Should we set GUESTFS_CRYPTSETUP_OPEN_READONLY if readonly is
>> + * set? This might break 'mount_ro'.
>> + */
>
> This XXX comment (copied from the old code) is bogus. It would
> definitely be wrong to use OPEN_READONLY. In r/o mode we defend
> against modifying the underlying devices using a qcow2 overlay
> (lib/drives.c:create_overlay). Writable guest devices are required to
> mount filesystems with journals, even when mounting with -o ro.
>
> So you might drop it, maybe as a separate commit.
Will do (separate commit indeed).
>
>> + r = guestfs_cryptsetup_open (g, mountable, key, mapname, -1);
>> +#else
>> + r = guestfs_luks_open (g, mountable, key, mapname);
>> +#endif
>> + guestfs_pop_error_handler (g);
>> +
>> + if (r == 0)
>> + break;
>> + }
>> +
>> + if (key == NULL)
>> + error (EXIT_FAILURE, 0,
>> + _("could not find key to open LUKS encrypted %s.\n\n"
>> + "Try using --key on the command line.\n\n"
>> + "Original error: %s (%d)"),
>> + mountable, guestfs_last_error (g), guestfs_last_errno (g));
>> +
>> + decrypted_some = true;
>> + }
>> +
>> + return decrypted_some;
>> +}
>> +
>> /**
>> * Simple implementation of decryption: look for any encrypted
>> * partitions and decrypt them, then rescan for VGs.
>> @@ -73,62 +145,12 @@ void
>> inspect_do_decrypt (guestfs_h *g, struct key_store *ks)
>> {
>> CLEANUP_FREE_STRING_LIST char **partitions = guestfs_list_partitions (g);
>> + bool need_rescan;
>> +
>> if (partitions == NULL)
>> exit (EXIT_FAILURE);
>>
>> - int need_rescan = 0, r;
>> - size_t i, j;
>> -
>> - for (i = 0; partitions[i] != NULL; ++i) {
>> - CLEANUP_FREE char *type = guestfs_vfs_type (g, partitions[i]);
>> - if (type &&
>> - (STREQ (type, "crypto_LUKS") || STREQ (type, "BitLocker"))) {
>> - bool is_bitlocker = STREQ (type, "BitLocker");
>> - char mapname[32];
>> - make_mapname (partitions[i], mapname, sizeof mapname);
>> -
>> -#ifdef GUESTFS_HAVE_LUKS_UUID
>> - CLEANUP_FREE char *uuid = NULL;
>> -
>> - /* This fails for Windows BitLocker disks because cryptsetup
>> - * luksUUID cannot read a UUID (unclear if this is a limitation
>> - * of the format or cryptsetup).
>> - */
>> - if (!is_bitlocker)
>> - uuid = guestfs_luks_uuid (g, partitions[i]);
>> -#else
>> - const char *uuid = NULL;
>> -#endif
>> -
>> - CLEANUP_FREE_STRING_LIST char **keys = get_keys (ks, partitions[i], uuid);
>> - assert (guestfs_int_count_strings (keys) > 0);
>> -
>> - /* Try each key in turn. */
>> - for (j = 0; keys[j] != NULL; ++j) {
>> - /* XXX Should we set GUESTFS_CRYPTSETUP_OPEN_READONLY if readonly
>> - * is set? This might break 'mount_ro'.
>> - */
>> - guestfs_push_error_handler (g, NULL, NULL);
>> -#ifdef GUESTFS_HAVE_CRYPTSETUP_OPEN
>> - r = guestfs_cryptsetup_open (g, partitions[i], keys[j], mapname, -1);
>> -#else
>> - r = guestfs_luks_open (g, partitions[i], keys[j], mapname);
>> -#endif
>> - guestfs_pop_error_handler (g);
>> - if (r == 0)
>> - goto opened;
>> - }
>> - error (EXIT_FAILURE, 0,
>> - _("could not find key to open LUKS encrypted %s.\n\n"
>> - "Try using --key on the command line.\n\n"
>> - "Original error: %s (%d)"),
>> - partitions[i], guestfs_last_error (g),
>> - guestfs_last_errno (g));
>> -
>> - opened:
>> - need_rescan = 1;
>> - }
>> - }
>> + need_rescan = decrypt_mountables (g, (const char * const *)partitions, ks);
>>
>> if (need_rescan) {
>> if (guestfs_lvm_scan (g, 1) == -1)
>
> Patch looks fine, ACK.
Thanks!
Laszlo
More information about the Libguestfs
mailing list