Compare commits

...

17 Commits

Author SHA1 Message Date
Ondrej Kozina
83a7310ca2 opal: do not initialize LRs array in activation.
The lr member in opal_lr_act kernel structure is
ingnored unless the device is being activated in SUM
mode.

See kernel implementation of IOC_OPAL_ACTIVATE_LSP
in block/sed-opal.c
2025-11-03 16:05:08 +01:00
Ondrej Kozina
441802773f opal: simplify User setup routine.
Reduce memory copying by reusing nested structure in opal_lock.
2025-11-03 16:05:08 +01:00
Ondrej Kozina
cc66b1fa52 opal: pull User setup in separate function. 2025-11-03 16:05:08 +01:00
Ondrej Kozina
a0d5d2bf5e opal: pull individual range setup in separate function. 2025-11-03 16:05:08 +01:00
Ondrej Kozina
61dbb69319 opal: pull reuse of active device in separate function. 2025-11-03 16:05:08 +01:00
Ondrej Kozina
32b33541a8 opal: pull LSP activation in separate function. 2025-11-03 16:05:08 +01:00
Ondrej Kozina
346db2e42a opal: add a named constant for TCG FAIL status.
Will be checked upon later when we add support
for OPAL2 SUM Reactivate method.
2025-11-03 16:05:08 +01:00
Milan Broz
0d07e80077 Fix typo in volume-key-file help.
Fixes: #966
2025-11-03 10:58:15 +01:00
Milan Broz
dc2251b88d man: Fix typo in integritysetup man page. 2025-10-31 08:31:21 +01:00
Ondrej Kozina
a8e8e39007 Fix possible use of uninitialized variable.
device_tag_size variable was not initialized and used
when device_is_nop_dif returned negative error code.
2025-10-30 13:59:54 +01:00
Kristina Hanicova
bcef385346 ci: Add Centos Stream 10 runner 2025-10-19 22:20:47 +02:00
Ondrej Kozina
9810c6fb2f Read integrity profile info from top level device.
When formating device with --integrity-inline option
there's a check if underlying device properly advertise
integrity profile support. The check did not work
properly for partition device nodes. We have to read
integrity profile info from top level block device.

Fixes: #964.
2025-10-17 15:25:32 +02:00
Ondrej Kozina
4d98add260 opal: Submit PSID reset command to R/W file descriptor.
The PSID reset erases the block device it's submitted to
succesfully.

By submitting the command to read-only fd previously
there were partition device nodes still visible in
the /dev directory because kernel does not trigger rescan
after OPAL2 PSID reset. Even though all the partition were
actually erased (including the partition table).

We workaround the issue by submitting the PSID reset
to R/W fd so that it triggers rescan event on close.
2025-10-06 10:37:37 +02:00
Milan Broz
0eaaa4553e Fix handling of too long label and subsystem fields
These LUKS2 labels are stored in the binary header area that has limited size.

While we have been silently truncating strings here, it is something that
is not expected, as the final label is then different than expected.

Let's fix the code to explicitly print and return error here.

Also remove the comment about duplicate check. It is incorrect  optimization,
as some users will expect a real write on disk, we should no skip it.

Fixes: #958
2025-10-01 21:41:55 +02:00
Ondrej Kozina
3a8feb8be7 Improve check for a function attribute support.
The compiler may advertise function attribute support
with __has_attribute operator even though it does
not implement the feature on some architecture.

This fixes the issue with  GCC 11 on ppc64le with
__attribute__((zero_call_used_regs("used"))).

Fixes: #959.
2025-09-11 14:18:39 +02:00
Kristina Hanicova
2b9523a1ef ci: Remove rhel runner 2025-08-29 15:21:26 +02:00
Maxim Suhanov
68d4749d8a bitlk: implement validation of FVE metadata
This commit implements FVE metadata block validation based on:
* CRC-32 (to detect random corruption);
* AES-CCM-encrypted SHA-256 (to detect malicious manipulations).

The hash-based validation requires us to decrypt the VMK first, so
it's only performed when obtaining the volume key.

This allows us to detect corrupted/altered FVE metadata blocks and
pick the valid one (before this commit: the first FVE metadata block
is always selected).

Fixes: #953

tests: add BitLocker image with corrupted headers

The image contains 2 manually corrupted metadata blocks (out of 3),
the library should use the third one to correctly load the volume.

Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
2025-08-29 15:16:36 +02:00
17 changed files with 524 additions and 345 deletions

View File

@@ -10,7 +10,6 @@ include:
- local: .gitlab/ci/debian.yml
- local: .gitlab/ci/fedora.yml
- local: .gitlab/ci/fedora-opal.yml
- local: .gitlab/ci/rhel.yml
- local: .gitlab/ci/centos.yml
# - local: .gitlab/ci/annocheck.yml
- local: .gitlab/ci/csmock.yml

View File

