TCRYPT: Clear mapping of system encrypted partitions.

TrueCrypt/VeraCrypt supports full system encryption (only a partition
table is not encrypted) or system partition encryption
(only a system partition is encrypted).
The metadata header then contains the offset and size of the encrypted area.
Cryptsetup needs to know the specific partition offset to calculate encryption parameters.
To properly map a partition, you must specify a real partition device so cryptsetup can calculate this offset.

As user can specify various combination, we need to determine the proper
IV and data offsets.

The logic for CRYPT_TCRYPT_SYSTEM_HEADER flag should be (in this order):
- if data device is a real partition, calculate offset from it.
- if --header is a real partition , calculate offset from it.
- if device is a real disk, try to search for partition using decrypted offset and size
(works only for system partition-only encryption).
- if data and metadata (header) device is the same, map whole encrypted area
(this is the ost confusing for user)
- if data and metadata (header) divice differs, expect data image contains
only partition (setting offset to 0, but using IV offset from header).

There are still situation that can end with wrong mapping, but user now has the option
to setup it properly.

Also this patch fixes use of stored encryption size in header,
so we do not map larger area.

Fixes:#889
This commit is contained in:
Milan Broz
2024-06-24 10:29:35 +02:00
parent 14fd0b5fc1
commit 0cc686af59
3 changed files with 91 additions and 50 deletions

View File

