Merge branch 'upstream' into tizen
[platform/upstream/cryptsetup.git] / lib / integrity / integrity.c
index ccb393e..aeadc82 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * 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)
@@ -38,11 +49,13 @@ static int INTEGRITY_read_superblock(struct crypt_device *cd,
                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);
@@ -63,7 +76,7 @@ int INTEGRITY_read_sb(struct crypt_device *cd,
        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;
 
@@ -120,7 +133,7 @@ int INTEGRITY_data_sectors(struct crypt_device *cd,
        return 0;
 }
 
-int INTEGRITY_key_size(struct crypt_device *cd, const char *integrity)
+int INTEGRITY_key_size(const char *integrity)
 {
        if (!integrity)
                return 0;
@@ -154,6 +167,9 @@ int INTEGRITY_hash_tag_size(const char *integrity)
        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);
@@ -163,8 +179,7 @@ int INTEGRITY_hash_tag_size(const char *integrity)
        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)
 {
@@ -189,7 +204,7 @@ int INTEGRITY_tag_size(struct crypt_device *cd,
        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)"))
@@ -228,13 +243,13 @@ int INTEGRITY_create_dmd_device(struct crypt_device *cd,
        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);
@@ -256,18 +271,8 @@ int INTEGRITY_activate_dmd_device(struct crypt_device *cd,
        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;
@@ -299,14 +304,33 @@ int INTEGRITY_activate(struct crypt_device *cd,
                       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;
 }
@@ -338,7 +362,7 @@ int INTEGRITY_format(struct crypt_device *cd,
        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);