Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Add support for LUKS header backup. #1302

Merged
merged 1 commit into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion data/org.freedesktop.UDisks2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2324,6 +2324,20 @@
<arg name="options" direction="in" type="a{sv}"/>
</method>

<!--
RestoreEncryptedHeader:
@backup_file: Path from where to backup header and keyslots.
@options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>).
@since: 2.11.0

Writes header and keyslots from the specified file to the specified device.
The specified file must exist.
-->
<method name="RestoreEncryptedHeader">
<arg name="backup_file" direction="in" type="s"/>
<arg name="options" direction="in" type="a{sv}"/>
</method>

</interface>

<!-- ********************************************************************** -->
Expand Down Expand Up @@ -2975,7 +2989,6 @@
<arg name="options" direction="in" type="a{sv}"/>
</method>


<!--
Convert:
@target_version: The LUKS version to convert to. Either 'luks1' or 'luks2'.
Expand All @@ -2990,6 +3003,20 @@
<arg name="options" direction="in" type="a{sv}"/>
</method>

<!--
HeaderBackup:
@backup_file: Path where to backup header and keyslots to.
@options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>).
@since: 2.11.0

Saves header and keyslots to the specified file.
The target file is not overwritten if exists, and an error is returned.
-->
<method name="HeaderBackup">
<arg name="backup_file" direction="in" type="s"/>
<arg name="options" direction="in" type="a{sv}"/>
</method>

</interface>

<!-- ********************************************************************** -->
Expand Down
8 changes: 8 additions & 0 deletions doc/udisks2-sections.txt.in.in
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,10 @@ udisks_encrypted_call_convert
udisks_encrypted_call_convert_finish
udisks_encrypted_call_convert_sync
udisks_encrypted_complete_convert
udisks_encrypted_call_header_backup
udisks_encrypted_call_header_backup_finish
udisks_encrypted_call_header_backup_sync
udisks_encrypted_complete_header_backup
UDisksEncryptedProxy
UDisksEncryptedProxyClass
udisks_encrypted_proxy_new
Expand Down Expand Up @@ -776,6 +780,10 @@ udisks_block_call_rescan
udisks_block_call_rescan_finish
udisks_block_call_rescan_sync
udisks_block_complete_rescan
udisks_block_call_restore_encrypted_header
udisks_block_call_restore_encrypted_header_finish
udisks_block_call_restore_encrypted_header_sync
udisks_block_complete_restore_encrypted_header
udisks_block_get_configuration
udisks_block_get_crypto_backing_device
udisks_block_get_device
Expand Down
46 changes: 46 additions & 0 deletions src/tests/dbus-tests/test_70_encrypted.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,52 @@ def test_convert_xfail(self):

self.assertEqual(2, _get_luks_version(disk))

def test_header_backup_restore(self):
disk = self.vdevs[0]
device = self.get_device(disk)
self._create_luks(device, self.PASSPHRASE)

self.addCleanup(self._remove_luks, device)
self.udev_settle()

# check that backup normally works
with tempfile.TemporaryDirectory() as temp_dir:
backup_file_path = os.path.join(temp_dir, "udisks_encrypted_header_backup.luks")

device.HeaderBackup(backup_file_path, self.no_options,
dbus_interface=self.iface_prefix + '.Encrypted')
self.assertTrue(os.path.exists(backup_file_path))

# check that backup will fail if there is already a file under the specified path
msg = 'org.freedesktop.UDisks2.Error.Failed: Error backing up header of encrypted device /dev/.+: Failed to backup LUKS header: Invalid argument'
with self.assertRaisesRegex(dbus.exceptions.DBusException, msg):
device.HeaderBackup(backup_file_path, self.no_options,
dbus_interface=self.iface_prefix + '.Encrypted')

self.assertTrue(os.path.exists(backup_file_path))

# check that after reaping device and restoring header, cryptsetup will recognize header
device.Lock(self.no_options, dbus_interface=self.iface_prefix + '.Encrypted')
DISK_SIZE = 100 # magic number from `targetcli_config.json`
ret, out = udiskstestcase.UdisksTestCase.run_command("dd if=/dev/zero of=%s bs=1M count=%d" % (disk, DISK_SIZE))
self.assertEqual(ret, 0)
ret, out = udiskstestcase.UdisksTestCase.run_command("cryptsetup luksDump %s" % disk)
self.assertEqual(1, ret)
self.assertTrue(("Device %s is not a valid LUKS device." % disk) in out)
# wait for changes to propagate from udev to udisks
fstype = self.get_property(device, '.Block', 'IdType')
fstype.assertEqual('') # this method waits

