mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-12 03:10:08 +01:00
Properly handle authenticated encryption on OPAL device.
This commit is contained in:
committed by
Luca Boccassi
parent
33bf0c6ae9
commit
4d487d5dcf
@@ -354,6 +354,10 @@ bool LUKS2_segment_set_size(struct luks2_hdr *hdr,
|
||||
int segment,
|
||||
const uint64_t *segment_size_bytes);
|
||||
|
||||
uint64_t LUKS2_opal_segment_size(struct luks2_hdr *hdr,
|
||||
int segment,
|
||||
unsigned blockwise);
|
||||
|
||||
int LUKS2_segment_is_type(struct luks2_hdr *hdr,
|
||||
int segment,
|
||||
const char *type);
|
||||
|
||||
@@ -704,7 +704,7 @@ static int validate_reencrypt_segments(struct crypt_device *cd, json_object *hdr
|
||||
static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
|
||||
{
|
||||
json_object *jobj_segments, *jobj_digests, *jobj_offset, *jobj_size, *jobj_type, *jobj_flags, *jobj;
|
||||
uint64_t offset, size;
|
||||
uint64_t offset, size, opal_segment_size;
|
||||
int i, r, count, first_backup = -1;
|
||||
struct interval *intervals = NULL;
|
||||
|
||||
@@ -787,9 +787,25 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
|
||||
|
||||
/* opal */
|
||||
if (!strncmp(json_object_get_string(jobj_type), "hw-opal", 7)) {
|
||||
if (!json_contains(cd, val, key, "Segment", "opal_segment_number", json_type_int) ||
|
||||
!json_contains(cd, val, key, "Segment", "opal_key_size", json_type_int))
|
||||
if (!size) {
|
||||
log_dbg(cd, "segment type %s does not support dynamic size.",
|
||||
json_object_get_string(jobj_type));
|
||||
return 1;
|
||||
}
|
||||
if (!json_contains(cd, val, key, "Segment", "opal_segment_number", json_type_int) ||
|
||||
!json_contains(cd, val, key, "Segment", "opal_key_size", json_type_int) ||
|
||||
!(jobj_size = json_contains_string(cd, val, key, "Segment", "opal_segment_size")))
|
||||
return 1;
|
||||
if (!numbered(cd, "opal_segment_size", json_object_get_string(jobj_size)))
|
||||
return 1;
|
||||
if (!json_str_to_uint64(jobj_size, &opal_segment_size) || !opal_segment_size) {
|
||||
log_dbg(cd, "Illegal opal segment size value.");
|
||||
return 1;
|
||||
}
|
||||
if (size > opal_segment_size) {
|
||||
log_dbg(cd, "segment size overflows opal locking range size.");
|
||||
return 1;
|
||||
}
|
||||
if (!strcmp(json_object_get_string(jobj_type), "hw-opal-crypt") &&
|
||||
hdr_validate_crypt_segment(cd, val, key, jobj_digests, size))
|
||||
return 1;
|
||||
@@ -2100,6 +2116,9 @@ static void hdr_dump_segments(struct crypt_device *cd, json_object *hdr_jobj)
|
||||
log_std(cd, "\tsegment number: %" PRIu32 "\n", crypt_jobj_get_uint32(jobj1));
|
||||
json_object_object_get_ex(jobj_segment, "opal_key_size", &jobj1);
|
||||
log_std(cd, "\topal key size: %" PRIu32 "\n", crypt_jobj_get_uint32(jobj1));
|
||||
json_object_object_get_ex(jobj_segment, "opal_segment_size", &jobj1);
|
||||
json_str_to_uint64(jobj1, &value);
|
||||
log_std(cd, "\topal length: %" PRIu64 " [bytes]\n", value);
|
||||
}
|
||||
|
||||
json_object_object_get_ex(jobj_segment, "offset", &jobj1);
|
||||
@@ -2617,7 +2636,7 @@ int LUKS2_activate(struct crypt_device *cd,
|
||||
int r;
|
||||
bool dynamic;
|
||||
uint32_t opal_segment_number;
|
||||
uint64_t range_offset_sectors, device_length_bytes;
|
||||
uint64_t range_offset_sectors, range_length_sectors, device_length_bytes;
|
||||
struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
|
||||
struct crypt_dm_active_device dmdi = {}, dmd = {
|
||||
.uuid = crypt_get_uuid(cd)
|
||||
@@ -2636,6 +2655,11 @@ int LUKS2_activate(struct crypt_device *cd,
|
||||
if ((r = LUKS2_get_data_size(hdr, &device_length_bytes, &dynamic)))
|
||||
return r;
|
||||
|
||||
if (dynamic && opal_key) {
|
||||
log_err(cd, _("OPAL device must have static device size."));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!dynamic)
|
||||
dmd.size = device_length_bytes / SECTOR_SIZE;
|
||||
|
||||
@@ -2648,9 +2672,23 @@ int LUKS2_activate(struct crypt_device *cd,
|
||||
if (r < 0)
|
||||
return -EINVAL;
|
||||
|
||||
range_length_sectors = LUKS2_opal_segment_size(hdr, CRYPT_DEFAULT_SEGMENT, 1);
|
||||
|
||||
if (crypt_get_integrity_tag_size(cd)) {
|
||||
if (dmd.size >= range_length_sectors) {
|
||||
log_err(cd, _("Encrypted OPAL device with integrity must be smaller than locking range."));
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
if (range_length_sectors != dmd.size) {
|
||||
log_err(cd, _("OPAL device must have same size as locking range."));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
range_offset_sectors = crypt_get_data_offset(cd) + crypt_dev_partition_offset(device_path(crypt_data_device(cd)));
|
||||
r = opal_range_check_attributes(cd, crypt_data_device(cd), opal_segment_number,
|
||||
opal_key, &range_offset_sectors, &dmd.size,
|
||||
opal_key, &range_offset_sectors, &range_length_sectors,
|
||||
NULL /* read locked */, NULL /* write locked */);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -2660,10 +2698,6 @@ int LUKS2_activate(struct crypt_device *cd,
|
||||
return r;
|
||||
}
|
||||
|
||||
/* FIXME: temporary workaround for dm-integrity */
|
||||
if (crypt_get_integrity_tag_size(cd))
|
||||
dmd.size = 0;
|
||||
|
||||
if (LUKS2_segment_is_type(hdr, CRYPT_DEFAULT_SEGMENT, "crypt") ||
|
||||
LUKS2_segment_is_type(hdr, CRYPT_DEFAULT_SEGMENT, "hw-opal-crypt")) {
|
||||
r = dm_crypt_target_set(&dmd.segment, 0,
|
||||
@@ -2704,9 +2738,16 @@ int LUKS2_activate(struct crypt_device *cd,
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
if (!dynamic && dmdi.size != dmd.size) {
|
||||
log_err(cd, _("Underlying dm-integrity device with unexpected provided data sectors."));
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dmdi.flags |= CRYPT_ACTIVATE_PRIVATE;
|
||||
dmdi.uuid = dmd.uuid;
|
||||
dmd.segment.u.crypt.offset = 0;
|
||||
if (dynamic)
|
||||
dmd.segment.size = dmdi.segment.size;
|
||||
|
||||
r = create_or_reload_device_with_integrity(cd, name, CRYPT_LUKS2, &dmd, &dmdi);
|
||||
|
||||
@@ -91,6 +91,17 @@ uint64_t json_segment_get_size(json_object *jobj_segment, unsigned blockwise)
|
||||
return blockwise ? crypt_jobj_get_uint64(jobj) >> SECTOR_SHIFT : crypt_jobj_get_uint64(jobj);
|
||||
}
|
||||
|
||||
static uint64_t json_segment_get_opal_size(json_object *jobj_segment, unsigned blockwise)
|
||||
{
|
||||
json_object *jobj;
|
||||
|
||||
if (!jobj_segment ||
|
||||
!json_object_object_get_ex(jobj_segment, "opal_segment_size", &jobj))
|
||||
return 0;
|
||||
|
||||
return blockwise ? crypt_jobj_get_uint64(jobj) >> SECTOR_SHIFT : crypt_jobj_get_uint64(jobj);
|
||||
}
|
||||
|
||||
static bool json_segment_set_size(json_object *jobj_segment, const uint64_t *size_bytes)
|
||||
{
|
||||
json_object *jobj;
|
||||
@@ -338,6 +349,17 @@ json_object *json_segment_create_crypt(uint64_t offset,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void json_add_opal_fields(json_object *jobj_segment, const uint64_t *length,
|
||||
uint32_t segment_number, uint32_t key_size)
|
||||
{
|
||||
assert(jobj_segment);
|
||||
assert(length);
|
||||
|
||||
json_object_object_add(jobj_segment, "opal_segment_number", json_object_new_int(segment_number));
|
||||
json_object_object_add(jobj_segment, "opal_key_size", json_object_new_int(key_size));
|
||||
json_object_object_add(jobj_segment, "opal_segment_size", crypt_jobj_new_uint64(*length));
|
||||
}
|
||||
|
||||
json_object *json_segment_create_opal(uint64_t offset, const uint64_t *length,
|
||||
uint32_t segment_number, uint32_t key_size)
|
||||
{
|
||||
@@ -345,8 +367,7 @@ json_object *json_segment_create_opal(uint64_t offset, const uint64_t *length,
|
||||
if (!jobj)
|
||||
return NULL;
|
||||
|
||||
json_object_object_add(jobj, "opal_segment_number", json_object_new_int(segment_number));
|
||||
json_object_object_add(jobj, "opal_key_size", json_object_new_int(key_size));
|
||||
json_add_opal_fields(jobj, length, segment_number, key_size);
|
||||
|
||||
return jobj;
|
||||
}
|
||||
@@ -361,8 +382,7 @@ json_object *json_segment_create_opal_crypt(uint64_t offset, const uint64_t *len
|
||||
if (!jobj)
|
||||
return NULL;
|
||||
|
||||
json_object_object_add(jobj, "opal_segment_number", json_object_new_int(segment_number));
|
||||
json_object_object_add(jobj, "opal_key_size", json_object_new_int(key_size));
|
||||
json_add_opal_fields(jobj, length, segment_number, key_size);
|
||||
|
||||
if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, sector_size, reencryption))
|
||||
return jobj;
|
||||
@@ -396,6 +416,11 @@ uint64_t LUKS2_segment_size(struct luks2_hdr *hdr, int segment, unsigned blockwi
|
||||
return json_segment_get_size(LUKS2_get_segment_jobj(hdr, segment), blockwise);
|
||||
}
|
||||
|
||||
uint64_t LUKS2_opal_segment_size(struct luks2_hdr *hdr, int segment, unsigned blockwise)
|
||||
{
|
||||
return json_segment_get_opal_size(LUKS2_get_segment_jobj(hdr, segment), blockwise);
|
||||
}
|
||||
|
||||
bool LUKS2_segment_set_size(struct luks2_hdr *hdr, int segment, const uint64_t *segment_size_bytes)
|
||||
{
|
||||
return json_segment_set_size(LUKS2_get_segment_jobj(hdr, segment), segment_size_bytes);
|
||||
|
||||
41
lib/setup.c
41
lib/setup.c
@@ -2278,8 +2278,9 @@ int crypt_format_luks2_opal(struct crypt_device *cd,
|
||||
const char *integrity = params ? params->integrity : NULL;
|
||||
uint32_t sector_size, opal_block_bytes, opal_segment_number = 1; /* We'll use the partition number if available later */
|
||||
uint64_t alignment_offset_bytes, data_offset_bytes, device_size_bytes, opal_alignment_granularity_blocks,
|
||||
partition_offset_sectors, range_offset_blocks, range_length_blocks,
|
||||
required_alignment_bytes, metadata_size_bytes, keyslots_size_bytes;
|
||||
partition_offset_sectors, range_offset_blocks, range_size_bytes,
|
||||
required_alignment_bytes, metadata_size_bytes, keyslots_size_bytes,
|
||||
provided_data_sectors;
|
||||
struct volume_key *user_key = NULL;
|
||||
|
||||
if (!cd || !params || !opal_params ||
|
||||
@@ -2418,15 +2419,14 @@ int crypt_format_luks2_opal(struct crypt_device *cd,
|
||||
}
|
||||
|
||||
device_size_bytes -= data_offset_bytes;
|
||||
if (MISALIGNED(device_size_bytes, opal_block_bytes * opal_alignment_granularity_blocks)) {
|
||||
log_err(cd, _("Compensating device size by %" PRIu64 " sectors to align it with OPAL alignment granularity."),
|
||||
(device_size_bytes % (opal_alignment_granularity_blocks * opal_block_bytes)) / SECTOR_SIZE);
|
||||
device_size_bytes -= (device_size_bytes % (opal_block_bytes * opal_alignment_granularity_blocks));
|
||||
}
|
||||
|
||||
if (!device_size_bytes)
|
||||
range_size_bytes = device_size_bytes - (device_size_bytes % (opal_block_bytes * opal_alignment_granularity_blocks));
|
||||
if (!range_size_bytes)
|
||||
goto out;
|
||||
|
||||
if (device_size_bytes != range_size_bytes)
|
||||
log_err(cd, _("Compensating device size by %" PRIu64 " sectors to align it with OPAL alignment granularity."),
|
||||
(device_size_bytes - range_size_bytes) / SECTOR_SIZE);
|
||||
|
||||
if (cipher) {
|
||||
r = LUKS2_check_encryption_sector(cd, device_size_bytes, data_offset_bytes, sector_size,
|
||||
sector_size_autodetect, integrity == NULL,
|
||||
@@ -2475,10 +2475,8 @@ int crypt_format_luks2_opal(struct crypt_device *cd,
|
||||
|
||||
range_offset_blocks = (data_offset_bytes + partition_offset_sectors * SECTOR_SIZE) / opal_block_bytes;
|
||||
|
||||
range_length_blocks = device_size_bytes / opal_block_bytes;
|
||||
|
||||
r = opal_setup_ranges(cd, crypt_data_device(cd), user_key ?: cd->volume_key,
|
||||
range_offset_blocks, range_length_blocks,
|
||||
range_offset_blocks, range_size_bytes / opal_block_bytes,
|
||||
opal_segment_number, opal_params->admin_key, opal_params->admin_key_size);
|
||||
if (r < 0) {
|
||||
if (r == -EPERM)
|
||||
@@ -2514,13 +2512,30 @@ int crypt_format_luks2_opal(struct crypt_device *cd,
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = INTEGRITY_format(cd, params->integrity_params, NULL, NULL, 0);
|
||||
r = INTEGRITY_format(cd, params->integrity_params, NULL, NULL,
|
||||
/*
|
||||
* Create reduced dm-integrity device only if locking range size does
|
||||
* not match device size.
|
||||
*/
|
||||
device_size_bytes != range_size_bytes ? range_size_bytes / SECTOR_SIZE : 0);
|
||||
if (r)
|
||||
log_err(cd, _("Cannot format integrity for device %s."),
|
||||
data_device_path(cd));
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = INTEGRITY_data_sectors(cd, crypt_data_device(cd),
|
||||
crypt_get_data_offset(cd) * SECTOR_SIZE,
|
||||
&provided_data_sectors);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
if (!LUKS2_segment_set_size(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT,
|
||||
&(uint64_t) {provided_data_sectors * SECTOR_SIZE})) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = opal_lock(cd, crypt_data_device(cd), opal_segment_number);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
Reference in New Issue
Block a user