@@ -1,6 +1,4 @@
.centos-openssl-backend:
variables:
DISTRO: cryptsetup-centos-stream-9
extends:
- .fail_if_coredump_generated
before_script:
@@ -29,6 +27,7 @@ test-main-commit-centos-stream9:
stage: test
interruptible: true
variables:
DISTRO: cryptsetup-centos-stream-9
RUN_SSH_PLUGIN_TEST: "1"
RUN_KEYRING_TRUSTED_TEST: "1"
rules:
@@ -51,6 +50,53 @@ test-mergerq-centos-stream9:
stage: test
interruptible: true
variables:
DISTRO: cryptsetup-centos-stream-9
RUN_SSH_PLUGIN_TEST: "1"
RUN_KEYRING_TRUSTED_TEST: "1"
rules:
- if: $RUN_SYSTEMD_PLUGIN_TEST != null
when: never
- if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup"
when: never
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- make -j
- make -j -C tests check-programs
- sudo -E make check
test-main-commit-centos-stream10:
extends:
- .centos-openssl-backend
tags:
- libvirt
- cryptsetup-centos-stream-10
stage: test
interruptible: true
variables:
DISTRO: cryptsetup-centos-stream-10
RUN_SSH_PLUGIN_TEST: "1"
RUN_KEYRING_TRUSTED_TEST: "1"
rules:
- if: $RUN_SYSTEMD_PLUGIN_TEST != null
when: never
- if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup"
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/
script:
- make -j
- make -j -C tests check-programs
- sudo -E make check
test-mergerq-centos-stream10:
extends:
- .centos-openssl-backend
tags:
- libvirt
- cryptsetup-centos-stream-10
stage: test
interruptible: true
variables:
DISTRO: cryptsetup-centos-stream-10
RUN_SSH_PLUGIN_TEST: "1"
RUN_KEYRING_TRUSTED_TEST: "1"
rules:

View File

@@ -1,157 +0,0 @@
.rhel-openssl-backend:
extends:
- .fail_if_coredump_generated
before_script:
- >
sudo yum -y -q install
autoconf automake device-mapper-devel gcc gettext-devel json-c-devel
libblkid-devel libpwquality-devel libselinux-devel libssh-devel libtool
libuuid-devel make popt-devel libsepol-devel nc openssh-clients passwd
pkgconfig sharutils sshpass tar uuid-devel vim-common device-mapper
expect gettext git jq keyutils openssl-devel openssl gem
- sudo gem install asciidoctor
- sudo -E git clean -xdf
- ./autogen.sh
- ./configure --enable-fips --enable-pwquality --with-crypto_backend=openssl --enable-asciidoc
# non-FIPS jobs
test-main-commit-rhel8:
extends:
- .rhel-openssl-backend
tags:
- libvirt
- cryptsetup-rhel-8
stage: test
interruptible: true
variables:
DISTRO: cryptsetup-rhel-8
RUN_SSH_PLUGIN_TEST: "1"
rules:
- if: $RUN_SYSTEMD_PLUGIN_TEST != null
when: never
- if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup"
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/
script:
- make -j
- make -j -C tests check-programs
- sudo -E make check
test-main-commit-rhel9:
extends:
- .rhel-openssl-backend
tags:
- libvirt
- cryptsetup-rhel-9
stage: test
interruptible: true
variables:
DISTRO: cryptsetup-rhel-9
RUN_SSH_PLUGIN_TEST: "1"
rules:
- if: $RUN_SYSTEMD_PLUGIN_TEST != null
when: never
- if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup"
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/
script:
- make -j
- make -j -C tests check-programs
- sudo -E make check
test-main-commit-rhel10:
extends:
- .rhel-openssl-backend
tags:
- libvirt
- cryptsetup-rhel-10
stage: test
interruptible: true
allow_failure: true
variables:
DISTRO: cryptsetup-rhel-10
RUN_SSH_PLUGIN_TEST: "1"
rules:
- if: $RUN_SYSTEMD_PLUGIN_TEST != null
when: never
- if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup"
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/
script:
- make -j
- make -j -C tests check-programs
- sudo -E make check
# FIPS jobs
test-main-commit-rhel8-fips:
extends:
- .rhel-openssl-backend
tags:
- libvirt
- cryptsetup-rhel-8-fips
stage: test
interruptible: true
variables:
DISTRO: cryptsetup-rhel-8-fips
RUN_SSH_PLUGIN_TEST: "1"
rules:
- if: $RUN_SYSTEMD_PLUGIN_TEST != null
when: never
- if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup"
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/
script:
- grep -q fips=1 /proc/cmdline || exit 1
- make -j
- make -j -C tests check-programs
- sudo -E make check
test-main-commit-rhel9-fips:
extends:
- .rhel-openssl-backend
tags:
- libvirt
- cryptsetup-rhel-9-fips
stage: test
interruptible: true
allow_failure: true
variables:
DISTRO: cryptsetup-rhel-9-fips
RUN_SSH_PLUGIN_TEST: "1"
rules:
- if: $RUN_SYSTEMD_PLUGIN_TEST != null
when: never
- if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup"
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/
script:
- grep -q fips=1 /proc/cmdline || exit 1
- make -j
- make -j -C tests check-programs
- sudo -E make check
test-main-commit-rhel10-fips:
extends:
- .rhel-openssl-backend
tags:
- libvirt
- cryptsetup-rhel-10-fips
stage: test
interruptible: true
allow_failure: true
variables:
DISTRO: cryptsetup-rhel-10-fips
RUN_SSH_PLUGIN_TEST: "1"
rules:
- if: $RUN_SYSTEMD_PLUGIN_TEST != null
when: never
- if: $CI_PROJECT_PATH != "cryptsetup/cryptsetup"
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /v2\..\.x$/
script:
- grep -q fips=1 /proc/cmdline || exit 1
- make -j
- make -j -C tests check-programs
- sudo -E make check

View File

