On Sun, 21 Mar 2021 at 00:51:06 +0100, Guilhem Moulin wrote: > I'm seeking pre-approval to upload cryptsetup/2:2.3.5-1 to unstable. > Hopefully the attached debdiff is not too large/intrusive for inclusion > in Bullseye. For convenience I'm also attaching a diff with the ‘tests’ > and ‘po’ directories (which arguably add a lot of clutter), as well as a > diff with only upstream's ‘lib’ and ‘src’ directories (given it's really > what this is about). Ooops Savaltore pointed out that the message wasn't delivered to debian-release@l.d.o due to its attachment size. Not a good sign perhaps, although most of the clutter is due to offset updates in .po file annotations… The original attachments can be found at https://bugs.debian.org/985629 , but here is a new try with only .debdiff-trimmed (source tree diff excluding ‘tests’ and ‘po’ directories) , as well as the output of `git diff -b debian/2%2.3.4-2 -- lib src debian` *after* reverting upstream's copyright year update in headings (so preserving only upstream code changes and debian changes). -- Guilhem.
Attachment:
cryptsetup.debdiff-trimmed.gz
Description: application/gzip
diff --git a/debian/README.source b/debian/README.source index 15c40923..a24270ac 100644 --- a/debian/README.source +++ b/debian/README.source @@ -21,31 +21,19 @@ Importing a new upstream release git remote add upstream https://gitlab.com/cryptsetup/cryptsetup.git git config remote.upstream.fetch +refs/heads/master:refs/remotes/upstream/master git branch -u refs/remotes/upstream/master upstream/latest + git config remote.upstream.tagOpt --tags - 1. Update 'upstream' remote - - git fetch --tags upstream - - 2. Determine the release tag corresponding to the new release. At the time - of this writing, upstream uses tags in the form: - - TAG="v$VERSION" - - This convention may change, so double-check with 'git tag'. - - 3. Validate the gpg signature for this release tag: - - git verify-tag "$TAG" - - The signing key can be found in 'debian/upstream-signing-key.asc'. - Use `gpg -q debian/upstream-signing-key.asc` to show its - fingerprint. - - 4. Merge the upstream release tag <tag> into the 'debian/latest' + 1. Merge the newest upstream release tag (pass --upstream-version=$VERSION + if you want a specific upstream version) into the 'debian/latest' branch of your packaging repository: - git checkout debian/latest - git merge -m "Updated version $VERSION from '$TAG'" "$TAG" + gbp import-orig --uscan + + That commands does all the magic, namely + - updating the `upstream` remote, + - verifying the cryptographic signature on the upstream tag 'v$VERSION', + - creating a new tag 'upstream/$VERSION' with 'v$VERSION' as additional parent, and + - merging 'upstream/$VERSION' it into 'debian/latest' N. After development and testing, the final packages to be uploaded to Debian are built and tagged in the repository as follows: diff --git a/debian/changelog b/debian/changelog index cf73b180..9f71768f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,18 @@ +cryptsetup (2:2.3.5-1) unstable; urgency=medium + + * New upstream bugfix release. (Closes: #985581) + * d/watch: Monitor upstream tags rather than tarballs. + * d/gbp.conf: Set 'upstream-vcs-tag' to add upstream tag as additional + parent. + * Simplify d/README.source in accordance with the above. + * Rename d/upstream-signing-key.asc to d/upstream/signing-key.asc as uscan + is now able to verify git tags. + * encrypted-boot.md: Clarify how to solve double password prompt for the + device holding /boot. + * d/copyright: Update copyright year. + + -- Guilhem Moulin <guilhem@debian.org> Thu, 11 Mar 2021 23:33:13 +0100 + cryptsetup (2:2.3.4-2) unstable; urgency=medium [ Guilhem Moulin ] diff --git a/debian/copyright b/debian/copyright index 058d0c35..8ce5f177 100644 --- a/debian/copyright +++ b/debian/copyright @@ -5,8 +5,8 @@ Source: https://gitlab.com/cryptsetup/cryptsetup Files: * Copyright: © 2004 Christophe Saout <christophe@saout.de> © 2004-2008 Clemens Fruhwirth <clemens@endorphin.org> - © 2008-2019 Red Hat, Inc. - © 2008-2019 Milan Broz <gmazyland@gmail.com> + © 2008-2021 Red Hat, Inc. + © 2008-2021 Milan Broz <gmazyland@gmail.com> License: GPL-2+ with OpenSSL exception Files: debian/* @@ -45,7 +45,7 @@ Copyright: © 2005-2015 Jonas Meurer <jonas@freesources.org> License: GPL-2+ Files: docs/examples/* -Copyright: © 2011-2019 Red Hat, Inc. +Copyright: © 2011-2021 Red Hat, Inc. License: LGPL-2.1+ Files: lib/base64.c @@ -53,13 +53,13 @@ Copyright: © 1999-2001, 2004-2006, 2009-2018 Free Software Foundation, Inc. License: GPL-2+ Files: lib/crypto_backend/* lib/loopaes/* lib/tcrypt/* lib/verity/* -Copyright: © 2009-2019 Red Hat, Inc. - © 2010-2019 Milan Broz <gmazyland@gmail.com> +Copyright: © 2009-2021 Red Hat, Inc. + © 2010-2021 Milan Broz <gmazyland@gmail.com> License: LGPL-2.1+ Files: lib/crypto_backend/crypto_openssl.c -Copyright: © 2009-2019 Red Hat, Inc. - © 2010-2019 Milan Broz <gmazyland@gmail.com> +Copyright: © 2009-2021 Red Hat, Inc. + © 2010-2021 Milan Broz <gmazyland@gmail.com> License: LGPL-2.1+ with OpenSSL exception Files: lib/crypto_backend/argon2/* diff --git a/debian/doc/pandoc/encrypted-boot.md b/debian/doc/pandoc/encrypted-boot.md index 39ca4deb..52db07f4 100644 --- a/debian/doc/pandoc/encrypted-boot.md +++ b/debian/doc/pandoc/encrypted-boot.md @@ -158,10 +158,11 @@ system: no need to reboot into a live CD or an initramfs shell. You can skip the next sub-section and go directly to [Enabling `cryptomount` in GRUB2]. Note that `init`(1) needs to unlock the `/boot` partition *again* during the boot process. See [Avoiding the -extra password prompt] for details and a proposed workaround. (Only -steps 1-3 from that section are relevant here; no need to copy the key -file to the initramfs image since `/boot` can be unlocked and mounted -later during the boot process.) +extra password prompt] for details and a proposed workaround. (You'll +need to substitute `/` resp. `sda5` with `/boot` resp. `sda1` in that +section, however only steps 1-3 are relevant here: no need to copy the +key file to the initramfs image since `/boot` can be unlocked and +mounted later during the boot process.) Moving `/boot` to the root file system diff --git a/debian/gbp.conf b/debian/gbp.conf index d9d405ee..823bf5ba 100644 --- a/debian/gbp.conf +++ b/debian/gbp.conf @@ -1,5 +1,7 @@ [DEFAULT] debian-branch = debian/latest upstream-branch = upstream/latest -upstream-tag = v%(version)s pristine-tar = False + +[import-orig] +upstream-vcs-tag = v%(version)s diff --git a/debian/upstream-signing-key.asc b/debian/upstream/signing-key.asc similarity index 100% rename from debian/upstream-signing-key.asc rename to debian/upstream/signing-key.asc diff --git a/debian/watch b/debian/watch index 39eee672..161cf2c2 100644 --- a/debian/watch +++ b/debian/watch @@ -1,4 +1,4 @@ version=4 -#options="decompress,pgpsigurlmangle=s/\.(?:gz|xz)$/.sign/" \ -https://www.kernel.org/pub/linux/utils/cryptsetup/v(\d[\d\.]*)/ \ - cryptsetup-(\d[\d\.]*)\.tar\.(?:gz|xz) +options="mode=git,pgpmode=gittag" \ + https://gitlab.com/cryptsetup/cryptsetup.git \ + refs/tags/v?@ANY_VERSION@ diff --git a/lib/bitlk/bitlk.c b/lib/bitlk/bitlk.c index ea3d0c5b..9c3fba37 100644 --- a/lib/bitlk/bitlk.c +++ b/lib/bitlk/bitlk.c @@ -54,6 +54,9 @@ #define BITLK_RECOVERY_PARTS 8 #define BITLK_RECOVERY_PART_LEN 6 +#define BITLK_BEK_FILE_HEADER_LEN 48 +#define BITLK_STARTUP_KEY_HEADER_LEN 24 + #define BITLK_KDF_HASH "sha256" #define BITLK_KDF_ITERATION_COUNT 0x100000 @@ -162,6 +165,18 @@ struct bitlk_kdf_data { uint64_t count; }; +struct bitlk_bek_header { + uint32_t metadata_size; + uint32_t metadata_version; + uint32_t metadata_header_size; + uint32_t metada_size_copy; + struct bitlk_guid guid; + uint32_t next_nonce; + uint16_t encryption; + uint16_t unknown; + uint64_t creation_time; +} __attribute__ ((packed)); + static BITLKVMKProtection get_vmk_protection(uint16_t protection) { switch (protection) { @@ -311,7 +326,9 @@ static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, in bool supported = false; /* only passphrase or recovery passphrase vmks are supported (can be used to activate) */ - supported = (*vmk)->protection == BITLK_PROTECTION_PASSPHRASE || (*vmk)->protection == BITLK_PROTECTION_RECOVERY_PASSPHRASE; + supported = (*vmk)->protection == BITLK_PROTECTION_PASSPHRASE || + (*vmk)->protection == BITLK_PROTECTION_RECOVERY_PASSPHRASE || + (*vmk)->protection == BITLK_PROTECTION_STARTUP_KEY; while (end - start > 2) { /* size of this entry */ @@ -394,6 +411,9 @@ static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, in (*vmk)->name = string; string = NULL; } + /* no idea what this is, lets hope it's not important */ + } else if (key_entry_value == BITLK_ENTRY_VALUE_USE_KEY && (*vmk)->protection == BITLK_PROTECTION_STARTUP_KEY) { + ; } else { if (supported) { log_err(cd, _("Unexpected metadata entry value '%u' found when parsing supported Volume Master Key."), key_entry_value); @@ -436,6 +456,9 @@ void BITLK_bitlk_vmk_free(struct bitlk_vmk *vmk) void BITLK_bitlk_metadata_free(struct bitlk_metadata *metadata) { + if (!metadata) + return; + free(metadata->guid); if (metadata->description) free(metadata->description); @@ -481,18 +504,6 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) goto out; } - if (memcmp(sig.boot_code, BITLK_BOOTCODE_V1, sizeof(sig.boot_code)) == 0) { - log_err(cd, _("BITLK version 1 is currently not supported.")); - r = -ENOTSUP; - goto out; - } else if (memcmp(sig.boot_code, BITLK_BOOTCODE_V2, sizeof(sig.boot_code)) == 0) - ; - else { - log_err(cd, _("Invalid or unknown boot signature for BITLK device.")); - r = -EINVAL; - goto out; - } - if (memcmp(sig.signature, BITLK_SIGNATURE, sizeof(sig.signature)) == 0) { params->togo = false; fve_offset = BITLK_HEADER_METADATA_OFFSET; @@ -505,6 +516,18 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) goto out; } + if (memcmp(sig.boot_code, BITLK_BOOTCODE_V1, sizeof(sig.boot_code)) == 0) { + log_err(cd, _("BITLK version 1 is currently not supported.")); + r = -ENOTSUP; + goto out; + } else if (memcmp(sig.boot_code, BITLK_BOOTCODE_V2, sizeof(sig.boot_code)) == 0) + ; + else { + log_err(cd, _("Invalid or unknown boot signature for BITLK device.")); + r = -EINVAL; + goto out; + } + params->sector_size = le16_to_cpu(sig.sector_size); if (params->sector_size == 0) { log_dbg(cd, "Got sector size 0, assuming 512."); @@ -564,12 +587,12 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) switch (le16_to_cpu(fve.encryption)) { /* AES-CBC with Elephant difuser */ case 0x8000: - params->key_size = 128; + params->key_size = 256; params->cipher = "aes"; params->cipher_mode = "cbc-elephant"; break; case 0x8001: - params->key_size = 256; + params->key_size = 512; params->cipher = "aes"; params->cipher_mode = "cbc-elephant"; break; @@ -586,12 +609,12 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) break; /* AES-XTS */ case 0x8004: - params->key_size = 128; + params->key_size = 256; params->cipher = "aes"; params->cipher_mode = "xts-plain64"; break; case 0x8005: - params->key_size = 256; + params->key_size = 512; params->cipher = "aes"; params->cipher_mode = "xts-plain64"; break; @@ -628,7 +651,7 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) if (read_lseek_blockwise(devfd, device_block_size(cd, device), device_alignment(device), fve_entries, fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN, - params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN) != fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN) { + params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN) != (ssize_t)(fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN)) { log_err(cd, _("Failed to read BITLK metadata entries from %s."), device_path(device)); r = -EINVAL; goto out; @@ -654,6 +677,10 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) sizeof(entry_vmk)); vmk = malloc(sizeof(struct bitlk_vmk)); + if (!vmk) { + r = -ENOMEM; + goto out; + } memset(vmk, 0, sizeof(struct bitlk_vmk)); guid_to_string(&entry_vmk.guid, guid_buf); @@ -682,6 +709,10 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) /* FVEK */ } else if (entry_type == BITLK_ENTRY_TYPE_FVEK) { params->fvek = malloc(sizeof(struct bitlk_fvek)); + if (!params->fvek) { + r = -ENOMEM; + goto out; + } memcpy(params->fvek->nonce, fve_entries + start + BITLK_ENTRY_HEADER_LEN, sizeof(params->fvek->nonce)); @@ -838,6 +869,120 @@ static int get_recovery_key(struct crypt_device *cd, return 0; } +static int parse_external_key_entry(struct crypt_device *cd, const char *data, int start, int end, struct volume_key **vk) +{ + uint16_t key_entry_size = 0; + uint16_t key_entry_type = 0; + uint16_t key_entry_value = 0; + size_t key_size = 0; + const char *key = NULL; + + while (end - start > 2) { + /* size of this entry */ + memcpy(&key_entry_size, data + start, sizeof(key_entry_size)); + key_entry_size = le16_to_cpu(key_entry_size); + if (key_entry_size == 0) + break; + + /* type and value of this entry */ + memcpy(&key_entry_type, data + start + sizeof(key_entry_size), sizeof(key_entry_type)); + memcpy(&key_entry_value, + data + start + sizeof(key_entry_size) + sizeof(key_entry_type), + sizeof(key_entry_value)); + key_entry_type = le16_to_cpu(key_entry_type); + key_entry_value = le16_to_cpu(key_entry_value); + + /* only properties should be in this entry */ + if (key_entry_type != BITLK_ENTRY_TYPE_PROPERTY) { + log_err(cd, _("Unexpected metadata entry type '%u' found when parsing external key."), key_entry_type); + return -EINVAL; + } + + if (key_entry_value == BITLK_ENTRY_VALUE_KEY) { + key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + 4); + key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + 4; + *vk = crypt_alloc_volume_key(key_size, key); + if (*vk == NULL) + return -ENOMEM; + return 0; + /* optional "ExternalKey" string, we can safely ignore it */ + } else if (key_entry_value == BITLK_ENTRY_VALUE_STRING) + ; + else { + log_err(cd, _("Unexpected metadata entry value '%u' found when parsing external key."), key_entry_value); + return -EINVAL; + } + + start += key_entry_size; + } + + /* if we got here we failed to parse the metadata */ + return -EINVAL; +} + +/* check if given passphrase can be a startup key (has right format) and convert it */ +static int get_startup_key(struct crypt_device *cd, + const char *password, + size_t passwordLen, + const struct bitlk_vmk *vmk, + struct volume_key **su_key) +{ + struct bitlk_bek_header bek_header = {0}; + char guid_buf[UUID_STR_LEN] = {0}; + + uint16_t key_entry_size = 0; + uint16_t key_entry_type = 0; + uint16_t key_entry_value = 0; + + if (passwordLen < BITLK_BEK_FILE_HEADER_LEN) + return -EPERM; + + memcpy(&bek_header, password, BITLK_BEK_FILE_HEADER_LEN); + + /* metadata should contain GUID of the VMK this startup key is used for */ + guid_to_string(&bek_header.guid, guid_buf); + if (strcmp(guid_buf, vmk->guid) == 0) + log_dbg(cd, "Found matching startup key for VMK %s", vmk->guid); + else + return -EPERM; + + if (bek_header.metadata_version != 1) { + log_err(cd, "Unsupported BEK metadata version %" PRIu32 "", bek_header.metadata_version); + return -ENOTSUP; + } + + if (bek_header.metadata_size != passwordLen) { + log_err(cd, "Unexpected BEK metadata size %" PRIu32 " does not match BEK file length", bek_header.metadata_size); + return -EINVAL; + } + + /* we are expecting exactly one metadata entry starting immediately after the header */ + memcpy(&key_entry_size, password + BITLK_BEK_FILE_HEADER_LEN, sizeof(key_entry_size)); + key_entry_size = le16_to_cpu(key_entry_size); + if (key_entry_size < BITLK_ENTRY_HEADER_LEN) { + log_dbg(cd, "Unexpected metadata entry size %" PRIu16 " when parsing BEK file", key_entry_size); + return -EINVAL; + } + + /* type and value of this entry */ + memcpy(&key_entry_type, password + BITLK_BEK_FILE_HEADER_LEN + sizeof(key_entry_size), sizeof(key_entry_type)); + memcpy(&key_entry_value, + password + BITLK_BEK_FILE_HEADER_LEN + sizeof(key_entry_size) + sizeof(key_entry_type), + sizeof(key_entry_value)); + key_entry_type = le16_to_cpu(key_entry_type); + key_entry_value = le16_to_cpu(key_entry_value); + + if (key_entry_type == BITLK_ENTRY_TYPE_STARTUP_KEY && key_entry_value == BITLK_ENTRY_VALUE_EXTERNAL_KEY) { + return parse_external_key_entry(cd, password, + BITLK_BEK_FILE_HEADER_LEN + BITLK_ENTRY_HEADER_LEN + BITLK_STARTUP_KEY_HEADER_LEN, + passwordLen, su_key); + } else { + log_err(cd, _("Unexpected metadata entry found when parsing startup key.")); + log_dbg(cd, "Entry type: %u, entry value: %u", key_entry_type, key_entry_value); + return -EINVAL; + } +} + static int bitlk_kdf(struct crypt_device *cd, const char *password, size_t passwordLen, @@ -939,7 +1084,7 @@ static int decrypt_key(struct crypt_device *cd, } if (is_fvek && strcmp(crypt_get_cipher_mode(cd), "cbc-elephant") == 0 && - crypt_get_volume_key_size(cd) == 16) { + crypt_get_volume_key_size(cd) == 32) { /* 128bit AES-CBC with Elephant -- key size is 256 bit (2 keys) but key data is 512 bits, data: 16B CBC key, 16B empty, 16B elephant key, 16B empty */ memcpy(outbuf + 16 + BITLK_OPEN_KEY_METADATA_LEN, @@ -1000,12 +1145,18 @@ int BITLK_activate(struct crypt_device *cd, while (next_vmk) { if (next_vmk->protection == BITLK_PROTECTION_PASSPHRASE) { r = bitlk_kdf(cd, password, passwordLen, false, next_vmk->salt, &vmk_dec_key); - if (r) - return r; + if (r) { + /* something wrong happened, but we still want to check other key slots */ + next_vmk = next_vmk->next; + continue; + } } else if (next_vmk->protection == BITLK_PROTECTION_RECOVERY_PASSPHRASE) { r = get_recovery_key(cd, password, passwordLen, &recovery_key); - if (r) - return r; + if (r) { + /* something wrong happened, but we still want to check other key slots */ + next_vmk = next_vmk->next; + continue; + } if (recovery_key == NULL) { /* r = 0 but no key -> given passphrase is not a recovery passphrase */ r = -EPERM; @@ -1018,8 +1169,15 @@ int BITLK_activate(struct crypt_device *cd, crypt_free_volume_key(recovery_key); if (r) return r; + } else if (next_vmk->protection == BITLK_PROTECTION_STARTUP_KEY) { + r = get_startup_key(cd, password, passwordLen, next_vmk, &vmk_dec_key); + if (r) { + next_vmk = next_vmk->next; + continue; + } + log_dbg(cd, "Trying to use external key found in provided password."); } else { - /* only passphrase and recovery passphrase VMKs supported right now */ + /* only passphrase, recovery passphrase and startup key VMKs supported right now */ log_dbg(cd, "Skipping %s", get_vmk_protection_string(next_vmk->protection)); next_vmk = next_vmk->next; if (r == 0) diff --git a/lib/crypto_backend/crypto_cipher_kernel.c b/lib/crypto_backend/crypto_cipher_kernel.c index 1a8aecfc..b2a92b51 100644 --- a/lib/crypto_backend/crypto_cipher_kernel.c +++ b/lib/crypto_backend/crypto_cipher_kernel.c @@ -152,6 +152,9 @@ static int _crypt_cipher_crypt(struct crypt_cipher_kernel *ctx, /* Set IV */ if (iv) { header = CMSG_NXTHDR(&msg, header); + if (!header) + return -EINVAL; + header->cmsg_level = SOL_ALG; header->cmsg_type = ALG_SET_IV; header->cmsg_len = iv_msg_size; diff --git a/lib/crypto_backend/pbkdf_check.c b/lib/crypto_backend/pbkdf_check.c index 7444e0aa..2c86036c 100644 --- a/lib/crypto_backend/pbkdf_check.c +++ b/lib/crypto_backend/pbkdf_check.c @@ -361,8 +361,10 @@ static int crypt_pbkdf_check(const char *kdf, const char *hash, ms = time_ms(&rstart, &rend); if (ms) { PBKDF2_temp = (double)iterations * target_ms / ms; - if (PBKDF2_temp > UINT32_MAX) - return -EINVAL; + if (PBKDF2_temp > UINT32_MAX) { + r = -EINVAL; + goto out; + } *iter_secs = (uint32_t)PBKDF2_temp; } diff --git a/lib/integrity/integrity.c b/lib/integrity/integrity.c index 86305ce4..39b6a39f 100644 --- a/lib/integrity/integrity.c +++ b/lib/integrity/integrity.c @@ -40,7 +40,7 @@ static int INTEGRITY_read_superblock(struct crypt_device *cd, 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_4) { + sb->version < SB_VERSION_1 || sb->version > SB_VERSION_5) { log_std(cd, "No integrity superblock detected on %s.\n", device_path(device)); r = -EINVAL; @@ -92,14 +92,15 @@ int INTEGRITY_dump(struct crypt_device *cd, struct device *device, uint64_t offs log_std(cd, "journal_sections %u\n", sb.journal_sections); log_std(cd, "provided_data_sectors %" PRIu64 "\n", sb.provided_data_sectors); log_std(cd, "sector_size %u\n", SECTOR_SIZE << sb.log2_sectors_per_block); - if (sb.version == SB_VERSION_2 && (sb.flags & SB_FLAG_RECALCULATING)) + if (sb.version >= SB_VERSION_2 && (sb.flags & SB_FLAG_RECALCULATING)) log_std(cd, "recalc_sector %" PRIu64 "\n", sb.recalc_sector); log_std(cd, "log2_blocks_per_bitmap %u\n", sb.log2_blocks_per_bitmap_bit); - log_std(cd, "flags %s%s%s%s\n", + log_std(cd, "flags %s%s%s%s%s\n", sb.flags & SB_FLAG_HAVE_JOURNAL_MAC ? "have_journal_mac " : "", sb.flags & SB_FLAG_RECALCULATING ? "recalculating " : "", sb.flags & SB_FLAG_DIRTY_BITMAP ? "dirty_bitmap " : "", - sb.flags & SB_FLAG_FIXED_PADDING ? "fix_padding " : ""); + sb.flags & SB_FLAG_FIXED_PADDING ? "fix_padding " : "", + sb.flags & SB_FLAG_FIXED_HMAC ? "fix_hmac " : ""); return 0; } @@ -278,6 +279,15 @@ int INTEGRITY_activate_dmd_device(struct crypt_device *cd, return -ENOTSUP; } + if (r < 0 && (dmd->flags & CRYPT_ACTIVATE_RECALCULATE) && + !(crypt_get_compatibility(cd) & CRYPT_COMPAT_LEGACY_INTEGRITY_RECALC) && + ((sb_flags & SB_FLAG_FIXED_HMAC) ? + (tgt->u.integrity.vk && !tgt->u.integrity.journal_integrity_key) : + (tgt->u.integrity.vk || tgt->u.integrity.journal_integrity_key))) { + log_err(cd, _("Kernel refuses to activate insecure recalculate option (see legacy activation options to override).")); + return -ENOTSUP; + } + return r; } diff --git a/lib/integrity/integrity.h b/lib/integrity/integrity.h index 38c4c5ee..72737dd6 100644 --- a/lib/integrity/integrity.h +++ b/lib/integrity/integrity.h @@ -35,11 +35,13 @@ struct crypt_dm_active_device; #define SB_VERSION_2 2 #define SB_VERSION_3 3 #define SB_VERSION_4 4 +#define SB_VERSION_5 5 #define SB_FLAG_HAVE_JOURNAL_MAC (1 << 0) #define SB_FLAG_RECALCULATING (1 << 1) /* V2 only */ #define SB_FLAG_DIRTY_BITMAP (1 << 2) /* V3 only */ #define SB_FLAG_FIXED_PADDING (1 << 3) /* V4 only */ +#define SB_FLAG_FIXED_HMAC (1 << 4) /* V5 only */ struct superblock { uint8_t magic[8]; diff --git a/lib/internal.h b/lib/internal.h index a418a465..89403cf2 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -266,4 +266,12 @@ int crypt_compare_dm_devices(struct crypt_device *cd, const struct crypt_dm_active_device *tgt); static inline void *crypt_zalloc(size_t size) { return calloc(1, size); } +static inline bool uint64_mult_overflow(uint64_t *u, uint64_t b, size_t size) +{ + *u = (uint64_t)b * size; + if ((uint64_t)(*u / size) != b) + return true; + return false; +} + #endif /* INTERNAL_H */ diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index 3d002c0c..9620ebbe 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -652,6 +652,10 @@ uint32_t crypt_get_compatibility(struct crypt_device *cd); /** dm-integrity device uses less effective (legacy) padding (old kernels) */ #define CRYPT_COMPAT_LEGACY_INTEGRITY_PADDING (1 << 0) +/** dm-integrity device does not protect superblock with HMAC (old kernels) */ +#define CRYPT_COMPAT_LEGACY_INTEGRITY_HMAC (1 << 1) +/** dm-integrity allow recalculating of volumes with HMAC keys (old kernels) */ +#define CRYPT_COMPAT_LEGACY_INTEGRITY_RECALC (1 << 2) /** * Convert to new type for already existing device. @@ -1485,11 +1489,11 @@ const char *crypt_get_cipher_mode(struct crypt_device *cd); const char *crypt_get_uuid(struct crypt_device *cd); /** - * Get path to underlaying device. + * Get path to underlying device. * * @param cd crypt device handle * - * @return path to underlaying device name + * @return path to underlying device name * */ const char *crypt_get_device_name(struct crypt_device *cd); @@ -1499,7 +1503,7 @@ const char *crypt_get_device_name(struct crypt_device *cd); * * @param cd crypt device handle * - * @return path to underlaying device name + * @return path to underlying device name * */ const char *crypt_get_metadata_device_name(struct crypt_device *cd); @@ -2295,7 +2299,7 @@ int crypt_reencrypt_init_by_keyring(struct crypt_device *cd, * Run data reencryption. * * @param cd crypt device handle - * @param progress is a callback funtion reporting device \b size, + * @param progress is a callback function reporting device \b size, * current \b offset of reencryption and provided \b usrptr identification * * @return @e 0 on success or negative errno value otherwise. @@ -2336,17 +2340,15 @@ crypt_reencrypt_info crypt_reencrypt_status(struct crypt_device *cd, * * @param size size of memory in bytes * - * @return pointer to allocate memory or @e NULL. + * @return pointer to allocated memory or @e NULL. */ void *crypt_safe_alloc(size_t size); /** - * Release safe memory, content is safely wiped + * Release safe memory, content is safely wiped. * The pointer must be allocated with @link crypt_safe_alloc @endlink * * @param data pointer to memory to be deallocated - * - * @return pointer to allocate memory or @e NULL. */ void crypt_safe_free(void *data); @@ -2356,17 +2358,15 @@ void crypt_safe_free(void *data); * @param data pointer to memory to be deallocated * @param size new size of memory in bytes * - * @return pointer to allocate memory or @e NULL. + * @return pointer to allocated memory or @e NULL. */ void *crypt_safe_realloc(void *data, size_t size); /** * Safe clear memory area (compile should not compile this call out). * - * @param data pointer to memory to cleared - * @param size new size of memory in bytes - * - * @return pointer to allocate memory or @e NULL. + * @param data pointer to memory to be cleared + * @param size size of memory in bytes */ void crypt_safe_memzero(void *data, size_t size); diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c index 8f8c94b5..aebd4513 100644 --- a/lib/libdevmapper.c +++ b/lib/libdevmapper.c @@ -239,6 +239,9 @@ static void _dm_set_integrity_compat(struct crypt_device *cd, if (_dm_satisfies_version(1, 6, 0, integrity_maj, integrity_min, integrity_patch)) _dm_flags |= DM_INTEGRITY_DISCARDS_SUPPORTED; + if (_dm_satisfies_version(1, 7, 0, integrity_maj, integrity_min, integrity_patch)) + _dm_flags |= DM_INTEGRITY_FIX_HMAC_SUPPORTED; + _dm_integrity_checked = true; } @@ -648,14 +651,16 @@ static char *get_dm_crypt_params(const struct dm_target *tgt, uint32_t flags) } else *features = '\0'; - if (!strncmp(cipher_dm, "cipher_null-", 12)) + if (crypt_is_cipher_null(cipher_dm)) null_cipher = 1; - if (flags & CRYPT_ACTIVATE_KEYRING_KEY) { + if (null_cipher) + hexkey = crypt_safe_alloc(2); + else if (flags & CRYPT_ACTIVATE_KEYRING_KEY) { keystr_len = strlen(tgt->u.crypt.vk->key_description) + int_log10(tgt->u.crypt.vk->keylength) + 10; hexkey = crypt_safe_alloc(keystr_len); } else - hexkey = crypt_safe_alloc(null_cipher ? 2 : (tgt->u.crypt.vk->keylength * 2 + 1)); + hexkey = crypt_safe_alloc(tgt->u.crypt.vk->keylength * 2 + 1); if (!hexkey) return NULL; @@ -728,7 +733,7 @@ static char *get_dm_verity_params(const struct dm_target *tgt, uint32_t flags) snprintf(fec_features, sizeof(fec_features)-1, " use_fec_from_device %s fec_start %" PRIu64 " fec_blocks %" PRIu64 " fec_roots %" PRIu32, device_block_path(tgt->u.verity.fec_device), tgt->u.verity.fec_offset, - vp->data_size + tgt->u.verity.hash_blocks, vp->fec_roots); + tgt->u.verity.fec_blocks, vp->fec_roots); } else *fec_features = '\0'; @@ -912,12 +917,25 @@ static char *get_dm_integrity_params(const struct dm_target *tgt, uint32_t flags strncat(features, feature, sizeof(features) - strlen(features) - 1); crypt_safe_free(hexkey); } + if (tgt->u.integrity.fix_padding) { num_options++; snprintf(feature, sizeof(feature), "fix_padding "); strncat(features, feature, sizeof(features) - strlen(features) - 1); } + if (tgt->u.integrity.fix_hmac) { + num_options++; + snprintf(feature, sizeof(feature), "fix_hmac "); + strncat(features, feature, sizeof(features) - strlen(features) - 1); + } + + if (tgt->u.integrity.legacy_recalc) { + num_options++; + snprintf(feature, sizeof(feature), "legacy_recalculate "); + strncat(features, feature, sizeof(features) - strlen(features) - 1); + } + if (flags & CRYPT_ACTIVATE_RECALCULATE) { num_options++; snprintf(feature, sizeof(feature), "recalculate "); @@ -1654,6 +1672,14 @@ int dm_create_device(struct crypt_device *cd, const char *name, r = _dm_create_device(cd, name, type, dmd->uuid, dmd); } + /* + * Print warning if activating dm-crypt cipher_null device unless it's reencryption helper or + * keyslot encryption helper device (LUKS1 cipher_null devices). + */ + if (!r && !(dmd->flags & CRYPT_ACTIVATE_PRIVATE) && single_segment(dmd) && dmd->segment.type == DM_CRYPT && + crypt_is_cipher_null(dmd->segment.u.crypt.cipher)) + log_dbg(cd, "Activated dm-crypt device with cipher_null. Device is not encrypted."); + if (r == -EINVAL && dmd->flags & (CRYPT_ACTIVATE_SAME_CPU_CRYPT|CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) && !(dmt_flags & (DM_SAME_CPU_CRYPT_SUPPORTED|DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED))) @@ -2474,6 +2500,10 @@ static int _dm_target_query_integrity(struct crypt_device *cd, *act_flags |= CRYPT_ACTIVATE_RECALCULATE; } else if (!strcmp(arg, "fix_padding")) { tgt->u.integrity.fix_padding = true; + } else if (!strcmp(arg, "fix_hmac")) { + tgt->u.integrity.fix_hmac = true; + } else if (!strcmp(arg, "legacy_recalculate")) { + tgt->u.integrity.legacy_recalc = true; } else if (!strcmp(arg, "allow_discards")) { *act_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; } else /* unknown option */ @@ -2913,7 +2943,9 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name, if (!(dmt_flags & DM_KEY_WIPE_SUPPORTED)) goto out; - if (vk->key_description) + if (!vk->keylength) + msg_size = 11; // key set - + else if (vk->key_description) msg_size = strlen(vk->key_description) + int_log10(vk->keylength) + 18; else msg_size = vk->keylength * 2 + 10; // key set <key> @@ -2925,7 +2957,9 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name, } strcpy(msg, "key set "); - if (vk->key_description) + if (!vk->keylength) + snprintf(msg + 8, msg_size - 8, "-"); + else if (vk->key_description) snprintf(msg + 8, msg_size - 8, ":%zu:logon:%s", vk->keylength, vk->key_description); else hex_key(&msg[8], vk->keylength, vk->key); @@ -3000,8 +3034,8 @@ err: int dm_verity_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t seg_size, struct device *data_device, struct device *hash_device, struct device *fec_device, - const char *root_hash, uint32_t root_hash_size, const char *root_hash_sig_key_desc, - uint64_t hash_offset_block, uint64_t hash_blocks, struct crypt_params_verity *vp) + const char *root_hash, uint32_t root_hash_size, const char* root_hash_sig_key_desc, + uint64_t hash_offset_block, uint64_t fec_blocks, struct crypt_params_verity *vp) { if (!data_device || !hash_device || !vp) return -EINVAL; @@ -3019,7 +3053,7 @@ int dm_verity_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t se tgt->u.verity.root_hash_sig_key_desc = root_hash_sig_key_desc; tgt->u.verity.hash_offset = hash_offset_block; tgt->u.verity.fec_offset = vp->fec_area_offset / vp->hash_block_size; - tgt->u.verity.hash_blocks = hash_blocks; + tgt->u.verity.fec_blocks = fec_blocks; tgt->u.verity.vp = vp; return 0; @@ -3060,6 +3094,15 @@ int dm_integrity_target_set(struct crypt_device *cd, !(crypt_get_compatibility(cd) & CRYPT_COMPAT_LEGACY_INTEGRITY_PADDING)) tgt->u.integrity.fix_padding = true; + if (!dm_flags(cd, DM_INTEGRITY, &dmi_flags) && + (dmi_flags & DM_INTEGRITY_FIX_HMAC_SUPPORTED) && + !(crypt_get_compatibility(cd) & CRYPT_COMPAT_LEGACY_INTEGRITY_HMAC)) + tgt->u.integrity.fix_hmac = true; + + /* This flag can be backported, just try to set it always */ + if (crypt_get_compatibility(cd) & CRYPT_COMPAT_LEGACY_INTEGRITY_RECALC) + tgt->u.integrity.legacy_recalc = true; + if (ip) { tgt->u.integrity.journal_size = ip->journal_size; tgt->u.integrity.journal_watermark = ip->journal_watermark; diff --git a/lib/luks1/keymanage.c b/lib/luks1/keymanage.c index a08ff50f..4cbb5f86 100644 --- a/lib/luks1/keymanage.c +++ b/lib/luks1/keymanage.c @@ -375,8 +375,13 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx) log_err(ctx, _("Non standard key size, manual repair required.")); return -EINVAL; } - /* cryptsetup 1.0 did not align to 4k, cannot repair this one */ - if (LUKS_keyslots_offset(phdr) < (LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE)) { + + /* + * cryptsetup 1.0 did not align keyslots to 4k, cannot repair this one + * Also we cannot trust possibly broken keyslots metadata here through LUKS_keyslots_offset(). + * Expect first keyslot is aligned, if not, then manual repair is neccessary. + */ + if (phdr->keyblock[0].keyMaterialOffset < (LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE)) { log_err(ctx, _("Non standard keyslots alignment, manual repair required.")); return -EINVAL; } @@ -386,6 +391,8 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx) return -EINVAL; vk = crypt_alloc_volume_key(phdr->keyBytes, NULL); + if (!vk) + return -ENOMEM; log_verbose(ctx, _("Repairing keyslots.")); @@ -955,12 +962,12 @@ static int LUKS_open_key(unsigned int keyIndex, const char *password, size_t passwordLen, struct luks_phdr *hdr, - struct volume_key *vk, + struct volume_key **vk, struct crypt_device *ctx) { crypt_keyslot_info ki = LUKS_keyslot_info(hdr, keyIndex); struct volume_key *derived_key; - char *AfKey; + char *AfKey = NULL; size_t AFEKSize; int r; @@ -974,8 +981,13 @@ static int LUKS_open_key(unsigned int keyIndex, if (!derived_key) return -ENOMEM; - assert(vk->keylength == hdr->keyBytes); - AFEKSize = AF_split_sectors(vk->keylength, hdr->keyblock[keyIndex].stripes) * SECTOR_SIZE; + *vk = crypt_alloc_volume_key(hdr->keyBytes, NULL); + if (!*vk) { + r = -ENOMEM; + goto out; + } + + AFEKSize = AF_split_sectors(hdr->keyBytes, hdr->keyblock[keyIndex].stripes) * SECTOR_SIZE; AfKey = crypt_safe_alloc(AFEKSize); if (!AfKey) { r = -ENOMEM; @@ -1001,16 +1013,20 @@ static int LUKS_open_key(unsigned int keyIndex, if (r < 0) goto out; - r = AF_merge(ctx, AfKey, vk->key, vk->keylength, hdr->keyblock[keyIndex].stripes, hdr->hashSpec); + r = AF_merge(ctx, AfKey, (*vk)->key, (*vk)->keylength, hdr->keyblock[keyIndex].stripes, hdr->hashSpec); if (r < 0) goto out; - r = LUKS_verify_volume_key(hdr, vk); + r = LUKS_verify_volume_key(hdr, *vk); /* Allow only empty passphrase with null cipher */ - if (!r && !strcmp(hdr->cipherName, "cipher_null") && passwordLen) + if (!r && crypt_is_cipher_null(hdr->cipherName) && passwordLen) r = -EPERM; out: + if (r < 0) { + crypt_free_volume_key(*vk); + *vk = NULL; + } crypt_safe_free(AfKey); crypt_free_volume_key(derived_key); return r; @@ -1026,16 +1042,14 @@ int LUKS_open_key_with_hdr(int keyIndex, unsigned int i, tried = 0; int r; - *vk = crypt_alloc_volume_key(hdr->keyBytes, NULL); - if (keyIndex >= 0) { - r = LUKS_open_key(keyIndex, password, passwordLen, hdr, *vk, ctx); + r = LUKS_open_key(keyIndex, password, passwordLen, hdr, vk, ctx); return (r < 0) ? r : keyIndex; } for (i = 0; i < LUKS_NUMKEYS; i++) { - r = LUKS_open_key(i, password, passwordLen, hdr, *vk, ctx); - if(r == 0) + r = LUKS_open_key(i, password, passwordLen, hdr, vk, ctx); + if (r == 0) return i; /* Do not retry for errors that are no -EPERM or -ENOENT, diff --git a/lib/luks2/luks2.h b/lib/luks2/luks2.h index 5b29a627..79560d33 100644 --- a/lib/luks2/luks2.h +++ b/lib/luks2/luks2.h @@ -23,6 +23,8 @@ #define _CRYPTSETUP_LUKS2_ONDISK_H #include <stdbool.h> +#include <stdint.h> +#include <sys/types.h> #include "libcryptsetup.h" @@ -330,6 +332,12 @@ int LUKS2_token_is_assigned(struct crypt_device *cd, int keyslot, int token); +int LUKS2_token_assignment_copy(struct crypt_device *cd, + struct luks2_hdr *hdr, + int keyslot_from, + int keyslot_to, + int commit); + int LUKS2_token_create(struct crypt_device *cd, struct luks2_hdr *hdr, int token, diff --git a/lib/luks2/luks2_disk_metadata.c b/lib/luks2/luks2_disk_metadata.c index 9654cdb8..47ad5462 100644 --- a/lib/luks2/luks2_disk_metadata.c +++ b/lib/luks2/luks2_disk_metadata.c @@ -175,13 +175,13 @@ static void hdr_to_disk(struct luks2_hdr *hdr, hdr_disk->hdr_offset = cpu_to_be64(offset); hdr_disk->seqid = cpu_to_be64(hdr->seqid); - strncpy(hdr_disk->label, hdr->label, LUKS2_LABEL_L); + memcpy(hdr_disk->label, hdr->label, MIN(strlen(hdr->label), LUKS2_LABEL_L)); hdr_disk->label[LUKS2_LABEL_L - 1] = '\0'; - strncpy(hdr_disk->subsystem, hdr->subsystem, LUKS2_LABEL_L); + memcpy(hdr_disk->subsystem, hdr->subsystem, MIN(strlen(hdr->subsystem), LUKS2_LABEL_L)); hdr_disk->subsystem[LUKS2_LABEL_L - 1] = '\0'; - strncpy(hdr_disk->checksum_alg, hdr->checksum_alg, LUKS2_CHECKSUM_ALG_L); + memcpy(hdr_disk->checksum_alg, hdr->checksum_alg, MIN(strlen(hdr->checksum_alg), LUKS2_CHECKSUM_ALG_L)); hdr_disk->checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0'; - strncpy(hdr_disk->uuid, hdr->uuid, LUKS2_UUID_L); + memcpy(hdr_disk->uuid, hdr->uuid, MIN(strlen(hdr->uuid), LUKS2_UUID_L)); hdr_disk->uuid[LUKS2_UUID_L - 1] = '\0'; memcpy(hdr_disk->salt, secondary ? hdr->salt2 : hdr->salt1, LUKS2_SALT_L); diff --git a/lib/luks2/luks2_json_format.c b/lib/luks2/luks2_json_format.c index fb695f08..5cc92d98 100644 --- a/lib/luks2/luks2_json_format.c +++ b/lib/luks2/luks2_json_format.c @@ -244,7 +244,8 @@ int LUKS2_generate_hdr( /* Decrease keyslots_size due to metadata device being too small */ if (!device_size(crypt_metadata_device(cd), &mdev_size) && ((keyslots_size + get_min_offset(hdr)) > mdev_size) && - device_fallocate(crypt_metadata_device(cd), keyslots_size + get_min_offset(hdr))) + device_fallocate(crypt_metadata_device(cd), keyslots_size + get_min_offset(hdr)) && + (get_min_offset(hdr) <= mdev_size)) keyslots_size = mdev_size - get_min_offset(hdr); } diff --git a/lib/luks2/luks2_keyslot.c b/lib/luks2/luks2_keyslot.c index 3b8c889d..c90cb728 100644 --- a/lib/luks2/luks2_keyslot.c +++ b/lib/luks2/luks2_keyslot.c @@ -148,7 +148,7 @@ int LUKS2_keyslot_cipher_incompatible(struct crypt_device *cd, const char *ciphe { char cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; - if (!cipher_spec || !strcmp(cipher_spec, "null") || !strcmp(cipher_spec, "cipher_null")) + if (!cipher_spec || crypt_is_cipher_null(cipher_spec)) return 1; if (crypt_parse_name_and_mode(cipher_spec, cipher, NULL, cipher_mode) < 0) diff --git a/lib/luks2/luks2_reencrypt.c b/lib/luks2/luks2_reencrypt.c index a76cb16e..ed914a27 100644 --- a/lib/luks2/luks2_reencrypt.c +++ b/lib/luks2/luks2_reencrypt.c @@ -2238,7 +2238,7 @@ static int reencrypt_verify_and_upload_keys(struct crypt_device *cd, struct luks if (LUKS2_digest_verify_by_digest(cd, hdr, digest_new, vk) != digest_new) return -EINVAL; - if (crypt_use_keyring_for_vk(cd) && + if (crypt_use_keyring_for_vk(cd) && !crypt_is_cipher_null(reencrypt_segment_cipher_new(hdr)) && (r = LUKS2_volume_key_load_in_keyring_by_digest(cd, hdr, vk, crypt_volume_key_get_id(vk)))) return r; } @@ -2254,7 +2254,7 @@ static int reencrypt_verify_and_upload_keys(struct crypt_device *cd, struct luks r = -EINVAL; goto err; } - if (crypt_use_keyring_for_vk(cd) && + if (crypt_use_keyring_for_vk(cd) && !crypt_is_cipher_null(reencrypt_segment_cipher_old(hdr)) && (r = LUKS2_volume_key_load_in_keyring_by_digest(cd, hdr, vk, crypt_volume_key_get_id(vk)))) goto err; } @@ -2664,6 +2664,7 @@ static int reencrypt_load_by_passphrase(struct crypt_device *cd, struct luks2_hdr *hdr; struct crypt_lock_handle *reencrypt_lock; struct luks2_reenc_context *rh; + const struct volume_key *vk; struct crypt_dm_active_device dmd_target, dmd_source = { .uuid = crypt_get_uuid(cd), .flags = CRYPT_ACTIVATE_SHARED /* turn off exclusive open checks */ @@ -2730,6 +2731,19 @@ static int reencrypt_load_by_passphrase(struct crypt_device *cd, goto err; flags = dmd_target.flags; + /* + * By default reencryption code aims to retain flags from existing dm device. + * The keyring activation flag can not be inherited if original cipher is null. + * + * In this case override the flag based on decision made in reencrypt_verify_and_upload_keys + * above. The code checks if new VK is eligible for keyring. + */ + vk = crypt_volume_key_by_id(*vks, LUKS2_reencrypt_digest_new(hdr)); + if (vk && vk->key_description && crypt_is_cipher_null(reencrypt_segment_cipher_old(hdr))) { + flags |= CRYPT_ACTIVATE_KEYRING_KEY; + dmd_source.flags |= CRYPT_ACTIVATE_KEYRING_KEY; + } + r = LUKS2_assembly_multisegment_dmd(cd, hdr, *vks, LUKS2_get_segments_jobj(hdr), &dmd_source); if (!r) { r = crypt_compare_dm_devices(cd, &dmd_source, &dmd_target); diff --git a/lib/luks2/luks2_token.c b/lib/luks2/luks2_token.c index ad6722a3..101f4f59 100644 --- a/lib/luks2/luks2_token.c +++ b/lib/luks2/luks2_token.c @@ -329,7 +329,7 @@ static void LUKS2_token_buffer_free(struct crypt_device *cd, { const crypt_token_handler *h = LUKS2_token_handler(cd, token); - if (h->buffer_free) + if (h && h->buffer_free) h->buffer_free(buffer, buffer_len); else { crypt_safe_memzero(buffer, buffer_len); @@ -383,6 +383,7 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd, uint32_t flags, void *usrptr) { + bool use_keyring; int keyslot, r; char *buffer; size_t buffer_len; @@ -404,7 +405,13 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd, keyslot = r; - if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd)) { + if (!crypt_use_keyring_for_vk(cd)) + use_keyring = false; + else + use_keyring = ((name && !crypt_is_cipher_null(crypt_get_cipher(cd))) || + (flags & CRYPT_ACTIVATE_KEYRING_KEY)); + + if (use_keyring) { if (!(r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, vk, keyslot))) flags |= CRYPT_ACTIVATE_KEYRING_KEY; } @@ -576,16 +583,12 @@ int LUKS2_token_assign(struct crypt_device *cd, struct luks2_hdr *hdr, return token; } -int LUKS2_token_is_assigned(struct crypt_device *cd, struct luks2_hdr *hdr, - int keyslot, int token) +static int token_is_assigned(struct luks2_hdr *hdr, int keyslot, int token) { int i; - json_object *jobj_token, *jobj_token_keyslots, *jobj; + json_object *jobj, *jobj_token_keyslots, + *jobj_token = LUKS2_get_token_jobj(hdr, token); - if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX || token < 0 || token >= LUKS2_TOKENS_MAX) - return -EINVAL; - - jobj_token = LUKS2_get_token_jobj(hdr, token); if (!jobj_token) return -ENOENT; @@ -600,6 +603,15 @@ int LUKS2_token_is_assigned(struct crypt_device *cd, struct luks2_hdr *hdr, return -ENOENT; } +int LUKS2_token_is_assigned(struct crypt_device *cd, struct luks2_hdr *hdr, + int keyslot, int token) +{ + if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX || token < 0 || token >= LUKS2_TOKENS_MAX) + return -EINVAL; + + return token_is_assigned(hdr, keyslot, token); +} + int LUKS2_tokens_count(struct luks2_hdr *hdr) { json_object *jobj_tokens = LUKS2_get_tokens_jobj(hdr); @@ -608,3 +620,28 @@ int LUKS2_tokens_count(struct luks2_hdr *hdr) return json_object_object_length(jobj_tokens); } + +int LUKS2_token_assignment_copy(struct crypt_device *cd, + struct luks2_hdr *hdr, + int keyslot_from, + int keyslot_to, + int commit) +{ + int i, r; + + if (keyslot_from < 0 || keyslot_from >= LUKS2_KEYSLOTS_MAX || keyslot_to < 0 || keyslot_to >= LUKS2_KEYSLOTS_MAX) + return -EINVAL; + + r = LUKS2_tokens_count(hdr); + if (r <= 0) + return r; + + for (i = 0; i < LUKS2_TOKENS_MAX; i++) { + if (!token_is_assigned(hdr, keyslot_from, i)) { + if ((r = assign_one_token(cd, hdr, keyslot_to, i, 1))) + return r; + } + } + + return commit ? LUKS2_hdr_write(cd, hdr) : 0; +} diff --git a/lib/setup.c b/lib/setup.c index d8e665c0..1e8e456f 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -1210,7 +1210,7 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name) } /* do not try to lookup LUKS2 header in detached header mode */ - if (!cd->metadata_device && !found) { + if (dmd.uuid && !cd->metadata_device && !found) { while (*dep && !found) { r = dm_query_device(cd, *dep, DM_ACTIVE_DEVICE, &dmdep); if (r < 0) @@ -2035,7 +2035,7 @@ static int _crypt_format_verity(struct crypt_device *cd, } else cd->u.verity.hdr.data_size = params->data_size; - if (device_is_identical(crypt_metadata_device(cd), crypt_data_device(cd)) && + if (device_is_identical(crypt_metadata_device(cd), crypt_data_device(cd)) > 0 && (cd->u.verity.hdr.data_size * params->data_block_size) > params->hash_area_offset) { log_err(cd, _("Data area overlaps with hash area.")); return -EINVAL; @@ -2060,14 +2060,14 @@ static int _crypt_format_verity(struct crypt_device *cd, } hash_blocks_size = VERITY_hash_blocks(cd, params) * params->hash_block_size; - if (device_is_identical(crypt_metadata_device(cd), fec_device) && + if (device_is_identical(crypt_metadata_device(cd), fec_device) > 0 && (params->hash_area_offset + hash_blocks_size) > params->fec_area_offset) { log_err(cd, _("Hash area overlaps with FEC area.")); r = -EINVAL; goto err; } - if (device_is_identical(crypt_data_device(cd), fec_device) && + if (device_is_identical(crypt_data_device(cd), fec_device) > 0 && (cd->u.verity.hdr.data_size * params->data_block_size) > params->fec_area_offset) { log_err(cd, _("Data area overlaps with FEC area.")); r = -EINVAL; @@ -2388,11 +2388,6 @@ static int _compare_crypt_devices(struct crypt_device *cd, if (!src->u.crypt.vk || !tgt->u.crypt.vk) return -EINVAL; - if (_compare_volume_keys(src->u.crypt.vk, 0, tgt->u.crypt.vk, tgt->u.crypt.vk->key_description != NULL)) { - log_dbg(cd, "Keys in context and target device do not match."); - return -EINVAL; - } - /* CIPHER checks */ if (!src->u.crypt.cipher || !tgt->u.crypt.cipher) return -EINVAL; @@ -2400,6 +2395,14 @@ static int _compare_crypt_devices(struct crypt_device *cd, log_dbg(cd, "Cipher specs do not match."); return -EINVAL; } + + if (tgt->u.crypt.vk->keylength == 0 && crypt_is_cipher_null(tgt->u.crypt.cipher)) + log_dbg(cd, "Existing device uses cipher null. Skipping key comparison."); + else if (_compare_volume_keys(src->u.crypt.vk, 0, tgt->u.crypt.vk, tgt->u.crypt.vk->key_description != NULL)) { + log_dbg(cd, "Keys in context and target device do not match."); + return -EINVAL; + } + if (crypt_strcmp(src->u.crypt.integrity, tgt->u.crypt.integrity)) { log_dbg(cd, "Integrity parameters do not match."); return -EINVAL; @@ -2413,7 +2416,7 @@ static int _compare_crypt_devices(struct crypt_device *cd, return -EINVAL; } - if (!device_is_identical(src->data_device, tgt->data_device)) { + if (device_is_identical(src->data_device, tgt->data_device) <= 0) { log_dbg(cd, "Data devices do not match."); return -EINVAL; } @@ -2467,7 +2470,7 @@ static int _compare_integrity_devices(struct crypt_device *cd, return -EINVAL; } - if (!device_is_identical(src->data_device, tgt->data_device)) { + if (device_is_identical(src->data_device, tgt->data_device) <= 0) { log_dbg(cd, "Data devices do not match."); return -EINVAL; } @@ -2770,6 +2773,11 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) if (!cd || !cd->type || !name) return -EINVAL; + if (isTCRYPT(cd->type) || isBITLK(cd->type)) { + log_err(cd, _("This operation is not supported for this device type.")); + return -ENOTSUP; + } + log_dbg(cd, "Resizing device %s to %" PRIu64 " sectors.", name, new_size); r = dm_query_device(cd, name, DM_ACTIVE_CRYPT_KEYSIZE | DM_ACTIVE_CRYPT_KEY, &dmdq); @@ -3090,6 +3098,45 @@ out: return r; } +/* key must be properly verified */ +static int resume_by_volume_key(struct crypt_device *cd, + struct volume_key *vk, + const char *name) +{ + int digest, r; + struct volume_key *zerokey = NULL; + + if (crypt_is_cipher_null(crypt_get_cipher_spec(cd))) { + zerokey = crypt_alloc_volume_key(0, NULL); + if (!zerokey) + return -ENOMEM; + vk = zerokey; + } else if (crypt_use_keyring_for_vk(cd)) { + /* LUKS2 path only */ + digest = LUKS2_digest_by_segment(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT); + if (digest < 0) + return -EINVAL; + r = LUKS2_volume_key_load_in_keyring_by_digest(cd, + &cd->u.luks2.hdr, vk, digest); + if (r < 0) + return r; + } + + r = dm_resume_and_reinstate_key(cd, name, vk); + + if (r == -ENOTSUP) + log_err(cd, _("Resume is not supported for device %s."), name); + else if (r) + log_err(cd, _("Error during resuming device %s."), name); + + if (r < 0) + crypt_drop_keyring_key(cd, vk); + + crypt_free_volume_key(zerokey); + + return r; +} + int crypt_resume_by_passphrase(struct crypt_device *cd, const char *name, int keyslot, @@ -3125,32 +3172,13 @@ int crypt_resume_by_passphrase(struct crypt_device *cd, r = LUKS2_keyslot_open(cd, keyslot, CRYPT_DEFAULT_SEGMENT, passphrase, passphrase_size, &vk); if (r < 0) - goto out; + return r; keyslot = r; - if (crypt_use_keyring_for_vk(cd)) { - if (!isLUKS2(cd->type)) { - r = -EINVAL; - goto out; - } - r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, - &cd->u.luks2.hdr, vk, keyslot); - if (r < 0) - goto out; - } - - r = dm_resume_and_reinstate_key(cd, name, vk); + r = resume_by_volume_key(cd, vk, name); - if (r == -ENOTSUP) - log_err(cd, _("Resume is not supported for device %s."), name); - else if (r) - log_err(cd, _("Error during resuming device %s."), name); -out: - if (r < 0) - crypt_drop_keyring_key(cd, vk); crypt_free_volume_key(vk); - return r < 0 ? r : keyslot; } @@ -3189,35 +3217,22 @@ int crypt_resume_by_keyfile_device_offset(struct crypt_device *cd, &passphrase_read, &passphrase_size_read, keyfile_offset, keyfile_size, 0); if (r < 0) - goto out; + return r; if (isLUKS1(cd->type)) r = LUKS_open_key_with_hdr(keyslot, passphrase_read, passphrase_size_read, &cd->u.luks1.hdr, &vk, cd); else r = LUKS2_keyslot_open(cd, keyslot, CRYPT_DEFAULT_SEGMENT, passphrase_read, passphrase_size_read, &vk); + + crypt_safe_free(passphrase_read); if (r < 0) - goto out; + return r; + keyslot = r; - if (crypt_use_keyring_for_vk(cd)) { - if (!isLUKS2(cd->type)) { - r = -EINVAL; - goto out; - } - r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, - &cd->u.luks2.hdr, vk, keyslot); - if (r < 0) - goto out; - } + r = resume_by_volume_key(cd, vk, name); - r = dm_resume_and_reinstate_key(cd, name, vk); - if (r < 0) - log_err(cd, _("Error during resuming device %s."), name); -out: - crypt_safe_free(passphrase_read); - if (r < 0) - crypt_drop_keyring_key(cd, vk); crypt_free_volume_key(vk); return r < 0 ? r : keyslot; } @@ -3280,24 +3295,10 @@ int crypt_resume_by_volume_key(struct crypt_device *cd, r = -EINVAL; if (r == -EPERM || r == -ENOENT) log_err(cd, _("Volume key does not match the volume.")); - if (r < 0) - goto out; - r = 0; - if (crypt_use_keyring_for_vk(cd)) { - r = LUKS2_key_description_by_segment(cd, &cd->u.luks2.hdr, vk, CRYPT_DEFAULT_SEGMENT); - if (!r) - r = crypt_volume_key_load_in_keyring(cd, vk); - } - if (r < 0) - goto out; + if (r >= 0) + r = resume_by_volume_key(cd, vk, name); - r = dm_resume_and_reinstate_key(cd, name, vk); - if (r < 0) - log_err(cd, _("Error during resuming device %s."), name); -out: - if (r < 0) - crypt_drop_keyring_key(cd, vk); crypt_free_volume_key(vk); return r; } @@ -3459,6 +3460,9 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd, r = LUKS2_digest_assign(cd, &cd->u.luks2.hdr, keyslot_new, digest, 1, 0); if (r < 0) goto out; + r = LUKS2_token_assignment_copy(cd, &cd->u.luks2.hdr, keyslot_old, keyslot_new, 0); + if (r < 0) + goto out; } else { log_dbg(cd, "Key slot %d is going to be overwritten.", keyslot_old); /* FIXME: improve return code so that we can detect area is damaged */ @@ -3686,7 +3690,7 @@ static int _check_header_data_overlap(struct crypt_device *cd, const char *name) if (!name || !isLUKS(cd->type)) return 0; - if (!device_is_identical(crypt_data_device(cd), crypt_metadata_device(cd))) + if (device_is_identical(crypt_data_device(cd), crypt_metadata_device(cd)) <= 0) return 0; /* FIXME: check real header size */ @@ -3879,6 +3883,7 @@ static int _open_and_activate(struct crypt_device *cd, size_t passphrase_size, uint32_t flags) { + bool use_keyring; int r; struct volume_key *vk = NULL; @@ -3890,8 +3895,13 @@ static int _open_and_activate(struct crypt_device *cd, return r; keyslot = r; - if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && - crypt_use_keyring_for_vk(cd)) { + if (!crypt_use_keyring_for_vk(cd)) + use_keyring = false; + else + use_keyring = ((name && !crypt_is_cipher_null(crypt_get_cipher(cd))) || + (flags & CRYPT_ACTIVATE_KEYRING_KEY)); + + if (use_keyring) { r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, &cd->u.luks2.hdr, vk, keyslot); if (r < 0) @@ -4274,6 +4284,7 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, size_t volume_key_size, uint32_t flags) { + bool use_keyring; struct volume_key *vk = NULL; int r; @@ -4349,8 +4360,12 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, if (r > 0) r = 0; - if (!r && (name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && - crypt_use_keyring_for_vk(cd)) { + if (!crypt_use_keyring_for_vk(cd)) + use_keyring = false; + else + use_keyring = (name && !crypt_is_cipher_null(crypt_get_cipher(cd))) || (flags & CRYPT_ACTIVATE_KEYRING_KEY); + + if (!r && use_keyring) { r = LUKS2_key_description_by_segment(cd, &cd->u.luks2.hdr, vk, CRYPT_DEFAULT_SEGMENT); if (!r) @@ -4411,7 +4426,7 @@ int crypt_activate_by_signed_key(struct crypt_device *cd, return -EINVAL; } - log_dbg(cd, "%s volume %s by signed key.", name ? "Activating" : "Checking", name ?: ""); + log_dbg(cd, "%s volume %s by %skey.", name ? "Activating" : "Checking", name ?: "", signature ? "signed " : ""); if (cd->u.verity.hdr.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE && !signature) { log_err(cd, _("Root hash signature required.")); diff --git a/lib/utils_blkid.h b/lib/utils_blkid.h index ca50da70..d03d4a50 100644 --- a/lib/utils_blkid.h +++ b/lib/utils_blkid.h @@ -21,6 +21,8 @@ #ifndef _UTILS_BLKID_H #define _UTILS_BLKID_H +#include <sys/types.h> + struct blkid_handle; typedef enum { PRB_OK = 0, PRB_EMPTY, PRB_AMBIGUOUS, PRB_FAIL } blk_probe_status; diff --git a/lib/utils_crypt.c b/lib/utils_crypt.c index f09871c2..d799162b 100644 --- a/lib/utils_crypt.c +++ b/lib/utils_crypt.c @@ -177,3 +177,10 @@ ssize_t crypt_hex_to_bytes(const char *hex, char **result, int safe_alloc) *result = bytes; return i; } + +bool crypt_is_cipher_null(const char *cipher_spec) +{ + if (!cipher_spec) + return false; + return (strstr(cipher_spec, "cipher_null") || !strcmp(cipher_spec, "null")); +} diff --git a/lib/utils_crypt.h b/lib/utils_crypt.h index 32b77cb8..c5aa7068 100644 --- a/lib/utils_crypt.h +++ b/lib/utils_crypt.h @@ -23,6 +23,7 @@ #ifndef _UTILS_CRYPT_H #define _UTILS_CRYPT_H +#include <stdbool.h> #include <unistd.h> #define MAX_CIPHER_LEN 32 @@ -38,4 +39,6 @@ int crypt_parse_pbkdf(const char *s, const char **pbkdf); ssize_t crypt_hex_to_bytes(const char *hex, char **result, int safe_alloc); +bool crypt_is_cipher_null(const char *cipher_spec); + #endif /* _UTILS_CRYPT_H */ diff --git a/lib/utils_device.c b/lib/utils_device.c index ddbe8362..5dda6924 100644 --- a/lib/utils_device.c +++ b/lib/utils_device.c @@ -300,6 +300,9 @@ static int device_open_internal(struct crypt_device *cd, struct device *device, int device_open(struct crypt_device *cd, struct device *device, int flags) { + if (!device) + return -EINVAL; + assert(!device_locked(device->lh)); return device_open_internal(cd, device, flags); } @@ -354,6 +357,9 @@ void device_release_excl(struct crypt_device *cd, struct device *device) int device_open_locked(struct crypt_device *cd, struct device *device, int flags) { + if (!device) + return -EINVAL; + assert(!crypt_metadata_locking_enabled() || device_locked(device->lh)); return device_open_internal(cd, device, flags); } @@ -528,7 +534,7 @@ void device_topology_alignment(struct crypt_device *cd, if ((temp_alignment < (unsigned long)opt_io_size) && !((unsigned long)opt_io_size % temp_alignment) && !MISALIGNED_4K(opt_io_size)) temp_alignment = (unsigned long)opt_io_size; - else if (opt_io_size) + else if (opt_io_size && (opt_io_size != min_io_size)) log_err(cd, _("Ignoring bogus optimal-io size for data device (%u bytes)."), opt_io_size); /* If calculated alignment is multiple of default, keep default */ @@ -589,8 +595,11 @@ int device_size(struct device *device, uint64_t *size) struct stat st; int devfd, r = -EINVAL; + if (!device) + return -EINVAL; + devfd = open(device->path, O_RDONLY); - if(devfd == -1) + if (devfd == -1) return -EINVAL; if (fstat(devfd, &st) < 0) @@ -612,6 +621,9 @@ int device_fallocate(struct device *device, uint64_t size) struct stat st; int devfd, r = -EINVAL; + if (!device) + return -EINVAL; + devfd = open(device_path(device), O_RDWR); if (devfd == -1) return -EINVAL; @@ -852,22 +864,30 @@ size_t size_round_up(size_t size, size_t block) void device_disable_direct_io(struct device *device) { + if (device) device->o_direct = 0; } int device_direct_io(const struct device *device) { - return device->o_direct; + return device ? device->o_direct : 0; } -static dev_t device_devno(const struct device *device) +static int device_compare_path(const char *path1, const char *path2) { - struct stat st; + struct stat st_path1, st_path2; - if (stat(device->path, &st) || !S_ISBLK(st.st_mode)) - return 0; + if (stat(path1, &st_path1 ) < 0 || stat(path2, &st_path2 ) < 0) + return -EINVAL; + + if (S_ISBLK(st_path1.st_mode) && S_ISBLK(st_path2.st_mode)) + return (st_path1.st_rdev == st_path2.st_rdev) ? 1 : 0; + + if (S_ISREG(st_path1.st_mode) && S_ISREG(st_path2.st_mode)) + return (st_path1.st_ino == st_path2.st_ino && + st_path1.st_dev == st_path2.st_dev) ? 1 : 0; - return st.st_rdev; + return 0; } int device_is_identical(struct device *device1, struct device *device2) @@ -878,21 +898,19 @@ int device_is_identical(struct device *device1, struct device *device2) if (device1 == device2) return 1; - if (device1->init_done && device2->init_done) - return (device_devno(device1) == device_devno(device2)); - else if (device1->init_done || device2->init_done) - return 0; - if (!strcmp(device_path(device1), device_path(device2))) return 1; - return 0; + return device_compare_path(device_path(device1), device_path(device2)); } int device_is_rotational(struct device *device) { struct stat st; + if (!device) + return -EINVAL; + if (stat(device_path(device), &st) < 0) return -EINVAL; @@ -906,6 +924,9 @@ size_t device_alignment(struct device *device) { int devfd; + if (!device) + return -EINVAL; + if (!device->alignment) { devfd = open(device_path(device), O_RDONLY); if (devfd != -1) { @@ -919,17 +940,18 @@ size_t device_alignment(struct device *device) void device_set_lock_handle(struct device *device, struct crypt_lock_handle *h) { + if (device) device->lh = h; } struct crypt_lock_handle *device_get_lock_handle(struct device *device) { - return device->lh; + return device ? device->lh : NULL; } int device_read_lock(struct crypt_device *cd, struct device *device) { - if (!crypt_metadata_locking_enabled()) + if (!device || !crypt_metadata_locking_enabled()) return 0; if (device_read_lock_internal(cd, device)) @@ -940,7 +962,7 @@ int device_read_lock(struct crypt_device *cd, struct device *device) int device_write_lock(struct crypt_device *cd, struct device *device) { - if (!crypt_metadata_locking_enabled()) + if (!device || !crypt_metadata_locking_enabled()) return 0; assert(!device_locked(device->lh) || !device_locked_readonly(device->lh)); @@ -950,7 +972,7 @@ int device_write_lock(struct crypt_device *cd, struct device *device) void device_read_unlock(struct crypt_device *cd, struct device *device) { - if (!crypt_metadata_locking_enabled()) + if (!device || !crypt_metadata_locking_enabled()) return; assert(device_locked(device->lh)); @@ -960,7 +982,7 @@ void device_read_unlock(struct crypt_device *cd, struct device *device) void device_write_unlock(struct crypt_device *cd, struct device *device) { - if (!crypt_metadata_locking_enabled()) + if (!device || !crypt_metadata_locking_enabled()) return; assert(device_locked(device->lh) && !device_locked_readonly(device->lh)); diff --git a/lib/utils_device_locking.c b/lib/utils_device_locking.c index dac8315a..c50dff8f 100644 --- a/lib/utils_device_locking.c +++ b/lib/utils_device_locking.c @@ -106,7 +106,7 @@ static int open_lock_dir(struct crypt_device *cd, const char *dir, const char *b lockdfd = openat(dirfd, base, O_RDONLY | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC); if (lockdfd < 0) { if (errno == ENOENT) { - log_std(cd, _("WARNING: Locking directory %s/%s is missing!\n"), dir, base); + log_dbg(cd, _("Locking directory %s/%s will be created with default compiled-in permissions."), dir, base); /* success or failure w/ errno == EEXIST either way just try to open the 'base' directory again */ if (mkdirat(dirfd, base, DEFAULT_LUKS2_LOCK_DIR_PERMS) && errno != EEXIST) diff --git a/lib/utils_device_locking.h b/lib/utils_device_locking.h index 41d09349..d38b1a91 100644 --- a/lib/utils_device_locking.h +++ b/lib/utils_device_locking.h @@ -22,6 +22,8 @@ #ifndef _CRYPTSETUP_UTILS_LOCKING_H #define _CRYPTSETUP_UTILS_LOCKING_H +#include <stdbool.h> + struct crypt_device; struct crypt_lock_handle; struct device; diff --git a/lib/utils_dm.h b/lib/utils_dm.h index 22add180..c16541cd 100644 --- a/lib/utils_dm.h +++ b/lib/utils_dm.h @@ -25,7 +25,8 @@ #define _UTILS_DM_H /* device-mapper library helpers */ -#include <inttypes.h> +#include <stddef.h> +#include <stdint.h> struct crypt_device; struct volume_key; @@ -71,6 +72,7 @@ static inline uint32_t act2dmflags(uint32_t act_flags) #define DM_INTEGRITY_DISCARDS_SUPPORTED (1 << 23) /* dm-integrity discards/TRIM option is supported */ #define DM_VERITY_PANIC_CORRUPTION_SUPPORTED (1 << 24) /* dm-verity panic on corruption */ #define DM_CRYPT_NO_WORKQUEUE_SUPPORTED (1 << 25) /* dm-crypt suppot for bypassing workqueues */ +#define DM_INTEGRITY_FIX_HMAC_SUPPORTED (1 << 26) /* hmac covers also superblock */ typedef enum { DM_CRYPT = 0, DM_VERITY, DM_INTEGRITY, DM_LINEAR, DM_ERROR, DM_ZERO, DM_UNKNOWN } dm_target_type; enum tdirection { TARGET_SET = 1, TARGET_QUERY }; @@ -120,9 +122,8 @@ struct dm_target { const char *root_hash_sig_key_desc; uint64_t hash_offset; /* hash offset in blocks (not header) */ - uint64_t hash_blocks; /* size of hash device (in hash blocks) */ uint64_t fec_offset; /* FEC offset in blocks (not header) */ - uint64_t fec_blocks; /* size of FEC device (in hash blocks) */ + uint64_t fec_blocks; /* FEC blocks covering data + hash + padding (foreign metadata)*/ struct crypt_params_verity *vp; } verity; struct { @@ -148,6 +149,8 @@ struct dm_target { struct device *meta_device; bool fix_padding; + bool fix_hmac; + bool legacy_recalc; } integrity; struct { uint64_t offset; @@ -187,8 +190,8 @@ int dm_crypt_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t seg uint32_t tag_size, uint32_t sector_size); int dm_verity_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t seg_size, struct device *data_device, struct device *hash_device, struct device *fec_device, - const char *root_hash, uint32_t root_hash_size, const char *root_hash_sig_key_desc, - uint64_t hash_offset_block, uint64_t hash_blocks, struct crypt_params_verity *vp); + const char *root_hash, uint32_t root_hash_size, const char* root_hash_sig_key_desc, + uint64_t hash_offset_block, uint64_t fec_blocks, struct crypt_params_verity *vp); int dm_integrity_target_set(struct crypt_device *cd, struct dm_target *tgt, uint64_t seg_offset, uint64_t seg_size, struct device *meta_device, diff --git a/lib/utils_safe_memory.c b/lib/utils_safe_memory.c index 8c1fb5cb..6908ea42 100644 --- a/lib/utils_safe_memory.c +++ b/lib/utils_safe_memory.c @@ -66,6 +66,7 @@ void *crypt_safe_alloc(size_t size) void crypt_safe_free(void *data) { struct safe_allocation *alloc; + volatile size_t *s; if (!data) return; @@ -75,7 +76,8 @@ void crypt_safe_free(void *data) crypt_safe_memzero(data, alloc->size); - alloc->size = 0x55aa55aa; + s = (volatile size_t *)&alloc->size; + *s = 0x55aa55aa; free(alloc); } diff --git a/lib/utils_storage_wrappers.c b/lib/utils_storage_wrappers.c index 09d16117..e7863380 100644 --- a/lib/utils_storage_wrappers.c +++ b/lib/utils_storage_wrappers.c @@ -199,7 +199,7 @@ int crypt_storage_wrapper_init(struct crypt_device *cd, goto err; } - if (!strcmp(_cipher, "cipher_null")) { + if (crypt_is_cipher_null(_cipher)) { log_dbg(cd, "Requested cipher_null, switching to noop wrapper."); w->type = NONE; *cw = w; diff --git a/lib/utils_storage_wrappers.h b/lib/utils_storage_wrappers.h index f360955b..2519c0af 100644 --- a/lib/utils_storage_wrappers.h +++ b/lib/utils_storage_wrappers.h @@ -22,6 +22,9 @@ #ifndef _UTILS_STORAGE_WRAPPERS_H #define _UTILS_STORAGE_WRAPPERS_H +#include <stdint.h> +#include <sys/types.h> + struct crypt_storage_wrapper; struct device; struct volume_key; diff --git a/lib/verity/verity.c b/lib/verity/verity.c index af31784d..d60af253 100644 --- a/lib/verity/verity.c +++ b/lib/verity/verity.c @@ -23,6 +23,7 @@ #include <stdlib.h> #include <string.h> #include <stdint.h> +#include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <netinet/in.h> @@ -145,6 +146,13 @@ int VERITY_read_sb(struct crypt_device *cd, return 0; } +static void _to_lower(char *str) +{ + for(; *str; str++) + if (isupper(*str)) + *str = tolower(*str); +} + /* Write verity superblock to disk */ int VERITY_write_sb(struct crypt_device *cd, uint64_t sb_offset, @@ -154,6 +162,7 @@ int VERITY_write_sb(struct crypt_device *cd, struct device *device = crypt_metadata_device(cd); struct verity_sb sb = {}; ssize_t hdr_size = sizeof(struct verity_sb); + size_t block_size; char *algorithm; uuid_t uuid; int r, devfd; @@ -173,6 +182,13 @@ int VERITY_write_sb(struct crypt_device *cd, return -EINVAL; } + /* Avoid possible increasing of image size - FEC could fail later because of it */ + block_size = device_block_size(cd, device); + if (block_size > params->hash_block_size) { + device_disable_direct_io(device); + block_size = params->hash_block_size; + } + devfd = device_open(cd, device, O_RDWR); if (devfd < 0) { log_err(cd, _("Cannot open device %s."), device_path(device)); @@ -186,13 +202,17 @@ int VERITY_write_sb(struct crypt_device *cd, sb.hash_block_size = cpu_to_le32(params->hash_block_size); sb.salt_size = cpu_to_le16(params->salt_size); sb.data_blocks = cpu_to_le64(params->data_size); + + /* Kernel always use lower-case */ algorithm = (char *)sb.algorithm; - algorithm[sizeof(sb.algorithm)-1] = '\0'; strncpy(algorithm, params->hash_name, sizeof(sb.algorithm)-1); + algorithm[sizeof(sb.algorithm)-1] = '\0'; + _to_lower(algorithm); + memcpy(sb.salt, params->salt, params->salt_size); memcpy(sb.uuid, uuid, sizeof(sb.uuid)); - r = write_lseek_blockwise(devfd, device_block_size(cd, device), device_alignment(device), + r = write_lseek_blockwise(devfd, block_size, device_alignment(device), (char*)&sb, hdr_size, sb_offset) < hdr_size ? -EIO : 0; if (r) log_err(cd, _("Error during update of verity header on device %s."), @@ -241,7 +261,7 @@ int VERITY_activate(struct crypt_device *cd, { uint32_t dmv_flags; unsigned int fec_errors = 0; - int r; + int r, v; struct crypt_dm_active_device dmd = { .size = verity_hdr->data_size * verity_hdr->data_block_size / 512, .flags = activation_flags, @@ -260,14 +280,19 @@ int VERITY_activate(struct crypt_device *cd, log_dbg(cd, "Verification of data in userspace required."); r = VERITY_verify(cd, verity_hdr, root_hash, root_hash_size); - if (r == -EPERM && fec_device) { + if ((r == -EPERM || r == -EFAULT) && fec_device) { + v = r; log_dbg(cd, "Verification failed, trying to repair with FEC device."); r = VERITY_FEC_process(cd, verity_hdr, fec_device, 1, &fec_errors); if (r < 0) log_err(cd, _("Errors cannot be repaired with FEC device.")); - else if (fec_errors) + else if (fec_errors) { log_err(cd, _("Found %u repairable errors with FEC device."), fec_errors); + /* If root hash failed, we cannot be sure it was properly repaired */ + } + if (v == -EFAULT) + r = -EPERM; } if (r < 0) @@ -298,7 +323,7 @@ int VERITY_activate(struct crypt_device *cd, crypt_metadata_device(cd), fec_device, root_hash, root_hash_size, signature_description, VERITY_hash_offset_block(verity_hdr), - VERITY_hash_blocks(cd, verity_hdr), verity_hdr); + VERITY_FEC_blocks(cd, fec_device, verity_hdr), verity_hdr); if (r) return r; diff --git a/lib/verity/verity.h b/lib/verity/verity.h index 0b7f0cca..f189f460 100644 --- a/lib/verity/verity.h +++ b/lib/verity/verity.h @@ -71,6 +71,10 @@ uint64_t VERITY_hash_offset_block(struct crypt_params_verity *params); uint64_t VERITY_hash_blocks(struct crypt_device *cd, struct crypt_params_verity *params); +uint64_t VERITY_FEC_blocks(struct crypt_device *cd, + struct device *fec_device, + struct crypt_params_verity *params); + int VERITY_UUID_generate(struct crypt_device *cd, char **uuid_string); #endif diff --git a/lib/verity/verity_fec.c b/lib/verity/verity_fec.c index a8a5e86f..5fd82d94 100644 --- a/lib/verity/verity_fec.c +++ b/lib/verity/verity_fec.c @@ -134,8 +134,11 @@ static int FEC_process_inputs(struct crypt_device *cd, /* calculate the total area covered by error correction codes */ ctx.size = 0; - for (n = 0; n < ctx.ninputs; ++n) + for (n = 0; n < ctx.ninputs; ++n) { + log_dbg(cd, "FEC input %s, offset %" PRIu64 " [bytes], length %" PRIu64 " [bytes]", + device_path(ctx.inputs[n].device), ctx.inputs[n].start, ctx.inputs[n].count); ctx.size += ctx.inputs[n].count; + } /* each byte in a data block is covered by a different code */ ctx.blocks = FEC_div_round_up(ctx.size, ctx.block_size); @@ -203,8 +206,8 @@ int VERITY_FEC_process(struct crypt_device *cd, struct device *fec_device, int check_fec, unsigned int *errors) { - int r; - int fd = -1; + int r = -EIO, fd = -1; + size_t ninputs = FEC_INPUT_DEVICES; struct fec_input_device inputs[FEC_INPUT_DEVICES] = { { .device = crypt_data_device(cd), @@ -214,7 +217,8 @@ int VERITY_FEC_process(struct crypt_device *cd, },{ .device = crypt_metadata_device(cd), .fd = -1, - .start = VERITY_hash_offset_block(params) * params->data_block_size + .start = VERITY_hash_offset_block(params) * params->data_block_size, + .count = (VERITY_FEC_blocks(cd, fec_device, params) - params->data_size) * params->data_block_size } }; @@ -230,7 +234,12 @@ int VERITY_FEC_process(struct crypt_device *cd, return -EINVAL; } - r = -EIO; + if (!inputs[0].count) { + log_err(cd, _("Invalid FEC segment length.")); + return -EINVAL; + } + if (!inputs[1].count) + ninputs--; if (check_fec) fd = open(device_path(fec_device), O_RDONLY); @@ -259,16 +268,7 @@ int VERITY_FEC_process(struct crypt_device *cd, goto out; } - /* cover the entire hash device starting from hash_offset */ - r = device_size(inputs[1].device, &inputs[1].count); - if (r) { - log_err(cd, _("Failed to determine size for device %s."), - device_path(inputs[1].device)); - goto out; - } - inputs[1].count -= inputs[1].start; - - r = FEC_process_inputs(cd, params, inputs, FEC_INPUT_DEVICES, fd, check_fec, errors); + r = FEC_process_inputs(cd, params, inputs, ninputs, fd, check_fec, errors); out: if (inputs[0].fd != -1) close(inputs[0].fd); @@ -279,3 +279,38 @@ out: return r; } + +uint64_t VERITY_FEC_blocks(struct crypt_device *cd, + struct device *fec_device, + struct crypt_params_verity *params) +{ + uint64_t blocks = 0; + + /* + * FEC covers this data: + * | protected data | hash area | padding (optional foreign metadata) | + * + * If hash device is in a separate image, metadata covers the whole rest of the image after hash area. + * If hash and FEC device is in the image, metadata ends on the FEC area offset. + */ + if (device_is_identical(crypt_metadata_device(cd), fec_device) > 0) { + log_dbg(cd, "FEC and hash device is the same."); + blocks = params->fec_area_offset; + } else { + /* cover the entire hash device starting from hash_offset */ + if (device_size(crypt_metadata_device(cd), &blocks)) { + log_err(cd, _("Failed to determine size for device %s."), + device_path(crypt_metadata_device(cd))); + return 0; + } + } + + blocks /= params->data_block_size; + if (blocks) + blocks -= VERITY_hash_offset_block(params); + + /* Protected data */ + blocks += params->data_size; + + return blocks; +} diff --git a/lib/verity/verity_hash.c b/lib/verity/verity_hash.c index 61424300..e6bc977f 100644 --- a/lib/verity/verity_hash.c +++ b/lib/verity/verity_hash.c @@ -89,20 +89,12 @@ out: return r; } -static int mult_overflow(off_t *u, off_t b, size_t size) -{ - *u = (uint64_t)b * size; - if ((off_t)(*u / size) != b || (off_t)*u < 0) - return 1; - return 0; -} - static int hash_levels(size_t hash_block_size, size_t digest_size, - off_t data_file_blocks, off_t *hash_position, int *levels, - off_t *hash_level_block, off_t *hash_level_size) + uint64_t data_file_blocks, uint64_t *hash_position, int *levels, + uint64_t *hash_level_block, uint64_t *hash_level_size) { size_t hash_per_block_bits; - off_t s, s_shift; + uint64_t s, s_shift; int i; if (!digest_size) @@ -127,11 +119,10 @@ static int hash_levels(size_t hash_block_size, size_t digest_size, s_shift = (i + 1) * hash_per_block_bits; if (s_shift > 63) return -EINVAL; - s = (data_file_blocks + ((off_t)1 << s_shift) - 1) >> ((i + 1) * hash_per_block_bits); + s = (data_file_blocks + ((uint64_t)1 << s_shift) - 1) >> ((i + 1) * hash_per_block_bits); if (hash_level_size) hash_level_size[i] = s; - if ((*hash_position + s) < *hash_position || - (*hash_position + s) < 0) + if ((*hash_position + s) < *hash_position) return -EINVAL; *hash_position += s; } @@ -140,9 +131,9 @@ static int hash_levels(size_t hash_block_size, size_t digest_size, } static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr, - off_t data_block, size_t data_block_size, - off_t hash_block, size_t hash_block_size, - off_t blocks, int version, + uint64_t data_block, size_t data_block_size, + uint64_t hash_block, size_t hash_block_size, + uint64_t blocks, int version, const char *hash_name, int verify, char *calculated_digest, size_t digest_size, const char *salt, size_t salt_size) @@ -152,14 +143,14 @@ static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr, char read_digest[digest_size]; size_t hash_per_block = 1 << get_bits_down(hash_block_size / digest_size); size_t digest_size_full = 1 << get_bits_up(digest_size); - off_t blocks_to_write = (blocks + hash_per_block - 1) / hash_per_block; - off_t seek_rd, seek_wr; + uint64_t blocks_to_write = (blocks + hash_per_block - 1) / hash_per_block; + uint64_t seek_rd, seek_wr; size_t left_bytes; unsigned i; int r; - if (mult_overflow(&seek_rd, data_block, data_block_size) || - mult_overflow(&seek_wr, hash_block, hash_block_size)) { + if (uint64_mult_overflow(&seek_rd, data_block, data_block_size) || + uint64_mult_overflow(&seek_wr, hash_block, hash_block_size)) { log_err(cd, _("Device offset overflow.")); return -EINVAL; } @@ -241,86 +232,68 @@ static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr, return 0; } -static int VERITY_create_or_verify_hash(struct crypt_device *cd, - int verify, - int version, - const char *hash_name, - struct device *hash_device, - struct device *data_device, - size_t hash_block_size, - size_t data_block_size, - off_t data_blocks, - off_t hash_position, - char *root_hash, - size_t digest_size, - const char *salt, - size_t salt_size) +static int VERITY_create_or_verify_hash(struct crypt_device *cd, bool verify, + struct crypt_params_verity *params, + char *root_hash, size_t digest_size) { char calculated_digest[digest_size]; FILE *data_file = NULL; FILE *hash_file = NULL, *hash_file_2; - off_t hash_level_block[VERITY_MAX_LEVELS]; - off_t hash_level_size[VERITY_MAX_LEVELS]; - off_t data_file_blocks; - off_t data_device_size = 0, hash_device_size = 0; + uint64_t hash_level_block[VERITY_MAX_LEVELS]; + uint64_t hash_level_size[VERITY_MAX_LEVELS]; + uint64_t data_file_blocks; + uint64_t data_device_offset_max = 0, hash_device_offset_max = 0; + uint64_t hash_position = VERITY_hash_offset_block(params); uint64_t dev_size; int levels, i, r; log_dbg(cd, "Hash %s %s, data device %s, data blocks %" PRIu64 ", hash_device %s, offset %" PRIu64 ".", - verify ? "verification" : "creation", hash_name, - device_path(data_device), data_blocks, - device_path(hash_device), hash_position); - - if (data_blocks < 0 || hash_position < 0) { - log_err(cd, _("Invalid size parameters for verity device.")); - return -EINVAL; - } + verify ? "verification" : "creation", params->hash_name, + device_path(crypt_data_device(cd)), params->data_size, + device_path(crypt_metadata_device(cd)), hash_position); - if (!data_blocks) { - r = device_size(data_device, &dev_size); + if (!params->data_size) { + r = device_size(crypt_data_device(cd), &dev_size); if (r < 0) return r; - data_file_blocks = dev_size / data_block_size; + data_file_blocks = dev_size / params->data_block_size; } else - data_file_blocks = data_blocks; + data_file_blocks = params->data_size; - if (mult_overflow(&data_device_size, data_blocks, data_block_size)) { + if (uint64_mult_overflow(&data_device_offset_max, params->data_size, params->data_block_size)) { log_err(cd, _("Device offset overflow.")); return -EINVAL; } + log_dbg(cd, "Data device size required: %" PRIu64 " bytes.", data_device_offset_max); - if (hash_levels(hash_block_size, digest_size, data_file_blocks, &hash_position, + if (hash_levels(params->hash_block_size, digest_size, data_file_blocks, &hash_position, &levels, &hash_level_block[0], &hash_level_size[0])) { log_err(cd, _("Hash area overflow.")); return -EINVAL; } - - log_dbg(cd, "Using %d hash levels.", levels); - - if (mult_overflow(&hash_device_size, hash_position, hash_block_size)) { + if (uint64_mult_overflow(&hash_device_offset_max, hash_position, params->hash_block_size)) { log_err(cd, _("Device offset overflow.")); return -EINVAL; } + log_dbg(cd, "Hash device size required: %" PRIu64 " bytes.", + hash_device_offset_max - params->hash_area_offset); + log_dbg(cd, "Using %d hash levels.", levels); - log_dbg(cd, "Data device size required: %" PRIu64 " bytes.", - data_device_size); - data_file = fopen(device_path(data_device), "r"); + data_file = fopen(device_path(crypt_data_device(cd)), "r"); if (!data_file) { log_err(cd, _("Cannot open device %s."), - device_path(data_device) + device_path(crypt_data_device(cd)) ); r = -EIO; goto out; } - log_dbg(cd, "Hash device size required: %" PRIu64 " bytes.", - hash_device_size); - hash_file = fopen(device_path(hash_device), verify ? "r" : "r+"); + hash_file = fopen(device_path(crypt_metadata_device(cd)), verify ? "r" : "r+"); if (!hash_file) { log_err(cd, _("Cannot open device %s."), - device_path(hash_device)); + device_path(crypt_metadata_device(cd))); r = -EIO; goto out; } @@ -330,25 +303,25 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, for (i = 0; i < levels; i++) { if (!i) { r = create_or_verify(cd, data_file, hash_file, - 0, data_block_size, - hash_level_block[i], hash_block_size, - data_file_blocks, version, hash_name, verify, - calculated_digest, digest_size, salt, salt_size); + 0, params->data_block_size, + hash_level_block[i], params->hash_block_size, + data_file_blocks, params->hash_type, params->hash_name, verify, + calculated_digest, digest_size, params->salt, params->salt_size); if (r) goto out; } else { - hash_file_2 = fopen(device_path(hash_device), "r"); + hash_file_2 = fopen(device_path(crypt_metadata_device(cd)), "r"); if (!hash_file_2) { log_err(cd, _("Cannot open device %s."), - device_path(hash_device)); + device_path(crypt_metadata_device(cd))); r = -EIO; goto out; } r = create_or_verify(cd, hash_file_2, hash_file, - hash_level_block[i - 1], hash_block_size, - hash_level_block[i], hash_block_size, - hash_level_size[i - 1], version, hash_name, verify, - calculated_digest, digest_size, salt, salt_size); + hash_level_block[i - 1], params->hash_block_size, + hash_level_block[i], params->hash_block_size, + hash_level_size[i - 1], params->hash_type, params->hash_name, verify, + calculated_digest, digest_size, params->salt, params->salt_size); fclose(hash_file_2); if (r) goto out; @@ -357,23 +330,23 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, if (levels) r = create_or_verify(cd, hash_file, NULL, - hash_level_block[levels - 1], hash_block_size, - 0, hash_block_size, - 1, version, hash_name, verify, - calculated_digest, digest_size, salt, salt_size); + hash_level_block[levels - 1], params->hash_block_size, + 0, params->hash_block_size, + 1, params->hash_type, params->hash_name, verify, + calculated_digest, digest_size, params->salt, params->salt_size); else r = create_or_verify(cd, data_file, NULL, - 0, data_block_size, - 0, hash_block_size, - data_file_blocks, version, hash_name, verify, - calculated_digest, digest_size, salt, salt_size); + 0, params->data_block_size, + 0, params->hash_block_size, + data_file_blocks, params->hash_type, params->hash_name, verify, + calculated_digest, digest_size, params->salt, params->salt_size); out: if (verify) { if (r) log_err(cd, _("Verification of data area failed.")); else { log_dbg(cd, "Verification of data area succeeded."); - r = memcmp(root_hash, calculated_digest, digest_size) ? -EPERM : 0; + r = memcmp(root_hash, calculated_digest, digest_size) ? -EFAULT : 0; if (r) log_err(cd, _("Verification of root hash failed.")); else @@ -403,19 +376,7 @@ int VERITY_verify(struct crypt_device *cd, const char *root_hash, size_t root_hash_size) { - return VERITY_create_or_verify_hash(cd, 1, - verity_hdr->hash_type, - verity_hdr->hash_name, - crypt_metadata_device(cd), - crypt_data_device(cd), - verity_hdr->hash_block_size, - verity_hdr->data_block_size, - verity_hdr->data_size, - VERITY_hash_offset_block(verity_hdr), - CONST_CAST(char*)root_hash, - root_hash_size, - verity_hdr->salt, - verity_hdr->salt_size); + return VERITY_create_or_verify_hash(cd, 1, verity_hdr, CONST_CAST(char*)root_hash, root_hash_size); } /* Create verity hash */ @@ -433,24 +394,12 @@ int VERITY_create(struct crypt_device *cd, log_err(cd, _("WARNING: Kernel cannot activate device if data " "block size exceeds page size (%u)."), pgsize); - return VERITY_create_or_verify_hash(cd, 0, - verity_hdr->hash_type, - verity_hdr->hash_name, - crypt_metadata_device(cd), - crypt_data_device(cd), - verity_hdr->hash_block_size, - verity_hdr->data_block_size, - verity_hdr->data_size, - VERITY_hash_offset_block(verity_hdr), - CONST_CAST(char*)root_hash, - root_hash_size, - verity_hdr->salt, - verity_hdr->salt_size); + return VERITY_create_or_verify_hash(cd, 0, verity_hdr, CONST_CAST(char*)root_hash, root_hash_size); } uint64_t VERITY_hash_blocks(struct crypt_device *cd, struct crypt_params_verity *params) { - off_t hash_position = 0; + uint64_t hash_position = 0; int levels = 0; if (hash_levels(params->hash_block_size, crypt_get_volume_key_size(cd), diff --git a/src/cryptsetup.c b/src/cryptsetup.c index b32f167d..70e1f390 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -24,22 +24,43 @@ #include "cryptsetup.h" #include <uuid/uuid.h> -static const char *opt_cipher = NULL; -static const char *opt_keyslot_cipher = NULL; -static const char *opt_hash = NULL; -static int opt_verify_passphrase = 0; +static char *opt_cipher = NULL; +static char *opt_keyslot_cipher = NULL; +static char *opt_hash = NULL; +static char *opt_json_file = NULL; +static char *opt_key_file = NULL; +static char *opt_keyfile_stdin = NULL; +static char *opt_keyfiles[MAX_KEYFILES]; +static char *opt_master_key_file = NULL; +static char *opt_header_backup_file = NULL; +static char *opt_uuid = NULL; +static char *opt_header_device = NULL; +static char *opt_type = NULL; +static char *opt_pbkdf = NULL; +static char *opt_priority = NULL; /* normal */ +static char *opt_integrity = NULL; /* none */ +static char *opt_key_description = NULL; +static char *opt_label = NULL; +static char *opt_subsystem = NULL; +static char *opt_active_name = NULL; +static char *opt_resilience_mode = NULL; /* default value "checksum" */ +static char *opt_resilience_hash = NULL; /* default value "sha256" */ + +/* helper strings converted to uint64_t later */ +static char *opt_reduce_size_str = NULL; +static char *opt_hotzone_size_str = NULL; +static char *opt_device_size_str = NULL; +static char *opt_luks2_metadata_size_str = NULL; +static char *opt_luks2_keyslots_size_str = NULL; -static const char *opt_json_file = NULL; -static const char *opt_key_file = NULL; -static const char *opt_keyfile_stdin = NULL; -static int opt_keyfiles_count = 0; -static const char *opt_keyfiles[MAX_KEYFILES]; +static uint64_t opt_reduce_size = 0; +static uint64_t opt_hotzone_size = 0; +static uint64_t opt_device_size = 0; +static uint64_t opt_luks2_metadata_size = 0; +static uint64_t opt_luks2_keyslots_size = 0; -static const char *opt_master_key_file = NULL; -static const char *opt_header_backup_file = NULL; -static const char *opt_uuid = NULL; -static const char *opt_header_device = NULL; -static const char *opt_type = "luks"; +static int opt_keyfiles_count = 0; +static int opt_verify_passphrase = 0; static int opt_key_size = 0; static int opt_keyslot_key_size = 0; static long opt_keyfile_size = 0; @@ -76,57 +97,68 @@ static int opt_veracrypt_query_pim = 0; static int opt_deferred_remove = 0; static int opt_serialize_memory_hard_pbkdf = 0; //FIXME: check uint32 overflow for long type -static const char *opt_pbkdf = NULL; static long opt_pbkdf_memory = DEFAULT_LUKS2_MEMORY_KB; static long opt_pbkdf_parallel = DEFAULT_LUKS2_PARALLEL_THREADS; static long opt_pbkdf_iterations = 0; static int opt_iteration_time = 0; static int opt_disable_locks = 0; static int opt_disable_keyring = 0; -static const char *opt_priority = NULL; /* normal */ -static const char *opt_integrity = NULL; /* none */ static int opt_integrity_nojournal = 0; static int opt_integrity_no_wipe = 0; static int opt_integrity_legacy_padding = 0; -static const char *opt_key_description = NULL; static int opt_sector_size = 0; static int opt_iv_large_sectors = 0; static int opt_persistent = 0; -static const char *opt_label = NULL; -static const char *opt_subsystem = NULL; static int opt_unbound = 0; static int opt_refresh = 0; /* LUKS2 reencryption parameters */ -static const char *opt_active_name = NULL; -static const char *opt_resilience_mode = "checksum"; // TODO: default resilience -static const char *opt_resilience_hash = "sha256"; // TODO: default checksum hash static int opt_encrypt = 0; static int opt_reencrypt_init_only = 0; static int opt_reencrypt_resume_only = 0; static int opt_decrypt = 0; -static const char *opt_reduce_size_str = NULL; -static uint64_t opt_reduce_size = 0; - -static const char *opt_hotzone_size_str = NULL; -static uint64_t opt_hotzone_size = 0; - -static const char *opt_device_size_str = NULL; -static uint64_t opt_device_size = 0; - /* do not set from command line, use helpers above */ static int64_t opt_data_shift; - -static const char *opt_luks2_metadata_size_str = NULL; -static uint64_t opt_luks2_metadata_size = 0; -static const char *opt_luks2_keyslots_size_str = NULL; -static uint64_t opt_luks2_keyslots_size = 0; +static const char *device_type = "luks"; +static const char *set_pbkdf = NULL; static const char **action_argv; static int action_argc; static const char *null_action_argv[] = {NULL, NULL}; +void tools_cleanup(void) +{ + FREE_AND_NULL(opt_cipher); + FREE_AND_NULL(opt_keyslot_cipher); + FREE_AND_NULL(opt_hash); + FREE_AND_NULL(opt_json_file); + FREE_AND_NULL(opt_key_file); + FREE_AND_NULL(opt_keyfile_stdin); + FREE_AND_NULL(opt_master_key_file); + FREE_AND_NULL(opt_header_backup_file); + FREE_AND_NULL(opt_uuid); + FREE_AND_NULL(opt_header_device); + FREE_AND_NULL(opt_type); + FREE_AND_NULL(opt_pbkdf); + FREE_AND_NULL(opt_priority); + FREE_AND_NULL(opt_integrity); + FREE_AND_NULL(opt_key_description); + FREE_AND_NULL(opt_label); + FREE_AND_NULL(opt_subsystem); + FREE_AND_NULL(opt_active_name); + FREE_AND_NULL(opt_resilience_mode); + FREE_AND_NULL(opt_resilience_hash); + FREE_AND_NULL(opt_reduce_size_str); + FREE_AND_NULL(opt_hotzone_size_str); + FREE_AND_NULL(opt_device_size_str); + FREE_AND_NULL(opt_luks2_metadata_size_str); + FREE_AND_NULL(opt_luks2_keyslots_size_str); + + while (opt_keyfiles_count) + free(opt_keyfiles[--opt_keyfiles_count]); +} + static const char *uuid_or_device_header(const char **data_device) { if (data_device) @@ -440,7 +472,7 @@ static int tcrypt_load(struct crypt_device *cd, struct crypt_params_tcrypt *para unsigned long long tmp_pim_ull = 0; r = tools_get_key(_("Enter VeraCrypt PIM: "), - CONST_CAST(char**)&tmp_pim_nptr, + &tmp_pim_nptr, &tmp_pim_size, 0, 0, opt_keyfile_stdin, opt_timeout, _verify_passphrase(0), 0, cd); if (r < 0) @@ -457,7 +489,7 @@ static int tcrypt_load(struct crypt_device *cd, struct crypt_params_tcrypt *para log_err(_("Invalid PIM value: outside of range.")); r = -ERANGE; } - crypt_safe_free(CONST_CAST(char*)tmp_pim_nptr); + crypt_safe_free(tmp_pim_nptr); if (r < 0) continue; @@ -500,7 +532,7 @@ static int action_open_tcrypt(void) { struct crypt_device *cd = NULL; struct crypt_params_tcrypt params = { - .keyfiles = opt_keyfiles, + .keyfiles = CONST_CAST(const char **)opt_keyfiles, .keyfiles_count = opt_keyfiles_count, .flags = CRYPT_TCRYPT_LEGACY_MODES | (opt_veracrypt ? CRYPT_TCRYPT_VERA_MODES : 0), @@ -618,7 +650,7 @@ static int action_tcryptDump(void) { struct crypt_device *cd = NULL; struct crypt_params_tcrypt params = { - .keyfiles = opt_keyfiles, + .keyfiles = CONST_CAST(const char **)opt_keyfiles, .keyfiles_count = opt_keyfiles_count, .flags = CRYPT_TCRYPT_LEGACY_MODES | (opt_veracrypt ? CRYPT_TCRYPT_VERA_MODES : 0), @@ -715,7 +747,9 @@ static int action_resize(void) r = crypt_activate_by_token(cd, NULL, opt_token, NULL, CRYPT_ACTIVATE_KEYRING_KEY); tools_keyslot_msg(r, UNLOCKED); - if (r < 0 && opt_token_only) + if (r >= 0) + goto resize; + else if (opt_token_only) goto out; r = tools_get_key(NULL, &password, &passwordLen, @@ -731,7 +765,7 @@ static int action_resize(void) tools_keyslot_msg(r, UNLOCKED); crypt_safe_free(password); } - +resize: if (opt_device_size) opt_size = opt_device_size / SECTOR_SIZE; @@ -961,10 +995,10 @@ static int action_benchmark(void) int i, r; log_std(_("# Tests are approximate using memory only (no storage IO).\n")); - if (opt_pbkdf || opt_hash) { - if (!opt_pbkdf && opt_hash) - opt_pbkdf = CRYPT_KDF_PBKDF2; - r = action_benchmark_kdf(opt_pbkdf, opt_hash, key_size); + if (set_pbkdf || opt_hash) { + if (!set_pbkdf && opt_hash) + set_pbkdf = CRYPT_KDF_PBKDF2; + r = action_benchmark_kdf(set_pbkdf, opt_hash, key_size); } else if (opt_cipher) { r = crypt_parse_name_and_mode(opt_cipher, cipher, NULL, cipher_mode); if (r < 0) { @@ -1036,7 +1070,7 @@ static int set_pbkdf_params(struct crypt_device *cd, const char *dev_type) if (!pbkdf_default) return -EINVAL; - pbkdf.type = opt_pbkdf ?: pbkdf_default->type; + pbkdf.type = set_pbkdf ?: pbkdf_default->type; pbkdf.hash = opt_hash ?: pbkdf_default->hash; pbkdf.time_ms = (uint32_t)opt_iteration_time ?: pbkdf_default->time_ms; if (strcmp(pbkdf.type, CRYPT_KDF_PBKDF2)) { @@ -1063,11 +1097,17 @@ static int set_keyslot_params(struct crypt_device *cd, int keyslot) if (!cipher) return -EINVAL; + if (crypt_is_cipher_null(cipher)) { + log_dbg("Keyslot %d uses cipher_null. Replacing with default encryption in new keyslot.", keyslot); + cipher = DEFAULT_LUKS2_KEYSLOT_CIPHER; + key_size = DEFAULT_LUKS2_KEYSLOT_KEYBITS / 8; + } + if (crypt_keyslot_set_encryption(cd, cipher, key_size)) return -EINVAL; /* if requested any of those just reinitialize context pbkdf */ - if (opt_pbkdf || opt_hash || opt_pbkdf_iterations || opt_iteration_time) + if (set_pbkdf || opt_hash || opt_pbkdf_iterations || opt_iteration_time) return set_pbkdf_params(cd, CRYPT_LUKS2); if (crypt_keyslot_get_pbkdf(cd, keyslot, &pbkdf)) @@ -1139,7 +1179,7 @@ static int action_luksRepair(void) goto out; crypt_set_log_callback(cd, quiet_log, NULL); - r = crypt_load(cd, luksType(opt_type), NULL); + r = crypt_load(cd, luksType(device_type), NULL); crypt_set_log_callback(cd, tool_log, NULL); if (r == 0) { log_verbose(_("No known problems detected for LUKS header.")); @@ -1153,7 +1193,7 @@ static int action_luksRepair(void) r = yesDialog(_("Really try to repair LUKS device header?"), _("Operation aborted.\n")) ? 0 : -EINVAL; if (r == 0) - r = crypt_repair(cd, luksType(opt_type), NULL); + r = crypt_repair(cd, luksType(device_type), NULL); skip_repair: if (!r && crypt_get_type(cd) && !strcmp(crypt_get_type(cd), CRYPT_LUKS2)) r = _do_luks2_reencrypt_recovery(cd); @@ -1202,6 +1242,21 @@ static int strcmp_or_null(const char *str, const char *expected) return !str ? 0 : strcmp(str, expected); } +static int get_adjusted_key_size(const char *cipher_mode, uint32_t default_size_bits, int integrity_keysize) +{ + uint32_t keysize_bits = opt_key_size; + +#ifdef ENABLE_LUKS_ADJUST_XTS_KEYSIZE + if (!opt_key_size && !strncmp(cipher_mode, "xts-", 4)) { + if (default_size_bits == 128) + keysize_bits = 256; + else if (default_size_bits == 256) + keysize_bits = 512; + } +#endif + return (keysize_bits ?: default_size_bits) / 8 + integrity_keysize; +} + static int _luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_passwordLen) { int r = -EINVAL, keysize, integrity_keysize = 0, fd, created = 0; @@ -1225,7 +1280,7 @@ static int _luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_ }; void *params; - type = luksType(opt_type); + type = luksType(device_type); if (!type) type = crypt_get_default_type(); @@ -1254,7 +1309,7 @@ static int _luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_ /* Create header file (must contain at least one sector)? */ if (opt_header_device && stat(opt_header_device, &st) < 0 && errno == ENOENT) { if (!opt_batch_mode && - !yesDialog("Header file does not exist, do you want to create it?", + !yesDialog(_("Header file does not exist, do you want to create it?"), _("Operation aborted.\n"))) return -EPERM; @@ -1293,7 +1348,7 @@ static int _luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_ } /* Never call pwquality if using null cipher */ - if (tools_is_cipher_null(cipher)) + if (crypt_is_cipher_null(cipher)) opt_force_password = 1; if ((r = crypt_init(&cd, header_device))) { @@ -1334,15 +1389,7 @@ static int _luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_ goto out; } -#ifdef ENABLE_LUKS_ADJUST_XTS_KEYSIZE - if (!opt_key_size && !strncmp(cipher_mode, "xts-", 4)) { - if (DEFAULT_LUKS1_KEYBITS == 128) - opt_key_size = 256; - else if (DEFAULT_LUKS1_KEYBITS == 256) - opt_key_size = 512; - } -#endif - keysize = (opt_key_size ?: DEFAULT_LUKS1_KEYBITS) / 8 + integrity_keysize; + keysize = get_adjusted_key_size(cipher_mode, DEFAULT_LUKS1_KEYBITS, integrity_keysize); if (opt_random) crypt_set_rng_type(cd, CRYPT_RNG_RANDOM); @@ -1441,7 +1488,7 @@ static int action_open_luks(void) if ((r = crypt_init_data_device(&cd, header_device, data_device))) goto out; - if ((r = crypt_load(cd, luksType(opt_type), NULL))) { + if ((r = crypt_load(cd, luksType(device_type), NULL))) { log_err(_("Device %s is not a valid LUKS device."), header_device); goto out; @@ -1567,7 +1614,7 @@ static int action_luksKillSlot(void) crypt_set_confirm_callback(cd, yesDialog, NULL); - if ((r = crypt_load(cd, luksType(opt_type), NULL))) { + if ((r = crypt_load(cd, luksType(device_type), NULL))) { log_err(_("Device %s is not a valid LUKS device."), uuid_or_device_header(NULL)); goto out; @@ -1624,7 +1671,7 @@ static int action_luksRemoveKey(void) crypt_set_confirm_callback(cd, yesDialog, NULL); - if ((r = crypt_load(cd, luksType(opt_type), NULL))) { + if ((r = crypt_load(cd, luksType(device_type), NULL))) { log_err(_("Device %s is not a valid LUKS device."), uuid_or_device_header(NULL)); goto out; @@ -1691,7 +1738,7 @@ static int luksAddUnboundKey(void) goto out; /* Never call pwquality if using null cipher */ - if (tools_is_cipher_null(crypt_get_cipher(cd))) + if (crypt_is_cipher_null(crypt_get_cipher(cd))) opt_force_password = 1; keysize = opt_key_size / 8; @@ -1747,7 +1794,7 @@ static int action_luksAddKey(void) crypt_set_confirm_callback(cd, yesDialog, NULL); - if ((r = crypt_load(cd, luksType(opt_type), NULL))) { + if ((r = crypt_load(cd, luksType(device_type), NULL))) { log_err(_("Device %s is not a valid LUKS device."), uuid_or_device_header(NULL)); goto out; @@ -1758,7 +1805,7 @@ static int action_luksAddKey(void) goto out; /* Never call pwquality if using null cipher */ - if (tools_is_cipher_null(crypt_get_cipher(cd))) + if (crypt_is_cipher_null(crypt_get_cipher(cd))) opt_force_password = 1; keysize = crypt_get_volume_key_size(cd); @@ -1850,7 +1897,7 @@ static int action_luksChangeKey(void) if ((r = crypt_init(&cd, uuid_or_device_header(NULL)))) goto out; - if ((r = crypt_load(cd, luksType(opt_type), NULL))) { + if ((r = crypt_load(cd, luksType(device_type), NULL))) { log_err(_("Device %s is not a valid LUKS device."), uuid_or_device_header(NULL)); goto out; @@ -1861,7 +1908,7 @@ static int action_luksChangeKey(void) goto out; /* Never call pwquality if using null cipher */ - if (tools_is_cipher_null(crypt_get_cipher(cd))) + if (crypt_is_cipher_null(crypt_get_cipher(cd))) opt_force_password = 1; r = set_pbkdf_params(cd, crypt_get_type(cd)); @@ -1968,7 +2015,7 @@ static int action_isLuks(void) goto out; crypt_set_log_callback(cd, quiet_log, NULL); - r = crypt_load(cd, luksType(opt_type), NULL); + r = crypt_load(cd, luksType(device_type), NULL); out: crypt_free(cd); return r; @@ -1985,7 +2032,7 @@ static int action_luksUUID(void) crypt_set_confirm_callback(cd, yesDialog, _("Operation aborted.\n")); - if ((r = crypt_load(cd, luksType(opt_type), NULL))) + if ((r = crypt_load(cd, luksType(device_type), NULL))) goto out; if (opt_uuid) @@ -2144,7 +2191,7 @@ static int action_luksDump(void) if ((r = crypt_init(&cd, uuid_or_device_header(NULL)))) goto out; - if ((r = crypt_load(cd, luksType(opt_type), NULL))) { + if ((r = crypt_load(cd, luksType(device_type), NULL))) { log_err(_("Device %s is not a valid LUKS device."), uuid_or_device_header(NULL)); goto out; @@ -2167,8 +2214,11 @@ static int action_luksSuspend(void) int r; r = crypt_init_by_name_and_header(&cd, action_argv[0], uuid_or_device(opt_header_device)); - if (!r) + if (!r) { r = crypt_suspend(cd, action_argv[0]); + if (r == -ENODEV) + log_err(_("%s is not active %s device name."), action_argv[0], "LUKS"); + } crypt_free(cd); return r; @@ -2180,12 +2230,25 @@ static int action_luksResume(void) char *password = NULL; size_t passwordLen; int r, tries; + const char *type, *req_type = luksType(device_type); + + if (req_type && strcmp(req_type, CRYPT_LUKS1) && strcmp(req_type, CRYPT_LUKS2)) + return -EINVAL; if ((r = crypt_init_by_name_and_header(&cd, action_argv[0], uuid_or_device(opt_header_device)))) + return r; + + r = -EINVAL; + type = crypt_get_type(cd); + if (!type || (strcmp(type, CRYPT_LUKS1) && strcmp(type, CRYPT_LUKS2))) { + log_err(_("%s is not active LUKS device name or header is missing."), action_argv[0]); goto out; + } - if ((r = crypt_load(cd, luksType(opt_type), NULL))) + if (req_type && strcmp(req_type, crypt_get_type(cd))) { + log_err(_("%s is not active %s device name."), action_argv[0], req_type); goto out; + } tries = (tools_is_stdin(opt_key_file) && isatty(STDIN_FILENO)) ? opt_tries : 1; do { @@ -2289,38 +2352,38 @@ static const char *_get_device_type(void) static int action_open(void) { - if (opt_refresh && !opt_type) + if (opt_refresh && !device_type) /* read device type from active mapping */ - opt_type = _get_device_type(); + device_type = _get_device_type(); - if (!opt_type) + if (!device_type) return -EINVAL; - if (!strcmp(opt_type, "luks") || - !strcmp(opt_type, "luks1") || - !strcmp(opt_type, "luks2")) { + if (!strcmp(device_type, "luks") || + !strcmp(device_type, "luks1") || + !strcmp(device_type, "luks2")) { if (action_argc < 2 && (!opt_test_passphrase && !opt_refresh)) goto args; return action_open_luks(); - } else if (!strcmp(opt_type, "plain")) { + } else if (!strcmp(device_type, "plain")) { if (action_argc < 2 && !opt_refresh) goto args; return action_open_plain(); - } else if (!strcmp(opt_type, "loopaes")) { + } else if (!strcmp(device_type, "loopaes")) { if (action_argc < 2 && !opt_refresh) goto args; return action_open_loopaes(); - } else if (!strcmp(opt_type, "tcrypt")) { + } else if (!strcmp(device_type, "tcrypt")) { if (action_argc < 2 && !opt_test_passphrase) goto args; return action_open_tcrypt(); - } else if (!strcmp(opt_type, "bitlk")) { + } else if (!strcmp(device_type, "bitlk")) { if (action_argc < 2 && !opt_test_passphrase) goto args; return action_open_bitlk(); } - log_err(_("Unrecognized metadata device type %s."), opt_type); + log_err(_("Unrecognized metadata device type %s."), device_type); return -EINVAL; args: log_err(_("Command requires device and mapped name as arguments.")); @@ -2339,7 +2402,7 @@ static int action_luksErase(void) crypt_set_confirm_callback(cd, yesDialog, NULL); - if ((r = crypt_load(cd, luksType(opt_type), NULL))) { + if ((r = crypt_load(cd, luksType(device_type), NULL))) { log_err(_("Device %s is not a valid LUKS device."), uuid_or_device_header(NULL)); goto out; @@ -2359,8 +2422,10 @@ static int action_luksErase(void) /* Safety check */ max = crypt_keyslot_max(crypt_get_type(cd)); - if (max <= 0) - return -EINVAL; + if (max <= 0) { + r = -EINVAL; + goto out; + } for (i = 0; i < max; i++) { ki = crypt_keyslot_status(cd, i); @@ -2384,9 +2449,9 @@ static int action_luksConvert(void) const char *to_type, *from_type; int r; - if (!strcmp(opt_type, "luks2")) { + if (!strcmp(device_type, "luks2")) { to_type = CRYPT_LUKS2; - } else if (!strcmp(opt_type, "luks1")) { + } else if (!strcmp(device_type, "luks1")) { to_type = CRYPT_LUKS1; } else { log_err(_("Invalid LUKS type, only luks1 and luks2 are supported.")); @@ -2705,8 +2770,8 @@ static int action_reencrypt_load(struct crypt_device *cd) char dm_name[PATH_MAX] = {}, *password = NULL; const char *active_name = NULL; struct crypt_params_reencrypt params = { - .resilience = opt_resilience_mode, - .hash = opt_resilience_hash, + .resilience = opt_resilience_mode ?: "checksum", + .hash = opt_resilience_hash ?: "sha256", .max_hotzone_size = opt_hotzone_size / SECTOR_SIZE, .device_size = opt_device_size / SECTOR_SIZE, .flags = CRYPT_REENCRYPT_RESUME_ONLY @@ -2750,8 +2815,8 @@ static int action_encrypt_luks2(struct crypt_device **cd) struct crypt_params_reencrypt params = { .mode = CRYPT_REENCRYPT_ENCRYPT, .direction = opt_data_shift < 0 ? CRYPT_REENCRYPT_BACKWARD : CRYPT_REENCRYPT_FORWARD, - .resilience = opt_resilience_mode, - .hash = opt_resilience_hash, + .resilience = opt_resilience_mode ?: "checksum", + .hash = opt_resilience_hash ?: "sha256", .max_hotzone_size = opt_hotzone_size / SECTOR_SIZE, .device_size = opt_device_size / SECTOR_SIZE, .luks2 = &luks2_params, @@ -2760,7 +2825,7 @@ static int action_encrypt_luks2(struct crypt_device **cd) _set_reencryption_flags(¶ms.flags); - type = luksType(opt_type); + type = luksType(device_type); if (!type) type = crypt_get_default_type(); @@ -2800,7 +2865,8 @@ static int action_encrypt_luks2(struct crypt_device **cd) if (!opt_uuid) { uuid_generate(uuid); uuid_unparse(uuid, uuid_str); - opt_uuid = uuid_str; + if (!(opt_uuid = strdup(uuid_str))) + return -ENOMEM; } /* Check the data device is not LUKS device already */ @@ -2839,7 +2905,10 @@ static int action_encrypt_luks2(struct crypt_device **cd) goto err; } - opt_header_device = header_file; + if (!(opt_header_device = strdup(header_file))) { + r = -ENOMEM; + goto err; + } /* * FIXME: just override offset here, but we should support both. * offset and implicit offset via data shift (lvprepend?) @@ -2925,8 +2994,8 @@ static int action_decrypt_luks2(struct crypt_device *cd) struct crypt_params_reencrypt params = { .mode = CRYPT_REENCRYPT_DECRYPT, .direction = opt_data_shift > 0 ? CRYPT_REENCRYPT_FORWARD : CRYPT_REENCRYPT_BACKWARD, - .resilience = opt_data_shift ? "datashift" : opt_resilience_mode, - .hash = opt_resilience_hash, + .resilience = opt_data_shift ? "datashift" : (opt_resilience_mode ?: "checksum"), + .hash = opt_resilience_hash ?: "sha256", .data_shift = imaxabs(opt_data_shift) / SECTOR_SIZE, .device_size = opt_device_size / SECTOR_SIZE, .max_hotzone_size = opt_hotzone_size / SECTOR_SIZE, @@ -3138,8 +3207,8 @@ static int action_reencrypt_luks2(struct crypt_device *cd) struct crypt_params_reencrypt params = { .mode = CRYPT_REENCRYPT_REENCRYPT, .direction = opt_data_shift < 0 ? CRYPT_REENCRYPT_BACKWARD : CRYPT_REENCRYPT_FORWARD, - .resilience = opt_data_shift ? "datashift" : opt_resilience_mode, - .hash = opt_resilience_hash, + .resilience = opt_data_shift ? "datashift" : (opt_resilience_mode ?: "checksum"), + .hash = opt_resilience_hash ?: "sha256", .data_shift = imaxabs(opt_data_shift) / SECTOR_SIZE, .max_hotzone_size = opt_hotzone_size / SECTOR_SIZE, .device_size = opt_device_size / SECTOR_SIZE, @@ -3148,6 +3217,11 @@ static int action_reencrypt_luks2(struct crypt_device *cd) _set_reencryption_flags(¶ms.flags); + if (!opt_cipher && crypt_is_cipher_null(crypt_get_cipher(cd))) { + opt_cipher = strdup(DEFAULT_CIPHER(LUKS1)); + log_std(_("Switching data encryption cipher to %s.\n"), opt_cipher); + } + if (!opt_cipher) { strncpy(cipher, crypt_get_cipher(cd), MAX_CIPHER_LEN - 1); strncpy(mode, crypt_get_cipher_mode(cd), MAX_CIPHER_LEN - 1); @@ -3164,10 +3238,8 @@ static int action_reencrypt_luks2(struct crypt_device *cd) if (r) return r; - if (opt_key_size) - key_size = opt_key_size / 8; - else if (opt_cipher) - key_size = DEFAULT_LUKS1_KEYBITS / 8; + if (opt_key_size || opt_cipher) + key_size = get_adjusted_key_size(mode, DEFAULT_LUKS1_KEYBITS, 0); else key_size = crypt_get_volume_key_size(cd); @@ -3450,10 +3522,12 @@ static void help(poptContext popt_context, #if defined(ENABLE_LUKS_ADJUST_XTS_KEYSIZE) && DEFAULT_LUKS1_KEYBITS != 512 log_std(_("\tLUKS: Default keysize with XTS mode (two internal keys) will be doubled.\n")); #endif + tools_cleanup(); poptFreeContext(popt_context); exit(EXIT_SUCCESS); } else if (key->shortName == 'V') { log_std("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); + tools_cleanup(); poptFreeContext(popt_context); exit(EXIT_SUCCESS); } else @@ -3494,7 +3568,6 @@ static int run_action(struct action_type *action) int main(int argc, const char **argv) { - static char *popt_tmp; static struct poptOption popt_help_options[] = { { NULL, '\0', POPT_ARG_CALLBACK, help, 0, NULL, NULL }, { "help", '?', POPT_ARG_NONE, NULL, 0, N_("Show this help message"), NULL }, @@ -3510,19 +3583,19 @@ int main(int argc, const char **argv) { "cipher", 'c', POPT_ARG_STRING, &opt_cipher, 0, N_("The cipher used to encrypt the disk (see /proc/crypto)"), NULL }, { "hash", 'h', POPT_ARG_STRING, &opt_hash, 0, N_("The hash used to create the encryption key from the passphrase"), NULL }, { "verify-passphrase", 'y', POPT_ARG_NONE, &opt_verify_passphrase, 0, N_("Verifies the passphrase by asking for it twice"), NULL }, - { "key-file", 'd', POPT_ARG_STRING, &opt_key_file, 6, N_("Read the key from a file"), NULL }, + { "key-file", 'd', POPT_ARG_STRING, NULL, 6, N_("Read the key from a file"), NULL }, { "master-key-file", '\0', POPT_ARG_STRING, &opt_master_key_file, 0, N_("Read the volume (master) key from file."), NULL }, { "dump-master-key", '\0', POPT_ARG_NONE, &opt_dump_master_key, 0, N_("Dump volume (master) key instead of keyslots info"), NULL }, { "key-size", 's', POPT_ARG_INT, &opt_key_size, 0, N_("The size of the encryption key"), N_("BITS") }, { "keyfile-size", 'l', POPT_ARG_LONG, &opt_keyfile_size, 0, N_("Limits the read from keyfile"), N_("bytes") }, - { "keyfile-offset", '\0', POPT_ARG_STRING, &popt_tmp, 4, N_("Number of bytes to skip in keyfile"), N_("bytes") }, + { "keyfile-offset", '\0', POPT_ARG_STRING, NULL, 4, N_("Number of bytes to skip in keyfile"), N_("bytes") }, { "new-keyfile-size", '\0', POPT_ARG_LONG, &opt_new_keyfile_size, 0, N_("Limits the read from newly added keyfile"), N_("bytes") }, - { "new-keyfile-offset",'\0', POPT_ARG_STRING, &popt_tmp, 5, N_("Number of bytes to skip in newly added keyfile"), N_("bytes") }, + { "new-keyfile-offset",'\0', POPT_ARG_STRING, NULL, 5, N_("Number of bytes to skip in newly added keyfile"), N_("bytes") }, { "key-slot", 'S', POPT_ARG_INT, &opt_key_slot, 0, N_("Slot number for new key (default is first free)"), NULL }, - { "size", 'b', POPT_ARG_STRING, &popt_tmp, 1, N_("The size of the device"), N_("SECTORS") }, + { "size", 'b', POPT_ARG_STRING, NULL, 1, N_("The size of the device"), N_("SECTORS") }, { "device-size", '\0', POPT_ARG_STRING, &opt_device_size_str, 0, N_("Use only specified device size (ignore rest of device). DANGEROUS!"), N_("bytes") }, - { "offset", 'o', POPT_ARG_STRING, &popt_tmp, 2, N_("The start offset in the backend device"), N_("SECTORS") }, - { "skip", 'p', POPT_ARG_STRING, &popt_tmp, 3, N_("How many sectors of the encrypted data to skip at the beginning"), N_("SECTORS") }, + { "offset", 'o', POPT_ARG_STRING, NULL, 2, N_("The start offset in the backend device"), N_("SECTORS") }, + { "skip", 'p', POPT_ARG_STRING, NULL, 3, N_("How many sectors of the encrypted data to skip at the beginning"), N_("SECTORS") }, { "readonly", 'r', POPT_ARG_NONE, &opt_readonly, 0, N_("Create a readonly mapping"), NULL }, { "batch-mode", 'q', POPT_ARG_NONE, &opt_batch_mode, 0, N_("Do not ask for confirmation"), NULL }, { "timeout", 't', POPT_ARG_INT, &opt_timeout, 0, N_("Timeout for interactive passphrase prompt (in seconds)"), N_("secs") }, @@ -3606,25 +3679,29 @@ int main(int argc, const char **argv) while((r = poptGetNextOpt(popt_context)) > 0) { unsigned long long ull_value; - char *endp; + char *endp, *str = poptGetOptArg(popt_context); if (r == 6) { - const char *kf = poptGetOptArg(popt_context); - if (tools_is_stdin(kf)) - opt_keyfile_stdin = kf; - else if (opt_keyfiles_count < MAX_KEYFILES) - opt_keyfiles[opt_keyfiles_count++] = kf; + free(opt_key_file); + opt_key_file = str; + if (tools_is_stdin(str)) { + free(opt_keyfile_stdin); + opt_keyfile_stdin = strdup(str); + } else if (opt_keyfiles_count < MAX_KEYFILES) + opt_keyfiles[opt_keyfiles_count++] = strdup(str); total_keyfiles++; continue; } errno = 0; - ull_value = strtoull(popt_tmp, &endp, 0); - if (*endp || !*popt_tmp || !isdigit(*popt_tmp) || + ull_value = strtoull(str, &endp, 0); + if (*endp || !*str || !isdigit(*str) || (errno == ERANGE && ull_value == ULLONG_MAX) || (errno != 0 && ull_value == 0)) r = POPT_ERROR_BADNUMBER; + free(str); + switch(r) { case 1: opt_size = ull_value; @@ -3675,26 +3752,26 @@ int main(int argc, const char **argv) action_argv[1] = tmp; } aname = "open"; - opt_type = "plain"; + device_type = "plain"; } else if (!strcmp(aname, "plainOpen")) { aname = "open"; - opt_type = "plain"; + device_type = "plain"; } else if (!strcmp(aname, "luksOpen")) { aname = "open"; - opt_type = "luks"; + device_type = "luks"; } else if (!strcmp(aname, "loopaesOpen")) { aname = "open"; - opt_type = "loopaes"; + device_type = "loopaes"; } else if (!strcmp(aname, "tcryptOpen")) { aname = "open"; - opt_type = "tcrypt"; + device_type = "tcrypt"; } else if (!strcmp(aname, "bitlkOpen")) { aname = "open"; - opt_type = "bitlk"; + device_type = "bitlk"; } else if (!strcmp(aname, "tcryptDump")) { - opt_type = "tcrypt"; + device_type = "tcrypt"; } else if (!strcmp(aname, "bitlkDump")) { - opt_type = "bitlk"; + device_type = "bitlk"; } else if (!strcmp(aname, "remove") || !strcmp(aname, "plainClose") || !strcmp(aname, "luksClose") || @@ -3704,18 +3781,19 @@ int main(int argc, const char **argv) aname = "close"; } else if (!strcmp(aname, "luksErase")) { aname = "erase"; - opt_type = "luks"; + device_type = "luks"; } else if (!strcmp(aname, "luksConfig")) { aname = "config"; - opt_type = "luks2"; + device_type = "luks2"; } else if (!strcmp(aname, "refresh")) { aname = "open"; opt_refresh = 1; - } + } else if (opt_type) + device_type = opt_type; /* ignore user supplied type and query device type instead */ if (opt_refresh) - opt_type = NULL; + device_type = NULL; for(action = action_types; action->type; action++) if (strcmp(action->type, aname) == 0) @@ -3740,7 +3818,7 @@ int main(int argc, const char **argv) _("Option --deferred is allowed only for close command."), poptGetInvocationName(popt_context)); - if (opt_shared && (strcmp(aname, "open") || strcmp_or_null(opt_type, "plain"))) + if (opt_shared && (strcmp(aname, "open") || strcmp_or_null(device_type, "plain"))) usage(popt_context, EXIT_FAILURE, _("Option --shared is allowed only for open of plain device."), poptGetInvocationName(popt_context)); @@ -3792,8 +3870,8 @@ int main(int argc, const char **argv) _("Options --label and --subsystem are allowed only for luksFormat and config LUKS2 operations."), poptGetInvocationName(popt_context)); - if (opt_test_passphrase && (strcmp(aname, "open") || !opt_type || - (strncmp(opt_type, "luks", 4) && strcmp(opt_type, "tcrypt") && strcmp(opt_type, "bitlk")))) + if (opt_test_passphrase && (strcmp(aname, "open") || !device_type || + (strncmp(device_type, "luks", 4) && strcmp(device_type, "tcrypt") && strcmp(device_type, "bitlk")))) usage(popt_context, EXIT_FAILURE, _("Option --test-passphrase is allowed only for open of LUKS, TCRYPT and BITLK devices."), poptGetInvocationName(popt_context)); @@ -3815,7 +3893,7 @@ int main(int argc, const char **argv) if (opt_key_file) log_err(_("Option --key-file takes precedence over specified key file argument.")); else - opt_key_file = action_argv[1]; + opt_key_file = strdup(action_argv[1]); } if (opt_keyfile_size < 0 || opt_new_keyfile_size < 0 || opt_key_size < 0) @@ -3823,7 +3901,7 @@ int main(int argc, const char **argv) _("Negative number for option not permitted."), poptGetInvocationName(popt_context)); - if (total_keyfiles > 1 && (strcmp_or_null(opt_type, "tcrypt"))) + if (total_keyfiles > 1 && (strcmp_or_null(device_type, "tcrypt"))) usage(popt_context, EXIT_FAILURE, _("Only one --key-file argument is allowed."), poptGetInvocationName(popt_context)); @@ -3861,20 +3939,20 @@ int main(int argc, const char **argv) poptGetInvocationName(popt_context)); if (opt_skip && (strcmp(aname, "open") || - (strcmp_or_null(opt_type, "plain") && strcmp(opt_type, "loopaes")))) + (strcmp_or_null(device_type, "plain") && strcmp(device_type, "loopaes")))) usage(popt_context, EXIT_FAILURE, _("Option --skip is supported only for open of plain and loopaes devices."), poptGetInvocationName(popt_context)); if (opt_offset && ((strcmp(aname, "reencrypt") && strcmp(aname, "open") && strcmp(aname, "luksFormat")) || - (!strcmp(aname, "open") && strcmp_or_null(opt_type, "plain") && strcmp(opt_type, "loopaes")) || - (!strcmp(aname, "luksFormat") && opt_type && strncmp(opt_type, "luks", 4)))) + (!strcmp(aname, "open") && strcmp_or_null(device_type, "plain") && strcmp(device_type, "loopaes")) || + (!strcmp(aname, "luksFormat") && device_type && strncmp(device_type, "luks", 4)))) usage(popt_context, EXIT_FAILURE, _("Option --offset is supported only for open of plain and loopaes devices, luksFormat and device reencryption."), poptGetInvocationName(popt_context)); if ((opt_tcrypt_hidden || opt_tcrypt_system || opt_tcrypt_backup) && strcmp(aname, "tcryptDump") && - (strcmp(aname, "open") || !opt_type || strcmp(opt_type, "tcrypt"))) + (strcmp(aname, "open") || !device_type || strcmp(device_type, "tcrypt"))) usage(popt_context, EXIT_FAILURE, _("Option --tcrypt-hidden, --tcrypt-system or --tcrypt-backup is supported only for TCRYPT device."), poptGetInvocationName(popt_context)); @@ -3884,7 +3962,7 @@ int main(int argc, const char **argv) _("Option --tcrypt-hidden cannot be combined with --allow-discards."), poptGetInvocationName(popt_context)); - if (opt_veracrypt && (!opt_type || strcmp(opt_type, "tcrypt"))) + if (opt_veracrypt && (!device_type || strcmp(device_type, "tcrypt"))) usage(popt_context, EXIT_FAILURE, _("Option --veracrypt is supported only for TCRYPT device type."), poptGetInvocationName(popt_context)); @@ -3923,7 +4001,7 @@ int main(int argc, const char **argv) _("Keyslot specification is required."), poptGetInvocationName(popt_context)); - if (opt_pbkdf && crypt_parse_pbkdf(opt_pbkdf, &opt_pbkdf)) + if (opt_pbkdf && crypt_parse_pbkdf(opt_pbkdf, &set_pbkdf)) usage(popt_context, EXIT_FAILURE, _("Password-based key derivation function (PBKDF) can be only pbkdf2 or argon2i/argon2id."), poptGetInvocationName(popt_context)); @@ -3934,7 +4012,7 @@ int main(int argc, const char **argv) poptGetInvocationName(popt_context)); if (opt_sector_size && strcmp(aname, "reencrypt") && strcmp(aname, "luksFormat") && - (strcmp(aname, "open") || strcmp_or_null(opt_type, "plain"))) + (strcmp(aname, "open") || strcmp_or_null(device_type, "plain"))) usage(popt_context, EXIT_FAILURE, _("Sector size option is not supported for this command."), poptGetInvocationName(popt_context)); @@ -3980,6 +4058,7 @@ int main(int argc, const char **argv) if (opt_disable_locks && crypt_metadata_locking(NULL, 0)) { log_std(_("Cannot disable metadata locking.")); + tools_cleanup(); poptFreeContext(popt_context); exit(EXIT_FAILURE); } @@ -4037,6 +4116,7 @@ int main(int argc, const char **argv) poptGetInvocationName(popt_context)); r = run_action(action); + tools_cleanup(); poptFreeContext(popt_context); return r; } diff --git a/src/cryptsetup.h b/src/cryptsetup.h index 1afcf433..66446308 100644 --- a/src/cryptsetup.h +++ b/src/cryptsetup.h @@ -24,6 +24,7 @@ #ifndef CRYPTSETUP_H #define CRYPTSETUP_H +#include <stdbool.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> @@ -97,7 +98,6 @@ int tools_get_key(const char *prompt, void tools_passphrase_msg(int r); int tools_is_stdin(const char *key_file); int tools_string_to_size(struct crypt_device *cd, const char *s, uint64_t *size); -int tools_is_cipher_null(const char *cipher); void tools_clear_line(void); @@ -116,6 +116,11 @@ int tools_wipe_all_signatures(const char *path); int tools_lookup_crypt_device(struct crypt_device *cd, const char *type, const char *data_device_path, char *name, size_t name_length); +/* each utility is required to implement it */ +void tools_cleanup(void); + +#define FREE_AND_NULL(x) do { free(x); x = NULL; } while (0) + /* Log */ #define log_dbg(x...) clogger(NULL, CRYPT_LOG_DEBUG, __FILE__, __LINE__, x) #define log_std(x...) clogger(NULL, CRYPT_LOG_NORMAL, __FILE__, __LINE__, x) diff --git a/src/cryptsetup_reencrypt.c b/src/cryptsetup_reencrypt.c index a536093b..e5f9786c 100644 --- a/src/cryptsetup_reencrypt.c +++ b/src/cryptsetup_reencrypt.c @@ -29,16 +29,25 @@ #define NO_UUID "cafecafe-cafe-cafe-cafe-cafecafeeeee" -static const char *opt_cipher = NULL; -static const char *opt_hash = NULL; -static const char *opt_key_file = NULL; -static const char *opt_master_key_file = NULL; -static const char *opt_uuid = NULL; -static const char *opt_type = "luks"; +static char *opt_cipher = NULL; +static char *opt_hash = NULL; +static char *opt_key_file = NULL; +static char *opt_master_key_file = NULL; +static char *opt_uuid = NULL; +static char *opt_type = NULL; +static char *opt_pbkdf = NULL; +static char *opt_header_device = NULL; + +/* helper strings converted to uint64_t later */ +static char *opt_reduce_size_str = NULL; +static char *opt_device_size_str = NULL; + +static uint64_t opt_reduce_size = 0; +static uint64_t opt_device_size = 0; + static long opt_keyfile_size = 0; static long opt_keyfile_offset = 0; static int opt_iteration_time = 0; -static const char *opt_pbkdf = NULL; static long opt_pbkdf_memory = DEFAULT_LUKS2_MEMORY_KB; static long opt_pbkdf_parallel = DEFAULT_LUKS2_PARALLEL_THREADS; static long opt_pbkdf_iterations = 0; @@ -54,16 +63,11 @@ static int opt_key_size = 0; static int opt_new = 0; static int opt_keep_key = 0; static int opt_decrypt = 0; -static const char *opt_header_device = NULL; - -static const char *opt_reduce_size_str = NULL; -static uint64_t opt_reduce_size = 0; - -static const char *opt_device_size_str = NULL; -static uint64_t opt_device_size = 0; static const char **action_argv; +static const char *set_pbkdf = NULL; + #define MAX_SLOT 32 #define MAX_TOKEN 32 struct reenc_ctx { @@ -113,6 +117,20 @@ typedef enum { CHECK_OPEN, } header_magic; +void tools_cleanup(void) +{ + FREE_AND_NULL(opt_cipher); + FREE_AND_NULL(opt_hash); + FREE_AND_NULL(opt_key_file); + FREE_AND_NULL(opt_master_key_file); + FREE_AND_NULL(opt_uuid); + FREE_AND_NULL(opt_type); + FREE_AND_NULL(opt_pbkdf); + FREE_AND_NULL(opt_header_device); + FREE_AND_NULL(opt_reduce_size_str); + FREE_AND_NULL(opt_device_size_str); +} + static void _quiet_log(int level, const char *msg, void *usrptr) { if (!opt_debug) @@ -482,7 +500,7 @@ static int set_pbkdf_params(struct crypt_device *cd, const char *dev_type) if (!pbkdf_default) return -EINVAL; - pbkdf.type = opt_pbkdf ?: pbkdf_default->type; + pbkdf.type = set_pbkdf ?: pbkdf_default->type; pbkdf.hash = opt_hash ?: pbkdf_default->hash; pbkdf.time_ms = (uint32_t)opt_iteration_time ?: pbkdf_default->time_ms; if (strcmp(pbkdf.type, CRYPT_KDF_PBKDF2)) { @@ -1590,10 +1608,12 @@ static void help(poptContext popt_context, if (key->shortName == '?') { log_std("%s %s\n", PACKAGE_REENC, PACKAGE_VERSION); poptPrintHelp(popt_context, stdout, 0); + tools_cleanup(); poptFreeContext(popt_context); exit(EXIT_SUCCESS); } else if (key->shortName == 'V') { log_std("%s %s\n", PACKAGE_REENC, PACKAGE_VERSION); + tools_cleanup(); poptFreeContext(popt_context); exit(EXIT_SUCCESS); } else @@ -1688,7 +1708,7 @@ int main(int argc, const char **argv) poptGetInvocationName(popt_context)); } - if (opt_pbkdf && crypt_parse_pbkdf(opt_pbkdf, &opt_pbkdf)) + if (opt_pbkdf && crypt_parse_pbkdf(opt_pbkdf, &set_pbkdf)) usage(popt_context, EXIT_FAILURE, _("Password-based key derivation function (PBKDF) can be only pbkdf2 or argon2i/argon2id."), poptGetInvocationName(popt_context)); @@ -1764,8 +1784,7 @@ int main(int argc, const char **argv) } r = run_reencrypt(action_argv[0]); - + tools_cleanup(); poptFreeContext(popt_context); - return translate_errno(r); } diff --git a/src/integritysetup.c b/src/integritysetup.c index 51778da9..26d5a285 100644 --- a/src/integritysetup.c +++ b/src/integritysetup.c @@ -27,8 +27,19 @@ #define DEFAULT_ALG_NAME "crc32c" #define MAX_KEY_SIZE 4096 -static const char *opt_journal_size_str = NULL; +static char *opt_data_device = NULL; +static char *opt_integrity = NULL; /* DEFAULT_ALG_NAME */ +static char *opt_integrity_key_file = NULL; +static char *opt_journal_integrity = NULL; /* none */ +static char *opt_journal_integrity_key_file = NULL; +static char *opt_journal_crypt = NULL; /* none */ +static char *opt_journal_crypt_key_file = NULL; + +/* helper strings converted to uint64_t later */ +static char *opt_journal_size_str = NULL; + static uint64_t opt_journal_size = 0; + static int opt_interleave_sectors = 0; static int opt_journal_watermark = 0; static int opt_bitmap_sectors_per_bit = 0; @@ -37,34 +48,35 @@ static int opt_bitmap_flush_time = 0; static int opt_tag_size = 0; static int opt_sector_size = 0; static int opt_buffer_sectors = 0; - static int opt_no_wipe = 0; - -static const char *opt_data_device = NULL; - -static const char *opt_integrity = DEFAULT_ALG_NAME; -static const char *opt_integrity_key_file = NULL; static int opt_integrity_key_size = 0; - -static const char *opt_journal_integrity = NULL; /* none */ -static const char *opt_journal_integrity_key_file = NULL; static int opt_journal_integrity_key_size = 0; - -static const char *opt_journal_crypt = NULL; /* none */ -static const char *opt_journal_crypt_key_file = NULL; static int opt_journal_crypt_key_size = 0; - static int opt_integrity_nojournal = 0; static int opt_integrity_recovery = 0; static int opt_integrity_bitmap = 0; static int opt_integrity_legacy_padding = 0; - +static int opt_integrity_legacy_hmac = 0; +static int opt_integrity_legacy_recalculate = 0; static int opt_integrity_recalculate = 0; static int opt_allow_discards = 0; +static const char *integrity_alg = DEFAULT_ALG_NAME; static const char **action_argv; static int action_argc; +void tools_cleanup(void) +{ + FREE_AND_NULL(opt_data_device); + FREE_AND_NULL(opt_integrity); + FREE_AND_NULL(opt_integrity_key_file); + FREE_AND_NULL(opt_journal_integrity); + FREE_AND_NULL(opt_journal_integrity_key_file); + FREE_AND_NULL(opt_journal_crypt); + FREE_AND_NULL(opt_journal_crypt_key_file); + FREE_AND_NULL(opt_journal_size_str); +} + // FIXME: move this to tools and handle EINTR static int _read_mk(const char *file, char **key, int keysize) { @@ -189,14 +201,12 @@ static int action_format(int arg) int r; size_t signatures; - if (opt_integrity) { - r = crypt_parse_hash_integrity_mode(opt_integrity, integrity); + r = crypt_parse_hash_integrity_mode(integrity_alg, integrity); if (r < 0) { log_err(_("No known integrity specification pattern detected.")); return r; } params.integrity = integrity; - } if (opt_journal_integrity) { r = crypt_parse_hash_integrity_mode(opt_journal_integrity, journal_integrity); @@ -246,6 +256,9 @@ static int action_format(int arg) if (opt_integrity_legacy_padding) crypt_set_compatibility(cd, CRYPT_COMPAT_LEGACY_INTEGRITY_PADDING); + if (opt_integrity_legacy_hmac) + crypt_set_compatibility(cd, CRYPT_COMPAT_LEGACY_INTEGRITY_HMAC); + r = crypt_format(cd, CRYPT_INTEGRITY, NULL, NULL, NULL, NULL, 0, ¶ms); if (r < 0) /* FIXME: call wipe signatures again */ goto out; @@ -278,14 +291,12 @@ static int action_open(int arg) char *integrity_key = NULL; int r; - if (opt_integrity) { - r = crypt_parse_hash_integrity_mode(opt_integrity, integrity); + r = crypt_parse_hash_integrity_mode(integrity_alg, integrity); if (r < 0) { log_err(_("No known integrity specification pattern detected.")); return r; } params.integrity = integrity; - } if (opt_journal_integrity) { r = crypt_parse_hash_integrity_mode(opt_journal_integrity, journal_integrity); @@ -313,7 +324,7 @@ static int action_open(int arg) if (opt_integrity_bitmap) activate_flags |= CRYPT_ACTIVATE_NO_JOURNAL_BITMAP; - if (opt_integrity_recalculate) + if (opt_integrity_recalculate || opt_integrity_legacy_recalculate) activate_flags |= CRYPT_ACTIVATE_RECALCULATE; if (opt_allow_discards) activate_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; @@ -329,6 +340,9 @@ static int action_open(int arg) if (r) goto out; + if (opt_integrity_legacy_recalculate) + crypt_set_compatibility(cd, CRYPT_COMPAT_LEGACY_INTEGRITY_RECALC); + r = crypt_activate_by_volume_key(cd, action_argv[1], integrity_key, opt_integrity_key_size, activate_flags); out: @@ -506,10 +520,12 @@ static void help(poptContext popt_context, log_std(_("\nDefault compiled-in dm-integrity parameters:\n" "\tChecksum algorithm: %s\n"), DEFAULT_ALG_NAME); + tools_cleanup(); poptFreeContext(popt_context); exit(EXIT_SUCCESS); } else if (key->shortName == 'V') { log_std("%s %s\n", PACKAGE_INTEGRITY, PACKAGE_VERSION); + tools_cleanup(); poptFreeContext(popt_context); exit(EXIT_SUCCESS); } else @@ -576,6 +592,9 @@ int main(int argc, const char **argv) { "integrity-recalculate", '\0', POPT_ARG_NONE, &opt_integrity_recalculate, 0, N_("Recalculate initial tags automatically."), NULL }, { "integrity-legacy-padding", '\0', POPT_ARG_NONE, &opt_integrity_legacy_padding, 0, N_("Use inefficient legacy padding (old kernels)"), NULL }, + { "integrity-legacy-hmac", '\0', POPT_ARG_NONE, &opt_integrity_legacy_hmac, 0, N_("Do not protect superblock with HMAC (old kernels)"), NULL }, + { "integrity-legacy-recalculate",'\0',POPT_ARG_NONE, &opt_integrity_legacy_recalculate, 0, N_("Allow recalculating of volumes with HMAC keys (old kernels)"), NULL }, + { "allow-discards", '\0', POPT_ARG_NONE, &opt_allow_discards, 0, N_("Allow discards (aka TRIM) requests for device"), NULL }, POPT_TABLEEND }; @@ -629,6 +648,9 @@ int main(int argc, const char **argv) aname = "close"; } + if (opt_integrity) + integrity_alg = opt_integrity; + for (action = action_types; action->type; action++) if (strcmp(action->type, aname) == 0) break; @@ -679,9 +701,6 @@ int main(int argc, const char **argv) (!opt_integrity_key_file && opt_integrity_key_size)) usage(popt_context, EXIT_FAILURE, _("Both key file and key size options must be specified."), poptGetInvocationName(popt_context)); - if (!opt_integrity && opt_integrity_key_file) - usage(popt_context, EXIT_FAILURE, _("Integrity algorithm must be specified if integrity key is used."), - poptGetInvocationName(popt_context)); if ((opt_journal_integrity_key_file && !opt_journal_integrity_key_size) || (!opt_journal_integrity_key_file && opt_journal_integrity_key_size)) @@ -718,6 +737,7 @@ int main(int argc, const char **argv) } r = run_action(action); + tools_cleanup(); poptFreeContext(popt_context); return r; } diff --git a/src/utils_password.c b/src/utils_password.c index 55c1343f..dce73fa1 100644 --- a/src/utils_password.c +++ b/src/utils_password.c @@ -62,51 +62,86 @@ static int tools_check_pwquality(const char *password) #elif defined ENABLE_PASSWDQC #include <passwdqc.h> -static int tools_check_pwquality(const char *password) +static int tools_check_passwdqc(const char *password) { passwdqc_params_t params; - char *parse_reason; + char *parse_reason = NULL; const char *check_reason; const char *config = PASSWDQC_CONFIG_FILE; + int r = -EINVAL; passwdqc_params_reset(¶ms); if (*config && passwdqc_params_load(¶ms, &parse_reason, config)) { log_err(_("Cannot check password quality: %s"), (parse_reason ? parse_reason : "Out of memory")); - free(parse_reason); - return -EINVAL; + goto out; } check_reason = passwdqc_check(¶ms.qc, password, NULL, NULL); if (check_reason) { log_err(_("Password quality check failed: Bad passphrase (%s)"), check_reason); - return -EPERM; - } - - return 0; + r = -EPERM; + } else + r = 0; +out: +#if HAVE_PASSWDQC_PARAMS_FREE + passwdqc_params_free(¶ms); +#endif + free(parse_reason); + return r; } -#else /* !(ENABLE_PWQUALITY || ENABLE_PASSWDQC) */ -static int tools_check_pwquality(const char *password) +#endif /* ENABLE_PWQUALITY || ENABLE_PASSWDQC */ + +/* coverity[ +tainted_string_sanitize_content : arg-0 ] */ +static int tools_check_password(const char *password) { +#if defined ENABLE_PWQUALITY + return tools_check_pwquality(password); +#elif defined ENABLE_PASSWDQC + return tools_check_passwdqc(password); +#else return 0; +#endif } -#endif /* ENABLE_PWQUALITY || ENABLE_PASSWDQC */ /* Password reading helpers */ + +static ssize_t read_tty_eol(int fd, char *pass, size_t maxlen) +{ + bool eol = false; + size_t read_size = 0; + ssize_t r; + + do { + r = read(fd, pass, maxlen - read_size); + if ((r == -1 && errno != EINTR) || quit) + return -1; + if (r >= 0) { + if (!r || pass[r-1] == '\n') + eol = true; + read_size += (size_t)r; + pass = pass + r; + } + } while (!eol && read_size != maxlen); + + return (ssize_t)read_size; +} + +/* The pass buffer is zeroed and has trailing \0 already " */ static int untimed_read(int fd, char *pass, size_t maxlen) { ssize_t i; - i = read(fd, pass, maxlen); + i = read_tty_eol(fd, pass, maxlen); if (i > 0) { + if (pass[i-1] == '\n') pass[i-1] = '\0'; i = 0; - } else if (i == 0) { /* EOF */ - *pass = 0; + } else if (i == 0) /* empty input */ i = -1; - } + return i; } @@ -193,10 +228,9 @@ static int crypt_get_key_tty(const char *prompt, log_err(_("Error reading passphrase from terminal.")); goto out_err; } - pass[key_size_max] = '\0'; if (verify) { - pass_verify = crypt_safe_alloc(key_size_max); + pass_verify = crypt_safe_alloc(key_size_max + 1); if (!pass_verify) { log_err(_("Out of memory while reading passphrase.")); r = -ENOMEM; @@ -276,7 +310,7 @@ int tools_get_key(const char *prompt, /* Check pwquality for password (not keyfile) */ if (pwquality && !opt_force_password && !key_file && !r) - r = tools_check_pwquality(*key); + r = tools_check_password(*key); return r; } @@ -293,7 +327,7 @@ int tools_read_mk(const char *file, char **key, int keysize) { int fd; - if (!keysize || !key) + if (keysize <= 0 || !key) return -EINVAL; *key = crypt_safe_alloc(keysize); @@ -323,6 +357,9 @@ int tools_write_mk(const char *file, const char *key, int keysize) { int fd, r = -EINVAL; + if (keysize <= 0 || !key) + return -EINVAL; + fd = open(file, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR); if (fd < 0) { log_err(_("Cannot open keyfile %s for write."), file); diff --git a/src/utils_tools.c b/src/utils_tools.c index 47bcfe12..32ff0b73 100644 --- a/src/utils_tools.c +++ b/src/utils_tools.c @@ -238,6 +238,7 @@ __attribute__ ((noreturn)) void usage(poptContext popt_context, poptPrintUsage(popt_context, stderr, 0); if (error) log_err("%s: %s", more, error); + tools_cleanup(); poptFreeContext(popt_context); exit(exitcode); } @@ -596,14 +597,6 @@ out: return r; } -int tools_is_cipher_null(const char *cipher) -{ - if (!cipher) - return 0; - - return !strcmp(cipher, "cipher_null") ? 1 : 0; -} - /* * Keyfile - is standard input treated as a binary file (no EOL handling). */ diff --git a/src/veritysetup.c b/src/veritysetup.c index fcc12a6b..92b8c5ea 100644 --- a/src/veritysetup.c +++ b/src/veritysetup.c @@ -23,29 +23,38 @@ #define PACKAGE_VERITY "veritysetup" -static int use_superblock = 1; - -static const char *fec_device = NULL; -static int fec_roots = DEFAULT_VERITY_FEC_ROOTS; -static const char *hash_algorithm = NULL; -static int hash_type = 1; -static int data_block_size = DEFAULT_VERITY_DATA_BLOCK; -static int hash_block_size = DEFAULT_VERITY_HASH_BLOCK; +static char *opt_fec_device = NULL; +static char *opt_hash_algorithm = NULL; +static char *opt_salt = NULL; +static char *opt_uuid = NULL; +static char *opt_root_hash_signature = NULL; + +static int opt_use_superblock = 1; +static int opt_fec_roots = DEFAULT_VERITY_FEC_ROOTS; +static int opt_hash_type = 1; +static int opt_data_block_size = DEFAULT_VERITY_DATA_BLOCK; +static int opt_hash_block_size = DEFAULT_VERITY_HASH_BLOCK; static uint64_t data_blocks = 0; -static const char *salt_string = NULL; static uint64_t hash_offset = 0; static uint64_t fec_offset = 0; -static const char *opt_uuid = NULL; static int opt_restart_on_corruption = 0; static int opt_panic_on_corruption = 0; static int opt_ignore_corruption = 0; static int opt_ignore_zero_blocks = 0; static int opt_check_at_most_once = 0; -static const char *opt_root_hash_signature = NULL; static const char **action_argv; static int action_argc; +void tools_cleanup(void) +{ + FREE_AND_NULL(opt_fec_device); + FREE_AND_NULL(opt_hash_algorithm); + FREE_AND_NULL(opt_salt); + FREE_AND_NULL(opt_uuid); + FREE_AND_NULL(opt_root_hash_signature); +} + static int _prepare_format(struct crypt_params_verity *params, const char *data_device, uint32_t flags) @@ -53,16 +62,16 @@ static int _prepare_format(struct crypt_params_verity *params, char *salt = NULL; int len; - params->hash_name = hash_algorithm ?: DEFAULT_VERITY_HASH; + params->hash_name = opt_hash_algorithm ?: DEFAULT_VERITY_HASH; params->data_device = data_device; - params->fec_device = fec_device; - params->fec_roots = fec_roots; + params->fec_device = opt_fec_device; + params->fec_roots = opt_fec_roots; - if (salt_string && !strcmp(salt_string, "-")) { + if (opt_salt && !strcmp(opt_salt, "-")) { params->salt_size = 0; params->salt = NULL; - } else if (salt_string) { - len = crypt_hex_to_bytes(salt_string, &salt, 0); + } else if (opt_salt) { + len = crypt_hex_to_bytes(opt_salt, &salt, 0); if (len < 0) { log_err(_("Invalid salt string specified.")); return -EINVAL; @@ -74,12 +83,12 @@ static int _prepare_format(struct crypt_params_verity *params, params->salt = NULL; } - params->data_block_size = data_block_size; - params->hash_block_size = hash_block_size; + params->data_block_size = opt_data_block_size; + params->hash_block_size = opt_hash_block_size; params->data_size = data_blocks; params->hash_area_offset = hash_offset; params->fec_area_offset = fec_offset; - params->hash_type = hash_type; + params->hash_type = opt_hash_type; params->flags = flags; return 0; @@ -102,13 +111,13 @@ static int action_format(int arg) close(r); } /* Try to create FEC image if doesn't exist */ - if (fec_device) { - r = open(fec_device, O_WRONLY | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR); + if (opt_fec_device) { + r = open(opt_fec_device, O_WRONLY | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR); if (r < 0 && errno != EEXIST) { - log_err(_("Cannot create FEC image %s for writing."), fec_device); + log_err(_("Cannot create FEC image %s for writing."), opt_fec_device); return -EINVAL; } else if (r >= 0) { - log_dbg("Created FEC image %s.", fec_device); + log_dbg("Created FEC image %s.", opt_fec_device); close(r); } } @@ -116,7 +125,7 @@ static int action_format(int arg) if ((r = crypt_init(&cd, action_argv[1]))) goto out; - if (!use_superblock) + if (!opt_use_superblock) flags |= CRYPT_VERITY_NO_HEADER; r = _prepare_format(¶ms, action_argv[0], flags); @@ -161,12 +170,12 @@ static int _activate(const char *dm_device, if (opt_check_at_most_once) activate_flags |= CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE; - if (use_superblock) { + if (opt_use_superblock) { params.flags = flags; params.hash_area_offset = hash_offset; params.fec_area_offset = fec_offset; - params.fec_device = fec_device; - params.fec_roots = fec_roots; + params.fec_device = opt_fec_device; + params.fec_roots = opt_fec_roots; r = crypt_load(cd, CRYPT_VERITY, ¶ms); } else { r = _prepare_format(¶ms, data_device, flags | CRYPT_VERITY_NO_HEADER); @@ -329,7 +338,7 @@ static int action_status(int arg) if (vp.fec_device) { log_std(" FEC device: %s\n", vp.fec_device); - if ((backing_file = crypt_loop_backing_file(vp.fec_device))) { + if ((backing_file = crypt_loop_backing_file(opt_fec_device))) { log_std(" FEC loop: %s\n", backing_file); free(backing_file); } @@ -431,10 +440,12 @@ static void help(poptContext popt_context, DEFAULT_VERITY_HASH, DEFAULT_VERITY_DATA_BLOCK, DEFAULT_VERITY_HASH_BLOCK, DEFAULT_VERITY_SALT_SIZE, 1); + tools_cleanup(); poptFreeContext(popt_context); exit(EXIT_SUCCESS); } else if (key->shortName == 'V') { log_std("%s %s\n", PACKAGE_VERITY, PACKAGE_VERSION); + tools_cleanup(); poptFreeContext(popt_context); exit(EXIT_SUCCESS); } else @@ -455,7 +466,6 @@ static int run_action(struct action_type *action) int main(int argc, const char **argv) { - static char *popt_tmp; static const char *null_action_argv[] = {NULL}; static struct poptOption popt_help_options[] = { { NULL, '\0', POPT_ARG_CALLBACK, help, 0, NULL, NULL }, @@ -468,17 +478,17 @@ int main(int argc, const char **argv) { NULL, '\0', POPT_ARG_INCLUDE_TABLE, popt_help_options, 0, N_("Help options:"), NULL }, { "verbose", 'v', POPT_ARG_NONE, &opt_verbose, 0, N_("Shows more detailed error messages"), NULL }, { "debug", '\0', POPT_ARG_NONE, &opt_debug, 0, N_("Show debug messages"), NULL }, - { "no-superblock", 0, POPT_ARG_VAL, &use_superblock, 0, N_("Do not use verity superblock"), NULL }, - { "format", 0, POPT_ARG_INT, &hash_type, 0, N_("Format type (1 - normal, 0 - original Chrome OS)"), N_("number") }, - { "data-block-size", 0, POPT_ARG_INT, &data_block_size, 0, N_("Block size on the data device"), N_("bytes") }, - { "hash-block-size", 0, POPT_ARG_INT, &hash_block_size, 0, N_("Block size on the hash device"), N_("bytes") }, - { "fec-roots", 0, POPT_ARG_INT, &fec_roots, 0, N_("FEC parity bytes"), N_("bytes") }, - { "data-blocks", 0, POPT_ARG_STRING, &popt_tmp, 1, N_("The number of blocks in the data file"), N_("blocks") }, - { "fec-device", 0, POPT_ARG_STRING, &fec_device, 0, N_("Path to device with error correction data"), N_("path") }, - { "hash-offset", 0, POPT_ARG_STRING, &popt_tmp, 2, N_("Starting offset on the hash device"), N_("bytes") }, - { "fec-offset", 0, POPT_ARG_STRING, &popt_tmp, 3, N_("Starting offset on the FEC device"), N_("bytes") }, - { "hash", 'h', POPT_ARG_STRING, &hash_algorithm, 0, N_("Hash algorithm"), N_("string") }, - { "salt", 's', POPT_ARG_STRING, &salt_string, 0, N_("Salt"), N_("hex string") }, + { "no-superblock", 0, POPT_ARG_VAL, &opt_use_superblock, 0, N_("Do not use verity superblock"), NULL }, + { "format", 0, POPT_ARG_INT, &opt_hash_type, 0, N_("Format type (1 - normal, 0 - original Chrome OS)"), N_("number") }, + { "data-block-size", 0, POPT_ARG_INT, &opt_data_block_size, 0, N_("Block size on the data device"), N_("bytes") }, + { "hash-block-size", 0, POPT_ARG_INT, &opt_hash_block_size, 0, N_("Block size on the hash device"), N_("bytes") }, + { "fec-roots", 0, POPT_ARG_INT, &opt_fec_roots, 0, N_("FEC parity bytes"), N_("bytes") }, + { "data-blocks", 0, POPT_ARG_STRING, NULL, 1, N_("The number of blocks in the data file"), N_("blocks") }, + { "fec-device", 0, POPT_ARG_STRING, &opt_fec_device, 0, N_("Path to device with error correction data"), N_("path") }, + { "hash-offset", 0, POPT_ARG_STRING, NULL, 2, N_("Starting offset on the hash device"), N_("bytes") }, + { "fec-offset", 0, POPT_ARG_STRING, NULL, 3, N_("Starting offset on the FEC device"), N_("bytes") }, + { "hash", 'h', POPT_ARG_STRING, &opt_hash_algorithm, 0, N_("Hash algorithm"), N_("string") }, + { "salt", 's', POPT_ARG_STRING, &opt_salt, 0, N_("Salt"), N_("hex string") }, { "uuid", '\0', POPT_ARG_STRING, &opt_uuid, 0, N_("UUID for device to use"), NULL }, { "root-hash-signature",'\0', POPT_ARG_STRING, &opt_root_hash_signature, 0, N_("Path to root hash signature file"), NULL }, { "restart-on-corruption", 0,POPT_ARG_NONE,&opt_restart_on_corruption, 0, N_("Restart kernel if corruption is detected"), NULL }, @@ -506,15 +516,17 @@ int main(int argc, const char **argv) while((r = poptGetNextOpt(popt_context)) > 0) { unsigned long long ull_value; - char *endp; + char *endp, *str = poptGetOptArg(popt_context); errno = 0; - ull_value = strtoull(popt_tmp, &endp, 10); - if (*endp || !*popt_tmp || !isdigit(*popt_tmp) || + ull_value = strtoull(str, &endp, 10); + if (*endp || !*str || !isdigit(*str) || (errno == ERANGE && ull_value == ULLONG_MAX) || (errno != 0 && ull_value == 0)) r = POPT_ERROR_BADNUMBER; + free(str); + switch(r) { case 1: data_blocks = ull_value; @@ -577,7 +589,7 @@ int main(int argc, const char **argv) poptGetInvocationName(popt_context)); } - if (data_block_size < 0 || hash_block_size < 0 || hash_type < 0) { + if (opt_data_block_size < 0 || opt_hash_block_size < 0 || opt_hash_type < 0) { usage(popt_context, EXIT_FAILURE, _("Negative number for option not permitted."), poptGetInvocationName(popt_context)); @@ -610,6 +622,7 @@ int main(int argc, const char **argv) } r = run_action(action); + tools_cleanup(); poptFreeContext(popt_context); return r; }
Attachment:
signature.asc
Description: PGP signature