Skip to content

Commit

Permalink
Core: Add support for LUKS header backup.
Browse files Browse the repository at this point in the history
Add methods:
  - Encrypted.HeaderBackup
  - Block.RestoreEncryptedHeader
  • Loading branch information
xyakimo1 committed Aug 1, 2024
1 parent db54112 commit 629bcce
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 1 deletion.
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

0 comments on commit 629bcce

Please sign in to comment.