@@ -680,6 +680,27 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
])
CFLAGS=$saved_CFLAGS
dnl Force compiler to use zero_call_used_regs("used") to check for the function attribute support.
dnl Otherwise the compiler may falsely advertise it with __has_attribute operator, even though
dnl it does not implement it on some archs.
AC_MSG_CHECKING([for zero_call_used_regs(user)])
saved_CFLAGS=$CFLAGS
CFLAGS="-O0 -Werror"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
void _test_function(void);
__attribute__((zero_call_used_regs("used"))) void _test_function(void) {
volatile int *i; volatile int j = 0; if (j) *i = 0;
}
]],
[[ _test_function() ]]
)],[
AC_DEFINE([HAVE_ATTRIBUTE_ZEROCALLUSEDREGS], 1, [Define to 1 to use __attribute__((zero_call_used_regs("used")))])
AC_MSG_RESULT([yes])
], [
AC_MSG_RESULT([no])
])
CFLAGS=$saved_CFLAGS
AC_MSG_CHECKING([for systemd tmpfiles config directory])
if test "x$prefix" != "xNONE"; then
saved_PKG_CONFIG=$PKG_CONFIG

View File

@@ -111,6 +111,7 @@ struct bitlk_superblock {
struct bitlk_fve_metadata {
/* FVE metadata block header */
uint8_t signature[8];
/* size of this block (in 16-byte units) */
uint16_t fve_size;
uint16_t fve_version;
uint16_t curr_state;
@@ -132,6 +133,32 @@ struct bitlk_fve_metadata {
uint64_t creation_time;
} __attribute__ ((packed));
struct bitlk_validation_hash {
uint16_t size;
uint16_t role;
uint16_t type;
uint16_t flags;
/* likely a hash type code, anything other than 0x2005 isn't supported */
uint16_t hash_type;
uint16_t unknown1;
/* SHA-256 */
uint8_t hash[32];
} __attribute__ ((packed));
struct bitlk_fve_metadata_validation {
/* FVE metadata validation block header */
uint16_t validation_size;
uint16_t validation_version;
uint32_t fve_crc32;
/* this is a single nested structure's header defined here for simplicity */
uint16_t nested_struct_size;
uint16_t nested_struct_role;
uint16_t nested_struct_type;
uint16_t nested_struct_flags;
/* datum containing a similar nested structure (encrypted using VMK) with hash (SHA256) */
uint8_t nested_struct_data[BITLK_VALIDATION_VMK_DATA_SIZE];
} __attribute__ ((packed));
struct bitlk_entry_header_block {
uint64_t offset;
uint64_t size;
@@ -361,6 +388,54 @@ static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, in
return 0;
}
static bool check_fve_metadata(struct bitlk_fve_metadata *fve)
{
if (memcmp(fve->signature, BITLK_SIGNATURE, sizeof(fve->signature)) || le16_to_cpu(fve->fve_version) != 2 ||
(fve->fve_size << 4) > BITLK_FVE_METADATA_SIZE)
return false;
return true;
}
static bool check_fve_metadata_validation(struct bitlk_fve_metadata_validation *validation)
{
/* only check if there is room for CRC-32, the actual size must be larger */
if (le16_to_cpu(validation->validation_size) < 8 || le16_to_cpu(validation->validation_version > 2))
return false;
return true;
}
static bool parse_fve_metadata_validation(struct bitlk_metadata *params, struct bitlk_fve_metadata_validation *validation)
{
/* extra checks for a nested structure (MAC) and BITLK FVE metadata */
if (le16_to_cpu(validation->validation_size) < sizeof(struct bitlk_fve_metadata_validation))
return false;
if (le16_to_cpu(validation->nested_struct_size != BITLK_VALIDATION_VMK_HEADER_SIZE + BITLK_VALIDATION_VMK_DATA_SIZE) ||
le16_to_cpu(validation->nested_struct_role) != 0 ||
le16_to_cpu(validation->nested_struct_type) != 5)
return false;
/* nonce */
memcpy(params->validation->nonce,
validation->nested_struct_data,
BITLK_NONCE_SIZE);
/* MAC tag */
memcpy(params->validation->mac_tag,
validation->nested_struct_data + BITLK_NONCE_SIZE,
BITLK_VMK_MAC_TAG_SIZE);
/* AES-CCM encrypted datum with SHA256 hash */
memcpy(params->validation->enc_datum,
validation->nested_struct_data + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE,
BITLK_VALIDATION_VMK_DATA_SIZE - BITLK_NONCE_SIZE - BITLK_VMK_MAC_TAG_SIZE);
return true;
}
void BITLK_bitlk_fvek_free(struct bitlk_fvek *fvek)
{
if (!fvek)
@@ -391,6 +466,7 @@ void BITLK_bitlk_metadata_free(struct bitlk_metadata *metadata)
free(metadata->guid);
free(metadata->description);
free(metadata->validation);
BITLK_bitlk_vmk_free(metadata->vmks);
BITLK_bitlk_fvek_free(metadata->fvek);
}
@@ -402,20 +478,25 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
struct bitlk_signature sig = {};
struct bitlk_superblock sb = {};
struct bitlk_fve_metadata fve = {};
struct bitlk_fve_metadata_validation validation = {};
struct bitlk_entry_vmk entry_vmk = {};
uint8_t *fve_entries = NULL;
uint8_t *fve_validated_block = NULL;
size_t fve_entries_size = 0;
uint32_t fve_metadata_size = 0;
uint32_t fve_size_real = 0;
int fve_offset = 0;
char guid_buf[UUID_STR_LEN] = {0};
uint16_t entry_size = 0;
uint16_t entry_type = 0;
int i = 0;
int r = 0;
int valid_fve_metadata_idx = -1;
int start = 0;
size_t key_size = 0;
const char *key = NULL;
char *description = NULL;
struct crypt_hash *hash;
struct bitlk_vmk *vmk = NULL;
struct bitlk_vmk *vmk_p = params->vmks;
@@ -490,15 +571,80 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
for (i = 0; i < 3; i++)
params->metadata_offset[i] = le64_to_cpu(sb.fve_offset[i]);
log_dbg(cd, "Reading BITLK FVE metadata of size %zu on device %s, offset %" PRIu64 ".",
sizeof(fve), device_path(device), params->metadata_offset[0]);
fve_validated_block = malloc(BITLK_FVE_METADATA_SIZE);
if (fve_validated_block == NULL) {
r = -ENOMEM;
goto out;
}
/* read FVE metadata from the first metadata area */
if (read_lseek_blockwise(devfd, device_block_size(cd, device),
device_alignment(device), &fve, sizeof(fve), params->metadata_offset[0]) != sizeof(fve) ||
memcmp(fve.signature, BITLK_SIGNATURE, sizeof(fve.signature)) ||
le16_to_cpu(fve.fve_version) != 2) {
log_err(cd, _("Failed to read BITLK FVE metadata from %s."), device_path(device));
for (i = 0; i < 3; i++) {
/* iterate over FVE metadata copies and pick the valid one */
log_dbg(cd, "Reading BITLK FVE metadata copy #%d of size %zu on device %s, offset %" PRIu64 ".",
i, sizeof(fve), device_path(device), params->metadata_offset[i]);
if (read_lseek_blockwise(devfd, device_block_size(cd, device),
device_alignment(device), &fve, sizeof(fve), params->metadata_offset[i]) != sizeof(fve) ||
!check_fve_metadata(&fve) ||
(fve_size_real = le16_to_cpu(fve.fve_size) << 4, read_lseek_blockwise(devfd, device_block_size(cd, device),
device_alignment(device), &validation, sizeof(validation), params->metadata_offset[i] + fve_size_real) != sizeof(validation)) ||
!check_fve_metadata_validation(&validation) ||
/* double-fetch is here, but we aren't validating MAC */
read_lseek_blockwise(devfd, device_block_size(cd, device), device_alignment(device), fve_validated_block, fve_size_real,
params->metadata_offset[i]) != fve_size_real ||
(crypt_crc32(~0, fve_validated_block, fve_size_real) ^ ~0) != le32_to_cpu(validation.fve_crc32)) {
/* found an invalid FVE metadata copy, log and skip */
log_dbg(cd, _("Failed to read or validate BITLK FVE metadata copy #%d from %s."), i, device_path(device));
} else {
/* found a valid FVE metadata copy, use it */
valid_fve_metadata_idx = i;
break;
}
}
if (valid_fve_metadata_idx < 0) {
/* all FVE metadata copies are invalid, fail */
log_err(cd, _("Failed to read and validate BITLK FVE metadata from %s."), device_path(device));
r = -EINVAL;
goto out;
}
/* check that a valid FVE metadata block is in its expected location */
if (params->metadata_offset[valid_fve_metadata_idx] != le64_to_cpu(fve.fve_offset[valid_fve_metadata_idx])) {
log_err(cd, _("Failed to validate the location of BITLK FVE metadata from %s."), device_path(device));
r = -EINVAL;
goto out;
}
/* update offsets from a valid FVE metadata copy */
for (i = 0; i < 3; i++)
params->metadata_offset[i] = le64_to_cpu(fve.fve_offset[i]);
/* check that the FVE metadata hasn't changed between reads, because we are preparing for the MAC check */
if (memcmp(&fve, fve_validated_block, sizeof(fve)) != 0) {
log_err(cd, _("BITLK FVE metadata changed between reads from %s."), device_path(device));
r = -EINVAL;
goto out;
}
crypt_backend_memzero(&params->sha256_fve, 32);
if (crypt_hash_init(&hash, "sha256")) {
log_err(cd, _("Failed to hash BITLK FVE metadata read from %s."), device_path(device));
r = -EINVAL;
goto out;
}
crypt_hash_write(hash, (const char *)fve_validated_block, fve_size_real);
crypt_hash_final(hash, (char *)&params->sha256_fve, 32);
crypt_hash_destroy(hash);
/* do some extended checks against FVE metadata, but not including MAC verification */
params->validation = malloc(sizeof(struct bitlk_validation));
if (!params->validation) {
r = -ENOMEM;
goto out;
}
if (!parse_fve_metadata_validation(params, &validation)) {
log_err(cd, _("Failed to parse BITLK FVE validation metadata from %s."), device_path(device));
r = -EINVAL;
goto out;
}
@@ -583,17 +729,18 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
}
memset(fve_entries, 0, fve_entries_size);
log_dbg(cd, "Reading BITLK FVE metadata entries of size %zu on device %s, offset %" PRIu64 ".",
fve_entries_size, device_path(device), params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN);
log_dbg(cd, "Getting BITLK FVE metadata entries of size %zu on device %s, offset %" PRIu64 ".",
fve_entries_size, device_path(device), params->metadata_offset[valid_fve_metadata_idx] + BITLK_FVE_METADATA_HEADERS_LEN);
if (read_lseek_blockwise(devfd, device_block_size(cd, device),
device_alignment(device), fve_entries, fve_entries_size,
params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN) != (ssize_t)fve_entries_size) {
log_err(cd, _("Failed to read BITLK metadata entries from %s."), device_path(device));
if (BITLK_FVE_METADATA_HEADERS_LEN + fve_entries_size > fve_size_real) {
log_err(cd, _("Failed to check BITLK metadata entries previously read from %s."), device_path(device));
r = -EINVAL;
goto out;
}
/* fetch these entries from validated buffer to avoid double-fetch */
memcpy(fve_entries, fve_validated_block + BITLK_FVE_METADATA_HEADERS_LEN, fve_entries_size);
while ((fve_entries_size - start) >= (sizeof(entry_size) + sizeof(entry_type))) {
/* size of this entry */
@@ -716,6 +863,8 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
}
out:
free(fve_entries);
free(fve_validated_block);
return r;
}
@@ -1110,6 +1259,7 @@ int BITLK_get_volume_key(struct crypt_device *cd,
struct volume_key *open_vmk_key = NULL;
struct volume_key *vmk_dec_key = NULL;
struct volume_key *recovery_key = NULL;
struct bitlk_validation_hash dec_hash = {};
const struct bitlk_vmk *next_vmk = NULL;
next_vmk = params->vmks;
@@ -1172,6 +1322,36 @@ int BITLK_get_volume_key(struct crypt_device *cd,
}
crypt_free_volume_key(vmk_dec_key);
log_dbg(cd, "Trying to decrypt validation metadata using VMK.");
r = crypt_bitlk_decrypt_key(crypt_volume_key_get_key(open_vmk_key),
crypt_volume_key_length(open_vmk_key),
(const char*)params->validation->enc_datum,
(char *)&dec_hash,
BITLK_VALIDATION_VMK_DATA_SIZE - BITLK_NONCE_SIZE - BITLK_VMK_MAC_TAG_SIZE,
(const char*)params->validation->nonce, BITLK_NONCE_SIZE,
(const char*)params->validation->mac_tag, BITLK_VMK_MAC_TAG_SIZE);
if (r < 0) {
log_dbg(cd, "Failed to decrypt validation metadata using VMK.");
crypt_free_volume_key(open_vmk_key);
if (r == -ENOTSUP)
return r;
break;
}
/* now, do the MAC validation */
if (le16_to_cpu(dec_hash.role) != 0 ||le16_to_cpu(dec_hash.type) != 1 ||
(le16_to_cpu(dec_hash.hash_type) != 0x2005)) {
log_dbg(cd, "Failed to parse decrypted validation metadata.");
crypt_free_volume_key(open_vmk_key);
return -ENOTSUP;
}
if (memcmp(dec_hash.hash, params->sha256_fve, sizeof(dec_hash.hash)) != 0) {
log_dbg(cd, "Failed MAC validation of BITLK FVE metadata.");
crypt_free_volume_key(open_vmk_key);
return -EINVAL;
}
r = decrypt_key(cd, open_fvek_key, params->fvek->vk, open_vmk_key,
params->fvek->mac_tag, BITLK_VMK_MAC_TAG_SIZE,
params->fvek->nonce, BITLK_NONCE_SIZE, true);

View File

@@ -21,6 +21,8 @@ struct volume_key;
#define BITLK_NONCE_SIZE 12
#define BITLK_SALT_SIZE 16
#define BITLK_VMK_MAC_TAG_SIZE 16
#define BITLK_VALIDATION_VMK_HEADER_SIZE 8
#define BITLK_VALIDATION_VMK_DATA_SIZE 72
#define BITLK_STATE_NORMAL 0x0004
@@ -85,6 +87,13 @@ struct bitlk_fvek {
struct volume_key *vk;
};
struct bitlk_validation {
uint8_t mac_tag[BITLK_VMK_MAC_TAG_SIZE];
uint8_t nonce[BITLK_NONCE_SIZE];
/* technically, this is not "VMK", but some sources call it this way */
uint8_t enc_datum[BITLK_VALIDATION_VMK_DATA_SIZE];
};
struct bitlk_metadata {
uint16_t sector_size;
uint64_t volume_size;
@@ -101,8 +110,10 @@ struct bitlk_metadata {
uint32_t metadata_version;
uint64_t volume_header_offset;
uint64_t volume_header_size;
const char *sha256_fve[32];
struct bitlk_vmk *vmks;
struct bitlk_fvek *fvek;
struct bitlk_validation *validation;
};
int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params);

View File

@@ -9,11 +9,9 @@
#define ATTR_NOINLINE __attribute__ ((noinline))
#define ATTR_ZERO_REGS
#if defined __has_attribute
# if __has_attribute (zero_call_used_regs)
#if HAVE_ATTRIBUTE_ZEROCALLUSEDREGS
# undef ATTR_ZERO_REGS
# define ATTR_ZERO_REGS __attribute__ ((zero_call_used_regs("used")))
# endif
#endif
/* Workaround for https://github.com/google/sanitizers/issues/1507 */

View File

@@ -58,6 +58,12 @@ typedef enum OpalStatus {
_OPAL_STATUS_MAX = 0x13,
} OpalStatus;
/*
* Also defined in TCG Core spec Section 5.1.5 but
* do not inflate the opal_status_table below
*/
#define OPAL_STATUS_FAIL 0x3f
static const char* const opal_status_table[_OPAL_STATUS_MAX] = {
[OPAL_STATUS_SUCCESS] = "success",
[OPAL_STATUS_NOT_AUTHORIZED] = "not authorized",
@@ -85,9 +91,9 @@ static const char *opal_status_to_string(int t)
if (t < 0)
return strerror(-t);
/* Fail, as defined by specification */
if (t == 0x3f)
return "unknown failure";
/* This will be checked upon 'Reactivate' method */
if (t == OPAL_STATUS_FAIL)
return "FAIL status";
if (t >= _OPAL_STATUS_MAX)
return "unknown error";
@@ -396,6 +402,182 @@ static int opal_enabled(struct crypt_device *cd, struct device *dev)
return opal_query_status(cd, dev, OPAL_FL_LOCKING_ENABLED);
}
static int opal_activate_lsp(struct crypt_device *cd, int fd,
const void *admin_key, size_t admin_key_len)
{
int r;
struct opal_lr_act *activate = crypt_safe_alloc(sizeof(*activate));
if (!activate)
return -ENOMEM;
*activate = (struct opal_lr_act) {
.key = {
.key_len = admin_key_len,
},
/* useless but due to kernel bug it requires (num_lrs > 0 && num_lrs <= 9) */
.num_lrs = 1,
};
crypt_safe_memcpy(activate->key.key, admin_key, admin_key_len);
r = opal_ioctl(cd, fd, IOC_OPAL_TAKE_OWNERSHIP, &activate->key);
if (r < 0) {
r = -ENOTSUP;
log_dbg(cd, "OPAL not supported on this kernel version, refusing.");
goto out;
}
if (r == OPAL_STATUS_NOT_AUTHORIZED) /* We'll try again with a different key. */ {
r = -EPERM;
log_dbg(cd, "Failed to take ownership of OPAL device '%s': permission denied",
crypt_get_device_name(cd));
goto out;
}
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to take ownership of OPAL device '%s': %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
r = opal_ioctl(cd, fd, IOC_OPAL_ACTIVATE_LSP, activate);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to activate OPAL device '%s': %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
}
out:
crypt_safe_free(activate);
return r;
}
static int opal_reuse_active_lsp(struct crypt_device *cd, int fd,
uint32_t segment_number,
const void *admin_key, size_t admin_key_len)
{
int r;
struct opal_session_info *user_session = crypt_safe_alloc(sizeof(*user_session));
if (!user_session)
return -ENOMEM;
*user_session = (struct opal_session_info) {
.who = OPAL_ADMIN1, /* irrelevant in SUM */
.opal_key = {
.lr = segment_number,
.key_len = admin_key_len,
},
};
/* If it is already enabled, wipe the locking range first */
crypt_safe_memcpy(user_session->opal_key.key, admin_key, admin_key_len);
r = opal_ioctl(cd, fd, IOC_OPAL_SECURE_ERASE_LR, user_session);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to reset (secure erase) OPAL locking range %u on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
}
crypt_safe_free(user_session);
return r;
}
static int opal_setup_range(struct crypt_device *cd, int fd, uint32_t segment_number,
uint64_t range_start_blocks, uint64_t range_length_blocks,
const void *admin_key, size_t admin_key_len)
{
int r;
struct opal_user_lr_setup *setup = crypt_safe_alloc(sizeof(*setup));
if (!setup)
return -ENOMEM;
*setup = (struct opal_user_lr_setup) {
.range_start = range_start_blocks,
.range_length = range_length_blocks,
/* Some drives do not enable Locking Ranges on setup. This have some
* interesting consequences: Lock command called later below will pass,
* but locking range will _not_ be locked at all.
*/
.RLE = 1,
.WLE = 1,
.session = {
.who = OPAL_ADMIN1,
.opal_key = {
.key_len = admin_key_len,
.lr = segment_number,
},
},
};
crypt_safe_memcpy(setup->session.opal_key.key, admin_key, admin_key_len);
r = opal_ioctl(cd, fd, IOC_OPAL_LR_SETUP, setup);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to setup locking range of length %llu at offset %llu on OPAL device '%s': %s",
setup->range_length, setup->range_start, crypt_get_device_name(cd),
opal_status_to_string(r));
r = -EINVAL;
}
crypt_safe_free(setup);
return r;
}
static int opal_setup_user(struct crypt_device *cd, int fd, uint32_t segment_number,
const void *admin_key, size_t admin_key_len)
{
int r;
struct opal_lock_unlock *user_add_to_lr = crypt_safe_alloc(sizeof(*user_add_to_lr));
if (!user_add_to_lr)
return -ENOMEM;
*user_add_to_lr = (struct opal_lock_unlock) {
.session = {
.who = segment_number + 1,
.opal_key = {
.lr = segment_number,
.key_len = admin_key_len,
},
},
.l_state = OPAL_RO,
};
crypt_safe_memcpy(user_add_to_lr->session.opal_key.key, admin_key, admin_key_len);
r = opal_ioctl(cd, fd, IOC_OPAL_ACTIVATE_USR, &user_add_to_lr->session);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to activate OPAL user on device '%s': %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
r = opal_ioctl(cd, fd, IOC_OPAL_ADD_USR_TO_LR, user_add_to_lr);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to add OPAL user to locking range %u (RO) on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
user_add_to_lr->l_state = OPAL_RW;
r = opal_ioctl(cd, fd, IOC_OPAL_ADD_USR_TO_LR, user_add_to_lr);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to add OPAL user to locking range %u (RW) on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
}
out:
crypt_safe_free(user_add_to_lr);
return r;
}
/* requires opal lock */
int opal_setup_ranges(struct crypt_device *cd,
struct device *dev,
@@ -407,11 +589,8 @@ int opal_setup_ranges(struct crypt_device *cd,
const void *admin_key,
size_t admin_key_len)
{
struct opal_lr_act *activate = NULL;
struct opal_session_info *user_session = NULL;
struct opal_lock_unlock *user_add_to_lr = NULL, *lock = NULL;
struct opal_lock_unlock *lock = NULL;
struct opal_new_pw *new_pw = NULL;
struct opal_user_lr_setup *setup = NULL;
int r, fd;
assert(cd);
@@ -437,130 +616,16 @@ int opal_setup_ranges(struct crypt_device *cd,
return r;
/* If OPAL has never been enabled, we need to take ownership and do basic setup first */
if (r == 0) {
activate = crypt_safe_alloc(sizeof(struct opal_lr_act));
if (!activate) {
r = -ENOMEM;
goto out;
}
*activate = (struct opal_lr_act) {
.key = {
.key_len = admin_key_len,
},
.num_lrs = 8,
/* A max of 9 segments are supported, enable them all as there's no reason not to
* (0 is whole-volume)
*/
.lr = { 1, 2, 3, 4, 5, 6, 7, 8 },
};
crypt_safe_memcpy(activate->key.key, admin_key, admin_key_len);
r = opal_ioctl(cd, fd, IOC_OPAL_TAKE_OWNERSHIP, &activate->key);
if (r < 0) {
r = -ENOTSUP;
log_dbg(cd, "OPAL not supported on this kernel version, refusing.");
goto out;
}
if (r == OPAL_STATUS_NOT_AUTHORIZED) /* We'll try again with a different key. */ {
r = -EPERM;
log_dbg(cd, "Failed to take ownership of OPAL device '%s': permission denied",
crypt_get_device_name(cd));
goto out;
}
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to take ownership of OPAL device '%s': %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
r = opal_ioctl(cd, fd, IOC_OPAL_ACTIVATE_LSP, activate);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to activate OPAL device '%s': %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
} else {
/* If it is already enabled, wipe the locking range first */
user_session = crypt_safe_alloc(sizeof(struct opal_session_info));
if (!user_session) {
r = -ENOMEM;
goto out;
}
*user_session = (struct opal_session_info) {
.who = OPAL_ADMIN1,
.opal_key = {
.lr = segment_number,
.key_len = admin_key_len,
},
};
crypt_safe_memcpy(user_session->opal_key.key, admin_key, admin_key_len);
r = opal_ioctl(cd, fd, IOC_OPAL_SECURE_ERASE_LR, user_session);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to reset (secure erase) OPAL locking range %u on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
}
crypt_safe_free(user_session);
user_session = crypt_safe_alloc(sizeof(struct opal_session_info));
if (!user_session) {
r = -ENOMEM;
if (r == 0)
r = opal_activate_lsp(cd, fd, admin_key, admin_key_len);
else
r = opal_reuse_active_lsp(cd, fd, segment_number, admin_key, admin_key_len);
if (r < 0)
goto out;
}
*user_session = (struct opal_session_info) {
.who = segment_number + 1,
.opal_key = {
.key_len = admin_key_len,
},
};
crypt_safe_memcpy(user_session->opal_key.key, admin_key, admin_key_len);
r = opal_ioctl(cd, fd, IOC_OPAL_ACTIVATE_USR, user_session);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to activate OPAL user on device '%s': %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
r = opal_setup_user(cd, fd, segment_number, admin_key, admin_key_len);
if (r < 0)
goto out;
}
user_add_to_lr = crypt_safe_alloc(sizeof(struct opal_lock_unlock));
if (!user_add_to_lr) {
r = -ENOMEM;
goto out;
}
*user_add_to_lr = (struct opal_lock_unlock) {
.session = {
.who = segment_number + 1,
.opal_key = {
.lr = segment_number,
.key_len = admin_key_len,
},
},
.l_state = OPAL_RO,
};
crypt_safe_memcpy(user_add_to_lr->session.opal_key.key, admin_key, admin_key_len);
r = opal_ioctl(cd, fd, IOC_OPAL_ADD_USR_TO_LR, user_add_to_lr);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to add OPAL user to locking range %u (RO) on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
user_add_to_lr->l_state = OPAL_RW;
r = opal_ioctl(cd, fd, IOC_OPAL_ADD_USR_TO_LR, user_add_to_lr);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to add OPAL user to locking range %u (RW) on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
new_pw = crypt_safe_alloc(sizeof(struct opal_new_pw));
if (!new_pw) {
@@ -595,37 +660,10 @@ int opal_setup_ranges(struct crypt_device *cd,
goto out;
}
setup = crypt_safe_alloc(sizeof(struct opal_user_lr_setup));
if (!setup) {
r = -ENOMEM;
r = opal_setup_range(cd, fd, segment_number, range_start_blocks, range_length_blocks,
admin_key, admin_key_len);
if (r < 0)
goto out;
}
*setup = (struct opal_user_lr_setup) {
.range_start = range_start_blocks,
.range_length = range_length_blocks,
/* Some drives do not enable Locking Ranges on setup. This have some
* interesting consequences: Lock command called later below will pass,
* but locking range will _not_ be locked at all.
*/
.RLE = 1,
.WLE = 1,
.session = {
.who = OPAL_ADMIN1,
.opal_key = {
.key_len = admin_key_len,
.lr = segment_number,
},
},
};
crypt_safe_memcpy(setup->session.opal_key.key, admin_key, admin_key_len);
r = opal_ioctl(cd, fd, IOC_OPAL_LR_SETUP, setup);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to setup locking range of length %llu at offset %llu on OPAL device '%s': %s",
setup->range_length, setup->range_start, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
/* After setup an OPAL device is unlocked, but the expectation with cryptsetup is that it needs
* to be activated separately, so lock it immediately. */
@@ -661,11 +699,7 @@ int opal_setup_ranges(struct crypt_device *cd,
&(uint64_t) {range_length_blocks * opal_block_bytes / SECTOR_SIZE},
&(bool) {true}, &(bool){true}, NULL, NULL);
out:
crypt_safe_free(activate);
crypt_safe_free(user_session);
crypt_safe_free(user_add_to_lr);
crypt_safe_free(new_pw);
crypt_safe_free(setup);
crypt_safe_free(lock);
return r;
@@ -790,7 +824,11 @@ int opal_factory_reset(struct crypt_device *cd,
if (password_len > OPAL_KEY_MAX)
return -EINVAL;
fd = device_open(cd, dev, O_RDONLY);
/*
* Submit PSID reset on R/W file descriptor so it
* triggers blkid rescan after we close it.
*/
fd = device_open(cd, dev, O_RDWR);
if (fd < 0)
return -EIO;

View File

@@ -1272,7 +1272,11 @@ int LUKS2_hdr_uuid(struct crypt_device *cd, struct luks2_hdr *hdr, const char *u
int LUKS2_hdr_labels(struct crypt_device *cd, struct luks2_hdr *hdr,
const char *label, const char *subsystem, int commit)
{
//FIXME: check if the labels are the same and skip this.
if ((label && strlen(label) >= LUKS2_LABEL_L) ||
(subsystem && strlen(subsystem) >= LUKS2_LABEL_L)) {
log_err(cd, _("Label is too long."));
return -EINVAL;
}
memset(hdr->label, 0, LUKS2_LABEL_L);
if (label)

View File

@@ -3045,7 +3045,11 @@ int crypt_format_inline(struct crypt_device *cd,
iparams->journal_integrity_key_size))
return -EINVAL;
if (!device_is_nop_dif(idevice, &device_tag_size)) {
r = device_is_nop_dif(idevice, &device_tag_size);
if (r < 0)
return r;
if (!r) {
log_err(cd, _("Device %s does not provide inline integrity data fields."), mdata_device_path(cd));
return -EINVAL;
}

View File

@@ -1004,12 +1004,26 @@ int device_is_zoned(struct device *device)
int device_is_nop_dif(struct device *device, uint32_t *tag_size)
{
char *base_device_path;
int r;
struct stat st;
if (!device)
return -EINVAL;
if (stat(device_path(device), &st) < 0)
/*
* For partition devices, check integrity profile on the base device.
* Partition device nodes don't advertise integrity profile directly
* via sysfs attributes.
*/
base_device_path = crypt_get_base_device(device_path(device));
if (base_device_path) {
r = stat(base_device_path, &st);
free(base_device_path);
} else
r = stat(device_path(device), &st);
if (r < 0)
return -EINVAL;
if (!S_ISBLK(st.st_mode))

View File

@@ -201,7 +201,7 @@ The file with the integrity key.
The size of the journal integrity key.
Maximum is 4096 bytes.
*--journal-size*, *-j* _butes_::
*--journal-size*, *-j* _bytes_::
Size of the journal.
*--journal-watermark* _percent_::

View File

@@ -697,6 +697,21 @@ if cc.links(
description: 'Define to 1 to use __attribute__((symver))')
endif
# ==========================================================================
# Check compiler support for zero_called_used_regs("used") function attribute
if cc.links(
'''void _test_fn(void);
__attribute__((zero_call_used_regs("used"))) void _test_fn(void) {
volatile int *i; volatile int j = 0; if (j) *i = 0;
}
int main(void) { _test_fn(); return 0; }''',
args: ['-O0', '-Werror' ],
name: 'for zero_call_used_regs("used") attribute support')
conf.set10('HAVE_ATTRIBUTE_ZEROCALLUSEDREGS', true,
description: 'Define to 1 to use __attribute__((zero_call_used_regs("used")))')
endif
# ==========================================================================
if get_option('dev-random')

View File

@@ -246,4 +246,4 @@ ARG(OPT_WRITE_LOG, '\0', POPT_ARG_NONE, N_("Update log file after every block"),
ARG(OPT_DUMP_MASTER_KEY, '\0', POPT_ARG_NONE, N_("Alias for --dump-volume-key"), NULL, CRYPT_ARG_ALIAS, { .o.id = OPT_DUMP_VOLUME_KEY_ID}, {})
ARG(OPT_MASTER_KEY_FILE, '\0', POPT_ARG_STRING, N_("Alias for --dump-volume-key-file"), NULL, CRYPT_ARG_ALIAS, { .o.id = OPT_VOLUME_KEY_FILE_ID}, {})
ARG(OPT_MASTER_KEY_FILE, '\0', POPT_ARG_STRING, N_("Alias for --volume-key-file"), NULL, CRYPT_ARG_ALIAS, { .o.id = OPT_VOLUME_KEY_FILE_ID}, {})

View File

@@ -4059,6 +4059,7 @@ static void Luks2Refresh(void)
static void Luks2Flags(void)
{
uint32_t flags = 42;
const char *longlabel = "0123456789abcedf0123456789abcedf0123456789abcedf";
OK_(crypt_init(&cd, DEVICE_1));
OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
@@ -4089,6 +4090,9 @@ static void Luks2Flags(void)
OK_(strcmp("", crypt_get_label(cd)));
OK_(strcmp("", crypt_get_subsystem(cd)));
FAIL_(crypt_set_label(cd, longlabel, NULL), "long label");
FAIL_(crypt_set_label(cd, NULL, longlabel), "long subsystem");
CRYPT_FREE(cd);
}

Binary file not shown.

View File

@@ -1278,6 +1278,8 @@ $CRYPTSETUP luksDump $LOOPDEV | grep "Label:" | grep -q "(no label)" || fail
$CRYPTSETUP config $LOOPDEV --subsystem SatelliteThree --label TheLabel
$CRYPTSETUP luksDump $LOOPDEV | grep "Subsystem:" | grep -q "SatelliteThree" || fail
$CRYPTSETUP luksDump $LOOPDEV | grep "Label:" | grep -q "TheLabel" || fail
$CRYPTSETUP config $LOOPDEV --label 0123456789abcdef0123456789abcdef0123456789abcdef 2>/dev/null && fail
$CRYPTSETUP config $LOOPDEV --subsystem 0123456789abcdef0123456789abcdef0123456789abcdef 2>/dev/null && fail
prepare "[36] LUKS PBKDF setting" wipe
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --pbkdf bla $LOOPDEV >/dev/null 2>&1 && fail