summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/Makemodule.am2
-rw-r--r--lib/bitlk/bitlk.c17
-rw-r--r--lib/bitlk/bitlk.h6
-rw-r--r--lib/crypt_plain.c4
-rw-r--r--lib/crypto_backend/argon2/meson.build28
-rw-r--r--lib/crypto_backend/argon2_generic.c39
-rw-r--r--lib/crypto_backend/base64.c2
-rw-r--r--lib/crypto_backend/cipher_check.c4
-rw-r--r--lib/crypto_backend/cipher_generic.c5
-rw-r--r--lib/crypto_backend/crc32.c4
-rw-r--r--lib/crypto_backend/crypto_backend.h6
-rw-r--r--lib/crypto_backend/crypto_backend_internal.h4
-rw-r--r--lib/crypto_backend/crypto_cipher_kernel.c7
-rw-r--r--lib/crypto_backend/crypto_gcrypt.c150
-rw-r--r--lib/crypto_backend/crypto_kernel.c6
-rw-r--r--lib/crypto_backend/crypto_nettle.c4
-rw-r--r--lib/crypto_backend/crypto_nss.c4
-rw-r--r--lib/crypto_backend/crypto_openssl.c90
-rw-r--r--lib/crypto_backend/crypto_storage.c2
-rw-r--r--lib/crypto_backend/meson.build40
-rw-r--r--lib/crypto_backend/pbkdf2_generic.c4
-rw-r--r--lib/crypto_backend/pbkdf_check.c4
-rw-r--r--lib/crypto_backend/utf8.c2
-rw-r--r--lib/integrity/integrity.c121
-rw-r--r--lib/integrity/integrity.h5
-rw-r--r--lib/internal.h31
-rw-r--r--lib/keyslot_context.c364
-rw-r--r--lib/keyslot_context.h52
-rw-r--r--lib/libcryptsetup.h288
-rw-r--r--lib/libcryptsetup.sym15
-rw-r--r--lib/libcryptsetup_macros.h4
-rw-r--r--lib/libcryptsetup_symver.h2
-rw-r--r--lib/libdevmapper.c39
-rw-r--r--lib/loopaes/loopaes.c4
-rw-r--r--lib/loopaes/loopaes.h4
-rw-r--r--lib/luks1/af.c2
-rw-r--r--lib/luks1/af.h2
-rw-r--r--lib/luks1/keyencryption.c4
-rw-r--r--lib/luks1/keymanage.c4
-rw-r--r--lib/luks1/luks.h2
-rw-r--r--lib/luks2/hw_opal/hw_opal.c1089
-rw-r--r--lib/luks2/hw_opal/hw_opal.h71
-rw-r--r--lib/luks2/luks2.h46
-rw-r--r--lib/luks2/luks2_digest.c18
-rw-r--r--lib/luks2/luks2_digest_pbkdf2.c16
-rw-r--r--lib/luks2/luks2_disk_metadata.c25
-rw-r--r--lib/luks2/luks2_internal.h37
-rw-r--r--lib/luks2/luks2_json_format.c227
-rw-r--r--lib/luks2/luks2_json_metadata.c303
-rw-r--r--lib/luks2/luks2_keyslot.c35
-rw-r--r--lib/luks2/luks2_keyslot_luks2.c40
-rw-r--r--lib/luks2/luks2_keyslot_reenc.c21
-rw-r--r--lib/luks2/luks2_luks1_convert.c43
-rw-r--r--lib/luks2/luks2_reencrypt.c428
-rw-r--r--lib/luks2/luks2_reencrypt_digest.c22
-rw-r--r--lib/luks2/luks2_segment.c244
-rw-r--r--lib/luks2/luks2_token.c295
-rw-r--r--lib/luks2/luks2_token_keyring.c13
-rw-r--r--lib/meson.build116
-rw-r--r--lib/random.c2
-rw-r--r--lib/setup.c2613
-rw-r--r--lib/tcrypt/tcrypt.c8
-rw-r--r--lib/tcrypt/tcrypt.h4
-rw-r--r--lib/utils.c69
-rw-r--r--lib/utils_benchmark.c13
-rw-r--r--lib/utils_blkid.c170
-rw-r--r--lib/utils_blkid.h2
-rw-r--r--lib/utils_crypt.c21
-rw-r--r--lib/utils_crypt.h7
-rw-r--r--lib/utils_device.c32
-rw-r--r--lib/utils_device_locking.c30
-rw-r--r--lib/utils_device_locking.h5
-rw-r--r--lib/utils_devpath.c33
-rw-r--r--lib/utils_dm.h7
-rw-r--r--lib/utils_io.c4
-rw-r--r--lib/utils_io.h4
-rw-r--r--lib/utils_keyring.c433
-rw-r--r--lib/utils_keyring.h39
-rw-r--r--lib/utils_loop.c6
-rw-r--r--lib/utils_loop.h4
-rw-r--r--lib/utils_pbkdf.c32
-rw-r--r--lib/utils_safe_memory.c4
-rw-r--r--lib/utils_storage_wrappers.c2
-rw-r--r--lib/utils_storage_wrappers.h2
-rw-r--r--lib/utils_wipe.c76
-rw-r--r--lib/verity/rs.h2
-rw-r--r--lib/verity/rs_decode_char.c2
-rw-r--r--lib/verity/rs_encode_char.c2
-rw-r--r--lib/verity/verity.c140
-rw-r--r--lib/verity/verity.h15
-rw-r--r--lib/verity/verity_fec.c2
-rw-r--r--lib/verity/verity_hash.c2
-rw-r--r--lib/volumekey.c4
93 files changed, 6769 insertions, 1488 deletions
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index 2e60a90..ae5fab9 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -103,6 +103,8 @@ libcryptsetup_la_SOURCES = \
lib/luks2/luks2_token.c \
lib/luks2/luks2_internal.h \
lib/luks2/luks2.h \
+ lib/luks2/hw_opal/hw_opal.c \
+ lib/luks2/hw_opal/hw_opal.h \
lib/utils_blkid.c \
lib/utils_blkid.h \
lib/bitlk/bitlk.h \
diff --git a/lib/bitlk/bitlk.c b/lib/bitlk/bitlk.c
index de7bcea..ae533e5 100644
--- a/lib/bitlk/bitlk.c
+++ b/lib/bitlk/bitlk.c
@@ -1,9 +1,9 @@
/*
* BITLK (BitLocker-compatible) volume handling
*
- * Copyright (C) 2019-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2019-2023 Milan Broz
- * Copyright (C) 2019-2023 Vojtech Trefny
+ * Copyright (C) 2019-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2019-2024 Milan Broz
+ * Copyright (C) 2019-2024 Vojtech Trefny
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -735,6 +735,7 @@ int BITLK_dump(struct crypt_device *cd, struct device *device, struct bitlk_meta
{
struct volume_key *vk_p;
struct bitlk_vmk *vmk_p;
+ char time[32];
int next_id = 0;
int i = 0;
@@ -743,7 +744,8 @@ int BITLK_dump(struct crypt_device *cd, struct device *device, struct bitlk_meta
log_std(cd, "GUID: \t%s\n", params->guid);
log_std(cd, "Sector size: \t%u [bytes]\n", params->sector_size);
log_std(cd, "Volume size: \t%" PRIu64 " [bytes]\n", params->volume_size);
- log_std(cd, "Created: \t%s", ctime((time_t *)&(params->creation_time)));
+ if (ctime_r((time_t *)&params->creation_time, time))
+ log_std(cd, "Created: \t%s", time);
log_std(cd, "Description: \t%s\n", params->description);
log_std(cd, "Cipher name: \t%s\n", params->cipher);
log_std(cd, "Cipher mode: \t%s\n", params->cipher_mode);
@@ -982,8 +984,7 @@ static int get_startup_key(struct crypt_device *cd,
}
}
-static int bitlk_kdf(struct crypt_device *cd,
- const char *password,
+static int bitlk_kdf(const char *password,
size_t passwordLen,
bool recovery,
const uint8_t *salt,
@@ -1120,7 +1121,7 @@ int BITLK_get_volume_key(struct crypt_device *cd,
next_vmk = params->vmks;
while (next_vmk) {
if (next_vmk->protection == BITLK_PROTECTION_PASSPHRASE) {
- r = bitlk_kdf(cd, password, passwordLen, false, next_vmk->salt, &vmk_dec_key);
+ r = bitlk_kdf(password, passwordLen, false, next_vmk->salt, &vmk_dec_key);
if (r) {
/* something wrong happened, but we still want to check other key slots */
next_vmk = next_vmk->next;
@@ -1140,7 +1141,7 @@ int BITLK_get_volume_key(struct crypt_device *cd,
continue;
}
log_dbg(cd, "Trying to use given password as a recovery key.");
- r = bitlk_kdf(cd, recovery_key->key, recovery_key->keylength,
+ r = bitlk_kdf(recovery_key->key, recovery_key->keylength,
true, next_vmk->salt, &vmk_dec_key);
crypt_free_volume_key(recovery_key);
if (r)
diff --git a/lib/bitlk/bitlk.h b/lib/bitlk/bitlk.h
index 54d3dc7..7eb7321 100644
--- a/lib/bitlk/bitlk.h
+++ b/lib/bitlk/bitlk.h
@@ -1,9 +1,9 @@
/*
* BITLK (BitLocker-compatible) header definition
*
- * Copyright (C) 2019-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2019-2023 Milan Broz
- * Copyright (C) 2019-2023 Vojtech Trefny
+ * Copyright (C) 2019-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2019-2024 Milan Broz
+ * Copyright (C) 2019-2024 Vojtech Trefny
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/crypt_plain.c b/lib/crypt_plain.c
index c839b09..99155e8 100644
--- a/lib/crypt_plain.c
+++ b/lib/crypt_plain.c
@@ -2,8 +2,8 @@
* cryptsetup plain device helper functions
*
* Copyright (C) 2004 Jana Saout <jana@saout.de>
- * Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2010-2023 Milan Broz
+ * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/crypto_backend/argon2/meson.build b/lib/crypto_backend/argon2/meson.build
new file mode 100644
index 0000000..bb68516
--- /dev/null
+++ b/lib/crypto_backend/argon2/meson.build
@@ -0,0 +1,28 @@
+libargon2_sources = files(
+ 'blake2/blake2b.c',
+ 'argon2.c',
+ 'core.c',
+ 'encoding.c',
+ 'thread.c',
+)
+
+if use_internal_sse_argon2
+ libargon2_sources += files(
+ 'opt.c',
+ )
+else
+ libargon2_sources += files(
+ 'ref.c',
+ )
+endif
+
+libargon2 = static_library('argon2',
+ libargon2_sources,
+ override_options : ['c_std=c89', 'optimization=3'],
+ build_by_default : false,
+ include_directories: include_directories(
+ 'blake2',
+ ),
+ dependencies : [
+ threads,
+ ])
diff --git a/lib/crypto_backend/argon2_generic.c b/lib/crypto_backend/argon2_generic.c
index 0ce67da..eca575b 100644
--- a/lib/crypto_backend/argon2_generic.c
+++ b/lib/crypto_backend/argon2_generic.c
@@ -1,8 +1,8 @@
/*
* Argon2 PBKDF2 library wrapper
*
- * Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2016-2023 Milan Broz
+ * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2016-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -29,14 +29,12 @@
#define CONST_CAST(x) (x)(uintptr_t)
+#if USE_INTERNAL_ARGON2 || HAVE_ARGON2_H
int argon2(const char *type, const char *password, size_t password_length,
const char *salt, size_t salt_length,
char *key, size_t key_length,
uint32_t iterations, uint32_t memory, uint32_t parallel)
{
-#if !USE_INTERNAL_ARGON2 && !HAVE_ARGON2_H
- return -EINVAL;
-#else
argon2_type atype;
argon2_context context = {
.flags = ARGON2_DEFAULT_FLAGS,
@@ -54,6 +52,9 @@ int argon2(const char *type, const char *password, size_t password_length,
};
int r;
+ /* This code must not be run if crypt backend library natively supports Argon2 */
+ assert(!(crypt_backend_flags() & CRYPT_BACKEND_ARGON2));
+
if (!strcmp(type, "argon2i"))
atype = Argon2_i;
else if(!strcmp(type, "argon2id"))
@@ -75,5 +76,33 @@ int argon2(const char *type, const char *password, size_t password_length,
}
return r;
+}
+
+#else /* USE_INTERNAL_ARGON2 || HAVE_ARGON2_H */
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+int argon2(const char *type, const char *password, size_t password_length,
+ const char *salt, size_t salt_length,
+ char *key, size_t key_length,
+ uint32_t iterations, uint32_t memory, uint32_t parallel)
+{
+ return -EINVAL;
+}
+
+#endif
+
+/* Additional string for crypt backend version */
+const char *crypt_argon2_version(void)
+{
+ const char *version = "";
+
+ if (crypt_backend_flags() & CRYPT_BACKEND_ARGON2)
+ return version;
+
+#if HAVE_ARGON2_H /* this has priority over internal argon2 */
+ version = " [external libargon2]";
+#elif USE_INTERNAL_ARGON2
+ version = " [cryptsetup libargon2]";
#endif
+ return version;
}
diff --git a/lib/crypto_backend/base64.c b/lib/crypto_backend/base64.c
index 42f70cb..92e558a 100644
--- a/lib/crypto_backend/base64.c
+++ b/lib/crypto_backend/base64.c
@@ -4,7 +4,7 @@
* Copyright (C) 2010 Lennart Poettering
*
* cryptsetup related changes
- * Copyright (C) 2021-2023 Milan Broz
+ * Copyright (C) 2021-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/crypto_backend/cipher_check.c b/lib/crypto_backend/cipher_check.c
index 98ec1a5..25200a4 100644
--- a/lib/crypto_backend/cipher_check.c
+++ b/lib/crypto_backend/cipher_check.c
@@ -1,8 +1,8 @@
/*
* Cipher performance check
*
- * Copyright (C) 2018-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2018-2023 Milan Broz
+ * Copyright (C) 2018-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2018-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/crypto_backend/cipher_generic.c b/lib/crypto_backend/cipher_generic.c
index b3a4407..746cfcf 100644
--- a/lib/crypto_backend/cipher_generic.c
+++ b/lib/crypto_backend/cipher_generic.c
@@ -1,8 +1,8 @@
/*
* Linux kernel cipher generic utilities
*
- * Copyright (C) 2018-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2018-2023 Milan Broz
+ * Copyright (C) 2018-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2018-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -51,6 +51,7 @@ static const struct cipher_alg cipher_algs[] = {
{ "xchacha12,aes", "adiantum", 32, false },
{ "xchacha20,aes", "adiantum", 32, false },
{ "sm4", NULL, 16, false },
+ { "aria", NULL, 16, false },
{ NULL, NULL, 0, false }
};
diff --git a/lib/crypto_backend/crc32.c b/lib/crypto_backend/crc32.c
index 9009b02..7a12a8e 100644
--- a/lib/crypto_backend/crc32.c
+++ b/lib/crypto_backend/crc32.c
@@ -158,7 +158,7 @@ static const uint32_t crc32c_tab[] = {
* whatever they need.
*/
static uint32_t compute_crc32(
- const uint32_t *crc32_tab,
+ const uint32_t *crc32_table,
uint32_t seed,
const unsigned char *buf,
size_t len)
@@ -167,7 +167,7 @@ static uint32_t compute_crc32(
const unsigned char *p = buf;
while(len-- > 0)
- crc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ crc = crc32_table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
return crc;
}
diff --git a/lib/crypto_backend/crypto_backend.h b/lib/crypto_backend/crypto_backend.h
index 88562e9..15ed745 100644
--- a/lib/crypto_backend/crypto_backend.h
+++ b/lib/crypto_backend/crypto_backend.h
@@ -1,8 +1,8 @@
/*
* crypto backend implementation
*
- * Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2010-2023 Milan Broz
+ * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -43,9 +43,11 @@ void crypt_backend_destroy(void);
#define CRYPT_BACKEND_KERNEL (1 << 0) /* Crypto uses kernel part, for benchmark */
#define CRYPT_BACKEND_PBKDF2_INT (1 << 1) /* Iteration in PBKDF2 is signed int and can overflow */
+#define CRYPT_BACKEND_ARGON2 (1 << 2) /* Backend provides native Argon2 implementation */
uint32_t crypt_backend_flags(void);
const char *crypt_backend_version(void);
+const char *crypt_argon2_version(void);
/* HASH */
int crypt_hash_size(const char *name);
diff --git a/lib/crypto_backend/crypto_backend_internal.h b/lib/crypto_backend/crypto_backend_internal.h
index 9b1cc69..539f11a 100644
--- a/lib/crypto_backend/crypto_backend_internal.h
+++ b/lib/crypto_backend/crypto_backend_internal.h
@@ -1,8 +1,8 @@
/*
* crypto backend implementation
*
- * Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2010-2023 Milan Broz
+ * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/crypto_backend/crypto_cipher_kernel.c b/lib/crypto_backend/crypto_cipher_kernel.c
index 3460717..77b3643 100644
--- a/lib/crypto_backend/crypto_cipher_kernel.c
+++ b/lib/crypto_backend/crypto_cipher_kernel.c
@@ -1,8 +1,8 @@
/*
* Linux kernel userspace API crypto backend implementation (skcipher)
*
- * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2012-2023 Milan Broz
+ * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -109,6 +109,7 @@ int crypt_cipher_init_kernel(struct crypt_cipher_kernel *ctx, const char *name,
}
/* The in/out should be aligned to page boundary */
+/* coverity[ -taint_source : arg-3 ] */
static int _crypt_cipher_crypt(struct crypt_cipher_kernel *ctx,
const char *in, size_t in_length,
char *out, size_t out_length,
@@ -312,6 +313,8 @@ int crypt_bitlk_decrypt_key_kernel(const void *key, size_t key_length,
}
#else /* ENABLE_AF_ALG */
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
int crypt_cipher_init_kernel(struct crypt_cipher_kernel *ctx, const char *name,
const char *mode, const void *key, size_t key_length)
{
diff --git a/lib/crypto_backend/crypto_gcrypt.c b/lib/crypto_backend/crypto_gcrypt.c
index e974aa8..8e3f14e 100644
--- a/lib/crypto_backend/crypto_gcrypt.c
+++ b/lib/crypto_backend/crypto_gcrypt.c
@@ -1,8 +1,8 @@
/*
* GCRYPT crypto backend implementation
*
- * Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2010-2023 Milan Broz
+ * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,6 +23,7 @@
#include <stdio.h>
#include <errno.h>
#include <gcrypt.h>
+#include <pthread.h>
#include "crypto_backend_internal.h"
static int crypto_backend_initialised = 0;
@@ -126,10 +127,11 @@ int crypt_backend_init(bool fips __attribute__((unused)))
crypto_backend_initialised = 1;
crypt_hash_test_whirlpool_bug();
- r = snprintf(version, sizeof(version), "gcrypt %s%s%s",
+ r = snprintf(version, sizeof(version), "gcrypt %s%s%s%s",
gcry_check_version(NULL),
crypto_backend_secmem ? "" : ", secmem disabled",
- crypto_backend_whirlpool_bug > 0 ? ", flawed whirlpool" : "");
+ crypto_backend_whirlpool_bug > 0 ? ", flawed whirlpool" : "",
+ crypt_backend_flags() & CRYPT_BACKEND_ARGON2 ? ", argon2" : "");
if (r < 0 || (size_t)r >= sizeof(version))
return -EINVAL;
@@ -151,7 +153,11 @@ const char *crypt_backend_version(void)
uint32_t crypt_backend_flags(void)
{
- return 0;
+ uint32_t flags = 0;
+#if HAVE_DECL_GCRY_KDF_ARGON2 && !USE_INTERNAL_ARGON2
+ flags |= CRYPT_BACKEND_ARGON2;
+#endif
+ return flags;
}
static const char *crypt_hash_compat_name(const char *name, unsigned int *flags)
@@ -266,7 +272,6 @@ int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length)
void crypt_hash_destroy(struct crypt_hash *ctx)
{
gcry_md_close(ctx->hd);
- memset(ctx, 0, sizeof(*ctx));
free(ctx);
}
@@ -341,7 +346,6 @@ int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length)
void crypt_hmac_destroy(struct crypt_hmac *ctx)
{
gcry_md_close(ctx->hd);
- memset(ctx, 0, sizeof(*ctx));
free(ctx);
}
@@ -386,6 +390,130 @@ static int pbkdf2(const char *hash,
#endif /* USE_INTERNAL_PBKDF2 */
}
+#if HAVE_DECL_GCRY_KDF_ARGON2 && !USE_INTERNAL_ARGON2
+struct gcrypt_thread_job
+{
+ pthread_t thread;
+ struct job_thread_param {
+ gcry_kdf_job_fn_t job;
+ void *p;
+ } work;
+};
+
+struct gcrypt_threads
+{
+ pthread_attr_t attr;
+ unsigned int num_threads;
+ unsigned int max_threads;
+ struct gcrypt_thread_job *jobs_ctx;
+};
+
+static void *gcrypt_job_thread(void *p)
+{
+ struct job_thread_param *param = p;
+ param->job(param->p);
+ pthread_exit(NULL);
+}
+
+static int gcrypt_wait_all_jobs(void *ctx)
+{
+ unsigned int i;
+ struct gcrypt_threads *threads = ctx;
+
+ for (i = 0; i < threads->num_threads; i++) {
+ pthread_join(threads->jobs_ctx[i].thread, NULL);
+ threads->jobs_ctx[i].thread = 0;
+ }
+
+ threads->num_threads = 0;
+ return 0;
+}
+
+static int gcrypt_dispatch_job(void *ctx, gcry_kdf_job_fn_t job, void *p)
+{
+ struct gcrypt_threads *threads = ctx;
+
+ if (threads->num_threads >= threads->max_threads)
+ return -1;
+
+ threads->jobs_ctx[threads->num_threads].work.job = job;
+ threads->jobs_ctx[threads->num_threads].work.p = p;
+
+ if (pthread_create(&threads->jobs_ctx[threads->num_threads].thread, &threads->attr,
+ gcrypt_job_thread, &threads->jobs_ctx[threads->num_threads].work))
+ return -1;
+
+ threads->num_threads++;
+ return 0;
+}
+
+static int gcrypt_argon2(const char *type,
+ const char *password, size_t password_length,
+ const char *salt, size_t salt_length,
+ char *key, size_t key_length,
+ uint32_t iterations, uint32_t memory, uint32_t parallel)
+{
+ gcry_kdf_hd_t hd;
+ int atype, r = -EINVAL;
+ unsigned long param[4];
+ struct gcrypt_threads threads = {
+ .max_threads = parallel,
+ .num_threads = 0
+ };
+ const gcry_kdf_thread_ops_t ops = {
+ .jobs_context = &threads,
+ .dispatch_job = gcrypt_dispatch_job,
+ .wait_all_jobs = gcrypt_wait_all_jobs
+ };
+
+ if (!strcmp(type, "argon2i"))
+ atype = GCRY_KDF_ARGON2I;
+ else if (!strcmp(type, "argon2id"))
+ atype = GCRY_KDF_ARGON2ID;
+ else
+ return -EINVAL;
+
+ param[0] = key_length;
+ param[1] = iterations;
+ param[2] = memory;
+ param[3] = parallel;
+
+ if (gcry_kdf_open(&hd, GCRY_KDF_ARGON2, atype, param, 4,
+ password, password_length, salt, salt_length,
+ NULL, 0, NULL, 0)) {
+ free(threads.jobs_ctx);
+ return -EINVAL;
+ }
+
+ if (parallel == 1) {
+ /* Do not use threads here */
+ if (gcry_kdf_compute(hd, NULL))
+ goto out;
+ } else {
+ threads.jobs_ctx = calloc(threads.max_threads,
+ sizeof(struct gcrypt_thread_job));
+ if (!threads.jobs_ctx)
+ goto out;
+
+ if (pthread_attr_init(&threads.attr))
+ goto out;
+
+ if (gcry_kdf_compute(hd, &ops))
+ goto out;
+ }
+
+ if (gcry_kdf_final(hd, key_length, key))
+ goto out;
+ r = 0;
+out:
+ gcry_kdf_close(hd);
+ pthread_attr_destroy(&threads.attr);
+ free(threads.jobs_ctx);
+
+ return r;
+}
+#endif
+
/* PBKDF */
int crypt_pbkdf(const char *kdf, const char *hash,
const char *password, size_t password_length,
@@ -400,8 +528,13 @@ int crypt_pbkdf(const char *kdf, const char *hash,
return pbkdf2(hash, password, password_length, salt, salt_length,
key, key_length, iterations);
else if (!strncmp(kdf, "argon2", 6))
+#if HAVE_DECL_GCRY_KDF_ARGON2 && !USE_INTERNAL_ARGON2
+ return gcrypt_argon2(kdf, password, password_length, salt, salt_length,
+ key, key_length, iterations, memory, parallel);
+#else
return argon2(kdf, password, password_length, salt, salt_length,
key, key_length, iterations, memory, parallel);
+#endif
return -EINVAL;
}
@@ -565,6 +698,9 @@ bool crypt_fips_mode(void)
if (fips_checked)
return fips_mode;
+ if (crypt_backend_init(false /* ignored */))
+ return false;
+
fips_mode = gcry_fips_mode_active();
fips_checked = true;
diff --git a/lib/crypto_backend/crypto_kernel.c b/lib/crypto_backend/crypto_kernel.c
index 8493c0a..be6051a 100644
--- a/lib/crypto_backend/crypto_kernel.c
+++ b/lib/crypto_backend/crypto_kernel.c
@@ -1,8 +1,8 @@
/*
* Linux kernel userspace API crypto backend implementation
*
- * Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2010-2023 Milan Broz
+ * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -245,7 +245,6 @@ void crypt_hash_destroy(struct crypt_hash *ctx)
close(ctx->tfmfd);
if (ctx->opfd >= 0)
close(ctx->opfd);
- memset(ctx, 0, sizeof(*ctx));
free(ctx);
}
@@ -324,7 +323,6 @@ void crypt_hmac_destroy(struct crypt_hmac *ctx)
close(ctx->tfmfd);
if (ctx->opfd >= 0)
close(ctx->opfd);
- memset(ctx, 0, sizeof(*ctx));
free(ctx);
}
diff --git a/lib/crypto_backend/crypto_nettle.c b/lib/crypto_backend/crypto_nettle.c
index 086e4fc..f08db74 100644
--- a/lib/crypto_backend/crypto_nettle.c
+++ b/lib/crypto_backend/crypto_nettle.c
@@ -1,8 +1,8 @@
/*
* Nettle crypto backend implementation
*
- * Copyright (C) 2011-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2011-2023 Milan Broz
+ * Copyright (C) 2011-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2011-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/crypto_backend/crypto_nss.c b/lib/crypto_backend/crypto_nss.c
index c154812..6b390a4 100644
--- a/lib/crypto_backend/crypto_nss.c
+++ b/lib/crypto_backend/crypto_nss.c
@@ -1,8 +1,8 @@
/*
* NSS crypto backend implementation
*
- * Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2010-2023 Milan Broz
+ * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/crypto_backend/crypto_openssl.c b/lib/crypto_backend/crypto_openssl.c
index 607ec38..4e85384 100644
--- a/lib/crypto_backend/crypto_openssl.c
+++ b/lib/crypto_backend/crypto_openssl.c
@@ -1,8 +1,8 @@
/*
* OPENSSL crypto backend implementation
*
- * Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2010-2023 Milan Broz
+ * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -44,9 +44,20 @@ static OSSL_PROVIDER *ossl_legacy = NULL;
static OSSL_PROVIDER *ossl_default = NULL;
static OSSL_LIB_CTX *ossl_ctx = NULL;
static char backend_version[256] = "OpenSSL";
+
+#define MAX_THREADS 8
+#if !HAVE_DECL_OSSL_GET_MAX_THREADS
+static int OSSL_set_max_threads(OSSL_LIB_CTX *ctx __attribute__((unused)),
+ uint64_t max_threads __attribute__((unused))) { return 0; }
+static uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx __attribute__((unused))) { return 0; }
+#else
+#include <openssl/thread.h>
+#endif
+
#endif
#define CONST_CAST(x) (x)(uintptr_t)
+#define UNUSED(x) (void)(x)
static int crypto_backend_initialised = 0;
@@ -162,6 +173,7 @@ static int openssl_backend_init(bool fips)
*/
#if OPENSSL_VERSION_MAJOR >= 3
int r;
+ bool ossl_threads = false;
/*
* In FIPS mode we keep default OpenSSL context & global config
@@ -181,16 +193,24 @@ static int openssl_backend_init(bool fips)
ossl_legacy = OSSL_PROVIDER_try_load(ossl_ctx, "legacy", 0);
}
- r = snprintf(backend_version, sizeof(backend_version), "%s %s%s%s",
+ if (OSSL_set_max_threads(ossl_ctx, MAX_THREADS) == 1 &&
+ OSSL_get_max_threads(ossl_ctx) == MAX_THREADS)
+ ossl_threads = true;
+
+ r = snprintf(backend_version, sizeof(backend_version), "%s %s%s%s%s%s",
OpenSSL_version(OPENSSL_VERSION),
ossl_default ? "[default]" : "",
ossl_legacy ? "[legacy]" : "",
- fips ? "[fips]" : "");
+ fips ? "[fips]" : "",
+ ossl_threads ? "[threads]" : "",
+ crypt_backend_flags() & CRYPT_BACKEND_ARGON2 ? "[argon2]" : "");
if (r < 0 || (size_t)r >= sizeof(backend_version)) {
openssl_backend_exit();
return -EINVAL;
}
+#else
+ UNUSED(fips);
#endif
return 0;
}
@@ -232,11 +252,14 @@ void crypt_backend_destroy(void)
uint32_t crypt_backend_flags(void)
{
-#if OPENSSL_VERSION_MAJOR >= 3
- return 0;
-#else
- return CRYPT_BACKEND_PBKDF2_INT;
+ uint32_t flags = 0;
+#if OPENSSL_VERSION_MAJOR < 3
+ flags |= CRYPT_BACKEND_PBKDF2_INT;
+#endif
+#if HAVE_DECL_OSSL_KDF_PARAM_ARGON2_VERSION
+ flags |= CRYPT_BACKEND_ARGON2;
#endif
+ return flags;
}
const char *crypt_backend_version(void)
@@ -281,6 +304,8 @@ static void hash_id_free(const EVP_MD *hash_id)
{
#if OPENSSL_VERSION_MAJOR >= 3
EVP_MD_free(CONST_CAST(EVP_MD*)hash_id);
+#else
+ UNUSED(hash_id);
#endif
}
@@ -297,6 +322,8 @@ static void cipher_type_free(const EVP_CIPHER *cipher_type)
{
#if OPENSSL_VERSION_MAJOR >= 3
EVP_CIPHER_free(CONST_CAST(EVP_CIPHER*)cipher_type);
+#else
+ UNUSED(cipher_type);
#endif
}
@@ -391,7 +418,6 @@ void crypt_hash_destroy(struct crypt_hash *ctx)
{
hash_id_free(ctx->hash_id);
EVP_MD_CTX_free(ctx->md);
- memset(ctx, 0, sizeof(*ctx));
free(ctx);
}
@@ -527,7 +553,6 @@ void crypt_hmac_destroy(struct crypt_hmac *ctx)
hash_id_free(ctx->hash_id);
HMAC_CTX_free(ctx->md);
#endif
- memset(ctx, 0, sizeof(*ctx));
free(ctx);
}
@@ -593,8 +618,53 @@ static int openssl_argon2(const char *type, const char *password, size_t passwor
const char *salt, size_t salt_length, char *key, size_t key_length,
uint32_t iterations, uint32_t memory, uint32_t parallel)
{
+#if HAVE_DECL_OSSL_KDF_PARAM_ARGON2_VERSION
+ EVP_KDF_CTX *ctx;
+ EVP_KDF *argon2;
+ unsigned int threads = parallel;
+ int r;
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD,
+ CONST_CAST(void*)password, password_length),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT,
+ CONST_CAST(void*)salt, salt_length),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_ITER, &iterations),
+ OSSL_PARAM_uint(OSSL_KDF_PARAM_THREADS, &threads),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_ARGON2_LANES, &parallel),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_ARGON2_MEMCOST, &memory),
+ OSSL_PARAM_END
+ };
+
+ if (OSSL_get_max_threads(ossl_ctx) == 0)
+ threads = 1;
+
+ argon2 = EVP_KDF_fetch(ossl_ctx, type, NULL);
+ if (!argon2)
+ return -EINVAL;
+
+ ctx = EVP_KDF_CTX_new(argon2);
+ if (!ctx) {
+ EVP_KDF_free(argon2);
+ return -EINVAL;;
+ }
+
+ if (EVP_KDF_CTX_set_params(ctx, params) != 1) {
+ EVP_KDF_CTX_free(ctx);
+ EVP_KDF_free(argon2);
+ return -EINVAL;
+ }
+
+ r = EVP_KDF_derive(ctx, (unsigned char*)key, key_length, NULL /*params*/);
+
+ EVP_KDF_CTX_free(ctx);
+ EVP_KDF_free(argon2);
+
+ /* _derive() returns 0 or negative value on error, 1 on success */
+ return r == 1 ? 0 : -EINVAL;
+#else
return argon2(type, password, password_length, salt, salt_length,
key, key_length, iterations, memory, parallel);
+#endif
}
/* PBKDF */
diff --git a/lib/crypto_backend/crypto_storage.c b/lib/crypto_backend/crypto_storage.c
index 13479dd..6c8f991 100644
--- a/lib/crypto_backend/crypto_storage.c
+++ b/lib/crypto_backend/crypto_storage.c
@@ -2,7 +2,7 @@
* Generic wrapper for storage encryption modes and Initial Vectors
* (reimplementation of some functions from Linux dm-crypt kernel)
*
- * Copyright (C) 2014-2023 Milan Broz
+ * Copyright (C) 2014-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/crypto_backend/meson.build b/lib/crypto_backend/meson.build
new file mode 100644
index 0000000..d6c31fd
--- /dev/null
+++ b/lib/crypto_backend/meson.build
@@ -0,0 +1,40 @@
+if use_internal_argon2
+ subdir('argon2')
+endif
+
+libcrypto_backend_dependencies = [
+ crypto_backend_library,
+ clock_gettime,
+]
+libcrypto_backend_link_with = []
+
+libcrypto_backend_sources = files(
+ 'argon2_generic.c',
+ 'base64.c',
+ 'cipher_check.c',
+ 'cipher_generic.c',
+ 'crc32.c',
+ 'crypto_cipher_kernel.c',
+ 'crypto_storage.c',
+ 'pbkdf_check.c',
+ 'utf8.c',
+)
+
+crypto_backend = get_option('crypto-backend')
+libcrypto_backend_sources += files('crypto_@0@.c'.format(crypto_backend))
+
+if use_internal_pbkdf2
+ libcrypto_backend_sources += files('pbkdf2_generic.c')
+endif
+
+if use_internal_argon2 and get_option('argon-implementation') == 'internal'
+ libcrypto_backend_link_with += libargon2
+elif get_option('argon-implementation') == 'libargon2'
+ libcrypto_backend_dependencies += libargon2_external
+endif
+
+libcrypto_backend = static_library('crypto_backend',
+ libcrypto_backend_sources,
+ include_directories: includes_lib,
+ dependencies: libcrypto_backend_dependencies,
+ link_with: libcrypto_backend_link_with)
diff --git a/lib/crypto_backend/pbkdf2_generic.c b/lib/crypto_backend/pbkdf2_generic.c
index 9e87e19..f7fe5bc 100644
--- a/lib/crypto_backend/pbkdf2_generic.c
+++ b/lib/crypto_backend/pbkdf2_generic.c
@@ -4,8 +4,8 @@
* Copyright (C) 2004 Free Software Foundation
*
* cryptsetup related changes
- * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2012-2023 Milan Broz
+ * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/crypto_backend/pbkdf_check.c b/lib/crypto_backend/pbkdf_check.c
index 53a2da9..54d6a34 100644
--- a/lib/crypto_backend/pbkdf_check.c
+++ b/lib/crypto_backend/pbkdf_check.c
@@ -1,7 +1,7 @@
/*
* PBKDF performance check
- * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2012-2023 Milan Broz
+ * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012-2024 Milan Broz
* Copyright (C) 2016-2020 Ondrej Mosnacek
*
* This file is free software; you can redistribute it and/or
diff --git a/lib/crypto_backend/utf8.c b/lib/crypto_backend/utf8.c
index 24e0d8d..c13e953 100644
--- a/lib/crypto_backend/utf8.c
+++ b/lib/crypto_backend/utf8.c
@@ -4,7 +4,7 @@
* Copyright (C) 2010 Lennart Poettering
*
* cryptsetup related changes
- * Copyright (C) 2021-2023 Vojtech Trefny
+ * Copyright (C) 2021-2024 Vojtech Trefny
* Parts of the original systemd implementation are based on the GLIB utf8
* validation functions.
diff --git a/lib/integrity/integrity.c b/lib/integrity/integrity.c
index aeadc82..ac2f0d0 100644
--- a/lib/integrity/integrity.c
+++ b/lib/integrity/integrity.c
@@ -1,7 +1,7 @@
/*
* Integrity volume handling
*
- * Copyright (C) 2016-2023 Milan Broz
+ * Copyright (C) 2016-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -335,13 +335,62 @@ int INTEGRITY_activate(struct crypt_device *cd,
return r;
}
+static int _create_reduced_device(struct crypt_device *cd,
+ const char *name,
+ uint64_t device_size_sectors,
+ struct device **ret_device)
+{
+ int r;
+ char path[PATH_MAX];
+ struct device *dev;
+
+ struct crypt_dm_active_device dmd = {
+ .size = device_size_sectors,
+ .flags = CRYPT_ACTIVATE_PRIVATE,
+ };
+
+ assert(cd);
+ assert(name);
+ assert(device_size_sectors);
+ assert(ret_device);
+
+ r = snprintf(path, sizeof(path), "%s/%s", dm_get_dir(), name);
+ if (r < 0 || (size_t)r >= sizeof(path))
+ return -EINVAL;
+
+ r = device_block_adjust(cd, crypt_data_device(cd), DEV_OK,
+ crypt_get_data_offset(cd), &device_size_sectors, &dmd.flags);
+ if (r)
+ return r;
+
+ log_dbg(cd, "Activating reduced helper device %s.", name);
+
+ r = dm_linear_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd), crypt_get_data_offset(cd));
+ if (!r)
+ r = dm_create_device(cd, name, CRYPT_SUBDEV, &dmd);
+ dm_targets_free(cd, &dmd);
+ if (r < 0)
+ return r;
+
+ r = device_alloc(cd, &dev, path);
+ if (!r) {
+ *ret_device = dev;
+ return 0;
+ }
+
+ dm_remove_device(cd, name, CRYPT_DEACTIVATE_FORCE);
+
+ return r;
+}
+
int INTEGRITY_format(struct crypt_device *cd,
const struct crypt_params_integrity *params,
struct volume_key *journal_crypt_key,
- struct volume_key *journal_mac_key)
+ struct volume_key *journal_mac_key,
+ uint64_t backing_device_sectors)
{
uint32_t dmi_flags;
- char tmp_name[64], tmp_uuid[40];
+ char reduced_device_name[70], tmp_name[64], tmp_uuid[40];
struct crypt_dm_active_device dmdi = {
.size = 8,
.flags = CRYPT_ACTIVATE_PRIVATE, /* We always create journal but it can be unused later */
@@ -349,6 +398,8 @@ int INTEGRITY_format(struct crypt_device *cd,
struct dm_target *tgt = &dmdi.segment;
int r;
uuid_t tmp_uuid_bin;
+ uint64_t data_offset_sectors;
+ struct device *p_metadata_device, *p_data_device, *reduced_device = NULL;
struct volume_key *vk = NULL;
uuid_generate(tmp_uuid_bin);
@@ -358,18 +409,42 @@ int INTEGRITY_format(struct crypt_device *cd,
if (r < 0 || (size_t)r >= sizeof(tmp_name))
return -EINVAL;
+ p_metadata_device = INTEGRITY_metadata_device(cd);
+
+ if (backing_device_sectors) {
+ r = snprintf(reduced_device_name, sizeof(reduced_device_name),
+ "temporary-cryptsetup-reduced-%s", tmp_uuid);
+ if (r < 0 || (size_t)r >= sizeof(reduced_device_name))
+ return -EINVAL;
+
+ /*
+ * Creates reduced dm-linear mapping over data device starting at
+ * crypt_data_offset(cd) and backing_device_sectors in size.
+ */
+ r = _create_reduced_device(cd, reduced_device_name,
+ backing_device_sectors, &reduced_device);
+ if (r < 0)
+ return r;
+
+ data_offset_sectors = 0;
+ p_data_device = reduced_device;
+ if (p_metadata_device == crypt_data_device(cd))
+ p_metadata_device = reduced_device;
+ } else {
+ data_offset_sectors = crypt_get_data_offset(cd);
+ p_data_device = crypt_data_device(cd);
+ }
+
/* There is no data area, we can actually use fake zeroed key */
if (params && params->integrity_key_size)
vk = crypt_alloc_volume_key(params->integrity_key_size, NULL);
- r = dm_integrity_target_set(cd, tgt, 0, dmdi.size, INTEGRITY_metadata_device(cd),
- crypt_data_device(cd), crypt_get_integrity_tag_size(cd),
- crypt_get_data_offset(cd), crypt_get_sector_size(cd), vk,
+ r = dm_integrity_target_set(cd, tgt, 0, dmdi.size, p_metadata_device,
+ p_data_device, crypt_get_integrity_tag_size(cd),
+ data_offset_sectors, crypt_get_sector_size(cd), vk,
journal_crypt_key, journal_mac_key, params);
- if (r < 0) {
- crypt_free_volume_key(vk);
- return r;
- }
+ if (r < 0)
+ goto err;
log_dbg(cd, "Trying to format INTEGRITY device on top of %s, tmp name %s, tag size %d.",
device_path(tgt->data_device), tmp_name, tgt->u.integrity.tag_size);
@@ -379,24 +454,26 @@ int INTEGRITY_format(struct crypt_device *cd,
log_err(cd, _("Kernel does not support dm-integrity mapping."));
r = -ENOTSUP;
}
- if (r) {
- dm_targets_free(cd, &dmdi);
- return r;
- }
+ if (r)
+ goto err;
if (tgt->u.integrity.meta_device) {
r = device_block_adjust(cd, tgt->u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL);
- if (r) {
- dm_targets_free(cd, &dmdi);
- return r;
- }
+ if (r)
+ goto err;
}
r = dm_create_device(cd, tmp_name, CRYPT_INTEGRITY, &dmdi);
- crypt_free_volume_key(vk);
- dm_targets_free(cd, &dmdi);
if (r)
- return r;
+ goto err;
- return dm_remove_device(cd, tmp_name, CRYPT_DEACTIVATE_FORCE);
+ r = dm_remove_device(cd, tmp_name, CRYPT_DEACTIVATE_FORCE);
+err:
+ dm_targets_free(cd, &dmdi);
+ crypt_free_volume_key(vk);
+ if (reduced_device) {
+ dm_remove_device(cd, reduced_device_name, CRYPT_DEACTIVATE_FORCE);
+ device_free(cd, reduced_device);
+ }
+ return r;
}
diff --git a/lib/integrity/integrity.h b/lib/integrity/integrity.h
index 2883ef8..55c7148 100644
--- a/lib/integrity/integrity.h
+++ b/lib/integrity/integrity.h
@@ -1,7 +1,7 @@
/*
* Integrity header definition
*
- * Copyright (C) 2016-2023 Milan Broz
+ * Copyright (C) 2016-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -75,7 +75,8 @@ int INTEGRITY_hash_tag_size(const char *integrity);
int INTEGRITY_format(struct crypt_device *cd,
const struct crypt_params_integrity *params,
struct volume_key *journal_crypt_key,
- struct volume_key *journal_mac_key);
+ struct volume_key *journal_mac_key,
+ uint64_t backing_device_sectors);
int INTEGRITY_activate(struct crypt_device *cd,
const char *name,
diff --git a/lib/internal.h b/lib/internal.h
index b5cb4e3..3a0d6e6 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -3,8 +3,8 @@
*
* Copyright (C) 2004 Jana Saout <jana@saout.de>
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -53,6 +53,7 @@
#define MAX_DM_DEPS 32
#define CRYPT_SUBDEV "SUBDEV" /* prefix for sublayered devices underneath public crypt types */
+#define CRYPT_LUKS2_HW_OPAL "LUKS2-OPAL" /* dm uuid prefix used for any HW OPAL enabled LUKS2 device */
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
@@ -89,6 +90,7 @@ int crypt_benchmark_pbkdf_internal(struct crypt_device *cd,
struct crypt_pbkdf_type *pbkdf,
size_t volume_key_size);
const char *crypt_get_cipher_spec(struct crypt_device *cd);
+uint32_t pbkdf_adjusted_phys_memory_kb(void);
/* Device backend */
struct device;
@@ -113,6 +115,7 @@ void device_release_excl(struct crypt_device *cd, struct device *device);
void device_disable_direct_io(struct device *device);
int device_is_identical(struct device *device1, struct device *device2);
int device_is_rotational(struct device *device);
+int device_is_dax(struct device *device);
size_t device_alignment(struct device *device);
int device_direct_io(const struct device *device);
int device_fallocate(struct device *device, uint64_t size);
@@ -153,21 +156,31 @@ int create_or_reload_device_with_integrity(struct crypt_device *cd, const char *
struct device *crypt_metadata_device(struct crypt_device *cd);
struct device *crypt_data_device(struct crypt_device *cd);
+uint64_t crypt_get_metadata_size_bytes(struct crypt_device *cd);
+uint64_t crypt_get_keyslots_size_bytes(struct crypt_device *cd);
+uint64_t crypt_get_data_offset_sectors(struct crypt_device *cd);
+int crypt_opal_supported(struct crypt_device *cd, struct device *opal_device);
+
int crypt_confirm(struct crypt_device *cd, const char *msg);
char *crypt_lookup_dev(const char *dev_id);
int crypt_dev_is_rotational(int major, int minor);
+int crypt_dev_is_dax(int major, int minor);
int crypt_dev_is_partition(const char *dev_path);
char *crypt_get_partition_device(const char *dev_path, uint64_t offset, uint64_t size);
+int crypt_dev_get_partition_number(const char *dev_path);
char *crypt_get_base_device(const char *dev_path);
uint64_t crypt_dev_partition_offset(const char *dev_path);
int lookup_by_disk_id(const char *dm_uuid);
int lookup_by_sysfs_uuid_field(const char *dm_uuid);
int crypt_uuid_cmp(const char *dm_uuid, const char *hdr_uuid);
+int crypt_uuid_type_cmp(const char *dm_uuid, const char *type);
size_t crypt_getpagesize(void);
unsigned crypt_cpusonline(void);
uint64_t crypt_getphysmemory_kb(void);
+uint64_t crypt_getphysmemoryfree_kb(void);
+bool crypt_swapavailable(void);
int init_crypto(struct crypt_device *ctx);
@@ -202,7 +215,7 @@ void crypt_set_luks2_reencrypt(struct crypt_device *cd, struct luks2_reencrypt *
struct luks2_reencrypt *crypt_get_luks2_reencrypt(struct crypt_device *cd);
int onlyLUKS2(struct crypt_device *cd);
-int onlyLUKS2mask(struct crypt_device *cd, uint32_t mask);
+int onlyLUKS2reencrypt(struct crypt_device *cd);
int crypt_wipe_device(struct crypt_device *cd,
struct device *device,
@@ -221,6 +234,14 @@ int crypt_get_integrity_tag_size(struct crypt_device *cd);
int crypt_key_in_keyring(struct crypt_device *cd);
void crypt_set_key_in_keyring(struct crypt_device *cd, unsigned key_in_keyring);
int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key *vk);
+int crypt_keyring_get_user_key(struct crypt_device *cd,
+ const char *key_description,
+ char **key,
+ size_t *key_size);
+int crypt_keyring_get_key_by_name(struct crypt_device *cd,
+ const char *key_description,
+ char **key,
+ size_t *key_size);
int crypt_use_keyring_for_vk(struct crypt_device *cd);
void crypt_drop_keyring_key_by_description(struct crypt_device *cd, const char *key_description, key_type_t ktype);
void crypt_drop_keyring_key(struct crypt_device *cd, struct volume_key *vks);
@@ -250,4 +271,8 @@ static inline bool uint64_mult_overflow(uint64_t *u, uint64_t b, size_t size)
return false;
}
+#define KEY_NOT_VERIFIED -2
+#define KEY_EXTERNAL_VERIFICATION -1
+#define KEY_VERIFIED 0
+
#endif /* INTERNAL_H */
diff --git a/lib/keyslot_context.c b/lib/keyslot_context.c
index 89bd433..5860247 100644
--- a/lib/keyslot_context.c
+++ b/lib/keyslot_context.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup, keyslot unlock helpers
*
- * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2022-2023 Ondrej Kozina
+ * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2022-2024 Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -173,7 +173,7 @@ static int get_luks1_volume_key_by_keyfile(struct crypt_device *cd,
return r;
}
-static int get_key_by_key(struct crypt_device *cd,
+static int get_key_by_key(struct crypt_device *cd __attribute__((unused)),
struct crypt_keyslot_context *kc,
int keyslot __attribute__((unused)),
int segment __attribute__((unused)),
@@ -204,19 +204,73 @@ static int get_volume_key_by_key(struct crypt_device *cd,
return get_key_by_key(cd, kc, -2 /* unused */, -2 /* unused */, r_vk);
}
+static int get_generic_volume_key_by_key(struct crypt_device *cd,
+ struct crypt_keyslot_context *kc,
+ struct volume_key **r_vk)
+{
+ return get_key_by_key(cd, kc, -2 /* unused */, -2 /* unused */, r_vk);
+}
+
+static int get_generic_signed_key_by_key(struct crypt_device *cd,
+ struct crypt_keyslot_context *kc,
+ struct volume_key **r_vk,
+ struct volume_key **r_signature)
+{
+ struct volume_key *vk, *vk_sig;
+
+ assert(kc && ((kc->type == CRYPT_KC_TYPE_KEY) ||
+ (kc->type == CRYPT_KC_TYPE_SIGNED_KEY)));
+ assert(r_vk);
+ assert(r_signature);
+
+ /* return key with no signature */
+ if (kc->type == CRYPT_KC_TYPE_KEY) {
+ *r_signature = NULL;
+ return get_key_by_key(cd, kc, -2 /* unused */, -2 /* unused */, r_vk);
+ }
+
+ if (!kc->u.ks.volume_key || !kc->u.ks.signature) {
+ kc->error = -EINVAL;
+ return kc->error;
+ }
+
+ vk = crypt_alloc_volume_key(kc->u.ks.volume_key_size, kc->u.ks.volume_key);
+ if (!vk) {
+ kc->error = -ENOMEM;
+ return kc->error;
+ }
+
+ vk_sig = crypt_alloc_volume_key(kc->u.ks.signature_size, kc->u.ks.signature);
+ if (!vk_sig) {
+ crypt_free_volume_key(vk);
+ kc->error = -ENOMEM;
+ return kc->error;
+ }
+
+ *r_vk = vk;
+ *r_signature = vk_sig;
+
+ return 0;
+}
+
static int get_luks2_key_by_token(struct crypt_device *cd,
struct crypt_keyslot_context *kc,
- int keyslot __attribute__((unused)),
+ int keyslot,
int segment,
struct volume_key **r_vk)
{
int r;
+ struct luks2_hdr *hdr;
assert(cd);
assert(kc && kc->type == CRYPT_KC_TYPE_TOKEN);
assert(r_vk);
- r = LUKS2_token_unlock_key(cd, crypt_get_hdr(cd, CRYPT_LUKS2), kc->u.t.id, kc->u.t.type,
+ hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
+ if (!hdr)
+ return -EINVAL;
+
+ r = LUKS2_token_unlock_key(cd, hdr, keyslot, kc->u.t.id, kc->u.t.type,
kc->u.t.pin, kc->u.t.pin_size, segment, kc->u.t.usrptr, r_vk);
if (r < 0)
kc->error = r;
@@ -226,10 +280,10 @@ static int get_luks2_key_by_token(struct crypt_device *cd,
static int get_luks2_volume_key_by_token(struct crypt_device *cd,
struct crypt_keyslot_context *kc,
- int keyslot __attribute__((unused)),
+ int keyslot,
struct volume_key **r_vk)
{
- return get_luks2_key_by_token(cd, kc, -2 /* unused */, CRYPT_DEFAULT_SEGMENT, r_vk);
+ return get_luks2_key_by_token(cd, kc, keyslot, CRYPT_DEFAULT_SEGMENT, r_vk);
}
static int get_passphrase_by_token(struct crypt_device *cd,
@@ -261,6 +315,136 @@ static int get_passphrase_by_token(struct crypt_device *cd,
return kc->u.t.id;
}
+static int get_passphrase_by_keyring(struct crypt_device *cd,
+ struct crypt_keyslot_context *kc,
+ const char **r_passphrase,
+ size_t *r_passphrase_size)
+{
+ int r;
+
+ assert(cd);
+ assert(kc && kc->type == CRYPT_KC_TYPE_KEYRING);
+ assert(r_passphrase);
+ assert(r_passphrase_size);
+
+ if (!kc->i_passphrase) {
+ r = crypt_keyring_get_user_key(cd, kc->u.kr.key_description,
+ &kc->i_passphrase, &kc->i_passphrase_size);
+ if (r < 0) {
+ log_err(cd, _("Failed to read passphrase from keyring."));
+ kc->error = -EINVAL;
+ return -EINVAL;
+ }
+ }
+
+ *r_passphrase = kc->i_passphrase;
+ *r_passphrase_size = kc->i_passphrase_size;
+
+ return 0;
+}
+
+static int get_luks2_key_by_keyring(struct crypt_device *cd,
+ struct crypt_keyslot_context *kc,
+ int keyslot,
+ int segment,
+ struct volume_key **r_vk)
+{
+ int r;
+
+ assert(cd);
+ assert(kc && kc->type == CRYPT_KC_TYPE_KEYRING);
+ assert(r_vk);
+
+ r = get_passphrase_by_keyring(cd, kc, CONST_CAST(const char **) &kc->i_passphrase,
+ &kc->i_passphrase_size);
+ if (r < 0) {
+ log_err(cd, _("Failed to read passphrase from keyring."));
+ kc->error = -EINVAL;
+ return -EINVAL;
+ }
+
+ r = LUKS2_keyslot_open(cd, keyslot, segment, kc->i_passphrase, kc->i_passphrase_size, r_vk);
+ if (r < 0)
+ kc->error = r;
+
+ return 0;
+}
+
+static int get_luks2_volume_key_by_keyring(struct crypt_device *cd,
+ struct crypt_keyslot_context *kc,
+ int keyslot,
+ struct volume_key **r_vk)
+{
+ return get_luks2_key_by_keyring(cd, kc, keyslot, CRYPT_DEFAULT_SEGMENT, r_vk);
+}
+
+static int get_luks1_volume_key_by_keyring(struct crypt_device *cd,
+ struct crypt_keyslot_context *kc,
+ int keyslot,
+ struct volume_key **r_vk)
+{
+ int r;
+
+ assert(cd);
+ assert(kc && kc->type == CRYPT_KC_TYPE_PASSPHRASE);
+ assert(r_vk);
+
+ r = get_passphrase_by_keyring(cd, kc, CONST_CAST(const char **) &kc->i_passphrase,
+ &kc->i_passphrase_size);
+ if (r < 0) {
+ log_err(cd, _("Failed to read passphrase from keyring."));
+ kc->error = -EINVAL;
+ return -EINVAL;
+ }
+
+ r = LUKS_open_key_with_hdr(keyslot, kc->i_passphrase, kc->i_passphrase_size,
+ crypt_get_hdr(cd, CRYPT_LUKS1), r_vk, cd);
+ if (r < 0)
+ kc->error = r;
+
+ return r;
+}
+
+static int get_key_by_vk_in_keyring(struct crypt_device *cd,
+ struct crypt_keyslot_context *kc,
+ int keyslot __attribute__((unused)),
+ int segment __attribute__((unused)),
+ struct volume_key **r_vk)
+{
+ char *key;
+ size_t key_size;
+ int r;
+
+ assert(cd);
+ assert(kc && kc->type == CRYPT_KC_TYPE_VK_KEYRING);
+ assert(r_vk);
+
+ r = crypt_keyring_get_key_by_name(cd, kc->u.vk_kr.key_description,
+ &key, &key_size);
+ if (r < 0) {
+ log_err(cd, _("Failed to read volume key candidate from keyring."));
+ kc->error = -EINVAL;
+ return -EINVAL;
+ }
+
+ *r_vk = crypt_alloc_volume_key(key_size, key);
+ crypt_safe_free(key);
+ if (!*r_vk) {
+ kc->error = -ENOMEM;
+ return kc->error;
+ }
+
+ return 0;
+}
+
+static int get_volume_key_by_vk_in_keyring(struct crypt_device *cd,
+ struct crypt_keyslot_context *kc,
+ int keyslot __attribute__((unused)),
+ struct volume_key **r_vk)
+{
+ return get_key_by_vk_in_keyring(cd, kc, -2 /* unused */, -2 /* unused */, r_vk);
+}
+
static void unlock_method_init_internal(struct crypt_keyslot_context *kc)
{
assert(kc);
@@ -270,6 +454,26 @@ static void unlock_method_init_internal(struct crypt_keyslot_context *kc)
kc->i_passphrase_size = 0;
}
+void crypt_keyslot_unlock_by_keyring_internal(struct crypt_keyslot_context *kc,
+ const char *key_description)
+{
+ assert(kc);
+
+ kc->type = CRYPT_KC_TYPE_KEYRING;
+ kc->u.kr.key_description = key_description;
+
+ kc->get_luks2_key = get_luks2_key_by_keyring;
+ kc->get_luks2_volume_key = get_luks2_volume_key_by_keyring;
+ kc->get_luks1_volume_key = get_luks1_volume_key_by_keyring;
+ kc->get_passphrase = get_passphrase_by_keyring;
+ kc->get_plain_volume_key = NULL;
+ kc->get_bitlk_volume_key = NULL;
+ kc->get_fvault2_volume_key = NULL;
+ kc->get_verity_volume_key = NULL;
+ kc->get_integrity_volume_key = NULL;
+ unlock_method_init_internal(kc);
+}
+
void crypt_keyslot_unlock_by_key_init_internal(struct crypt_keyslot_context *kc,
const char *volume_key,
size_t volume_key_size)
@@ -283,6 +487,36 @@ void crypt_keyslot_unlock_by_key_init_internal(struct crypt_keyslot_context *kc,
kc->get_luks2_volume_key = get_volume_key_by_key;
kc->get_luks1_volume_key = get_volume_key_by_key;
kc->get_passphrase = NULL; /* keyslot key context does not provide passphrase */
+ kc->get_plain_volume_key = get_generic_volume_key_by_key;
+ kc->get_bitlk_volume_key = get_generic_volume_key_by_key;
+ kc->get_fvault2_volume_key = get_generic_volume_key_by_key;
+ kc->get_verity_volume_key = get_generic_signed_key_by_key;
+ kc->get_integrity_volume_key = get_generic_volume_key_by_key;
+ unlock_method_init_internal(kc);
+}
+
+void crypt_keyslot_unlock_by_signed_key_init_internal(struct crypt_keyslot_context *kc,
+ const char *volume_key,
+ size_t volume_key_size,
+ const char *signature,
+ size_t signature_size)
+{
+ assert(kc);
+
+ kc->type = CRYPT_KC_TYPE_SIGNED_KEY;
+ kc->u.ks.volume_key = volume_key;
+ kc->u.ks.volume_key_size = volume_key_size;
+ kc->u.ks.signature = signature;
+ kc->u.ks.signature_size = signature_size;
+ kc->get_luks2_key = NULL;
+ kc->get_luks2_volume_key = NULL;
+ kc->get_luks1_volume_key = NULL;
+ kc->get_passphrase = NULL;
+ kc->get_plain_volume_key = NULL;
+ kc->get_bitlk_volume_key = NULL;
+ kc->get_fvault2_volume_key = NULL;
+ kc->get_verity_volume_key = get_generic_signed_key_by_key;
+ kc->get_integrity_volume_key = NULL;
unlock_method_init_internal(kc);
}
@@ -299,6 +533,11 @@ void crypt_keyslot_unlock_by_passphrase_init_internal(struct crypt_keyslot_conte
kc->get_luks2_volume_key = get_luks2_volume_key_by_passphrase;
kc->get_luks1_volume_key = get_luks1_volume_key_by_passphrase;
kc->get_passphrase = get_passphrase_by_passphrase;
+ kc->get_plain_volume_key = NULL;
+ kc->get_bitlk_volume_key = NULL;
+ kc->get_fvault2_volume_key = NULL;
+ kc->get_verity_volume_key = NULL;
+ kc->get_integrity_volume_key = NULL;
unlock_method_init_internal(kc);
}
@@ -317,6 +556,11 @@ void crypt_keyslot_unlock_by_keyfile_init_internal(struct crypt_keyslot_context
kc->get_luks2_volume_key = get_luks2_volume_key_by_keyfile;
kc->get_luks1_volume_key = get_luks1_volume_key_by_keyfile;
kc->get_passphrase = get_passphrase_by_keyfile;
+ kc->get_plain_volume_key = NULL;
+ kc->get_bitlk_volume_key = NULL;
+ kc->get_fvault2_volume_key = NULL;
+ kc->get_verity_volume_key = NULL;
+ kc->get_integrity_volume_key = NULL;
unlock_method_init_internal(kc);
}
@@ -339,9 +583,35 @@ void crypt_keyslot_unlock_by_token_init_internal(struct crypt_keyslot_context *k
kc->get_luks2_volume_key = get_luks2_volume_key_by_token;
kc->get_luks1_volume_key = NULL; /* LUKS1 is not supported */
kc->get_passphrase = get_passphrase_by_token;
+ kc->get_plain_volume_key = NULL;
+ kc->get_bitlk_volume_key = NULL;
+ kc->get_fvault2_volume_key = NULL;
+ kc->get_verity_volume_key = NULL;
+ kc->get_integrity_volume_key = NULL;
unlock_method_init_internal(kc);
}
+void crypt_keyslot_unlock_by_vk_in_keyring_internal(struct crypt_keyslot_context *kc,
+ const char *key_description)
+{
+ assert(kc);
+
+ kc->type = CRYPT_KC_TYPE_VK_KEYRING;
+ kc->u.vk_kr.key_description = key_description;
+
+ kc->get_luks2_key = get_key_by_vk_in_keyring;
+ kc->get_luks2_volume_key = get_volume_key_by_vk_in_keyring;
+ kc->get_luks1_volume_key = NULL;
+ kc->get_passphrase = NULL; /* keyslot key context does not provide passphrase */
+ kc->get_plain_volume_key = NULL;
+ kc->get_bitlk_volume_key = NULL;
+ kc->get_fvault2_volume_key = NULL;
+ kc->get_verity_volume_key = NULL;
+ kc->get_integrity_volume_key = NULL;
+ unlock_method_init_internal(kc);
+}
+
+
void crypt_keyslot_context_destroy_internal(struct crypt_keyslot_context *kc)
{
if (!kc)
@@ -358,7 +628,7 @@ void crypt_keyslot_context_free(struct crypt_keyslot_context *kc)
free(kc);
}
-int crypt_keyslot_context_init_by_passphrase(struct crypt_device *cd,
+int crypt_keyslot_context_init_by_passphrase(struct crypt_device *cd __attribute__((unused)),
const char *passphrase,
size_t passphrase_size,
struct crypt_keyslot_context **kc)
@@ -379,7 +649,7 @@ int crypt_keyslot_context_init_by_passphrase(struct crypt_device *cd,
return 0;
}
-int crypt_keyslot_context_init_by_keyfile(struct crypt_device *cd,
+int crypt_keyslot_context_init_by_keyfile(struct crypt_device *cd __attribute__((unused)),
const char *keyfile,
size_t keyfile_size,
uint64_t keyfile_offset,
@@ -401,7 +671,7 @@ int crypt_keyslot_context_init_by_keyfile(struct crypt_device *cd,
return 0;
}
-int crypt_keyslot_context_init_by_token(struct crypt_device *cd,
+int crypt_keyslot_context_init_by_token(struct crypt_device *cd __attribute__((unused)),
int token,
const char *type,
const char *pin, size_t pin_size,
@@ -424,7 +694,7 @@ int crypt_keyslot_context_init_by_token(struct crypt_device *cd,
return 0;
}
-int crypt_keyslot_context_init_by_volume_key(struct crypt_device *cd,
+int crypt_keyslot_context_init_by_volume_key(struct crypt_device *cd __attribute__((unused)),
const char *volume_key,
size_t volume_key_size,
struct crypt_keyslot_context **kc)
@@ -445,12 +715,76 @@ int crypt_keyslot_context_init_by_volume_key(struct crypt_device *cd,
return 0;
}
+int crypt_keyslot_context_init_by_signed_key(struct crypt_device *cd __attribute__((unused)),
+ const char *volume_key,
+ size_t volume_key_size,
+ const char *signature,
+ size_t signature_size,
+ struct crypt_keyslot_context **kc)
+{
+ struct crypt_keyslot_context *tmp;
+
+ if (!kc)
+ return -EINVAL;
+
+ tmp = malloc(sizeof(*tmp));
+ if (!tmp)
+ return -ENOMEM;
+
+ crypt_keyslot_unlock_by_signed_key_init_internal(tmp, volume_key, volume_key_size,
+ signature, signature_size);
+
+ *kc = tmp;
+
+ return 0;
+}
+
+int crypt_keyslot_context_init_by_keyring(struct crypt_device *cd __attribute__((unused)),
+ const char *key_description,
+ struct crypt_keyslot_context **kc)
+{
+ struct crypt_keyslot_context *tmp;
+
+ if (!kc)
+ return -EINVAL;
+
+ tmp = malloc(sizeof(*tmp));
+ if (!tmp)
+ return -ENOMEM;
+
+ crypt_keyslot_unlock_by_keyring_internal(tmp, key_description);
+
+ *kc = tmp;
+
+ return 0;
+}
+
+int crypt_keyslot_context_init_by_vk_in_keyring(struct crypt_device *cd __attribute__((unused)),
+ const char *key_description,
+ struct crypt_keyslot_context **kc)
+{
+ struct crypt_keyslot_context *tmp;
+
+ if (!kc)
+ return -EINVAL;
+
+ tmp = malloc(sizeof(*tmp));
+ if (!tmp)
+ return -ENOMEM;
+
+ crypt_keyslot_unlock_by_vk_in_keyring_internal(tmp, key_description);
+
+ *kc = tmp;
+
+ return 0;
+}
+
int crypt_keyslot_context_get_error(struct crypt_keyslot_context *kc)
{
return kc ? kc->error : -EINVAL;
}
-int crypt_keyslot_context_set_pin(struct crypt_device *cd,
+int crypt_keyslot_context_set_pin(struct crypt_device *cd __attribute__((unused)),
const char *pin, size_t pin_size,
struct crypt_keyslot_context *kc)
{
@@ -482,6 +816,12 @@ const char *keyslot_context_type_string(const struct crypt_keyslot_context *kc)
return "token";
case CRYPT_KC_TYPE_KEY:
return "key";
+ case CRYPT_KC_TYPE_KEYRING:
+ return "keyring";
+ case CRYPT_KC_TYPE_VK_KEYRING:
+ return "volume key in keyring";
+ case CRYPT_KC_TYPE_SIGNED_KEY:
+ return "signed key";
default:
return "<unknown>";
}
diff --git a/lib/keyslot_context.h b/lib/keyslot_context.h
index 7ca7428..fd15159 100644
--- a/lib/keyslot_context.h
+++ b/lib/keyslot_context.h
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup, keyslot unlock helpers
*
- * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2022-2023 Ondrej Kozina
+ * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2022-2024 Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -40,6 +40,17 @@ typedef int (*keyslot_context_get_volume_key) (
int keyslot,
struct volume_key **r_vk);
+typedef int (*keyslot_context_get_generic_volume_key) (
+ struct crypt_device *cd,
+ struct crypt_keyslot_context *kc,
+ struct volume_key **r_vk);
+
+typedef int (*keyslot_context_get_generic_signed_key) (
+ struct crypt_device *cd,
+ struct crypt_keyslot_context *kc,
+ struct volume_key **r_vk,
+ struct volume_key **r_signature);
+
typedef int (*keyslot_context_get_passphrase) (
struct crypt_device *cd,
struct crypt_keyslot_context *kc,
@@ -71,6 +82,18 @@ struct crypt_keyslot_context {
const char *volume_key;
size_t volume_key_size;
} k;
+ struct {
+ const char *volume_key;
+ size_t volume_key_size;
+ const char *signature;
+ size_t signature_size;
+ } ks;
+ struct {
+ const char *key_description;
+ } kr;
+ struct {
+ const char *key_description;
+ } vk_kr;
} u;
int error;
@@ -78,10 +101,15 @@ struct crypt_keyslot_context {
char *i_passphrase;
size_t i_passphrase_size;
- keyslot_context_get_key get_luks2_key;
- keyslot_context_get_volume_key get_luks1_volume_key;
- keyslot_context_get_volume_key get_luks2_volume_key;
- keyslot_context_get_passphrase get_passphrase;
+ keyslot_context_get_key get_luks2_key;
+ keyslot_context_get_volume_key get_luks1_volume_key;
+ keyslot_context_get_volume_key get_luks2_volume_key;
+ keyslot_context_get_generic_volume_key get_plain_volume_key;
+ keyslot_context_get_generic_volume_key get_bitlk_volume_key;
+ keyslot_context_get_generic_volume_key get_fvault2_volume_key;
+ keyslot_context_get_generic_signed_key get_verity_volume_key;
+ keyslot_context_get_generic_volume_key get_integrity_volume_key;
+ keyslot_context_get_passphrase get_passphrase;
};
void crypt_keyslot_context_destroy_internal(struct crypt_keyslot_context *method);
@@ -90,6 +118,12 @@ void crypt_keyslot_unlock_by_key_init_internal(struct crypt_keyslot_context *kc,
const char *volume_key,
size_t volume_key_size);
+void crypt_keyslot_unlock_by_signed_key_init_internal(struct crypt_keyslot_context *kc,
+ const char *volume_key,
+ size_t volume_key_size,
+ const char *signature,
+ size_t signature_size);
+
void crypt_keyslot_unlock_by_passphrase_init_internal(struct crypt_keyslot_context *kc,
const char *passphrase,
size_t passphrase_size);
@@ -106,6 +140,12 @@ void crypt_keyslot_unlock_by_token_init_internal(struct crypt_keyslot_context *k
size_t pin_size,
void *usrptr);
+void crypt_keyslot_unlock_by_keyring_internal(struct crypt_keyslot_context *kc,
+ const char *key_description);
+
+void crypt_keyslot_unlock_by_vk_in_keyring_internal(struct crypt_keyslot_context *kc,
+ const char *key_description);
+
const char *keyslot_context_type_string(const struct crypt_keyslot_context *kc);
#endif /* KEYSLOT_CONTEXT_H */
diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h
index e899829..82d042f 100644
--- a/lib/libcryptsetup.h
+++ b/lib/libcryptsetup.h
@@ -3,8 +3,8 @@
*
* Copyright (C) 2004 Jana Saout <jana@saout.de>
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -273,7 +273,7 @@ struct crypt_pbkdf_type {
/** Iteration time set by crypt_set_iteration_time(), for compatibility only. */
#define CRYPT_PBKDF_ITER_TIME_SET (UINT32_C(1) << 0)
-/** Never run benchmarks, use pre-set value or defaults. */
+/** Never run benchmarks or limit by system resources, use pre-set values or defaults. */
#define CRYPT_PBKDF_NO_BENCHMARK (UINT32_C(1) << 1)
/** PBKDF2 according to RFC2898, LUKS1 legacy */
@@ -451,6 +451,34 @@ const char *crypt_get_type(struct crypt_device *cd);
const char *crypt_get_default_type(void);
/**
+ * @defgroup crypt-hw-encryption-types HW encryption type
+ * @addtogroup crypt-hw-encryption-types
+ * @{
+ */
+/** SW encryption, no OPAL encryption in place (default) */
+#define CRYPT_SW_ONLY INT16_C(0)
+/** OPAL HW encryption only (no SW encryption!) */
+#define CRYPT_OPAL_HW_ONLY INT16_C(1)
+/** SW encryption stacked over OPAL HW encryption */
+#define CRYPT_SW_AND_OPAL_HW INT16_C(2)
+/** @} */
+
+/**
+ * Get HW encryption type
+ *
+ * @return HW encryption type (see @link crypt-hw-encryption-types @endlink)
+ * or negative errno otherwise.
+ */
+int crypt_get_hw_encryption_type(struct crypt_device *cd);
+
+/**
+ * Get HW encryption (like OPAL) key size (in bytes)
+ *
+ * @return key size or 0 if no HW encryption is used.
+ */
+int crypt_get_hw_encryption_key_size(struct crypt_device *cd);
+
+/**
*
* Structure used as parameter for PLAIN device type.
*
@@ -609,6 +637,18 @@ struct crypt_params_luks2 {
const char *label; /**< header label or @e NULL*/
const char *subsystem; /**< header subsystem label or @e NULL*/
};
+
+/**
+ * Structure used as parameter for OPAL (HW encrypted) device type.
+ *
+ * @see crypt_format_luks2_opal
+ *
+ */
+struct crypt_params_hw_opal {
+ const char *admin_key; /**< admin key */
+ size_t admin_key_size; /**< admin key size in bytes */
+ size_t user_key_size; /**< user authority key size part in bytes */
+};
/** @} */
/**
@@ -649,6 +689,34 @@ int crypt_format(struct crypt_device *cd,
void *params);
/**
+ * Create (format) new LUKS2 crypt device over HW OPAL device but do not activate it.
+ *
+ * @pre @e cd contains initialized and not formatted device context (device type must @b not be set)
+ *
+ * @param cd crypt device handle
+ * @param cipher for SW encryption (e.g. "aes") or NULL for HW encryption only
+ * @param cipher_mode including IV specification (e.g. "xts-plain") or NULL for HW encryption only
+ * @param uuid requested UUID or @e NULL if it should be generated
+ * @param volume_keys pre-generated volume keys or @e NULL if it should be generated (only for LUKS2 SW encryption)
+ * @param volume_keys_size size of volume keys in bytes (only for SW encryption).
+ * @param params LUKS2 crypt type specific parameters (see @link crypt-type @endlink)
+ * @param opal_params OPAL specific parameters
+ *
+ * @returns @e 0 on success or negative errno value otherwise.
+ *
+ * @note Note that crypt_format_luks2_opal does not create LUKS keyslot.
+ * To create keyslot call any crypt_keyslot_add_* function.
+ */
+int crypt_format_luks2_opal(struct crypt_device *cd,
+ const char *cipher,
+ const char *cipher_mode,
+ const char *uuid,
+ const char *volume_keys,
+ size_t volume_keys_size,
+ struct crypt_params_luks2 *params,
+ struct crypt_params_hw_opal *opal_params);
+
+/**
* Set format compatibility flags.
*
* @param cd crypt device handle
@@ -941,6 +1009,23 @@ int crypt_resume_by_token_pin(struct crypt_device *cd,
const char *pin,
size_t pin_size,
void *usrptr);
+
+/**
+ * Resume crypt device using keyslot context.
+ *
+ * @param cd crypt device handle
+ * @param name name of device to resume
+ * @param keyslot requested keyslot to check or @e CRYPT_ANY_SLOT, keyslot is
+ * ignored for unlock methods not based on passphrase
+ * @param kc keyslot context providing volume key or passphrase.
+ *
+ * @return unlocked key slot number for passphrase-based unlock, zero for other
+ * unlock methods (e.g. volume key context) or negative errno on error.
+ */
+int crypt_resume_by_keyslot_context(struct crypt_device *cd,
+ const char *name,
+ int keyslot,
+ struct crypt_keyslot_context *kc);
/** @} */
/**
@@ -1099,7 +1184,7 @@ int crypt_keyslot_add_by_volume_key(struct crypt_device *cd,
* @warning CRYPT_VOLUME_KEY_SET flag force updates volume key. It is @b not @b reencryption!
* By doing so you will most probably destroy your ciphertext data device. It's supposed
* to be used only in wrapped keys scheme for key refresh process where real (inner) volume
- * key stays untouched. It may be involed on active @e keyslot which makes the (previously
+ * key stays untouched. It may be involved on active @e keyslot which makes the (previously
* unbound) keyslot new regular keyslot.
*/
int crypt_keyslot_add_by_key(struct crypt_device *cd,
@@ -1195,6 +1280,59 @@ int crypt_keyslot_context_init_by_volume_key(struct crypt_device *cd,
struct crypt_keyslot_context **kc);
/**
+ * Initialize keyslot context via signed key.
+ *
+ * @param cd crypt device handle initialized to device context
+ *
+ * @param volume_key provided volume key
+ * @param volume_key_size size of volume_key
+ * @param signature buffer with signature for the key
+ * @param signature_size bsize of signature buffer
+ * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_SIGNED_KEY
+ *
+ * @return zero on success or negative errno otherwise.
+ *
+ * @note currently supported only with VERITY devices.
+ */
+int crypt_keyslot_context_init_by_signed_key(struct crypt_device *cd,
+ const char *volume_key,
+ size_t volume_key_size,
+ const char *signature,
+ size_t signature_size,
+ struct crypt_keyslot_context **kc);
+
+/**
+ * Initialize keyslot context via passphrase stored in a keyring.
+ *
+ * @param cd crypt device handle initialized to LUKS device context
+ *
+ * @param key_description kernel keyring key description library should look
+ * for passphrase in
+ * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_KEYRING
+ *
+ * @return zero on success or negative errno otherwise.
+ */
+int crypt_keyslot_context_init_by_keyring(struct crypt_device *cd,
+ const char *key_description,
+ struct crypt_keyslot_context **kc);
+
+/**
+ * Initialize keyslot context via volume key stored in a keyring.
+ *
+ * @param cd crypt device handle initialized to LUKS device context
+ *
+ * @param key_description kernel keyring key description library should look
+ * for passphrase in. The key can be passed either as number in ASCII,
+ * or a text representation in the form "%<key_type>:<key_name>"
+ * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_KEYRING
+ *
+ * @return zero on success or negative errno otherwise.
+ */
+int crypt_keyslot_context_init_by_vk_in_keyring(struct crypt_device *cd,
+ const char *key_description,
+ struct crypt_keyslot_context **kc);
+
+/**
* Get error code per keyslot context from last failed call.
*
* @note If @link crypt_keyslot_add_by_keyslot_context @endlink passed with
@@ -1225,7 +1363,7 @@ int crypt_keyslot_context_set_pin(struct crypt_device *cd,
struct crypt_keyslot_context *kc);
/**
- * @defgroup crypt-keyslot-context-types Crypt keyslot context
+ * @defgroup crypt-keyslot-context-types Crypt keyslot context types
* @addtogroup crypt-keyslot-context-types
* @{
*/
@@ -1237,6 +1375,16 @@ int crypt_keyslot_context_set_pin(struct crypt_device *cd,
#define CRYPT_KC_TYPE_TOKEN INT16_C(3)
/** keyslot context initialized by volume key or unbound key (@link crypt_keyslot_context_init_by_volume_key @endlink) */
#define CRYPT_KC_TYPE_KEY INT16_C(4)
+/** keyslot context initialized by description of a keyring key
+ * (@link crypt_keyslot_context_init_by_keyring @endlink)
+ */
+#define CRYPT_KC_TYPE_KEYRING INT16_C(5)
+/** keyslot context initialized by description of a keyring key containing the volume key
+ * (@link crypt_keyslot_context_init_by_vk_in_keyring @endlink)
+ */
+#define CRYPT_KC_TYPE_VK_KEYRING INT16_C(6)
+/** keyslot context initialized by signed key (@link crypt_keyslot_context_init_by_signed_key @endlink) */
+#define CRYPT_KC_TYPE_SIGNED_KEY INT16_C(7)
/** @} */
/**
@@ -1281,7 +1429,7 @@ int crypt_keyslot_context_get_type(const struct crypt_keyslot_context *kc);
* @warning CRYPT_VOLUME_KEY_SET flag force updates volume key. It is @b not @b reencryption!
* By doing so you will most probably destroy your ciphertext data device. It's supposed
* to be used only in wrapped keys scheme for key refresh process where real (inner) volume
- * key stays untouched. It may be involed on active @e keyslot which makes the (previously
+ * key stays untouched. It may be involved on active @e keyslot which makes the (previously
* unbound) keyslot new regular keyslot.
*/
int crypt_keyslot_add_by_keyslot_context(struct crypt_device *cd,
@@ -1420,6 +1568,8 @@ uint64_t crypt_get_active_integrity_failures(struct crypt_device *cd,
#define CRYPT_REQUIREMENT_OFFLINE_REENCRYPT (UINT32_C(1) << 0)
/** Online reencryption in-progress */
#define CRYPT_REQUIREMENT_ONLINE_REENCRYPT (UINT32_C(1) << 1)
+/** Device configured with OPAL support */
+#define CRYPT_REQUIREMENT_OPAL (UINT32_C(1) << 2)
/** unknown requirement in header (output only) */
#define CRYPT_REQUIREMENT_UNKNOWN (UINT32_C(1) << 31)
@@ -1474,6 +1624,39 @@ int crypt_persistent_flags_get(struct crypt_device *cd,
*/
/**
+ * Activate device or check using keyslot context. In some cases (device under
+ * reencryption), more than one keyslot context is required (e.g. one for the old
+ * volume key and one for the new volume key). The order of the keyslot
+ * contexts does not matter. When less keyslot contexts are supplied than
+ * required to unlock the device an -ESRCH error code is returned and you
+ * should call the function again with an additional keyslot context specified.
+ *
+ * NOTE: the API at the moment fully works for single keyslot context only,
+ * the additional keyslot context currently works only with
+ * @e CRYPT_KC_TYPE_VK_KEYRING or @e CRYPT_KC_TYPE_KEY contexts.
+ *
+ * @param cd crypt device handle
+ * @param name name of device to create, if @e NULL only check passphrase
+ * @param keyslot requested keyslot to check or @e CRYPT_ANY_SLOT, keyslot is
+ * ignored for unlock methods not based on passphrase
+ * @param kc keyslot context providing volume key or passphrase.
+ * @param additional_keyslot requested additional keyslot to check or @e CRYPT_ANY_SLOT
+ * @param additional_kc keyslot context providing additional volume key or
+ * passphrase (e.g. old volume key for device under reencryption).
+ * @param flags activation flags
+ *
+ * @return unlocked key slot number for passphrase-based unlock, zero for other
+ * unlock methods (e.g. volume key context) or negative errno on error.
+ */
+int crypt_activate_by_keyslot_context(struct crypt_device *cd,
+ const char *name,
+ int keyslot,
+ struct crypt_keyslot_context *kc,
+ int additional_keyslot,
+ struct crypt_keyslot_context *additional_kc,
+ uint32_t flags);
+
+/**
* Activate device or check passphrase.
*
* @param cd crypt device handle
@@ -1553,6 +1736,9 @@ int crypt_activate_by_keyfile(struct crypt_device *cd,
* CRYPT_ACTIVATE_READONLY flag always.
* @note For TCRYPT the volume key should be always NULL
* the key from decrypted header is used instead.
+ * @note For BITLK the name cannot be @e NULL checking volume key is not
+ * supported for BITLK, the device will be activated even if the
+ * provided key is not correct.
*/
int crypt_activate_by_volume_key(struct crypt_device *cd,
const char *name,
@@ -2259,6 +2445,36 @@ int crypt_wipe(struct crypt_device *cd,
/** Use direct-io */
#define CRYPT_WIPE_NO_DIRECT_IO (UINT32_C(1) << 0)
+
+enum {
+ CRYPT_LUKS2_SEGMENT = -2,
+ CRYPT_NO_SEGMENT = -1,
+};
+
+/**
+ * Safe erase of a partition or an entire OPAL device. WARNING: ALL DATA ON
+ * PARTITION/DISK WILL BE LOST. If the CRYPT_NO_SEGMENT is passed as the segment
+ * parameter, the entire device will be wiped, not just what is included in the
+ * LUKS2 device/partition.
+ *
+ * @param cd crypt device handle
+ * @param segment the segment number to wipe (0..8), or CRYPT_LUKS2_SEGMENT
+ * to wipe the segment configured in the LUKS2 header, or CRYPT_NO_SEGMENT
+ * to wipe the entire device via a factory reset.
+ * @param password admin password/PSID (for factory reset) to wipe the
+ * partition/device
+ * @param password_size length of password/PSID
+ * @param flags (currently unused)
+ *
+ * @return @e 0 on success or negative errno value otherwise.
+ */
+int crypt_wipe_hw_opal(struct crypt_device *cd,
+ int segment, /* 0..8, CRYPT_LUKS2_SEGMENT -2, CRYPT_NO_SEGMENT -1 */
+ const char *password, /* Admin1 PIN or PSID */
+ size_t password_size,
+ uint32_t flags /* currently unused */
+);
+
/** @} */
/**
@@ -2567,6 +2783,17 @@ int crypt_token_register(const crypt_token_handler *handler);
const char *crypt_token_external_path(void);
/**
+ * Override configured external token handlers path for the library.
+ *
+ * @param path Absolute path (starts with '/') to new external token handlers directory or @e NULL.
+ *
+ * @note if @e path is @e NULL the external token path is reset to default path.
+ *
+ * @return @e 0 on success or negative errno value otherwise.
+ */
+int crypt_token_set_external_path(const char *path);
+
+/**
* Disable external token handlers (plugins) support
* If disabled, it cannot be enabled again.
*/
@@ -2875,6 +3102,55 @@ void crypt_safe_memzero(void *data, size_t size);
/** @} */
+/**
+ * @defgroup crypt-keyring Kernel keyring manipulation
+ * @addtogroup crypt-keyring
+ * @{
+ */
+
+/**
+ * Link the volume key to the specified kernel keyring.
+ *
+ * The volume can have one or two keys. Normally, the device has one key.
+ * However if reencryption was started and not finished yet, the volume will
+ * have two volume keys (the new VK for the already reencrypted segment and old
+ * VK for the not yet reencrypted segment).
+ *
+ * The @e old_key_description argument is required only for
+ * devices that are in re-encryption and have two volume keys at the same time
+ * (old and new). You can set the @e old_key_description to NULL,
+ * but if you supply number of keys less than required, the function will
+ * return -ESRCH. In that case you need to call the function again and set
+ * the missing key description. When supplying just one key description, make
+ * sure to supply it in the @e key_description.
+ *
+ * @param cd crypt device handle
+ * @param key_description the key description of the volume key linked in desired keyring.
+ * @param old_key_description the key description of the old volume key linked in desired keyring
+ * (for devices in re-encryption).
+ * @param key_type_desc the key type used for the volume key. Currently only "user" and "logon" types are
+ * supported. if @e NULL is specified the default "user" type is applied.
+ * @param keyring_to_link_vk the keyring description of the keyring in which volume key should
+ * be linked, if @e NULL is specified, linking will be disabled.
+ *
+ * @note keyring_to_link_vk may be passed in various string formats:
+ * It can be kernel key numeric id of existing keyring written as a string,
+ * keyring name prefixed optionally be either "%:" or "%keyring:" substrings or keyctl
+ * special values for keyrings "@t", "@p", "@s" and so on. See keyctl(1) man page,
+ * section KEY IDENTIFIERS for more information. All other prefixes starting "%<type>:"
+ * are ignored.
+ *
+ * @note key_description "%<type>:" prefixes are ignored. Type is applied based on key_type parameter
+ * value.
+ */
+int crypt_set_keyring_to_link(struct crypt_device* cd,
+ const char* key_description,
+ const char* old_key_description,
+ const char* key_type_desc,
+ const char* keyring_to_link_vk);
+
+/** @} */
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/libcryptsetup.sym b/lib/libcryptsetup.sym
index d0f0d98..89d6468 100644
--- a/lib/libcryptsetup.sym
+++ b/lib/libcryptsetup.sym
@@ -165,3 +165,18 @@ CRYPTSETUP_2.6 {
crypt_keyslot_add_by_keyslot_context;
crypt_volume_key_get_by_keyslot_context;
} CRYPTSETUP_2.5;
+
+CRYPTSETUP_2.7 {
+ global:
+ crypt_activate_by_keyslot_context;
+ crypt_format_luks2_opal;
+ crypt_get_hw_encryption_type;
+ crypt_get_hw_encryption_key_size;
+ crypt_keyslot_context_init_by_keyring;
+ crypt_keyslot_context_init_by_vk_in_keyring;
+ crypt_keyslot_context_init_by_signed_key;
+ crypt_resume_by_keyslot_context;
+ crypt_token_set_external_path;
+ crypt_set_keyring_to_link;
+ crypt_wipe_hw_opal;
+} CRYPTSETUP_2.6;
diff --git a/lib/libcryptsetup_macros.h b/lib/libcryptsetup_macros.h
index 55187ab..89c1e10 100644
--- a/lib/libcryptsetup_macros.h
+++ b/lib/libcryptsetup_macros.h
@@ -1,8 +1,8 @@
/*
* Definitions of common constant and generic macros of libcryptsetup
*
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/libcryptsetup_symver.h b/lib/libcryptsetup_symver.h
index a5aa8f9..3ea31bf 100644
--- a/lib/libcryptsetup_symver.h
+++ b/lib/libcryptsetup_symver.h
@@ -1,7 +1,7 @@
/*
* Helpers for defining versioned symbols
*
- * Copyright (C) 2021-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2021-2024 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c
index 9c5fc0c..ebee542 100644
--- a/lib/libdevmapper.c
+++ b/lib/libdevmapper.c
@@ -3,8 +3,8 @@
*
* Copyright (C) 2004 Jana Saout <jana@saout.de>
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -602,7 +602,8 @@ static char *get_dm_crypt_params(const struct dm_target *tgt, uint32_t flags)
hexkey = crypt_safe_alloc(keystr_len);
if (!hexkey)
goto out;
- r = snprintf(hexkey, keystr_len, ":%zu:logon:%s", tgt->u.crypt.vk->keylength, tgt->u.crypt.vk->key_description);
+ r = snprintf(hexkey, keystr_len, ":%zu:logon:%s", tgt->u.crypt.vk->keylength,
+ tgt->u.crypt.vk->key_description);
if (r < 0 || r >= keystr_len)
goto out;
} else
@@ -1330,7 +1331,15 @@ static int _dm_create_device(struct crypt_device *cd, const char *name, const ch
goto out;
if (!dm_task_run(dmt)) {
- r = dm_status_device(cd, name);;
+
+ r = -dm_task_get_errno(dmt);
+ if (r == -ENOKEY || r == -EKEYREVOKED || r == -EKEYEXPIRED) {
+ /* propagate DM errors around key management as such */
+ r = -ENOKEY;
+ goto out;
+ }
+
+ r = dm_status_device(cd, name);
if (r >= 0)
r = -EEXIST;
if (r != -EEXIST && r != -ENODEV)
@@ -1663,6 +1672,11 @@ int dm_create_device(struct crypt_device *cd, const char *name,
log_err(cd, _("Requested sector_size option is not supported."));
r = -EINVAL;
}
+ if (dmd->segment.u.crypt.sector_size > SECTOR_SIZE &&
+ dmd->size % dmd->segment.u.crypt.sector_size) {
+ log_err(cd, _("The device size is not multiple of the requested sector size."));
+ r = -EINVAL;
+ }
}
if (dmd->segment.type == DM_INTEGRITY && (dmd->flags & CRYPT_ACTIVATE_RECALCULATE) &&
@@ -2829,7 +2843,7 @@ static int _process_deps(struct crypt_device *cd, const char *prefix, struct dm_
int dm_device_deps(struct crypt_device *cd, const char *name, const char *prefix,
char **names, size_t names_length)
{
- struct dm_task *dmt;
+ struct dm_task *dmt = NULL;
struct dm_info dmi;
struct dm_deps *deps;
int r = -EINVAL;
@@ -2989,7 +3003,8 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
}
if (vk->key_description) {
- r = snprintf(msg, msg_size, "key set :%zu:logon:%s", vk->keylength, vk->key_description);
+ r = snprintf(msg, msg_size, "key set :%zu:logon:%s", vk->keylength,
+ vk->key_description);
} else {
key = crypt_bytes_to_hex(vk->keylength, vk->key);
if (!key) {
@@ -3026,6 +3041,18 @@ const char *dm_get_dir(void)
return dm_dir();
}
+int dm_get_iname(const char *name, char **iname, bool with_path)
+{
+ int r;
+
+ if (with_path)
+ r = asprintf(iname, "%s/%s_dif", dm_get_dir(), name);
+ else
+ r = asprintf(iname, "%s_dif", name);
+
+ return r < 0 ? -ENOMEM : 0;
+}
+
int dm_is_dm_device(int major)
{
return dm_is_dm_major((uint32_t)major);
diff --git a/lib/loopaes/loopaes.c b/lib/loopaes/loopaes.c
index 224d3d0..4ff4fc9 100644
--- a/lib/loopaes/loopaes.c
+++ b/lib/loopaes/loopaes.c
@@ -1,8 +1,8 @@
/*
* loop-AES compatible volume handling
*
- * Copyright (C) 2011-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2011-2023 Milan Broz
+ * Copyright (C) 2011-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2011-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/loopaes/loopaes.h b/lib/loopaes/loopaes.h
index a921694..fe9e71c 100644
--- a/lib/loopaes/loopaes.h
+++ b/lib/loopaes/loopaes.h
@@ -1,8 +1,8 @@
/*
* loop-AES compatible volume handling
*
- * Copyright (C) 2011-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2011-2023 Milan Broz
+ * Copyright (C) 2011-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2011-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/luks1/af.c b/lib/luks1/af.c
index 76afeac..cafa468 100644
--- a/lib/luks1/af.c
+++ b/lib/luks1/af.c
@@ -2,7 +2,7 @@
* AFsplitter - Anti forensic information splitter
*
* Copyright (C) 2004 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
*
* AFsplitter diffuses information over a large stripe of data,
* therefore supporting secure data destruction.
diff --git a/lib/luks1/af.h b/lib/luks1/af.h
index 8a2bceb..efc1133 100644
--- a/lib/luks1/af.h
+++ b/lib/luks1/af.h
@@ -2,7 +2,7 @@
* AFsplitter - Anti forensic information splitter
*
* Copyright (C) 2004 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
*
* AFsplitter diffuses information over a large stripe of data,
* therefore supporting secure data destruction.
diff --git a/lib/luks1/keyencryption.c b/lib/luks1/keyencryption.c
index c1c8201..64fdf2d 100644
--- a/lib/luks1/keyencryption.c
+++ b/lib/luks1/keyencryption.c
@@ -2,8 +2,8 @@
* LUKS - Linux Unified Key Setup
*
* Copyright (C) 2004-2006 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2012-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/luks1/keymanage.c b/lib/luks1/keymanage.c
index fe49a00..24ab160 100644
--- a/lib/luks1/keymanage.c
+++ b/lib/luks1/keymanage.c
@@ -2,8 +2,8 @@
* LUKS - Linux Unified Key Setup
*
* Copyright (C) 2004-2006 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2013-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2013-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/luks1/luks.h b/lib/luks1/luks.h
index 9c3f386..74cb7a7 100644
--- a/lib/luks1/luks.h
+++ b/lib/luks1/luks.h
@@ -2,7 +2,7 @@
* LUKS - Linux Unified Key Setup
*
* Copyright (C) 2004-2006 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/luks2/hw_opal/hw_opal.c b/lib/luks2/hw_opal/hw_opal.c
new file mode 100644
index 0000000..31ef87e
--- /dev/null
+++ b/lib/luks2/hw_opal/hw_opal.c
@@ -0,0 +1,1089 @@
+/*
+ * OPAL utilities
+ *
+ * Copyright (C) 2022-2023 Luca Boccassi <bluca@debian.org>
+ * 2023 Ondrej Kozina <okozina@redhat.com>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_SYS_SYSMACROS_H
+# include <sys/sysmacros.h> /* for major, minor */
+#endif
+
+#include "internal.h"
+#include "libcryptsetup.h"
+#include "luks2/hw_opal/hw_opal.h"
+#include "utils_device_locking.h"
+
+#if HAVE_HW_OPAL
+
+#include <linux/sed-opal.h>
+
+/* Error codes are defined in the specification:
+ * TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
+ * Section 5.1.5: Method Status Codes
+ * Names and values from table 166 */
+typedef enum OpalStatus {
+ OPAL_STATUS_SUCCESS,
+ OPAL_STATUS_NOT_AUTHORIZED,
+ OPAL_STATUS_OBSOLETE0, /* Undefined but possible return values are called 'obsolete' */
+ OPAL_STATUS_SP_BUSY,
+ OPAL_STATUS_SP_FAILED,
+ OPAL_STATUS_SP_DISABLED,
+ OPAL_STATUS_SP_FROZEN,
+ OPAL_STATUS_NO_SESSIONS_AVAILABLE,
+ OPAL_STATUS_UNIQUENESS_CONFLICT,
+ OPAL_STATUS_INSUFFICIENT_SPACE,
+ OPAL_STATUS_INSUFFICIENT_ROWS,
+ OPAL_STATUS_INVALID_PARAMETER,
+ OPAL_STATUS_OBSOLETE1,
+ OPAL_STATUS_OBSOLETE2,
+ OPAL_STATUS_TPER_MALFUNCTION,
+ OPAL_STATUS_TRANSACTION_FAILURE,
+ OPAL_STATUS_RESPONSE_OVERFLOW,
+ OPAL_STATUS_AUTHORITY_LOCKED_OUT,
+ OPAL_STATUS_FAIL = 0x3F, /* As defined by specification */
+ _OPAL_STATUS_MAX,
+ _OPAL_STATUS_INVALID = -EINVAL,
+} OpalStatus;
+
+static const char* const opal_status_table[_OPAL_STATUS_MAX] = {
+ [OPAL_STATUS_SUCCESS] = "success",
+ [OPAL_STATUS_NOT_AUTHORIZED] = "not authorized",
+ [OPAL_STATUS_OBSOLETE0] = "obsolete",
+ [OPAL_STATUS_SP_BUSY] = "SP busy",
+ [OPAL_STATUS_SP_FAILED] = "SP failed",
+ [OPAL_STATUS_SP_DISABLED] = "SP disabled",
+ [OPAL_STATUS_SP_FROZEN] = "SP frozen",
+ [OPAL_STATUS_NO_SESSIONS_AVAILABLE] = "no sessions available",
+ [OPAL_STATUS_UNIQUENESS_CONFLICT] = "uniqueness conflict",
+ [OPAL_STATUS_INSUFFICIENT_SPACE] = "insufficient space",
+ [OPAL_STATUS_INSUFFICIENT_ROWS] = "insufficient rows",
+ [OPAL_STATUS_INVALID_PARAMETER] = "invalid parameter",
+ [OPAL_STATUS_OBSOLETE1] = "obsolete",
+ [OPAL_STATUS_OBSOLETE2] = "obsolete",
+ [OPAL_STATUS_TPER_MALFUNCTION] = "TPer malfunction",
+ [OPAL_STATUS_TRANSACTION_FAILURE] = "transaction failure",
+ [OPAL_STATUS_RESPONSE_OVERFLOW] = "response overflow",
+ [OPAL_STATUS_AUTHORITY_LOCKED_OUT] = "authority locked out",
+ [OPAL_STATUS_FAIL] = "unknown failure",
+};
+
+static const char *opal_status_to_string(int t)
+{
+ if (t < 0)
+ return strerror(-t);
+
+ if (t >= _OPAL_STATUS_MAX)
+ return "unknown error";
+
+ return opal_status_table[t];
+}
+
+static const char *opal_ioctl_to_string(unsigned long rq)
+{
+ switch(rq) {
+ case IOC_OPAL_GET_STATUS: return "GET_STATUS";
+ case IOC_OPAL_GET_GEOMETRY: return "GET_GEOMETRY";
+ case IOC_OPAL_GET_LR_STATUS: return "GET_LR_STATUS";
+ case IOC_OPAL_TAKE_OWNERSHIP: return "TAKE_OWNERSHIP";
+ case IOC_OPAL_ACTIVATE_USR: return "ACTIVATE_USR";
+ case IOC_OPAL_ACTIVATE_LSP: return "ACTIVATE_LSP";
+ case IOC_OPAL_ERASE_LR: return "ERASE_LR";
+ case IOC_OPAL_SECURE_ERASE_LR: return "SECURE_ERASE_LR";
+ case IOC_OPAL_ADD_USR_TO_LR: return "ADD_USR_TO_LR";
+ case IOC_OPAL_SET_PW: return "SET_PW";
+ case IOC_OPAL_LR_SETUP: return "LR_SETUP";
+ case IOC_OPAL_LOCK_UNLOCK: return "LOCK_UNLOCK";
+ case IOC_OPAL_SAVE: return "SAVE";
+ case IOC_OPAL_PSID_REVERT_TPR: return "PSID_REVERT_TPR";
+ }
+
+ assert(false && "unknown OPAL ioctl");
+ return NULL;
+}
+
+static void opal_ioctl_debug(struct crypt_device *cd,
+ unsigned long rq,
+ void *args,
+ bool post,
+ int ret)
+{
+ const char *cmd = opal_ioctl_to_string(rq);
+
+ if (ret) {
+ log_dbg(cd, "OPAL %s failed: %s", cmd, opal_status_to_string(ret));
+ return;
+ }
+
+ if (post) switch(rq) {
+ case IOC_OPAL_GET_STATUS: { /* OUT */
+ struct opal_status *st = args;
+ log_dbg(cd, "OPAL %s: flags:%" PRIu32, cmd, st->flags);
+ };
+ break;
+ case IOC_OPAL_GET_GEOMETRY: { /* OUT */
+ struct opal_geometry *geo = args;
+ log_dbg(cd, "OPAL %s: align:%" PRIu8 ", lb_size:%" PRIu32 ", gran:%" PRIu64 ", lowest_lba:%" PRIu64,
+ cmd, geo->align, geo->logical_block_size, geo->alignment_granularity, geo->lowest_aligned_lba);
+ };
+ break;
+ case IOC_OPAL_GET_LR_STATUS: { /* OUT */
+ struct opal_lr_status *lrs = args;
+ log_dbg(cd, "OPAL %s: sum:%" PRIu32 ", who:%" PRIu32 ", lr:%" PRIu8
+ ", start:%" PRIu64 ", length:%" PRIu64 ", rle:%" PRIu32 ", rwe:%" PRIu32 ", state:%" PRIu32,
+ cmd, lrs->session.sum, lrs->session.who, lrs->session.opal_key.lr,
+ lrs->range_start, lrs->range_length, lrs->RLE, lrs->WLE, lrs->l_state);
+ };
+ break;
+ } else switch (rq) {
+ case IOC_OPAL_TAKE_OWNERSHIP: { /* IN */
+ log_dbg(cd, "OPAL %s", cmd);
+ };
+ break;
+ case IOC_OPAL_ACTIVATE_USR: { /* IN */
+ struct opal_session_info *ui = args;
+ log_dbg(cd, "OPAL %s: sum:%" PRIu32 ", who:%" PRIu32 ", lr:%" PRIu8,
+ cmd, ui->sum, ui->who, ui->opal_key.lr);
+ };
+ break;
+ case IOC_OPAL_ACTIVATE_LSP: { /* IN */
+ struct opal_lr_act *act = args;
+ log_dbg(cd, "OPAL %s: k.lr:%" PRIu8 ", sum:%" PRIu32 ", num_lrs:%" PRIu8 ", lr:"
+ "%"PRIu8"|%"PRIu8"|%"PRIu8"|%"PRIu8"|%"PRIu8"|%"PRIu8"|%"PRIu8"|%"PRIu8"|%"PRIu8,
+ cmd, act->key.lr, act->sum, act->num_lrs,
+ act->lr[0], act->lr[1], act->lr[2], act->lr[3], act->lr[4],
+ act->lr[5], act->lr[6], act->lr[7], act->lr[8]);
+ };
+ break;
+ case IOC_OPAL_ERASE_LR: { /* IN */
+ struct opal_session_info *ui = args;
+ log_dbg(cd, "OPAL %s: sum:%" PRIu32 ", who:%" PRIu32 ", lr:%" PRIu8,
+ cmd, ui->sum, ui->who, ui->opal_key.lr);
+ };
+ break;
+ case IOC_OPAL_SECURE_ERASE_LR: { /* IN */
+ struct opal_session_info *ui = args;
+ log_dbg(cd, "OPAL %s: sum:%" PRIu32 ", who:%" PRIu32 ", lr:%" PRIu8,
+ cmd, ui->sum, ui->who, ui->opal_key.lr);
+ };
+ break;
+ case IOC_OPAL_ADD_USR_TO_LR: { /* IN */
+ struct opal_lock_unlock *lu = args;
+ log_dbg(cd, "OPAL %s: sum:%" PRIu32 ", who:%" PRIu32 ", lr:%" PRIu8
+ ", l_state:%" PRIu32 ", flags:%" PRIu16,
+ cmd, lu->session.sum, lu->session.who, lu->session.opal_key.lr,
+ lu->l_state, lu->flags);
+ };
+ break;
+ case IOC_OPAL_SET_PW: { /* IN */
+ struct opal_new_pw *pw = args;
+ log_dbg(cd, "OPAL %s: sum:%" PRIu32 ", who:%" PRIu32 ", lr:%" PRIu8,
+ cmd, pw->session.sum, pw->session.who, pw->session.opal_key.lr);
+ };
+ break;
+ case IOC_OPAL_LR_SETUP: { /* IN */
+ struct opal_user_lr_setup *lrs = args;
+ log_dbg(cd, "OPAL %s: sum:%" PRIu32 ", who:%" PRIu32 ", lr:%" PRIu8
+ ", start:%" PRIu64 ", length:%" PRIu64 ", rle:%" PRIu32 ", rwe:%" PRIu32,
+ cmd, lrs->session.sum, lrs->session.who, lrs->session.opal_key.lr,
+ lrs->range_start, lrs->range_length, lrs->RLE, lrs->WLE);
+ };
+ break;
+ case IOC_OPAL_LOCK_UNLOCK: { /* IN */
+ struct opal_lock_unlock *lu = args;
+ log_dbg(cd, "OPAL %s: sum:%" PRIu32 ", who:%" PRIu32 ", lr:%" PRIu8
+ ", l_state:%" PRIu32 ", flags:%" PRIu16,
+ cmd, lu->session.sum, lu->session.who, lu->session.opal_key.lr,
+ lu->l_state, lu->flags);
+ };
+ break;
+ case IOC_OPAL_SAVE: { /* IN */
+ struct opal_lock_unlock *lu = args;
+ log_dbg(cd, "OPAL %s: sum:%" PRIu32 ", who:%" PRIu32 ", lr:%" PRIu8
+ ", l_state:%" PRIu32 ", flags:%" PRIu16,
+ cmd, lu->session.sum, lu->session.who, lu->session.opal_key.lr,
+ lu->l_state, lu->flags);
+ };
+ break;
+ case IOC_OPAL_PSID_REVERT_TPR: { /* IN */
+ struct opal_key *key = args;
+ log_dbg(cd, "OPAL %s: lr:%" PRIu8,
+ cmd, key->lr);
+ };
+ break;
+ }
+}
+
+static int opal_ioctl(struct crypt_device *cd, int fd, unsigned long rq, void *args)
+{
+ int r;
+
+ opal_ioctl_debug(cd, rq, args, false, 0);
+ r = ioctl(fd, rq, args);
+ opal_ioctl_debug(cd, rq, args, true, r);
+
+ return r;
+}
+
+static int opal_geometry_fd(struct crypt_device *cd,
+ int fd,
+ bool *ret_align,
+ uint32_t *ret_block_size,
+ uint64_t *ret_alignment_granularity_blocks,
+ uint64_t *ret_lowest_lba_blocks)
+{
+ int r;
+ struct opal_geometry geo;
+
+ assert(fd >= 0);
+
+ r = opal_ioctl(cd, fd, IOC_OPAL_GET_GEOMETRY, &geo);
+ if (r != OPAL_STATUS_SUCCESS)
+ return r;
+
+ if (ret_align)
+ *ret_align = (geo.align == 1);
+ if (ret_block_size)
+ *ret_block_size = geo.logical_block_size;
+ if (ret_alignment_granularity_blocks)
+ *ret_alignment_granularity_blocks = geo.alignment_granularity;
+ if (ret_lowest_lba_blocks)
+ *ret_lowest_lba_blocks = geo.lowest_aligned_lba;
+
+ return r;
+}
+
+static int opal_range_check_attributes_fd(struct crypt_device *cd,
+ int fd,
+ uint32_t segment_number,
+ const struct volume_key *vk,
+ const uint64_t *check_offset_sectors,
+ const uint64_t *check_length_sectors,
+ bool *check_read_locked,
+ bool *check_write_locked,
+ bool *ret_read_locked,
+ bool *ret_write_locked)
+{
+ int r;
+ struct opal_lr_status *lrs;
+ uint32_t opal_block_bytes = 0;
+ uint64_t offset, length;
+ bool read_locked, write_locked;
+
+ assert(fd >= 0);
+ assert(cd);
+ assert(vk);
+
+ if (check_offset_sectors || check_length_sectors) {
+ r = opal_geometry_fd(cd, fd, NULL, &opal_block_bytes, NULL, NULL);
+ if (r != OPAL_STATUS_SUCCESS)
+ return -EINVAL;
+ }
+
+ lrs = crypt_safe_alloc(sizeof(*lrs));
+ if (!lrs)
+ return -ENOMEM;
+
+ *lrs = (struct opal_lr_status) {
+ .session = {
+ .who = segment_number + 1,
+ .opal_key = {
+ .key_len = vk->keylength,
+ .lr = segment_number
+ }
+ }
+ };
+ memcpy(lrs->session.opal_key.key, vk->key, vk->keylength);
+
+ r = opal_ioctl(cd, fd, IOC_OPAL_GET_LR_STATUS, lrs);
+ if (r != OPAL_STATUS_SUCCESS) {
+ log_dbg(cd, "Failed to get locking range status on device '%s'.",
+ crypt_get_device_name(cd));
+ r = -EINVAL;
+ goto out;
+ }
+
+ r = 0;
+
+ if (check_offset_sectors) {
+ offset = lrs->range_start * opal_block_bytes / SECTOR_SIZE;
+ if (offset != *check_offset_sectors) {
+ log_err(cd, _("OPAL range %d offset %" PRIu64 " does not match expected values %" PRIu64 "."),
+ segment_number, offset, *check_offset_sectors);
+ r = -EINVAL;
+ }
+ }
+
+ if (check_length_sectors) {
+ length = lrs->range_length * opal_block_bytes / SECTOR_SIZE;
+ if (length != *check_length_sectors) {
+ log_err(cd, _("OPAL range %d length %" PRIu64" does not match device length %" PRIu64 "."),
+ segment_number, length, *check_length_sectors);
+ r = -EINVAL;
+ }
+ }
+
+ if (!lrs->RLE || !lrs->WLE) {
+ log_err(cd, _("OPAL range %d locking is disabled."), segment_number);
+ r = -EINVAL;
+ }
+
+ read_locked = (lrs->l_state == OPAL_LK);
+ write_locked = !!(lrs->l_state & (OPAL_RO | OPAL_LK));
+
+ if (check_read_locked && (read_locked != *check_read_locked)) {
+ log_dbg(cd, "OPAL range %d read lock is %slocked.",
+ segment_number, *check_read_locked ? "" : "not ");
+ log_err(cd, _("Unexpected OPAL range %d lock state."), segment_number);
+ r = -EINVAL;
+ }
+
+ if (check_write_locked && (write_locked != *check_write_locked)) {
+ log_dbg(cd, "OPAL range %d write lock is %slocked.",
+ segment_number, *check_write_locked ? "" : "not ");
+ log_err(cd, _("Unexpected OPAL range %d lock state."), segment_number);
+ r = -EINVAL;
+ }
+
+ if (ret_read_locked)
+ *ret_read_locked = read_locked;
+ if (ret_write_locked)
+ *ret_write_locked = write_locked;
+out:
+ crypt_safe_free(lrs);
+
+ return r;
+}
+
+static int opal_query_status(struct crypt_device *cd, struct device *dev, unsigned expected)
+{
+ struct opal_status st = { };
+ int fd, r;
+
+ assert(cd);
+ assert(dev);
+
+ fd = device_open(cd, dev, O_RDONLY);
+ if (fd < 0)
+ return -EIO;
+
+ r = opal_ioctl(cd, fd, IOC_OPAL_GET_STATUS, &st);
+
+ return r < 0 ? -EINVAL : (st.flags & expected) ? 1 : 0;
+}
+
+static int opal_enabled(struct crypt_device *cd, struct device *dev)
+{
+ return opal_query_status(cd, dev, OPAL_FL_LOCKING_ENABLED);
+}
+
+/* requires opal lock */
+int opal_setup_ranges(struct crypt_device *cd,
+ struct device *dev,
+ const struct volume_key *vk,
+ uint64_t range_start,
+ uint64_t range_length,
+ uint32_t segment_number,
+ 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_new_pw *new_pw = NULL;
+ struct opal_user_lr_setup *setup = NULL;
+ int r, fd;
+
+ assert(cd);
+ assert(dev);
+ assert(vk);
+ assert(admin_key);
+ assert(vk->keylength <= OPAL_KEY_MAX);
+
+ if (admin_key_len > OPAL_KEY_MAX)
+ return -EINVAL;
+
+ fd = device_open(cd, dev, O_RDONLY);
+ if (fd < 0)
+ return -EIO;
+
+ r = opal_enabled(cd, dev);
+ if (r < 0)
+ 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 },
+ };
+ 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,
+ },
+ };
+ memcpy(user_session->opal_key.key, admin_key, admin_key_len);
+
+ r = opal_ioctl(cd, fd, IOC_OPAL_ERASE_LR, user_session);
+ if (r != OPAL_STATUS_SUCCESS) {
+ log_dbg(cd, "Failed to reset (erase) OPAL locking range %u on device '%s': %s",
+ segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
+ 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;
+ goto out;
+ }
+ *user_session = (struct opal_session_info) {
+ .who = segment_number + 1,
+ .opal_key = {
+ .key_len = admin_key_len,
+ },
+ };
+ 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;
+ 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,
+ };
+ 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) {
+ r = -ENOMEM;
+ goto out;
+ }
+ *new_pw = (struct opal_new_pw) {
+ .session = {
+ .who = OPAL_ADMIN1,
+ .opal_key = {
+ .lr = segment_number,
+ .key_len = admin_key_len,
+ },
+ },
+ .new_user_pw = {
+ .who = segment_number + 1,
+ .opal_key = {
+ .key_len = vk->keylength,
+ .lr = segment_number,
+ },
+ },
+ };
+ memcpy(new_pw->new_user_pw.opal_key.key, vk->key, vk->keylength);
+ memcpy(new_pw->session.opal_key.key, admin_key, admin_key_len);
+
+ r = opal_ioctl(cd, fd, IOC_OPAL_SET_PW, new_pw);
+ if (r != OPAL_STATUS_SUCCESS) {
+ log_dbg(cd, "Failed to set OPAL user password on device '%s': (%d) %s",
+ crypt_get_device_name(cd), r, opal_status_to_string(r));
+ r = -EINVAL;
+ goto out;
+ }
+
+ setup = crypt_safe_alloc(sizeof(struct opal_user_lr_setup));
+ if (!setup) {
+ r = -ENOMEM;
+ goto out;
+ }
+ *setup = (struct opal_user_lr_setup) {
+ .range_start = range_start,
+ .range_length = range_length,
+ /* 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,
+ },
+ },
+ };
+ 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. */
+ lock = crypt_safe_alloc(sizeof(struct opal_lock_unlock));
+ if (!lock) {
+ r = -ENOMEM;
+ goto out;
+ }
+ *lock = (struct opal_lock_unlock) {
+ .l_state = OPAL_LK,
+ .session = {
+ .who = segment_number + 1,
+ .opal_key = {
+ .key_len = vk->keylength,
+ .lr = segment_number,
+ },
+ }
+ };
+ memcpy(lock->session.opal_key.key, vk->key, vk->keylength);
+
+ r = opal_ioctl(cd, fd, IOC_OPAL_LOCK_UNLOCK, lock);
+ if (r != OPAL_STATUS_SUCCESS) {
+ log_dbg(cd, "Failed to lock OPAL device '%s': %s",
+ crypt_get_device_name(cd), opal_status_to_string(r));
+ r = -EINVAL;
+ goto out;
+ }
+
+ /* Double check the locking range is locked and the ranges are set up as configured */
+ r = opal_range_check_attributes_fd(cd, fd, segment_number, vk, &range_start,
+ &range_length, &(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;
+}
+
+static int opal_lock_unlock(struct crypt_device *cd,
+ struct device *dev,
+ uint32_t segment_number,
+ const struct volume_key *vk,
+ bool lock)
+{
+ struct opal_lock_unlock unlock = {
+ .l_state = lock ? OPAL_LK : OPAL_RW,
+ .session = {
+ .who = segment_number + 1,
+ .opal_key = {
+ .lr = segment_number,
+ },
+ },
+ };
+ int r, fd;
+
+ if (opal_supported(cd, dev) <= 0)
+ return -ENOTSUP;
+ if (!lock && !vk)
+ return -EINVAL;
+
+ fd = device_open(cd, dev, O_RDONLY);
+ if (fd < 0)
+ return -EIO;
+
+ if (!lock) {
+ assert(vk->keylength <= OPAL_KEY_MAX);
+
+ unlock.session.opal_key.key_len = vk->keylength;
+ memcpy(unlock.session.opal_key.key, vk->key, vk->keylength);
+ }
+
+ r = opal_ioctl(cd, fd, IOC_OPAL_LOCK_UNLOCK, &unlock);
+ 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 %slock OPAL device '%s': permission denied",
+ lock ? "" : "un", crypt_get_device_name(cd));
+ goto out;
+ }
+ if (r != OPAL_STATUS_SUCCESS) {
+ log_dbg(cd, "Failed to %slock OPAL device '%s': %s",
+ lock ? "" : "un", crypt_get_device_name(cd), opal_status_to_string(r));
+ r = -EINVAL;
+ goto out;
+ }
+
+ /* If we are unlocking, also tell the kernel to automatically unlock when resuming
+ * from suspend, otherwise the drive will be locked and everything will go up in flames.
+ * Also set the flag to allow locking without having to pass the key again.
+ * But do not error out if this fails, as the device will already be unlocked.
+ *
+ * On a lock path we have to overwrite the cached key from kernel otherwise the locking range
+ * gets unlocked automatically after system resume even when cryptsetup previously locked it
+ * on purpose (crypt_deactivate* or crypt_suspend)
+ */
+ if (!lock)
+ unlock.flags = OPAL_SAVE_FOR_LOCK;
+
+ r = opal_ioctl(cd, fd, IOC_OPAL_SAVE, &unlock);
+ if (r != OPAL_STATUS_SUCCESS) {
+ if (!lock)
+ log_std(cd, "Failed to prepare OPAL device '%s' for sleep resume, be aware before suspending: %s",
+ crypt_get_device_name(cd), opal_status_to_string(r));
+ else
+ log_std(cd, "Failed to erase OPAL key for device '%s' from kernel: %s",
+ crypt_get_device_name(cd), opal_status_to_string(r));
+ r = 0;
+ }
+out:
+ if (!lock)
+ crypt_safe_memzero(unlock.session.opal_key.key, unlock.session.opal_key.key_len);
+
+ return r;
+}
+
+/* requires opal lock */
+int opal_lock(struct crypt_device *cd, struct device *dev, uint32_t segment_number)
+{
+ return opal_lock_unlock(cd, dev, segment_number, NULL, /* lock= */ true);
+}
+
+/* requires opal lock */
+int opal_unlock(struct crypt_device *cd,
+ struct device *dev,
+ uint32_t segment_number,
+ const struct volume_key *vk)
+{
+ return opal_lock_unlock(cd, dev, segment_number, vk, /* lock= */ false);
+}
+
+/*
+ * It does not require opal lock. This completely destroys
+ * data on whole OPAL block device. Serialization does not
+ * make sense here.
+ */
+int opal_factory_reset(struct crypt_device *cd,
+ struct device *dev,
+ const char *password,
+ size_t password_len)
+{
+ struct opal_key reset = {
+ .key_len = password_len,
+ };
+ int r, fd;
+
+ assert(cd);
+ assert(dev);
+ assert(password);
+
+ if (password_len > OPAL_KEY_MAX)
+ return -EINVAL;
+
+ fd = device_open(cd, dev, O_RDONLY);
+ if (fd < 0)
+ return -EIO;
+
+ memcpy(reset.key, password, password_len);
+
+ r = opal_ioctl(cd, fd, IOC_OPAL_PSID_REVERT_TPR, &reset);
+ 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 reset OPAL device '%s', incorrect PSID?",
+ crypt_get_device_name(cd));
+ goto out;
+ }
+ if (r != OPAL_STATUS_SUCCESS) {
+ r = -EINVAL;
+ log_dbg(cd, "Failed to reset OPAL device '%s' with PSID: %s",
+ crypt_get_device_name(cd), opal_status_to_string(r));
+ goto out;
+ }
+out:
+ crypt_safe_memzero(reset.key, reset.key_len);
+
+ return r;
+}
+
+/* requires opal lock */
+int opal_reset_segment(struct crypt_device *cd,
+ struct device *dev,
+ uint32_t segment_number,
+ const char *password,
+ size_t password_len)
+{
+ struct opal_session_info *user_session = NULL;
+ struct opal_user_lr_setup *setup = NULL;
+ int r, fd;
+
+ assert(cd);
+ assert(dev);
+ assert(password);
+
+ if (password_len > OPAL_KEY_MAX)
+ return -EINVAL;
+
+ if (opal_enabled(cd, dev) <= 0)
+ return -EINVAL;
+
+ user_session = crypt_safe_alloc(sizeof(struct opal_session_info));
+ if (!user_session)
+ return -ENOMEM;
+ *user_session = (struct opal_session_info) {
+ .who = OPAL_ADMIN1,
+ .opal_key = {
+ .lr = segment_number,
+ .key_len = password_len,
+ },
+ };
+ memcpy(user_session->opal_key.key, password, password_len);
+
+ fd = device_open(cd, dev, O_RDONLY);
+ if (fd < 0) {
+ r = -EIO;
+ goto out;
+ }
+
+ r = opal_ioctl(cd, fd, IOC_OPAL_ERASE_LR, user_session);
+ if (r != OPAL_STATUS_SUCCESS) {
+ log_dbg(cd, "Failed to reset (erase) OPAL locking range %u on device '%s': %s",
+ segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
+ 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;
+ }
+
+ /* Unlike IOC_OPAL_ERASE_LR, IOC_OPAL_SECURE_ERASE_LR does not disable the locking range,
+ * we have to do that by hand.
+ */
+ setup = crypt_safe_alloc(sizeof(struct opal_user_lr_setup));
+ if (!setup) {
+ r = -ENOMEM;
+ goto out;
+ }
+ *setup = (struct opal_user_lr_setup) {
+ .range_start = 0,
+ .range_length = 0,
+ .session = {
+ .who = OPAL_ADMIN1,
+ .opal_key = user_session->opal_key,
+ },
+ };
+
+ r = opal_ioctl(cd, fd, IOC_OPAL_LR_SETUP, setup);
+ if (r != OPAL_STATUS_SUCCESS) {
+ log_dbg(cd, "Failed to disable locking range on OPAL device '%s': %s",
+ crypt_get_device_name(cd), opal_status_to_string(r));
+ r = -EINVAL;
+ goto out;
+ }
+ }
+out:
+ crypt_safe_free(user_session);
+ crypt_safe_free(setup);
+
+ return r;
+}
+
+/*
+ * Does not require opal lock (immutable).
+ */
+int opal_supported(struct crypt_device *cd, struct device *dev)
+{
+ return opal_query_status(cd, dev, OPAL_FL_SUPPORTED|OPAL_FL_LOCKING_SUPPORTED);
+}
+
+/*
+ * Does not require opal lock (immutable).
+ */
+int opal_geometry(struct crypt_device *cd,
+ struct device *dev,
+ bool *ret_align,
+ uint32_t *ret_block_size,
+ uint64_t *ret_alignment_granularity_blocks,
+ uint64_t *ret_lowest_lba_blocks)
+{
+ int fd;
+
+ assert(cd);
+ assert(dev);
+
+ fd = device_open(cd, dev, O_RDONLY);
+ if (fd < 0)
+ return -EIO;
+
+ return opal_geometry_fd(cd, fd, ret_align, ret_block_size,
+ ret_alignment_granularity_blocks, ret_lowest_lba_blocks);
+}
+
+/* requires opal lock */
+int opal_range_check_attributes_and_get_lock_state(struct crypt_device *cd,
+ struct device *dev,
+ uint32_t segment_number,
+ const struct volume_key *vk,
+ const uint64_t *check_offset_sectors,
+ const uint64_t *check_length_sectors,
+ bool *ret_read_locked,
+ bool *ret_write_locked)
+{
+ int fd;
+
+ assert(cd);
+ assert(dev);
+ assert(vk);
+
+ fd = device_open(cd, dev, O_RDONLY);
+ if (fd < 0)
+ return -EIO;
+
+ return opal_range_check_attributes_fd(cd, fd, segment_number, vk,
+ check_offset_sectors, check_length_sectors, NULL,
+ NULL, ret_read_locked, ret_write_locked);
+}
+
+static int opal_lock_internal(struct crypt_device *cd, struct device *opal_device, struct crypt_lock_handle **opal_lock)
+{
+ char *lock_resource;
+ int devfd, r;
+ struct stat st;
+
+ if (!crypt_metadata_locking_enabled()) {
+ *opal_lock = NULL;
+ return 0;
+ }
+
+ /*
+ * This also asserts we do not hold any metadata lock on the same device to
+ * avoid deadlock (OPAL lock must be taken first)
+ */
+ devfd = device_open(cd, opal_device, O_RDONLY);
+ if (devfd < 0)
+ return -EINVAL;
+
+ if (fstat(devfd, &st) || !S_ISBLK(st.st_mode))
+ return -EINVAL;
+
+ r = asprintf(&lock_resource, "OPAL_%d:%d", major(st.st_rdev), minor(st.st_rdev));
+ if (r < 0)
+ return -ENOMEM;
+
+ r = crypt_write_lock(cd, lock_resource, true, opal_lock);
+
+ free(lock_resource);
+
+ return r;
+}
+
+int opal_exclusive_lock(struct crypt_device *cd, struct device *opal_device, struct crypt_lock_handle **opal_lock)
+{
+ if (!cd || !opal_device || (crypt_get_type(cd) && strcmp(crypt_get_type(cd), CRYPT_LUKS2)))
+ return -EINVAL;
+
+ return opal_lock_internal(cd, opal_device, opal_lock);
+}
+
+void opal_exclusive_unlock(struct crypt_device *cd, struct crypt_lock_handle *opal_lock)
+{
+ crypt_unlock_internal(cd, opal_lock);
+}
+
+#else
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+int opal_setup_ranges(struct crypt_device *cd,
+ struct device *dev,
+ const struct volume_key *vk,
+ uint64_t range_start,
+ uint64_t range_length,
+ uint32_t segment_number,
+ const void *admin_key,
+ size_t admin_key_len)
+{
+ return -ENOTSUP;
+}
+
+int opal_lock(struct crypt_device *cd, struct device *dev, uint32_t segment_number)
+{
+ return -ENOTSUP;
+}
+
+int opal_unlock(struct crypt_device *cd,
+ struct device *dev,
+ uint32_t segment_number,
+ const struct volume_key *vk)
+{
+ return -ENOTSUP;
+}
+
+int opal_supported(struct crypt_device *cd, struct device *dev)
+{
+ return -ENOTSUP;
+}
+
+int opal_factory_reset(struct crypt_device *cd,
+ struct device *dev,
+ const char *password,
+ size_t password_len)
+{
+ return -ENOTSUP;
+}
+
+int opal_reset_segment(struct crypt_device *cd,
+ struct device *dev,
+ uint32_t segment_number,
+ const char *password,
+ size_t password_len)
+{
+ return -ENOTSUP;
+}
+
+int opal_geometry(struct crypt_device *cd,
+ struct device *dev,
+ bool *ret_align,
+ uint32_t *ret_block_size,
+ uint64_t *ret_alignment_granularity_blocks,
+ uint64_t *ret_lowest_lba_blocks)
+{
+ return -ENOTSUP;
+}
+
+int opal_range_check_attributes_and_get_lock_state(struct crypt_device *cd,
+ struct device *dev,
+ uint32_t segment_number,
+ const struct volume_key *vk,
+ const uint64_t *check_offset_sectors,
+ const uint64_t *check_length_sectors,
+ bool *ret_read_locked,
+ bool *ret_write_locked)
+{
+ return -ENOTSUP;
+}
+
+int opal_exclusive_lock(struct crypt_device *cd, struct device *opal_device, struct crypt_lock_handle **opal_lock)
+{
+ return -ENOTSUP;
+}
+
+void opal_exclusive_unlock(struct crypt_device *cd, struct crypt_lock_handle *opal_lock)
+{
+}
+
+#endif
diff --git a/lib/luks2/hw_opal/hw_opal.h b/lib/luks2/hw_opal/hw_opal.h
new file mode 100644
index 0000000..f1823bf
--- /dev/null
+++ b/lib/luks2/hw_opal/hw_opal.h
@@ -0,0 +1,71 @@
+/*
+ * OPAL utilities
+ *
+ * Copyright (C) 2022-2023 Luca Boccassi <bluca@debian.org>
+ * 2023 Ondrej Kozina <okozina@redhat.com>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _UTILS_OPAL
+#define _UTILS_OPAL
+
+#include "internal.h"
+
+struct crypt_lock_handle;
+
+int opal_setup_ranges(struct crypt_device *cd,
+ struct device *dev,
+ const struct volume_key *vk,
+ uint64_t range_start,
+ uint64_t range_length,
+ uint32_t segment_number,
+ const void *admin_key,
+ size_t admin_key_len);
+int opal_lock(struct crypt_device *cd, struct device *dev, uint32_t segment_number);
+int opal_unlock(struct crypt_device *cd,
+ struct device *dev,
+ uint32_t segment_number,
+ const struct volume_key *vk);
+int opal_supported(struct crypt_device *cd, struct device *dev);
+int opal_factory_reset(struct crypt_device *cd,
+ struct device *dev,
+ const char *password,
+ size_t password_len);
+int opal_reset_segment(struct crypt_device *cd,
+ struct device *dev,
+ uint32_t segment_number,
+ const char *password,
+ size_t password_len);
+int opal_geometry(struct crypt_device *cd,
+ struct device *dev,
+ bool *ret_align,
+ uint32_t *ret_block_size,
+ uint64_t *ret_alignment_granularity_blocks,
+ uint64_t *ret_lowest_lba_blocks);
+int opal_range_check_attributes_and_get_lock_state(struct crypt_device *cd,
+ struct device *dev,
+ uint32_t segment_number,
+ const struct volume_key *vk,
+ const uint64_t *check_offset_sectors,
+ const uint64_t *check_length_sectors,
+ bool *ret_read_locked,
+ bool *ret_write_locked);
+int opal_exclusive_lock(struct crypt_device *cd,
+ struct device *opal_device,
+ struct crypt_lock_handle **opal_lock);
+void opal_exclusive_unlock(struct crypt_device *cd, struct crypt_lock_handle *opal_lock);
+
+#endif
diff --git a/lib/luks2/luks2.h b/lib/luks2/luks2.h
index dfccf02..25ae1dd 100644
--- a/lib/luks2/luks2.h
+++ b/lib/luks2/luks2.h
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2
*
- * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2023 Milan Broz
+ * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -224,8 +224,7 @@ int LUKS2_keyslot_store(struct crypt_device *cd,
int LUKS2_keyslot_wipe(struct crypt_device *cd,
struct luks2_hdr *hdr,
- int keyslot,
- int wipe_area_only);
+ int keyslot);
crypt_keyslot_priority LUKS2_keyslot_priority_get(struct luks2_hdr *hdr, int keyslot);
@@ -277,6 +276,7 @@ crypt_token_info LUKS2_token_status(struct crypt_device *cd,
int LUKS2_token_open_and_activate(struct crypt_device *cd,
struct luks2_hdr *hdr,
+ int keyslot,
int token,
const char *name,
const char *type,
@@ -287,6 +287,7 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd,
int LUKS2_token_unlock_key(struct crypt_device *cd,
struct luks2_hdr *hdr,
+ int keyslot,
int token,
const char *type,
const char *pin,
@@ -359,7 +360,8 @@ int LUKS2_digest_create(struct crypt_device *cd,
*/
int LUKS2_activate(struct crypt_device *cd,
const char *name,
- struct volume_key *vk,
+ struct volume_key *crypt_key,
+ struct volume_key *opal_key,
uint32_t flags);
int LUKS2_activate_multi(struct crypt_device *cd,
@@ -378,16 +380,23 @@ int LUKS2_generate_hdr(
struct crypt_device *cd,
struct luks2_hdr *hdr,
const struct volume_key *vk,
- const char *cipherName,
- const char *cipherMode,
+ const char *cipher_spec,
const char *integrity,
const char *uuid,
unsigned int sector_size,
uint64_t data_offset,
- uint64_t align_offset,
- uint64_t required_alignment,
- uint64_t metadata_size,
- uint64_t keyslots_size);
+ uint64_t metadata_size_bytes,
+ uint64_t keyslots_size_bytes,
+ uint64_t device_size_bytes,
+ uint32_t opal_segment_number,
+ uint32_t opal_key_size);
+
+int LUKS2_hdr_get_storage_params(struct crypt_device *cd,
+ uint64_t alignment_offset_bytes,
+ uint64_t alignment_bytes,
+ uint64_t *ret_metadata_size_bytes,
+ uint64_t *ret_keyslots_size_bytes,
+ uint64_t *ret_data_offset_bytes);
int LUKS2_check_metadata_area_size(uint64_t metadata_size);
int LUKS2_check_keyslots_area_size(uint64_t keyslots_size);
@@ -414,6 +423,12 @@ int LUKS2_keyslot_area(struct luks2_hdr *hdr,
uint64_t *length);
int LUKS2_keyslot_pbkdf(struct luks2_hdr *hdr, int keyslot, struct crypt_pbkdf_type *pbkdf);
+int LUKS2_split_crypt_and_opal_keys(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ const struct volume_key *vk,
+ struct volume_key **ret_crypt_key,
+ struct volume_key **ret_opal_key);
+
/*
* Permanent activation flags stored in header
*/
@@ -457,6 +472,9 @@ int LUKS2_reencrypt_locked_recovery_by_passphrase(struct crypt_device *cd,
size_t passphrase_size,
struct volume_key **vks);
+int LUKS2_reencrypt_locked_recovery_by_vks(struct crypt_device *cd,
+ struct volume_key *vks);
+
void LUKS2_reencrypt_free(struct crypt_device *cd,
struct luks2_reencrypt *rh);
@@ -479,9 +497,13 @@ int LUKS2_reencrypt_check_device_size(struct crypt_device *cd,
struct luks2_hdr *hdr,
uint64_t check_size,
uint64_t *dev_size,
- bool activation,
+ bool device_exclusive_check,
bool dynamic);
+void LUKS2_reencrypt_lookup_key_ids(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ struct volume_key *vk);
+
int LUKS2_reencrypt_digest_verify(struct crypt_device *cd,
struct luks2_hdr *hdr,
struct volume_key *vks);
diff --git a/lib/luks2/luks2_digest.c b/lib/luks2/luks2_digest.c
index 933b059..293df3e 100644
--- a/lib/luks2/luks2_digest.c
+++ b/lib/luks2/luks2_digest.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2, digest handling
*
- * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2023 Milan Broz
+ * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -157,7 +157,7 @@ int LUKS2_digest_dump(struct crypt_device *cd, int digest)
}
int LUKS2_digest_any_matching(struct crypt_device *cd,
- struct luks2_hdr *hdr,
+ struct luks2_hdr *hdr __attribute__((unused)),
const struct volume_key *vk)
{
int digest;
@@ -174,6 +174,18 @@ int LUKS2_digest_verify_by_segment(struct crypt_device *cd,
int segment,
const struct volume_key *vk)
{
+ int r = -EINVAL;
+ unsigned s;
+
+ if (segment == CRYPT_ANY_SEGMENT) {
+ for (s = 0; s < json_segments_count(LUKS2_get_segments_jobj(hdr)); s++) {
+ if ((r = LUKS2_digest_verify_by_digest(cd, LUKS2_digest_by_segment(hdr, s), vk)) >= 0)
+ return r;
+ }
+
+ return -EPERM;
+ }
+
return LUKS2_digest_verify_by_digest(cd, LUKS2_digest_by_segment(hdr, segment), vk);
}
diff --git a/lib/luks2/luks2_digest_pbkdf2.c b/lib/luks2/luks2_digest_pbkdf2.c
index 1009cfb..e8fd00d 100644
--- a/lib/luks2/luks2_digest_pbkdf2.c
+++ b/lib/luks2/luks2_digest_pbkdf2.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2, PBKDF2 digest handler (LUKS1 compatible)
*
- * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2023 Milan Broz
+ * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -147,6 +147,9 @@ static int PBKDF2_digest_store(struct crypt_device *cd,
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
}
+ if (!jobj_digest)
+ return -ENOMEM;
+
json_object_object_add(jobj_digest, "type", json_object_new_string("pbkdf2"));
json_object_object_add(jobj_digest, "keyslots", json_object_new_array());
json_object_object_add(jobj_digest, "segments", json_object_new_array());
@@ -169,8 +172,13 @@ static int PBKDF2_digest_store(struct crypt_device *cd,
json_object_object_add(jobj_digest, "digest", json_object_new_string(base64_str));
free(base64_str);
- if (jobj_digests)
- json_object_object_add_by_uint(jobj_digests, digest, jobj_digest);
+ if (jobj_digests) {
+ r = json_object_object_add_by_uint(jobj_digests, digest, jobj_digest);
+ if (r < 0) {
+ json_object_put(jobj_digest);
+ return r;
+ }
+ }
JSON_DBG(cd, jobj_digest, "Digest JSON:");
return 0;
diff --git a/lib/luks2/luks2_disk_metadata.c b/lib/luks2/luks2_disk_metadata.c
index e995959..d7f360c 100644
--- a/lib/luks2/luks2_disk_metadata.c
+++ b/lib/luks2/luks2_disk_metadata.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2
*
- * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2023 Milan Broz
+ * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -256,6 +256,7 @@ static int hdr_read_disk(struct crypt_device *cd,
if (read_lseek_blockwise(devfd, device_block_size(cd, device),
device_alignment(device), hdr_disk,
LUKS2_HDR_BIN_LEN, offset) != LUKS2_HDR_BIN_LEN) {
+ memset(hdr_disk, 0, LUKS2_HDR_BIN_LEN);
return -EIO;
}
@@ -537,11 +538,20 @@ static int validate_luks2_json_object(struct crypt_device *cd, json_object *jobj
}
static json_object *parse_and_validate_json(struct crypt_device *cd,
- const char *json_area, uint64_t max_length)
+ const char *json_area, uint64_t hdr_size)
{
int json_len, r;
- json_object *jobj = parse_json_len(cd, json_area, max_length, &json_len);
+ json_object *jobj;
+ uint64_t max_length;
+
+ if (hdr_size <= LUKS2_HDR_BIN_LEN || hdr_size > LUKS2_HDR_OFFSET_MAX) {
+ log_dbg(cd, "LUKS2 header JSON has bogus size 0x%04" PRIx64 ".", hdr_size);
+ return NULL;
+ }
+
+ max_length = hdr_size - LUKS2_HDR_BIN_LEN;
+ jobj = parse_json_len(cd, json_area, max_length, &json_len);
if (!jobj)
return NULL;
@@ -635,7 +645,7 @@ int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
state_hdr1 = HDR_FAIL;
r = hdr_read_disk(cd, device, &hdr_disk1, &json_area1, 0, 0);
if (r == 0) {
- jobj_hdr1 = parse_and_validate_json(cd, json_area1, be64_to_cpu(hdr_disk1.hdr_size) - LUKS2_HDR_BIN_LEN);
+ jobj_hdr1 = parse_and_validate_json(cd, json_area1, be64_to_cpu(hdr_disk1.hdr_size));
state_hdr1 = jobj_hdr1 ? HDR_OK : HDR_OBSOLETE;
} else if (r == -EIO)
state_hdr1 = HDR_FAIL_IO;
@@ -647,7 +657,7 @@ int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
if (state_hdr1 != HDR_FAIL && state_hdr1 != HDR_FAIL_IO) {
r = hdr_read_disk(cd, device, &hdr_disk2, &json_area2, be64_to_cpu(hdr_disk1.hdr_size), 1);
if (r == 0) {
- jobj_hdr2 = parse_and_validate_json(cd, json_area2, be64_to_cpu(hdr_disk2.hdr_size) - LUKS2_HDR_BIN_LEN);
+ jobj_hdr2 = parse_and_validate_json(cd, json_area2, be64_to_cpu(hdr_disk2.hdr_size));
state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE;
} else if (r == -EIO)
state_hdr2 = HDR_FAIL_IO;
@@ -655,11 +665,12 @@ int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
/*
* No header size, check all known offsets.
*/
+ hdr_disk2.hdr_size = 0;
for (r = -EINVAL,i = 0; r < 0 && i < ARRAY_SIZE(hdr2_offsets); i++)
r = hdr_read_disk(cd, device, &hdr_disk2, &json_area2, hdr2_offsets[i], 1);
if (r == 0) {
- jobj_hdr2 = parse_and_validate_json(cd, json_area2, be64_to_cpu(hdr_disk2.hdr_size) - LUKS2_HDR_BIN_LEN);
+ jobj_hdr2 = parse_and_validate_json(cd, json_area2, be64_to_cpu(hdr_disk2.hdr_size));
state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE;
} else if (r == -EIO)
state_hdr2 = HDR_FAIL_IO;
diff --git a/lib/luks2/luks2_internal.h b/lib/luks2/luks2_internal.h
index b564a48..aacc75e 100644
--- a/lib/luks2/luks2_internal.h
+++ b/lib/luks2/luks2_internal.h
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2
*
- * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2023 Milan Broz
+ * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -62,6 +62,7 @@ uint32_t crypt_jobj_get_uint32(json_object *jobj);
json_object *crypt_jobj_new_uint64(uint64_t value);
int json_object_object_add_by_uint(json_object *jobj, unsigned key, json_object *jobj_val);
+int json_object_object_add_by_uint_by_ref(json_object *jobj, unsigned key, json_object **jobj_val_ref);
void json_object_object_del_by_uint(json_object *jobj, unsigned key);
int json_object_copy(json_object *jobj_src, json_object **jobj_dst);
@@ -295,13 +296,24 @@ uint64_t json_segment_get_iv_offset(json_object *jobj_segment);
uint64_t json_segment_get_size(json_object *jobj_segment, unsigned blockwise);
const char *json_segment_get_cipher(json_object *jobj_segment);
uint32_t json_segment_get_sector_size(json_object *jobj_segment);
+int json_segment_get_opal_segment_id(json_object *jobj_segment, uint32_t *ret_opal_segment_id);
+int json_segment_get_opal_key_size(json_object *jobj_segment, size_t *ret_key_size);
bool json_segment_is_backup(json_object *jobj_segment);
json_object *json_segments_get_segment(json_object *jobj_segments, int segment);
unsigned json_segments_count(json_object *jobj_segments);
void json_segment_remove_flag(json_object *jobj_segment, const char *flag);
uint64_t json_segments_get_minimal_offset(json_object *jobj_segments, unsigned blockwise);
json_object *json_segment_create_linear(uint64_t offset, const uint64_t *length, unsigned reencryption);
-json_object *json_segment_create_crypt(uint64_t offset, uint64_t iv_offset, const uint64_t *length, const char *cipher, uint32_t sector_size, unsigned reencryption);
+json_object *json_segment_create_crypt(uint64_t offset, uint64_t iv_offset, const uint64_t *length,
+ const char *cipher, const char *integrity,
+ uint32_t sector_size, unsigned reencryption);
+json_object *json_segment_create_opal(uint64_t offset, const uint64_t *length,
+ uint32_t segment_number, uint32_t key_size);
+json_object *json_segment_create_opal_crypt(uint64_t offset, const uint64_t *length,
+ uint32_t segment_number, uint32_t key_size,
+ uint64_t iv_offset, const char *cipher,
+ const char *integrity, uint32_t sector_size,
+ unsigned reencryption);
int json_segments_segment_in_reencrypt(json_object *jobj_segments);
bool json_segment_cmp(json_object *jobj_segment_1, json_object *jobj_segment_2);
bool json_segment_contains_flag(json_object *jobj_segment, const char *flag_str, size_t len);
@@ -338,10 +350,26 @@ uint64_t LUKS2_segment_size(struct luks2_hdr *hdr,
int segment,
unsigned blockwise);
+bool LUKS2_segment_set_size(struct luks2_hdr *hdr,
+ int segment,
+ const uint64_t *segment_size_bytes);
+
+uint64_t LUKS2_opal_segment_size(struct luks2_hdr *hdr,
+ int segment,
+ unsigned blockwise);
+
int LUKS2_segment_is_type(struct luks2_hdr *hdr,
int segment,
const char *type);
+bool LUKS2_segment_is_hw_opal(struct luks2_hdr *hdr, int segment);
+bool LUKS2_segment_is_hw_opal_crypt(struct luks2_hdr *hdr, int segment);
+bool LUKS2_segment_is_hw_opal_only(struct luks2_hdr *hdr, int segment);
+
+int LUKS2_get_opal_segment_number(struct luks2_hdr *hdr, int segment,
+ uint32_t *ret_opal_segment_number);
+int LUKS2_get_opal_key_size(struct luks2_hdr *hdr, int segment);
+
int LUKS2_segment_by_type(struct luks2_hdr *hdr,
const char *type);
@@ -350,8 +378,11 @@ int LUKS2_last_segment_by_type(struct luks2_hdr *hdr,
int LUKS2_get_default_segment(struct luks2_hdr *hdr);
+bool LUKS2_segments_dynamic_size(struct luks2_hdr *hdr);
+
int LUKS2_reencrypt_digest_new(struct luks2_hdr *hdr);
int LUKS2_reencrypt_digest_old(struct luks2_hdr *hdr);
+unsigned LUKS2_reencrypt_vks_count(struct luks2_hdr *hdr);
int LUKS2_reencrypt_data_offset(struct luks2_hdr *hdr, bool blockwise);
/*
diff --git a/lib/luks2/luks2_json_format.c b/lib/luks2/luks2_json_format.c
index 4456358..100e026 100644
--- a/lib/luks2/luks2_json_format.c
+++ b/lib/luks2/luks2_json_format.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2, LUKS2 header format code
*
- * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2023 Milan Broz
+ * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -204,76 +204,33 @@ int LUKS2_generate_hdr(
struct crypt_device *cd,
struct luks2_hdr *hdr,
const struct volume_key *vk,
- const char *cipherName,
- const char *cipherMode,
+ const char *cipher_spec,
const char *integrity,
const char *uuid,
unsigned int sector_size, /* in bytes */
uint64_t data_offset, /* in bytes */
- uint64_t align_offset, /* in bytes */
- uint64_t required_alignment,
- uint64_t metadata_size,
- uint64_t keyslots_size)
+ uint64_t metadata_size_bytes,
+ uint64_t keyslots_size_bytes,
+ uint64_t device_size_bytes,
+ uint32_t opal_segment_number,
+ uint32_t opal_key_size)
{
- struct json_object *jobj_segment, *jobj_integrity, *jobj_keyslots, *jobj_segments, *jobj_config;
- char cipher[128];
+ struct json_object *jobj_segment, *jobj_keyslots, *jobj_segments, *jobj_config;
uuid_t partitionUuid;
int r, digest;
- uint64_t mdev_size;
- if (!metadata_size)
- metadata_size = LUKS2_HDR_16K_LEN;
- hdr->hdr_size = metadata_size;
+ assert(cipher_spec || (opal_key_size > 0 && device_size_bytes));
- if (data_offset && data_offset < get_min_offset(hdr)) {
- log_err(cd, _("Requested data offset is too small."));
- return -EINVAL;
- }
-
- /* Increase keyslot size according to data offset */
- if (!keyslots_size && data_offset)
- keyslots_size = data_offset - get_min_offset(hdr);
-
- /* keyslots size has to be 4 KiB aligned */
- keyslots_size -= (keyslots_size % 4096);
-
- if (keyslots_size > LUKS2_MAX_KEYSLOTS_SIZE)
- keyslots_size = LUKS2_MAX_KEYSLOTS_SIZE;
-
- if (!keyslots_size) {
- assert(LUKS2_DEFAULT_HDR_SIZE > 2 * LUKS2_HDR_OFFSET_MAX);
- keyslots_size = LUKS2_DEFAULT_HDR_SIZE - get_min_offset(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)) &&
- (get_min_offset(hdr) <= mdev_size))
- keyslots_size = mdev_size - get_min_offset(hdr);
- }
-
- /* Decrease keyslots_size if we have smaller data_offset */
- if (data_offset && (keyslots_size + get_min_offset(hdr)) > data_offset) {
- keyslots_size = data_offset - get_min_offset(hdr);
- log_dbg(cd, "Decreasing keyslot area size to %" PRIu64
- " bytes due to the requested data offset %"
- PRIu64 " bytes.", keyslots_size, data_offset);
- }
-
- /* Data offset has priority */
- if (!data_offset && required_alignment) {
- data_offset = size_round_up(get_min_offset(hdr) + keyslots_size,
- (size_t)required_alignment);
- data_offset += align_offset;
- }
+ hdr->hdr_size = metadata_size_bytes;
log_dbg(cd, "Formatting LUKS2 with JSON metadata area %" PRIu64
" bytes and keyslots area %" PRIu64 " bytes.",
- metadata_size - LUKS2_HDR_BIN_LEN, keyslots_size);
+ metadata_size_bytes - LUKS2_HDR_BIN_LEN, keyslots_size_bytes);
- if (keyslots_size < (LUKS2_HDR_OFFSET_MAX - 2*LUKS2_HDR_16K_LEN))
+ if (keyslots_size_bytes < (LUKS2_HDR_OFFSET_MAX - 2*LUKS2_HDR_16K_LEN))
log_std(cd, _("WARNING: keyslots area (%" PRIu64 " bytes) is very small,"
" available LUKS2 keyslot count is very limited.\n"),
- keyslots_size);
+ keyslots_size_bytes);
hdr->seqid = 1;
hdr->version = 2;
@@ -291,54 +248,81 @@ int LUKS2_generate_hdr(
uuid_unparse(partitionUuid, hdr->uuid);
- if (*cipherMode != '\0')
- r = snprintf(cipher, sizeof(cipher), "%s-%s", cipherName, cipherMode);
- else
- r = snprintf(cipher, sizeof(cipher), "%s", cipherName);
- if (r < 0 || (size_t)r >= sizeof(cipher))
- return -EINVAL;
-
hdr->jobj = json_object_new_object();
+ if (!hdr->jobj) {
+ r = -ENOMEM;
+ goto err;
+ }
jobj_keyslots = json_object_new_object();
+ if (!jobj_keyslots) {
+ r = -ENOMEM;
+ goto err;
+ }
+
json_object_object_add(hdr->jobj, "keyslots", jobj_keyslots);
json_object_object_add(hdr->jobj, "tokens", json_object_new_object());
jobj_segments = json_object_new_object();
+ if (!jobj_segments) {
+ r = -ENOMEM;
+ goto err;
+ }
+
json_object_object_add(hdr->jobj, "segments", jobj_segments);
json_object_object_add(hdr->jobj, "digests", json_object_new_object());
jobj_config = json_object_new_object();
+ if (!jobj_config) {
+ r = -ENOMEM;
+ goto err;
+ }
+
json_object_object_add(hdr->jobj, "config", jobj_config);
digest = LUKS2_digest_create(cd, "pbkdf2", hdr, vk);
- if (digest < 0)
+ if (digest < 0) {
+ r = -EINVAL;
goto err;
+ }
- if (LUKS2_digest_segment_assign(cd, hdr, 0, digest, 1, 0) < 0)
+ if (LUKS2_digest_segment_assign(cd, hdr, 0, digest, 1, 0) < 0) {
+ r = -EINVAL;
goto err;
+ }
- jobj_segment = json_segment_create_crypt(data_offset, 0, NULL, cipher, sector_size, 0);
- if (!jobj_segment)
- goto err;
+ if (!opal_key_size)
+ jobj_segment = json_segment_create_crypt(data_offset, 0,
+ NULL, cipher_spec,
+ integrity, sector_size,
+ 0);
+ else if (opal_key_size && cipher_spec)
+ jobj_segment = json_segment_create_opal_crypt(data_offset, &device_size_bytes,
+ opal_segment_number, opal_key_size, 0,
+ cipher_spec, integrity,
+ sector_size, 0);
+ else
+ jobj_segment = json_segment_create_opal(data_offset, &device_size_bytes,
+ opal_segment_number, opal_key_size);
- if (integrity) {
- jobj_integrity = json_object_new_object();
- json_object_object_add(jobj_integrity, "type", json_object_new_string(integrity));
- json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string("none"));
- json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string("none"));
- json_object_object_add(jobj_segment, "integrity", jobj_integrity);
+ if (!jobj_segment) {
+ r = -EINVAL;
+ goto err;
}
- json_object_object_add_by_uint(jobj_segments, 0, jobj_segment);
+ if (json_object_object_add_by_uint(jobj_segments, 0, jobj_segment)) {
+ json_object_put(jobj_segment);
+ r = -ENOMEM;
+ goto err;
+ }
- json_object_object_add(jobj_config, "json_size", crypt_jobj_new_uint64(metadata_size - LUKS2_HDR_BIN_LEN));
- json_object_object_add(jobj_config, "keyslots_size", crypt_jobj_new_uint64(keyslots_size));
+ json_object_object_add(jobj_config, "json_size", crypt_jobj_new_uint64(metadata_size_bytes - LUKS2_HDR_BIN_LEN));
+ json_object_object_add(jobj_config, "keyslots_size", crypt_jobj_new_uint64(keyslots_size_bytes));
JSON_DBG(cd, hdr->jobj, "Header JSON:");
return 0;
err:
json_object_put(hdr->jobj);
hdr->jobj = NULL;
- return -EINVAL;
+ return r;
}
int LUKS2_wipe_header_areas(struct crypt_device *cd,
@@ -379,6 +363,14 @@ int LUKS2_wipe_header_areas(struct crypt_device *cd,
offset = get_min_offset(hdr);
length = LUKS2_keyslots_size(hdr);
+ /*
+ * Skip keyslots area wipe in case it is not defined.
+ * Otherwise we would wipe whole data device (length == 0)
+ * starting at offset get_min_offset(hdr).
+ */
+ if (!length)
+ return 0;
+
log_dbg(cd, "Wiping keyslots area (0x%06" PRIx64 " - 0x%06" PRIx64") with random data.",
offset, length + offset);
@@ -409,3 +401,80 @@ int LUKS2_set_keyslots_size(struct luks2_hdr *hdr, uint64_t data_offset)
json_object_object_add(jobj_config, "keyslots_size", crypt_jobj_new_uint64(keyslots_size));
return 0;
}
+
+int LUKS2_hdr_get_storage_params(struct crypt_device *cd,
+ uint64_t alignment_offset_bytes,
+ uint64_t alignment_bytes,
+ uint64_t *ret_metadata_size_bytes,
+ uint64_t *ret_keyslots_size_bytes,
+ uint64_t *ret_data_offset_bytes)
+{
+ uint64_t data_offset_bytes, keyslots_size_bytes, metadata_size_bytes, mdev_size_bytes;
+
+ assert(cd);
+ assert(ret_metadata_size_bytes);
+ assert(ret_keyslots_size_bytes);
+ assert(ret_data_offset_bytes);
+
+ metadata_size_bytes = crypt_get_metadata_size_bytes(cd);
+ keyslots_size_bytes = crypt_get_keyslots_size_bytes(cd);
+ data_offset_bytes = crypt_get_data_offset_sectors(cd) * SECTOR_SIZE;
+
+ if (!metadata_size_bytes)
+ metadata_size_bytes = LUKS2_HDR_16K_LEN;
+
+ if (data_offset_bytes && data_offset_bytes < 2 * metadata_size_bytes) {
+ log_err(cd, _("Requested data offset is too small."));
+ return -EINVAL;
+ }
+
+ /* Increase keyslot size according to data offset */
+ if (!keyslots_size_bytes && data_offset_bytes)
+ keyslots_size_bytes = data_offset_bytes - 2 * metadata_size_bytes;
+
+ /* keyslots size has to be 4 KiB aligned */
+ keyslots_size_bytes -= (keyslots_size_bytes % 4096);
+
+ if (keyslots_size_bytes > LUKS2_MAX_KEYSLOTS_SIZE)
+ keyslots_size_bytes = LUKS2_MAX_KEYSLOTS_SIZE;
+
+ if (!keyslots_size_bytes) {
+ assert(LUKS2_DEFAULT_HDR_SIZE > 2 * LUKS2_HDR_OFFSET_MAX);
+ keyslots_size_bytes = LUKS2_DEFAULT_HDR_SIZE - 2 * metadata_size_bytes;
+ /* Decrease keyslots_size due to metadata device being too small */
+ if (!device_size(crypt_metadata_device(cd), &mdev_size_bytes) &&
+ ((keyslots_size_bytes + 2 * metadata_size_bytes) > mdev_size_bytes) &&
+ device_fallocate(crypt_metadata_device(cd), keyslots_size_bytes + 2 * metadata_size_bytes) &&
+ ((2 * metadata_size_bytes) <= mdev_size_bytes))
+ keyslots_size_bytes = mdev_size_bytes - 2 * metadata_size_bytes;
+ }
+
+ /* Decrease keyslots_size if we have smaller data_offset */
+ if (data_offset_bytes && (keyslots_size_bytes + 2 * metadata_size_bytes) > data_offset_bytes) {
+ keyslots_size_bytes = data_offset_bytes - 2 * metadata_size_bytes;
+ log_dbg(cd, "Decreasing keyslot area size to %" PRIu64
+ " bytes due to the requested data offset %"
+ PRIu64 " bytes.", keyslots_size_bytes, data_offset_bytes);
+ }
+
+ /* Data offset has priority */
+ if (!data_offset_bytes && alignment_bytes) {
+ data_offset_bytes = size_round_up(2 * metadata_size_bytes + keyslots_size_bytes,
+ (size_t)alignment_bytes);
+ data_offset_bytes += alignment_offset_bytes;
+ }
+
+ if (crypt_get_metadata_size_bytes(cd) && (crypt_get_metadata_size_bytes(cd) != metadata_size_bytes))
+ log_std(cd, _("WARNING: LUKS2 metadata size changed to %" PRIu64 " bytes.\n"),
+ metadata_size_bytes);
+
+ if (crypt_get_keyslots_size_bytes(cd) && (crypt_get_keyslots_size_bytes(cd) != keyslots_size_bytes))
+ log_std(cd, _("WARNING: LUKS2 keyslots area size changed to %" PRIu64 " bytes.\n"),
+ keyslots_size_bytes);
+
+ *ret_metadata_size_bytes = metadata_size_bytes;
+ *ret_keyslots_size_bytes = keyslots_size_bytes;
+ *ret_data_offset_bytes = data_offset_bytes;
+
+ return 0;
+}
diff --git a/lib/luks2/luks2_json_metadata.c b/lib/luks2/luks2_json_metadata.c
index 4771f04..22f3e3d 100644
--- a/lib/luks2/luks2_json_metadata.c
+++ b/lib/luks2/luks2_json_metadata.c
@@ -1,9 +1,9 @@
/*
* LUKS - Linux Unified Key Setup v2
*
- * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2023 Milan Broz
- * Copyright (C) 2015-2023 Ondrej Kozina
+ * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2024 Milan Broz
+ * Copyright (C) 2015-2024 Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -21,6 +21,7 @@
*/
#include "luks2_internal.h"
+#include "luks2/hw_opal/hw_opal.h"
#include "../integrity/integrity.h"
#include <ctype.h>
#include <uuid/uuid.h>
@@ -88,6 +89,9 @@ struct json_object *LUKS2_array_remove(struct json_object *array, const char *nu
/* Create new array without jobj_removing. */
array_new = json_object_new_array();
+ if (!array_new)
+ return NULL;
+
for (i = 0; i < (int) json_object_array_length(array); i++) {
jobj1 = json_object_array_get_idx(array, i);
if (jobj1 != jobj_removing)
@@ -478,6 +482,9 @@ static int hdr_validate_json_size(struct crypt_device *cd, json_object *hdr_jobj
json = json_object_to_json_string_ext(hdr_jobj,
JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE);
+ if (!json)
+ return 1;
+
json_area_size = crypt_jobj_get_uint64(jobj1);
json_size = (uint64_t)strlen(json);
@@ -637,6 +644,11 @@ static int reqs_reencrypt_online(uint32_t reqs)
return reqs & CRYPT_REQUIREMENT_ONLINE_REENCRYPT;
}
+static int reqs_opal(uint32_t reqs)
+{
+ return reqs & CRYPT_REQUIREMENT_OPAL;
+}
+
/*
* Config section requirements object must be valid.
* Also general segments section must be validated first.
@@ -697,7 +709,7 @@ static int validate_reencrypt_segments(struct crypt_device *cd, json_object *hdr
static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
{
json_object *jobj_segments, *jobj_digests, *jobj_offset, *jobj_size, *jobj_type, *jobj_flags, *jobj;
- uint64_t offset, size;
+ uint64_t offset, size, opal_segment_size;
int i, r, count, first_backup = -1;
struct interval *intervals = NULL;
@@ -777,6 +789,32 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
if (!strcmp(json_object_get_string(jobj_type), "crypt") &&
hdr_validate_crypt_segment(cd, val, key, jobj_digests, size))
return 1;
+
+ /* opal */
+ if (!strncmp(json_object_get_string(jobj_type), "hw-opal", 7)) {
+ if (!size) {
+ log_dbg(cd, "segment type %s does not support dynamic size.",
+ json_object_get_string(jobj_type));
+ return 1;
+ }
+ if (!json_contains(cd, val, key, "Segment", "opal_segment_number", json_type_int) ||
+ !json_contains(cd, val, key, "Segment", "opal_key_size", json_type_int) ||
+ !(jobj_size = json_contains_string(cd, val, key, "Segment", "opal_segment_size")))
+ return 1;
+ if (!numbered(cd, "opal_segment_size", json_object_get_string(jobj_size)))
+ return 1;
+ if (!json_str_to_uint64(jobj_size, &opal_segment_size) || !opal_segment_size) {
+ log_dbg(cd, "Illegal OPAL segment size value.");
+ return 1;
+ }
+ if (size > opal_segment_size) {
+ log_dbg(cd, "segment size overflows OPAL locking range size.");
+ return 1;
+ }
+ if (!strcmp(json_object_get_string(jobj_type), "hw-opal-crypt") &&
+ hdr_validate_crypt_segment(cd, val, key, jobj_digests, size))
+ return 1;
+ }
}
if (first_backup == 0) {
@@ -1575,6 +1613,8 @@ int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint3
return 0;
jobj_flags = json_object_new_array();
+ if (!jobj_flags)
+ return -ENOMEM;
for (i = 0; persistent_flags[i].description; i++) {
if (flags & persistent_flags[i].flag) {
@@ -1615,6 +1655,7 @@ static const struct requirement_flag requirements_flags[] = {
{ CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 2, "online-reencrypt-v2" },
{ CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 3, "online-reencrypt-v3" },
{ CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 1, "online-reencrypt" },
+ { CRYPT_REQUIREMENT_OPAL, 1, "opal" },
{ 0, 0, NULL }
};
@@ -1707,7 +1748,7 @@ int LUKS2_config_get_reencrypt_version(struct luks2_hdr *hdr, uint8_t *version)
return -ENOENT;
}
-static const struct requirement_flag *stored_requirement_name_by_id(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t req_id)
+static const struct requirement_flag *stored_requirement_name_by_id(struct luks2_hdr *hdr, uint32_t req_id)
{
json_object *jobj_mandatory, *jobj;
int i, len;
@@ -1786,7 +1827,7 @@ int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr
req_id = reqs & requirements_flags[i].flag;
if (req_id) {
/* retain already stored version of requirement flag */
- req = stored_requirement_name_by_id(cd, hdr, req_id);
+ req = stored_requirement_name_by_id(hdr, req_id);
if (req)
jobj = json_object_new_string(req->description);
else
@@ -2090,6 +2131,8 @@ static void hdr_dump_segments(struct crypt_device *cd, json_object *hdr_jobj)
if (json_object_object_get_ex(jobj_segment, "encryption", &jobj1))
log_std(cd, "\tcipher: %s\n", json_object_get_string(jobj1));
+ else
+ log_std(cd, "\tcipher: (no SW encryption)\n");
if (json_object_object_get_ex(jobj_segment, "sector_size", &jobj1))
log_std(cd, "\tsector: %" PRIu32 " [bytes]\n", crypt_jobj_get_uint32(jobj1));
@@ -2109,6 +2152,18 @@ static void hdr_dump_segments(struct crypt_device *cd, json_object *hdr_jobj)
log_std(cd, "\n");
}
+ json_object_object_get_ex(jobj_segment, "type", &jobj1);
+ if (!strncmp(json_object_get_string(jobj1), "hw-opal", 7)) {
+ log_std(cd, "\tHW OPAL encryption:\n");
+ json_object_object_get_ex(jobj_segment, "opal_segment_number", &jobj1);
+ log_std(cd, "\t\tOPAL segment number: %" PRIu32 "\n", crypt_jobj_get_uint32(jobj1));
+ json_object_object_get_ex(jobj_segment, "opal_key_size", &jobj1);
+ log_std(cd, "\t\tOPAL key: %" PRIu32 " bits\n", crypt_jobj_get_uint32(jobj1) * 8);
+ json_object_object_get_ex(jobj_segment, "opal_segment_size", &jobj1);
+ json_str_to_uint64(jobj1, &value);
+ log_std(cd, "\t\tOPAL segment length: %" PRIu64 " [bytes]\n", value);
+ }
+
log_std(cd, "\n");
}
}
@@ -2584,26 +2639,104 @@ int LUKS2_activate_multi(struct crypt_device *cd,
int LUKS2_activate(struct crypt_device *cd,
const char *name,
- struct volume_key *vk,
+ struct volume_key *crypt_key,
+ struct volume_key *opal_key,
uint32_t flags)
{
int r;
+ bool dynamic, read_lock, write_lock, opal_lock_on_error = false;
+ uint32_t opal_segment_number;
+ uint64_t range_offset_sectors, range_length_sectors, device_length_bytes;
struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
struct crypt_dm_active_device dmdi = {}, dmd = {
.uuid = crypt_get_uuid(cd)
};
+ struct crypt_lock_handle *opal_lh = NULL;
/* do not allow activation when particular requirements detected */
- if ((r = LUKS2_unmet_requirements(cd, hdr, 0, 0)))
+ if ((r = LUKS2_unmet_requirements(cd, hdr, CRYPT_REQUIREMENT_OPAL, 0)))
return r;
- r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd),
- vk, crypt_get_cipher_spec(cd), crypt_get_iv_offset(cd),
- crypt_get_data_offset(cd), crypt_get_integrity(cd) ?: "none",
- crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd));
- if (r < 0)
+ /* Check that cipher is in compatible format */
+ if (!crypt_get_cipher(cd)) {
+ log_err(cd, _("No known cipher specification pattern detected in LUKS2 header."));
+ return -EINVAL;
+ }
+
+ if ((r = LUKS2_get_data_size(hdr, &device_length_bytes, &dynamic)))
return r;
+ if (dynamic && opal_key) {
+ log_err(cd, _("OPAL device must have static device size."));
+ return -EINVAL;
+ }
+
+ if (!dynamic)
+ dmd.size = device_length_bytes / SECTOR_SIZE;
+
+ if (opal_key) {
+ r = crypt_opal_supported(cd, crypt_data_device(cd));
+ if (r < 0)
+ return r;
+
+ r = LUKS2_get_opal_segment_number(hdr, CRYPT_DEFAULT_SEGMENT, &opal_segment_number);
+ if (r < 0)
+ return -EINVAL;
+
+ range_length_sectors = LUKS2_opal_segment_size(hdr, CRYPT_DEFAULT_SEGMENT, 1);
+
+ if (crypt_get_integrity_tag_size(cd)) {
+ if (dmd.size >= range_length_sectors) {
+ log_err(cd, _("Encrypted OPAL device with integrity must be smaller than locking range."));
+ return -EINVAL;
+ }
+ } else {
+ if (range_length_sectors != dmd.size) {
+ log_err(cd, _("OPAL device must have same size as locking range."));
+ return -EINVAL;
+ }
+ }
+
+ range_offset_sectors = crypt_get_data_offset(cd) + crypt_dev_partition_offset(device_path(crypt_data_device(cd)));
+ r = opal_exclusive_lock(cd, crypt_data_device(cd), &opal_lh);
+ if (r < 0) {
+ log_err(cd, _("Failed to acquire OPAL lock on device %s."), device_path(crypt_data_device(cd)));
+ return -EINVAL;
+ }
+
+ r = opal_range_check_attributes_and_get_lock_state(cd, crypt_data_device(cd), opal_segment_number,
+ opal_key, &range_offset_sectors, &range_length_sectors,
+ &read_lock, &write_lock);
+ if (r < 0)
+ goto out;
+
+ opal_lock_on_error = read_lock && write_lock;
+ if (!opal_lock_on_error && !(flags & CRYPT_ACTIVATE_REFRESH))
+ log_std(cd, _("OPAL device is %s already unlocked.\n"),
+ device_path(crypt_data_device(cd)));
+
+ r = opal_unlock(cd, crypt_data_device(cd), opal_segment_number, opal_key);
+ if (r < 0)
+ goto out;
+ }
+
+ if (LUKS2_segment_is_type(hdr, CRYPT_DEFAULT_SEGMENT, "crypt") ||
+ LUKS2_segment_is_type(hdr, CRYPT_DEFAULT_SEGMENT, "hw-opal-crypt")) {
+ r = dm_crypt_target_set(&dmd.segment, 0,
+ dmd.size, crypt_data_device(cd),
+ crypt_key, crypt_get_cipher_spec(cd),
+ crypt_get_iv_offset(cd), crypt_get_data_offset(cd),
+ crypt_get_integrity(cd) ?: "none",
+ crypt_get_integrity_tag_size(cd),
+ crypt_get_sector_size(cd));
+ } else
+ r = dm_linear_target_set(&dmd.segment, 0,
+ dmd.size, crypt_data_device(cd),
+ crypt_get_data_offset(cd));
+
+ if (r < 0)
+ goto out;
+
/* Add persistent activation flags */
if (!(flags & CRYPT_ACTIVATE_IGNORE_PERSISTENT))
LUKS2_config_get_flags(cd, hdr, &dmd.flags);
@@ -2613,29 +2746,47 @@ int LUKS2_activate(struct crypt_device *cd,
if (crypt_get_integrity_tag_size(cd)) {
if (!LUKS2_integrity_compatible(hdr)) {
log_err(cd, _("Unsupported device integrity configuration."));
- return -EINVAL;
+ r = -EINVAL;
+ goto out;
}
if (dmd.flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) {
log_err(cd, _("Discard/TRIM is not supported."));
- return -EINVAL;
+ r = -EINVAL;
+ goto out;
}
r = INTEGRITY_create_dmd_device(cd, NULL, NULL, NULL, NULL, &dmdi, dmd.flags, 0);
if (r)
- return r;
+ goto out;
+
+ if (!dynamic && dmdi.size != dmd.size) {
+ log_err(cd, _("Underlying dm-integrity device with unexpected provided data sectors."));
+ r = -EINVAL;
+ goto out;
+ }
dmdi.flags |= CRYPT_ACTIVATE_PRIVATE;
dmdi.uuid = dmd.uuid;
dmd.segment.u.crypt.offset = 0;
- dmd.segment.size = dmdi.segment.size;
+ if (dynamic)
+ dmd.segment.size = dmdi.segment.size;
- r = create_or_reload_device_with_integrity(cd, name, CRYPT_LUKS2, &dmd, &dmdi);
+ r = create_or_reload_device_with_integrity(cd, name,
+ opal_key ? CRYPT_LUKS2_HW_OPAL : CRYPT_LUKS2,
+ &dmd, &dmdi);
} else
- r = create_or_reload_device(cd, name, CRYPT_LUKS2, &dmd);
+ r = create_or_reload_device(cd, name,
+ opal_key ? CRYPT_LUKS2_HW_OPAL : CRYPT_LUKS2,
+ &dmd);
dm_targets_free(cd, &dmd);
dm_targets_free(cd, &dmdi);
+out:
+ if (r < 0 && opal_lock_on_error)
+ opal_lock(cd, crypt_data_device(cd), opal_segment_number);
+
+ opal_exclusive_unlock(cd, opal_lh);
return r;
}
@@ -2665,13 +2816,15 @@ static bool contains_reencryption_helper(char **names)
int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr *hdr, struct crypt_dm_active_device *dmd, uint32_t flags)
{
+ bool dm_opal_uuid;
int r, ret;
struct dm_target *tgt;
crypt_status_info ci;
struct crypt_dm_active_device dmdc;
+ uint32_t opal_segment_number;
char **dep, deps_uuid_prefix[40], *deps[MAX_DM_DEPS+1] = { 0 };
const char *namei = NULL;
- struct crypt_lock_handle *reencrypt_lock = NULL;
+ struct crypt_lock_handle *reencrypt_lock = NULL, *opal_lh = NULL;
if (!dmd || !dmd->uuid || strncmp(CRYPT_LUKS2, dmd->uuid, sizeof(CRYPT_LUKS2)-1))
return -EINVAL;
@@ -2684,6 +2837,11 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr
if (r < 0 || (size_t)r != (sizeof(deps_uuid_prefix) - 1))
return -EINVAL;
+ /* check if active device has LUKS2-OPAL dm uuid prefix */
+ dm_opal_uuid = !crypt_uuid_type_cmp(dmd->uuid, CRYPT_LUKS2_HW_OPAL);
+ if (dm_opal_uuid && hdr && !LUKS2_segment_is_hw_opal(hdr, CRYPT_DEFAULT_SEGMENT))
+ return -EINVAL;
+
tgt = &dmd->segment;
/* TODO: We have LUKS2 dependencies now */
@@ -2726,7 +2884,8 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr
tgt = &dmdc.segment;
while (tgt) {
if (tgt->type == DM_CRYPT)
- crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description, LOGON_KEY);
+ crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description,
+ LOGON_KEY);
tgt = tgt->next;
}
}
@@ -2761,7 +2920,8 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr
tgt = &dmdc.segment;
while (tgt) {
if (tgt->type == DM_CRYPT)
- crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description, LOGON_KEY);
+ crypt_drop_keyring_key_by_description(cd, tgt->u.crypt.vk->key_description,
+ LOGON_KEY);
tgt = tgt->next;
}
}
@@ -2773,7 +2933,35 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr
r = ret;
}
+ if (!r && dm_opal_uuid) {
+ if (hdr) {
+ if (LUKS2_get_opal_segment_number(hdr, CRYPT_DEFAULT_SEGMENT, &opal_segment_number)) {
+ log_err(cd, _("Device %s was deactivated but hardware OPAL device cannot be locked."),
+ name);
+ r = -EINVAL;
+ goto out;
+ }
+ } else {
+ /* Guess OPAL range number for LUKS2-OPAL device with missing header */
+ opal_segment_number = 1;
+ ret = crypt_dev_get_partition_number(device_path(crypt_data_device(cd)));
+ if (ret > 0)
+ opal_segment_number = ret;
+ }
+
+ if (crypt_data_device(cd)) {
+ r = opal_exclusive_lock(cd, crypt_data_device(cd), &opal_lh);
+ if (r < 0) {
+ log_err(cd, _("Failed to acquire OPAL lock on device %s."), device_path(crypt_data_device(cd)));
+ goto out;
+ }
+ }
+
+ if (!crypt_data_device(cd) || opal_lock(cd, crypt_data_device(cd), opal_segment_number))
+ log_err(cd, _("Device %s was deactivated but hardware OPAL device cannot be locked."), name);
+ }
out:
+ opal_exclusive_unlock(cd, opal_lh);
LUKS2_reencrypt_unlock(cd, reencrypt_lock);
dep = deps;
while (*dep)
@@ -2807,6 +2995,8 @@ int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uin
log_err(cd, _("Operation incompatible with device marked for legacy reencryption. Aborting."));
if (reqs_reencrypt_online(reqs) && !quiet)
log_err(cd, _("Operation incompatible with device marked for LUKS2 reencryption. Aborting."));
+ if (reqs_opal(reqs) && !quiet)
+ log_err(cd, _("Operation incompatible with device using OPAL. Aborting."));
/* any remaining unmasked requirement fails the check */
return reqs ? -EINVAL : 0;
@@ -2859,6 +3049,20 @@ int json_object_object_add_by_uint(json_object *jobj, unsigned key, json_object
#endif
}
+int json_object_object_add_by_uint_by_ref(json_object *jobj, unsigned key, json_object **jobj_val_ref)
+{
+ int r;
+
+ assert(jobj);
+ assert(jobj_val_ref);
+
+ r = json_object_object_add_by_uint(jobj, key, *jobj_val_ref);
+ if (!r)
+ *jobj_val_ref = NULL;
+
+ return r;
+}
+
/* jobj_dst must contain pointer initialized to NULL (see json-c json_object_deep_copy API) */
int json_object_copy(json_object *jobj_src, json_object **jobj_dst)
{
@@ -2872,3 +3076,58 @@ int json_object_copy(json_object *jobj_src, json_object **jobj_dst)
return *jobj_dst ? 0 : -1;
#endif
}
+
+int LUKS2_split_crypt_and_opal_keys(struct crypt_device *cd __attribute__((unused)),
+ struct luks2_hdr *hdr,
+ const struct volume_key *vk,
+ struct volume_key **ret_crypt_key,
+ struct volume_key **ret_opal_key)
+{
+ int r;
+ uint32_t opal_segment_number;
+ size_t opal_user_key_size;
+ json_object *jobj_segment;
+ struct volume_key *opal_key, *crypt_key;
+
+ assert(vk);
+ assert(ret_crypt_key);
+ assert(ret_opal_key);
+
+ jobj_segment = LUKS2_get_segment_jobj(hdr, CRYPT_DEFAULT_SEGMENT);
+ if (!jobj_segment)
+ return -EINVAL;
+
+ r = json_segment_get_opal_segment_id(jobj_segment, &opal_segment_number);
+ if (r < 0)
+ return -EINVAL;
+
+ r = json_segment_get_opal_key_size(jobj_segment, &opal_user_key_size);
+ if (r < 0)
+ return -EINVAL;
+
+ if (vk->keylength < opal_user_key_size)
+ return -EINVAL;
+
+ /* OPAL SEGMENT only */
+ if (vk->keylength == opal_user_key_size) {
+ *ret_crypt_key = NULL;
+ *ret_opal_key = NULL;
+ return 0;
+ }
+
+ opal_key = crypt_alloc_volume_key(opal_user_key_size, vk->key);
+ if (!opal_key)
+ return -ENOMEM;
+
+ crypt_key = crypt_alloc_volume_key(vk->keylength - opal_user_key_size,
+ vk->key + opal_user_key_size);
+ if (!crypt_key) {
+ crypt_free_volume_key(opal_key);
+ return -ENOMEM;
+ }
+
+ *ret_opal_key = opal_key;
+ *ret_crypt_key = crypt_key;
+
+ return 0;
+}
diff --git a/lib/luks2/luks2_keyslot.c b/lib/luks2/luks2_keyslot.c
index 5cf4b83..40816eb 100644
--- a/lib/luks2/luks2_keyslot.c
+++ b/lib/luks2/luks2_keyslot.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2, keyslot handling
*
- * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2023 Milan Broz
+ * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -578,6 +578,8 @@ int LUKS2_keyslot_open(struct crypt_device *cd,
int r_prio, r = -EINVAL;
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
+ if (!hdr)
+ return -EINVAL;
if (keyslot == CRYPT_ANY_SLOT) {
r_prio = LUKS2_keyslot_open_priority(cd, hdr, CRYPT_SLOT_PRIORITY_PREFER,
@@ -676,8 +678,7 @@ int LUKS2_keyslot_store(struct crypt_device *cd,
int LUKS2_keyslot_wipe(struct crypt_device *cd,
struct luks2_hdr *hdr,
- int keyslot,
- int wipe_area_only)
+ int keyslot)
{
struct device *device = crypt_metadata_device(cd);
uint64_t area_offset, area_length;
@@ -694,9 +695,6 @@ int LUKS2_keyslot_wipe(struct crypt_device *cd,
if (!jobj_keyslot)
return -ENOENT;
- if (wipe_area_only)
- log_dbg(cd, "Wiping keyslot %d area only.", keyslot);
-
r = LUKS2_device_write_lock(cd, hdr, device);
if (r)
return r;
@@ -720,9 +718,6 @@ int LUKS2_keyslot_wipe(struct crypt_device *cd,
}
}
- if (wipe_area_only)
- goto out;
-
/* Slot specific wipe */
if (h) {
r = h->wipe(cd, keyslot);
@@ -803,6 +798,9 @@ int placeholder_keyslot_alloc(struct crypt_device *cd,
return -EINVAL;
jobj_keyslot = json_object_new_object();
+ if (!jobj_keyslot)
+ return -ENOMEM;
+
json_object_object_add(jobj_keyslot, "type", json_object_new_string("placeholder"));
/*
* key_size = -1 makes placeholder keyslot impossible to pass validation.
@@ -813,11 +811,19 @@ int placeholder_keyslot_alloc(struct crypt_device *cd,
/* Area object */
jobj_area = json_object_new_object();
+ if (!jobj_area) {
+ json_object_put(jobj_keyslot);
+ return -ENOMEM;
+ }
+
json_object_object_add(jobj_area, "offset", crypt_jobj_new_uint64(area_offset));
json_object_object_add(jobj_area, "size", crypt_jobj_new_uint64(area_length));
json_object_object_add(jobj_keyslot, "area", jobj_area);
- json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot);
+ if (json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot)) {
+ json_object_put(jobj_keyslot);
+ return -EINVAL;
+ }
return 0;
}
@@ -899,7 +905,7 @@ int LUKS2_keyslots_validate(struct crypt_device *cd, json_object *hdr_jobj)
return 0;
}
-void LUKS2_keyslots_repair(struct crypt_device *cd, json_object *jobj_keyslots)
+void LUKS2_keyslots_repair(struct crypt_device *cd __attribute__((unused)), json_object *jobj_keyslots)
{
const keyslot_handler *h;
json_object *jobj_type;
@@ -964,14 +970,17 @@ int LUKS2_keyslot_swap(struct crypt_device *cd, struct luks2_hdr *hdr,
json_object_object_del_by_uint(jobj_keyslots, keyslot);
r = json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot2);
if (r < 0) {
+ json_object_put(jobj_keyslot2);
log_dbg(cd, "Failed to swap keyslot %d.", keyslot);
return r;
}
json_object_object_del_by_uint(jobj_keyslots, keyslot2);
r = json_object_object_add_by_uint(jobj_keyslots, keyslot2, jobj_keyslot);
- if (r < 0)
+ if (r < 0) {
+ json_object_put(jobj_keyslot);
log_dbg(cd, "Failed to swap keyslot2 %d.", keyslot2);
+ }
return r;
}
diff --git a/lib/luks2/luks2_keyslot_luks2.c b/lib/luks2/luks2_keyslot_luks2.c
index 491dcad..2c1f400 100644
--- a/lib/luks2/luks2_keyslot_luks2.c
+++ b/lib/luks2/luks2_keyslot_luks2.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2, LUKS2 type keyslot handler
*
- * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2023 Milan Broz
+ * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -307,7 +307,7 @@ static int luks2_keyslot_get_key(struct crypt_device *cd,
char *volume_key, size_t volume_key_len)
{
struct volume_key *derived_key = NULL;
- struct crypt_pbkdf_type pbkdf;
+ struct crypt_pbkdf_type pbkdf, *cd_pbkdf;
char *AfKey = NULL;
size_t AFEKSize;
const char *af_hash = NULL;
@@ -361,6 +361,16 @@ static int luks2_keyslot_get_key(struct crypt_device *cd,
}
/*
+ * Print warning when keyslot requires more memory than available
+ * (if maximum memory was adjusted - no swap, not enough memory),
+ * but be silent if user set keyslot memory cost above default limit intentionally.
+ */
+ cd_pbkdf = crypt_get_pbkdf(cd);
+ if (cd_pbkdf->max_memory_kb && pbkdf.max_memory_kb > cd_pbkdf->max_memory_kb &&
+ pbkdf.max_memory_kb <= DEFAULT_LUKS2_MEMORY_KB)
+ log_std(cd, _("Warning: keyslot operation could fail as it requires more than available memory.\n"));
+
+ /*
* If requested, serialize unlocking for memory-hard KDF. Usually NOOP.
*/
if (pbkdf.max_memory_kb > MIN_MEMORY_FOR_SERIALIZE_LOCK_KB)
@@ -512,23 +522,42 @@ static int luks2_keyslot_alloc(struct crypt_device *cd,
}
jobj_keyslot = json_object_new_object();
+ if (!jobj_keyslot) {
+ r = -ENOMEM;
+ goto err;
+ }
+
json_object_object_add(jobj_keyslot, "type", json_object_new_string("luks2"));
json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(volume_key_len));
/* AF object */
jobj_af = json_object_new_object();
+ if (!jobj_af) {
+ r = -ENOMEM;
+ goto err;
+ }
+
json_object_object_add(jobj_af, "type", json_object_new_string("luks1"));
json_object_object_add(jobj_af, "stripes", json_object_new_int(params->af.luks1.stripes));
json_object_object_add(jobj_keyslot, "af", jobj_af);
/* Area object */
jobj_area = json_object_new_object();
+ if (!jobj_area) {
+ r = -ENOMEM;
+ goto err;
+ }
+
json_object_object_add(jobj_area, "type", json_object_new_string("raw"));
json_object_object_add(jobj_area, "offset", crypt_jobj_new_uint64(area_offset));
json_object_object_add(jobj_area, "size", crypt_jobj_new_uint64(area_length));
json_object_object_add(jobj_keyslot, "area", jobj_area);
- json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot);
+ r = json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot);
+ if (r) {
+ json_object_put(jobj_keyslot);
+ return r;
+ }
r = luks2_keyslot_update_json(cd, jobj_keyslot, params);
@@ -541,6 +570,9 @@ static int luks2_keyslot_alloc(struct crypt_device *cd,
json_object_object_del_by_uint(jobj_keyslots, keyslot);
return r;
+err:
+ json_object_put(jobj_keyslot);
+ return r;
}
static int luks2_keyslot_open(struct crypt_device *cd,
diff --git a/lib/luks2/luks2_keyslot_reenc.c b/lib/luks2/luks2_keyslot_reenc.c
index 4291d0c..e847673 100644
--- a/lib/luks2/luks2_keyslot_reenc.c
+++ b/lib/luks2/luks2_keyslot_reenc.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2, reencryption keyslot handler
*
- * Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2016-2023 Ondrej Kozina
+ * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2016-2024 Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -145,7 +145,12 @@ static int reenc_keyslot_alloc(struct crypt_device *cd,
else
json_object_object_add(jobj_keyslot, "direction", json_object_new_string("backward"));
- json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot);
+ r = json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot);
+ if (r) {
+ json_object_put(jobj_keyslot);
+ return r;
+ }
+
if (LUKS2_check_json_size(cd, hdr)) {
log_dbg(cd, "New keyslot too large to fit in free metadata space.");
json_object_object_del_by_uint(jobj_keyslots, keyslot);
@@ -371,8 +376,7 @@ static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_key
return 0;
}
-static int reenc_keyslot_update_needed(struct crypt_device *cd,
- json_object *jobj_keyslot,
+static int reenc_keyslot_update_needed(json_object *jobj_keyslot,
const struct crypt_params_reencrypt *params,
size_t alignment)
{
@@ -537,8 +541,7 @@ static int reenc_keyslot_load_resilience(struct crypt_device *cd,
return reenc_keyslot_load_resilience_secondary(cd, type, jobj_area, area_length, rp);
}
-static bool reenc_keyslot_update_is_valid(struct crypt_device *cd,
- json_object *jobj_area,
+static bool reenc_keyslot_update_is_valid(json_object *jobj_area,
const struct crypt_params_reencrypt *params)
{
const char *type;
@@ -589,7 +592,7 @@ static int reenc_keyslot_update(struct crypt_device *cd,
if (!params || !params->resilience)
jobj_area_new = reencrypt_keyslot_area_jobj_update_block_size(cd, jobj_area, alignment);
else {
- if (!reenc_keyslot_update_is_valid(cd, jobj_area, params)) {
+ if (!reenc_keyslot_update_is_valid(jobj_area, params)) {
log_err(cd, _("Invalid reencryption resilience mode change requested."));
return -EINVAL;
}
@@ -661,7 +664,7 @@ int LUKS2_keyslot_reencrypt_update_needed(struct crypt_device *cd,
strcmp(json_object_get_string(jobj_type), "reencrypt"))
return -EINVAL;
- r = reenc_keyslot_update_needed(cd, jobj_keyslot, params, alignment);
+ r = reenc_keyslot_update_needed(jobj_keyslot, params, alignment);
if (!r)
log_dbg(cd, "No update of reencrypt keyslot needed.");
diff --git a/lib/luks2/luks2_luks1_convert.c b/lib/luks2/luks2_luks1_convert.c
index 6d3fa1e..9513217 100644
--- a/lib/luks2/luks2_luks1_convert.c
+++ b/lib/luks2/luks2_luks1_convert.c
@@ -1,9 +1,9 @@
/*
* LUKS - Linux Unified Key Setup v2, LUKS1 conversion code
*
- * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2023 Ondrej Kozina
- * Copyright (C) 2015-2023 Milan Broz
+ * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2024 Ondrej Kozina
+ * Copyright (C) 2015-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -67,11 +67,21 @@ static int json_luks1_keyslot(const struct luks_phdr *hdr_v1, int keyslot, struc
int r;
keyslot_obj = json_object_new_object();
+ if (!keyslot_obj) {
+ r = -ENOMEM;
+ goto err;
+ }
+
json_object_object_add(keyslot_obj, "type", json_object_new_string("luks2"));
json_object_object_add(keyslot_obj, "key_size", json_object_new_int64(hdr_v1->keyBytes));
/* KDF */
jobj_kdf = json_object_new_object();
+ if (!jobj_kdf) {
+ r = -ENOMEM;
+ goto err;
+ }
+
json_object_object_add(jobj_kdf, "type", json_object_new_string(CRYPT_KDF_PBKDF2));
json_object_object_add(jobj_kdf, "hash", json_object_new_string(hdr_v1->hashSpec));
json_object_object_add(jobj_kdf, "iterations", json_object_new_int64(hdr_v1->keyblock[keyslot].passwordIterations));
@@ -89,6 +99,11 @@ static int json_luks1_keyslot(const struct luks_phdr *hdr_v1, int keyslot, struc
/* AF */
jobj_af = json_object_new_object();
+ if (!jobj_af) {
+ r = -ENOMEM;
+ goto err;
+ }
+
json_object_object_add(jobj_af, "type", json_object_new_string("luks1"));
json_object_object_add(jobj_af, "hash", json_object_new_string(hdr_v1->hashSpec));
/* stripes field ignored, fixed to LUKS_STRIPES (4000) */
@@ -97,6 +112,11 @@ static int json_luks1_keyslot(const struct luks_phdr *hdr_v1, int keyslot, struc
/* Area */
jobj_area = json_object_new_object();
+ if (!jobj_area) {
+ r = -ENOMEM;
+ goto err;
+ }
+
json_object_object_add(jobj_area, "type", json_object_new_string("raw"));
/* encryption algorithm field */
@@ -124,6 +144,9 @@ static int json_luks1_keyslot(const struct luks_phdr *hdr_v1, int keyslot, struc
*keyslot_object = keyslot_obj;
return 0;
+err:
+ json_object_put(keyslot_obj);
+ return r;
}
static int json_luks1_keyslots(const struct luks_phdr *hdr_v1, struct json_object **keyslots_object)
@@ -143,7 +166,12 @@ static int json_luks1_keyslots(const struct luks_phdr *hdr_v1, struct json_objec
json_object_put(keyslot_obj);
return r;
}
- json_object_object_add_by_uint(keyslot_obj, keyslot, field);
+ r = json_object_object_add_by_uint(keyslot_obj, keyslot, field);
+ if (r) {
+ json_object_put(field);
+ json_object_put(keyslot_obj);
+ return r;
+ }
}
*keyslots_object = keyslot_obj;
@@ -238,7 +266,12 @@ static int json_luks1_segments(const struct luks_phdr *hdr_v1, struct json_objec
json_object_put(segments_obj);
return r;
}
- json_object_object_add_by_uint(segments_obj, 0, field);
+ r = json_object_object_add_by_uint(segments_obj, 0, field);
+ if (r) {
+ json_object_put(field);
+ json_object_put(segments_obj);
+ return r;
+ }
*segments_object = segments_obj;
return 0;
diff --git a/lib/luks2/luks2_reencrypt.c b/lib/luks2/luks2_reencrypt.c
index b0dcd6d..b7af206 100644
--- a/lib/luks2/luks2_reencrypt.c
+++ b/lib/luks2/luks2_reencrypt.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2, reencryption helpers
*
- * Copyright (C) 2015-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2015-2023 Ondrej Kozina
+ * Copyright (C) 2015-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2015-2024 Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -162,6 +162,7 @@ static uint64_t reencrypt_get_data_offset_old(struct luks2_hdr *hdr)
return reencrypt_data_offset(hdr, 0);
}
#endif
+
static int reencrypt_digest(struct luks2_hdr *hdr, unsigned new)
{
int segment = LUKS2_get_segment_id_by_flag(hdr, new ? "backup-final" : "backup-previous");
@@ -182,6 +183,21 @@ int LUKS2_reencrypt_digest_old(struct luks2_hdr *hdr)
return reencrypt_digest(hdr, 0);
}
+unsigned LUKS2_reencrypt_vks_count(struct luks2_hdr *hdr)
+{
+ int digest_old, digest_new;
+ unsigned vks_count = 0;
+
+ if ((digest_new = LUKS2_reencrypt_digest_new(hdr)) >= 0)
+ vks_count++;
+ if ((digest_old = LUKS2_reencrypt_digest_old(hdr)) >= 0) {
+ if (digest_old != digest_new)
+ vks_count++;
+ }
+
+ return vks_count;
+}
+
/* none, checksums, journal or shift */
static const char *reencrypt_resilience_type(struct luks2_hdr *hdr)
{
@@ -224,7 +240,7 @@ static const char *reencrypt_resilience_hash(struct luks2_hdr *hdr)
static json_object *_enc_create_segments_shift_after(struct luks2_reencrypt *rh, uint64_t data_offset)
{
int reenc_seg, i = 0;
- json_object *jobj_copy, *jobj_seg_new = NULL, *jobj_segs_post = json_object_new_object();
+ json_object *jobj, *jobj_copy = NULL, *jobj_seg_new = NULL, *jobj_segs_post = json_object_new_object();
uint64_t tmp;
if (!rh->jobj_segs_hot || !jobj_segs_post)
@@ -239,17 +255,21 @@ static json_object *_enc_create_segments_shift_after(struct luks2_reencrypt *rh,
while (i < reenc_seg) {
jobj_copy = json_segments_get_segment(rh->jobj_segs_hot, i);
- if (!jobj_copy)
+ if (!jobj_copy || json_object_object_add_by_uint(jobj_segs_post, i++, json_object_get(jobj_copy)))
goto err;
- json_object_object_add_by_uint(jobj_segs_post, i++, json_object_get(jobj_copy));
}
+ jobj_copy = NULL;
- if (json_object_copy(json_segments_get_segment(rh->jobj_segs_hot, reenc_seg + 1), &jobj_seg_new)) {
- if (json_object_copy(json_segments_get_segment(rh->jobj_segs_hot, reenc_seg), &jobj_seg_new))
+ jobj = json_segments_get_segment(rh->jobj_segs_hot, reenc_seg + 1);
+ if (!jobj) {
+ jobj = json_segments_get_segment(rh->jobj_segs_hot, reenc_seg);
+ if (!jobj || json_object_copy(jobj, &jobj_seg_new))
goto err;
json_segment_remove_flag(jobj_seg_new, "in-reencryption");
tmp = rh->length;
} else {
+ if (json_object_copy(jobj, &jobj_seg_new))
+ goto err;
json_object_object_add(jobj_seg_new, "offset", crypt_jobj_new_uint64(rh->offset + data_offset));
json_object_object_add(jobj_seg_new, "iv_tweak", crypt_jobj_new_uint64(rh->offset >> SECTOR_SHIFT));
tmp = json_segment_get_size(jobj_seg_new, 0) + rh->length;
@@ -257,10 +277,12 @@ static json_object *_enc_create_segments_shift_after(struct luks2_reencrypt *rh,
/* alter size of new segment, reenc_seg == 0 we're finished */
json_object_object_add(jobj_seg_new, "size", reenc_seg > 0 ? crypt_jobj_new_uint64(tmp) : json_object_new_string("dynamic"));
- json_object_object_add_by_uint(jobj_segs_post, reenc_seg, jobj_seg_new);
+ if (!json_object_object_add_by_uint(jobj_segs_post, reenc_seg, jobj_seg_new))
+ return jobj_segs_post;
- return jobj_segs_post;
err:
+ json_object_put(jobj_seg_new);
+ json_object_put(jobj_copy);
json_object_put(jobj_segs_post);
return NULL;
}
@@ -271,7 +293,7 @@ static json_object *reencrypt_make_hot_segments_encrypt_shift(struct luks2_hdr *
{
int sg, crypt_seg, i = 0;
uint64_t segment_size;
- json_object *jobj_seg_shrunk, *jobj_seg_new, *jobj_copy, *jobj_enc_seg = NULL,
+ json_object *jobj_seg_shrunk = NULL, *jobj_seg_new = NULL, *jobj_copy = NULL, *jobj_enc_seg = NULL,
*jobj_segs_hot = json_object_new_object();
if (!jobj_segs_hot)
@@ -290,38 +312,41 @@ static json_object *reencrypt_make_hot_segments_encrypt_shift(struct luks2_hdr *
rh->offset >> SECTOR_SHIFT,
&rh->length,
reencrypt_segment_cipher_new(hdr),
+ NULL, /* integrity */
reencrypt_get_sector_size_new(hdr),
1);
while (i < sg) {
jobj_copy = LUKS2_get_segment_jobj(hdr, i);
- if (!jobj_copy)
+ if (!jobj_copy || json_object_object_add_by_uint(jobj_segs_hot, i++, json_object_get(jobj_copy)))
goto err;
- json_object_object_add_by_uint(jobj_segs_hot, i++, json_object_get(jobj_copy));
}
+ jobj_copy = NULL;
segment_size = LUKS2_segment_size(hdr, sg, 0);
if (segment_size > rh->length) {
- jobj_seg_shrunk = NULL;
if (json_object_copy(LUKS2_get_segment_jobj(hdr, sg), &jobj_seg_shrunk))
goto err;
json_object_object_add(jobj_seg_shrunk, "size", crypt_jobj_new_uint64(segment_size - rh->length));
- json_object_object_add_by_uint(jobj_segs_hot, sg++, jobj_seg_shrunk);
+ if (json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg++, &jobj_seg_shrunk))
+ goto err;
}
- json_object_object_add_by_uint(jobj_segs_hot, sg++, jobj_enc_seg);
- jobj_enc_seg = NULL; /* see err: label */
+ if (json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg++, &jobj_enc_seg))
+ goto err;
/* first crypt segment after encryption ? */
if (crypt_seg >= 0) {
jobj_seg_new = LUKS2_get_segment_jobj(hdr, crypt_seg);
- if (!jobj_seg_new)
+ if (!jobj_seg_new || json_object_object_add_by_uint(jobj_segs_hot, sg, json_object_get(jobj_seg_new)))
goto err;
- json_object_object_add_by_uint(jobj_segs_hot, sg, json_object_get(jobj_seg_new));
}
return jobj_segs_hot;
err:
+ json_object_put(jobj_copy);
+ json_object_put(jobj_seg_new);
+ json_object_put(jobj_seg_shrunk);
json_object_put(jobj_enc_seg);
json_object_put(jobj_segs_hot);
@@ -343,6 +368,7 @@ static json_object *reencrypt_make_segment_new(struct crypt_device *cd,
crypt_get_iv_offset(cd) + (iv_offset >> SECTOR_SHIFT),
segment_length,
reencrypt_segment_cipher_new(hdr),
+ NULL, /* integrity */
reencrypt_get_sector_size_new(hdr), 0);
case CRYPT_REENCRYPT_DECRYPT:
return json_segment_create_linear(data_offset + segment_offset, segment_length, 0);
@@ -357,7 +383,7 @@ static json_object *reencrypt_make_post_segments_forward(struct crypt_device *cd
uint64_t data_offset)
{
int reenc_seg;
- json_object *jobj_new_seg_after, *jobj_old_seg, *jobj_old_seg_copy = NULL,
+ json_object *jobj_old_seg, *jobj_new_seg_after = NULL, *jobj_old_seg_copy = NULL,
*jobj_segs_post = json_object_new_object();
uint64_t fixed_length = rh->offset + rh->length;
@@ -366,7 +392,7 @@ static json_object *reencrypt_make_post_segments_forward(struct crypt_device *cd
reenc_seg = json_segments_segment_in_reencrypt(rh->jobj_segs_hot);
if (reenc_seg < 0)
- return NULL;
+ goto err;
jobj_old_seg = json_segments_get_segment(rh->jobj_segs_hot, reenc_seg + 1);
@@ -375,24 +401,26 @@ static json_object *reencrypt_make_post_segments_forward(struct crypt_device *cd
* Set size to 'dynamic' again.
*/
jobj_new_seg_after = reencrypt_make_segment_new(cd, hdr, rh, data_offset, 0, 0, jobj_old_seg ? &fixed_length : NULL);
- if (!jobj_new_seg_after)
+ if (!jobj_new_seg_after || json_object_object_add_by_uint_by_ref(jobj_segs_post, 0, &jobj_new_seg_after))
goto err;
- json_object_object_add_by_uint(jobj_segs_post, 0, jobj_new_seg_after);
if (jobj_old_seg) {
if (rh->fixed_length) {
if (json_object_copy(jobj_old_seg, &jobj_old_seg_copy))
goto err;
- jobj_old_seg = jobj_old_seg_copy;
fixed_length = rh->device_size - fixed_length;
- json_object_object_add(jobj_old_seg, "size", crypt_jobj_new_uint64(fixed_length));
+ json_object_object_add(jobj_old_seg_copy, "size", crypt_jobj_new_uint64(fixed_length));
} else
- json_object_get(jobj_old_seg);
- json_object_object_add_by_uint(jobj_segs_post, 1, jobj_old_seg);
+ jobj_old_seg_copy = json_object_get(jobj_old_seg);
+
+ if (json_object_object_add_by_uint_by_ref(jobj_segs_post, 1, &jobj_old_seg_copy))
+ goto err;
}
return jobj_segs_post;
err:
+ json_object_put(jobj_new_seg_after);
+ json_object_put(jobj_old_seg_copy);
json_object_put(jobj_segs_post);
return NULL;
}
@@ -405,7 +433,7 @@ static json_object *reencrypt_make_post_segments_backward(struct crypt_device *c
int reenc_seg;
uint64_t fixed_length;
- json_object *jobj_new_seg_after, *jobj_old_seg,
+ json_object *jobj_new_seg_after = NULL, *jobj_old_seg = NULL,
*jobj_segs_post = json_object_new_object();
if (!rh->jobj_segs_hot || !jobj_segs_post)
@@ -413,22 +441,26 @@ static json_object *reencrypt_make_post_segments_backward(struct crypt_device *c
reenc_seg = json_segments_segment_in_reencrypt(rh->jobj_segs_hot);
if (reenc_seg < 0)
- return NULL;
+ goto err;
jobj_old_seg = json_segments_get_segment(rh->jobj_segs_hot, reenc_seg - 1);
- if (jobj_old_seg)
- json_object_object_add_by_uint(jobj_segs_post, reenc_seg - 1, json_object_get(jobj_old_seg));
+ if (jobj_old_seg) {
+ json_object_get(jobj_old_seg);
+ if (json_object_object_add_by_uint_by_ref(jobj_segs_post, reenc_seg - 1, &jobj_old_seg))
+ goto err;
+ }
+
if (rh->fixed_length && rh->offset) {
fixed_length = rh->device_size - rh->offset;
jobj_new_seg_after = reencrypt_make_segment_new(cd, hdr, rh, data_offset, rh->offset, rh->offset, &fixed_length);
} else
jobj_new_seg_after = reencrypt_make_segment_new(cd, hdr, rh, data_offset, rh->offset, rh->offset, NULL);
- if (!jobj_new_seg_after)
- goto err;
- json_object_object_add_by_uint(jobj_segs_post, reenc_seg, jobj_new_seg_after);
- return jobj_segs_post;
+ if (jobj_new_seg_after && !json_object_object_add_by_uint(jobj_segs_post, reenc_seg, jobj_new_seg_after))
+ return jobj_segs_post;
err:
+ json_object_put(jobj_new_seg_after);
+ json_object_put(jobj_old_seg);
json_object_put(jobj_segs_post);
return NULL;
}
@@ -448,6 +480,7 @@ static json_object *reencrypt_make_segment_reencrypt(struct crypt_device *cd,
crypt_get_iv_offset(cd) + (iv_offset >> SECTOR_SHIFT),
segment_length,
reencrypt_segment_cipher_new(hdr),
+ NULL, /* integrity */
reencrypt_get_sector_size_new(hdr), 1);
case CRYPT_REENCRYPT_DECRYPT:
return json_segment_create_linear(data_offset + segment_offset, segment_length, 1);
@@ -472,6 +505,7 @@ static json_object *reencrypt_make_segment_old(struct crypt_device *cd,
crypt_get_iv_offset(cd) + (segment_offset >> SECTOR_SHIFT),
segment_length,
reencrypt_segment_cipher_old(hdr),
+ NULL, /* integrity */
reencrypt_get_sector_size_old(hdr),
0);
break;
@@ -488,38 +522,40 @@ static json_object *reencrypt_make_hot_segments_forward(struct crypt_device *cd,
uint64_t device_size,
uint64_t data_offset)
{
- json_object *jobj_segs_hot, *jobj_reenc_seg, *jobj_old_seg, *jobj_new_seg;
uint64_t fixed_length, tmp = rh->offset + rh->length;
+ json_object *jobj_segs_hot = json_object_new_object(), *jobj_reenc_seg = NULL,
+ *jobj_old_seg = NULL, *jobj_new_seg = NULL;
unsigned int sg = 0;
- jobj_segs_hot = json_object_new_object();
if (!jobj_segs_hot)
return NULL;
if (rh->offset) {
jobj_new_seg = reencrypt_make_segment_new(cd, hdr, rh, data_offset, 0, 0, &rh->offset);
- if (!jobj_new_seg)
+ if (!jobj_new_seg || json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg++, &jobj_new_seg))
goto err;
- json_object_object_add_by_uint(jobj_segs_hot, sg++, jobj_new_seg);
}
jobj_reenc_seg = reencrypt_make_segment_reencrypt(cd, hdr, rh, data_offset, rh->offset, rh->offset, &rh->length);
if (!jobj_reenc_seg)
goto err;
- json_object_object_add_by_uint(jobj_segs_hot, sg++, jobj_reenc_seg);
+ if (json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg++, &jobj_reenc_seg))
+ goto err;
if (tmp < device_size) {
fixed_length = device_size - tmp;
jobj_old_seg = reencrypt_make_segment_old(cd, hdr, rh, data_offset + data_shift_value(&rh->rp),
rh->offset + rh->length, rh->fixed_length ? &fixed_length : NULL);
- if (!jobj_old_seg)
+ if (!jobj_old_seg || json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg, &jobj_old_seg))
goto err;
- json_object_object_add_by_uint(jobj_segs_hot, sg, jobj_old_seg);
}
return jobj_segs_hot;
err:
+ json_object_put(jobj_reenc_seg);
+ json_object_put(jobj_old_seg);
+ json_object_put(jobj_new_seg);
json_object_put(jobj_segs_hot);
return NULL;
}
@@ -528,29 +564,31 @@ static json_object *reencrypt_make_hot_segments_decrypt_shift(struct crypt_devic
struct luks2_hdr *hdr, struct luks2_reencrypt *rh,
uint64_t device_size, uint64_t data_offset)
{
- json_object *jobj_segs_hot, *jobj_reenc_seg, *jobj_old_seg, *jobj_new_seg;
uint64_t fixed_length, tmp = rh->offset + rh->length, linear_length = rh->progress;
+ json_object *jobj, *jobj_segs_hot = json_object_new_object(), *jobj_reenc_seg = NULL,
+ *jobj_old_seg = NULL, *jobj_new_seg = NULL;
unsigned int sg = 0;
- jobj_segs_hot = json_object_new_object();
if (!jobj_segs_hot)
return NULL;
if (rh->offset) {
- jobj_new_seg = LUKS2_get_segment_jobj(hdr, 0);
- if (!jobj_new_seg)
+ jobj = LUKS2_get_segment_jobj(hdr, 0);
+ if (!jobj)
+ goto err;
+
+ jobj_new_seg = json_object_get(jobj);
+ if (json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg++, &jobj_new_seg))
goto err;
- json_object_object_add_by_uint(jobj_segs_hot, sg++, json_object_get(jobj_new_seg));
if (linear_length) {
jobj_new_seg = reencrypt_make_segment_new(cd, hdr, rh,
data_offset,
- json_segment_get_size(jobj_new_seg, 0),
+ json_segment_get_size(jobj, 0),
0,
&linear_length);
- if (!jobj_new_seg)
+ if (!jobj_new_seg || json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg++, &jobj_new_seg))
goto err;
- json_object_object_add_by_uint(jobj_segs_hot, sg++, jobj_new_seg);
}
}
@@ -558,27 +596,29 @@ static json_object *reencrypt_make_hot_segments_decrypt_shift(struct crypt_devic
rh->offset,
rh->offset,
&rh->length);
- if (!jobj_reenc_seg)
+ if (!jobj_reenc_seg || json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg++, &jobj_reenc_seg))
goto err;
- json_object_object_add_by_uint(jobj_segs_hot, sg++, jobj_reenc_seg);
-
- if (!rh->offset && (jobj_new_seg = LUKS2_get_segment_jobj(hdr, 1)) &&
- !json_segment_is_backup(jobj_new_seg))
- json_object_object_add_by_uint(jobj_segs_hot, sg++, json_object_get(jobj_new_seg));
- else if (tmp < device_size) {
+ if (!rh->offset && (jobj = LUKS2_get_segment_jobj(hdr, 1)) &&
+ !json_segment_is_backup(jobj)) {
+ jobj_new_seg = json_object_get(jobj);
+ if (json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg++, &jobj_new_seg))
+ goto err;
+ } else if (tmp < device_size) {
fixed_length = device_size - tmp;
jobj_old_seg = reencrypt_make_segment_old(cd, hdr, rh,
data_offset + data_shift_value(&rh->rp),
rh->offset + rh->length,
rh->fixed_length ? &fixed_length : NULL);
- if (!jobj_old_seg)
+ if (!jobj_old_seg || json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg, &jobj_old_seg))
goto err;
- json_object_object_add_by_uint(jobj_segs_hot, sg, jobj_old_seg);
}
return jobj_segs_hot;
err:
+ json_object_put(jobj_reenc_seg);
+ json_object_put(jobj_old_seg);
+ json_object_put(jobj_new_seg);
json_object_put(jobj_segs_hot);
return NULL;
}
@@ -589,7 +629,7 @@ static json_object *_dec_create_segments_shift_after(struct crypt_device *cd,
uint64_t data_offset)
{
int reenc_seg, i = 0;
- json_object *jobj_copy, *jobj_seg_old, *jobj_seg_new,
+ json_object *jobj_seg_old, *jobj_copy = NULL, *jobj_seg_old_copy = NULL, *jobj_seg_new = NULL,
*jobj_segs_post = json_object_new_object();
unsigned segs;
uint64_t tmp;
@@ -607,9 +647,8 @@ static json_object *_dec_create_segments_shift_after(struct crypt_device *cd,
if (reenc_seg == 0) {
jobj_seg_new = reencrypt_make_segment_new(cd, hdr, rh, data_offset, 0, 0, NULL);
- if (!jobj_seg_new)
+ if (!jobj_seg_new || json_object_object_add_by_uint(jobj_segs_post, 0, jobj_seg_new))
goto err;
- json_object_object_add_by_uint(jobj_segs_post, 0, jobj_seg_new);
return jobj_segs_post;
}
@@ -617,22 +656,29 @@ static json_object *_dec_create_segments_shift_after(struct crypt_device *cd,
jobj_copy = json_segments_get_segment(rh->jobj_segs_hot, 0);
if (!jobj_copy)
goto err;
- json_object_object_add_by_uint(jobj_segs_post, i++, json_object_get(jobj_copy));
+ json_object_get(jobj_copy);
+ if (json_object_object_add_by_uint_by_ref(jobj_segs_post, i++, &jobj_copy))
+ goto err;
- jobj_seg_old = json_segments_get_segment(rh->jobj_segs_hot, reenc_seg + 1);
+ if ((jobj_seg_old = json_segments_get_segment(rh->jobj_segs_hot, reenc_seg + 1)))
+ jobj_seg_old_copy = json_object_get(jobj_seg_old);
tmp = rh->length + rh->progress;
jobj_seg_new = reencrypt_make_segment_new(cd, hdr, rh, data_offset,
json_segment_get_size(rh->jobj_segment_moved, 0),
data_shift_value(&rh->rp),
jobj_seg_old ? &tmp : NULL);
- json_object_object_add_by_uint(jobj_segs_post, i++, jobj_seg_new);
+ if (!jobj_seg_new || json_object_object_add_by_uint_by_ref(jobj_segs_post, i++, &jobj_seg_new))
+ goto err;
- if (jobj_seg_old)
- json_object_object_add_by_uint(jobj_segs_post, i, json_object_get(jobj_seg_old));
+ if (jobj_seg_old_copy && json_object_object_add_by_uint(jobj_segs_post, i, jobj_seg_old_copy))
+ goto err;
return jobj_segs_post;
err:
+ json_object_put(jobj_copy);
+ json_object_put(jobj_seg_old_copy);
+ json_object_put(jobj_seg_new);
json_object_put(jobj_segs_post);
return NULL;
}
@@ -643,10 +689,10 @@ static json_object *reencrypt_make_hot_segments_backward(struct crypt_device *cd
uint64_t device_size,
uint64_t data_offset)
{
- json_object *jobj_reenc_seg, *jobj_new_seg, *jobj_old_seg = NULL,
+ uint64_t fixed_length, tmp = rh->offset + rh->length;
+ json_object *jobj_reenc_seg = NULL, *jobj_new_seg = NULL, *jobj_old_seg = NULL,
*jobj_segs_hot = json_object_new_object();
int sg = 0;
- uint64_t fixed_length, tmp = rh->offset + rh->length;
if (!jobj_segs_hot)
return NULL;
@@ -656,26 +702,27 @@ static json_object *reencrypt_make_hot_segments_backward(struct crypt_device *cd
goto err;
json_object_object_add(jobj_old_seg, "size", crypt_jobj_new_uint64(rh->offset));
- json_object_object_add_by_uint(jobj_segs_hot, sg++, jobj_old_seg);
+ if (json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg++, &jobj_old_seg))
+ goto err;
}
jobj_reenc_seg = reencrypt_make_segment_reencrypt(cd, hdr, rh, data_offset, rh->offset, rh->offset, &rh->length);
- if (!jobj_reenc_seg)
+ if (!jobj_reenc_seg || json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg++, &jobj_reenc_seg))
goto err;
- json_object_object_add_by_uint(jobj_segs_hot, sg++, jobj_reenc_seg);
-
if (tmp < device_size) {
fixed_length = device_size - tmp;
jobj_new_seg = reencrypt_make_segment_new(cd, hdr, rh, data_offset, rh->offset + rh->length,
rh->offset + rh->length, rh->fixed_length ? &fixed_length : NULL);
- if (!jobj_new_seg)
+ if (!jobj_new_seg || json_object_object_add_by_uint_by_ref(jobj_segs_hot, sg, &jobj_new_seg))
goto err;
- json_object_object_add_by_uint(jobj_segs_hot, sg, jobj_new_seg);
}
return jobj_segs_hot;
err:
+ json_object_put(jobj_reenc_seg);
+ json_object_put(jobj_new_seg);
+ json_object_put(jobj_old_seg);
json_object_put(jobj_segs_hot);
return NULL;
}
@@ -733,6 +780,7 @@ static int reencrypt_make_post_segments(struct crypt_device *cd,
return rh->jobj_segs_post ? 0 : -EINVAL;
}
#endif
+
static uint64_t reencrypt_data_shift(struct luks2_hdr *hdr)
{
json_object *jobj_keyslot, *jobj_area, *jobj_data_shift;
@@ -847,13 +895,13 @@ void LUKS2_reencrypt_free(struct crypt_device *cd, struct luks2_reencrypt *rh)
free(rh);
}
-int LUKS2_reencrypt_max_hotzone_size(struct crypt_device *cd,
+#if USE_LUKS2_REENCRYPTION
+int LUKS2_reencrypt_max_hotzone_size(struct crypt_device *cd __attribute__((unused)),
struct luks2_hdr *hdr,
const struct reenc_protection *rp,
int reencrypt_keyslot,
uint64_t *r_length)
{
-#if USE_LUKS2_REENCRYPTION
int r;
uint64_t dummy, area_length;
@@ -886,11 +934,8 @@ int LUKS2_reencrypt_max_hotzone_size(struct crypt_device *cd,
}
return -EINVAL;
-#else
- return -ENOTSUP;
-#endif
}
-#if USE_LUKS2_REENCRYPTION
+
static size_t reencrypt_get_alignment(struct crypt_device *cd,
struct luks2_hdr *hdr)
{
@@ -971,7 +1016,6 @@ static int reencrypt_offset_backward_moved(struct luks2_hdr *hdr, json_object *j
}
static int reencrypt_offset_forward_moved(struct luks2_hdr *hdr,
- json_object *jobj_segments,
uint64_t data_shift,
uint64_t *offset)
{
@@ -1049,7 +1093,7 @@ static int reencrypt_offset(struct luks2_hdr *hdr,
if (di == CRYPT_REENCRYPT_FORWARD) {
if (reencrypt_mode(hdr) == CRYPT_REENCRYPT_DECRYPT &&
LUKS2_get_segment_id_by_flag(hdr, "backup-moved-segment") >= 0) {
- r = reencrypt_offset_forward_moved(hdr, jobj_segments, data_shift, offset);
+ r = reencrypt_offset_forward_moved(hdr, data_shift, offset);
if (!r && *offset > device_size)
*offset = device_size;
return r;
@@ -1386,7 +1430,7 @@ static int reencrypt_init_storage_wrappers(struct crypt_device *cd,
static int reencrypt_context_set_names(struct luks2_reencrypt *rh, const char *name)
{
- if (!rh | !name)
+ if (!rh || !name)
return -EINVAL;
if (*name == '/') {
@@ -1964,9 +2008,7 @@ static int reencrypt_set_decrypt_shift_segments(struct crypt_device *cd,
crypt_reencrypt_direction_info di)
{
int r;
- uint64_t first_segment_offset, first_segment_length,
- second_segment_offset, second_segment_length,
- data_offset = LUKS2_get_data_offset(hdr) << SECTOR_SHIFT;
+ uint64_t data_offset = LUKS2_get_data_offset(hdr) << SECTOR_SHIFT;
json_object *jobj_segment_first = NULL, *jobj_segment_second = NULL, *jobj_segments;
if (di == CRYPT_REENCRYPT_BACKWARD)
@@ -1976,47 +2018,49 @@ static int reencrypt_set_decrypt_shift_segments(struct crypt_device *cd,
* future data_device layout:
* [encrypted first segment (max data shift size)][gap (data shift size)][second encrypted data segment]
*/
- first_segment_offset = 0;
- first_segment_length = moved_segment_length;
- if (dev_size > moved_segment_length) {
- second_segment_offset = data_offset + first_segment_length;
- second_segment_length = 0;
- }
-
jobj_segments = json_object_new_object();
if (!jobj_segments)
return -ENOMEM;
r = -EINVAL;
- jobj_segment_first = json_segment_create_crypt(first_segment_offset,
- crypt_get_iv_offset(cd), &first_segment_length,
- crypt_get_cipher_spec(cd), crypt_get_sector_size(cd), 0);
+ jobj_segment_first = json_segment_create_crypt(0, crypt_get_iv_offset(cd),
+ &moved_segment_length, crypt_get_cipher_spec(cd),
+ NULL, crypt_get_sector_size(cd), 0);
if (!jobj_segment_first) {
log_dbg(cd, "Failed generate 1st segment.");
- return r;
+ goto err;
}
+ r = json_object_object_add_by_uint_by_ref(jobj_segments, 0, &jobj_segment_first);
+ if (r)
+ goto err;
+
if (dev_size > moved_segment_length) {
- jobj_segment_second = json_segment_create_crypt(second_segment_offset,
- crypt_get_iv_offset(cd) + (first_segment_length >> SECTOR_SHIFT),
- second_segment_length ? &second_segment_length : NULL,
+ jobj_segment_second = json_segment_create_crypt(data_offset + moved_segment_length,
+ crypt_get_iv_offset(cd) + (moved_segment_length >> SECTOR_SHIFT),
+ NULL,
crypt_get_cipher_spec(cd),
+ NULL, /* integrity */
crypt_get_sector_size(cd), 0);
if (!jobj_segment_second) {
- json_object_put(jobj_segment_first);
+ r = -EINVAL;
log_dbg(cd, "Failed generate 2nd segment.");
- return r;
+ goto err;
}
- }
-
- json_object_object_add(jobj_segments, "0", jobj_segment_first);
- if (jobj_segment_second)
- json_object_object_add(jobj_segments, "1", jobj_segment_second);
- r = LUKS2_segments_set(cd, hdr, jobj_segments, 0);
+ r = json_object_object_add_by_uint_by_ref(jobj_segments, 1, &jobj_segment_second);
+ if (r)
+ goto err;
+ }
- return r ?: LUKS2_digest_segment_assign(cd, hdr, CRYPT_ANY_SEGMENT, 0, 1, 0);
+ if (!(r = LUKS2_segments_set(cd, hdr, jobj_segments, 0)))
+ return LUKS2_digest_segment_assign(cd, hdr, CRYPT_ANY_SEGMENT, 0, 1, 0);
+err:
+ json_object_put(jobj_segment_first);
+ json_object_put(jobj_segment_second);
+ json_object_put(jobj_segments);
+ return r;
}
static int reencrypt_make_targets(struct crypt_device *cd,
@@ -2429,6 +2473,7 @@ static int reencrypt_make_backup_segments(struct crypt_device *cd,
uint64_t data_offset,
const struct crypt_params_reencrypt *params)
{
+ const char *type;
int r, segment, moved_segment = -1, digest_old = -1, digest_new = -1;
json_object *jobj_tmp, *jobj_segment_new = NULL, *jobj_segment_old = NULL, *jobj_segment_bcp = NULL;
uint32_t sector_size = params->luks2 ? params->luks2->sector_size : SECTOR_SIZE;
@@ -2460,9 +2505,17 @@ static int reencrypt_make_backup_segments(struct crypt_device *cd,
if (r)
goto err;
moved_segment = segment++;
- json_object_object_add_by_uint(LUKS2_get_segments_jobj(hdr), moved_segment, jobj_segment_bcp);
- if (!strcmp(json_segment_type(jobj_segment_bcp), "crypt"))
- LUKS2_digest_segment_assign(cd, hdr, moved_segment, digest_old, 1, 0);
+ r = json_object_object_add_by_uint_by_ref(LUKS2_get_segments_jobj(hdr), moved_segment, &jobj_segment_bcp);
+ if (r)
+ goto err;
+
+ if (!(type = json_segment_type(LUKS2_get_segment_jobj(hdr, moved_segment)))) {
+ r = -EINVAL;
+ goto err;
+ }
+
+ if (!strcmp(type, "crypt") && ((r = LUKS2_digest_segment_assign(cd, hdr, moved_segment, digest_old, 1, 0))))
+ goto err;
}
/* FIXME: Add detection for case (digest old == digest new && old segment == new segment) */
@@ -2478,6 +2531,7 @@ static int reencrypt_make_backup_segments(struct crypt_device *cd,
json_segment_get_iv_offset(jobj_tmp),
device_size ? &device_size : NULL,
json_segment_get_cipher(jobj_tmp),
+ NULL, /* integrity */
json_segment_get_sector_size(jobj_tmp),
0);
} else {
@@ -2505,10 +2559,14 @@ static int reencrypt_make_backup_segments(struct crypt_device *cd,
r = LUKS2_segment_set_flag(jobj_segment_old, "backup-previous");
if (r)
goto err;
- json_object_object_add_by_uint(LUKS2_get_segments_jobj(hdr), segment, jobj_segment_old);
- jobj_segment_old = NULL;
- if (digest_old >= 0)
- LUKS2_digest_segment_assign(cd, hdr, segment, digest_old, 1, 0);
+
+ r = json_object_object_add_by_uint_by_ref(LUKS2_get_segments_jobj(hdr), segment, &jobj_segment_old);
+ if (r)
+ goto err;
+
+ if (digest_old >= 0 && (r = LUKS2_digest_segment_assign(cd, hdr, segment, digest_old, 1, 0)))
+ goto err;
+
segment++;
if (digest_new >= 0) {
@@ -2520,7 +2578,7 @@ static int reencrypt_make_backup_segments(struct crypt_device *cd,
}
jobj_segment_new = json_segment_create_crypt(segment_offset,
crypt_get_iv_offset(cd),
- NULL, cipher, sector_size, 0);
+ NULL, cipher, NULL, sector_size, 0);
} else if (params->mode == CRYPT_REENCRYPT_DECRYPT) {
segment_offset = data_offset;
if (modify_offset(&segment_offset, data_shift, params->direction)) {
@@ -2538,10 +2596,13 @@ static int reencrypt_make_backup_segments(struct crypt_device *cd,
r = LUKS2_segment_set_flag(jobj_segment_new, "backup-final");
if (r)
goto err;
- json_object_object_add_by_uint(LUKS2_get_segments_jobj(hdr), segment, jobj_segment_new);
- jobj_segment_new = NULL;
- if (digest_new >= 0)
- LUKS2_digest_segment_assign(cd, hdr, segment, digest_new, 1, 0);
+
+ r = json_object_object_add_by_uint_by_ref(LUKS2_get_segments_jobj(hdr), segment, &jobj_segment_new);
+ if (r)
+ goto err;
+
+ if (digest_new >= 0 && (r = LUKS2_digest_segment_assign(cd, hdr, segment, digest_new, 1, 0)))
+ goto err;
/* FIXME: also check occupied space by keyslot in shrunk area */
if (params->direction == CRYPT_REENCRYPT_FORWARD && data_shift &&
@@ -2556,6 +2617,7 @@ static int reencrypt_make_backup_segments(struct crypt_device *cd,
err:
json_object_put(jobj_segment_new);
json_object_put(jobj_segment_old);
+ json_object_put(jobj_segment_bcp);
return r;
}
@@ -2590,7 +2652,6 @@ static int reencrypt_verify_keys(struct crypt_device *cd,
}
static int reencrypt_upload_single_key(struct crypt_device *cd,
- struct luks2_hdr *hdr,
int digest,
struct volume_key *vks)
{
@@ -2615,11 +2676,11 @@ static int reencrypt_upload_keys(struct crypt_device *cd,
return 0;
if (digest_new >= 0 && !crypt_is_cipher_null(reencrypt_segment_cipher_new(hdr)) &&
- (r = reencrypt_upload_single_key(cd, hdr, digest_new, vks)))
+ (r = reencrypt_upload_single_key(cd, digest_new, vks)))
return r;
if (digest_old >= 0 && !crypt_is_cipher_null(reencrypt_segment_cipher_old(hdr)) &&
- (r = reencrypt_upload_single_key(cd, hdr, digest_old, vks))) {
+ (r = reencrypt_upload_single_key(cd, digest_old, vks))) {
crypt_drop_keyring_key(cd, vks);
return r;
}
@@ -3256,7 +3317,17 @@ static int reencrypt_load(struct crypt_device *cd, struct luks2_hdr *hdr,
return 0;
}
+#else
+int LUKS2_reencrypt_max_hotzone_size(struct crypt_device *cd __attribute__((unused)),
+ struct luks2_hdr *hdr __attribute__((unused)),
+ const struct reenc_protection *rp __attribute__((unused)),
+ int reencrypt_keyslot __attribute__((unused)),
+ uint64_t *r_length __attribute__((unused)))
+{
+ return -ENOTSUP;
+}
#endif
+
static int reencrypt_lock_internal(struct crypt_device *cd, const char *uuid, struct crypt_lock_handle **reencrypt_lock)
{
int r;
@@ -3705,7 +3776,7 @@ out:
return r;
}
-#endif
+
static int reencrypt_init_by_passphrase(struct crypt_device *cd,
const char *name,
const char *passphrase,
@@ -3716,7 +3787,6 @@ static int reencrypt_init_by_passphrase(struct crypt_device *cd,
const char *cipher_mode,
const struct crypt_params_reencrypt *params)
{
-#if USE_LUKS2_REENCRYPTION
int r;
crypt_reencrypt_info ri;
struct volume_key *vks = NULL;
@@ -3778,11 +3848,22 @@ out:
crypt_drop_keyring_key(cd, vks);
crypt_free_volume_key(vks);
return r < 0 ? r : LUKS2_find_keyslot(hdr, "reencrypt");
+}
#else
+static int reencrypt_init_by_passphrase(struct crypt_device *cd,
+ const char *name __attribute__((unused)),
+ const char *passphrase __attribute__((unused)),
+ size_t passphrase_size __attribute__((unused)),
+ int keyslot_old __attribute__((unused)),
+ int keyslot_new __attribute__((unused)),
+ const char *cipher __attribute__((unused)),
+ const char *cipher_mode __attribute__((unused)),
+ const struct crypt_params_reencrypt *params __attribute__((unused)))
+{
log_err(cd, _("This operation is not supported for this device type."));
return -ENOTSUP;
-#endif
}
+#endif
int crypt_reencrypt_init_by_keyring(struct crypt_device *cd,
const char *name,
@@ -3797,14 +3878,20 @@ int crypt_reencrypt_init_by_keyring(struct crypt_device *cd,
char *passphrase;
size_t passphrase_size;
- if (onlyLUKS2mask(cd, CRYPT_REQUIREMENT_ONLINE_REENCRYPT) || !passphrase_description)
+ if (onlyLUKS2reencrypt(cd) || !passphrase_description)
return -EINVAL;
if (params && (params->flags & CRYPT_REENCRYPT_INITIALIZE_ONLY) && (params->flags & CRYPT_REENCRYPT_RESUME_ONLY))
return -EINVAL;
- r = keyring_get_passphrase(passphrase_description, &passphrase, &passphrase_size);
+ if (device_is_dax(crypt_data_device(cd)) > 0) {
+ log_err(cd, _("Reencryption is not supported for DAX (persistent memory) devices."));
+ return -EINVAL;
+ }
+
+ r = crypt_keyring_get_user_key(cd, passphrase_description, &passphrase, &passphrase_size);
if (r < 0) {
- log_err(cd, _("Failed to read passphrase from keyring (error %d)."), r);
+ log_dbg(cd, "crypt_keyring_get_user_key failed (error %d)", r);
+ log_err(cd, _("Failed to read passphrase from keyring."));
return -EINVAL;
}
@@ -3826,11 +3913,16 @@ int crypt_reencrypt_init_by_passphrase(struct crypt_device *cd,
const char *cipher_mode,
const struct crypt_params_reencrypt *params)
{
- if (onlyLUKS2mask(cd, CRYPT_REQUIREMENT_ONLINE_REENCRYPT) || !passphrase)
+ if (onlyLUKS2reencrypt(cd) || !passphrase)
return -EINVAL;
if (params && (params->flags & CRYPT_REENCRYPT_INITIALIZE_ONLY) && (params->flags & CRYPT_REENCRYPT_RESUME_ONLY))
return -EINVAL;
+ if (device_is_dax(crypt_data_device(cd)) > 0) {
+ log_err(cd, _("Reencryption is not supported for DAX (persistent memory) devices."));
+ return -EINVAL;
+ }
+
return reencrypt_init_by_passphrase(cd, name, passphrase, passphrase_size, keyslot_old, keyslot_new, cipher, cipher_mode, params);
}
@@ -4112,14 +4204,12 @@ static int reencrypt_teardown(struct crypt_device *cd, struct luks2_hdr *hdr,
return r;
}
-#endif
int crypt_reencrypt_run(
struct crypt_device *cd,
int (*progress)(uint64_t size, uint64_t offset, void *usrptr),
void *usrptr)
{
-#if USE_LUKS2_REENCRYPTION
int r;
crypt_reencrypt_info ri;
struct luks2_hdr *hdr;
@@ -4127,7 +4217,7 @@ int crypt_reencrypt_run(
reenc_status_t rs;
bool quit = false;
- if (onlyLUKS2mask(cd, CRYPT_REQUIREMENT_ONLINE_REENCRYPT))
+ if (onlyLUKS2reencrypt(cd))
return -EINVAL;
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
@@ -4180,19 +4270,9 @@ int crypt_reencrypt_run(
r = reencrypt_teardown(cd, hdr, rh, rs, quit, progress, usrptr);
return r;
-#else
- log_err(cd, _("This operation is not supported for this device type."));
- return -ENOTSUP;
-#endif
}
-int crypt_reencrypt(
- struct crypt_device *cd,
- int (*progress)(uint64_t size, uint64_t offset, void *usrptr))
-{
- return crypt_reencrypt_run(cd, progress, NULL);
-}
-#if USE_LUKS2_REENCRYPTION
+
static int reencrypt_recovery(struct crypt_device *cd,
struct luks2_hdr *hdr,
uint64_t device_size,
@@ -4228,7 +4308,27 @@ out:
return r;
}
+#else /* USE_LUKS2_REENCRYPTION */
+int crypt_reencrypt_run(
+ struct crypt_device *cd,
+ int (*progress)(uint64_t size, uint64_t offset, void *usrptr),
+ void *usrptr)
+{
+ UNUSED(progress);
+ UNUSED(usrptr);
+
+ log_err(cd, _("This operation is not supported for this device type."));
+ return -ENOTSUP;
+}
#endif
+
+int crypt_reencrypt(
+ struct crypt_device *cd,
+ int (*progress)(uint64_t size, uint64_t offset, void *usrptr))
+{
+ return crypt_reencrypt_run(cd, progress, NULL);
+}
+
/*
* use only for calculation of minimal data device size.
* The real data offset is taken directly from segments!
@@ -4246,7 +4346,7 @@ int LUKS2_reencrypt_data_offset(struct luks2_hdr *hdr, bool blockwise)
/* internal only */
int LUKS2_reencrypt_check_device_size(struct crypt_device *cd, struct luks2_hdr *hdr,
- uint64_t check_size, uint64_t *dev_size, bool activation, bool dynamic)
+ uint64_t check_size, uint64_t *dev_size, bool device_exclusive_check, bool dynamic)
{
int r;
uint64_t data_offset, real_size = 0;
@@ -4255,7 +4355,8 @@ int LUKS2_reencrypt_check_device_size(struct crypt_device *cd, struct luks2_hdr
(LUKS2_get_segment_by_flag(hdr, "backup-moved-segment") || dynamic))
check_size += reencrypt_data_shift(hdr);
- r = device_check_access(cd, crypt_data_device(cd), activation ? DEV_EXCL : DEV_OK);
+ r = device_check_access(cd, crypt_data_device(cd),
+ device_exclusive_check ? DEV_EXCL : DEV_OK);
if (r)
return r;
@@ -4333,6 +4434,39 @@ out:
return r < 0 ? r : keyslot;
}
+
+int LUKS2_reencrypt_locked_recovery_by_vks(struct crypt_device *cd,
+ struct volume_key *vks)
+{
+ uint64_t minimal_size, device_size;
+ int r = -EINVAL;
+ struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
+ struct volume_key *vk = NULL;
+
+ log_dbg(cd, "Entering reencryption crash recovery.");
+
+ if (LUKS2_get_data_size(hdr, &minimal_size, NULL))
+ return r;
+
+ if (crypt_use_keyring_for_vk(cd))
+ vk = vks;
+ while (vk) {
+ r = LUKS2_volume_key_load_in_keyring_by_digest(cd, vk, crypt_volume_key_get_id(vk));
+ if (r < 0)
+ goto out;
+ vk = crypt_volume_key_next(vk);
+ }
+
+ if (LUKS2_reencrypt_check_device_size(cd, hdr, minimal_size, &device_size, true, false))
+ goto out;
+
+ r = reencrypt_recovery(cd, hdr, device_size, vks);
+
+out:
+ if (r < 0)
+ crypt_drop_keyring_key(cd, vks);
+ return r;
+}
#endif
crypt_reencrypt_info LUKS2_reencrypt_get_params(struct luks2_hdr *hdr,
struct crypt_params_reencrypt *params)
diff --git a/lib/luks2/luks2_reencrypt_digest.c b/lib/luks2/luks2_reencrypt_digest.c
index bc86f54..fcdad12 100644
--- a/lib/luks2/luks2_reencrypt_digest.c
+++ b/lib/luks2/luks2_reencrypt_digest.c
@@ -1,9 +1,9 @@
/*
* LUKS - Linux Unified Key Setup v2, reencryption digest helpers
*
- * Copyright (C) 2022-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2022-2023 Ondrej Kozina
- * Copyright (C) 2022-2023 Milan Broz
+ * Copyright (C) 2022-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2022-2024 Ondrej Kozina
+ * Copyright (C) 2022-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -375,6 +375,22 @@ int LUKS2_keyslot_reencrypt_digest_create(struct crypt_device *cd,
return LUKS2_digest_assign(cd, hdr, keyslot_reencrypt, digest_reencrypt, 1, 0);
}
+void LUKS2_reencrypt_lookup_key_ids(struct crypt_device *cd, struct luks2_hdr *hdr, struct volume_key *vk)
+{
+ int digest_old, digest_new;
+
+ digest_old = LUKS2_reencrypt_digest_old(hdr);
+ digest_new = LUKS2_reencrypt_digest_new(hdr);
+
+ while (vk) {
+ if (digest_old >= 0 && LUKS2_digest_verify_by_digest(cd, digest_old, vk) == digest_old)
+ crypt_volume_key_set_id(vk, digest_old);
+ if (digest_new >= 0 && LUKS2_digest_verify_by_digest(cd, digest_new, vk) == digest_new)
+ crypt_volume_key_set_id(vk, digest_new);
+ vk = vk->next;
+ }
+}
+
int LUKS2_reencrypt_digest_verify(struct crypt_device *cd,
struct luks2_hdr *hdr,
struct volume_key *vks)
diff --git a/lib/luks2/luks2_segment.c b/lib/luks2/luks2_segment.c
index 63e7c14..af87f4f 100644
--- a/lib/luks2/luks2_segment.c
+++ b/lib/luks2/luks2_segment.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2, internal segment handling
*
- * Copyright (C) 2018-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2018-2023 Ondrej Kozina
+ * Copyright (C) 2018-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2018-2024 Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -91,6 +91,33 @@ uint64_t json_segment_get_size(json_object *jobj_segment, unsigned blockwise)
return blockwise ? crypt_jobj_get_uint64(jobj) >> SECTOR_SHIFT : crypt_jobj_get_uint64(jobj);
}
+static uint64_t json_segment_get_opal_size(json_object *jobj_segment, unsigned blockwise)
+{
+ json_object *jobj;
+
+ if (!jobj_segment ||
+ !json_object_object_get_ex(jobj_segment, "opal_segment_size", &jobj))
+ return 0;
+
+ return blockwise ? crypt_jobj_get_uint64(jobj) >> SECTOR_SHIFT : crypt_jobj_get_uint64(jobj);
+}
+
+static bool json_segment_set_size(json_object *jobj_segment, const uint64_t *size_bytes)
+{
+ json_object *jobj;
+
+ if (!jobj_segment)
+ return false;
+
+ jobj = size_bytes ? crypt_jobj_new_uint64(*size_bytes) : json_object_new_string("dynamic");
+ if (!jobj)
+ return false;
+
+ json_object_object_add(jobj_segment, "size", jobj);
+
+ return true;
+}
+
const char *json_segment_get_cipher(json_object *jobj_segment)
{
json_object *jobj;
@@ -116,6 +143,37 @@ uint32_t json_segment_get_sector_size(json_object *jobj_segment)
return i < 0 ? SECTOR_SIZE : i;
}
+int json_segment_get_opal_segment_id(json_object *jobj_segment, uint32_t *ret_opal_segment_id)
+{
+ json_object *jobj_segment_id;
+
+ assert(ret_opal_segment_id);
+
+ if (!json_object_object_get_ex(jobj_segment, "opal_segment_number", &jobj_segment_id))
+ return -EINVAL;
+
+ *ret_opal_segment_id = json_object_get_int(jobj_segment_id);
+
+ return 0;
+}
+
+int json_segment_get_opal_key_size(json_object *jobj_segment, size_t *ret_key_size)
+{
+ json_object *jobj_key_size;
+
+ assert(ret_key_size);
+
+ if (!jobj_segment)
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(jobj_segment, "opal_key_size", &jobj_key_size))
+ return -EINVAL;
+
+ *ret_key_size = json_object_get_int(jobj_key_size);
+
+ return 0;
+}
+
static json_object *json_segment_get_flags(json_object *jobj_segment)
{
json_object *jobj;
@@ -245,24 +303,94 @@ json_object *json_segment_create_linear(uint64_t offset, const uint64_t *length,
return jobj;
}
+static bool json_add_crypt_fields(json_object *jobj_segment, uint64_t iv_offset,
+ const char *cipher, const char *integrity,
+ uint32_t sector_size, unsigned reencryption)
+{
+ json_object *jobj_integrity;
+
+ assert(cipher);
+
+ json_object_object_add(jobj_segment, "iv_tweak", crypt_jobj_new_uint64(iv_offset));
+ json_object_object_add(jobj_segment, "encryption", json_object_new_string(cipher));
+ json_object_object_add(jobj_segment, "sector_size", json_object_new_int(sector_size));
+
+ if (integrity) {
+ jobj_integrity = json_object_new_object();
+ if (!jobj_integrity)
+ return false;
+
+ json_object_object_add(jobj_integrity, "type", json_object_new_string(integrity));
+ json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string("none"));
+ json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string("none"));
+ json_object_object_add(jobj_segment, "integrity", jobj_integrity);
+ }
+
+ if (reencryption)
+ LUKS2_segment_set_flag(jobj_segment, "in-reencryption");
+
+ return true;
+}
+
json_object *json_segment_create_crypt(uint64_t offset,
uint64_t iv_offset, const uint64_t *length,
- const char *cipher, uint32_t sector_size,
- unsigned reencryption)
+ const char *cipher, const char *integrity,
+ uint32_t sector_size, unsigned reencryption)
{
json_object *jobj = _segment_create_generic("crypt", offset, length);
+
if (!jobj)
return NULL;
- json_object_object_add(jobj, "iv_tweak", crypt_jobj_new_uint64(iv_offset));
- json_object_object_add(jobj, "encryption", json_object_new_string(cipher));
- json_object_object_add(jobj, "sector_size", json_object_new_int(sector_size));
- if (reencryption)
- LUKS2_segment_set_flag(jobj, "in-reencryption");
+ if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, sector_size, reencryption))
+ return jobj;
+
+ json_object_put(jobj);
+ return NULL;
+}
+
+static void json_add_opal_fields(json_object *jobj_segment, const uint64_t *length,
+ uint32_t segment_number, uint32_t key_size)
+{
+ assert(jobj_segment);
+ assert(length);
+
+ json_object_object_add(jobj_segment, "opal_segment_number", json_object_new_int(segment_number));
+ json_object_object_add(jobj_segment, "opal_key_size", json_object_new_int(key_size));
+ json_object_object_add(jobj_segment, "opal_segment_size", crypt_jobj_new_uint64(*length));
+}
+
+json_object *json_segment_create_opal(uint64_t offset, const uint64_t *length,
+ uint32_t segment_number, uint32_t key_size)
+{
+ json_object *jobj = _segment_create_generic("hw-opal", offset, length);
+ if (!jobj)
+ return NULL;
+
+ json_add_opal_fields(jobj, length, segment_number, key_size);
return jobj;
}
+json_object *json_segment_create_opal_crypt(uint64_t offset, const uint64_t *length,
+ uint32_t segment_number, uint32_t key_size,
+ uint64_t iv_offset, const char *cipher,
+ const char *integrity, uint32_t sector_size,
+ unsigned reencryption)
+{
+ json_object *jobj = _segment_create_generic("hw-opal-crypt", offset, length);
+ if (!jobj)
+ return NULL;
+
+ json_add_opal_fields(jobj, length, segment_number, key_size);
+
+ if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, sector_size, reencryption))
+ return jobj;
+
+ json_object_put(jobj);
+ return NULL;
+}
+
uint64_t LUKS2_segment_offset(struct luks2_hdr *hdr, int segment, unsigned blockwise)
{
return json_segment_get_offset(LUKS2_get_segment_jobj(hdr, segment), blockwise);
@@ -288,11 +416,85 @@ uint64_t LUKS2_segment_size(struct luks2_hdr *hdr, int segment, unsigned blockwi
return json_segment_get_size(LUKS2_get_segment_jobj(hdr, segment), blockwise);
}
+uint64_t LUKS2_opal_segment_size(struct luks2_hdr *hdr, int segment, unsigned blockwise)
+{
+ return json_segment_get_opal_size(LUKS2_get_segment_jobj(hdr, segment), blockwise);
+}
+
+bool LUKS2_segment_set_size(struct luks2_hdr *hdr, int segment, const uint64_t *segment_size_bytes)
+{
+ return json_segment_set_size(LUKS2_get_segment_jobj(hdr, segment), segment_size_bytes);
+}
+
int LUKS2_segment_is_type(struct luks2_hdr *hdr, int segment, const char *type)
{
return !strcmp(json_segment_type(LUKS2_get_segment_jobj(hdr, segment)) ?: "", type);
}
+static bool json_segment_is_hw_opal_only(json_object *jobj_segment)
+{
+ const char *type = json_segment_type(jobj_segment);
+
+ if (!type)
+ return false;
+
+ return !strcmp(type, "hw-opal");
+}
+
+static bool json_segment_is_hw_opal_crypt(json_object *jobj_segment)
+{
+ const char *type = json_segment_type(jobj_segment);
+
+ if (!type)
+ return false;
+
+ return !strcmp(type, "hw-opal-crypt");
+}
+
+static bool json_segment_is_hw_opal(json_object *jobj_segment)
+{
+ return json_segment_is_hw_opal_crypt(jobj_segment) ||
+ json_segment_is_hw_opal_only(jobj_segment);
+}
+
+bool LUKS2_segment_is_hw_opal_only(struct luks2_hdr *hdr, int segment)
+{
+ return json_segment_is_hw_opal_only(LUKS2_get_segment_jobj(hdr, segment));
+}
+
+bool LUKS2_segment_is_hw_opal_crypt(struct luks2_hdr *hdr, int segment)
+{
+ return json_segment_is_hw_opal_crypt(LUKS2_get_segment_jobj(hdr, segment));
+}
+
+bool LUKS2_segment_is_hw_opal(struct luks2_hdr *hdr, int segment)
+{
+ return json_segment_is_hw_opal(LUKS2_get_segment_jobj(hdr, segment));
+}
+
+int LUKS2_get_opal_segment_number(struct luks2_hdr *hdr, int segment, uint32_t *ret_opal_segment_number)
+{
+ json_object *jobj_segment = LUKS2_get_segment_jobj(hdr, segment);
+
+ assert(ret_opal_segment_number);
+
+ if (!json_segment_is_hw_opal(jobj_segment))
+ return -ENOENT;
+
+ return json_segment_get_opal_segment_id(jobj_segment, ret_opal_segment_number);
+}
+
+int LUKS2_get_opal_key_size(struct luks2_hdr *hdr, int segment)
+{
+ size_t key_size = 0;
+ json_object *jobj_segment = LUKS2_get_segment_jobj(hdr, segment);
+
+ if (json_segment_get_opal_key_size(jobj_segment, &key_size) < 0)
+ return 0;
+
+ return key_size;
+}
+
int LUKS2_last_segment_by_type(struct luks2_hdr *hdr, const char *type)
{
json_object *jobj_segments;
@@ -424,3 +626,27 @@ bool json_segment_cmp(json_object *jobj_segment_1, json_object *jobj_segment_2)
return true;
}
+
+bool LUKS2_segments_dynamic_size(struct luks2_hdr *hdr)
+{
+ json_object *jobj_segments, *jobj_size;
+
+ assert(hdr);
+
+ jobj_segments = LUKS2_get_segments_jobj(hdr);
+ if (!jobj_segments)
+ return false;
+
+ json_object_object_foreach(jobj_segments, key, val) {
+ UNUSED(key);
+
+ if (json_segment_is_backup(val))
+ continue;
+
+ if (json_object_object_get_ex(val, "size", &jobj_size) &&
+ !strcmp(json_object_get_string(jobj_size), "dynamic"))
+ return true;
+ }
+
+ return false;
+}
diff --git a/lib/luks2/luks2_token.c b/lib/luks2/luks2_token.c
index 5f65918..9c09be2 100644
--- a/lib/luks2/luks2_token.c
+++ b/lib/luks2/luks2_token.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2, token handling
*
- * Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2016-2023 Milan Broz
+ * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2016-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -25,7 +25,9 @@
#include "luks2_internal.h"
#if USE_EXTERNAL_TOKENS
+#define TOKENS_PATH_MAX PATH_MAX
static bool external_tokens_enabled = true;
+static char external_tokens_path[TOKENS_PATH_MAX] = EXTERNAL_LUKS2_TOKENS_PATH;
#else
static bool external_tokens_enabled = false;
#endif
@@ -51,31 +53,37 @@ void crypt_token_external_disable(void)
const char *crypt_token_external_path(void)
{
- return external_tokens_enabled ? EXTERNAL_LUKS2_TOKENS_PATH : NULL;
+#if USE_EXTERNAL_TOKENS
+ return external_tokens_enabled ? external_tokens_path : NULL;
+#else
+ return NULL;
+#endif
}
#if USE_EXTERNAL_TOKENS
-static void *token_dlvsym(struct crypt_device *cd,
- void *handle,
- const char *symbol,
- const char *version)
+int crypt_token_set_external_path(const char *path)
{
- char *error;
- void *sym;
+ int r;
+ char tokens_path[TOKENS_PATH_MAX];
-#ifdef HAVE_DLVSYM
- log_dbg(cd, "Loading symbol %s@%s.", symbol, version);
- sym = dlvsym(handle, symbol, version);
-#else
- log_dbg(cd, "Loading default version of symbol %s.", symbol);
- sym = dlsym(handle, symbol);
-#endif
- error = dlerror();
+ if (!path)
+ path = EXTERNAL_LUKS2_TOKENS_PATH;
+ else if (*path != '/')
+ return -EINVAL;
- if (error)
- log_dbg(cd, "%s", error);
+ r = snprintf(tokens_path, sizeof(tokens_path), "%s", path);
+ if (r < 0 || (size_t)r >= sizeof(tokens_path))
+ return -EINVAL;
- return sym;
+ (void)strcpy(external_tokens_path, tokens_path);
+
+ return 0;
+}
+#else
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+int crypt_token_set_external_path(const char *path)
+{
+ return -ENOTSUP;
}
#endif
@@ -98,6 +106,29 @@ static bool token_validate_v1(struct crypt_device *cd, const crypt_token_handler
}
#if USE_EXTERNAL_TOKENS
+static void *token_dlvsym(struct crypt_device *cd,
+ void *handle,
+ const char *symbol,
+ const char *version)
+{
+ char *error;
+ void *sym;
+
+#ifdef HAVE_DLVSYM
+ log_dbg(cd, "Loading symbol %s@%s.", symbol, version);
+ sym = dlvsym(handle, symbol, version);
+#else
+ log_dbg(cd, "Loading default version of symbol %s.", symbol);
+ sym = dlsym(handle, symbol);
+#endif
+ error = dlerror();
+
+ if (error)
+ log_dbg(cd, "%s", error);
+
+ return sym;
+}
+
static bool token_validate_v2(struct crypt_device *cd, const struct crypt_token_handler_internal *h)
{
if (!h)
@@ -127,12 +158,10 @@ static bool external_token_name_valid(const char *name)
return true;
}
-#endif
static int
crypt_token_load_external(struct crypt_device *cd, const char *name, struct crypt_token_handler_internal *ret)
{
-#if USE_EXTERNAL_TOKENS
struct crypt_token_handler_v2 *token;
void *h;
char buf[PATH_MAX];
@@ -192,11 +221,40 @@ crypt_token_load_external(struct crypt_device *cd, const char *name, struct cryp
ret->version = 2;
return 0;
-#else
+}
+
+void crypt_token_unload_external_all(struct crypt_device *cd)
+{
+ int i;
+
+ for (i = LUKS2_TOKENS_MAX - 1; i >= 0; i--) {
+ if (token_handlers[i].version < 2)
+ continue;
+
+ log_dbg(cd, "Unloading %s token handler.", token_handlers[i].u.v2.name);
+
+ free(CONST_CAST(void *)token_handlers[i].u.v2.name);
+
+ if (dlclose(CONST_CAST(void *)token_handlers[i].u.v2.dlhandle))
+ log_dbg(cd, "%s", dlerror());
+ }
+}
+
+#else /* USE_EXTERNAL_TOKENS */
+
+static int crypt_token_load_external(struct crypt_device *cd __attribute__((unused)),
+ const char *name __attribute__((unused)),
+ struct crypt_token_handler_internal *ret __attribute__((unused)))
+{
return -ENOTSUP;
-#endif
}
+void crypt_token_unload_external_all(struct crypt_device *cd __attribute__((unused)))
+{
+}
+
+#endif
+
static int is_builtin_candidate(const char *type)
{
return !strncmp(type, LUKS2_BUILTIN_TOKEN_PREFIX, LUKS2_BUILTIN_TOKEN_PREFIX_LEN);
@@ -243,25 +301,6 @@ int crypt_token_register(const crypt_token_handler *handler)
return 0;
}
-void crypt_token_unload_external_all(struct crypt_device *cd)
-{
-#if USE_EXTERNAL_TOKENS
- int i;
-
- for (i = LUKS2_TOKENS_MAX - 1; i >= 0; i--) {
- if (token_handlers[i].version < 2)
- continue;
-
- log_dbg(cd, "Unloading %s token handler.", token_handlers[i].u.v2.name);
-
- free(CONST_CAST(void *)token_handlers[i].u.v2.name);
-
- if (dlclose(CONST_CAST(void *)token_handlers[i].u.v2.dlhandle))
- log_dbg(cd, "%s", dlerror());
- }
-#endif
-}
-
static const void
*LUKS2_token_handler_type(struct crypt_device *cd, const char *type)
{
@@ -423,12 +462,12 @@ static const char *token_json_to_string(json_object *jobj_token)
JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE);
}
-static int token_is_usable(struct luks2_hdr *hdr, json_object *jobj_token, int segment,
+static int token_is_usable(struct luks2_hdr *hdr, json_object *jobj_token, int keyslot, int segment,
crypt_keyslot_priority minimal_priority, bool requires_keyslot)
{
crypt_keyslot_priority keyslot_priority;
json_object *jobj_array;
- int i, keyslot, len, r = -ENOENT;
+ int i, slot, len, r = -ENOENT;
if (!jobj_token)
return -EINVAL;
@@ -451,16 +490,19 @@ static int token_is_usable(struct luks2_hdr *hdr, json_object *jobj_token, int s
return -ENOENT;
for (i = 0; i < len; i++) {
- keyslot = atoi(json_object_get_string(json_object_array_get_idx(jobj_array, i)));
+ slot = atoi(json_object_get_string(json_object_array_get_idx(jobj_array, i)));
+
+ if (keyslot != CRYPT_ANY_SLOT && slot != keyslot)
+ continue;
- keyslot_priority = LUKS2_keyslot_priority_get(hdr, keyslot);
+ keyslot_priority = LUKS2_keyslot_priority_get(hdr, slot);
if (keyslot_priority == CRYPT_SLOT_PRIORITY_INVALID)
return -EINVAL;
if (keyslot_priority < minimal_priority)
continue;
- r = LUKS2_keyslot_for_segment(hdr, keyslot, segment);
+ r = LUKS2_keyslot_for_segment(hdr, slot, segment);
if (r != -ENOENT)
return r;
}
@@ -480,6 +522,7 @@ static int translate_errno(struct crypt_device *cd, int ret_val, const char *typ
static int token_open(struct crypt_device *cd,
struct luks2_hdr *hdr,
+ int keyslot,
int token,
json_object *jobj_token,
const char *type,
@@ -507,7 +550,7 @@ static int token_open(struct crypt_device *cd,
return -ENOENT;
}
- r = token_is_usable(hdr, jobj_token, segment, priority, requires_keyslot);
+ r = token_is_usable(hdr, jobj_token, keyslot, segment, priority, requires_keyslot);
if (r < 0) {
if (r == -ENOENT)
log_dbg(cd, "Token %d unusable for segment %d with desired keyslot priority %d.",
@@ -569,32 +612,22 @@ static void update_return_errno(int r, int *stored)
*stored = r;
}
-static int LUKS2_keyslot_open_by_token(struct crypt_device *cd,
+static int try_token_keyslot_unlock(struct crypt_device *cd,
struct luks2_hdr *hdr,
+ const char *type,
+ json_object *jobj_token_keyslots,
int token,
int segment,
crypt_keyslot_priority priority,
const char *buffer,
size_t buffer_len,
- struct volume_key **vk)
+ struct volume_key **r_vk)
{
+ json_object *jobj;
crypt_keyslot_priority keyslot_priority;
- json_object *jobj_token, *jobj_token_keyslots, *jobj_type, *jobj;
- unsigned int num = 0;
int i, r = -ENOENT, stored_retval = -ENOENT;
+ unsigned int num = 0;
- jobj_token = LUKS2_get_token_jobj(hdr, token);
- if (!jobj_token)
- return -EINVAL;
-
- if (!json_object_object_get_ex(jobj_token, "type", &jobj_type))
- return -EINVAL;
-
- json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots);
- if (!jobj_token_keyslots)
- return -EINVAL;
-
- /* Try to open keyslot referenced in token */
for (i = 0; i < (int) json_object_array_length(jobj_token_keyslots) && r < 0; i++) {
jobj = json_object_array_get_idx(jobj_token_keyslots, i);
num = atoi(json_object_get_string(jobj));
@@ -604,8 +637,8 @@ static int LUKS2_keyslot_open_by_token(struct crypt_device *cd,
if (keyslot_priority < priority)
continue;
log_dbg(cd, "Trying to open keyslot %u with token %d (type %s).",
- num, token, json_object_get_string(jobj_type));
- r = LUKS2_keyslot_open(cd, num, segment, buffer, buffer_len, vk);
+ num, token, type);
+ r = LUKS2_keyslot_open(cd, num, segment, buffer, buffer_len, r_vk);
/* short circuit on fatal error */
if (r < 0 && r != -EPERM && r != -ENOENT)
return r;
@@ -620,6 +653,53 @@ static int LUKS2_keyslot_open_by_token(struct crypt_device *cd,
return num;
}
+static int LUKS2_keyslot_open_by_token(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ int keyslot,
+ int token,
+ int segment,
+ crypt_keyslot_priority min_priority,
+ const char *buffer,
+ size_t buffer_len,
+ struct volume_key **vk)
+{
+ json_object *jobj_token, *jobj_token_keyslots, *jobj_type;
+ crypt_keyslot_priority priority = CRYPT_SLOT_PRIORITY_PREFER;
+ int r = -ENOENT, stored_retval = -ENOENT;
+
+ jobj_token = LUKS2_get_token_jobj(hdr, token);
+ if (!jobj_token)
+ return -EINVAL;
+
+ if (!json_object_object_get_ex(jobj_token, "type", &jobj_type))
+ return -EINVAL;
+
+ json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots);
+ if (!jobj_token_keyslots)
+ return -EINVAL;
+
+ /* with specific keyslot just ignore priorities and unlock */
+ if (keyslot != CRYPT_ANY_SLOT) {
+ log_dbg(cd, "Trying to open keyslot %u with token %d (type %s).",
+ keyslot, token, json_object_get_string(jobj_type));
+ return LUKS2_keyslot_open(cd, keyslot, segment, buffer, buffer_len, vk);
+ }
+
+ /* Try to open keyslot referenced in token */
+ while (priority >= min_priority) {
+ r = try_token_keyslot_unlock(cd, hdr, json_object_get_string(jobj_type),
+ jobj_token_keyslots, token, segment,
+ priority, buffer, buffer_len, vk);
+ if (r == -EINVAL || r >= 0)
+ return r;
+ if (r == -EPERM)
+ stored_retval = r;
+ priority--;
+ }
+
+ return stored_retval;
+}
+
static bool token_is_blocked(int token, uint32_t *block_list)
{
/* it is safe now, but have assert in case LUKS2_TOKENS_MAX grows */
@@ -640,6 +720,7 @@ static int token_open_priority(struct crypt_device *cd,
struct luks2_hdr *hdr,
json_object *jobj_tokens,
const char *type,
+ int keyslot,
int segment,
crypt_keyslot_priority priority,
const char *pin,
@@ -660,9 +741,10 @@ static int token_open_priority(struct crypt_device *cd,
token = atoi(slot);
if (token_is_blocked(token, block_list))
continue;
- r = token_open(cd, hdr, token, val, type, segment, priority, pin, pin_size, &buffer, &buffer_size, usrptr, true);
+ r = token_open(cd, hdr, keyslot, token, val, type, segment, priority, pin, pin_size,
+ &buffer, &buffer_size, usrptr, true);
if (!r) {
- r = LUKS2_keyslot_open_by_token(cd, hdr, token, segment, priority,
+ r = LUKS2_keyslot_open_by_token(cd, hdr, keyslot, token, segment, priority,
buffer, buffer_size, vk);
LUKS2_token_buffer_free(cd, token, buffer, buffer_size);
}
@@ -679,8 +761,9 @@ static int token_open_priority(struct crypt_device *cd,
return *stored_retval;
}
-static int token_open_any(struct crypt_device *cd, struct luks2_hdr *hdr, const char *type, int segment,
- const char *pin, size_t pin_size, void *usrptr, struct volume_key **vk)
+static int token_open_any(struct crypt_device *cd, struct luks2_hdr *hdr, const char *type,
+ int keyslot, int segment, const char *pin, size_t pin_size, void *usrptr,
+ struct volume_key **vk)
{
json_object *jobj_tokens;
int r, retval = -ENOENT;
@@ -692,17 +775,22 @@ static int token_open_any(struct crypt_device *cd, struct luks2_hdr *hdr, const
if (!type)
usrptr = NULL;
- r = token_open_priority(cd, hdr, jobj_tokens, type, segment, CRYPT_SLOT_PRIORITY_PREFER,
+ if (keyslot != CRYPT_ANY_SLOT)
+ return token_open_priority(cd, hdr, jobj_tokens, type, keyslot, segment, CRYPT_SLOT_PRIORITY_IGNORE,
+ pin, pin_size, usrptr, &retval, &blocked, vk);
+
+ r = token_open_priority(cd, hdr, jobj_tokens, type, keyslot, segment, CRYPT_SLOT_PRIORITY_PREFER,
pin, pin_size, usrptr, &retval, &blocked, vk);
if (break_loop_retval(r))
return r;
- return token_open_priority(cd, hdr, jobj_tokens, type, segment, CRYPT_SLOT_PRIORITY_NORMAL,
+ return token_open_priority(cd, hdr, jobj_tokens, type, keyslot, segment, CRYPT_SLOT_PRIORITY_NORMAL,
pin, pin_size, usrptr, &retval, &blocked, vk);
}
int LUKS2_token_unlock_key(struct crypt_device *cd,
struct luks2_hdr *hdr,
+ int keyslot,
int token,
const char *type,
const char *pin,
@@ -714,6 +802,7 @@ int LUKS2_token_unlock_key(struct crypt_device *cd,
char *buffer;
size_t buffer_size;
json_object *jobj_token;
+ crypt_keyslot_priority min_priority;
int r = -ENOENT;
assert(vk);
@@ -724,13 +813,27 @@ int LUKS2_token_unlock_key(struct crypt_device *cd,
if (segment < 0 && segment != CRYPT_ANY_SEGMENT)
return -EINVAL;
+ if (keyslot != CRYPT_ANY_SLOT || token != CRYPT_ANY_TOKEN)
+ min_priority = CRYPT_SLOT_PRIORITY_IGNORE;
+ else
+ min_priority = CRYPT_SLOT_PRIORITY_NORMAL;
+
+ if (keyslot != CRYPT_ANY_SLOT) {
+ r = LUKS2_keyslot_for_segment(hdr, keyslot, segment);
+ if (r < 0) {
+ if (r == -ENOENT)
+ log_dbg(cd, "Keyslot %d unusable for segment %d.", keyslot, segment);
+ return r;
+ }
+ }
+
if (token >= 0 && token < LUKS2_TOKENS_MAX) {
if ((jobj_token = LUKS2_get_token_jobj(hdr, token))) {
- r = token_open(cd, hdr, token, jobj_token, type, segment, CRYPT_SLOT_PRIORITY_IGNORE,
+ r = token_open(cd, hdr, keyslot, token, jobj_token, type, segment, min_priority,
pin, pin_size, &buffer, &buffer_size, usrptr, true);
if (!r) {
- r = LUKS2_keyslot_open_by_token(cd, hdr, token, segment, CRYPT_SLOT_PRIORITY_IGNORE,
- buffer, buffer_size, vk);
+ r = LUKS2_keyslot_open_by_token(cd, hdr, keyslot, token, segment,
+ min_priority, buffer, buffer_size, vk);
LUKS2_token_buffer_free(cd, token, buffer, buffer_size);
}
}
@@ -745,7 +848,7 @@ int LUKS2_token_unlock_key(struct crypt_device *cd,
* success (>= 0) or any other negative errno short-circuits token activation loop
* immediately
*/
- r = token_open_any(cd, hdr, type, segment, pin, pin_size, usrptr, vk);
+ r = token_open_any(cd, hdr, type, keyslot, segment, pin, pin_size, usrptr, vk);
else
r = -EINVAL;
@@ -754,6 +857,7 @@ int LUKS2_token_unlock_key(struct crypt_device *cd,
int LUKS2_token_open_and_activate(struct crypt_device *cd,
struct luks2_hdr *hdr,
+ int keyslot,
int token,
const char *name,
const char *type,
@@ -763,15 +867,15 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd,
void *usrptr)
{
bool use_keyring;
- int keyslot, r, segment;
- struct volume_key *vk = NULL;
+ int r, segment;
+ struct volume_key *p_crypt, *p_opal, *crypt_key = NULL, *opal_key = NULL, *vk = NULL;
if (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY)
segment = CRYPT_ANY_SEGMENT;
else
segment = CRYPT_DEFAULT_SEGMENT;
- r = LUKS2_token_unlock_key(cd, hdr, token, type, pin, pin_size, segment, usrptr, &vk);
+ r = LUKS2_token_unlock_key(cd, hdr, keyslot, token, type, pin, pin_size, segment, usrptr, &vk);
if (r < 0)
return r;
@@ -779,23 +883,39 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd,
keyslot = r;
- if (!crypt_use_keyring_for_vk(cd))
+ if (LUKS2_segment_is_hw_opal(hdr, CRYPT_DEFAULT_SEGMENT)) {
+ r = LUKS2_split_crypt_and_opal_keys(cd, hdr, vk, &crypt_key, &opal_key);
+ if (r < 0) {
+ crypt_free_volume_key(vk);
+ return r;
+ }
+
+ p_crypt = crypt_key;
+ p_opal = opal_key ?: vk;
+ } else {
+ p_crypt = vk;
+ p_opal = NULL;
+ }
+
+ if (!crypt_use_keyring_for_vk(cd) || !p_crypt)
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)))
+ if (!(r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, p_crypt, keyslot)))
flags |= CRYPT_ACTIVATE_KEYRING_KEY;
}
if (r >= 0 && name)
- r = LUKS2_activate(cd, name, vk, flags);
+ r = LUKS2_activate(cd, name, p_crypt, p_opal, flags);
if (r < 0)
- crypt_drop_keyring_key(cd, vk);
+ crypt_drop_keyring_key(cd, p_crypt);
crypt_free_volume_key(vk);
+ crypt_free_volume_key(crypt_key);
+ crypt_free_volume_key(opal_key);
return r < 0 ? r : keyslot;
}
@@ -995,8 +1115,9 @@ int LUKS2_token_unlock_passphrase(struct crypt_device *cd,
if (token >= 0 && token < LUKS2_TOKENS_MAX) {
if ((jobj_token = LUKS2_get_token_jobj(hdr, token)))
- r = token_open(cd, hdr, token, jobj_token, type, CRYPT_ANY_SEGMENT, CRYPT_SLOT_PRIORITY_IGNORE,
- pin, pin_size, &buffer, &buffer_size, usrptr, false);
+ r = token_open(cd, hdr, CRYPT_ANY_SLOT, token, jobj_token, type,
+ CRYPT_ANY_SEGMENT, CRYPT_SLOT_PRIORITY_IGNORE, pin, pin_size,
+ &buffer, &buffer_size, usrptr, false);
} else if (token == CRYPT_ANY_TOKEN) {
json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens);
@@ -1005,7 +1126,7 @@ int LUKS2_token_unlock_passphrase(struct crypt_device *cd,
json_object_object_foreach(jobj_tokens, slot, val) {
token = atoi(slot);
- r = token_open(cd, hdr, token, val, type, CRYPT_ANY_SEGMENT, CRYPT_SLOT_PRIORITY_IGNORE,
+ r = token_open(cd, hdr, CRYPT_ANY_SLOT, token, val, type, CRYPT_ANY_SEGMENT, CRYPT_SLOT_PRIORITY_IGNORE,
pin, pin_size, &buffer, &buffer_size, usrptr, false);
/*
diff --git a/lib/luks2/luks2_token_keyring.c b/lib/luks2/luks2_token_keyring.c
index ad18798..1d141b9 100644
--- a/lib/luks2/luks2_token_keyring.c
+++ b/lib/luks2/luks2_token_keyring.c
@@ -1,8 +1,8 @@
/*
* LUKS - Linux Unified Key Setup v2, kernel keyring token
*
- * Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2016-2023 Ondrej Kozina
+ * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2016-2024 Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -40,14 +40,11 @@ int keyring_open(struct crypt_device *cd,
json_object_object_get_ex(jobj_token, "key_description", &jobj_key);
- r = keyring_get_passphrase(json_object_get_string(jobj_key), buffer, buffer_len);
- if (r == -ENOTSUP) {
- log_dbg(cd, "Kernel keyring features disabled.");
+ r = crypt_keyring_get_user_key(cd, json_object_get_string(jobj_key), buffer, buffer_len);
+ if (r == -ENOTSUP)
return -ENOENT;
- } else if (r < 0) {
- log_dbg(cd, "keyring_get_passphrase failed (error %d)", r);
+ else if (r < 0)
return -EPERM;
- }
return 0;
}
diff --git a/lib/meson.build b/lib/meson.build
new file mode 100644
index 0000000..9f503b6
--- /dev/null
+++ b/lib/meson.build
@@ -0,0 +1,116 @@
+subdir('crypto_backend')
+lib_build_dir = meson.current_build_dir()
+
+libutils_io = static_library('utils_io',
+ files(
+ 'utils_io.c',
+ ))
+
+libcryptsetup_sym_path = join_paths(meson.current_source_dir(), 'libcryptsetup.sym')
+
+libcryptsetup_deps = [
+ uuid,
+ devmapper,
+ libargon2_external,
+ jsonc,
+ blkid,
+ dl,
+]
+
+libcryptsetup_sources = files(
+ 'bitlk/bitlk.c',
+ 'fvault2/fvault2.c',
+ 'integrity/integrity.c',
+ 'loopaes/loopaes.c',
+ 'luks1/af.c',
+ 'luks1/keyencryption.c',
+ 'luks1/keymanage.c',
+ 'luks2/hw_opal/hw_opal.c',
+ 'luks2/luks2_digest.c',
+ 'luks2/luks2_digest_pbkdf2.c',
+ 'luks2/luks2_disk_metadata.c',
+ 'luks2/luks2_json_format.c',
+ 'luks2/luks2_json_metadata.c',
+ 'luks2/luks2_keyslot.c',
+ 'luks2/luks2_keyslot_luks2.c',
+ 'luks2/luks2_keyslot_reenc.c',
+ 'luks2/luks2_luks1_convert.c',
+ 'luks2/luks2_reencrypt.c',
+ 'luks2/luks2_reencrypt_digest.c',
+ 'luks2/luks2_segment.c',
+ 'luks2/luks2_token.c',
+ 'luks2/luks2_token_keyring.c',
+ 'tcrypt/tcrypt.c',
+ 'verity/rs_decode_char.c',
+ 'verity/rs_encode_char.c',
+ 'verity/verity.c',
+ 'verity/verity_fec.c',
+ 'verity/verity_hash.c',
+ 'crypt_plain.c',
+ 'keyslot_context.c',
+ 'libdevmapper.c',
+ 'random.c',
+ 'setup.c',
+ 'utils.c',
+ 'utils_benchmark.c',
+ 'utils_blkid.c',
+ 'utils_crypt.c',
+ 'utils_device.c',
+ 'utils_device_locking.c',
+ 'utils_devpath.c',
+ 'utils_keyring.c',
+ 'utils_loop.c',
+ 'utils_pbkdf.c',
+ 'utils_safe_memory.c',
+ 'utils_storage_wrappers.c',
+ 'utils_wipe.c',
+ 'volumekey.c',
+)
+
+if enable_static
+ libcryptsetup = static_library('cryptsetup',
+ libcryptsetup_sources,
+ dependencies: libcryptsetup_deps,
+ link_with: [
+ libcrypto_backend,
+ libutils_io,
+ ],
+ install: true)
+else
+ libcryptsetup = library('cryptsetup',
+ libcryptsetup_sources,
+ dependencies: libcryptsetup_deps,
+ version: libcryptsetup_version,
+ link_args: [
+ '-Wl,--version-script=' +
+ libcryptsetup_sym_path,
+ ],
+ link_with: [
+ libcrypto_backend,
+ libutils_io,
+ ],
+ install: true)
+endif
+
+lib_tools_files = files(
+ 'utils_blkid.c',
+ 'utils_crypt.c',
+ 'utils_io.c',
+ 'utils_loop.c',
+)
+lib_utils_crypt_files = files(
+ 'utils_crypt.c',
+)
+lib_ssh_token_files = files(
+ 'utils_io.c',
+ 'utils_loop.c',
+)
+
+install_headers(
+ 'libcryptsetup.h',
+)
+pkgconfig.generate(
+ libcryptsetup,
+ name: 'libcryptsetup',
+ version: PACKAGE_VERSION,
+ description: 'cryptsetup library')
diff --git a/lib/random.c b/lib/random.c
index 0dfcff9..c86492d 100644
--- a/lib/random.c
+++ b/lib/random.c
@@ -1,7 +1,7 @@
/*
* cryptsetup kernel RNG access functions
*
- * Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/setup.c b/lib/setup.c
index 1c9d47d..ff84292 100644
--- a/lib/setup.c
+++ b/lib/setup.c
@@ -3,8 +3,8 @@
*
* Copyright (C) 2004 Jana Saout <jana@saout.de>
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -31,6 +31,7 @@
#include "libcryptsetup.h"
#include "luks1/luks.h"
#include "luks2/luks2.h"
+#include "luks2/luks2_internal.h"
#include "loopaes/loopaes.h"
#include "verity/verity.h"
#include "tcrypt/tcrypt.h"
@@ -40,6 +41,7 @@
#include "utils_device_locking.h"
#include "internal.h"
#include "keyslot_context.h"
+#include "luks2/hw_opal/hw_opal.h"
#define CRYPT_CD_UNRESTRICTED (1 << 0)
#define CRYPT_CD_QUIET (1 << 1)
@@ -58,6 +60,12 @@ struct crypt_device {
/* global context scope settings */
unsigned key_in_keyring:1;
+ bool link_vk_to_keyring;
+ int32_t keyring_to_link_vk;
+ const char *user_key_name1;
+ const char *user_key_name2;
+ key_type_t keyring_key_type;
+
uint64_t data_offset;
uint64_t metadata_size; /* Used in LUKS2 format */
uint64_t keyslots_size; /* Used in LUKS2 format */
@@ -122,8 +130,10 @@ struct crypt_device {
/* buffers, must refresh from kernel on every query */
char cipher_spec[MAX_CIPHER_LEN*2+1];
char cipher[MAX_CIPHER_LEN];
+ char integrity_spec[MAX_INTEGRITY_LEN];
const char *cipher_mode;
unsigned int key_size;
+ uint32_t sector_size;
} none;
} u;
@@ -221,6 +231,45 @@ struct device *crypt_data_device(struct crypt_device *cd)
return cd->device;
}
+uint64_t crypt_get_metadata_size_bytes(struct crypt_device *cd)
+{
+ assert(cd);
+ return cd->metadata_size;
+}
+
+uint64_t crypt_get_keyslots_size_bytes(struct crypt_device *cd)
+{
+ assert(cd);
+ return cd->keyslots_size;
+}
+
+uint64_t crypt_get_data_offset_sectors(struct crypt_device *cd)
+{
+ assert(cd);
+ return cd->data_offset;
+}
+
+int crypt_opal_supported(struct crypt_device *cd, struct device *opal_device)
+{
+ int r;
+
+ assert(cd);
+ assert(opal_device);
+
+ r = opal_supported(cd, opal_device);
+ if (r <= 0) {
+ if (r == -ENOTSUP)
+ log_err(cd, _("OPAL support is disabled in libcryptsetup."));
+ else
+ log_err(cd, _("Device %s or kernel does not support OPAL encryption."),
+ device_path(opal_device));
+ r = -EINVAL;
+ } else
+ r = 0;
+
+ return r;
+}
+
int init_crypto(struct crypt_device *ctx)
{
struct utsname uts;
@@ -237,8 +286,9 @@ int init_crypto(struct crypt_device *ctx)
log_err(ctx, _("Cannot initialize crypto backend."));
if (!r && !_crypto_logged) {
- log_dbg(ctx, "Crypto backend (%s) initialized in cryptsetup library version %s.",
- crypt_backend_version(), PACKAGE_VERSION);
+ log_dbg(ctx, "Crypto backend (%s%s) initialized in cryptsetup library version %s.",
+ crypt_backend_version(), crypt_argon2_version(), PACKAGE_VERSION);
+
if (!uname(&uts))
log_dbg(ctx, "Detected kernel %s %s %s.",
uts.sysname, uts.release, uts.machine);
@@ -333,7 +383,7 @@ static int isFVAULT2(const char *type)
return (type && !strcmp(CRYPT_FVAULT2, type));
}
-static int _onlyLUKS(struct crypt_device *cd, uint32_t cdflags)
+static int _onlyLUKS(struct crypt_device *cd, uint32_t cdflags, uint32_t mask)
{
int r = 0;
@@ -352,12 +402,22 @@ static int _onlyLUKS(struct crypt_device *cd, uint32_t cdflags)
if (r || (cdflags & CRYPT_CD_UNRESTRICTED) || isLUKS1(cd->type))
return r;
- return LUKS2_unmet_requirements(cd, &cd->u.luks2.hdr, 0, cdflags & CRYPT_CD_QUIET);
+ return LUKS2_unmet_requirements(cd, &cd->u.luks2.hdr, mask, cdflags & CRYPT_CD_QUIET);
+}
+
+static int onlyLUKSunrestricted(struct crypt_device *cd)
+{
+ return _onlyLUKS(cd, CRYPT_CD_UNRESTRICTED, 0);
+}
+
+static int onlyLUKSnoRequirements(struct crypt_device *cd)
+{
+ return _onlyLUKS(cd, 0, 0);
}
static int onlyLUKS(struct crypt_device *cd)
{
- return _onlyLUKS(cd, 0);
+ return _onlyLUKS(cd, 0, CRYPT_REQUIREMENT_OPAL);
}
static int _onlyLUKS2(struct crypt_device *cd, uint32_t cdflags, uint32_t mask)
@@ -382,16 +442,21 @@ static int _onlyLUKS2(struct crypt_device *cd, uint32_t cdflags, uint32_t mask)
return LUKS2_unmet_requirements(cd, &cd->u.luks2.hdr, mask, cdflags & CRYPT_CD_QUIET);
}
+static int onlyLUKS2unrestricted(struct crypt_device *cd)
+{
+ return _onlyLUKS2(cd, CRYPT_CD_UNRESTRICTED, 0);
+}
+
/* Internal only */
int onlyLUKS2(struct crypt_device *cd)
{
- return _onlyLUKS2(cd, 0, 0);
+ return _onlyLUKS2(cd, 0, CRYPT_REQUIREMENT_OPAL);
}
/* Internal only */
-int onlyLUKS2mask(struct crypt_device *cd, uint32_t mask)
+int onlyLUKS2reencrypt(struct crypt_device *cd)
{
- return _onlyLUKS2(cd, 0, mask);
+ return _onlyLUKS2(cd, 0, CRYPT_REQUIREMENT_ONLINE_REENCRYPT);
}
static void crypt_set_null_type(struct crypt_device *cd)
@@ -461,6 +526,10 @@ int crypt_uuid_cmp(const char *dm_uuid, const char *hdr_uuid)
if (!dm_uuid || !hdr_uuid)
return -EINVAL;
+ /* skip beyond LUKS2_HW_OPAL prefix */
+ if (!strncmp(dm_uuid, CRYPT_LUKS2_HW_OPAL, strlen(CRYPT_LUKS2_HW_OPAL)))
+ dm_uuid = dm_uuid + strlen(CRYPT_LUKS2_HW_OPAL);
+
str = strchr(dm_uuid, '-');
if (!str)
return -EINVAL;
@@ -481,33 +550,55 @@ int crypt_uuid_cmp(const char *dm_uuid, const char *hdr_uuid)
}
/*
- * compares type of active device to provided string (only if there is no explicit type)
+ * compares two UUIDs returned by device-mapper (striped by cryptsetup)
+ * used for stacked LUKS2 & INTEGRITY devices
*/
-static int crypt_uuid_type_cmp(struct crypt_device *cd, const char *type)
+static int crypt_uuid_integrity_cmp(const char *dm_uuid, const char *dmi_uuid)
{
- struct crypt_dm_active_device dmd;
- size_t len;
- int r;
+ int i;
+ char *str, *stri;
- /* Must use header-on-disk if we know the type here */
- if (cd->type || !cd->u.none.active_name)
+ if (!dm_uuid || !dmi_uuid)
return -EINVAL;
- log_dbg(cd, "Checking if active device %s without header has UUID type %s.",
- cd->u.none.active_name, type);
+ /* skip beyond LUKS2_HW_OPAL prefix */
+ if (!strncmp(dm_uuid, CRYPT_LUKS2_HW_OPAL, strlen(CRYPT_LUKS2_HW_OPAL)))
+ dm_uuid = dm_uuid + strlen(CRYPT_LUKS2_HW_OPAL);
- r = dm_query_device(cd, cd->u.none.active_name, DM_ACTIVE_UUID, &dmd);
- if (r < 0)
- return r;
+ str = strchr(dm_uuid, '-');
+ if (!str)
+ return -EINVAL;
+
+ stri = strchr(dmi_uuid, '-');
+ if (!stri)
+ return -EINVAL;
+
+ for (i = 1; str[i] && str[i] != '-'; i++) {
+ if (!stri[i])
+ return -EINVAL;
+
+ if (str[i] != stri[i])
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * compares type of active device to provided string
+ */
+int crypt_uuid_type_cmp(const char *dm_uuid, const char *type)
+{
+ size_t len;
+
+ assert(type);
- r = -ENODEV;
len = strlen(type);
- if (dmd.uuid && strlen(dmd.uuid) > len &&
- !strncmp(dmd.uuid, type, len) && dmd.uuid[len] == '-')
- r = 0;
+ if (dm_uuid && strlen(dm_uuid) > len &&
+ !strncmp(dm_uuid, type, len) && dm_uuid[len] == '-')
+ return 0;
- free(CONST_CAST(void*)dmd.uuid);
- return r;
+ return -ENODEV;
}
int PLAIN_activate(struct crypt_device *cd,
@@ -763,9 +854,12 @@ static int _crypt_load_luks2(struct crypt_device *cd, int reload, int repair)
if (r)
return r;
- if (!reload && !(type = strdup(CRYPT_LUKS2))) {
- r = -ENOMEM;
- goto out;
+ if (!reload) {
+ type = strdup(CRYPT_LUKS2);
+ if (!type) {
+ r = -ENOMEM;
+ goto out;
+ }
}
if (verify_pbkdf_params(cd, &cd->pbkdf)) {
@@ -1188,6 +1282,17 @@ static int _init_by_name_crypt_none(struct crypt_device *cd)
}
}
+ if (!r && tgt->u.crypt.integrity) {
+ r = snprintf(cd->u.none.integrity_spec, sizeof(cd->u.none.integrity_spec),
+ "%s", tgt->u.crypt.integrity);
+ if (r < 0 || (size_t)r >= sizeof(cd->u.none.integrity_spec))
+ r = -EINVAL;
+ else
+ r = 0;
+ }
+
+ cd->u.none.sector_size = tgt->u.crypt.sector_size;
+
dm_targets_free(cd, &dmd);
return r;
}
@@ -1245,7 +1350,13 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name)
r = crypt_parse_name_and_mode(tgt->type == DM_LINEAR ? "null" : tgt->u.crypt.cipher, cipher,
&key_nums, cipher_mode);
if (r < 0) {
- log_dbg(cd, "Cannot parse cipher and mode from active device.");
+ /* Allow crypt null context with unknown cipher string */
+ if (tgt->type == DM_CRYPT && !tgt->u.crypt.integrity) {
+ crypt_set_null_type(cd);
+ r = 0;
+ goto out;
+ }
+ log_err(cd, _("No known cipher specification pattern detected for active device %s."), name);
goto out;
}
@@ -1260,10 +1371,13 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name)
r = -EINVAL;
goto out;
}
- if (!cd->metadata_device) {
- device_free(cd, cd->device);
- MOVE_REF(cd->device, tgti->data_device);
- }
+
+ /*
+ * Data device for crypt with integrity is not dm-integrity device,
+ * but always the device underlying dm-integrity.
+ */
+ device_free(cd, cd->device);
+ MOVE_REF(cd->device, tgti->data_device);
}
/* do not try to lookup LUKS2 header in detached header mode */
@@ -1717,6 +1831,9 @@ static int _crypt_format_luks1(struct crypt_device *cd,
return -ENOMEM;
}
+ if (device_is_dax(crypt_data_device(cd)) > 0)
+ log_std(cd, _("WARNING: DAX device can corrupt data as it does not guarantee atomic sector updates.\n"));
+
if (params && cd->metadata_device) {
/* For detached header the alignment is used directly as data offset */
if (!cd->data_offset)
@@ -1772,6 +1889,116 @@ static int _crypt_format_luks1(struct crypt_device *cd,
return 0;
}
+static int LUKS2_check_encryption_params(struct crypt_device *cd,
+ const char *cipher,
+ const char *cipher_mode,
+ const char *integrity,
+ size_t volume_key_size,
+ const struct crypt_params_luks2 *params,
+ const char **ret_integrity)
+{
+ int r, integrity_key_size = 0;
+
+ assert(cipher);
+ assert(cipher_mode);
+ assert(ret_integrity);
+
+ if (integrity) {
+ if (params->integrity_params) {
+ /* Standalone dm-integrity must not be used */
+ if (params->integrity_params->integrity ||
+ params->integrity_params->integrity_key_size)
+ return -EINVAL;
+ /* FIXME: journal encryption and MAC is here not yet supported */
+ if (params->integrity_params->journal_crypt ||
+ params->integrity_params->journal_integrity)
+ return -ENOTSUP;
+ }
+ if (!INTEGRITY_tag_size(integrity, cipher, cipher_mode)) {
+ /* merge "none" string into NULL to make branching logic is easier */
+ if (!strcmp(integrity, "none"))
+ integrity = NULL;
+ else
+ return -EINVAL;
+ }
+ integrity_key_size = INTEGRITY_key_size(integrity);
+ if ((integrity_key_size < 0) || (integrity_key_size >= (int)volume_key_size)) {
+ log_err(cd, _("Volume key is too small for encryption with integrity extensions."));
+ return -EINVAL;
+ }
+ }
+
+ /* FIXME: allow this later also for normal ciphers (check AF_ALG availability. */
+ if (integrity && !integrity_key_size) {
+ r = crypt_cipher_check_kernel(cipher, cipher_mode, integrity, volume_key_size);
+ if (r < 0) {
+ log_err(cd, _("Cipher %s-%s (key size %zd bits) is not available."),
+ cipher, cipher_mode, volume_key_size * 8);
+ return r;
+ }
+ }
+
+ if ((!integrity || integrity_key_size) && !crypt_cipher_wrapped_key(cipher, cipher_mode) &&
+ !INTEGRITY_tag_size(NULL, cipher, cipher_mode)) {
+ r = LUKS_check_cipher(cd, volume_key_size - integrity_key_size,
+ cipher, cipher_mode);
+ if (r < 0)
+ return r;
+ }
+
+ *ret_integrity = integrity;
+
+ return 0;
+}
+
+static int LUKS2_check_encryption_sector(struct crypt_device *cd, uint64_t device_size_bytes,
+ uint64_t data_offset_bytes, uint32_t sector_size, bool modify_sector_size,
+ bool verify_data_area_alignment, uint32_t *ret_sector_size)
+{
+ uint32_t dmc_flags;
+
+ assert(ret_sector_size);
+
+ if (sector_size < SECTOR_SIZE || sector_size > MAX_SECTOR_SIZE ||
+ NOTPOW2(sector_size)) {
+ log_err(cd, _("Unsupported encryption sector size."));
+ return -EINVAL;
+ }
+
+ if (sector_size != SECTOR_SIZE && !dm_flags(cd, DM_CRYPT, &dmc_flags) &&
+ !(dmc_flags & DM_SECTOR_SIZE_SUPPORTED)) {
+ if (modify_sector_size) {
+ log_dbg(cd, "dm-crypt does not support encryption sector size option. Reverting to 512 bytes.");
+ sector_size = SECTOR_SIZE;
+ } else
+ log_std(cd, _("WARNING: The device activation will fail, dm-crypt is missing "
+ "support for requested encryption sector size.\n"));
+ }
+
+ if (modify_sector_size) {
+ if (data_offset_bytes && MISALIGNED(data_offset_bytes, sector_size)) {
+ log_dbg(cd, "Data offset not aligned to sector size. Reverting to 512 bytes.");
+ sector_size = SECTOR_SIZE;
+ } else if (MISALIGNED(device_size_bytes - data_offset_bytes, sector_size)) {
+ /* underflow does not affect misalignment checks */
+ log_dbg(cd, "Device size is not aligned to sector size. Reverting to 512 bytes.");
+ sector_size = SECTOR_SIZE;
+ }
+ }
+
+ /* underflow does not affect misalignment checks */
+ if (verify_data_area_alignment &&
+ sector_size > SECTOR_SIZE &&
+ MISALIGNED(device_size_bytes - data_offset_bytes, sector_size)) {
+ log_err(cd, _("Device size is not aligned to requested sector size."));
+ return -EINVAL;
+ }
+
+ *ret_sector_size = sector_size;
+
+ return 0;
+}
+
static int _crypt_format_luks2(struct crypt_device *cd,
const char *cipher,
const char *cipher_mode,
@@ -1781,13 +2008,13 @@ static int _crypt_format_luks2(struct crypt_device *cd,
struct crypt_params_luks2 *params,
bool sector_size_autodetect)
{
- int r, integrity_key_size = 0;
+ int r;
unsigned long required_alignment = DEFAULT_DISK_ALIGNMENT;
unsigned long alignment_offset = 0;
unsigned int sector_size;
+ char cipher_spec[2*MAX_CAPI_ONE_LEN];
const char *integrity = params ? params->integrity : NULL;
- uint64_t dev_size;
- uint32_t dmc_flags;
+ uint64_t data_offset_bytes, dev_size, metadata_size_bytes, keyslots_size_bytes;
cd->u.luks2.hdr.jobj = NULL;
cd->u.luks2.keyslot_cipher = NULL;
@@ -1819,6 +2046,9 @@ static int _crypt_format_luks2(struct crypt_device *cd,
return -ENOMEM;
}
+ if (device_is_dax(crypt_data_device(cd)) > 0)
+ log_std(cd, _("WARNING: DAX device can corrupt data as it does not guarantee atomic sector updates.\n"));
+
if (sector_size_autodetect) {
sector_size = device_optimal_encryption_sector_size(cd, crypt_data_device(cd));
log_dbg(cd, "Auto-detected optimal encryption sector size for device %s is %d bytes.",
@@ -1826,45 +2056,6 @@ static int _crypt_format_luks2(struct crypt_device *cd,
} else
sector_size = params ? params->sector_size : SECTOR_SIZE;
- if (sector_size < SECTOR_SIZE || sector_size > MAX_SECTOR_SIZE ||
- NOTPOW2(sector_size)) {
- log_err(cd, _("Unsupported encryption sector size."));
- return -EINVAL;
- }
- if (sector_size != SECTOR_SIZE && !dm_flags(cd, DM_CRYPT, &dmc_flags) &&
- !(dmc_flags & DM_SECTOR_SIZE_SUPPORTED)) {
- if (sector_size_autodetect) {
- log_dbg(cd, "dm-crypt does not support encryption sector size option. Reverting to 512 bytes.");
- sector_size = SECTOR_SIZE;
- } else
- log_std(cd, _("WARNING: The device activation will fail, dm-crypt is missing "
- "support for requested encryption sector size.\n"));
- }
-
- if (integrity) {
- if (params->integrity_params) {
- /* Standalone dm-integrity must not be used */
- if (params->integrity_params->integrity ||
- params->integrity_params->integrity_key_size)
- return -EINVAL;
- /* FIXME: journal encryption and MAC is here not yet supported */
- if (params->integrity_params->journal_crypt ||
- params->integrity_params->journal_integrity)
- return -ENOTSUP;
- }
- if (!INTEGRITY_tag_size(integrity, cipher, cipher_mode)) {
- if (!strcmp(integrity, "none"))
- integrity = NULL;
- else
- return -EINVAL;
- }
- integrity_key_size = INTEGRITY_key_size(integrity);
- if ((integrity_key_size < 0) || (integrity_key_size >= (int)volume_key_size)) {
- log_err(cd, _("Volume key is too small for encryption with integrity extensions."));
- return -EINVAL;
- }
- }
-
r = device_check_access(cd, crypt_metadata_device(cd), DEV_EXCL);
if (r < 0)
return r;
@@ -1901,67 +2092,45 @@ static int _crypt_format_luks2(struct crypt_device *cd,
&required_alignment,
&alignment_offset, DEFAULT_DISK_ALIGNMENT);
+ r = LUKS2_check_encryption_params(cd, cipher, cipher_mode, integrity,
+ volume_key_size, params, &integrity);
+ if (r < 0)
+ goto out;
+
r = device_size(crypt_data_device(cd), &dev_size);
if (r < 0)
goto out;
- if (sector_size_autodetect) {
- if (cd->data_offset && MISALIGNED(cd->data_offset, sector_size)) {
- log_dbg(cd, "Data offset not aligned to sector size. Reverting to 512 bytes.");
- sector_size = SECTOR_SIZE;
- } else if (MISALIGNED(dev_size - (uint64_t)required_alignment - (uint64_t)alignment_offset, sector_size)) {
- /* underflow does not affect misalignment checks */
- log_dbg(cd, "Device size is not aligned to sector size. Reverting to 512 bytes.");
- sector_size = SECTOR_SIZE;
- }
- }
+ r = LUKS2_hdr_get_storage_params(cd, alignment_offset, required_alignment,
+ &metadata_size_bytes, &keyslots_size_bytes, &data_offset_bytes);
+ if (r < 0)
+ goto out;
- /* FIXME: allow this later also for normal ciphers (check AF_ALG availability. */
- if (integrity && !integrity_key_size) {
- r = crypt_cipher_check_kernel(cipher, cipher_mode, integrity, volume_key_size);
- if (r < 0) {
- log_err(cd, _("Cipher %s-%s (key size %zd bits) is not available."),
- cipher, cipher_mode, volume_key_size * 8);
- goto out;
- }
- }
+ r = LUKS2_check_encryption_sector(cd, dev_size, data_offset_bytes, sector_size,
+ sector_size_autodetect, integrity == NULL,
+ &sector_size);
+ if (r < 0)
+ goto out;
- if ((!integrity || integrity_key_size) && !crypt_cipher_wrapped_key(cipher, cipher_mode) &&
- !INTEGRITY_tag_size(NULL, cipher, cipher_mode)) {
- r = LUKS_check_cipher(cd, volume_key_size - integrity_key_size,
- cipher, cipher_mode);
- if (r < 0)
- goto out;
+ if (*cipher_mode != '\0')
+ r = snprintf(cipher_spec, sizeof(cipher_spec), "%s-%s", cipher, cipher_mode);
+ else
+ r = snprintf(cipher_spec, sizeof(cipher_spec), "%s", cipher);
+ if (r < 0 || (size_t)r >= sizeof(cipher_spec)) {
+ r = -EINVAL;
+ goto out;
}
r = LUKS2_generate_hdr(cd, &cd->u.luks2.hdr, cd->volume_key,
- cipher, cipher_mode,
+ cipher_spec,
integrity, uuid,
sector_size,
- cd->data_offset * SECTOR_SIZE,
- alignment_offset,
- required_alignment,
- cd->metadata_size, cd->keyslots_size);
+ data_offset_bytes,
+ metadata_size_bytes, keyslots_size_bytes,
+ 0, 0, 0);
if (r < 0)
goto out;
- if (cd->metadata_size && (cd->metadata_size != LUKS2_metadata_size(&cd->u.luks2.hdr)))
- log_std(cd, _("WARNING: LUKS2 metadata size changed to %" PRIu64 " bytes.\n"),
- LUKS2_metadata_size(&cd->u.luks2.hdr));
-
- if (cd->keyslots_size && (cd->keyslots_size != LUKS2_keyslots_size(&cd->u.luks2.hdr)))
- log_std(cd, _("WARNING: LUKS2 keyslots area size changed to %" PRIu64 " bytes.\n"),
- LUKS2_keyslots_size(&cd->u.luks2.hdr));
-
- if (!integrity && sector_size > SECTOR_SIZE) {
- dev_size -= (crypt_get_data_offset(cd) * SECTOR_SIZE);
- if (dev_size % sector_size) {
- log_err(cd, _("Device size is not aligned to requested sector size."));
- r = -EINVAL;
- goto out;
- }
- }
-
if (params && (params->label || params->subsystem)) {
r = LUKS2_hdr_labels(cd, &cd->u.luks2.hdr,
params->label, params->subsystem, 0);
@@ -2000,7 +2169,7 @@ static int _crypt_format_luks2(struct crypt_device *cd,
goto out;
}
- r = INTEGRITY_format(cd, params ? params->integrity_params : NULL, NULL, NULL);
+ r = INTEGRITY_format(cd, params ? params->integrity_params : NULL, NULL, NULL, 0);
if (r)
log_err(cd, _("Cannot format integrity for device %s."),
data_device_path(cd));
@@ -2039,6 +2208,464 @@ out:
return 0;
}
+static int opal_topology_alignment(struct crypt_device *cd,
+ uint64_t partition_offset_sectors,
+ uint64_t data_offset_sectors,
+ uint64_t required_alignment_sectors,
+ uint64_t default_alignment_bytes,
+ uint64_t *ret_alignment_offset_bytes,
+ uint64_t *ret_alignment_bytes,
+ uint32_t *ret_opal_block_bytes,
+ uint64_t *ret_opal_alignment_granularity_blocks)
+{
+ bool opal_align;
+ int r;
+ uint32_t opal_block_bytes;
+ uint64_t opal_alignment_granularity_blocks, opal_lowest_lba_blocks;
+
+ assert(cd);
+ assert(ret_alignment_offset_bytes);
+ assert(ret_alignment_bytes);
+ assert(ret_opal_block_bytes);
+ assert(ret_opal_alignment_granularity_blocks);
+
+ r = opal_geometry(cd, crypt_data_device(cd), &opal_align, &opal_block_bytes,
+ &opal_alignment_granularity_blocks, &opal_lowest_lba_blocks);
+ if (r) {
+ log_err(cd, _("Cannot get OPAL alignment parameters."));
+ return -EINVAL;
+ }
+
+ log_dbg(cd, "OPAL geometry: alignment: '%c', logical block size: %" PRIu32
+ ", alignment granularity: %" PRIu64 ", lowest aligned LBA: %" PRIu64,
+ opal_align ? 'y' : 'n', opal_block_bytes, opal_alignment_granularity_blocks, opal_lowest_lba_blocks);
+
+ if (opal_block_bytes < SECTOR_SIZE || NOTPOW2(opal_block_bytes)) {
+ log_err(cd, _("Bogus OPAL logical block size."));
+ return -EINVAL;
+ }
+
+ if (data_offset_sectors &&
+ MISALIGNED(data_offset_sectors + partition_offset_sectors, opal_block_bytes / SECTOR_SIZE)) {
+ log_err(cd, _("Requested data offset is not compatible with OPAL block size."));
+ return -EINVAL;
+ }
+
+ /* Data offset has priority over data alignment parameter */
+ if (!data_offset_sectors &&
+ MISALIGNED(required_alignment_sectors, opal_block_bytes / SECTOR_SIZE)) {
+ log_err(cd, _("Requested data alignment is not compatible with OPAL alignment."));
+ return -EINVAL;
+ }
+
+ if (!opal_align) {
+ /* For detached header the alignment is used directly as data offset */
+ if (required_alignment_sectors || cd->metadata_device)
+ *ret_alignment_bytes = required_alignment_sectors * SECTOR_SIZE;
+ else
+ *ret_alignment_bytes = default_alignment_bytes;
+ *ret_alignment_offset_bytes = 0;
+ *ret_opal_block_bytes = opal_block_bytes;
+ *ret_opal_alignment_granularity_blocks = 1;
+ return 0;
+ }
+
+ if (data_offset_sectors) {
+ if (MISALIGNED((((data_offset_sectors + partition_offset_sectors) * SECTOR_SIZE) / opal_block_bytes) - opal_lowest_lba_blocks,
+ opal_alignment_granularity_blocks)) {
+ // FIXME: Add hint to user on how to fix it
+ log_err(cd, _("Data offset does not satisfy OPAL alignment requirements."));
+ return -EINVAL;
+ }
+
+ *ret_alignment_offset_bytes = 0;
+ *ret_alignment_bytes = 0;
+ *ret_opal_block_bytes = opal_block_bytes;
+ *ret_opal_alignment_granularity_blocks = opal_alignment_granularity_blocks;
+
+ return 0;
+ }
+
+ if (MISALIGNED(required_alignment_sectors * SECTOR_SIZE, opal_block_bytes * opal_alignment_granularity_blocks)) {
+ log_err(cd, _("Requested data alignment does not satisfy locking range alignment requirements."));
+ return -EINVAL;
+ }
+
+ /* For detached header the alignment is used directly as data offset */
+ if (required_alignment_sectors || cd->metadata_device)
+ *ret_alignment_bytes = required_alignment_sectors * SECTOR_SIZE;
+ else
+ *ret_alignment_bytes = size_round_up(default_alignment_bytes, opal_block_bytes * opal_alignment_granularity_blocks);
+
+ /* data offset is not set, calculate proper alignment */
+ *ret_alignment_offset_bytes = (partition_offset_sectors * SECTOR_SIZE) % (opal_block_bytes * opal_alignment_granularity_blocks);
+ if (*ret_alignment_offset_bytes)
+ *ret_alignment_offset_bytes = opal_block_bytes * opal_alignment_granularity_blocks - *ret_alignment_offset_bytes;
+
+ if (*ret_alignment_offset_bytes)
+ log_dbg(cd, "Compensating misaligned partition offset by %" PRIu64 "bytes.",
+ *ret_alignment_offset_bytes);
+
+ *ret_alignment_offset_bytes += (opal_lowest_lba_blocks * opal_block_bytes);
+ *ret_opal_block_bytes = opal_block_bytes;
+ *ret_opal_alignment_granularity_blocks = opal_alignment_granularity_blocks;
+
+ log_dbg(cd, "OPAL alignment (%" PRIu32 "/%" PRIu64 "), offset = %" PRIu64 ". Required alignment is %" PRIu64 ".",
+ opal_block_bytes, opal_alignment_granularity_blocks, *ret_alignment_offset_bytes, *ret_alignment_bytes);
+
+ return 0;
+}
+
+int crypt_format_luks2_opal(struct crypt_device *cd,
+ const char *cipher,
+ const char *cipher_mode,
+ const char *uuid,
+ const char *volume_keys,
+ size_t volume_keys_size,
+ struct crypt_params_luks2 *params,
+ struct crypt_params_hw_opal *opal_params)
+{
+ bool opal_range_reset = false, subsystem_overridden = false, sector_size_autodetect = cipher != NULL;
+ int r;
+ char cipher_spec[128];
+ const char *integrity = params ? params->integrity : NULL;
+ uint32_t sector_size, opal_block_bytes, opal_segment_number = 1; /* We'll use the partition number if available later */
+ uint64_t alignment_offset_bytes, data_offset_bytes, device_size_bytes, opal_alignment_granularity_blocks,
+ partition_offset_sectors, range_offset_blocks, range_size_bytes,
+ required_alignment_bytes, metadata_size_bytes, keyslots_size_bytes,
+ provided_data_sectors;
+ struct volume_key *user_key = NULL;
+ struct crypt_lock_handle *opal_lh = NULL;
+
+ if (!cd || !params || !opal_params ||
+ !opal_params->admin_key || !opal_params->admin_key_size || !opal_params->user_key_size)
+ return -EINVAL;
+
+ if (cd->type) {
+ log_dbg(cd, "Context already formatted as %s.", cd->type);
+ return -EINVAL;
+ }
+
+ log_dbg(cd, "Formatting device %s as type LUKS2 with OPAL HW encryption.", mdata_device_path(cd) ?: "(none)");
+
+ if (volume_keys_size < opal_params->user_key_size)
+ return -EINVAL;
+
+ if (cipher && (volume_keys_size == opal_params->user_key_size))
+ return -EINVAL;
+
+ if (!crypt_metadata_device(cd)) {
+ log_err(cd, _("Can't format LUKS without device."));
+ return -EINVAL;
+ }
+
+ if (params->data_alignment &&
+ MISALIGNED(cd->data_offset, params->data_alignment)) {
+ log_err(cd, _("Requested data alignment is not compatible with data offset."));
+ return -EINVAL;
+ }
+
+ if (params->data_device) {
+ if (!cd->metadata_device)
+ cd->metadata_device = cd->device;
+ else
+ device_free(cd, cd->device);
+ cd->device = NULL;
+ if (device_alloc(cd, &cd->device, params->data_device) < 0)
+ return -ENOMEM;
+ }
+
+ r = crypt_opal_supported(cd, crypt_data_device(cd));
+ if (r < 0)
+ return r;
+
+ if (params->sector_size)
+ sector_size_autodetect = false;
+
+ partition_offset_sectors = crypt_dev_partition_offset(device_path(crypt_data_device(cd)));
+
+ r = device_check_access(cd, crypt_metadata_device(cd), DEV_EXCL);
+ if (r < 0)
+ return r;
+
+ /*
+ * Check both data and metadata devices for exclusive access since
+ * we don't want to setup locking range on already used partition.
+ */
+ if (crypt_metadata_device(cd) != crypt_data_device(cd)) {
+ r = device_check_access(cd, crypt_data_device(cd), DEV_EXCL);
+ if (r < 0)
+ return r;
+ }
+
+ if (!(cd->type = strdup(CRYPT_LUKS2)))
+ return -ENOMEM;
+
+ if (volume_keys)
+ cd->volume_key = crypt_alloc_volume_key(volume_keys_size, volume_keys);
+ else
+ cd->volume_key = crypt_generate_volume_key(cd, volume_keys_size);
+
+ if (!cd->volume_key) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ if (cipher) {
+ user_key = crypt_alloc_volume_key(opal_params->user_key_size, cd->volume_key->key);
+ if (!user_key) {
+ r = -ENOMEM;
+ goto out;
+ }
+ }
+
+ r = 0;
+ if (params->pbkdf)
+ r = crypt_set_pbkdf_type(cd, params->pbkdf);
+ else if (verify_pbkdf_params(cd, &cd->pbkdf))
+ r = init_pbkdf_type(cd, NULL, CRYPT_LUKS2);
+
+ if (r < 0)
+ goto out;
+
+ if (cd->metadata_device && !cd->data_offset)
+ /* For detached header the alignment is used directly as data offset */
+ cd->data_offset = params->data_alignment;
+
+ r = opal_topology_alignment(cd, partition_offset_sectors,
+ cd->data_offset, params->data_alignment,
+ DEFAULT_DISK_ALIGNMENT, &alignment_offset_bytes, &required_alignment_bytes,
+ &opal_block_bytes, &opal_alignment_granularity_blocks);
+ if (r < 0)
+ goto out;
+
+ if (sector_size_autodetect) {
+ sector_size = device_optimal_encryption_sector_size(cd, crypt_data_device(cd));
+ if ((opal_block_bytes * opal_alignment_granularity_blocks) > sector_size)
+ sector_size = opal_block_bytes * opal_alignment_granularity_blocks;
+ if (sector_size > MAX_SECTOR_SIZE)
+ sector_size = MAX_SECTOR_SIZE;
+ log_dbg(cd, "Auto-detected optimal encryption sector size for device %s is %d bytes.",
+ device_path(crypt_data_device(cd)), sector_size);
+ } else
+ sector_size = params->sector_size;
+
+ /* To ensure it is obvious and explicit that OPAL is being used, set the
+ * subsystem tag if the user hasn't passed one. */
+ if (!params->subsystem) {
+ params->subsystem = "HW-OPAL";
+ subsystem_overridden = true;
+ }
+
+ /* We need to give the drive a segment number - use the partition number if there is
+ * one, otherwise the first valid (1) number if it's a single-volume setup */
+ r = crypt_dev_get_partition_number(device_path(crypt_data_device(cd)));
+ if (r > 0)
+ opal_segment_number = r;
+
+ if (cipher) {
+ r = LUKS2_check_encryption_params(cd, cipher, cipher_mode, integrity,
+ volume_keys_size - opal_params->user_key_size,
+ params, &integrity);
+ if (r < 0)
+ goto out;
+ }
+
+ r = device_size(crypt_data_device(cd), &device_size_bytes);
+ if (r < 0)
+ goto out;
+
+ r = LUKS2_hdr_get_storage_params(cd, alignment_offset_bytes, required_alignment_bytes,
+ &metadata_size_bytes, &keyslots_size_bytes, &data_offset_bytes);
+ if (r < 0)
+ goto out;
+
+ r = -EINVAL;
+ if (device_size_bytes < data_offset_bytes && !cd->metadata_device) {
+ log_err(cd, _("Device %s is too small."), device_path(crypt_data_device(cd)));
+ goto out;
+ }
+
+ device_size_bytes -= data_offset_bytes;
+ range_size_bytes = device_size_bytes - (device_size_bytes % (opal_block_bytes * opal_alignment_granularity_blocks));
+ if (!range_size_bytes)
+ goto out;
+
+ if (device_size_bytes != range_size_bytes)
+ log_err(cd, _("Compensating device size by %" PRIu64 " sectors to align it with OPAL alignment granularity."),
+ (device_size_bytes - range_size_bytes) / SECTOR_SIZE);
+
+ if (cipher) {
+ r = LUKS2_check_encryption_sector(cd, device_size_bytes, data_offset_bytes, sector_size,
+ sector_size_autodetect, integrity == NULL,
+ &sector_size);
+ if (r < 0)
+ goto out;
+
+ if (*cipher_mode != '\0')
+ r = snprintf(cipher_spec, sizeof(cipher_spec), "%s-%s", cipher, cipher_mode);
+ else
+ r = snprintf(cipher_spec, sizeof(cipher_spec), "%s", cipher);
+ if (r < 0 || (size_t)r >= sizeof(cipher_spec)) {
+ r = -EINVAL;
+ goto out;
+ }
+ }
+
+ r = LUKS2_generate_hdr(cd, &cd->u.luks2.hdr, cd->volume_key,
+ cipher ? cipher_spec : NULL, integrity, uuid,
+ sector_size,
+ data_offset_bytes,
+ metadata_size_bytes, keyslots_size_bytes,
+ device_size_bytes,
+ opal_segment_number,
+ opal_params->user_key_size);
+ if (r < 0)
+ goto out;
+
+ log_dbg(cd, "Adding LUKS2 OPAL requirement flag.");
+ r = LUKS2_config_set_requirement_version(cd, &cd->u.luks2.hdr, CRYPT_REQUIREMENT_OPAL, 1, false);
+ if (r < 0)
+ goto out;
+
+ if (params->label || params->subsystem) {
+ r = LUKS2_hdr_labels(cd, &cd->u.luks2.hdr,
+ params->label, params->subsystem, 0);
+ if (r < 0)
+ goto out;
+ }
+
+ device_set_block_size(crypt_data_device(cd), sector_size);
+
+ r = LUKS2_wipe_header_areas(cd, &cd->u.luks2.hdr, cd->metadata_device != NULL);
+ if (r < 0) {
+ log_err(cd, _("Cannot wipe header on device %s."),
+ mdata_device_path(cd));
+ if (device_size_bytes < LUKS2_hdr_and_areas_size(&cd->u.luks2.hdr))
+ log_err(cd, _("Device %s is too small."), device_path(crypt_metadata_device(cd)));
+ goto out;
+ }
+
+ range_offset_blocks = (data_offset_bytes + partition_offset_sectors * SECTOR_SIZE) / opal_block_bytes;
+
+ r = opal_exclusive_lock(cd, crypt_data_device(cd), &opal_lh);
+ if (r < 0) {
+ log_err(cd, _("Failed to acquire OPAL lock on device %s."), device_path(crypt_data_device(cd)));
+ goto out;
+ }
+
+ r = opal_setup_ranges(cd, crypt_data_device(cd), user_key ?: cd->volume_key,
+ range_offset_blocks, range_size_bytes / opal_block_bytes,
+ opal_segment_number, opal_params->admin_key, opal_params->admin_key_size);
+ if (r < 0) {
+ if (r == -EPERM)
+ log_err(cd, _("Incorrect OPAL Admin key."));
+ else
+ log_err(cd, _("Cannot setup OPAL segment."));
+ goto out;
+ }
+
+ opal_range_reset = true;
+
+ /* integrity metadata goes in unlocked OPAL locking range */
+ if (crypt_get_integrity_tag_size(cd)) {
+ r = opal_unlock(cd, crypt_data_device(cd), opal_segment_number, user_key ?: cd->volume_key);
+ if (r < 0)
+ goto out;
+
+ r = crypt_wipe_device(cd, crypt_data_device(cd), CRYPT_WIPE_ZERO,
+ crypt_get_data_offset(cd) * SECTOR_SIZE,
+ 8 * SECTOR_SIZE, 8 * SECTOR_SIZE, NULL, NULL);
+ if (r < 0) {
+ if (r == -EBUSY)
+ log_err(cd, _("Cannot format device %s in use."),
+ data_device_path(cd));
+ else if (r == -EACCES) {
+ log_err(cd, _("Cannot format device %s, permission denied."),
+ data_device_path(cd));
+ r = -EINVAL;
+ } else
+ log_err(cd, _("Cannot wipe header on device %s."),
+ data_device_path(cd));
+
+ goto out;
+ }
+
+ r = INTEGRITY_format(cd, params->integrity_params, NULL, NULL,
+ /*
+ * Create reduced dm-integrity device only if locking range size does
+ * not match device size.
+ */
+ device_size_bytes != range_size_bytes ? range_size_bytes / SECTOR_SIZE : 0);
+ if (r)
+ log_err(cd, _("Cannot format integrity for device %s."),
+ data_device_path(cd));
+ if (r < 0)
+ goto out;
+
+ r = INTEGRITY_data_sectors(cd, crypt_data_device(cd),
+ crypt_get_data_offset(cd) * SECTOR_SIZE,
+ &provided_data_sectors);
+ if (r < 0)
+ goto out;
+
+ if (!LUKS2_segment_set_size(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT,
+ &(uint64_t) {provided_data_sectors * SECTOR_SIZE})) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ r = opal_lock(cd, crypt_data_device(cd), opal_segment_number);
+ if (r < 0)
+ goto out;
+ }
+
+ /* override sequence id check with format */
+ r = LUKS2_hdr_write_force(cd, &cd->u.luks2.hdr);
+ if (r < 0) {
+ if (r == -EBUSY)
+ log_err(cd, _("Cannot format device %s in use."),
+ mdata_device_path(cd));
+ else if (r == -EACCES) {
+ log_err(cd, _("Cannot format device %s, permission denied."),
+ mdata_device_path(cd));
+ r = -EINVAL;
+ } else if (r == -EIO) {
+ log_err(cd, _("Cannot format device %s, OPAL device seems to be fully write-protected now."),
+ mdata_device_path(cd));
+ log_err(cd, _("This is perhaps a bug in firmware. Run OPAL PSID reset and reconnect for recovery."));
+ } else
+ log_err(cd, _("Cannot format device %s."),
+ mdata_device_path(cd));
+ }
+
+out:
+ crypt_free_volume_key(user_key);
+
+ if (subsystem_overridden)
+ params->subsystem = NULL;
+
+ if (r >= 0) {
+ opal_exclusive_unlock(cd, opal_lh);
+ return 0;
+ }
+
+ if (opal_range_reset &&
+ (opal_reset_segment(cd, crypt_data_device(cd), opal_segment_number,
+ opal_params->admin_key, opal_params->admin_key_size) < 0))
+ log_err(cd, _("Locking range %d reset on device %s failed."),
+ opal_segment_number, device_path(crypt_data_device(cd)));
+
+ opal_exclusive_unlock(cd, opal_lh);
+ LUKS2_hdr_free(cd, &cd->u.luks2.hdr);
+
+ crypt_set_null_type(cd);
+ crypt_free_volume_key(cd->volume_key);
+ cd->volume_key = NULL;
+
+ return r;
+}
+
static int _crypt_format_loopaes(struct crypt_device *cd,
const char *cipher,
const char *uuid,
@@ -2329,7 +2956,7 @@ static int _crypt_format_integrity(struct crypt_device *cd,
cd->u.integrity.params.journal_integrity = journal_integrity;
cd->u.integrity.params.journal_crypt = journal_crypt;
- r = INTEGRITY_format(cd, params, cd->u.integrity.journal_crypt_key, cd->u.integrity.journal_mac_key);
+ r = INTEGRITY_format(cd, params, cd->u.integrity.journal_crypt_key, cd->u.integrity.journal_mac_key, 0);
if (r)
log_err(cd, _("Cannot format integrity for device %s."),
mdata_device_path(cd));
@@ -2674,7 +3301,7 @@ int crypt_compare_dm_devices(struct crypt_device *cd,
}
static int _reload_device(struct crypt_device *cd, const char *name,
- struct crypt_dm_active_device *sdmd)
+ struct crypt_dm_active_device *sdmd, uint32_t dmflags)
{
int r;
struct crypt_dm_active_device tdmd;
@@ -2742,7 +3369,7 @@ static int _reload_device(struct crypt_device *cd, const char *name,
tdmd.flags = sdmd->flags;
tgt->size = tdmd.size = sdmd->size;
- r = dm_reload_device(cd, name, &tdmd, 0, 1);
+ r = dm_reload_device(cd, name, &tdmd, dmflags, 1);
out:
dm_targets_free(cd, &tdmd);
free(CONST_CAST(void*)tdmd.uuid);
@@ -2925,15 +3552,10 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size)
struct crypt_dm_active_device dmdq, dmd = {};
struct dm_target *tgt = &dmdq.segment;
struct crypt_params_integrity params = {};
- uint32_t supported_flags = 0;
+ uint32_t supported_flags = 0, dmflags = 0;
uint64_t old_size;
int r;
- /*
- * FIXME: Also with LUKS2 we must not allow resize when there's
- * explicit size stored in metadata (length != "dynamic")
- */
-
/* Device context type must be initialized */
if (!cd || !cd->type || !name)
return -EINVAL;
@@ -2943,7 +3565,15 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size)
return -ENOTSUP;
}
- log_dbg(cd, "Resizing device %s to %" PRIu64 " sectors.", name, new_size);
+ if (isLUKS2(cd->type) && !LUKS2_segments_dynamic_size(&cd->u.luks2.hdr)) {
+ log_err(cd, _("Can not resize LUKS2 device with static size."));
+ return -EINVAL;
+ }
+
+ if (new_size)
+ log_dbg(cd, "Resizing device %s to %" PRIu64 " sectors.", name, new_size);
+ else
+ log_dbg(cd, "Resizing device %s to underlying device size.", name);
r = dm_query_device(cd, name, DM_ACTIVE_CRYPT_KEYSIZE | DM_ACTIVE_CRYPT_KEY |
DM_ACTIVE_INTEGRITY_PARAMS | DM_ACTIVE_JOURNAL_CRYPT_KEY |
@@ -3011,7 +3641,8 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size)
tgt->u.integrity.journal_integrity_key, &params);
if (r)
goto out;
- r = _reload_device(cd, name, &dmd);
+ /* Backend device cannot be smaller here, device_block_adjust() will fail if so. */
+ r = _reload_device(cd, name, &dmd, DM_SUSPEND_SKIP_LOCKFS | DM_SUSPEND_NOFLUSH);
if (r)
goto out;
@@ -3079,8 +3710,13 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size)
r = -ENOTSUP;
else if (isLUKS2(cd->type))
r = LUKS2_unmet_requirements(cd, &cd->u.luks2.hdr, 0, 0);
- if (!r)
- r = _reload_device(cd, name, &dmd);
+
+ if (!r) {
+ /* Skip flush and lockfs if extending device */
+ if (new_size > dmdq.size)
+ dmflags = DM_SUSPEND_SKIP_LOCKFS | DM_SUSPEND_NOFLUSH;
+ r = _reload_device(cd, name, &dmd, dmflags);
+ }
if (r && tgt->type == DM_INTEGRITY &&
!dm_flags(cd, tgt->type, &supported_flags) &&
@@ -3271,6 +3907,8 @@ void crypt_free(struct crypt_device *cd)
free(CONST_CAST(void*)cd->pbkdf.type);
free(CONST_CAST(void*)cd->pbkdf.hash);
+ free(CONST_CAST(void*)cd->user_key_name1);
+ free(CONST_CAST(void*)cd->user_key_name2);
/* Some structures can contain keys (TCRYPT), wipe it */
crypt_safe_memzero(cd, sizeof(*cd));
@@ -3298,38 +3936,85 @@ static char *crypt_get_device_key_description(struct crypt_device *cd, const cha
int crypt_suspend(struct crypt_device *cd,
const char *name)
{
- char *key_desc;
+ bool dm_opal_uuid;
crypt_status_info ci;
int r;
- uint32_t dmflags = DM_SUSPEND_WIPE_KEY;
-
- /* FIXME: check context uuid matches the dm-crypt device uuid (onlyLUKS branching) */
+ struct crypt_dm_active_device dmd, dmdi = {};
+ uint32_t opal_segment_number = 1, dmflags = DM_SUSPEND_WIPE_KEY;
+ struct dm_target *tgt = &dmd.segment;
+ char *key_desc = NULL, *iname = NULL;
+ struct crypt_lock_handle *opal_lh = NULL;
if (!cd || !name)
return -EINVAL;
log_dbg(cd, "Suspending volume %s.", name);
- if (cd->type)
- r = onlyLUKS(cd);
- else {
- r = crypt_uuid_type_cmp(cd, CRYPT_LUKS1);
- if (r < 0)
- r = crypt_uuid_type_cmp(cd, CRYPT_LUKS2);
- if (r < 0)
- log_err(cd, _("This operation is supported only for LUKS device."));
- }
-
- if (r < 0)
+ if (cd->type && ((r = onlyLUKS(cd)) < 0))
return r;
- ci = crypt_status(NULL, name);
+ ci = crypt_status(cd, name);
if (ci < CRYPT_ACTIVE) {
log_err(cd, _("Volume %s is not active."), name);
return -EINVAL;
}
- dm_backend_init(cd);
+ r = dm_query_device(cd, name, DM_ACTIVE_UUID, &dmd);
+ if (r < 0)
+ return r;
+
+ log_dbg(cd, "Checking if active device %s has UUID type LUKS.", name);
+
+ r = crypt_uuid_type_cmp(dmd.uuid, CRYPT_LUKS2);
+ if (r < 0)
+ r = crypt_uuid_type_cmp(dmd.uuid, CRYPT_LUKS1);
+
+ if (r < 0) {
+ log_err(cd, _("This operation is supported only for LUKS device."));
+ goto out;
+ }
+
+ r = -EINVAL;
+
+ if (isLUKS2(cd->type) && crypt_uuid_type_cmp(dmd.uuid, CRYPT_LUKS2)) {
+ log_dbg(cd, "LUKS device header type: %s mismatches DM device type.", cd->type);
+ goto out;
+ }
+
+ if (isLUKS1(cd->type) && crypt_uuid_type_cmp(dmd.uuid, CRYPT_LUKS1)) {
+ log_dbg(cd, "LUKS device header type: %s mismatches DM device type.", cd->type);
+ goto out;
+ }
+
+ /* check if active device has LUKS2-OPAL dm uuid prefix */
+ dm_opal_uuid = !crypt_uuid_type_cmp(dmd.uuid, CRYPT_LUKS2_HW_OPAL);
+
+ if (!dm_opal_uuid && isLUKS2(cd->type) &&
+ LUKS2_segment_is_hw_opal(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT))
+ goto out;
+
+ if (cd->type && (r = crypt_uuid_cmp(dmd.uuid, LUKS_UUID(cd))) < 0) {
+ log_dbg(cd, "LUKS device header uuid: %s mismatches DM returned uuid %s",
+ LUKS_UUID(cd), dmd.uuid);
+ goto out;
+ }
+
+ /* check UUID of integrity device underneath crypt device */
+ if (crypt_get_integrity_tag_size(cd)) {
+ r = dm_get_iname(name, &iname, false);
+ if (r)
+ goto out;
+
+ r = dm_query_device(cd, iname, DM_ACTIVE_UUID, &dmdi);
+ if (r < 0)
+ goto out;
+
+ r = crypt_uuid_integrity_cmp(dmd.uuid, dmdi.uuid);
+ if (r < 0) {
+ log_dbg(cd, "Integrity device uuid: %s mismatches crypt device uuid %s", dmdi.uuid, dmd.uuid);
+ goto out;
+ }
+ }
r = dm_status_suspended(cd, name);
if (r < 0)
@@ -3343,44 +4028,78 @@ int crypt_suspend(struct crypt_device *cd,
key_desc = crypt_get_device_key_description(cd, name);
- /* we can't simply wipe wrapped keys */
- if (crypt_cipher_wrapped_key(crypt_get_cipher(cd), crypt_get_cipher_mode(cd)))
+ if (dm_opal_uuid && crypt_data_device(cd)) {
+ if (isLUKS2(cd->type)) {
+ r = LUKS2_get_opal_segment_number(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, &opal_segment_number);
+ if (r < 0)
+ goto out;
+ } else {
+ /* Guess OPAL range number for LUKS2-OPAL device with missing header */
+ r = crypt_dev_get_partition_number(device_path(crypt_data_device(cd)));
+ if (r > 0)
+ opal_segment_number = r;
+ }
+ }
+
+ /* we can't simply wipe wrapped keys. HW OPAL only encryption does not use dm-crypt target */
+ if (crypt_cipher_wrapped_key(crypt_get_cipher(cd), crypt_get_cipher_mode(cd)) ||
+ (dm_opal_uuid && tgt->type == DM_LINEAR))
dmflags &= ~DM_SUSPEND_WIPE_KEY;
r = dm_suspend_device(cd, name, dmflags);
- if (r == -ENOTSUP)
- log_err(cd, _("Suspend is not supported for device %s."), name);
- else if (r)
- log_err(cd, _("Error during suspending device %s."), name);
- else
- crypt_drop_keyring_key_by_description(cd, key_desc, LOGON_KEY);
- free(key_desc);
+ if (r) {
+ if (r == -ENOTSUP)
+ log_err(cd, _("Suspend is not supported for device %s."), name);
+ else
+ log_err(cd, _("Error during suspending device %s."), name);
+ goto out;
+ }
+
+ /* Suspend integrity device underneath; keep crypt suspended if it fails */
+ if (crypt_get_integrity_tag_size(cd)) {
+ r = dm_suspend_device(cd, iname, 0);
+ if (r)
+ log_err(cd, _("Error during suspending device %s."), iname);
+ }
+
+ crypt_drop_keyring_key_by_description(cd, key_desc, cd->keyring_key_type);
+
+ if (dm_opal_uuid && crypt_data_device(cd)) {
+ r = opal_exclusive_lock(cd, crypt_data_device(cd), &opal_lh);
+ if (r < 0) {
+ log_err(cd, _("Failed to acquire OPAL lock on device %s."), device_path(crypt_data_device(cd)));
+ goto out;
+ }
+ }
+
+ if (dm_opal_uuid && (!crypt_data_device(cd) || opal_lock(cd, crypt_data_device(cd), opal_segment_number)))
+ log_err(cd, _("Device %s was suspended but hardware OPAL device cannot be locked."), name);
out:
- dm_backend_exit(cd);
+ opal_exclusive_unlock(cd, opal_lh);
+ free(key_desc);
+ free(iname);
+ dm_targets_free(cd, &dmd);
+ dm_targets_free(cd, &dmdi);
+ free(CONST_CAST(void*)dmd.uuid);
+ free(CONST_CAST(void*)dmdi.uuid);
return r;
}
-/* key must be properly verified */
-static int resume_by_volume_key(struct crypt_device *cd,
+static int resume_luks1_by_volume_key(struct crypt_device *cd,
struct volume_key *vk,
const char *name)
{
- int digest, r;
+ int r;
struct volume_key *zerokey = NULL;
+ assert(vk && crypt_volume_key_get_id(vk) == 0);
+ assert(name);
+
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, vk, digest);
- if (r < 0)
- return r;
}
r = dm_resume_and_reinstate_key(cd, name, vk);
@@ -3390,77 +4109,230 @@ static int resume_by_volume_key(struct crypt_device *cd,
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,
- const char *passphrase,
- size_t passphrase_size)
+static void crypt_unlink_key_from_custom_keyring(struct crypt_device *cd, key_serial_t kid)
{
- struct volume_key *vk = NULL;
- int r;
+ assert(cd);
+ assert(cd->keyring_to_link_vk);
+
+ log_dbg(cd, "Unlinking volume key (id: %" PRIi32 ") from kernel keyring (id: %" PRIi32 ").",
+ kid, cd->keyring_to_link_vk);
+
+ if (!keyring_unlink_key_from_keyring(kid, cd->keyring_to_link_vk))
+ return;
+
+ log_dbg(cd, "keyring_unlink_key_from_keyring failed with errno %d.", errno);
+ log_err(cd, _("Failed to unlink volume key from user specified keyring."));
+}
- /* FIXME: check context uuid matches the dm-crypt device uuid */
+static key_serial_t crypt_single_volume_key_load_in_user_keyring(struct crypt_device *cd, struct volume_key *vk, const char *user_key_name)
+{
+ key_serial_t kid;
+ const char *type_name;
+
+ assert(cd);
+ assert(cd->link_vk_to_keyring);
- if (!passphrase || !name)
+ if (!vk || !(type_name = key_type_name(cd->keyring_key_type)))
return -EINVAL;
- log_dbg(cd, "Resuming volume %s.", name);
+ log_dbg(cd, "Linking volume key (type %s, name %s) to the specified keyring",
+ type_name, user_key_name);
- if ((r = onlyLUKS(cd)))
- return r;
+ kid = keyring_add_key_to_custom_keyring(cd->keyring_key_type, user_key_name, vk->key, vk->keylength, cd->keyring_to_link_vk);
+ if (kid <= 0) {
+ log_dbg(cd, "The keyring_link_key_to_keyring function failed (error %d).", errno);
+ }
- r = dm_status_suspended(cd, name);
- if (r < 0)
- return r;
+ return kid;
+}
- if (!r) {
- log_err(cd, _("Volume %s is not suspended."), name);
+static int crypt_volume_key_load_in_user_keyring(struct crypt_device *cd, struct volume_key *vk, key_serial_t *kid1_out, key_serial_t *kid2_out)
+{
+ key_serial_t kid1, kid2 = 0;
+
+ assert(cd);
+ assert(cd->link_vk_to_keyring);
+ assert(cd->user_key_name1);
+
+ if (!vk || !key_type_name(cd->keyring_key_type))
+ return -EINVAL;
+
+ kid1 = crypt_single_volume_key_load_in_user_keyring(cd, vk, cd->user_key_name1);
+ if (kid1 <= 0)
return -EINVAL;
+
+ vk = vk->next;
+ if (vk) {
+ assert(cd->user_key_name2);
+ kid2 = crypt_single_volume_key_load_in_user_keyring(cd, vk, cd->user_key_name2);
+ if (kid2 <= 0) {
+ crypt_unlink_key_from_custom_keyring(cd, kid1);
+ return -EINVAL;
+ }
}
- if (isLUKS1(cd->type))
- r = LUKS_open_key_with_hdr(keyslot, passphrase, passphrase_size,
- &cd->u.luks1.hdr, &vk, cd);
+ *kid2_out = kid2;
+ *kid1_out = kid1;
+ return 0;
+}
+
+static int resume_luks2_by_volume_key(struct crypt_device *cd,
+ int digest,
+ struct volume_key *vk,
+ const char *name)
+{
+ bool use_keyring;
+ int r, enc_type;
+ uint32_t opal_segment_number;
+ struct volume_key *p_crypt = vk, *p_opal = NULL, *zerokey = NULL, *crypt_key = NULL, *opal_key = NULL;
+ char *iname = NULL;
+ struct crypt_lock_handle *opal_lh = NULL;
+ key_serial_t kid1 = 0, kid2 = 0;
+
+ assert(digest >= 0);
+ assert(vk && crypt_volume_key_get_id(vk) == digest);
+ assert(name);
+
+ enc_type = crypt_get_hw_encryption_type(cd);
+ if (enc_type < 0)
+ return enc_type;
+
+ use_keyring = crypt_use_keyring_for_vk(cd);
+
+ if (enc_type == CRYPT_OPAL_HW_ONLY || enc_type == CRYPT_SW_AND_OPAL_HW) {
+ r = LUKS2_get_opal_segment_number(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT,
+ &opal_segment_number);
+ if (r < 0)
+ return r;
+
+ r = LUKS2_split_crypt_and_opal_keys(cd, &cd->u.luks2.hdr,
+ vk, &crypt_key,
+ &opal_key);
+ if (r < 0)
+ return r;
+
+ p_crypt = crypt_key;
+ p_opal = opal_key ?: vk;
+ }
+
+ if (enc_type != CRYPT_OPAL_HW_ONLY && crypt_is_cipher_null(crypt_get_cipher_spec(cd))) {
+ zerokey = crypt_alloc_volume_key(0, NULL);
+ if (!zerokey) {
+ r = -ENOMEM;
+ goto out;
+ }
+ p_crypt = zerokey;
+ use_keyring = false;
+ }
+
+ if (use_keyring) {
+ if (p_crypt) {
+ r = LUKS2_volume_key_load_in_keyring_by_digest(cd, p_crypt, digest);
+ if (r < 0)
+ goto out;
+ }
+
+ /* upload volume key in custom keyring if requested */
+ if (cd->link_vk_to_keyring) {
+ r = crypt_volume_key_load_in_user_keyring(cd, vk, &kid1, &kid2);
+ if (r < 0) {
+ log_err(cd, _("Failed to link volume key in user defined keyring."));
+ goto out;
+ }
+ }
+ }
+
+ if (p_opal) {
+ r = opal_exclusive_lock(cd, crypt_data_device(cd), &opal_lh);
+ if (r < 0) {
+ log_err(cd, _("Failed to acquire OPAL lock on device %s."), device_path(crypt_data_device(cd)));
+ goto out;
+ }
+
+ r = opal_unlock(cd, crypt_data_device(cd), opal_segment_number, p_opal);
+ if (r < 0) {
+ p_opal = NULL; /* do not lock on error path */
+ goto out;
+ }
+ }
+
+ if (crypt_get_integrity_tag_size(cd)) {
+ r = dm_get_iname(name, &iname, false);
+ if (r)
+ goto out;
+
+ r = dm_resume_device(cd, iname, 0);
+ if (r)
+ log_err(cd, _("Error during resuming device %s."), iname);
+ }
+
+ if (enc_type == CRYPT_OPAL_HW_ONLY)
+ r = dm_resume_device(cd, name, 0);
else
- r = LUKS2_keyslot_open(cd, keyslot, CRYPT_DEFAULT_SEGMENT, passphrase, passphrase_size, &vk);
+ r = dm_resume_and_reinstate_key(cd, name, p_crypt);
- if (r < 0)
- return r;
+ 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);
- keyslot = r;
+out:
+ if (r < 0) {
+ crypt_drop_keyring_key(cd, p_crypt);
+ if (cd->link_vk_to_keyring && kid1)
+ crypt_unlink_key_from_custom_keyring(cd, kid1);
+ if (cd->link_vk_to_keyring && kid2)
+ crypt_unlink_key_from_custom_keyring(cd, kid2);
+ }
- r = resume_by_volume_key(cd, vk, name);
+ if (r < 0 && p_opal)
+ opal_lock(cd, crypt_data_device(cd), opal_segment_number);
- crypt_free_volume_key(vk);
- return r < 0 ? r : keyslot;
+ opal_exclusive_unlock(cd, opal_lh);
+ crypt_free_volume_key(zerokey);
+ crypt_free_volume_key(opal_key);
+ crypt_free_volume_key(crypt_key);
+ free(iname);
+
+ return r;
}
-int crypt_resume_by_keyfile_device_offset(struct crypt_device *cd,
- const char *name,
- int keyslot,
- const char *keyfile,
- size_t keyfile_size,
- uint64_t keyfile_offset)
+/* key must be properly verified */
+static int resume_by_volume_key(struct crypt_device *cd,
+ struct volume_key *vk,
+ const char *name)
{
- struct volume_key *vk = NULL;
- char *passphrase_read = NULL;
- size_t passphrase_size_read;
- int r;
+ assert(cd);
- /* FIXME: check context uuid matches the dm-crypt device uuid */
+ if (isLUKS2(cd->type))
+ return resume_luks2_by_volume_key(cd,
+ LUKS2_digest_by_segment(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT),
+ vk, name);
- if (!name || !keyfile)
+ if (isLUKS1(cd->type))
+ return resume_luks1_by_volume_key(cd, vk, name);
+
+ return -EINVAL;
+}
+
+int crypt_resume_by_keyslot_context(struct crypt_device *cd,
+ const char *name,
+ int keyslot,
+ struct crypt_keyslot_context *kc)
+{
+ int r;
+ struct volume_key *vk = NULL;
+ int unlocked_keyslot = -EINVAL;
+
+ if (!name)
return -EINVAL;
- log_dbg(cd, "Resuming volume %s.", name);
+ log_dbg(cd, "Resuming volume %s [keyslot %d] using %s.", name, keyslot, keyslot_context_type_string(kc));
if ((r = onlyLUKS(cd)))
return r;
@@ -3474,29 +4346,67 @@ int crypt_resume_by_keyfile_device_offset(struct crypt_device *cd,
return -EINVAL;
}
- r = crypt_keyfile_device_read(cd, keyfile,
- &passphrase_read, &passphrase_size_read,
- keyfile_offset, keyfile_size, 0);
- if (r < 0)
- return r;
-
- if (isLUKS1(cd->type))
- r = LUKS_open_key_with_hdr(keyslot, passphrase_read, passphrase_size_read,
- &cd->u.luks1.hdr, &vk, cd);
+ if (isLUKS1(cd->type) && kc->get_luks1_volume_key)
+ r = kc->get_luks1_volume_key(cd, kc, keyslot, &vk);
+ else if (isLUKS2(cd->type) && kc->get_luks2_volume_key)
+ r = kc->get_luks2_volume_key(cd, kc, keyslot, &vk);
else
- r = LUKS2_keyslot_open(cd, keyslot, CRYPT_DEFAULT_SEGMENT,
- passphrase_read, passphrase_size_read, &vk);
-
- crypt_safe_free(passphrase_read);
+ r = -EINVAL;
if (r < 0)
- return r;
+ goto out;
+ unlocked_keyslot = r;
- keyslot = r;
+ if (isLUKS1(cd->type)) {
+ r = LUKS_verify_volume_key(&cd->u.luks1.hdr, vk);
+ crypt_volume_key_set_id(vk, 0);
+ } else if (isLUKS2(cd->type)) {
+ r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk);
+ crypt_volume_key_set_id(vk, r);
+ } else
+ r = -EINVAL;
+ if (r < 0)
+ goto out;
r = resume_by_volume_key(cd, vk, name);
crypt_free_volume_key(vk);
- return r < 0 ? r : keyslot;
+ return r < 0 ? r : unlocked_keyslot;
+out:
+ crypt_free_volume_key(vk);
+ return r;
+}
+
+int crypt_resume_by_passphrase(struct crypt_device *cd,
+ const char *name,
+ int keyslot,
+ const char *passphrase,
+ size_t passphrase_size)
+{
+ int r;
+ struct crypt_keyslot_context kc;
+
+ crypt_keyslot_unlock_by_passphrase_init_internal(&kc, passphrase, passphrase_size);
+ r = crypt_resume_by_keyslot_context(cd, name, keyslot, &kc);
+ crypt_keyslot_context_destroy_internal(&kc);
+
+ return r;
+}
+
+int crypt_resume_by_keyfile_device_offset(struct crypt_device *cd,
+ const char *name,
+ int keyslot,
+ const char *keyfile,
+ size_t keyfile_size,
+ uint64_t keyfile_offset)
+{
+ int r;
+ struct crypt_keyslot_context kc;
+
+ crypt_keyslot_unlock_by_keyfile_init_internal(&kc, keyfile, keyfile_size, keyfile_offset);
+ r = crypt_resume_by_keyslot_context(cd, name, keyslot, &kc);
+ crypt_keyslot_context_destroy_internal(&kc);
+
+ return r;
}
int crypt_resume_by_keyfile(struct crypt_device *cd,
@@ -3525,43 +4435,16 @@ int crypt_resume_by_volume_key(struct crypt_device *cd,
const char *volume_key,
size_t volume_key_size)
{
- struct volume_key *vk = NULL;
int r;
+ struct crypt_keyslot_context kc;
- if (!name || !volume_key)
- return -EINVAL;
-
- log_dbg(cd, "Resuming volume %s by volume key.", name);
-
- if ((r = onlyLUKS(cd)))
- return r;
-
- r = dm_status_suspended(cd, name);
- if (r < 0)
- return r;
-
- if (!r) {
- log_err(cd, _("Volume %s is not suspended."), name);
- return -EINVAL;
- }
-
- vk = crypt_alloc_volume_key(volume_key_size, volume_key);
- if (!vk)
- return -ENOMEM;
+ crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size);
+ r = crypt_resume_by_keyslot_context(cd, name, CRYPT_ANY_SLOT /* unused */, &kc);
+ crypt_keyslot_context_destroy_internal(&kc);
- if (isLUKS1(cd->type))
- r = LUKS_verify_volume_key(&cd->u.luks1.hdr, vk);
- else if (isLUKS2(cd->type))
- r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk);
- else
- r = -EINVAL;
if (r == -EPERM || r == -ENOENT)
log_err(cd, _("Volume key does not match the volume."));
- if (r >= 0)
- r = resume_by_volume_key(cd, vk, name);
-
- crypt_free_volume_key(vk);
return r;
}
@@ -3569,35 +4452,14 @@ int crypt_resume_by_token_pin(struct crypt_device *cd, const char *name,
const char *type, int token, const char *pin, size_t pin_size,
void *usrptr)
{
- struct volume_key *vk = NULL;
- int r, keyslot;
-
- if (!name)
- return -EINVAL;
-
- log_dbg(cd, "Resuming volume %s by token (%s type) %d.",
- name, type ?: "any", token);
-
- if ((r = _onlyLUKS2(cd, CRYPT_CD_QUIET, 0)))
- return r;
-
- r = dm_status_suspended(cd, name);
- if (r < 0)
- return r;
-
- if (!r) {
- log_err(cd, _("Volume %s is not suspended."), name);
- return -EINVAL;
- }
+ int r;
+ struct crypt_keyslot_context kc;
- r = LUKS2_token_unlock_key(cd, &cd->u.luks2.hdr, token, type,
- pin, pin_size, CRYPT_DEFAULT_SEGMENT, usrptr, &vk);
- keyslot = r;
- if (r >= 0)
- r = resume_by_volume_key(cd, vk, name);
+ crypt_keyslot_unlock_by_token_init_internal(&kc, token, type, pin, pin_size, usrptr);
+ r = crypt_resume_by_keyslot_context(cd, name, CRYPT_ANY_SLOT, &kc);
+ crypt_keyslot_context_destroy_internal(&kc);
- crypt_free_volume_key(vk);
- return r < 0 ? r : keyslot;
+ return r;
}
/*
@@ -3635,7 +4497,8 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd,
const char *new_passphrase,
size_t new_passphrase_size)
{
- int digest = -1, r, keyslot_new_orig = keyslot_new;
+ bool keyslot_swap = false;
+ int digest = -1, r;
struct luks2_keyslot_params params;
struct volume_key *vk = NULL;
@@ -3670,13 +4533,21 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd,
}
keyslot_old = r;
- if (keyslot_new == CRYPT_ANY_SLOT) {
- if (isLUKS1(cd->type))
- keyslot_new = LUKS_keyslot_find_empty(&cd->u.luks1.hdr);
- else if (isLUKS2(cd->type))
+ if (isLUKS2(cd->type)) {
+ /* If there is a free keyslot (both id and binary area) avoid in-place keyslot area overwrite */
+ if (keyslot_new == CRYPT_ANY_SLOT || keyslot_new == keyslot_old) {
keyslot_new = LUKS2_keyslot_find_empty(cd, &cd->u.luks2.hdr, vk->keylength);
- if (keyslot_new < 0)
- keyslot_new = keyslot_old;
+ if (keyslot_new < 0)
+ keyslot_new = keyslot_old;
+ else
+ keyslot_swap = true;
+ }
+ } else if (isLUKS1(cd->type)) {
+ if (keyslot_new == CRYPT_ANY_SLOT) {
+ keyslot_new = LUKS_keyslot_find_empty(&cd->u.luks1.hdr);
+ if (keyslot_new < 0)
+ keyslot_new = keyslot_old;
+ }
}
log_dbg(cd, "Key change, old slot %d, new slot %d.", keyslot_old, keyslot_new);
@@ -3699,16 +4570,8 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd,
r = LUKS2_token_assignment_copy(cd, &cd->u.luks2.hdr, keyslot_old, keyslot_new, 0);
if (r < 0)
goto out;
- } else {
+ } 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 */
- r = LUKS2_keyslot_wipe(cd, &cd->u.luks2.hdr, keyslot_old, 1);
- if (r) {
- /* (void)crypt_keyslot_destroy(cd, keyslot_old); */
- r = -EINVAL;
- goto out;
- }
- }
r = LUKS2_keyslot_store(cd, &cd->u.luks2.hdr,
keyslot_new, new_passphrase,
@@ -3717,7 +4580,7 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd,
goto out;
/* Swap old & new so the final keyslot number remains */
- if (keyslot_new_orig == CRYPT_ANY_SLOT && keyslot_old != keyslot_new) {
+ if (keyslot_swap && keyslot_old != keyslot_new) {
r = LUKS2_keyslot_swap(cd, &cd->u.luks2.hdr, keyslot_old, keyslot_new);
if (r < 0)
goto out;
@@ -3827,7 +4690,7 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot)
log_dbg(cd, "Destroying keyslot %d.", keyslot);
- if ((r = _onlyLUKS(cd, CRYPT_CD_UNRESTRICTED)))
+ if ((r = onlyLUKSunrestricted(cd)))
return r;
ki = crypt_keyslot_status(cd, keyslot);
@@ -3844,7 +4707,7 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot)
return LUKS_del_key(keyslot, &cd->u.luks1.hdr, cd);
}
- return LUKS2_keyslot_wipe(cd, &cd->u.luks2.hdr, keyslot, 0);
+ return LUKS2_keyslot_wipe(cd, &cd->u.luks2.hdr, keyslot);
}
static int _check_header_data_overlap(struct crypt_device *cd, const char *name)
@@ -3960,12 +4823,14 @@ int create_or_reload_device(struct crypt_device *cd, const char *name,
int r;
enum devcheck device_check;
struct dm_target *tgt;
+ uint64_t offset;
+ uint32_t dmflags = 0;
if (!type || !name || !single_segment(dmd))
return -EINVAL;
tgt = &dmd->segment;
- if (tgt->type != DM_CRYPT && tgt->type != DM_INTEGRITY)
+ if (tgt->type != DM_CRYPT && tgt->type != DM_INTEGRITY && tgt->type != DM_LINEAR)
return -EINVAL;
/* drop CRYPT_ACTIVATE_REFRESH flag if any device is inactive */
@@ -3973,14 +4838,18 @@ int create_or_reload_device(struct crypt_device *cd, const char *name,
if (r)
return r;
- if (dmd->flags & CRYPT_ACTIVATE_REFRESH)
- r = _reload_device(cd, name, dmd);
- else {
- if (tgt->type == DM_CRYPT) {
+ if (dmd->flags & CRYPT_ACTIVATE_REFRESH) {
+ /* Refresh and recalculate means increasing dm-integrity device */
+ if (tgt->type == DM_INTEGRITY && dmd->flags & CRYPT_ACTIVATE_RECALCULATE)
+ dmflags = DM_SUSPEND_SKIP_LOCKFS | DM_SUSPEND_NOFLUSH;;
+ r = _reload_device(cd, name, dmd, dmflags);
+ } else {
+ if (tgt->type == DM_CRYPT || tgt->type == DM_LINEAR) {
device_check = dmd->flags & CRYPT_ACTIVATE_SHARED ? DEV_OK : DEV_EXCL;
+ offset = tgt->type == DM_CRYPT ? tgt->u.crypt.offset : tgt->u.linear.offset;
r = device_block_adjust(cd, tgt->data_device, device_check,
- tgt->u.crypt.offset, &dmd->size, &dmd->flags);
+ offset, &dmd->size, &dmd->flags);
if (!r) {
tgt->size = dmd->size;
r = dm_create_device(cd, name, type, dmd);
@@ -4009,15 +4878,18 @@ int create_or_reload_device_with_integrity(struct crypt_device *cd, const char *
struct crypt_dm_active_device *dmdi)
{
int r;
- const char *iname = NULL;
- char *ipath = NULL;
+ char *iname = NULL, *ipath = NULL;
if (!type || !name || !dmd || !dmdi)
return -EINVAL;
- if (asprintf(&ipath, "%s/%s_dif", dm_get_dir(), name) < 0)
- return -ENOMEM;
- iname = ipath + strlen(dm_get_dir()) + 1;
+ r = dm_get_iname(name, &iname, false);
+ if (r)
+ goto out;
+
+ r = dm_get_iname(name, &ipath, true);
+ if (r)
+ goto out;
/* drop CRYPT_ACTIVATE_REFRESH flag if any device is inactive */
r = check_devices(cd, name, iname, &dmd->flags);
@@ -4030,6 +4902,7 @@ int create_or_reload_device_with_integrity(struct crypt_device *cd, const char *
r = _create_device_with_integrity(cd, type, name, iname, ipath, dmd, dmdi);
out:
free(ipath);
+ free(iname);
return r;
}
@@ -4043,7 +4916,8 @@ static int _open_and_activate(struct crypt_device *cd,
{
bool use_keyring;
int r;
- struct volume_key *vk = NULL;
+ struct volume_key *p_crypt = NULL, *p_opal = NULL, *crypt_key = NULL, *opal_key = NULL, *vk = NULL;
+ key_serial_t kid1 = 0, kid2 = 0;
r = LUKS2_keyslot_open(cd, keyslot,
(flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) ?
@@ -4053,6 +4927,22 @@ static int _open_and_activate(struct crypt_device *cd,
return r;
keyslot = r;
+ /* split the key only if we do activation */
+ if (name && LUKS2_segment_is_hw_opal(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT)) {
+ r = LUKS2_split_crypt_and_opal_keys(cd, &cd->u.luks2.hdr,
+ vk, &crypt_key,
+ &opal_key);
+ if (r < 0)
+ goto out;
+
+ /* copy volume key digest id in crypt subkey */
+ crypt_volume_key_set_id(crypt_key, crypt_volume_key_get_id(vk));
+
+ p_crypt = crypt_key;
+ p_opal = opal_key ?: vk;
+ } else
+ p_crypt = vk;
+
if (!crypt_use_keyring_for_vk(cd))
use_keyring = false;
else
@@ -4060,25 +4950,44 @@ static int _open_and_activate(struct crypt_device *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)
- goto out;
- flags |= CRYPT_ACTIVATE_KEYRING_KEY;
+ /* upload dm-crypt part of volume key in thread keyring if requested */
+ if (p_crypt) {
+ r = LUKS2_volume_key_load_in_keyring_by_digest(cd, p_crypt,
+ crypt_volume_key_get_id(p_crypt));
+ if (r < 0)
+ goto out;
+ flags |= CRYPT_ACTIVATE_KEYRING_KEY;
+ }
+
+ /* upload the volume key in custom user keyring if requested */
+ if (cd->link_vk_to_keyring) {
+ r = crypt_volume_key_load_in_user_keyring(cd, vk, &kid1, &kid2);
+ if (r < 0) {
+ log_err(cd, _("Failed to link volume key in user defined keyring."));
+ goto out;
+ }
+ }
}
if (name)
- r = LUKS2_activate(cd, name, vk, flags);
+ r = LUKS2_activate(cd, name, p_crypt, p_opal, flags);
out:
- if (r < 0)
- crypt_drop_keyring_key(cd, vk);
+ if (r < 0) {
+ crypt_drop_keyring_key(cd, p_crypt);
+ if (cd->link_vk_to_keyring && kid1)
+ crypt_unlink_key_from_custom_keyring(cd, kid1);
+ if (cd->link_vk_to_keyring && kid2)
+ crypt_unlink_key_from_custom_keyring(cd, kid2);
+ }
crypt_free_volume_key(vk);
+ crypt_free_volume_key(crypt_key);
+ crypt_free_volume_key(opal_key);
return r < 0 ? r : keyslot;
}
#if USE_LUKS2_REENCRYPTION
-static int load_all_keys(struct crypt_device *cd, struct luks2_hdr *hdr, struct volume_key *vks)
+static int load_all_keys(struct crypt_device *cd, struct volume_key *vks)
{
int r;
struct volume_key *vk = vks;
@@ -4129,7 +5038,7 @@ static int _open_all_keys(struct crypt_device *cd,
keyslot = r;
if (r >= 0 && (flags & CRYPT_ACTIVATE_KEYRING_KEY))
- r = load_all_keys(cd, hdr, _vks);
+ r = load_all_keys(cd, _vks);
if (r >= 0 && vks)
MOVE_REF(*vks, _vks);
@@ -4141,6 +5050,107 @@ static int _open_all_keys(struct crypt_device *cd,
return r < 0 ? r : keyslot;
}
+static int _open_and_activate_reencrypt_device_by_vk(struct crypt_device *cd,
+ struct luks2_hdr *hdr,
+ const char *name,
+ struct volume_key *vks,
+ uint32_t flags)
+{
+ bool dynamic_size;
+ crypt_reencrypt_info ri;
+ uint64_t minimal_size, device_size;
+ int r = 0;
+ struct crypt_lock_handle *reencrypt_lock = NULL;
+ key_serial_t kid1 = 0, kid2 = 0;
+ struct volume_key *vk;
+
+ if (!vks)
+ return -EINVAL;
+
+ if (crypt_use_keyring_for_vk(cd))
+ flags |= CRYPT_ACTIVATE_KEYRING_KEY;
+
+ r = LUKS2_reencrypt_lock(cd, &reencrypt_lock);
+ if (r) {
+ if (r == -EBUSY)
+ log_err(cd, _("Reencryption in-progress. Cannot activate device."));
+ else
+ log_err(cd, _("Failed to get reencryption lock."));
+ return r;
+ }
+
+ if ((r = crypt_load(cd, CRYPT_LUKS2, NULL)))
+ goto out;
+
+ ri = LUKS2_reencrypt_status(hdr);
+
+ if (ri == CRYPT_REENCRYPT_CRASH) {
+ r = LUKS2_reencrypt_locked_recovery_by_vks(cd, vks);
+ if (r < 0) {
+ log_err(cd, _("LUKS2 reencryption recovery using volume key(s) failed."));
+ goto out;
+ }
+
+ ri = LUKS2_reencrypt_status(hdr);
+ }
+ /* recovery finished reencryption or it's already finished */
+ if (ri == CRYPT_REENCRYPT_NONE) {
+ vk = crypt_volume_key_by_id(vks, LUKS2_digest_by_segment(hdr, CRYPT_DEFAULT_SEGMENT));
+ if (!vk) {
+ r = -EPERM;
+ goto out;
+ }
+
+ r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk);
+ if (r == -EPERM || r == -ENOENT)
+ log_err(cd, _("Volume key does not match the volume."));
+ if (r >= 0 && cd->link_vk_to_keyring) {
+ kid1 = crypt_single_volume_key_load_in_user_keyring(cd, vk, cd->user_key_name1);
+ if (kid1 <= 0)
+ r = -EINVAL;
+ }
+ if (r >= 0)
+ r = LUKS2_activate(cd, name, vk, NULL, flags);
+ goto out;
+ }
+ if (ri > CRYPT_REENCRYPT_CLEAN) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ if ((flags & CRYPT_ACTIVATE_KEYRING_KEY)) {
+ r = load_all_keys(cd, vks);
+ if (r < 0)
+ goto out;
+ }
+
+ if ((r = LUKS2_get_data_size(hdr, &minimal_size, &dynamic_size)))
+ goto out;
+
+ r = LUKS2_reencrypt_digest_verify(cd, hdr, vks);
+ if (r < 0)
+ goto out;
+
+ log_dbg(cd, "Entering clean reencryption state mode.");
+
+ r = LUKS2_reencrypt_check_device_size(cd, hdr, minimal_size, &device_size, true, dynamic_size);
+ if (r < 0)
+ goto out;
+ if (cd->link_vk_to_keyring) {
+ r = crypt_volume_key_load_in_user_keyring(cd, vks, &kid1, &kid2);
+ if (r < 0) {
+ log_err(cd, _("Failed to link volume keys in user defined keyring."));
+ goto out;
+ }
+ }
+ r = LUKS2_activate_multi(cd, name, vks, device_size >> SECTOR_SHIFT, flags);
+out:
+ LUKS2_reencrypt_unlock(cd, reencrypt_lock);
+ crypt_drop_keyring_key(cd, vks);
+
+ return r;
+}
+
static int _open_and_activate_reencrypt_device(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
@@ -4155,6 +5165,7 @@ static int _open_and_activate_reencrypt_device(struct crypt_device *cd,
struct volume_key *vks = NULL;
int r = 0;
struct crypt_lock_handle *reencrypt_lock = NULL;
+ key_serial_t kid1 = 0, kid2 = 0;
if (crypt_use_keyring_for_vk(cd))
flags |= CRYPT_ACTIVATE_KEYRING_KEY;
@@ -4215,15 +5226,31 @@ static int _open_and_activate_reencrypt_device(struct crypt_device *cd,
log_dbg(cd, "Entering clean reencryption state mode.");
+ if (cd->link_vk_to_keyring) {
+ r = crypt_volume_key_load_in_user_keyring(cd, vks, &kid1, &kid2);
+ if (r < 0) {
+ log_err(cd, _("Failed to link volume keys in user defined keyring."));
+ goto out;
+ }
+ }
+
if (r >= 0)
- r = LUKS2_reencrypt_check_device_size(cd, hdr, minimal_size, &device_size, true, dynamic_size);
+ r = LUKS2_reencrypt_check_device_size(cd, hdr, minimal_size, &device_size,
+ !(flags & CRYPT_ACTIVATE_SHARED),
+ dynamic_size);
if (r >= 0)
r = LUKS2_activate_multi(cd, name, vks, device_size >> SECTOR_SHIFT, flags);
out:
LUKS2_reencrypt_unlock(cd, reencrypt_lock);
- if (r < 0)
+ if (r < 0) {
crypt_drop_keyring_key(cd, vks);
+ if (cd->link_vk_to_keyring && kid1)
+ crypt_unlink_key_from_custom_keyring(cd, kid1);
+ if (cd->link_vk_to_keyring && kid2)
+ crypt_unlink_key_from_custom_keyring(cd, kid2);
+ }
+
crypt_free_volume_key(vks);
return r < 0 ? r : keyslot;
@@ -4269,6 +5296,43 @@ static int _open_and_activate_luks2(struct crypt_device *cd,
return r;
}
+
+static int _activate_luks2_by_volume_key(struct crypt_device *cd,
+ const char *name,
+ struct volume_key *vk,
+ struct volume_key *external_key,
+ uint32_t flags)
+{
+ int r;
+ crypt_reencrypt_info ri;
+ int digest_new, digest_old;
+ struct volume_key *vk_old = NULL, *vk_new = NULL;
+ ri = LUKS2_reencrypt_status(&cd->u.luks2.hdr);
+ if (ri == CRYPT_REENCRYPT_INVALID)
+ return -EINVAL;
+
+ if (ri > CRYPT_REENCRYPT_NONE) {
+ digest_new = LUKS2_reencrypt_digest_new(&cd->u.luks2.hdr);
+ digest_old = LUKS2_reencrypt_digest_old(&cd->u.luks2.hdr);
+
+ if (digest_new >= 0) {
+ vk_new = crypt_volume_key_by_id(vk, digest_new);
+ assert(vk_new);
+ assert(crypt_volume_key_get_id(vk_new) == digest_new);
+ }
+ if (digest_old >= 0) {
+ vk_old = crypt_volume_key_by_id(vk, digest_old);
+ assert(vk_old);
+ assert(crypt_volume_key_get_id(vk_old) == digest_old);
+ }
+ r = _open_and_activate_reencrypt_device_by_vk(cd, &cd->u.luks2.hdr, name, vk, flags);
+ } else {
+ assert(crypt_volume_key_get_id(vk) == LUKS2_digest_by_segment(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT));
+ r = LUKS2_activate(cd, name, vk, external_key, flags);
+ }
+
+ return r;
+}
#else
static int _open_and_activate_luks2(struct crypt_device *cd,
int keyslot,
@@ -4290,6 +5354,29 @@ static int _open_and_activate_luks2(struct crypt_device *cd,
return _open_and_activate(cd, keyslot, name, passphrase, passphrase_size, flags);
}
+
+static int _activate_luks2_by_volume_key(struct crypt_device *cd,
+ const char *name,
+ struct volume_key *vk,
+ struct volume_key *external_key,
+ uint32_t flags)
+{
+ int r;
+ crypt_reencrypt_info ri;
+ ri = LUKS2_reencrypt_status(&cd->u.luks2.hdr);
+ if (ri == CRYPT_REENCRYPT_INVALID)
+ return -EINVAL;
+
+ if (ri > CRYPT_REENCRYPT_NONE) {
+ log_err(cd, _("This operation is not supported for this device type."));
+ r = -ENOTSUP;
+ } else {
+ assert(crypt_volume_key_get_id(vk) == LUKS2_digest_by_segment(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT));
+ r = LUKS2_activate(cd, name, vk, external_key, flags);
+ }
+
+ return r;
+}
#endif
static int _activate_by_passphrase(struct crypt_device *cd,
@@ -4364,16 +5451,23 @@ out:
static int _activate_loopaes(struct crypt_device *cd,
const char *name,
- char *buffer,
+ const char *buffer,
size_t buffer_size,
uint32_t flags)
{
int r;
unsigned int key_count = 0;
struct volume_key *vk = NULL;
+ char *buffer_copy;
+
+ buffer_copy = crypt_safe_alloc(buffer_size);
+ if (!buffer_copy)
+ return -ENOMEM;
+ memcpy(buffer_copy, buffer, buffer_size);
r = LOOPAES_parse_keyfile(cd, &vk, cd->u.loopaes.hdr.hash, &key_count,
- buffer, buffer_size);
+ buffer_copy, buffer_size);
+ crypt_safe_free(buffer_copy);
if (!r && name)
r = LOOPAES_activate(cd, name, cd->u.loopaes.cipher, key_count,
@@ -4408,66 +5502,352 @@ static int _activate_check_status(struct crypt_device *cd, const char *name, uns
return r;
}
-// activation/deactivation of device mapping
-int crypt_activate_by_passphrase(struct crypt_device *cd,
+static int _verify_key(struct crypt_device *cd,
+ int segment,
+ struct volume_key *vk)
+{
+ int r = -EINVAL;
+ crypt_reencrypt_info ri;
+ struct luks2_hdr *hdr = &cd->u.luks2.hdr;
+
+ assert(cd);
+
+ if (isPLAIN(cd->type)) {
+ if (vk && vk->keylength == cd->u.plain.key_size) {
+ r = KEY_VERIFIED;
+ } else
+ log_err(cd, _("Incorrect volume key specified for plain device."));
+ } else if (isLUKS1(cd->type)) {
+ r = LUKS_verify_volume_key(&cd->u.luks1.hdr, vk);
+ if (r == -EPERM)
+ log_err(cd, _("Volume key does not match the volume."));
+ } else if (isLUKS2(cd->type)) {
+ ri = LUKS2_reencrypt_status(hdr);
+ if (ri == CRYPT_REENCRYPT_INVALID)
+ return -EINVAL;
+
+ if (ri > CRYPT_REENCRYPT_NONE) {
+ LUKS2_reencrypt_lookup_key_ids(cd, hdr, vk);
+ r = LUKS2_reencrypt_digest_verify(cd, hdr, vk);
+ if (r == -EPERM || r == -ENOENT || r == -EINVAL)
+ log_err(cd, _("Reencryption volume keys do not match the volume."));
+ return r;
+ }
+
+ if (segment == CRYPT_ANY_SEGMENT)
+ r = LUKS2_digest_any_matching(cd, &cd->u.luks2.hdr, vk);
+ else {
+ r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, segment, vk);
+ if (r == -EPERM || r == -ENOENT)
+ log_err(cd, _("Volume key does not match the volume."));
+ }
+ } else if (isVERITY(cd->type))
+ r = KEY_VERIFIED;
+ else if (isTCRYPT(cd->type))
+ r = KEY_VERIFIED;
+ else if (isINTEGRITY(cd->type))
+ r = KEY_VERIFIED;
+ else if (isBITLK(cd->type))
+ r = KEY_VERIFIED;
+ else
+ log_err(cd, _("Device type is not properly initialized."));
+
+ if (r >= KEY_VERIFIED)
+ crypt_volume_key_set_id(vk, r);
+
+ return r > 0 ? 0 : r;
+}
+
+/* activation/deactivation of device mapping */
+static int _activate_by_volume_key(struct crypt_device *cd,
const char *name,
- int keyslot,
- const char *passphrase,
- size_t passphrase_size,
+ struct volume_key *vk,
+ struct volume_key *external_key,
uint32_t flags)
{
int r;
- if (!cd || !passphrase || (!name && (flags & CRYPT_ACTIVATE_REFRESH)))
- return -EINVAL;
-
- log_dbg(cd, "%s volume %s [keyslot %d] using passphrase.",
- name ? "Activating" : "Checking", name ?: "passphrase",
- keyslot);
+ assert(cd);
+ assert(name);
- r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH);
+ r = _check_header_data_overlap(cd, name);
if (r < 0)
return r;
- return _activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags);
+ /* use key directly, no hash */
+ if (isPLAIN(cd->type)) {
+ assert(!external_key);
+ assert(crypt_volume_key_get_id(vk) == KEY_VERIFIED);
+
+ r = PLAIN_activate(cd, name, vk, cd->u.plain.hdr.size, flags);
+ } else if (isLUKS1(cd->type)) {
+ assert(!external_key);
+ assert(crypt_volume_key_get_id(vk) == KEY_VERIFIED);
+
+ r = LUKS1_activate(cd, name, vk, flags);
+ } else if (isLUKS2(cd->type)) {
+ r = _activate_luks2_by_volume_key(cd, name, vk, external_key, flags);
+ } else if (isVERITY(cd->type)) {
+ assert(crypt_volume_key_get_id(vk) == KEY_VERIFIED);
+ r = VERITY_activate(cd, name, vk, external_key, cd->u.verity.fec_device,
+ &cd->u.verity.hdr, flags);
+ } else if (isTCRYPT(cd->type)) {
+ assert(!external_key);
+ r = TCRYPT_activate(cd, name, &cd->u.tcrypt.hdr,
+ &cd->u.tcrypt.params, flags);
+ } else if (isINTEGRITY(cd->type)) {
+ assert(!external_key);
+ assert(!vk || crypt_volume_key_get_id(vk) == KEY_VERIFIED);
+ r = INTEGRITY_activate(cd, name, &cd->u.integrity.params, vk,
+ cd->u.integrity.journal_crypt_key,
+ cd->u.integrity.journal_mac_key, flags,
+ cd->u.integrity.sb_flags);
+ } else if (isBITLK(cd->type)) {
+ assert(!external_key);
+ r = BITLK_activate_by_volume_key(cd, name, vk->key, vk->keylength,
+ &cd->u.bitlk.params, flags);
+ } else {
+ log_err(cd, _("Device type is not properly initialized."));
+ r = -EINVAL;
+ }
+
+ return r;
}
-int crypt_activate_by_keyfile_device_offset(struct crypt_device *cd,
- const char *name,
+int crypt_activate_by_keyslot_context(struct crypt_device *cd,
+const char *name,
int keyslot,
- const char *keyfile,
- size_t keyfile_size,
- uint64_t keyfile_offset,
+ struct crypt_keyslot_context *kc,
+ int additional_keyslot,
+ struct crypt_keyslot_context *additional_kc,
uint32_t flags)
{
- char *passphrase_read = NULL;
- size_t passphrase_size_read;
- int r;
+ bool use_keyring;
+ struct volume_key *p_ext_key, *crypt_key = NULL, *opal_key = NULL, *vk = NULL,
+ *vk_sign = NULL, *p_crypt = NULL;
+ size_t passphrase_size;
+ const char *passphrase = NULL;
+ int unlocked_keyslot, required_keys, unlocked_keys = 0, r = -EINVAL;
+ key_serial_t kid1 = 0, kid2 = 0;
+ struct luks2_hdr *hdr = &cd->u.luks2.hdr;
- if (!cd || !keyfile ||
- ((flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_use_keyring_for_vk(cd)))
+ if (!cd || !kc)
return -EINVAL;
- log_dbg(cd, "%s volume %s [keyslot %d] using keyfile %s.",
- name ? "Activating" : "Checking", name ?: "passphrase", keyslot, keyfile);
-
+ log_dbg(cd, "%s volume %s [keyslot %d] using %s.",
+ name ? "Activating" : "Checking", name ?: "passphrase", keyslot, keyslot_context_type_string(kc));
+ if (!name && (flags & CRYPT_ACTIVATE_REFRESH))
+ return -EINVAL;
+ if ((flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_use_keyring_for_vk(cd))
+ return -EINVAL;
+ if ((flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) && name)
+ return -EINVAL;
+ if ((kc->type == CRYPT_KC_TYPE_KEYRING) && !kernel_keyring_support()) {
+ log_err(cd, _("Kernel keyring is not supported by the kernel."));
+ return -EINVAL;
+ }
+ if ((kc->type == CRYPT_KC_TYPE_SIGNED_KEY) && !kernel_keyring_support()) {
+ log_err(cd, _("Kernel keyring missing: required for passing signature to kernel."));
+ return -EINVAL;
+ }
+ r = _check_header_data_overlap(cd, name);
+ if (r < 0)
+ return r;
r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH);
if (r < 0)
return r;
- r = crypt_keyfile_device_read(cd, keyfile,
- &passphrase_read, &passphrase_size_read,
- keyfile_offset, keyfile_size, 0);
+ /* for TCRYPT and token skip passphrase activation */
+ if (kc->get_passphrase && kc->type != CRYPT_KC_TYPE_TOKEN && !isTCRYPT(cd->type)) {
+ r = kc->get_passphrase(cd, kc, &passphrase, &passphrase_size);
+ if (r < 0)
+ return r;
+ /* TODO: Only loopaes should by activated by passphrase method */
+ if (passphrase) {
+ if (isLOOPAES(cd->type))
+ return _activate_loopaes(cd, name, passphrase, passphrase_size, flags);
+ else
+ return _activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags);
+ }
+ }
+ /* only passphrase unlock is supported with loopaes */
+ if (isLOOPAES(cd->type))
+ return -EINVAL;
+
+ /* activate by volume key */
+ r = -EINVAL;
+ if (isLUKS1(cd->type)) {
+ if (kc->get_luks1_volume_key)
+ r = kc->get_luks1_volume_key(cd, kc, keyslot, &vk);
+ } else if (isLUKS2(cd->type)) {
+ required_keys = LUKS2_reencrypt_vks_count(hdr);
+
+ if (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY && kc->get_luks2_key)
+ r = kc->get_luks2_key(cd, kc, keyslot, CRYPT_ANY_SEGMENT, &vk);
+ else if (kc->get_luks2_volume_key)
+ r = kc->get_luks2_volume_key(cd, kc, keyslot, &vk);
+ if (r >= 0) {
+ unlocked_keys++;
+
+ if (required_keys > 1 && vk && additional_kc) {
+ if (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY && additional_kc->get_luks2_key)
+ r = additional_kc->get_luks2_key(cd, additional_kc, additional_keyslot, CRYPT_ANY_SEGMENT, &vk->next);
+ else if (additional_kc->get_luks2_volume_key)
+ r = additional_kc->get_luks2_volume_key(cd, additional_kc, additional_keyslot, &vk->next);
+ if (r >= 0)
+ unlocked_keys++;
+ }
+
+ if (unlocked_keys < required_keys)
+ r = -ESRCH;
+ }
+ } else if (isTCRYPT(cd->type)) {
+ r = 0;
+ } else if (name && isPLAIN(cd->type)) {
+ if (kc->get_plain_volume_key)
+ r = kc->get_plain_volume_key(cd, kc, &vk);
+ } else if (name && isBITLK(cd->type)) {
+ if (kc->get_bitlk_volume_key)
+ r = kc->get_bitlk_volume_key(cd, kc, &vk);
+ } else if (isFVAULT2(cd->type)) {
+ if (kc->get_fvault2_volume_key)
+ r = kc->get_fvault2_volume_key(cd, kc, &vk);
+ } else if (isVERITY(cd->type) && (name || kc->type != CRYPT_KC_TYPE_SIGNED_KEY)) {
+ if (kc->get_verity_volume_key)
+ r = kc->get_verity_volume_key(cd, kc, &vk, &vk_sign);
+ if (r >= 0)
+ r = VERITY_verify_params(cd, &cd->u.verity.hdr, vk_sign != NULL,
+ cd->u.verity.fec_device, vk);
+
+ free(CONST_CAST(void*)cd->u.verity.root_hash);
+ cd->u.verity.root_hash = NULL;
+ flags |= CRYPT_ACTIVATE_READONLY;
+ } else if (isINTEGRITY(cd->type)) {
+ if (kc->get_integrity_volume_key)
+ r = kc->get_integrity_volume_key(cd, kc, &vk);
+ }
+ if (r < 0 && (r != -ENOENT || kc->type == CRYPT_KC_TYPE_TOKEN))
+ goto out;
+ unlocked_keyslot = r;
+
+ if (r == -ENOENT && isLUKS(cd->type) && cd->volume_key) {
+ vk = crypt_alloc_volume_key(cd->volume_key->keylength, cd->volume_key->key);
+ r = vk ? 0 : -ENOMEM;
+ }
+ if (r == -ENOENT && isINTEGRITY(cd->type))
+ r = 0;
+
if (r < 0)
goto out;
- if (isLOOPAES(cd->type))
- r = _activate_loopaes(cd, name, passphrase_read, passphrase_size_read, flags);
- else
- r = _activate_by_passphrase(cd, name, keyslot, passphrase_read, passphrase_size_read, flags);
+ r = _verify_key(cd,
+ flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY ? CRYPT_ANY_SEGMENT : CRYPT_DEFAULT_SEGMENT,
+ vk);
+ if (r < 0)
+ goto out;
+
+ if (isLUKS2(cd->type)) {
+ /* split the key only if we do activation */
+ if (name && LUKS2_segment_is_hw_opal(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT)) {
+ r = LUKS2_split_crypt_and_opal_keys(cd, &cd->u.luks2.hdr,
+ vk, &crypt_key,
+ &opal_key);
+ if (r < 0)
+ goto out;
+
+ /* copy volume key digest id in crypt subkey */
+ crypt_volume_key_set_id(crypt_key, crypt_volume_key_get_id(vk));
+
+ p_crypt = crypt_key;
+ p_ext_key = opal_key ?: vk;
+ } else {
+ p_crypt = vk;
+ p_ext_key = NULL;
+ }
+
+ 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) {
+ /* upload dm-crypt part of volume key in thread keyring if requested */
+ if (p_crypt) {
+ r = LUKS2_volume_key_load_in_keyring_by_digest(cd, p_crypt, crypt_volume_key_get_id(p_crypt));
+ if (r < 0)
+ goto out;
+ flags |= CRYPT_ACTIVATE_KEYRING_KEY;
+ }
+
+ /* upload the volume key in custom user keyring if requested */
+ if (cd->link_vk_to_keyring) {
+ r = crypt_volume_key_load_in_user_keyring(cd, vk, &kid1, &kid2);
+ if (r < 0) {
+ log_err(cd, _("Failed to link volume key in user defined keyring."));
+ goto out;
+ }
+ }
+ }
+ } else {
+ p_crypt = vk;
+ p_ext_key = vk_sign;
+ }
+
+ if (name)
+ r = _activate_by_volume_key(cd, name, p_crypt, p_ext_key, flags);
+ if (r >= 0 && unlocked_keyslot >= 0)
+ r = unlocked_keyslot;
out:
- crypt_safe_free(passphrase_read);
+ if (r < 0) {
+ crypt_drop_keyring_key(cd, vk);
+ crypt_drop_keyring_key(cd, p_crypt);
+ if (cd->link_vk_to_keyring && kid1)
+ crypt_unlink_key_from_custom_keyring(cd, kid1);
+ if (cd->link_vk_to_keyring && kid2)
+ crypt_unlink_key_from_custom_keyring(cd, kid2);
+ }
+
+ crypt_free_volume_key(vk);
+ crypt_free_volume_key(crypt_key);
+ crypt_free_volume_key(opal_key);
+ crypt_free_volume_key(vk_sign);
+ return r;
+}
+
+int crypt_activate_by_passphrase(struct crypt_device *cd,
+ const char *name,
+ int keyslot,
+ const char *passphrase,
+ size_t passphrase_size,
+ uint32_t flags)
+{
+ int r;
+ struct crypt_keyslot_context kc;
+
+ crypt_keyslot_unlock_by_passphrase_init_internal(&kc, passphrase, passphrase_size);
+ r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, CRYPT_ANY_SLOT, NULL, flags);
+ crypt_keyslot_context_destroy_internal(&kc);
+
+ return r;
+}
+
+int crypt_activate_by_keyfile_device_offset(struct crypt_device *cd,
+ const char *name,
+ int keyslot,
+ const char *keyfile,
+ size_t keyfile_size,
+ uint64_t keyfile_offset,
+ uint32_t flags)
+{
+ int r;
+ struct crypt_keyslot_context kc;
+
+ crypt_keyslot_unlock_by_keyfile_init_internal(&kc, keyfile, keyfile_size, keyfile_offset);
+ r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, CRYPT_ANY_SLOT, NULL, flags);
+ crypt_keyslot_context_destroy_internal(&kc);
+
return r;
}
@@ -4493,135 +5873,19 @@ int crypt_activate_by_keyfile_offset(struct crypt_device *cd,
return crypt_activate_by_keyfile_device_offset(cd, name, keyslot, keyfile,
keyfile_size, keyfile_offset, flags);
}
+
int crypt_activate_by_volume_key(struct crypt_device *cd,
const char *name,
const char *volume_key,
size_t volume_key_size,
uint32_t flags)
{
- bool use_keyring;
- struct volume_key *vk = NULL;
int r;
+ struct crypt_keyslot_context kc;
- if (!cd ||
- ((flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_use_keyring_for_vk(cd)))
- return -EINVAL;
-
- log_dbg(cd, "%s volume %s by volume key.", name ? "Activating" : "Checking",
- name ?: "");
-
- r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH);
- if (r < 0)
- return r;
-
- r = _check_header_data_overlap(cd, name);
- if (r < 0)
- return r;
-
- /* use key directly, no hash */
- if (isPLAIN(cd->type)) {
- if (!name)
- return -EINVAL;
-
- if (!volume_key || !volume_key_size || volume_key_size != cd->u.plain.key_size) {
- log_err(cd, _("Incorrect volume key specified for plain device."));
- return -EINVAL;
- }
-
- vk = crypt_alloc_volume_key(volume_key_size, volume_key);
- if (!vk)
- return -ENOMEM;
-
- r = PLAIN_activate(cd, name, vk, cd->u.plain.hdr.size, flags);
- } else if (isLUKS1(cd->type)) {
- /* If key is not provided, try to use internal key */
- if (!volume_key) {
- if (!cd->volume_key) {
- log_err(cd, _("Volume key does not match the volume."));
- return -EINVAL;
- }
- volume_key_size = cd->volume_key->keylength;
- volume_key = cd->volume_key->key;
- }
-
- vk = crypt_alloc_volume_key(volume_key_size, volume_key);
- if (!vk)
- return -ENOMEM;
- r = LUKS_verify_volume_key(&cd->u.luks1.hdr, vk);
-
- if (r == -EPERM)
- log_err(cd, _("Volume key does not match the volume."));
-
- if (!r && name)
- r = LUKS1_activate(cd, name, vk, flags);
- } else if (isLUKS2(cd->type)) {
- /* If key is not provided, try to use internal key */
- if (!volume_key) {
- if (!cd->volume_key) {
- log_err(cd, _("Volume key does not match the volume."));
- return -EINVAL;
- }
- volume_key_size = cd->volume_key->keylength;
- volume_key = cd->volume_key->key;
- }
-
- vk = crypt_alloc_volume_key(volume_key_size, volume_key);
- if (!vk)
- return -ENOMEM;
-
- r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk);
- if (r == -EPERM || r == -ENOENT)
- log_err(cd, _("Volume key does not match the volume."));
- if (r > 0)
- r = 0;
-
- 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)
- r = crypt_volume_key_load_in_keyring(cd, vk);
- if (!r)
- flags |= CRYPT_ACTIVATE_KEYRING_KEY;
- }
-
- if (!r && name)
- r = LUKS2_activate(cd, name, vk, flags);
- } else if (isVERITY(cd->type)) {
- r = crypt_activate_by_signed_key(cd, name, volume_key, volume_key_size, NULL, 0, flags);
- } else if (isTCRYPT(cd->type)) {
- if (!name)
- return 0;
- r = TCRYPT_activate(cd, name, &cd->u.tcrypt.hdr,
- &cd->u.tcrypt.params, flags);
- } else if (isINTEGRITY(cd->type)) {
- if (!name)
- return 0;
- if (volume_key) {
- vk = crypt_alloc_volume_key(volume_key_size, volume_key);
- if (!vk)
- return -ENOMEM;
- }
- r = INTEGRITY_activate(cd, name, &cd->u.integrity.params, vk,
- cd->u.integrity.journal_crypt_key,
- cd->u.integrity.journal_mac_key, flags,
- cd->u.integrity.sb_flags);
- } else if (isBITLK(cd->type)) {
- r = BITLK_activate_by_volume_key(cd, name, volume_key, volume_key_size,
- &cd->u.bitlk.params, flags);
- } else {
- log_err(cd, _("Device type is not properly initialized."));
- r = -EINVAL;
- }
-
- if (r < 0)
- crypt_drop_keyring_key(cd, vk);
- crypt_free_volume_key(vk);
+ crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size);
+ r = crypt_activate_by_keyslot_context(cd, name, CRYPT_ANY_SLOT /* unused */, &kc, CRYPT_ANY_SLOT, NULL, flags);
+ crypt_keyslot_context_destroy_internal(&kc);
return r;
}
@@ -4634,8 +5898,8 @@ int crypt_activate_by_signed_key(struct crypt_device *cd,
size_t signature_size,
uint32_t flags)
{
- char description[512];
int r;
+ struct crypt_keyslot_context kc;
if (!cd || !isVERITY(cd->type))
return -EINVAL;
@@ -4645,57 +5909,13 @@ int crypt_activate_by_signed_key(struct crypt_device *cd,
return -EINVAL;
}
- if (name)
- log_dbg(cd, "Activating volume %s by %skey.", name, signature ? "signed " : "");
- else
- log_dbg(cd, "Checking volume by key.");
-
- if (cd->u.verity.hdr.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE && !signature) {
- log_err(cd, _("Root hash signature required."));
- return -EINVAL;
- }
-
- r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH);
- if (r < 0)
- return r;
-
- if (signature && !kernel_keyring_support()) {
- log_err(cd, _("Kernel keyring missing: required for passing signature to kernel."));
- return -EINVAL;
- }
-
- /* volume_key == root hash */
- free(CONST_CAST(void*)cd->u.verity.root_hash);
- cd->u.verity.root_hash = NULL;
-
- if (signature) {
- r = snprintf(description, sizeof(description)-1, "cryptsetup:%s%s%s",
- crypt_get_uuid(cd) ?: "", crypt_get_uuid(cd) ? "-" : "", name);
- if (r < 0)
- return -EINVAL;
-
- log_dbg(cd, "Adding signature into keyring %s", description);
- r = keyring_add_key_in_thread_keyring(USER_KEY, description, signature, signature_size);
- if (r) {
- log_err(cd, _("Failed to load key in kernel keyring."));
- return r;
- }
- }
-
- r = VERITY_activate(cd, name, volume_key, volume_key_size,
- signature ? description : NULL,
- cd->u.verity.fec_device,
- &cd->u.verity.hdr, flags | CRYPT_ACTIVATE_READONLY);
-
- if (!r) {
- cd->u.verity.root_hash_size = volume_key_size;
- cd->u.verity.root_hash = malloc(volume_key_size);
- if (cd->u.verity.root_hash)
- memcpy(CONST_CAST(void*)cd->u.verity.root_hash, volume_key, volume_key_size);
- }
-
if (signature)
- crypt_drop_keyring_key_by_description(cd, description, USER_KEY);
+ crypt_keyslot_unlock_by_signed_key_init_internal(&kc, volume_key, volume_key_size,
+ signature, signature_size);
+ else
+ crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size);
+ r = crypt_activate_by_keyslot_context(cd, name, -2 /* unused */, &kc, CRYPT_ANY_SLOT, NULL, flags);
+ crypt_keyslot_context_destroy_internal(&kc);
return r;
}
@@ -4723,6 +5943,17 @@ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t
cd = fake_cd;
}
+ if (flags & (CRYPT_DEACTIVATE_DEFERRED | CRYPT_DEACTIVATE_DEFERRED_CANCEL)) {
+ struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
+ if (hdr) {
+ json_object *jobj = json_segments_get_segment(LUKS2_get_segments_jobj(hdr), 0);
+ if (jobj && !strcmp(json_segment_type(jobj), "hw-opal")) {
+ log_err(cd, _("OPAL does not support deferred deactivation."));
+ return -EINVAL;
+ }
+ }
+ }
+
/* skip holders detection and early abort when some flags raised */
if (flags & (CRYPT_DEACTIVATE_FORCE | CRYPT_DEACTIVATE_DEFERRED | CRYPT_DEACTIVATE_DEFERRED_CANCEL))
get_flags &= ~DM_ACTIVE_HOLDERS;
@@ -4986,7 +6217,7 @@ int crypt_volume_key_verify(struct crypt_device *cd,
struct volume_key *vk;
int r;
- if ((r = _onlyLUKS(cd, CRYPT_CD_UNRESTRICTED)))
+ if ((r = onlyLUKSunrestricted(cd)))
return r;
vk = crypt_alloc_volume_key(volume_key_size, volume_key);
@@ -5031,6 +6262,9 @@ int crypt_get_rng_type(struct crypt_device *cd)
int crypt_memory_lock(struct crypt_device *cd, int lock)
{
+ UNUSED(cd);
+ UNUSED(lock);
+
return 0;
}
@@ -5264,6 +6498,9 @@ const char *crypt_get_integrity(struct crypt_device *cd)
if (isLUKS2(cd->type))
return LUKS2_get_integrity(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT);
+ if (!cd->type && *cd->u.none.integrity_spec)
+ return cd->u.none.integrity_spec;
+
return NULL;
}
@@ -5272,10 +6509,7 @@ int crypt_get_integrity_key_size(struct crypt_device *cd)
{
int key_size = 0;
- if (isINTEGRITY(cd->type))
- key_size = INTEGRITY_key_size(crypt_get_integrity(cd));
-
- if (isLUKS2(cd->type))
+ if (isINTEGRITY(cd->type) || isLUKS2(cd->type) || !cd->type)
key_size = INTEGRITY_key_size(crypt_get_integrity(cd));
return key_size > 0 ? key_size : 0;
@@ -5287,7 +6521,7 @@ int crypt_get_integrity_tag_size(struct crypt_device *cd)
if (isINTEGRITY(cd->type))
return cd->u.integrity.params.tag_size;
- if (isLUKS2(cd->type))
+ if (isLUKS2(cd->type) || !cd->type)
return INTEGRITY_tag_size(crypt_get_integrity(cd),
crypt_get_cipher(cd),
crypt_get_cipher_mode(cd));
@@ -5308,6 +6542,9 @@ int crypt_get_sector_size(struct crypt_device *cd)
if (isLUKS2(cd->type))
return LUKS2_get_sector_size(&cd->u.luks2.hdr);
+ if (!cd->type && cd->u.none.sector_size)
+ return cd->u.none.sector_size;
+
return SECTOR_SIZE;
}
@@ -5403,6 +6640,14 @@ int crypt_get_volume_key_size(struct crypt_device *cd)
return 0;
}
+int crypt_get_hw_encryption_key_size(struct crypt_device *cd)
+{
+ if (!cd || !isLUKS2(cd->type))
+ return 0;
+
+ return LUKS2_get_opal_key_size(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT);
+}
+
int crypt_keyslot_get_key_size(struct crypt_device *cd, int keyslot)
{
if (!cd || !isLUKS(cd->type))
@@ -5466,6 +6711,12 @@ const char *crypt_keyslot_get_encryption(struct crypt_device *cd, int keyslot, s
return cd->u.luks2.keyslot_cipher;
}
+ if (LUKS2_segment_is_hw_opal(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT)) {
+ /* Fallback to default LUKS2 keyslot encryption */
+ *key_size = DEFAULT_LUKS2_KEYSLOT_KEYBITS / 8;
+ return DEFAULT_LUKS2_KEYSLOT_CIPHER;
+ }
+
/* Try to reuse volume encryption parameters */
cipher = LUKS2_get_cipher(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT);
if (!LUKS2_keyslot_cipher_incompatible(cd, cipher)) {
@@ -5606,7 +6857,7 @@ uint64_t crypt_get_iv_offset(struct crypt_device *cd)
crypt_keyslot_info crypt_keyslot_status(struct crypt_device *cd, int keyslot)
{
- if (_onlyLUKS(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED) < 0)
+ if (_onlyLUKS(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED, 0) < 0)
return CRYPT_SLOT_INVALID;
if (isLUKS1(cd->type))
@@ -5633,7 +6884,7 @@ int crypt_keyslot_area(struct crypt_device *cd,
uint64_t *offset,
uint64_t *length)
{
- if (_onlyLUKS(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED) || !offset || !length)
+ if (_onlyLUKS(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED, 0) || !offset || !length)
return -EINVAL;
if (isLUKS2(cd->type))
@@ -5644,7 +6895,7 @@ int crypt_keyslot_area(struct crypt_device *cd,
crypt_keyslot_priority crypt_keyslot_get_priority(struct crypt_device *cd, int keyslot)
{
- if (_onlyLUKS(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED))
+ if (_onlyLUKS(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED, 0))
return CRYPT_SLOT_PRIORITY_INVALID;
if (keyslot < 0 || keyslot >= crypt_keyslot_max(cd->type))
@@ -5684,6 +6935,21 @@ const char *crypt_get_default_type(void)
return DEFAULT_LUKS_FORMAT;
}
+int crypt_get_hw_encryption_type(struct crypt_device *cd)
+{
+ if (!cd)
+ return -EINVAL;
+
+ if (isLUKS2(cd->type)) {
+ if (LUKS2_segment_is_hw_opal_crypt(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT))
+ return CRYPT_SW_AND_OPAL_HW;
+ else if (LUKS2_segment_is_hw_opal_only(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT))
+ return CRYPT_OPAL_HW_ONLY;
+ }
+
+ return CRYPT_SW_ONLY;
+}
+
int crypt_get_verity_info(struct crypt_device *cd,
struct crypt_params_verity *vp)
{
@@ -5753,6 +7019,11 @@ int crypt_get_integrity_info(struct crypt_device *cd,
ip->journal_crypt_key_size = 0;
ip->journal_crypt_key = NULL;
return 0;
+ } else if (!cd->type) {
+ memset(ip, 0, sizeof(*ip));
+ ip->integrity = crypt_get_integrity(cd);
+ ip->integrity_key_size = crypt_get_integrity_key_size(cd);
+ ip->tag_size = crypt_get_integrity_tag_size(cd);
}
return -ENOTSUP;
@@ -5771,7 +7042,7 @@ int crypt_convert(struct crypt_device *cd,
log_dbg(cd, "Converting LUKS device to type %s", type);
- if ((r = onlyLUKS(cd)))
+ if ((r = onlyLUKSnoRequirements(cd)))
return r;
if (isLUKS1(cd->type) && isLUKS2(type))
@@ -5797,6 +7068,10 @@ int crypt_convert(struct crypt_device *cd,
/* Internal access function to header pointer */
void *crypt_get_hdr(struct crypt_device *cd, const char *type)
{
+ /* One type can be OPAL */
+ if (isLUKS2(type) && isLUKS2(cd->type))
+ return &cd->u.luks2.hdr;
+
/* If requested type differs, ignore it */
if (strcmp(cd->type, type))
return NULL;
@@ -5807,9 +7082,6 @@ void *crypt_get_hdr(struct crypt_device *cd, const char *type)
if (isLUKS1(cd->type))
return &cd->u.luks1.hdr;
- if (isLUKS2(cd->type))
- return &cd->u.luks2.hdr;
-
if (isLOOPAES(cd->type))
return &cd->u.loopaes;
@@ -5842,26 +7114,13 @@ int crypt_activate_by_token_pin(struct crypt_device *cd, const char *name,
void *usrptr, uint32_t flags)
{
int r;
+ struct crypt_keyslot_context kc;
- log_dbg(cd, "%s volume %s using token (%s type) %d.",
- name ? "Activating" : "Checking", name ?: "passphrase",
- type ?: "any", token);
-
- if ((r = _onlyLUKS2(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED, 0)))
- return r;
-
- if ((flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_use_keyring_for_vk(cd))
- return -EINVAL;
-
- if ((flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) && name)
- return -EINVAL;
-
- r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH);
- if (r < 0)
- return r;
+ crypt_keyslot_unlock_by_token_init_internal(&kc, token, type, pin, pin_size, usrptr);
+ r = crypt_activate_by_keyslot_context(cd, name, CRYPT_ANY_SLOT, &kc, CRYPT_ANY_SLOT, NULL, flags);
+ crypt_keyslot_context_destroy_internal(&kc);
- return LUKS2_token_open_and_activate(cd, &cd->u.luks2.hdr, token, name, type,
- pin, pin_size, flags, usrptr);
+ return r;
}
int crypt_activate_by_token(struct crypt_device *cd,
@@ -5879,7 +7138,7 @@ int crypt_token_json_get(struct crypt_device *cd, int token, const char **json)
log_dbg(cd, "Requesting JSON for token %d.", token);
- if ((r = _onlyLUKS2(cd, CRYPT_CD_UNRESTRICTED, 0)))
+ if ((r = onlyLUKS2unrestricted(cd)))
return r;
return LUKS2_token_json_get(&cd->u.luks2.hdr, token, json) ?: token;
@@ -5926,7 +7185,7 @@ int crypt_token_luks2_keyring_get(struct crypt_device *cd,
log_dbg(cd, "Requesting LUKS2 keyring token %d.", token);
- if ((r = _onlyLUKS2(cd, CRYPT_CD_UNRESTRICTED, 0)))
+ if ((r = onlyLUKS2unrestricted(cd)))
return r;
token_info = LUKS2_token_status(cd, &cd->u.luks2.hdr, token, &type);
@@ -6041,7 +7300,7 @@ int crypt_persistent_flags_get(struct crypt_device *cd, crypt_flags_type type, u
if (!flags)
return -EINVAL;
- if ((r = _onlyLUKS2(cd, CRYPT_CD_UNRESTRICTED, 0)))
+ if ((r = onlyLUKS2unrestricted(cd)))
return r;
if (type == CRYPT_FLAGS_ACTIVATION)
@@ -6404,10 +7663,9 @@ int crypt_volume_key_keyring(struct crypt_device *cd __attribute__((unused)), in
/* internal only */
int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key *vk)
{
- int r;
- const char *type_name = key_type_name(LOGON_KEY);
+ key_serial_t kid;
- if (!vk || !cd || !type_name)
+ if (!vk || !cd)
return -EINVAL;
if (!vk->key_description) {
@@ -6415,15 +7673,83 @@ int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key
return -EINVAL;
}
- log_dbg(cd, "Loading key (%zu bytes, type %s) in thread keyring.", vk->keylength, type_name);
+ log_dbg(cd, "Loading key (type logon, name %s) in thread keyring.", vk->key_description);
- r = keyring_add_key_in_thread_keyring(LOGON_KEY, vk->key_description, vk->key, vk->keylength);
- if (r) {
- log_dbg(cd, "keyring_add_key_in_thread_keyring failed (error %d)", r);
+ kid = keyring_add_key_in_thread_keyring(LOGON_KEY, vk->key_description, vk->key, vk->keylength);
+ if (kid < 0) {
+ log_dbg(cd, "keyring_add_key_in_thread_keyring failed (error %d)", errno);
log_err(cd, _("Failed to load key in kernel keyring."));
} else
crypt_set_key_in_keyring(cd, 1);
+ return kid < 0 ? -EINVAL : 0;
+}
+
+/* internal only */
+int crypt_keyring_get_user_key(struct crypt_device *cd,
+ const char *key_description,
+ char **key,
+ size_t *key_size)
+{
+ int r;
+ key_serial_t kid;
+
+ if (!key_description || !key || !key_size)
+ return -EINVAL;
+
+ log_dbg(cd, "Requesting key %s (user type)", key_description);
+
+ kid = keyring_request_key_id(USER_KEY, key_description);
+ if (kid == -ENOTSUP) {
+ log_dbg(cd, "Kernel keyring features disabled.");
+ return -ENOTSUP;
+ } else if (kid < 0) {
+ log_dbg(cd, "keyring_request_key_id failed with errno %d.", errno);
+ return -EINVAL;
+ }
+
+ log_dbg(cd, "Reading content of kernel key (id %" PRIi32 ").", kid);
+
+ r = keyring_read_key(kid, key, key_size);
+ if (r < 0)
+ log_dbg(cd, "keyring_read_key failed with errno %d.", errno);
+
+ return r;
+}
+
+/* internal only */
+int crypt_keyring_get_key_by_name(struct crypt_device *cd,
+ const char *key_description,
+ char **key,
+ size_t *key_size)
+{
+ int r;
+ key_serial_t kid;
+
+ if (!key_description || !key || !key_size)
+ return -EINVAL;
+
+ log_dbg(cd, "Searching for key by name %s.", key_description);
+
+ kid = keyring_find_key_id_by_name(key_description);
+ if (kid == -ENOTSUP) {
+ log_dbg(cd, "Kernel keyring features disabled.");
+ return -ENOTSUP;
+ } else if (kid < 0) {
+ log_dbg(cd, "keyring_find_key_id_by_name failed with errno %d.", errno);
+ return -EINVAL;
+ }
+ else if (kid == 0) {
+ log_dbg(cd, "keyring_find_key_id_by_name failed with errno %d.", ENOENT);
+ return -ENOENT;
+ }
+
+ log_dbg(cd, "Reading content of kernel key (id %" PRIi32 ").", kid);
+
+ r = keyring_read_key(kid, key, key_size);
+ if (r < 0)
+ log_dbg(cd, "keyring_read_key failed with errno %d.", errno);
+
return r;
}
@@ -6445,18 +7771,96 @@ void crypt_set_key_in_keyring(struct crypt_device *cd, unsigned key_in_keyring)
/* internal only */
void crypt_drop_keyring_key_by_description(struct crypt_device *cd, const char *key_description, key_type_t ktype)
{
- int r;
+ key_serial_t kid;
const char *type_name = key_type_name(ktype);
if (!key_description || !type_name)
return;
- log_dbg(cd, "Requesting keyring %s key for revoke and unlink.", type_name);
+ log_dbg(cd, "Requesting kernel key %s (type %s) for unlink from thread keyring.", key_description, type_name);
- r = keyring_revoke_and_unlink_key(ktype, key_description);
- if (r)
- log_dbg(cd, "keyring_revoke_and_unlink_key failed (error %d)", r);
crypt_set_key_in_keyring(cd, 0);
+
+ kid = keyring_request_key_id(ktype, key_description);
+ if (kid == -ENOTSUP) {
+ log_dbg(cd, "Kernel keyring features disabled.");
+ return;
+ } else if (kid < 0) {
+ log_dbg(cd, "keyring_request_key_id failed with errno %d.", errno);
+ return;
+ }
+
+ log_dbg(cd, "Unlinking volume key (id: %" PRIi32 ") from thread keyring.", kid);
+
+ if (!keyring_unlink_key_from_thread_keyring(kid))
+ return;
+
+ log_dbg(cd, "keyring_unlink_key_from_thread_keyring failed with errno %d.", errno);
+ log_err(cd, _("Failed to unlink volume key from thread keyring."));
+
+}
+
+int crypt_set_keyring_to_link(struct crypt_device *cd, const char *key_description,
+ const char *old_key_description,
+ const char *key_type_desc, const char *keyring_to_link_vk)
+{
+ key_type_t key_type = USER_KEY;
+ const char *name1 = NULL, *name2 = NULL;
+ int32_t id = 0;
+ int r, ri;
+ struct luks2_hdr *hdr;
+ unsigned user_descriptions_count, vks_count = 1;
+
+ if (!cd || ((!key_description && !old_key_description) && (keyring_to_link_vk || key_type_desc)) ||
+ ((key_description || old_key_description) && !keyring_to_link_vk))
+ return -EINVAL;
+
+ hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
+
+ /* if only one key description is supplied, force it to be the first one */
+ if (!key_description && old_key_description)
+ return -EINVAL;
+
+ if ((r = _onlyLUKS2(cd, 0, CRYPT_REQUIREMENT_OPAL | CRYPT_REQUIREMENT_ONLINE_REENCRYPT)))
+ return r;
+
+ if (key_type_desc)
+ key_type = key_type_by_name(key_type_desc);
+ if (key_type != LOGON_KEY && key_type != USER_KEY)
+ return -EINVAL;
+
+ ri = crypt_reencrypt_status(cd, NULL);
+ if (ri > CRYPT_REENCRYPT_NONE && ri < CRYPT_REENCRYPT_INVALID)
+ vks_count = LUKS2_reencrypt_vks_count(hdr);
+
+ user_descriptions_count = (key_description ? 1 : 0) + (old_key_description ? 1 : 0);
+ if (user_descriptions_count != 0 && vks_count > user_descriptions_count)
+ return -ESRCH;
+
+ if (keyring_to_link_vk) {
+ id = keyring_find_keyring_id_by_name(keyring_to_link_vk);
+ if (id == 0) {
+ log_err(cd, _("Could not find keyring described by \"%s\"."), keyring_to_link_vk);
+ return -EINVAL;
+ }
+ if (key_description && !(name1 = strdup(key_description)))
+ return -ENOMEM;
+ if (old_key_description && !(name2 = strdup(old_key_description))) {
+ free(CONST_CAST(void*)name1);
+ return -ENOMEM;
+ }
+ }
+
+ cd->keyring_key_type = key_type;
+
+ free(CONST_CAST(void*)cd->user_key_name1);
+ free(CONST_CAST(void*)cd->user_key_name2);
+ cd->user_key_name1 = name1;
+ cd->user_key_name2 = name2;
+ cd->keyring_to_link_vk = id;
+ cd->link_vk_to_keyring = id != 0;
+
+ return 0;
}
/* internal only */
@@ -6476,34 +7880,15 @@ int crypt_activate_by_keyring(struct crypt_device *cd,
int keyslot,
uint32_t flags)
{
- char *passphrase;
- size_t passphrase_size;
int r;
+ struct crypt_keyslot_context kc;
if (!cd || !key_description)
return -EINVAL;
- log_dbg(cd, "%s volume %s [keyslot %d] using passphrase in keyring.",
- name ? "Activating" : "Checking", name ?: "passphrase", keyslot);
-
- if (!kernel_keyring_support()) {
- log_err(cd, _("Kernel keyring is not supported by the kernel."));
- return -EINVAL;
- }
-
- r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH);
- if (r < 0)
- return r;
-
- r = keyring_get_passphrase(key_description, &passphrase, &passphrase_size);
- if (r < 0) {
- log_err(cd, _("Failed to read passphrase from keyring (error %d)."), r);
- return -EINVAL;
- }
-
- r = _activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags);
-
- crypt_safe_free(passphrase);
+ crypt_keyslot_unlock_by_keyring_internal(&kc, key_description);
+ r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, CRYPT_ANY_SLOT, NULL, flags);
+ crypt_keyslot_context_destroy_internal(&kc);
return r;
}
diff --git a/lib/tcrypt/tcrypt.c b/lib/tcrypt/tcrypt.c
index 60e4966..9ae7aaa 100644
--- a/lib/tcrypt/tcrypt.c
+++ b/lib/tcrypt/tcrypt.c
@@ -1,8 +1,8 @@
/*
* TCRYPT (TrueCrypt-compatible) and VeraCrypt volume handling
*
- * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2012-2023 Milan Broz
+ * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -47,6 +47,8 @@ static const struct {
{ 0, 1, "pbkdf2", "whirlpool", 500000, 15000, 1000 },
{ 0, 1, "pbkdf2", "sha256", 500000, 15000, 1000 }, // VeraCrypt 1.0f
{ 0, 1, "pbkdf2", "sha256", 200000, 0, 2048 }, // boot only
+ { 0, 1, "pbkdf2", "blake2s-256", 500000, 15000, 1000 }, // VeraCrypt 1.26.2
+ { 0, 1, "pbkdf2", "blake2s-256", 200000, 0, 2048 }, // boot only
{ 0, 1, "pbkdf2", "ripemd160", 655331, 15000, 1000 },
{ 0, 1, "pbkdf2", "ripemd160", 327661, 0, 2048 }, // boot only
{ 0, 1, "pbkdf2", "stribog512",500000, 15000, 1000 },
@@ -572,7 +574,7 @@ static int TCRYPT_init_hdr(struct crypt_device *cd,
pwd[i] += params->passphrase[i];
for (i = 0; tcrypt_kdf[i].name; i++) {
- if (params->hash_name && strcmp(params->hash_name, tcrypt_kdf[i].hash))
+ if (params->hash_name && !strstr(tcrypt_kdf[i].hash, params->hash_name))
continue;
if (!(params->flags & CRYPT_TCRYPT_LEGACY_MODES) && tcrypt_kdf[i].legacy)
continue;
diff --git a/lib/tcrypt/tcrypt.h b/lib/tcrypt/tcrypt.h
index b95d74d..1e8765a 100644
--- a/lib/tcrypt/tcrypt.h
+++ b/lib/tcrypt/tcrypt.h
@@ -1,8 +1,8 @@
/*
* TCRYPT (TrueCrypt-compatible) header definition
*
- * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2012-2023 Milan Broz
+ * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012-2024 Milan Broz
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/utils.c b/lib/utils.c
index bfcf60d..cf86816 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -3,8 +3,8 @@
*
* Copyright (C) 2004 Jana Saout <jana@saout.de>
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -45,20 +45,77 @@ unsigned crypt_cpusonline(void)
uint64_t crypt_getphysmemory_kb(void)
{
long pagesize, phys_pages;
- uint64_t phys_memory_kb;
+ uint64_t phys_memory_kb, page_size_kb;
pagesize = sysconf(_SC_PAGESIZE);
phys_pages = sysconf(_SC_PHYS_PAGES);
- if (pagesize < 0 || phys_pages < 0)
+ if (pagesize <= 0 || phys_pages <= 0)
return 0;
- phys_memory_kb = pagesize / 1024;
- phys_memory_kb *= phys_pages;
+ page_size_kb = pagesize / 1024;
+ phys_memory_kb = page_size_kb * phys_pages;
+ /* sanity check for overflow */
+ if (phys_memory_kb / phys_pages != page_size_kb)
+ return 0;
+
+ /* coverity[return_overflow:FALSE] */
return phys_memory_kb;
}
+uint64_t crypt_getphysmemoryfree_kb(void)
+{
+ long pagesize, phys_pages;
+ uint64_t phys_memoryfree_kb, page_size_kb;
+
+ pagesize = sysconf(_SC_PAGESIZE);
+ phys_pages = sysconf(_SC_AVPHYS_PAGES);
+
+ if (pagesize <= 0 || phys_pages <= 0)
+ return 0;
+
+ page_size_kb = pagesize / 1024;
+ phys_memoryfree_kb = page_size_kb * phys_pages;
+
+ /* sanity check for overflow */
+ if (phys_memoryfree_kb / phys_pages != page_size_kb)
+ return 0;
+
+ /* coverity[return_overflow:FALSE] */
+ return phys_memoryfree_kb;
+}
+
+bool crypt_swapavailable(void)
+{
+ int fd;
+ ssize_t size;
+ char buf[4096], *p;
+ uint64_t total;
+
+ if ((fd = open("/proc/meminfo", O_RDONLY)) < 0)
+ return true;
+
+ size = read(fd, buf, sizeof(buf));
+ close(fd);
+ if (size < 1)
+ return true;
+
+ if (size < (ssize_t)sizeof(buf))
+ buf[size] = 0;
+ else
+ buf[sizeof(buf) - 1] = 0;
+
+ p = strstr(buf, "SwapTotal:");
+ if (!p)
+ return true;
+
+ if (sscanf(p, "SwapTotal: %" PRIu64 " kB", &total) != 1)
+ return true;
+
+ return total > 0;
+}
+
void crypt_process_priority(struct crypt_device *cd, int *priority, bool raise)
{
int _priority, new_priority;
diff --git a/lib/utils_benchmark.c b/lib/utils_benchmark.c
index 728e4df..6f2077c 100644
--- a/lib/utils_benchmark.c
+++ b/lib/utils_benchmark.c
@@ -1,8 +1,8 @@
/*
* libcryptsetup - cryptsetup library, cipher benchmark
*
- * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2012-2023 Milan Broz
+ * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -101,6 +101,7 @@ int crypt_benchmark_pbkdf(struct crypt_device *cd,
{
int r, priority;
const char *kdf_opt;
+ uint32_t memory_kb;
if (!pbkdf || (!password && password_size))
return -EINVAL;
@@ -113,6 +114,14 @@ int crypt_benchmark_pbkdf(struct crypt_device *cd,
log_dbg(cd, "Running %s(%s) benchmark.", pbkdf->type, kdf_opt);
+ memory_kb = pbkdf_adjusted_phys_memory_kb();
+ if (memory_kb < pbkdf->max_memory_kb) {
+ log_dbg(cd, "Not enough physical memory detected, "
+ "PBKDF max memory decreased from %dkB to %dkB.",
+ pbkdf->max_memory_kb, memory_kb);
+ pbkdf->max_memory_kb = memory_kb;
+ }
+
crypt_process_priority(cd, &priority, true);
r = crypt_pbkdf_perf(pbkdf->type, pbkdf->hash, password, password_size,
salt, salt_size, volume_key_size, pbkdf->time_ms,
diff --git a/lib/utils_blkid.c b/lib/utils_blkid.c
index 5a848a1..230dcab 100644
--- a/lib/utils_blkid.c
+++ b/lib/utils_blkid.c
@@ -1,7 +1,7 @@
/*
* blkid probe utilities
*
- * Copyright (C) 2018-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2018-2024 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -29,6 +29,7 @@
#include "utils_io.h"
#ifdef HAVE_BLKID
+
#include <blkid/blkid.h>
/* make bad checksums flag optional */
#ifndef BLKID_SUBLKS_BADCSUM
@@ -45,11 +46,9 @@ static size_t crypt_getpagesize(void)
return r <= 0 ? 4096 : (size_t)r;
}
#endif
-#endif
void blk_set_chains_for_wipes(struct blkid_handle *h)
{
-#ifdef HAVE_BLKID
blkid_probe_enable_partitions(h->pr, 1);
blkid_probe_set_partitions_flags(h->pr, 0
#ifdef HAVE_BLKID_WIPE
@@ -65,7 +64,6 @@ void blk_set_chains_for_wipes(struct blkid_handle *h)
BLKID_SUBLKS_VERSION |
BLKID_SUBLKS_MAGIC |
BLKID_SUBLKS_BADCSUM);
-#endif
}
void blk_set_chains_for_full_print(struct blkid_handle *h)
@@ -75,25 +73,19 @@ void blk_set_chains_for_full_print(struct blkid_handle *h)
void blk_set_chains_for_superblocks(struct blkid_handle *h)
{
-#ifdef HAVE_BLKID
blkid_probe_enable_superblocks(h->pr, 1);
blkid_probe_set_superblocks_flags(h->pr, BLKID_SUBLKS_TYPE);
-#endif
}
void blk_set_chains_for_fast_detection(struct blkid_handle *h)
{
-#ifdef HAVE_BLKID
blkid_probe_enable_partitions(h->pr, 1);
blkid_probe_set_partitions_flags(h->pr, 0);
blk_set_chains_for_superblocks(h);
-#endif
}
int blk_init_by_path(struct blkid_handle **h, const char *path)
{
- int r = -ENOTSUP;
-#ifdef HAVE_BLKID
struct blkid_handle *tmp = malloc(sizeof(*tmp));
if (!tmp)
return -ENOMEM;
@@ -107,16 +99,11 @@ int blk_init_by_path(struct blkid_handle **h, const char *path)
}
*h = tmp;
-
- r = 0;
-#endif
- return r;
+ return 0;
}
int blk_init_by_fd(struct blkid_handle **h, int fd)
{
- int r = -ENOTSUP;
-#ifdef HAVE_BLKID
struct blkid_handle *tmp = malloc(sizeof(*tmp));
if (!tmp)
return -ENOMEM;
@@ -136,13 +123,9 @@ int blk_init_by_fd(struct blkid_handle **h, int fd)
tmp->fd = fd;
*h = tmp;
-
- r = 0;
-#endif
- return r;
+ return 0;
}
-#ifdef HAVE_BLKID
static int blk_superblocks_luks(struct blkid_handle *h, bool enable)
{
char luks[] = "crypto_LUKS";
@@ -154,47 +137,34 @@ static int blk_superblocks_luks(struct blkid_handle *h, bool enable)
enable ? BLKID_FLTR_ONLYIN : BLKID_FLTR_NOTIN,
luks_filter);
}
-#endif
int blk_superblocks_filter_luks(struct blkid_handle *h)
{
- int r = -ENOTSUP;
-#ifdef HAVE_BLKID
- r = blk_superblocks_luks(h, false);
-#endif
- return r;
+ return blk_superblocks_luks(h, false);
}
int blk_superblocks_only_luks(struct blkid_handle *h)
{
- int r = -ENOTSUP;
-#ifdef HAVE_BLKID
- r = blk_superblocks_luks(h, true);
-#endif
- return r;
+ return blk_superblocks_luks(h, true);
}
blk_probe_status blk_probe(struct blkid_handle *h)
{
blk_probe_status pr = PRB_FAIL;
-#ifdef HAVE_BLKID
+
int r = blkid_do_probe(h->pr);
if (r == 0)
pr = PRB_OK;
else if (r == 1)
pr = PRB_EMPTY;
-#endif
+
return pr;
}
blk_probe_status blk_safeprobe(struct blkid_handle *h)
{
- int r = -1;
-#ifdef HAVE_BLKID
- r = blkid_do_safeprobe(h->pr);
-#endif
- switch (r) {
+ switch (blkid_do_safeprobe(h->pr)) {
case -2:
return PRB_AMBIGUOUS;
case 1:
@@ -208,43 +178,30 @@ blk_probe_status blk_safeprobe(struct blkid_handle *h)
int blk_is_partition(struct blkid_handle *h)
{
- int r = 0;
-#ifdef HAVE_BLKID
- r = blkid_probe_has_value(h->pr, "PTTYPE");
-#endif
- return r;
+ return blkid_probe_has_value(h->pr, "PTTYPE");
}
int blk_is_superblock(struct blkid_handle *h)
{
- int r = 0;
-#ifdef HAVE_BLKID
- r = blkid_probe_has_value(h->pr, "TYPE");
-#endif
- return r;
+ return blkid_probe_has_value(h->pr, "TYPE");;
}
const char *blk_get_partition_type(struct blkid_handle *h)
{
const char *value = NULL;
-#ifdef HAVE_BLKID
(void) blkid_probe_lookup_value(h->pr, "PTTYPE", &value, NULL);
-#endif
return value;
}
const char *blk_get_superblock_type(struct blkid_handle *h)
{
const char *value = NULL;
-#ifdef HAVE_BLKID
(void) blkid_probe_lookup_value(h->pr, "TYPE", &value, NULL);
-#endif
return value;
}
void blk_free(struct blkid_handle *h)
{
-#ifdef HAVE_BLKID
if (!h)
return;
@@ -252,10 +209,8 @@ void blk_free(struct blkid_handle *h)
blkid_free_probe(h->pr);
free(h);
-#endif
}
-#ifdef HAVE_BLKID
#ifndef HAVE_BLKID_WIPE
static int blk_step_back(struct blkid_handle *h)
{
@@ -268,11 +223,9 @@ static int blk_step_back(struct blkid_handle *h)
#endif
}
#endif /* not HAVE_BLKID_WIPE */
-#endif /* HAVE_BLKID */
int blk_do_wipe(struct blkid_handle *h)
{
-#ifdef HAVE_BLKID
#ifdef HAVE_BLKID_WIPE
return blkid_do_wipe(h->pr, 0);
#else
@@ -319,29 +272,110 @@ int blk_do_wipe(struct blkid_handle *h)
return -EIO;
#endif
-#else /* HAVE_BLKID */
- return -ENOTSUP;
-#endif
}
int blk_supported(void)
{
- int r = 0;
-#ifdef HAVE_BLKID
- r = 1;
-#endif
- return r;
+ return 1;
}
unsigned blk_get_block_size(struct blkid_handle *h)
{
unsigned block_size = 0;
-#ifdef HAVE_BLKID
const char *data;
if (!blk_is_superblock(h) || !blkid_probe_has_value(h->pr, "BLOCK_SIZE") ||
blkid_probe_lookup_value(h->pr, "BLOCK_SIZE", &data, NULL) ||
sscanf(data, "%u", &block_size) != 1)
block_size = 0;
-#endif
+
return block_size;
}
+
+#else /* HAVE_BLKID */
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+void blk_set_chains_for_wipes(struct blkid_handle *h)
+{
+}
+
+void blk_set_chains_for_full_print(struct blkid_handle *h)
+{
+}
+
+void blk_set_chains_for_superblocks(struct blkid_handle *h)
+{
+}
+
+void blk_set_chains_for_fast_detection(struct blkid_handle *h)
+{
+}
+
+int blk_init_by_path(struct blkid_handle **h, const char *path)
+{
+ return -ENOTSUP;
+}
+
+int blk_init_by_fd(struct blkid_handle **h, int fd)
+{
+ return -ENOTSUP;
+}
+
+int blk_superblocks_filter_luks(struct blkid_handle *h)
+{
+ return -ENOTSUP;
+}
+
+int blk_superblocks_only_luks(struct blkid_handle *h)
+{
+ return -ENOTSUP;
+}
+
+blk_probe_status blk_probe(struct blkid_handle *h)
+{
+ return PRB_FAIL;
+}
+
+blk_probe_status blk_safeprobe(struct blkid_handle *h)
+{
+ return PRB_FAIL;
+}
+
+int blk_is_partition(struct blkid_handle *h)
+{
+ return 0;
+}
+
+int blk_is_superblock(struct blkid_handle *h)
+{
+ return 0;
+}
+
+const char *blk_get_partition_type(struct blkid_handle *h)
+{
+ return NULL;
+}
+
+const char *blk_get_superblock_type(struct blkid_handle *h)
+{
+ return NULL;
+}
+
+void blk_free(struct blkid_handle *h)
+{
+}
+
+int blk_do_wipe(struct blkid_handle *h)
+{
+ return -ENOTSUP;
+}
+
+int blk_supported(void)
+{
+ return 0;
+}
+
+unsigned blk_get_block_size(struct blkid_handle *h)
+{
+ return 0;
+}
+#endif
diff --git a/lib/utils_blkid.h b/lib/utils_blkid.h
index 3ee1434..7e005f0 100644
--- a/lib/utils_blkid.h
+++ b/lib/utils_blkid.h
@@ -1,7 +1,7 @@
/*
* blkid probe utilities
*
- * Copyright (C) 2018-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2018-2024 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/utils_crypt.c b/lib/utils_crypt.c
index 0b7dc37..1e97610 100644
--- a/lib/utils_crypt.c
+++ b/lib/utils_crypt.c
@@ -2,8 +2,8 @@
* utils_crypt - cipher utilities for cryptsetup
*
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -43,7 +43,13 @@ int crypt_parse_name_and_mode(const char *s, char *cipher, int *key_nums,
cipher, cipher_mode) == 2) {
if (!strcmp(cipher_mode, "plain"))
strcpy(cipher_mode, "cbc-plain");
- if (key_nums) {
+ if (!strncmp(cipher, "capi:", 5)) {
+ /* CAPI must not use internal cipher driver names with dash */
+ if (strchr(cipher_mode, ')'))
+ return -EINVAL;
+ if (key_nums)
+ *key_nums = 1;
+ } else if (key_nums) {
char *tmp = strchr(cipher, ':');
*key_nums = tmp ? atoi(++tmp) : 1;
if (!*key_nums)
@@ -300,6 +306,15 @@ int crypt_capi_to_cipher(char **org_c, char **org_i, const char *c_dm, const cha
if (i != 2)
return -EINVAL;
+ /* non-cryptsetup compatible mode (generic driver with dash?) */
+ if (strrchr(iv, ')')) {
+ if (i_dm)
+ return -EINVAL;
+ if (!(*org_c = strdup(c_dm)))
+ return -ENOMEM;
+ return 0;
+ }
+
len = strlen(tmp);
if (len < 2)
return -EINVAL;
diff --git a/lib/utils_crypt.h b/lib/utils_crypt.h
index 92e0705..0a4b5d6 100644
--- a/lib/utils_crypt.h
+++ b/lib/utils_crypt.h
@@ -2,8 +2,8 @@
* utils_crypt - cipher utilities for cryptsetup
*
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -30,9 +30,12 @@ struct crypt_device;
#define MAX_CIPHER_LEN 32
#define MAX_CIPHER_LEN_STR "31"
#define MAX_KEYFILES 32
+#define MAX_KEYRING_LINKS 2
+#define MAX_VK_IN_KEYRING 2
#define MAX_CAPI_ONE_LEN 2 * MAX_CIPHER_LEN
#define MAX_CAPI_ONE_LEN_STR "63" /* for sscanf length + '\0' */
#define MAX_CAPI_LEN 144 /* should be enough to fit whole capi string */
+#define MAX_INTEGRITY_LEN 64
int crypt_parse_name_and_mode(const char *s, char *cipher,
int *key_nums, char *cipher_mode);
diff --git a/lib/utils_device.c b/lib/utils_device.c
index d80ea62..8bc329d 100644
--- a/lib/utils_device.c
+++ b/lib/utils_device.c
@@ -3,8 +3,8 @@
*
* Copyright (C) 2004 Jana Saout <jana@saout.de>
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -178,6 +178,7 @@ static int device_ready(struct crypt_device *cd, struct device *device)
int devfd = -1, r = 0;
struct stat st;
size_t tmp_size;
+ const char *dm_name;
if (!device)
return -EINVAL;
@@ -188,7 +189,12 @@ static int device_ready(struct crypt_device *cd, struct device *device)
device->o_direct = 0;
devfd = open(device_path(device), O_RDONLY | O_DIRECT);
if (devfd >= 0) {
- if (device_read_test(devfd) == 0) {
+ /* skip check for suspended DM devices */
+ dm_name = device_dm_name(device);
+ if (dm_name && dm_status_suspended(cd, dm_name)) {
+ close(devfd);
+ devfd = -1;
+ } else if (device_read_test(devfd) == 0) {
device->o_direct = 1;
} else {
close(devfd);
@@ -470,7 +476,7 @@ void device_free(struct crypt_device *cd, struct device *device)
/* Get block device path */
const char *device_block_path(const struct device *device)
{
- if (!device || !device->init_done)
+ if (!device)
return NULL;
return device->path;
@@ -482,7 +488,7 @@ const char *device_dm_name(const struct device *device)
const char *dmdir = dm_get_dir();
size_t dmdir_len = strlen(dmdir);
- if (!device || !device->init_done)
+ if (!device)
return NULL;
if (strncmp(device->path, dmdir, dmdir_len))
@@ -985,6 +991,22 @@ int device_is_rotational(struct device *device)
return crypt_dev_is_rotational(major(st.st_rdev), minor(st.st_rdev));
}
+int device_is_dax(struct device *device)
+{
+ struct stat st;
+
+ if (!device)
+ return -EINVAL;
+
+ if (stat(device_path(device), &st) < 0)
+ return -EINVAL;
+
+ if (!S_ISBLK(st.st_mode))
+ return 0;
+
+ return crypt_dev_is_dax(major(st.st_rdev), minor(st.st_rdev));
+}
+
size_t device_alignment(struct device *device)
{
int devfd;
diff --git a/lib/utils_device_locking.c b/lib/utils_device_locking.c
index e18ea77..ef3f6b4 100644
--- a/lib/utils_device_locking.c
+++ b/lib/utils_device_locking.c
@@ -1,8 +1,8 @@
/*
* Metadata on-disk locking for processes serialization
*
- * Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2016-2023 Ondrej Kozina
+ * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2016-2024 Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -134,7 +134,7 @@ static int open_resource(struct crypt_device *cd, const char *res)
return -EINVAL;
log_dbg(cd, "Opening lock resource file %s/%s", DEFAULT_LUKS2_LOCK_PATH, res);
- r = openat(lockdir_fd, res, O_CREAT | O_NOFOLLOW | O_RDWR | O_CLOEXEC, 0777);
+ r = openat(lockdir_fd, res, O_CREAT|O_NOFOLLOW|O_RDWR|O_CLOEXEC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
err = errno;
close(lockdir_fd);
@@ -405,30 +405,6 @@ int device_write_lock_internal(struct crypt_device *cd, struct device *device)
return 1;
}
-int crypt_read_lock(struct crypt_device *cd, const char *resource, bool blocking, struct crypt_lock_handle **lock)
-{
- int r;
- struct crypt_lock_handle *h;
-
- if (!resource)
- return -EINVAL;
-
- log_dbg(cd, "Acquiring %sblocking read lock for resource %s.", blocking ? "" : "non", resource);
-
- r = acquire_and_verify(cd, NULL, resource, LOCK_SH | (blocking ? 0 : LOCK_NB), &h);
- if (r < 0)
- return r;
-
- h->type = DEV_LOCK_READ;
- h->refcnt = 1;
-
- log_dbg(cd, "READ lock for resource %s taken.", resource);
-
- *lock = h;
-
- return 0;
-}
-
int crypt_write_lock(struct crypt_device *cd, const char *resource, bool blocking, struct crypt_lock_handle **lock)
{
int r;
diff --git a/lib/utils_device_locking.h b/lib/utils_device_locking.h
index b73f15d..3fa09a5 100644
--- a/lib/utils_device_locking.h
+++ b/lib/utils_device_locking.h
@@ -1,8 +1,8 @@
/*
* Metadata on-disk locking for processes serialization
*
- * Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2016-2023 Ondrej Kozina
+ * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2016-2024 Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -37,7 +37,6 @@ void device_unlock_internal(struct crypt_device *cd, struct device *device);
int device_locked_verify(struct crypt_device *cd, int fd, struct crypt_lock_handle *h);
-int crypt_read_lock(struct crypt_device *cd, const char *name, bool blocking, struct crypt_lock_handle **lock);
int crypt_write_lock(struct crypt_device *cd, const char *name, bool blocking, struct crypt_lock_handle **lock);
void crypt_unlock_internal(struct crypt_device *cd, struct crypt_lock_handle *h);
diff --git a/lib/utils_devpath.c b/lib/utils_devpath.c
index dc5a5bb..5e7e13e 100644
--- a/lib/utils_devpath.c
+++ b/lib/utils_devpath.c
@@ -3,8 +3,8 @@
*
* Copyright (C) 2004 Jana Saout <jana@saout.de>
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -210,6 +210,24 @@ static int _path_get_uint64(const char *sysfs_path, uint64_t *value, const char
return _read_uint64(path, value);
}
+int crypt_dev_get_partition_number(const char *dev_path)
+{
+ uint64_t partno;
+ struct stat st;
+
+ if (stat(dev_path, &st) < 0)
+ return 0;
+
+ if (!S_ISBLK(st.st_mode))
+ return 0;
+
+ if (!_sysfs_get_uint64(major(st.st_rdev), minor(st.st_rdev),
+ &partno, "partition"))
+ return -EINVAL;
+
+ return (int)partno;
+}
+
int crypt_dev_is_rotational(int major, int minor)
{
uint64_t val;
@@ -220,6 +238,16 @@ int crypt_dev_is_rotational(int major, int minor)
return val ? 1 : 0;
}
+int crypt_dev_is_dax(int major, int minor)
+{
+ uint64_t val;
+
+ if (!_sysfs_get_uint64(major, minor, &val, "queue/dax"))
+ return 0; /* if failed, expect non-DAX device */
+
+ return val ? 1 : 0;
+}
+
int crypt_dev_is_partition(const char *dev_path)
{
uint64_t val;
@@ -253,6 +281,7 @@ uint64_t crypt_dev_partition_offset(const char *dev_path)
&val, "start"))
return 0;
+ /* coverity[tainted_data_return:FALSE] */
return val;
}
diff --git a/lib/utils_dm.h b/lib/utils_dm.h
index 79212a2..dbbd470 100644
--- a/lib/utils_dm.h
+++ b/lib/utils_dm.h
@@ -3,8 +3,8 @@
*
* Copyright (C) 2004 Jana Saout <jana@saout.de>
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -72,7 +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_INTEGRITY_RESIZE_SUPPORTED (1 << 23) /* dm-integrity resize of the integrity device supported (introduced in the same version as discards)*/
#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_CRYPT_NO_WORKQUEUE_SUPPORTED (1 << 25) /* dm-crypt support for bypassing workqueues */
#define DM_INTEGRITY_FIX_HMAC_SUPPORTED (1 << 26) /* hmac covers also superblock */
#define DM_INTEGRITY_RESET_RECALC_SUPPORTED (1 << 27) /* dm-integrity automatic recalculation supported */
#define DM_VERITY_TASKLETS_SUPPORTED (1 << 28) /* dm-verity tasklets supported */
@@ -234,6 +234,7 @@ int dm_clear_device(struct crypt_device *cd, const char *name);
int dm_cancel_deferred_removal(const char *name);
const char *dm_get_dir(void);
+int dm_get_iname(const char *name, char **iname, bool with_path);
int lookup_dm_dev_by_uuid(struct crypt_device *cd, const char *uuid, const char *type);
diff --git a/lib/utils_io.c b/lib/utils_io.c
index a5bc501..1c6b456 100644
--- a/lib/utils_io.c
+++ b/lib/utils_io.c
@@ -3,8 +3,8 @@
*
* Copyright (C) 2004 Jana Saout <jana@saout.de>
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/utils_io.h b/lib/utils_io.h
index f8b3f00..ce6a6ed 100644
--- a/lib/utils_io.h
+++ b/lib/utils_io.h
@@ -3,8 +3,8 @@
*
* Copyright (C) 2004 Jana Saout <jana@saout.de>
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/utils_keyring.c b/lib/utils_keyring.c
index a0c4db1..6bd3c48 100644
--- a/lib/utils_keyring.c
+++ b/lib/utils_keyring.c
@@ -1,8 +1,8 @@
/*
* kernel keyring utilities
*
- * Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2016-2023 Ondrej Kozina
+ * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2016-2024 Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,9 +19,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <assert.h>
+#include <ctype.h>
#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
+#include <stdbool.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
@@ -29,11 +34,6 @@
#include "libcryptsetup_macros.h"
#include "utils_keyring.h"
-#ifndef HAVE_KEY_SERIAL_T
-#define HAVE_KEY_SERIAL_T
-typedef int32_t key_serial_t;
-#endif
-
#ifdef KERNEL_KEYRING
static const struct {
@@ -42,6 +42,9 @@ static const struct {
} key_types[] = {
{ LOGON_KEY, "logon" },
{ USER_KEY, "user" },
+ { BIG_KEY, "big_key" },
+ { TRUSTED_KEY, "trusted" },
+ { ENCRYPTED_KEY, "encrypted" },
};
#include <linux/keyctl.h>
@@ -65,16 +68,22 @@ static key_serial_t add_key(const char *type,
return syscall(__NR_add_key, type, description, payload, plen, keyring);
}
+/* keyctl_describe */
+static long keyctl_describe(key_serial_t id, char *buffer, size_t buflen)
+{
+ return syscall(__NR_keyctl, KEYCTL_DESCRIBE, id, buffer, buflen);
+}
+
/* keyctl_read */
static long keyctl_read(key_serial_t key, char *buffer, size_t buflen)
{
return syscall(__NR_keyctl, KEYCTL_READ, key, buffer, buflen);
}
-/* keyctl_revoke */
-static long keyctl_revoke(key_serial_t key)
+/* keyctl_link */
+static long keyctl_link(key_serial_t key, key_serial_t keyring)
{
- return syscall(__NR_keyctl, KEYCTL_REVOKE, key);
+ return syscall(__NR_keyctl, KEYCTL_LINK, key, keyring);
}
/* keyctl_unlink */
@@ -82,156 +91,380 @@ static long keyctl_unlink(key_serial_t key, key_serial_t keyring)
{
return syscall(__NR_keyctl, KEYCTL_UNLINK, key, keyring);
}
-#endif
-int keyring_check(void)
+/* inspired by keyutils written by David Howells (dhowells@redhat.com) */
+static key_serial_t keyring_process_proc_keys_line(char *line, const char *type, const char *desc,
+ key_serial_t destringid)
{
-#ifdef KERNEL_KEYRING
- /* logon type key descriptions must be in format "prefix:description" */
- return syscall(__NR_request_key, "logon", "dummy", NULL, 0) == -1l && errno != ENOSYS;
-#else
+ char typebuf[41], rdesc[1024], *kdesc, *cp;
+ int ndesc, n;
+ key_serial_t id;
+ int dlen;
+
+ assert(desc);
+ dlen = strlen(desc);
+ cp = line + strlen(line);
+
+ ndesc = 0;
+ n = sscanf(line, "%x %*s %*u %*s %*x %*d %*d %40s %n",
+ &id, typebuf, &ndesc);
+ if (n == 2 && ndesc > 0 && ndesc <= cp - line) {
+ if (strcmp(typebuf, type) != 0)
+ return 0;
+ kdesc = line + ndesc;
+ if (memcmp(kdesc, desc, dlen) != 0)
+ return 0;
+ if (kdesc[dlen] != ':' &&
+ kdesc[dlen] != '\0' &&
+ kdesc[dlen] != ' ')
+ return 0;
+ kdesc[dlen] = '\0';
+
+ /* The key type appends extra stuff to the end of the
+ * description after a colon in /proc/keys. Colons,
+ * however, are allowed in descriptions, so we need to
+ * make a further check. */
+ n = keyctl_describe(id, rdesc, sizeof(rdesc) - 1);
+ if (n < 0)
+ return 0;
+ if ((size_t)n >= sizeof(rdesc) - 1)
+ return 0;
+ rdesc[n] = '\0';
+
+ cp = strrchr(rdesc, ';');
+ if (!cp)
+ return 0;
+ cp++;
+ if (strcmp(cp, desc) != 0)
+ return 0;
+
+
+ if (destringid && keyctl_link(id, destringid) == -1)
+ return 0;
+
+ return id;
+ }
+
return 0;
-#endif
}
-int keyring_add_key_in_thread_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size)
-{
-#ifdef KERNEL_KEYRING
- key_serial_t kid;
- const char *type_name = key_type_name(ktype);
+/* inspired by keyutils written by David Howells (dhowells@redhat.com), returns 0 ID on failure */
- if (!type_name || !key_desc)
- return -EINVAL;
+static key_serial_t find_key_by_type_and_desc(const char *type, const char *desc, key_serial_t destringid)
+{
+ key_serial_t id;
+ int f;
+ char buf[1024];
+ char *newline;
+ size_t buffer_len = 0;
+
+ int n;
+
+ do {
+ id = request_key(type, desc, NULL, 0);
+ } while (id < 0 && errno == EINTR);
+ if (id >= 0 || errno == ENOMEM)
+ return id;
+
+ f = open("/proc/keys", O_RDONLY);
+ if (f < 0)
+ return 0;
- kid = add_key(type_name, key_desc, key, key_size, KEY_SPEC_THREAD_KEYRING);
- if (kid < 0)
- return -errno;
+ while ((n = read(f, buf + buffer_len, sizeof(buf) - buffer_len - 1)) > 0) {
+ buffer_len += n;
+ buf[buffer_len] = '\0';
+ newline = strchr(buf, '\n');
+ while (newline != NULL && buffer_len != 0) {
+ *newline = '\0';
+
+ if ((id = keyring_process_proc_keys_line(buf, type, desc, destringid))) {
+ close(f);
+ return id;
+ }
+
+ buffer_len -= newline - buf + 1;
+ assert(buffer_len <= sizeof(buf) - 1);
+ memmove(buf, newline + 1, buffer_len);
+ buf[buffer_len] = '\0';
+ newline = strchr(buf, '\n');
+ }
+ }
+ close(f);
return 0;
-#else
- return -ENOTSUP;
-#endif
}
-/* currently used in client utilities only */
-int keyring_add_key_in_user_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size)
+int keyring_check(void)
+{
+ /* logon type key descriptions must be in format "prefix:description" */
+ return syscall(__NR_request_key, "logon", "dummy", NULL, 0) == -1l && errno != ENOSYS;
+}
+
+static key_serial_t keyring_add_key_in_keyring(key_type_t ktype,
+ const char *key_desc,
+ const void *key,
+ size_t key_size,
+ key_serial_t keyring)
{
-#ifdef KERNEL_KEYRING
const char *type_name = key_type_name(ktype);
- key_serial_t kid;
if (!type_name || !key_desc)
return -EINVAL;
- kid = add_key(type_name, key_desc, key, key_size, KEY_SPEC_USER_KEYRING);
- if (kid < 0)
- return -errno;
-
- return 0;
-#else
- return -ENOTSUP;
-#endif
+ return add_key(type_name, key_desc, key, key_size, keyring);
}
-/* alias for the same code */
-int keyring_get_key(const char *key_desc,
- char **key,
- size_t *key_size)
+key_serial_t keyring_add_key_in_thread_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size)
{
- return keyring_get_passphrase(key_desc, key, key_size);
+ return keyring_add_key_in_keyring(ktype, key_desc, key, key_size, KEY_SPEC_THREAD_KEYRING);
}
-int keyring_get_passphrase(const char *key_desc,
- char **passphrase,
- size_t *passphrase_len)
+key_serial_t keyring_request_key_id(key_type_t key_type,
+ const char *key_description)
{
-#ifdef KERNEL_KEYRING
- int err;
key_serial_t kid;
- long ret;
+
+ do {
+ kid = request_key(key_type_name(key_type), key_description, NULL, 0);
+ } while (kid < 0 && errno == EINTR);
+
+ return kid;
+}
+
+int keyring_read_key(key_serial_t kid,
+ char **key,
+ size_t *key_size)
+{
+ long r;
char *buf = NULL;
size_t len = 0;
- do
- kid = request_key(key_type_name(USER_KEY), key_desc, NULL, 0);
- while (kid < 0 && errno == EINTR);
-
- if (kid < 0)
- return -errno;
+ assert(key);
+ assert(key_size);
/* just get payload size */
- ret = keyctl_read(kid, NULL, 0);
- if (ret > 0) {
- len = ret;
+ r = keyctl_read(kid, NULL, 0);
+ if (r > 0) {
+ len = r;
buf = crypt_safe_alloc(len);
if (!buf)
return -ENOMEM;
/* retrieve actual payload data */
- ret = keyctl_read(kid, buf, len);
+ r = keyctl_read(kid, buf, len);
}
- if (ret < 0) {
- err = errno;
+ if (r < 0) {
crypt_safe_free(buf);
- return -err;
+ return -EINVAL;
}
- *passphrase = buf;
- *passphrase_len = len;
+ *key = buf;
+ *key_size = len;
return 0;
-#else
- return -ENOTSUP;
-#endif
}
-static int keyring_revoke_and_unlink_key_type(const char *type_name, const char *key_desc)
+int keyring_unlink_key_from_keyring(key_serial_t kid, key_serial_t keyring_id)
{
-#ifdef KERNEL_KEYRING
- key_serial_t kid;
+ return keyctl_unlink(kid, keyring_id) < 0 ? -EINVAL : 0;
+}
- if (!type_name || !key_desc)
- return -EINVAL;
+int keyring_unlink_key_from_thread_keyring(key_serial_t kid)
+{
+ return keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING) < 0 ? -EINVAL : 0;
+}
+
+const char *key_type_name(key_type_t type)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(key_types); i++)
+ if (type == key_types[i].type)
+ return key_types[i].type_name;
+
+ return NULL;
+}
+
+key_serial_t keyring_find_key_id_by_name(const char *key_name)
+{
+ key_serial_t id = 0;
+ char *end;
+ char *name_copy, *name_copy_p;
- do
- kid = request_key(type_name, key_desc, NULL, 0);
- while (kid < 0 && errno == EINTR);
+ assert(key_name);
+
+ if (key_name[0] == '@') {
+ if (strcmp(key_name, "@t" ) == 0) return KEY_SPEC_THREAD_KEYRING;
+ if (strcmp(key_name, "@p" ) == 0) return KEY_SPEC_PROCESS_KEYRING;
+ if (strcmp(key_name, "@s" ) == 0) return KEY_SPEC_SESSION_KEYRING;
+ if (strcmp(key_name, "@u" ) == 0) return KEY_SPEC_USER_KEYRING;
+ if (strcmp(key_name, "@us") == 0) return KEY_SPEC_USER_SESSION_KEYRING;
+ if (strcmp(key_name, "@g" ) == 0) return KEY_SPEC_GROUP_KEYRING;
+ if (strcmp(key_name, "@a" ) == 0) return KEY_SPEC_REQKEY_AUTH_KEY;
- if (kid < 0)
return 0;
+ }
- if (keyctl_revoke(kid))
- return -errno;
+ /* handle a lookup-by-name request "%<type>:<desc>", eg: "%keyring:_ses" */
+ name_copy = strdup(key_name);
+ if (!name_copy)
+ goto out;
+ name_copy_p = name_copy;
+
+ if (name_copy_p[0] == '%') {
+ const char *type;
+
+ name_copy_p++;
+ if (!*name_copy_p)
+ goto out;
+
+ if (*name_copy_p == ':') {
+ type = "keyring";
+ name_copy_p++;
+ } else {
+ type = name_copy_p;
+ name_copy_p = strchr(name_copy_p, ':');
+ if (!name_copy_p)
+ goto out;
+ *(name_copy_p++) = '\0';
+ }
+
+ if (!*name_copy_p)
+ goto out;
+
+ id = find_key_by_type_and_desc(type, name_copy_p, 0);
+ goto out;
+ }
+
+ id = strtoul(key_name, &end, 0);
+ if (*end)
+ id = 0;
- /*
- * best effort only. the key could have been linked
- * in some other keyring and its payload is now
- * revoked anyway.
- */
- keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING);
- keyctl_unlink(kid, KEY_SPEC_PROCESS_KEYRING);
- keyctl_unlink(kid, KEY_SPEC_USER_KEYRING);
+out:
+ if (name_copy)
+ free(name_copy);
+
+ return id;
+}
+
+static bool numbered(const char *str)
+{
+ char *endp;
+
+ errno = 0;
+ (void) strtol(str, &endp, 0);
+ if (errno == ERANGE)
+ return false;
+
+ return *endp == '\0' ? true : false;
+}
+
+key_serial_t keyring_find_keyring_id_by_name(const char *keyring_name)
+{
+ assert(keyring_name);
+
+ /* "%:" is abbreviation for the type keyring */
+ if ((keyring_name[0] == '@' && keyring_name[1] != 'a') ||
+ strstr(keyring_name, "%:") || strstr(keyring_name, "%keyring:") ||
+ numbered(keyring_name))
+ return keyring_find_key_id_by_name(keyring_name);
return 0;
-#else
- return -ENOTSUP;
-#endif
}
-const char *key_type_name(key_type_t type)
+key_type_t key_type_by_name(const char *name)
{
-#ifdef KERNEL_KEYRING
unsigned int i;
for (i = 0; i < ARRAY_SIZE(key_types); i++)
- if (type == key_types[i].type)
- return key_types[i].type_name;
-#endif
+ if (!strcmp(key_types[i].type_name, name))
+ return key_types[i].type;
+
+ return INVALID_KEY;
+}
+
+key_serial_t keyring_add_key_to_custom_keyring(key_type_t ktype,
+ const char *key_desc,
+ const void *key,
+ size_t key_size,
+ key_serial_t keyring_to_link)
+{
+ const char *type_name = key_type_name(ktype);
+
+ if (!type_name || !key_desc)
+ return -EINVAL;
+
+ return add_key(type_name, key_desc, key, key_size, keyring_to_link);
+}
+
+#else /* KERNEL_KEYRING */
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+int keyring_check(void)
+{
+ return 0;
+}
+
+key_serial_t keyring_add_key_in_thread_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size)
+{
+ return -ENOTSUP;
+}
+
+key_serial_t keyring_request_key_id(key_type_t key_type,
+ const char *key_description)
+{
+ return -ENOTSUP;
+}
+
+int keyring_read_key(key_serial_t kid,
+ char **key,
+ size_t *key_size)
+{
+ return -ENOTSUP;
+}
+
+int keyring_read_by_id(const char *key_desc, char **passphrase, size_t *passphrase_len)
+{
+ return -ENOTSUP;
+}
+
+const char *key_type_name(key_type_t type)
+{
return NULL;
}
-int keyring_revoke_and_unlink_key(key_type_t ktype, const char *key_desc)
+key_serial_t keyring_find_key_id_by_name(const char *key_name)
{
- return keyring_revoke_and_unlink_key_type(key_type_name(ktype), key_desc);
+ return 0;
}
+
+key_serial_t keyring_find_keyring_id_by_name(const char *keyring_name)
+{
+ return 0;
+}
+
+key_type_t key_type_by_name(const char *name)
+{
+ return INVALID_KEY;
+}
+
+key_serial_t keyring_add_key_to_custom_keyring(key_type_t ktype,
+ const char *key_desc,
+ const void *key,
+ size_t key_size,
+ key_serial_t keyring_to_link)
+{
+ return -ENOTSUP;
+}
+
+int keyring_unlink_key_from_keyring(key_serial_t kid, key_serial_t keyring_id)
+{
+ return -ENOTSUP;
+}
+
+int keyring_unlink_key_from_thread_keyring(key_serial_t kid)
+{
+ return -ENOTSUP;
+}
+#endif
diff --git a/lib/utils_keyring.h b/lib/utils_keyring.h
index 0248862..896f8d8 100644
--- a/lib/utils_keyring.h
+++ b/lib/utils_keyring.h
@@ -1,8 +1,8 @@
/*
* kernel keyring syscall wrappers
*
- * Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2016-2023 Ondrej Kozina
+ * Copyright (C) 2016-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2016-2024 Ondrej Kozina
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -23,33 +23,38 @@
#define _UTILS_KEYRING
#include <stddef.h>
+#include <stdint.h>
-typedef enum { LOGON_KEY = 0, USER_KEY } key_type_t;
+#ifndef HAVE_KEY_SERIAL_T
+#define HAVE_KEY_SERIAL_T
+typedef int32_t key_serial_t;
+#endif
+
+typedef enum { LOGON_KEY = 0, USER_KEY, BIG_KEY, TRUSTED_KEY, ENCRYPTED_KEY, INVALID_KEY } key_type_t;
const char *key_type_name(key_type_t ktype);
+key_type_t key_type_by_name(const char *name);
+key_serial_t keyring_find_key_id_by_name(const char *key_name);
+key_serial_t keyring_find_keyring_id_by_name(const char *keyring_name);
int keyring_check(void);
-int keyring_get_key(const char *key_desc,
- char **key,
- size_t *key_size);
+key_serial_t keyring_request_key_id(key_type_t key_type,
+ const char *key_description);
-int keyring_get_passphrase(const char *key_desc,
- char **passphrase,
- size_t *passphrase_len);
-
-int keyring_add_key_in_thread_keyring(
- key_type_t ktype,
- const char *key_desc,
- const void *key,
- size_t key_size);
+int keyring_read_key(key_serial_t kid,
+ char **key,
+ size_t *key_size);
-int keyring_add_key_in_user_keyring(
+key_serial_t keyring_add_key_in_thread_keyring(
key_type_t ktype,
const char *key_desc,
const void *key,
size_t key_size);
-int keyring_revoke_and_unlink_key(key_type_t ktype, const char *key_desc);
+key_serial_t keyring_add_key_to_custom_keyring(key_type_t ktype, const char *key_desc, const void *key,
+ size_t key_size, key_serial_t keyring_to_link);
+int keyring_unlink_key_from_keyring(key_serial_t kid, key_serial_t keyring_id);
+int keyring_unlink_key_from_thread_keyring(key_serial_t kid);
#endif
diff --git a/lib/utils_loop.c b/lib/utils_loop.c
index 9b31603..092ebfc 100644
--- a/lib/utils_loop.c
+++ b/lib/utils_loop.c
@@ -1,8 +1,8 @@
/*
* loopback block device utilities
*
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -282,7 +282,7 @@ static char *_sysfs_backing_file(const char *loop)
{
struct stat st;
char buf[PATH_MAX];
- size_t len;
+ ssize_t len;
int fd;
if (stat(loop, &st) || !S_ISBLK(st.st_mode))
diff --git a/lib/utils_loop.h b/lib/utils_loop.h
index c1f6356..17a78aa 100644
--- a/lib/utils_loop.h
+++ b/lib/utils_loop.h
@@ -1,8 +1,8 @@
/*
* loopback block device utilities
*
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/utils_pbkdf.c b/lib/utils_pbkdf.c
index 4d7e18d..4341e91 100644
--- a/lib/utils_pbkdf.c
+++ b/lib/utils_pbkdf.c
@@ -1,8 +1,8 @@
/*
* utils_pbkdf - PBKDF settings for libcryptsetup
*
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -61,9 +61,9 @@ const struct crypt_pbkdf_type *crypt_get_pbkdf_type_params(const char *pbkdf_typ
return NULL;
}
-static uint32_t adjusted_phys_memory(void)
+uint32_t pbkdf_adjusted_phys_memory_kb(void)
{
- uint64_t memory_kb = crypt_getphysmemory_kb();
+ uint64_t free_kb, memory_kb = crypt_getphysmemory_kb();
/* Ignore bogus value */
if (memory_kb < (128 * 1024) || memory_kb > UINT32_MAX)
@@ -75,6 +75,22 @@ static uint32_t adjusted_phys_memory(void)
*/
memory_kb /= 2;
+ /*
+ * Never use more that half of available free memory on system without swap.
+ */
+ if (!crypt_swapavailable()) {
+ free_kb = crypt_getphysmemoryfree_kb();
+
+ /*
+ * Using exactly free memory causes OOM too, use only half of the value.
+ * Ignore small values (< 64MB), user should use PBKDF2 in such environment.
+ */
+ free_kb /= 2;
+
+ if (free_kb > (64 * 1024) && free_kb < memory_kb)
+ return free_kb;
+ }
+
return memory_kb;
}
@@ -238,7 +254,8 @@ int init_pbkdf_type(struct crypt_device *cd,
cd_pbkdf->parallel_threads = pbkdf_limits.max_parallel;
}
- if (cd_pbkdf->parallel_threads) {
+ /* Do not limit threads by online CPUs if user forced values (no benchmark). */
+ if (cd_pbkdf->parallel_threads && !(cd_pbkdf->flags & CRYPT_PBKDF_NO_BENCHMARK)) {
cpus = crypt_cpusonline();
if (cd_pbkdf->parallel_threads > cpus) {
log_dbg(cd, "Only %u active CPUs detected, "
@@ -248,8 +265,9 @@ int init_pbkdf_type(struct crypt_device *cd,
}
}
- if (cd_pbkdf->max_memory_kb) {
- memory_kb = adjusted_phys_memory();
+ /* Do not limit by available physical memory if user forced values (no benchmark). */
+ if (cd_pbkdf->max_memory_kb && !(cd_pbkdf->flags & CRYPT_PBKDF_NO_BENCHMARK)) {
+ memory_kb = pbkdf_adjusted_phys_memory_kb();
if (cd_pbkdf->max_memory_kb > memory_kb) {
log_dbg(cd, "Not enough physical memory detected, "
"PBKDF max memory decreased from %dkB to %dkB.",
diff --git a/lib/utils_safe_memory.c b/lib/utils_safe_memory.c
index b161369..753842d 100644
--- a/lib/utils_safe_memory.c
+++ b/lib/utils_safe_memory.c
@@ -1,8 +1,8 @@
/*
* utils_safe_memory - safe memory helpers
*
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/lib/utils_storage_wrappers.c b/lib/utils_storage_wrappers.c
index 6ff5afa..4a3aae3 100644
--- a/lib/utils_storage_wrappers.c
+++ b/lib/utils_storage_wrappers.c
@@ -2,7 +2,7 @@
* Generic wrapper for storage functions
* (experimental only)
*
- * Copyright (C) 2018-2023 Ondrej Kozina
+ * Copyright (C) 2018-2024 Ondrej Kozina
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/utils_storage_wrappers.h b/lib/utils_storage_wrappers.h
index f7781e8..272c5c1 100644
--- a/lib/utils_storage_wrappers.h
+++ b/lib/utils_storage_wrappers.h
@@ -2,7 +2,7 @@
* Generic wrapper for storage functions
* (experimental only)
*
- * Copyright (C) 2018-2023 Ondrej Kozina
+ * Copyright (C) 2018-2024 Ondrej Kozina
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/utils_wipe.c b/lib/utils_wipe.c
index 1df46c1..368e6dc 100644
--- a/lib/utils_wipe.c
+++ b/lib/utils_wipe.c
@@ -2,8 +2,8 @@
* utils_wipe - wipe a device
*
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2009-2024 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2024 Milan Broz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -26,6 +26,8 @@
#include <sys/stat.h>
#include <linux/fs.h>
#include "internal.h"
+#include "luks2/luks2_internal.h"
+#include "luks2/hw_opal/hw_opal.h"
/* block device zeroout ioctls, introduced in Linux kernel 3.7 */
#ifndef BLKZEROOUT
@@ -309,3 +311,73 @@ int crypt_wipe(struct crypt_device *cd,
return r;
}
+
+int crypt_wipe_hw_opal(struct crypt_device *cd,
+ int segment,
+ const char *password,
+ size_t password_size,
+ uint32_t flags)
+{
+ int r;
+ struct luks2_hdr *hdr;
+ uint32_t opal_segment_number;
+ struct crypt_lock_handle *opal_lh = NULL;
+
+ UNUSED(flags);
+
+ if (!cd)
+ return -EINVAL;
+
+ if (!password)
+ return -EINVAL;
+
+ if (segment < CRYPT_LUKS2_SEGMENT || segment > 8)
+ return -EINVAL;
+
+ r = crypt_opal_supported(cd, crypt_data_device(cd));
+ if (r < 0)
+ return r;
+
+ if (segment == CRYPT_NO_SEGMENT) {
+ r = opal_factory_reset(cd, crypt_data_device(cd), password, password_size);
+ if (r == -EPERM)
+ log_err(cd, _("Incorrect OPAL PSID."));
+ else if (r < 0)
+ log_err(cd, _("Cannot erase OPAL device."));
+ return r;
+ }
+
+ if (onlyLUKS2(cd) < 0)
+ return -EINVAL;
+
+ hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
+ if (!hdr)
+ return -EINVAL;
+
+ if (segment == CRYPT_LUKS2_SEGMENT) {
+ r = LUKS2_get_opal_segment_number(hdr, CRYPT_DEFAULT_SEGMENT, &opal_segment_number);
+ if (r < 0) {
+ log_dbg(cd, "Can not get OPAL segment number.");
+ return r;
+ }
+ } else
+ opal_segment_number = segment;
+
+ r = opal_exclusive_lock(cd, crypt_data_device(cd), &opal_lh);
+ if (r < 0) {
+ log_err(cd, _("Failed to acquire OPAL lock on device %s."), device_path(crypt_data_device(cd)));
+ return -EINVAL;
+ }
+
+ r = opal_reset_segment(cd,
+ crypt_data_device(cd),
+ opal_segment_number,
+ password,
+ password_size);
+
+ opal_exclusive_unlock(cd, opal_lh);
+ if (r < 0)
+ return r;
+
+ return LUKS2_wipe_header_areas(cd, hdr, crypt_header_is_detached(cd));
+}
diff --git a/lib/verity/rs.h b/lib/verity/rs.h
index 7638924..34785aa 100644
--- a/lib/verity/rs.h
+++ b/lib/verity/rs.h
@@ -3,7 +3,7 @@
*
* Copyright (C) 2004 Phil Karn, KA9Q
* libcryptsetup modifications
- * Copyright (C) 2017-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2017-2024 Red Hat, Inc. All rights reserved.
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/verity/rs_decode_char.c b/lib/verity/rs_decode_char.c
index 4473202..94c8523 100644
--- a/lib/verity/rs_decode_char.c
+++ b/lib/verity/rs_decode_char.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2002, Phil Karn, KA9Q
* libcryptsetup modifications
- * Copyright (C) 2017-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2017-2024 Red Hat, Inc. All rights reserved.
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/verity/rs_encode_char.c b/lib/verity/rs_encode_char.c
index 55b502a..a520562 100644
--- a/lib/verity/rs_encode_char.c
+++ b/lib/verity/rs_encode_char.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2002, Phil Karn, KA9Q
* libcryptsetup modifications
- * Copyright (C) 2017-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2017-2024 Red Hat, Inc. All rights reserved.
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/verity/verity.c b/lib/verity/verity.c
index 0d7a8f5..b3dd1b3 100644
--- a/lib/verity/verity.c
+++ b/lib/verity/verity.c
@@ -1,7 +1,7 @@
/*
* dm-verity volume handling
*
- * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved.
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -251,91 +251,133 @@ int VERITY_UUID_generate(char **uuid_string)
return 0;
}
+int VERITY_verify_params(struct crypt_device *cd,
+ struct crypt_params_verity *hdr,
+ bool signed_root_hash,
+ struct device *fec_device,
+ struct volume_key *root_hash)
+{
+ bool userspace_verification;
+ int v, r;
+ unsigned int fec_errors = 0;
+
+ assert(cd);
+ assert(hdr);
+ assert(root_hash);
+
+ log_dbg(cd, "Verifying VERITY device using hash %s.",
+ hdr->hash_name);
+
+ userspace_verification = hdr->flags & CRYPT_VERITY_CHECK_HASH;
+
+ if (userspace_verification && signed_root_hash) {
+ log_err(cd, _("Root hash signature verification is not supported."));
+ return -EINVAL;
+ }
+
+ if ((hdr->flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE) && !signed_root_hash) {
+ log_err(cd, _("Root hash signature required."));
+ return -EINVAL;
+ }
+
+ if (!userspace_verification)
+ return 0;
+
+ log_dbg(cd, "Verification of VERITY data in userspace required.");
+ r = VERITY_verify(cd, hdr, root_hash->key, root_hash->keylength);
+
+ 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, hdr, fec_device, 1, &fec_errors);
+ if (r < 0)
+ log_err(cd, _("Errors cannot be repaired with FEC device."));
+ 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;
+ }
+
+ return r;
+}
+
/* Activate verity device in kernel device-mapper */
int VERITY_activate(struct crypt_device *cd,
const char *name,
- const char *root_hash,
- size_t root_hash_size,
- const char *signature_description,
+ struct volume_key *root_hash,
+ struct volume_key *signature,
struct device *fec_device,
struct crypt_params_verity *verity_hdr,
uint32_t activation_flags)
{
uint32_t dmv_flags;
- unsigned int fec_errors = 0;
- int r, v;
- struct crypt_dm_active_device dmd = {
- .size = verity_hdr->data_size * verity_hdr->data_block_size / 512,
- .flags = activation_flags,
- .uuid = crypt_get_uuid(cd),
- };
-
- log_dbg(cd, "Trying to activate VERITY device %s using hash %s.",
- name ?: "[none]", verity_hdr->hash_name);
-
- if (verity_hdr->flags & CRYPT_VERITY_CHECK_HASH) {
- if (signature_description) {
- log_err(cd, _("Root hash signature verification is not supported."));
- return -EINVAL;
- }
+ int r;
+ key_serial_t kid;
+ char *description = NULL;
+ struct crypt_dm_active_device dmd = { 0 };
- log_dbg(cd, "Verification of data in userspace required.");
- r = VERITY_verify(cd, verity_hdr, root_hash, root_hash_size);
-
- 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) {
- 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;
- }
+ assert(name);
+ assert(root_hash);
+ assert(verity_hdr);
+
+ dmd.size = verity_hdr->data_size * verity_hdr->data_block_size / 512;
+ dmd.flags = activation_flags;
+ dmd.uuid = crypt_get_uuid(cd);
+
+ log_dbg(cd, "Activating VERITY device %s using hash %s.",
+ name, verity_hdr->hash_name);
+ if (signature) {
+ r = asprintf(&description, "cryptsetup:%s%s%s",
+ crypt_get_uuid(cd) ?: "", crypt_get_uuid(cd) ? "-" : "", name);
if (r < 0)
- return r;
- }
+ return -EINVAL;
- if (!name)
- return 0;
+ log_dbg(cd, "Adding signature %s (type user) into thread keyring.", description);
+ kid = keyring_add_key_in_thread_keyring(USER_KEY, description, signature->key, signature->keylength);
+ if (kid < 0) {
+ log_dbg(cd, "keyring_add_key_in_thread_keyring failed with errno %d.", errno);
+ log_err(cd, _("Failed to load key in kernel keyring."));
+ free(description);
+ return -EINVAL;
+ }
+ }
r = device_block_adjust(cd, crypt_metadata_device(cd), DEV_OK,
0, NULL, NULL);
if (r)
- return r;
+ goto out;
r = device_block_adjust(cd, crypt_data_device(cd), DEV_EXCL,
0, &dmd.size, &dmd.flags);
if (r)
- return r;
+ goto out;
if (fec_device) {
r = device_block_adjust(cd, fec_device, DEV_OK,
0, NULL, NULL);
if (r)
- return r;
+ goto out;
}
r = dm_verity_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd),
- crypt_metadata_device(cd), fec_device, root_hash,
- root_hash_size, signature_description,
+ crypt_metadata_device(cd), fec_device, root_hash->key,
+ root_hash->keylength, description,
VERITY_hash_offset_block(verity_hdr),
VERITY_FEC_blocks(cd, fec_device, verity_hdr), verity_hdr);
if (r)
- return r;
+ goto out;
r = dm_create_device(cd, name, CRYPT_VERITY, &dmd);
if (r < 0 && (dm_flags(cd, DM_VERITY, &dmv_flags) || !(dmv_flags & DM_VERITY_SUPPORTED))) {
log_err(cd, _("Kernel does not support dm-verity mapping."));
r = -ENOTSUP;
}
- if (r < 0 && signature_description && !(dmv_flags & DM_VERITY_SIGNATURE_SUPPORTED)) {
+ if (r < 0 && signature && !(dmv_flags & DM_VERITY_SIGNATURE_SUPPORTED)) {
log_err(cd, _("Kernel does not support dm-verity signature option."));
r = -ENOTSUP;
}
@@ -351,6 +393,8 @@ int VERITY_activate(struct crypt_device *cd,
r = 0;
out:
+ crypt_drop_keyring_key_by_description(cd, description, USER_KEY);
+ free(description);
dm_targets_free(cd, &dmd);
return r;
}
diff --git a/lib/verity/verity.h b/lib/verity/verity.h
index afc411e..00e9867 100644
--- a/lib/verity/verity.h
+++ b/lib/verity/verity.h
@@ -1,7 +1,7 @@
/*
* dm-verity volume handling
*
- * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved.
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,6 +23,7 @@
#include <stddef.h>
#include <stdint.h>
+#include <stdbool.h>
#define VERITY_MAX_HASH_TYPE 1
#define VERITY_BLOCK_SIZE_OK(x) ((x) % 512 || (x) < 512 || \
@@ -31,6 +32,7 @@
struct crypt_device;
struct crypt_params_verity;
struct device;
+struct volume_key;
int VERITY_read_sb(struct crypt_device *cd,
uint64_t sb_offset,
@@ -44,13 +46,18 @@ int VERITY_write_sb(struct crypt_device *cd,
int VERITY_activate(struct crypt_device *cd,
const char *name,
- const char *root_hash,
- size_t root_hash_size,
- const char *signature_description,
+ struct volume_key *root_hash,
+ struct volume_key *signature,
struct device *fec_device,
struct crypt_params_verity *verity_hdr,
uint32_t activation_flags);
+int VERITY_verify_params(struct crypt_device *cd,
+ struct crypt_params_verity *hdr,
+ bool signed_root_hash,
+ struct device *fec_device,
+ struct volume_key *root_hash);
+
int VERITY_verify(struct crypt_device *cd,
struct crypt_params_verity *verity_hdr,
const char *root_hash,
diff --git a/lib/verity/verity_fec.c b/lib/verity/verity_fec.c
index 2dbf59e..15608fd 100644
--- a/lib/verity/verity_fec.c
+++ b/lib/verity/verity_fec.c
@@ -2,7 +2,7 @@
* dm-verity Forward Error Correction (FEC) support
*
* Copyright (C) 2015 Google, Inc. All rights reserved.
- * Copyright (C) 2017-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2017-2024 Red Hat, Inc. All rights reserved.
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/verity/verity_hash.c b/lib/verity/verity_hash.c
index f33b737..0e351aa 100644
--- a/lib/verity/verity_hash.c
+++ b/lib/verity/verity_hash.c
@@ -1,7 +1,7 @@
/*
* dm-verity volume handling
*
- * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2012-2024 Red Hat, Inc. All rights reserved.
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/lib/volumekey.c b/lib/volumekey.c
index 00791ac..3de7f76 100644
--- a/lib/volumekey.c
+++ b/lib/volumekey.c
@@ -2,7 +2,7 @@
* cryptsetup volume key implementation
*
* Copyright (C) 2004-2006 Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2024 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -39,7 +39,7 @@ struct volume_key *crypt_alloc_volume_key(size_t keylength, const char *key)
vk->key_description = NULL;
vk->keylength = keylength;
- vk->id = -1;
+ vk->id = KEY_NOT_VERIFIED;
vk->next = NULL;
/* keylength 0 is valid => no key */