device = self.get_device(disk)
device.RestoreEncryptedHeader(backup_file_path, self.no_options,
dbus_interface=self.iface_prefix + '.Block')
ret, out = udiskstestcase.UdisksTestCase.run_command("cryptsetup luksDump %s" % disk)
self.assertEqual(ret, 0)

# get the Encrypted interface back
device = self.get_device(disk)
self.assertHasIface(device, "org.freedesktop.UDisks2.Encrypted")

def _get_default_luks_version(self):
manager = self.get_object('/Manager')
default_encryption_type = self.get_property(manager, '.Manager', 'DefaultEncryptionType')
Expand Down
80 changes: 80 additions & 0 deletions src/udiskslinuxblock.c
Original file line number Diff line number Diff line change
Expand Up @@ -4196,6 +4196,85 @@ handle_rescan (UDisksBlock *block,

/* ---------------------------------------------------------------------------------------------------- */

/* runs in thread dedicated to handling method call */
static gboolean
handle_restore_encrypted_header (UDisksBlock *encrypted,
GDBusMethodInvocation *invocation,
const gchar *backup_file,
GVariant *options)
{
UDisksObject *object = NULL;
UDisksBlock *block;
UDisksDaemon *daemon;
UDisksState *state = NULL;
uid_t caller_uid;
GError *error = NULL;
UDisksBaseJob *job = NULL;

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)));

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;
}

job = udisks_daemon_launch_simple_job (daemon,
UDISKS_OBJECT (object),
"block-restore-encrypted-header",
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_header_restore (udisks_block_get_device (block), backup_file, &error))
{
g_dbus_method_invocation_return_error (invocation,
UDISKS_ERROR,
UDISKS_ERROR_FAILED,
"Error restoring header of 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_block_complete_restore_encrypted_header (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
block_iface_init (UDisksBlockIface *iface)
{
Expand All @@ -4209,4 +4288,5 @@ block_iface_init (UDisksBlockIface *iface)
iface->handle_open_for_benchmark = handle_open_for_benchmark;
iface->handle_open_device = handle_open_device;
iface->handle_rescan = handle_rescan;
iface->handle_restore_encrypted_header = handle_restore_encrypted_header;
}
92 changes: 92 additions & 0 deletions src/udiskslinuxencrypted.c
Original file line number Diff line number Diff line change
Expand Up @@ -1330,6 +1330,97 @@ handle_convert (UDisksEncrypted *encrypted,

/* ---------------------------------------------------------------------------------------------------- */

/* runs in thread dedicated to handling method call */
static gboolean
handle_header_backup (UDisksEncrypted *encrypted,
GDBusMethodInvocation *invocation,
const gchar *backup_file,
GVariant *options)
{
UDisksObject *object = NULL;
UDisksBlock *block;
UDisksDaemon *daemon;
UDisksState *state = NULL;
uid_t caller_uid;
GError *error = NULL;
UDisksBaseJob *job = NULL;

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;
}

job = udisks_daemon_launch_simple_job (daemon,
UDISKS_OBJECT (object),
"encrypted-header-backup",
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_header_backup (udisks_block_get_device (block), backup_file, &error))
{
g_dbus_method_invocation_return_error (invocation,
UDISKS_ERROR,
UDISKS_ERROR_FAILED,
"Error backing up header of 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_header_backup (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)
{
Expand All @@ -1338,4 +1429,5 @@ encrypted_iface_init (UDisksEncryptedIface *iface)
iface->handle_change_passphrase = handle_change_passphrase;
iface->handle_resize = handle_resize;
iface->handle_convert = handle_convert;
iface->handle_header_backup = handle_header_backup;
}
2 changes: 2 additions & 0 deletions udisks/udisksclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -2735,6 +2735,8 @@ udisks_client_get_job_description_from_operation (const gchar *operation)
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) "encrypted-header-backup", (gpointer) C_("job", "Backing Up Header of an Encrypted Device"));
g_hash_table_insert (hash, (gpointer) "block-restore-encrypted-header", (gpointer) C_("job", "Restoring Header of an 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"));
Expand Down