summaryrefslogtreecommitdiffstats
path: root/src/cryptenroll
diff options
context:
space:
mode:
authorGabríel Arthúr Pétursson <gabriel.petursson@marel.com>2024-01-03 17:10:45 +0100
committerGabríel Arthúr Pétursson <gabriel.petursson@marel.com>2024-02-01 13:37:12 +0100
commit631cf7f0040234d2bca81bdfdf9efecc4fb5f40f (patch)
treed0811f8856516387b1965e5a41d4b1618f72b80e /src/cryptenroll
parentcryptenroll: Lock memory pages before operating on the device (diff)
downloadsystemd-631cf7f0040234d2bca81bdfdf9efecc4fb5f40f.tar.xz
systemd-631cf7f0040234d2bca81bdfdf9efecc4fb5f40f.zip
cryptenroll: Add support for unlocking through TPM2 enrollments
Diffstat (limited to 'src/cryptenroll')
-rw-r--r--src/cryptenroll/cryptenroll-tpm2.c111
-rw-r--r--src/cryptenroll/cryptenroll-tpm2.h8
-rw-r--r--src/cryptenroll/cryptenroll.c34
-rw-r--r--src/cryptenroll/cryptenroll.h1
4 files changed, 151 insertions, 3 deletions
diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c
index b556c70931..b0937ec684 100644
--- a/src/cryptenroll/cryptenroll-tpm2.c
+++ b/src/cryptenroll/cryptenroll-tpm2.c
@@ -3,10 +3,13 @@
#include "alloc-util.h"
#include "ask-password-api.h"
#include "cryptenroll-tpm2.h"
+#include "cryptsetup-tpm2.h"
#include "env-util.h"
+#include "errno-util.h"
#include "fileio.h"
#include "hexdecoct.h"
#include "json.h"
+#include "log.h"
#include "memory-util.h"
#include "random-util.h"
#include "sha256.h"
@@ -129,6 +132,114 @@ static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) {
return 0;
}
+int load_volume_key_tpm2(
+ struct crypt_device *cd,
+ const char *cd_node,
+ const char *device,
+ void *ret_vk,
+ size_t *ret_vks) {
+
+ _cleanup_(iovec_done_erase) struct iovec decrypted_key = {};
+ _cleanup_(erase_and_freep) char *passphrase = NULL;
+ ssize_t passphrase_size;
+ int r;
+
+ assert_se(cd);
+ assert_se(cd_node);
+ assert_se(ret_vk);
+ assert_se(ret_vks);
+
+ bool found_some = false;
+ int token = 0; /* first token to look at */
+
+ for (;;) {
+ _cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
+ _cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {};
+ uint32_t hash_pcr_mask, pubkey_pcr_mask;
+ uint16_t pcr_bank, primary_alg;
+ TPM2Flags tpm2_flags;
+ int keyslot;
+
+ r = find_tpm2_auto_data(
+ cd,
+ UINT32_MAX,
+ token,
+ &hash_pcr_mask,
+ &pcr_bank,
+ &pubkey,
+ &pubkey_pcr_mask,
+ &primary_alg,
+ &blob,
+ &policy_hash,
+ &salt,
+ &srk,
+ &pcrlock_nv,
+ &tpm2_flags,
+ &keyslot,
+ &token);
+ if (r == -ENXIO)
+ return log_full_errno(LOG_NOTICE,
+ SYNTHETIC_ERRNO(EAGAIN),
+ found_some
+ ? "No TPM2 metadata matching the current system state found in LUKS2 header."
+ : "No TPM2 metadata enrolled in LUKS2 header.");
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ /* TPM2 support not compiled in? */
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 support not available.");
+ if (r < 0)
+ return r;
+
+ found_some = true;
+
+ r = acquire_tpm2_key(
+ cd_node,
+ device,
+ hash_pcr_mask,
+ pcr_bank,
+ &pubkey,
+ pubkey_pcr_mask,
+ /* signature_path= */ NULL,
+ /* pcrlock_path= */ NULL,
+ primary_alg,
+ /* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */
+ &blob,
+ &policy_hash,
+ &salt,
+ &srk,
+ &pcrlock_nv,
+ tpm2_flags,
+ /* until= */ 0,
+ /* headless= */ false,
+ /* ask_password_flags */ false,
+ &decrypted_key);
+ if (IN_SET(r, -EACCES, -ENOLCK))
+ return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 PIN unlock failed");
+ if (r != -EPERM)
+ break;
+
+ token++; /* try a different token next time */
+ }
+
+ if (r < 0)
+ return log_error_errno(r, "Unlocking via TPM2 device failed: %m");
+
+ passphrase_size = base64mem(decrypted_key.iov_base, decrypted_key.iov_len, &passphrase);
+ if (passphrase_size < 0)
+ return log_oom();
+
+ r = crypt_volume_key_get(
+ cd,
+ CRYPT_ANY_SLOT,
+ ret_vk,
+ ret_vks,
+ passphrase,
+ passphrase_size);
+ if (r < 0)
+ return log_error_errno(r, "Unlocking via TPM2 device failed: %m");
+
+ return r;
+}
+
int enroll_tpm2(struct crypt_device *cd,
const void *volume_key,
size_t volume_key_size,
diff --git a/src/cryptenroll/cryptenroll-tpm2.h b/src/cryptenroll/cryptenroll-tpm2.h
index 2fbcdd4b2f..7908b03310 100644
--- a/src/cryptenroll/cryptenroll-tpm2.h
+++ b/src/cryptenroll/cryptenroll-tpm2.h
@@ -8,9 +8,15 @@
#include "tpm2-util.h"
#if HAVE_TPM2
+int load_volume_key_tpm2(struct crypt_device *cd, const char *cd_node, const char *device, void *ret_vk, size_t *ret_vks);
int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, const char *device_key, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path);
#else
-static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, const char *device_key, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path) {
+static inline int load_volume_key_tpm2(struct crypt_device *cd, const char *cd_node, const char *device, void *ret_vk, size_t *ret_vks) {
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "TPM2 unlocking not supported.");
+}
+
+static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, const char *device_key, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path, int *slot_to_wipe) {
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"TPM2 key enrollment not supported.");
}
diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c
index 0674116ec8..76ef3da624 100644
--- a/src/cryptenroll/cryptenroll.c
+++ b/src/cryptenroll/cryptenroll.c
@@ -34,6 +34,7 @@ static EnrollType arg_enroll_type = _ENROLL_TYPE_INVALID;
static char *arg_unlock_keyfile = NULL;
static UnlockType arg_unlock_type = UNLOCK_PASSWORD;
static char *arg_unlock_fido2_device = NULL;
+static char *arg_unlock_tpm2_device = NULL;
static char *arg_pkcs11_token_uri = NULL;
static char *arg_fido2_device = NULL;
static char *arg_tpm2_device = NULL;
@@ -62,6 +63,7 @@ assert_cc(sizeof(arg_wipe_slots_mask) * 8 >= _ENROLL_TYPE_MAX);
STATIC_DESTRUCTOR_REGISTER(arg_unlock_keyfile, freep);
STATIC_DESTRUCTOR_REGISTER(arg_unlock_fido2_device, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_unlock_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, freep);
STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
@@ -118,6 +120,8 @@ static int help(void) {
" Use a file to unlock the volume\n"
" --unlock-fido2-device=PATH\n"
" Use a FIDO2 device to unlock the volume\n"
+ " --unlock-tpm2-device=PATH\n"
+ " Use a TPM2 device to unlock the volume\n"
"\n%3$sSimple Enrollment:%4$s\n"
" --password Enroll a user-supplied password\n"
" --recovery-key Enroll a recovery key\n"
@@ -173,6 +177,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_RECOVERY_KEY,
ARG_UNLOCK_KEYFILE,
ARG_UNLOCK_FIDO2_DEVICE,
+ ARG_UNLOCK_TPM2_DEVICE,
ARG_PKCS11_TOKEN_URI,
ARG_FIDO2_DEVICE,
ARG_TPM2_DEVICE,
@@ -198,6 +203,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "recovery-key", no_argument, NULL, ARG_RECOVERY_KEY },
{ "unlock-key-file", required_argument, NULL, ARG_UNLOCK_KEYFILE },
{ "unlock-fido2-device", required_argument, NULL, ARG_UNLOCK_FIDO2_DEVICE },
+ { "unlock-tpm2-device", required_argument, NULL, ARG_UNLOCK_TPM2_DEVICE },
{ "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
{ "fido2-credential-algorithm", required_argument, NULL, ARG_FIDO2_CRED_ALG },
{ "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE },
@@ -305,6 +311,26 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
+ case ARG_UNLOCK_TPM2_DEVICE: {
+ _cleanup_free_ char *device = NULL;
+
+ if (arg_unlock_type != UNLOCK_PASSWORD)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Multiple unlock methods specified at once, refusing.");
+
+ assert(!arg_unlock_tpm2_device);
+
+ if (!streq(optarg, "auto")) {
+ device = strdup(optarg);
+ if (!device)
+ return log_oom();
+ }
+
+ arg_unlock_type = UNLOCK_TPM2;
+ arg_unlock_tpm2_device = TAKE_PTR(device);
+ break;
+ }
+
case ARG_PKCS11_TOKEN_URI: {
_cleanup_free_ char *uri = NULL;
@@ -667,6 +693,10 @@ static int prepare_luks(
switch (arg_unlock_type) {
+ case UNLOCK_PASSWORD:
+ r = load_volume_key_password(cd, arg_node, vk, &vks);
+ break;
+
case UNLOCK_KEYFILE:
r = load_volume_key_keyfile(cd, vk, &vks);
break;
@@ -675,8 +705,8 @@ static int prepare_luks(
r = load_volume_key_fido2(cd, arg_node, arg_unlock_fido2_device, vk, &vks);
break;
- case UNLOCK_PASSWORD:
- r = load_volume_key_password(cd, arg_node, vk, &vks);
+ case UNLOCK_TPM2:
+ r = load_volume_key_tpm2(cd, arg_node, arg_unlock_tpm2_device, vk, &vks);
break;
default:
diff --git a/src/cryptenroll/cryptenroll.h b/src/cryptenroll/cryptenroll.h
index 335d9ccd71..08ded3e0e8 100644
--- a/src/cryptenroll/cryptenroll.h
+++ b/src/cryptenroll/cryptenroll.h
@@ -17,6 +17,7 @@ typedef enum UnlockType {
UNLOCK_PASSWORD,
UNLOCK_KEYFILE,
UNLOCK_FIDO2,
+ UNLOCK_TPM2,
_UNLOCK_TYPE_MAX,
_UNLOCK_TYPE_INVALID = -EINVAL,
} UnlockType;