From 7e14d30d54fe1953fbbe7a775a07a83332668706 Mon Sep 17 00:00:00 2001 From: "Stepan \"Sciapan\" Yakimovich" Date: Sun, 17 Mar 2024 15:02:55 +0100 Subject: [PATCH] core: Add Encrypted.Convert method --- data/org.freedesktop.UDisks2.xml | 17 +++ doc/udisks2-sections.txt.in.in | 4 + src/tests/dbus-tests/test_70_encrypted.py | 50 ++++++++ src/udiskslinuxencrypted.c | 137 ++++++++++++++++++++++ udisks/udisksclient.c | 1 + 5 files changed, 209 insertions(+) diff --git a/data/org.freedesktop.UDisks2.xml b/data/org.freedesktop.UDisks2.xml index bc68c25bc..3575e0b61 100644 --- a/data/org.freedesktop.UDisks2.xml +++ b/data/org.freedesktop.UDisks2.xml @@ -2975,6 +2975,21 @@ + + + + + + + @@ -3396,6 +3411,8 @@ Modifying encrypted device. encrypted-resize Resizing encrypted device. + encrypted-convert + Converting encrypted device. swapspace-start Starting swapspace. swapspace-stop diff --git a/doc/udisks2-sections.txt.in.in b/doc/udisks2-sections.txt.in.in index 7e4187740..df219c5d9 100644 --- a/doc/udisks2-sections.txt.in.in +++ b/doc/udisks2-sections.txt.in.in @@ -691,6 +691,10 @@ udisks_encrypted_call_resize udisks_encrypted_call_resize_finish udisks_encrypted_call_resize_sync udisks_encrypted_complete_resize +udisks_encrypted_call_convert +udisks_encrypted_call_convert_finish +udisks_encrypted_call_convert_sync +udisks_encrypted_complete_convert UDisksEncryptedProxy UDisksEncryptedProxyClass udisks_encrypted_proxy_new diff --git a/src/tests/dbus-tests/test_70_encrypted.py b/src/tests/dbus-tests/test_70_encrypted.py index 319d42cc5..30259534b 100644 --- a/src/tests/dbus-tests/test_70_encrypted.py +++ b/src/tests/dbus-tests/test_70_encrypted.py @@ -33,6 +33,13 @@ def _get_blkid_version(): raise RuntimeError('Failed to determine blkid version from: %s' % out) return Version(m.groups()[0]) +def _get_luks_version(disk): + ret, out = udiskstestcase.UdisksTestCase.run_command("cryptsetup luksDump %s" % disk) + m = re.search(r'Version:\s*([1-2])', out) + if not m or len(m.groups()) != 1: + raise RuntimeError('Failed to determine LUKS version of device: %s.' % disk) + return int(m.groups()[0]) + class UdisksEncryptedTest(udiskstestcase.UdisksTestCase): '''This is an encrypted device test suite''' @@ -449,6 +456,24 @@ def _get_metadata_size_from_dump(self, disk): # offset value is in 512B blocks; we need to multiply to get the real metadata size return int(m.group(1)) * 512 + def test_convert(self): + disk = self.vdevs[0] + device = self.get_device(disk) + self._create_luks(device, self.PASSPHRASE) + self.assertEqual(1, _get_luks_version(disk)) + + self.addCleanup(self._remove_luks, device) + self.udev_settle() + device.Lock(self.no_options, dbus_interface=self.iface_prefix + '.Encrypted') + + device.Convert("luks2", self.no_options, + dbus_interface=self.iface_prefix + '.Encrypted') + self.assertEqual(2, _get_luks_version(disk)) + + device.Convert("luks1", self.no_options, + dbus_interface=self.iface_prefix + '.Encrypted') + self.assertEqual(1, _get_luks_version(disk)) + class UdisksEncryptedTestLUKS2(UdisksEncryptedTest): '''This is a LUKS2 encrypted device test suite''' @@ -559,6 +584,31 @@ def test_resize(self): clear_size3 = self.get_block_size(clear_dev) self.assertEqual(clear_size3, clear_size) + def test_convert_xfail(self): + disk = self.vdevs[0] + device = self.get_device(disk) + self._create_luks(device, self.PASSPHRASE) + self.assertEqual(2, _get_luks_version(disk)) + + self.addCleanup(self._remove_luks, device) + self.udev_settle() + device.Lock(self.no_options, dbus_interface=self.iface_prefix + '.Encrypted') + + # check that the device is not LUKS1 compatible + ret, out = udiskstestcase.UdisksTestCase.run_command("cryptsetup luksDump %s" % disk) + m = re.search(r'PBKDF:\s*(.*)', out) + if not m or len(m.groups()) != 1: + raise RuntimeError('Failed to determine PBKDF of LUKS device: %s.' % disk) + self.assertEqual("argon2id", m.groups()[0]) + + # check that conversion fails + msg = 'org.freedesktop.UDisks2.Error.Failed: Error converting encrypted device /dev/.+: Conversion failed: Invalid argument' + with self.assertRaisesRegex(dbus.exceptions.DBusException, msg): + device.Convert("luks1", self.no_options, + dbus_interface=self.iface_prefix + '.Encrypted') + + self.assertEqual(2, _get_luks_version(disk)) + def _get_default_luks_version(self): manager = self.get_object('/Manager') default_encryption_type = self.get_property(manager, '.Manager', 'DefaultEncryptionType') diff --git a/src/udiskslinuxencrypted.c b/src/udiskslinuxencrypted.c index d62f3e566..fbd19a210 100644 --- a/src/udiskslinuxencrypted.c +++ b/src/udiskslinuxencrypted.c @@ -1194,6 +1194,142 @@ handle_resize (UDisksEncrypted *encrypted, /* ---------------------------------------------------------------------------------------------------- */ +/* runs in thread dedicated to handling method call */ +static gboolean +handle_convert (UDisksEncrypted *encrypted, + GDBusMethodInvocation *invocation, + const gchar *target_version, + GVariant *options) +{ + UDisksObject *object = NULL; + UDisksBlock *block; + UDisksDaemon *daemon; + UDisksState *state = NULL; + uid_t caller_uid; + const gchar *action_id = NULL; + const gchar *message = NULL; + GError *error = NULL; + UDisksBaseJob *job = NULL; + BDCryptoLUKSVersion bd_target_version; + + object = udisks_daemon_util_dup_object (encrypted, &error); + if (object == NULL) + { + g_dbus_method_invocation_return_gerror (invocation, error); + goto out; + } + + block = udisks_object_peek_block (object); + daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object)); + state = udisks_daemon_get_state (daemon); + + udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object)); + udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object))); + + /* Fail if the device is not a LUKS device */ + if (!(g_strcmp0 (udisks_block_get_id_usage (block), "crypto") == 0 && + g_strcmp0 (udisks_block_get_id_type (block), "crypto_LUKS") == 0)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Device %s does not appear to be a LUKS device", + udisks_block_get_device (block)); + goto out; + } + + if (!udisks_daemon_util_get_caller_uid_sync (daemon, invocation, NULL /* GCancellable */, &caller_uid, &error)) + { + g_dbus_method_invocation_return_gerror (invocation, error); + goto out; + } + + action_id = "org.freedesktop.udisks2.modify-device"; + /* Translators: Shown in authentication dialog when the user + * requests conversion of an encrypted block device. + * + * Do not translate $(drive), it's a placeholder and + * will be replaced by the name of the drive/device in question + */ + message = N_("Authentication is required to convert device $(drive) to a different LUKS version."); + if (! udisks_daemon_util_setup_by_user (daemon, object, caller_uid)) + { + if (udisks_block_get_hint_system (block)) + { + action_id = "org.freedesktop.udisks2.modify-device-system"; + } + else if (! udisks_daemon_util_on_user_seat (daemon, UDISKS_OBJECT (object), caller_uid)) + { + action_id = "org.freedesktop.udisks2.modify-device-other-seat"; + } + } + + /* Check that the user is actually authorized to convert the device. */ + if (! udisks_daemon_util_check_authorization_sync (daemon, + object, + action_id, + options, + message, + invocation)) + goto out; + + if (g_strcmp0 (target_version, "luks1") == 0) { + bd_target_version = BD_CRYPTO_LUKS_VERSION_LUKS1; + } else if (g_strcmp0 (target_version, "luks2") == 0) { + bd_target_version = BD_CRYPTO_LUKS_VERSION_LUKS2; + } else { + g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "Unsupported target LUKS version: '%s'. Only 'luks1' and 'luks2' are supported.", + target_version); + goto out; + } + + job = udisks_daemon_launch_simple_job (daemon, + UDISKS_OBJECT (object), + "encrypted-convert", + caller_uid, + NULL); + if (job == NULL) + { + g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, + "Failed to create a job object"); + goto out; + } + + udisks_linux_block_encrypted_lock (block); + + if (! bd_crypto_luks_convert (udisks_block_get_device (block), + bd_target_version, + &error)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error converting encrypted device %s: %s", + udisks_block_get_device (block), + error->message); + udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message); + udisks_linux_block_encrypted_unlock (block); + goto out; + } + + udisks_linux_block_encrypted_unlock (block); + + udisks_encrypted_complete_convert (encrypted, invocation); + udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, NULL); + + out: + if (object != NULL) + udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object)); + if (state != NULL) + udisks_state_check (state); + g_clear_object (&object); + g_clear_error (&error); + return TRUE; /* returning TRUE means that we handled the method invocation */ +} + +/* ---------------------------------------------------------------------------------------------------- */ + static void encrypted_iface_init (UDisksEncryptedIface *iface) { @@ -1201,4 +1337,5 @@ encrypted_iface_init (UDisksEncryptedIface *iface) iface->handle_lock = handle_lock; iface->handle_change_passphrase = handle_change_passphrase; iface->handle_resize = handle_resize; + iface->handle_convert = handle_convert; } diff --git a/udisks/udisksclient.c b/udisks/udisksclient.c index 191ff6014..4be53b25a 100644 --- a/udisks/udisksclient.c +++ b/udisks/udisksclient.c @@ -2734,6 +2734,7 @@ udisks_client_get_job_description_from_operation (const gchar *operation) g_hash_table_insert (hash, (gpointer) "encrypted-lock", (gpointer) C_("job", "Locking Device")); g_hash_table_insert (hash, (gpointer) "encrypted-modify", (gpointer) C_("job", "Modifying Encrypted Device")); g_hash_table_insert (hash, (gpointer) "encrypted-resize", (gpointer) C_("job", "Resizing Encrypted Device")); + g_hash_table_insert (hash, (gpointer) "encrypted-convert", (gpointer) C_("job", "Converting Encrypted Device")); g_hash_table_insert (hash, (gpointer) "swapspace-start", (gpointer) C_("job", "Starting Swap Device")); g_hash_table_insert (hash, (gpointer) "swapspace-stop", (gpointer) C_("job", "Stopping Swap Device")); g_hash_table_insert (hash, (gpointer) "swapspace-modify", (gpointer) C_("job", "Modifying Swap Device"));