mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-12 11:20:10 +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,
|
int segment,
|
||||||
const uint64_t *segment_size_bytes);
|
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 LUKS2_segment_is_type(struct luks2_hdr *hdr,
|
||||||
int segment,
|
int segment,
|
||||||
const char *type);
|
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)
|
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;
|
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;
|
int i, r, count, first_backup = -1;
|
||||||
struct interval *intervals = NULL;
|
struct interval *intervals = NULL;
|
||||||
|
|
||||||
@@ -787,9 +787,25 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
|
|||||||
|
|
||||||
/* opal */
|
/* opal */
|
||||||
if (!strncmp(json_object_get_string(jobj_type), "hw-opal", 7)) {
|
if (!strncmp(json_object_get_string(jobj_type), "hw-opal", 7)) {
|
||||||
if (!json_contains(cd, val, key, "Segment", "opal_segment_number", json_type_int) ||
|
if (!size) {
|
||||||
!json_contains(cd, val, key, "Segment", "opal_key_size", json_type_int))
|
log_dbg(cd, "segment type %s does not support dynamic size.",
|
||||||
|
json_object_get_string(jobj_type));
|
||||||
return 1;
|
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") &&
|
if (!strcmp(json_object_get_string(jobj_type), "hw-opal-crypt") &&
|
||||||
hdr_validate_crypt_segment(cd, val, key, jobj_digests, size))
|
hdr_validate_crypt_segment(cd, val, key, jobj_digests, size))
|
||||||
return 1;
|
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));
|
log_std(cd, "\tsegment number: %" PRIu32 "\n", crypt_jobj_get_uint32(jobj1));
|
||||||
json_object_object_get_ex(jobj_segment, "opal_key_size", &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));
|
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);
|
json_object_object_get_ex(jobj_segment, "offset", &jobj1);
|
||||||
@@ -2617,7 +2636,7 @@ int LUKS2_activate(struct crypt_device *cd,
|
|||||||
int r;
|
int r;
|
||||||
bool dynamic;
|
bool dynamic;
|
||||||
uint32_t opal_segment_number;
|
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 luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
|
||||||
struct crypt_dm_active_device dmdi = {}, dmd = {
|
struct crypt_dm_active_device dmdi = {}, dmd = {
|
||||||
.uuid = crypt_get_uuid(cd)
|
.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)))
|
if ((r = LUKS2_get_data_size(hdr, &device_length_bytes, &dynamic)))
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
if (dynamic && opal_key) {
|
||||||
|
log_err(cd, _("OPAL device must have static device size."));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (!dynamic)
|
if (!dynamic)
|
||||||
dmd.size = device_length_bytes / SECTOR_SIZE;
|
dmd.size = device_length_bytes / SECTOR_SIZE;
|
||||||
|
|
||||||
@@ -2648,9 +2672,23 @@ int LUKS2_activate(struct crypt_device *cd,
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -EINVAL;
|
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)));
|
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,
|
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 */);
|
NULL /* read locked */, NULL /* write locked */);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@@ -2660,10 +2698,6 @@ int LUKS2_activate(struct crypt_device *cd,
|
|||||||
return r;
|
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") ||
|
if (LUKS2_segment_is_type(hdr, CRYPT_DEFAULT_SEGMENT, "crypt") ||
|
||||||
LUKS2_segment_is_type(hdr, CRYPT_DEFAULT_SEGMENT, "hw-opal-crypt")) {
|
LUKS2_segment_is_type(hdr, CRYPT_DEFAULT_SEGMENT, "hw-opal-crypt")) {
|
||||||
r = dm_crypt_target_set(&dmd.segment, 0,
|
r = dm_crypt_target_set(&dmd.segment, 0,
|
||||||
@@ -2704,9 +2738,16 @@ int LUKS2_activate(struct crypt_device *cd,
|
|||||||
if (r)
|
if (r)
|
||||||
goto out;
|
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.flags |= CRYPT_ACTIVATE_PRIVATE;
|
||||||
dmdi.uuid = dmd.uuid;
|
dmdi.uuid = dmd.uuid;
|
||||||
dmd.segment.u.crypt.offset = 0;
|
dmd.segment.u.crypt.offset = 0;
|
||||||
|
if (dynamic)
|
||||||
dmd.segment.size = dmdi.segment.size;
|
dmd.segment.size = dmdi.segment.size;
|
||||||
|
|
||||||
r = create_or_reload_device_with_integrity(cd, name, CRYPT_LUKS2, &dmd, &dmdi);
|
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);
|
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)
|
static bool json_segment_set_size(json_object *jobj_segment, const uint64_t *size_bytes)
|
||||||
{
|
{
|
||||||
json_object *jobj;
|
json_object *jobj;
|
||||||
@@ -338,6 +349,17 @@ json_object *json_segment_create_crypt(uint64_t offset,
|
|||||||
return NULL;
|
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,
|
json_object *json_segment_create_opal(uint64_t offset, const uint64_t *length,
|
||||||
uint32_t segment_number, uint32_t key_size)
|
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)
|
if (!jobj)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
json_object_object_add(jobj, "opal_segment_number", json_object_new_int(segment_number));
|
json_add_opal_fields(jobj, length, segment_number, key_size);
|
||||||
json_object_object_add(jobj, "opal_key_size", json_object_new_int(key_size));
|
|
||||||
|
|
||||||
return jobj;
|
return jobj;
|
||||||
}
|
}
|
||||||
@@ -361,8 +382,7 @@ json_object *json_segment_create_opal_crypt(uint64_t offset, const uint64_t *len
|
|||||||
if (!jobj)
|
if (!jobj)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
json_object_object_add(jobj, "opal_segment_number", json_object_new_int(segment_number));
|
json_add_opal_fields(jobj, length, segment_number, key_size);
|
||||||
json_object_object_add(jobj, "opal_key_size", json_object_new_int(key_size));
|
|
||||||
|
|
||||||
if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, sector_size, reencryption))
|
if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, sector_size, reencryption))
|
||||||
return jobj;
|
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);
|
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)
|
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);
|
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;
|
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 */
|
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,
|
uint64_t alignment_offset_bytes, data_offset_bytes, device_size_bytes, opal_alignment_granularity_blocks,
|
||||||
partition_offset_sectors, range_offset_blocks, range_length_blocks,
|
partition_offset_sectors, range_offset_blocks, range_size_bytes,
|
||||||
required_alignment_bytes, metadata_size_bytes, keyslots_size_bytes;
|
required_alignment_bytes, metadata_size_bytes, keyslots_size_bytes,
|
||||||
|
provided_data_sectors;
|
||||||
struct volume_key *user_key = NULL;
|
struct volume_key *user_key = NULL;
|
||||||
|
|
||||||
if (!cd || !params || !opal_params ||
|
if (!cd || !params || !opal_params ||
|
||||||
@@ -2418,15 +2419,14 @@ int crypt_format_luks2_opal(struct crypt_device *cd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
device_size_bytes -= data_offset_bytes;
|
device_size_bytes -= data_offset_bytes;
|
||||||
if (MISALIGNED(device_size_bytes, opal_block_bytes * opal_alignment_granularity_blocks)) {
|
range_size_bytes = device_size_bytes - (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."),
|
if (!range_size_bytes)
|
||||||
(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)
|
|
||||||
goto out;
|
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) {
|
if (cipher) {
|
||||||
r = LUKS2_check_encryption_sector(cd, device_size_bytes, data_offset_bytes, sector_size,
|
r = LUKS2_check_encryption_sector(cd, device_size_bytes, data_offset_bytes, sector_size,
|
||||||
sector_size_autodetect, integrity == NULL,
|
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_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,
|
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);
|
opal_segment_number, opal_params->admin_key, opal_params->admin_key_size);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (r == -EPERM)
|
if (r == -EPERM)
|
||||||
@@ -2514,13 +2512,30 @@ int crypt_format_luks2_opal(struct crypt_device *cd,
|
|||||||
goto out;
|
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)
|
if (r)
|
||||||
log_err(cd, _("Cannot format integrity for device %s."),
|
log_err(cd, _("Cannot format integrity for device %s."),
|
||||||
data_device_path(cd));
|
data_device_path(cd));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto out;
|
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);
|
r = opal_lock(cd, crypt_data_device(cd), opal_segment_number);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|||||||
Reference in New Issue
Block a user