"Fossies" - the Fresh Open Source Software Archive

Member "cryptsetup-2.4.3/lib/integrity/integrity.c" (13 Jan 2022, 11450 Bytes) of package /linux/misc/cryptsetup-2.4.3.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "integrity.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.3.6_vs_2.4.0.

    1 /*
    2  * Integrity volume handling
    3  *
    4  * Copyright (C) 2016-2021 Milan Broz
    5  *
    6  * This file is free software; you can redistribute it and/or
    7  * modify it under the terms of the GNU Lesser General Public
    8  * License as published by the Free Software Foundation; either
    9  * version 2.1 of the License, or (at your option) any later version.
   10  *
   11  * This file is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14  * Lesser General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU Lesser General Public
   17  * License along with this file; if not, write to the Free Software
   18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   19  */
   20 
   21 #include <errno.h>
   22 #include <stdio.h>
   23 #include <stdlib.h>
   24 #include <string.h>
   25 #include <uuid/uuid.h>
   26 
   27 #include "integrity.h"
   28 #include "internal.h"
   29 
   30 static int INTEGRITY_read_superblock(struct crypt_device *cd,
   31                      struct device *device,
   32                      uint64_t offset, struct superblock *sb)
   33 {
   34     int devfd, r;
   35 
   36     devfd = device_open(cd, device, O_RDONLY);
   37     if(devfd < 0)
   38         return -EINVAL;
   39 
   40     if (read_lseek_blockwise(devfd, device_block_size(cd, device),
   41         device_alignment(device), sb, sizeof(*sb), offset) != sizeof(*sb) ||
   42         memcmp(sb->magic, SB_MAGIC, sizeof(sb->magic)) ||
   43         sb->version < SB_VERSION_1 || sb->version > SB_VERSION_5) {
   44         log_std(cd, "No integrity superblock detected on %s.\n",
   45             device_path(device));
   46         r = -EINVAL;
   47     } else {
   48         sb->integrity_tag_size = le16toh(sb->integrity_tag_size);
   49         sb->journal_sections = le32toh(sb->journal_sections);
   50         sb->provided_data_sectors = le64toh(sb->provided_data_sectors);
   51         sb->recalc_sector = le64toh(sb->recalc_sector);
   52         sb->flags = le32toh(sb->flags);
   53         r = 0;
   54     }
   55 
   56     return r;
   57 }
   58 
   59 int INTEGRITY_read_sb(struct crypt_device *cd,
   60               struct crypt_params_integrity *params,
   61               uint32_t *flags)
   62 {
   63     struct superblock sb;
   64     int r;
   65 
   66     r = INTEGRITY_read_superblock(cd, crypt_metadata_device(cd), 0, &sb);
   67     if (r)
   68         return r;
   69 
   70     params->sector_size = SECTOR_SIZE << sb.log2_sectors_per_block;
   71     params->tag_size = sb.integrity_tag_size;
   72 
   73     if (flags)
   74         *flags = sb.flags;
   75 
   76     return 0;
   77 }
   78 
   79 int INTEGRITY_dump(struct crypt_device *cd, struct device *device, uint64_t offset)
   80 {
   81     struct superblock sb;
   82     int r;
   83 
   84     r = INTEGRITY_read_superblock(cd, device, offset, &sb);
   85     if (r)
   86         return r;
   87 
   88     log_std(cd, "Info for integrity device %s.\n", device_path(device));
   89     log_std(cd, "superblock_version %d\n", (unsigned)sb.version);
   90     log_std(cd, "log2_interleave_sectors %d\n", sb.log2_interleave_sectors);
   91     log_std(cd, "integrity_tag_size %u\n", sb.integrity_tag_size);
   92     log_std(cd, "journal_sections %u\n", sb.journal_sections);
   93     log_std(cd, "provided_data_sectors %" PRIu64 "\n", sb.provided_data_sectors);
   94     log_std(cd, "sector_size %u\n", SECTOR_SIZE << sb.log2_sectors_per_block);
   95     if (sb.version >= SB_VERSION_2 && (sb.flags & SB_FLAG_RECALCULATING))
   96         log_std(cd, "recalc_sector %" PRIu64 "\n", sb.recalc_sector);
   97     log_std(cd, "log2_blocks_per_bitmap %u\n", sb.log2_blocks_per_bitmap_bit);
   98     log_std(cd, "flags %s%s%s%s%s\n",
   99         sb.flags & SB_FLAG_HAVE_JOURNAL_MAC ? "have_journal_mac " : "",
  100         sb.flags & SB_FLAG_RECALCULATING ? "recalculating " : "",
  101         sb.flags & SB_FLAG_DIRTY_BITMAP ? "dirty_bitmap " : "",
  102         sb.flags & SB_FLAG_FIXED_PADDING ? "fix_padding " : "",
  103         sb.flags & SB_FLAG_FIXED_HMAC ? "fix_hmac " : "");
  104 
  105     return 0;
  106 }
  107 
  108 int INTEGRITY_data_sectors(struct crypt_device *cd,
  109                struct device *device, uint64_t offset,
  110                uint64_t *data_sectors)
  111 {
  112     struct superblock sb;
  113     int r;
  114 
  115     r = INTEGRITY_read_superblock(cd, device, offset, &sb);
  116     if (r)
  117         return r;
  118 
  119     *data_sectors = sb.provided_data_sectors;
  120     return 0;
  121 }
  122 
  123 int INTEGRITY_key_size(struct crypt_device *cd __attribute__((unused)), const char *integrity)
  124 {
  125     if (!integrity)
  126         return 0;
  127 
  128     //FIXME: use crypto backend hash size
  129     if (!strcmp(integrity, "aead"))
  130         return 0;
  131     else if (!strcmp(integrity, "hmac(sha1)"))
  132         return 20;
  133     else if (!strcmp(integrity, "hmac(sha256)"))
  134         return 32;
  135     else if (!strcmp(integrity, "hmac(sha512)"))
  136         return 64;
  137     else if (!strcmp(integrity, "poly1305"))
  138         return 0;
  139     else if (!strcmp(integrity, "none"))
  140         return 0;
  141 
  142     return -EINVAL;
  143 }
  144 
  145 /* Return hash or hmac(hash) size, if known */
  146 int INTEGRITY_hash_tag_size(const char *integrity)
  147 {
  148     char hash[MAX_CIPHER_LEN];
  149     int r;
  150 
  151     if (!integrity)
  152         return 0;
  153 
  154     if (!strcmp(integrity, "crc32") || !strcmp(integrity, "crc32c"))
  155         return 4;
  156 
  157     r = sscanf(integrity, "hmac(%" MAX_CIPHER_LEN_STR "[^)]s", hash);
  158     if (r == 1)
  159         r = crypt_hash_size(hash);
  160     else
  161         r = crypt_hash_size(integrity);
  162 
  163     return r < 0 ? 0 : r;
  164 }
  165 
  166 int INTEGRITY_tag_size(struct crypt_device *cd __attribute__((unused)),
  167                const char *integrity,
  168                const char *cipher,
  169                const char *cipher_mode)
  170 {
  171     int iv_tag_size = 0, auth_tag_size = 0;
  172 
  173     if (!cipher_mode)
  174         iv_tag_size = 0;
  175     else if (!strcmp(cipher_mode, "xts-random"))
  176         iv_tag_size = 16;
  177     else if (!strcmp(cipher_mode, "gcm-random"))
  178         iv_tag_size = 12;
  179     else if (!strcmp(cipher_mode, "ccm-random"))
  180         iv_tag_size = 8;
  181     else if (!strcmp(cipher_mode, "ctr-random"))
  182         iv_tag_size = 16;
  183     else if (!strcmp(cipher, "aegis256") && !strcmp(cipher_mode, "random"))
  184         iv_tag_size = 32;
  185     else if (!strcmp(cipher_mode, "random"))
  186         iv_tag_size = 16;
  187 
  188     //FIXME: use crypto backend hash size
  189     if (!integrity || !strcmp(integrity, "none"))
  190         auth_tag_size = 0;
  191     else if (!strcmp(integrity, "aead"))
  192         auth_tag_size = 16; /* gcm- mode only */
  193     else if (!strcmp(integrity, "cmac(aes)"))
  194         auth_tag_size = 16;
  195     else if (!strcmp(integrity, "hmac(sha1)"))
  196         auth_tag_size = 20;
  197     else if (!strcmp(integrity, "hmac(sha256)"))
  198         auth_tag_size = 32;
  199     else if (!strcmp(integrity, "hmac(sha512)"))
  200         auth_tag_size = 64;
  201     else if (!strcmp(integrity, "poly1305")) {
  202         if (iv_tag_size)
  203             iv_tag_size = 12;
  204         auth_tag_size = 16;
  205     }
  206 
  207     return iv_tag_size + auth_tag_size;
  208 }
  209 
  210 int INTEGRITY_create_dmd_device(struct crypt_device *cd,
  211                const struct crypt_params_integrity *params,
  212                struct volume_key *vk,
  213                struct volume_key *journal_crypt_key,
  214                struct volume_key *journal_mac_key,
  215                struct crypt_dm_active_device *dmd,
  216                uint32_t flags, uint32_t sb_flags)
  217 {
  218     int r;
  219 
  220     if (!dmd)
  221         return -EINVAL;
  222 
  223     *dmd = (struct crypt_dm_active_device) {
  224         .flags = flags,
  225     };
  226 
  227     /* Workaround for kernel dm-integrity table bug */
  228     if (sb_flags & SB_FLAG_RECALCULATING)
  229         dmd->flags |= CRYPT_ACTIVATE_RECALCULATE;
  230 
  231     r = INTEGRITY_data_sectors(cd, crypt_metadata_device(cd),
  232                    crypt_get_data_offset(cd) * SECTOR_SIZE, &dmd->size);
  233     if (r < 0)
  234         return r;
  235 
  236     return dm_integrity_target_set(cd, &dmd->segment, 0, dmd->size,
  237             crypt_metadata_device(cd), crypt_data_device(cd),
  238             crypt_get_integrity_tag_size(cd), crypt_get_data_offset(cd),
  239             crypt_get_sector_size(cd), vk, journal_crypt_key,
  240             journal_mac_key, params);
  241 }
  242 
  243 int INTEGRITY_activate_dmd_device(struct crypt_device *cd,
  244                const char *name,
  245                const char *type,
  246                struct crypt_dm_active_device *dmd,
  247                uint32_t sb_flags)
  248 {
  249     int r;
  250     uint32_t dmi_flags;
  251     struct dm_target *tgt = &dmd->segment;
  252 
  253     if (!single_segment(dmd) || tgt->type != DM_INTEGRITY)
  254         return -EINVAL;
  255 
  256     log_dbg(cd, "Trying to activate INTEGRITY device on top of %s, using name %s, tag size %d, provided sectors %" PRIu64".",
  257         device_path(tgt->data_device), name, tgt->u.integrity.tag_size, dmd->size);
  258 
  259     r = device_block_adjust(cd, tgt->data_device, DEV_EXCL,
  260                 tgt->u.integrity.offset, NULL, &dmd->flags);
  261     if (r)
  262         return r;
  263 
  264     if (tgt->u.integrity.meta_device) {
  265         r = device_block_adjust(cd, tgt->u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL);
  266         if (r)
  267             return r;
  268     }
  269 
  270     r = dm_create_device(cd, name, type, dmd);
  271     if (r < 0 && (dm_flags(cd, DM_INTEGRITY, &dmi_flags) || !(dmi_flags & DM_INTEGRITY_SUPPORTED))) {
  272         log_err(cd, _("Kernel does not support dm-integrity mapping."));
  273         return -ENOTSUP;
  274     }
  275 
  276     if (r < 0 && (sb_flags & SB_FLAG_FIXED_PADDING) && !dm_flags(cd, DM_INTEGRITY, &dmi_flags) &&
  277         !(dmi_flags & DM_INTEGRITY_FIX_PADDING_SUPPORTED)) {
  278         log_err(cd, _("Kernel does not support dm-integrity fixed metadata alignment."));
  279         return -ENOTSUP;
  280     }
  281 
  282     if (r < 0 && (dmd->flags & CRYPT_ACTIVATE_RECALCULATE) &&
  283         !(crypt_get_compatibility(cd) & CRYPT_COMPAT_LEGACY_INTEGRITY_RECALC) &&
  284         ((sb_flags & SB_FLAG_FIXED_HMAC) ?
  285         (tgt->u.integrity.vk && !tgt->u.integrity.journal_integrity_key) :
  286         (tgt->u.integrity.vk || tgt->u.integrity.journal_integrity_key))) {
  287         log_err(cd, _("Kernel refuses to activate insecure recalculate option (see legacy activation options to override)."));
  288         return -ENOTSUP;
  289     }
  290 
  291     return r;
  292 }
  293 
  294 int INTEGRITY_activate(struct crypt_device *cd,
  295                const char *name,
  296                const struct crypt_params_integrity *params,
  297                struct volume_key *vk,
  298                struct volume_key *journal_crypt_key,
  299                struct volume_key *journal_mac_key,
  300                uint32_t flags, uint32_t sb_flags)
  301 {
  302     struct crypt_dm_active_device dmd = {};
  303     int r = INTEGRITY_create_dmd_device(cd, params, vk, journal_crypt_key,
  304                         journal_mac_key, &dmd, flags, sb_flags);
  305 
  306     if (r < 0)
  307         return r;
  308 
  309     r = INTEGRITY_activate_dmd_device(cd, name, CRYPT_INTEGRITY, &dmd, sb_flags);
  310     dm_targets_free(cd, &dmd);
  311     return r;
  312 }
  313 
  314 int INTEGRITY_format(struct crypt_device *cd,
  315              const struct crypt_params_integrity *params,
  316              struct volume_key *journal_crypt_key,
  317              struct volume_key *journal_mac_key)
  318 {
  319     uint32_t dmi_flags;
  320     char tmp_name[64], tmp_uuid[40];
  321     struct crypt_dm_active_device dmdi = {
  322         .size = 8,
  323         .flags = CRYPT_ACTIVATE_PRIVATE, /* We always create journal but it can be unused later */
  324     };
  325     struct dm_target *tgt = &dmdi.segment;
  326     int r;
  327     uuid_t tmp_uuid_bin;
  328     struct volume_key *vk = NULL;
  329 
  330     uuid_generate(tmp_uuid_bin);
  331     uuid_unparse(tmp_uuid_bin, tmp_uuid);
  332 
  333     r = snprintf(tmp_name, sizeof(tmp_name), "temporary-cryptsetup-%s", tmp_uuid);
  334     if (r < 0 || (size_t)r >= sizeof(tmp_name))
  335         return -EINVAL;
  336 
  337     /* There is no data area, we can actually use fake zeroed key */
  338     if (params && params->integrity_key_size)
  339         vk = crypt_alloc_volume_key(params->integrity_key_size, NULL);
  340 
  341     r = dm_integrity_target_set(cd, tgt, 0, dmdi.size, crypt_metadata_device(cd),
  342             crypt_data_device(cd), crypt_get_integrity_tag_size(cd),
  343             crypt_get_data_offset(cd), crypt_get_sector_size(cd), vk,
  344             journal_crypt_key, journal_mac_key, params);
  345     if (r < 0) {
  346         crypt_free_volume_key(vk);
  347         return r;
  348     }
  349 
  350     log_dbg(cd, "Trying to format INTEGRITY device on top of %s, tmp name %s, tag size %d.",
  351         device_path(tgt->data_device), tmp_name, tgt->u.integrity.tag_size);
  352 
  353     r = device_block_adjust(cd, tgt->data_device, DEV_EXCL, tgt->u.integrity.offset, NULL, NULL);
  354     if (r < 0 && (dm_flags(cd, DM_INTEGRITY, &dmi_flags) || !(dmi_flags & DM_INTEGRITY_SUPPORTED))) {
  355         log_err(cd, _("Kernel does not support dm-integrity mapping."));
  356         r = -ENOTSUP;
  357     }
  358     if (r) {
  359         dm_targets_free(cd, &dmdi);
  360         return r;
  361     }
  362 
  363     if (tgt->u.integrity.meta_device) {
  364         r = device_block_adjust(cd, tgt->u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL);
  365         if (r) {
  366             dm_targets_free(cd, &dmdi);
  367             return r;
  368         }
  369     }
  370 
  371     r = dm_create_device(cd, tmp_name, CRYPT_INTEGRITY, &dmdi);
  372     crypt_free_volume_key(vk);
  373     dm_targets_free(cd, &dmdi);
  374     if (r)
  375         return r;
  376 
  377     return dm_remove_device(cd, tmp_name, CRYPT_DEACTIVATE_FORCE);
  378 }