@@ -735,7 +735,7 @@ int TCRYPT_activate(struct crypt_device *cd,
uint32_t req_flags, dmc_flags;
struct tcrypt_algs *algs;
enum devcheck device_check;
uint64_t offset = crypt_get_data_offset(cd);
uint64_t offset, iv_offset;
struct volume_key *vk = NULL;
struct device *ptr_dev = crypt_data_device(cd), *device = NULL, *part_device = NULL;
struct crypt_dm_active_device dmd = {
@@ -779,24 +779,64 @@ int TCRYPT_activate(struct crypt_device *cd,
else
device_check = DEV_EXCL;
if ((params->flags & CRYPT_TCRYPT_SYSTEM_HEADER) &&
!crypt_dev_is_partition(device_path(crypt_data_device(cd)))) {
offset = crypt_get_data_offset(cd);
iv_offset = crypt_get_iv_offset(cd);
/*
* System encryption is tricky, as the TCRYPT header is outside the partition area.
* It can be a system partition only (TCRYPT header offset contains MK offset to
* a particular partition) or the whole system (then MK offset starts on the header itself).
* IV offset is always partition offset, but device offset depends on whether the user
* copied the whole disk or just one encrypted partition.
* This code tries to guess the most common situations but can still fail and use wrong offsets.
* Recent UEFI systems never use whole system encryption.
*/
if (params->flags & CRYPT_TCRYPT_SYSTEM_HEADER) {
if (crypt_dev_is_partition(device_path(crypt_data_device(cd)))) {
/* One partition */
offset = 0;
iv_offset = crypt_dev_partition_offset(device_path(crypt_data_device(cd)));
} else if (crypt_dev_is_partition(device_path(crypt_metadata_device(cd)))) {
/* One partition image, header is the original partition */
offset = 0;
iv_offset = crypt_dev_partition_offset(device_path(crypt_metadata_device(cd)));
} else {
/* No partition info, try partition-only mode searching for partition. */
part_path = crypt_get_partition_device(device_path(crypt_data_device(cd)),
crypt_get_data_offset(cd), dmd.size);
iv_offset, hdr->d.volume_size / SECTOR_SIZE);
if (!part_path)
part_path = crypt_get_partition_device(device_path(crypt_metadata_device(cd)),
iv_offset, hdr->d.volume_size / SECTOR_SIZE);
if (part_path) {
if (!device_alloc(cd, &part_device, part_path)) {
log_verbose(cd, _("Activating TCRYPT system encryption for partition %s."),
part_path);
ptr_dev = part_device;
offset = 0;
iv_offset = crypt_dev_partition_offset(part_path);
}
free(part_path);
} else
} else if (device_is_identical(crypt_metadata_device(cd), crypt_data_device(cd))) {
/*
* System encryption use the whole device mapping, there can
* be active partitions.
* We have no partition offset and TCRYPT system header is on the data device.
* Use the whole device mapping.
* There can be active partitions, do not use exclusive flag.
*/
device_check = DEV_OK;
dmd.size = hdr->d.volume_size / SECTOR_SIZE;
log_err(cd, _("Cannot determine TCRYPT system partition offset, activating whole encrypted area."));
} else {
/*
* We have no partition offset and TCRYPT system header is on the metadata device
* (TCRYPT system header was NOT read from data device).
* Expect that data device is a copy of partition and not the whole device.
* This will not work for whole system encryption, though.
*/
offset = 0;
log_err(cd, _("Cannot determine TCRYPT system partition offset, activating device as a system partition."));
}
}
log_dbg(cd, "TCRYPT system encryption data_offset %" PRIu64 ", iv_offset %" PRIu64 ".", offset, iv_offset);
}
r = device_block_adjust(cd, ptr_dev, device_check,
@@ -847,7 +887,7 @@ int TCRYPT_activate(struct crypt_device *cd,
}
r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, ptr_dev, vk,
cipher_spec, crypt_get_iv_offset(cd), offset,
cipher_spec, iv_offset, offset,
crypt_get_integrity(cd),
crypt_get_integrity_tag_size(cd),
crypt_get_sector_size(cd));
@@ -1028,9 +1068,7 @@ uint64_t TCRYPT_get_data_offset(struct crypt_device *cd,
if (!hdr->d.version) {
/* No real header loaded, initialized by active device, use default mk_offset */
} else if (params->flags & CRYPT_TCRYPT_SYSTEM_HEADER) {
/* Mapping through whole device, not partition! */
if (crypt_dev_is_partition(device_path(crypt_data_device(cd))))
return 0;
/* Mapping through whole device or partition, return mk_offset */
} else if (params->mode && !strncmp(params->mode, "xts", 3)) {
if (hdr->d.version < 3)
return 1;
@@ -1057,25 +1095,12 @@ uint64_t TCRYPT_get_iv_offset(struct crypt_device *cd,
struct tcrypt_phdr *hdr,
struct crypt_params_tcrypt *params)
{
uint64_t iv_offset, partition_offset;
if (params->mode && !strncmp(params->mode, "xts", 3))
iv_offset = TCRYPT_get_data_offset(cd, hdr, params);
return TCRYPT_get_data_offset(cd, hdr, params);
else if (params->mode && !strncmp(params->mode, "lrw", 3))
iv_offset = 0;
else
iv_offset = hdr->d.mk_offset / SECTOR_SIZE;
return 0;
if (params->flags & CRYPT_TCRYPT_SYSTEM_HEADER) {
partition_offset = crypt_dev_partition_offset(device_path(crypt_data_device(cd)));
/* FIXME: we need to deal with overflow sooner */
if (iv_offset > (UINT64_MAX - partition_offset))
iv_offset = UINT64_MAX;
else
iv_offset += partition_offset;
}
return iv_offset;
return hdr->d.mk_offset / SECTOR_SIZE;
}
int TCRYPT_get_volume_key(struct crypt_device *cd,

View File

@@ -1052,6 +1052,32 @@ ifdef::ACTION_OPEN,ACTION_TCRYPTDUMP[]
Specify which TrueCrypt on-disk
header will be used to open the device. See _TCRYPT_ section in
*cryptsetup*(8) for more info.
+
Using a system-encrypted device with the *--tcrypt-system* option
requires specific settings to work as expected.
+
TrueCrypt/VeraCrypt supports full system encryption
(only a partition table is not encrypted) or system partition encryption
(only a system partition is encrypted).
The metadata header then contains the offset and size of the encrypted area.
Cryptsetup needs to know the specific partition offset to calculate
encryption parameters. To properly map a partition, you must specify
a real partition device so cryptsetup can calculate this offset.
+
While you can use a full device as a parameter (/dev/sdb), always prefer
to specify the partition you want to map (/dev/sdb1) as only system partition
mode can be detected this way.
+
For mapping images (stored in a file), you can use the additional
*--header* option with the real partition device.
If the *--header* is used (and it is different from the data image),
cryptsetup expects that the data image contains a snapshot of the data partition only.
+
If *--header* is not used (or points to the same image), cryptsetup expects that
the image contains a full disk (including the partition table).
This can map a full encrypted area not directly mountable as a filesystem.
Please prefer creating a loop device with partitions (*losetup -P*,
see *losetup(8)* man page) and use a real partition (/dev/loopXp1) as the device parameter.
endif::[]
ifdef::ACTION_OPEN[]

View File

@@ -297,19 +297,9 @@ The *tcryptDump* command should work for all recognized TCRYPT devices
and doesn't require superuser privilege.
To map system device (device with boot loader where the whole encrypted
system resides) use *--tcrypt-system* option. You can use partition
device as the parameter (parameter must be real partition device, not an
image in a file), then only this partition is mapped.
If you have the whole TCRYPT device as a file image and you want to map
multiple partition encrypted with system encryption, please create
loopback mapping with partitions first (*losetup -P*, see *losetup(8)*
man page for more info), and use loop partition as the device parameter.
If you use the whole base device as a parameter, one device for the
whole system encryption is mapped. This mode is available only for
backward compatibility with older cryptsetup versions which mapped
TCRYPT system encryption using the whole device.
system resides) use *--tcrypt-system* option.
Please read specific info in *cryptsetup-tcryptOpen*(8) *--tcrypt-system*
option section as mapping system-encrypted device is tricky.
To use hidden header (and map hidden device, if available), use
*--tcrypt-hidden* option.