/*
* Integrity volume handling
*
- * Copyright (C) 2016-2021 Milan Broz
+ * Copyright (C) 2016-2023 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "integrity.h"
#include "internal.h"
+/* For LUKS2, integrity metadata are on DATA device even for detached header! */
+static struct device *INTEGRITY_metadata_device(struct crypt_device *cd)
+{
+ const char *type = crypt_get_type(cd);
+
+ if (type && !strcmp(type, CRYPT_LUKS2))
+ return crypt_data_device(cd);
+
+ return crypt_metadata_device(cd);
+}
+
static int INTEGRITY_read_superblock(struct crypt_device *cd,
struct device *device,
uint64_t offset, struct superblock *sb)
return -EINVAL;
if (read_lseek_blockwise(devfd, device_block_size(cd, device),
- device_alignment(device), sb, sizeof(*sb), offset) != sizeof(*sb) ||
- memcmp(sb->magic, SB_MAGIC, sizeof(sb->magic)) ||
- sb->version < SB_VERSION_1 || sb->version > SB_VERSION_5) {
- log_std(cd, "No integrity superblock detected on %s.\n",
- device_path(device));
+ device_alignment(device), sb, sizeof(*sb), offset) != sizeof(*sb) ||
+ memcmp(sb->magic, SB_MAGIC, sizeof(sb->magic))) {
+ log_dbg(cd, "No kernel dm-integrity metadata detected on %s.", device_path(device));
+ r = -EINVAL;
+ } else if (sb->version < SB_VERSION_1 || sb->version > SB_VERSION_5) {
+ log_err(cd, _("Incompatible kernel dm-integrity metadata (version %u) detected on %s."),
+ sb->version, device_path(device));
r = -EINVAL;
} else {
sb->integrity_tag_size = le16toh(sb->integrity_tag_size);
struct superblock sb;
int r;
- r = INTEGRITY_read_superblock(cd, crypt_metadata_device(cd), 0, &sb);
+ r = INTEGRITY_read_superblock(cd, INTEGRITY_metadata_device(cd), 0, &sb);
if (r)
return r;
return 0;
}
-int INTEGRITY_key_size(struct crypt_device *cd, const char *integrity)
+int INTEGRITY_key_size(const char *integrity)
{
if (!integrity)
return 0;
if (!strcmp(integrity, "crc32") || !strcmp(integrity, "crc32c"))
return 4;
+ if (!strcmp(integrity, "xxhash64"))
+ return 8;
+
r = sscanf(integrity, "hmac(%" MAX_CIPHER_LEN_STR "[^)]s", hash);
if (r == 1)
r = crypt_hash_size(hash);
return r < 0 ? 0 : r;
}
-int INTEGRITY_tag_size(struct crypt_device *cd,
- const char *integrity,
+int INTEGRITY_tag_size(const char *integrity,
const char *cipher,
const char *cipher_mode)
{
if (!integrity || !strcmp(integrity, "none"))
auth_tag_size = 0;
else if (!strcmp(integrity, "aead"))
- auth_tag_size = 16; //FIXME gcm- mode only
+ auth_tag_size = 16; /* gcm- mode only */
else if (!strcmp(integrity, "cmac(aes)"))
auth_tag_size = 16;
else if (!strcmp(integrity, "hmac(sha1)"))
if (sb_flags & SB_FLAG_RECALCULATING)
dmd->flags |= CRYPT_ACTIVATE_RECALCULATE;
- r = INTEGRITY_data_sectors(cd, crypt_metadata_device(cd),
+ r = INTEGRITY_data_sectors(cd, INTEGRITY_metadata_device(cd),
crypt_get_data_offset(cd) * SECTOR_SIZE, &dmd->size);
if (r < 0)
return r;
return dm_integrity_target_set(cd, &dmd->segment, 0, dmd->size,
- crypt_metadata_device(cd), crypt_data_device(cd),
+ INTEGRITY_metadata_device(cd), crypt_data_device(cd),
crypt_get_integrity_tag_size(cd), crypt_get_data_offset(cd),
crypt_get_sector_size(cd), vk, journal_crypt_key,
journal_mac_key, params);
log_dbg(cd, "Trying to activate INTEGRITY device on top of %s, using name %s, tag size %d, provided sectors %" PRIu64".",
device_path(tgt->data_device), name, tgt->u.integrity.tag_size, dmd->size);
- r = device_block_adjust(cd, tgt->data_device, DEV_EXCL,
- tgt->u.integrity.offset, NULL, &dmd->flags);
- if (r)
- return r;
+ r = create_or_reload_device(cd, name, type, dmd);
- if (tgt->u.integrity.meta_device) {
- r = device_block_adjust(cd, tgt->u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL);
- if (r)
- return r;
- }
-
- r = dm_create_device(cd, name, type, dmd);
if (r < 0 && (dm_flags(cd, DM_INTEGRITY, &dmi_flags) || !(dmi_flags & DM_INTEGRITY_SUPPORTED))) {
log_err(cd, _("Kernel does not support dm-integrity mapping."));
return -ENOTSUP;
struct volume_key *journal_mac_key,
uint32_t flags, uint32_t sb_flags)
{
- struct crypt_dm_active_device dmd = {};
- int r = INTEGRITY_create_dmd_device(cd, params, vk, journal_crypt_key,
- journal_mac_key, &dmd, flags, sb_flags);
+ struct crypt_dm_active_device dmdq = {}, dmd = {};
+ int r;
- if (r < 0)
- return r;
+ if (flags & CRYPT_ACTIVATE_REFRESH) {
+ r = dm_query_device(cd, name, DM_ACTIVE_CRYPT_KEYSIZE |
+ DM_ACTIVE_CRYPT_KEY |
+ DM_ACTIVE_INTEGRITY_PARAMS |
+ DM_ACTIVE_JOURNAL_CRYPT_KEY |
+ DM_ACTIVE_JOURNAL_MAC_KEY, &dmdq);
+ if (r < 0)
+ return r;
+
+ r = INTEGRITY_create_dmd_device(cd, params, vk ?: dmdq.segment.u.integrity.vk,
+ journal_crypt_key ?: dmdq.segment.u.integrity.journal_crypt_key,
+ journal_mac_key ?: dmdq.segment.u.integrity.journal_integrity_key,
+ &dmd, flags, sb_flags);
+
+ if (!r)
+ dmd.size = dmdq.size;
+ } else
+ r = INTEGRITY_create_dmd_device(cd, params, vk, journal_crypt_key,
+ journal_mac_key, &dmd, flags, sb_flags);
+
+ if (!r)
+ r = INTEGRITY_activate_dmd_device(cd, name, CRYPT_INTEGRITY, &dmd, sb_flags);
- r = INTEGRITY_activate_dmd_device(cd, name, CRYPT_INTEGRITY, &dmd, sb_flags);
+ dm_targets_free(cd, &dmdq);
dm_targets_free(cd, &dmd);
return r;
}
if (params && params->integrity_key_size)
vk = crypt_alloc_volume_key(params->integrity_key_size, NULL);
- r = dm_integrity_target_set(cd, tgt, 0, dmdi.size, crypt_metadata_device(cd),
+ r = dm_integrity_target_set(cd, tgt, 0, dmdi.size, INTEGRITY_metadata_device(cd),
crypt_data_device(cd), crypt_get_integrity_tag_size(cd),
crypt_get_data_offset(cd), crypt_get_sector_size(cd), vk,
journal_crypt_key, journal_mac_key, params);