summaryrefslogtreecommitdiffstats
path: root/src/cryptenroll
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:42 +0000
commit78e9bb837c258ac0ec7712b3d612cc2f407e731e (patch)
treef515d16b6efd858a9aeb5b0ef5d6f90bf288283d /src/cryptenroll
parentAdding debian version 255.5-1. (diff)
downloadsystemd-78e9bb837c258ac0ec7712b3d612cc2f407e731e.tar.xz
systemd-78e9bb837c258ac0ec7712b3d612cc2f407e731e.zip
Merging upstream version 256.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cryptenroll')
-rw-r--r--src/cryptenroll/cryptenroll-fido2.c7
-rw-r--r--src/cryptenroll/cryptenroll-list.c2
-rw-r--r--src/cryptenroll/cryptenroll-password.c42
-rw-r--r--src/cryptenroll/cryptenroll-pkcs11.c86
-rw-r--r--src/cryptenroll/cryptenroll-tpm2.c269
-rw-r--r--src/cryptenroll/cryptenroll-tpm2.h10
-rw-r--r--src/cryptenroll/cryptenroll.c201
-rw-r--r--src/cryptenroll/cryptenroll.h1
8 files changed, 462 insertions, 156 deletions
diff --git a/src/cryptenroll/cryptenroll-fido2.c b/src/cryptenroll/cryptenroll-fido2.c
index 2baeb92..baa630a 100644
--- a/src/cryptenroll/cryptenroll-fido2.c
+++ b/src/cryptenroll/cryptenroll-fido2.c
@@ -33,10 +33,10 @@ int load_volume_key_fido2(
cd_node,
device,
/* until= */ 0,
- /* headless= */ false,
+ "cryptenroll.fido2-pin",
+ ASK_PASSWORD_PUSH_CACHE|ASK_PASSWORD_ACCEPT_CACHED,
&decrypted_key,
- &decrypted_key_size,
- ASK_PASSWORD_PUSH_CACHE|ASK_PASSWORD_ACCEPT_CACHED);
+ &decrypted_key_size);
if (r == -EAGAIN)
return log_error_errno(r, "FIDO2 token does not exist, or UV is blocked. Please try again.");
if (r < 0)
@@ -97,6 +97,7 @@ int enroll_fido2(
/* user_display_name= */ node,
/* user_icon_name= */ NULL,
/* askpw_icon_name= */ "drive-harddisk",
+ /* askpw_credential= */ "cryptenroll.fido2-pin",
lock_with,
cred_alg,
&cid, &cid_size,
diff --git a/src/cryptenroll/cryptenroll-list.c b/src/cryptenroll/cryptenroll-list.c
index d21df71..00a1a8e 100644
--- a/src/cryptenroll/cryptenroll-list.c
+++ b/src/cryptenroll/cryptenroll-list.c
@@ -114,7 +114,7 @@ int list_enrolled(struct crypt_device *cd) {
return table_log_add_error(r);
}
- if (table_get_rows(t) <= 1) {
+ if (table_isempty(t)) {
log_info("No slots found.");
return 0;
}
diff --git a/src/cryptenroll/cryptenroll-password.c b/src/cryptenroll/cryptenroll-password.c
index c35b609..a9bd8a1 100644
--- a/src/cryptenroll/cryptenroll-password.c
+++ b/src/cryptenroll/cryptenroll-password.c
@@ -38,9 +38,8 @@ int load_volume_key_password(
return log_error_errno(r, "Password from environment variable $PASSWORD did not work: %m");
} else {
AskPasswordFlags ask_password_flags = ASK_PASSWORD_PUSH_CACHE|ASK_PASSWORD_ACCEPT_CACHED;
- _cleanup_free_ char *question = NULL, *disk_path = NULL;
+ _cleanup_free_ char *question = NULL, *id = NULL, *disk_path = NULL;
unsigned i = 5;
- const char *id;
question = strjoin("Please enter current passphrase for disk ", cd_node, ":");
if (!question)
@@ -50,7 +49,17 @@ int load_volume_key_password(
if (!disk_path)
return log_oom();
- id = strjoina("cryptsetup:", disk_path);
+ id = strjoin("cryptenroll:", disk_path);
+ if (!id)
+ return log_oom();
+
+ AskPasswordRequest req = {
+ .message = question,
+ .icon = "drive-harddisk",
+ .id = id,
+ .keyring = "cryptenroll",
+ .credential = "cryptenroll.passphrase",
+ };
for (;;) {
_cleanup_strv_free_erase_ char **passwords = NULL;
@@ -59,10 +68,7 @@ int load_volume_key_password(
return log_error_errno(SYNTHETIC_ERRNO(ENOKEY),
"Too many attempts, giving up.");
- r = ask_password_auto(
- question, "drive-harddisk", id, "cryptenroll", "cryptenroll.passphrase", USEC_INFINITY,
- ask_password_flags,
- &passwords);
+ r = ask_password_auto(&req, USEC_INFINITY, ask_password_flags, &passwords);
if (r < 0)
return log_error_errno(r, "Failed to query password: %m");
@@ -105,9 +111,8 @@ int enroll_password(
if (r < 0)
return log_error_errno(r, "Failed to acquire password from environment: %m");
if (r == 0) {
- _cleanup_free_ char *disk_path = NULL;
+ _cleanup_free_ char *disk_path = NULL, *id = NULL;
unsigned i = 5;
- const char *id;
assert_se(node = crypt_get_device_name(cd));
@@ -117,7 +122,16 @@ int enroll_password(
if (!disk_path)
return log_oom();
- id = strjoina("cryptsetup:", disk_path);
+ id = strjoin("cryptenroll-new:", disk_path);
+ if (!id)
+ return log_oom();
+
+ AskPasswordRequest req = {
+ .icon = "drive-harddisk",
+ .id = id,
+ .keyring = "cryptenroll",
+ .credential = "cryptenroll.new-passphrase",
+ };
for (;;) {
_cleanup_strv_free_erase_ char **passwords = NULL, **passwords2 = NULL;
@@ -131,7 +145,9 @@ int enroll_password(
if (!question)
return log_oom();
- r = ask_password_auto(question, "drive-harddisk", id, "cryptenroll", "cryptenroll.new-passphrase", USEC_INFINITY, 0, &passwords);
+ req.message = question;
+
+ r = ask_password_auto(&req, USEC_INFINITY, /* flags= */ 0, &passwords);
if (r < 0)
return log_error_errno(r, "Failed to query password: %m");
@@ -142,7 +158,9 @@ int enroll_password(
if (!question)
return log_oom();
- r = ask_password_auto(question, "drive-harddisk", id, "cryptenroll", "cryptenroll.new-passphrase", USEC_INFINITY, 0, &passwords2);
+ req.message = question;
+
+ r = ask_password_auto(&req, USEC_INFINITY, /* flags= */ 0, &passwords2);
if (r < 0)
return log_error_errno(r, "Failed to query password: %m");
diff --git a/src/cryptenroll/cryptenroll-pkcs11.c b/src/cryptenroll/cryptenroll-pkcs11.c
index 54b6b86..1e4be00 100644
--- a/src/cryptenroll/cryptenroll-pkcs11.c
+++ b/src/cryptenroll/cryptenroll-pkcs11.c
@@ -6,7 +6,30 @@
#include "memory-util.h"
#include "openssl-util.h"
#include "pkcs11-util.h"
-#include "random-util.h"
+
+static int uri_set_private_class(const char *uri, char **ret_uri) {
+ _cleanup_(sym_p11_kit_uri_freep) P11KitUri *p11kit_uri = NULL;
+ _cleanup_free_ char *private_uri = NULL;
+ int r;
+
+ r = uri_from_string(uri, &p11kit_uri);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse PKCS#11 URI '%s': %m", uri);
+
+ if (sym_p11_kit_uri_get_attribute(p11kit_uri, CKA_CLASS)) {
+ CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE attribute = { CKA_CLASS, &class, sizeof(class) };
+
+ if (sym_p11_kit_uri_set_attribute(p11kit_uri, &attribute) != P11_KIT_URI_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set class for URI '%s'.", uri);
+
+ if (sym_p11_kit_uri_format(p11kit_uri, P11_KIT_URI_FOR_ANY, &private_uri) != P11_KIT_URI_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to format PKCS#11 URI.");
+ }
+
+ *ret_uri = TAKE_PTR(private_uri);
+ return 0;
+}
int enroll_pkcs11(
struct crypt_device *cd,
@@ -17,14 +40,13 @@ int enroll_pkcs11(
_cleanup_(erase_and_freep) void *decrypted_key = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
- _cleanup_free_ char *keyslot_as_string = NULL;
- size_t decrypted_key_size, encrypted_key_size;
- _cleanup_free_ void *encrypted_key = NULL;
- _cleanup_(X509_freep) X509 *cert = NULL;
+ _cleanup_free_ char *keyslot_as_string = NULL, *private_uri = NULL;
+ size_t decrypted_key_size, saved_key_size;
+ _cleanup_free_ void *saved_key = NULL;
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
ssize_t base64_encoded_size;
const char *node;
- EVP_PKEY *pkey;
- int keyslot, r;
+ int r;
assert_se(cd);
assert_se(volume_key);
@@ -33,31 +55,20 @@ int enroll_pkcs11(
assert_se(node = crypt_get_device_name(cd));
- r = pkcs11_acquire_certificate(uri, "volume enrollment operation", "drive-harddisk", &cert, NULL);
+ r = pkcs11_acquire_public_key(
+ uri,
+ "volume enrollment operation",
+ "drive-harddisk",
+ "cryptenroll.pkcs11-pin",
+ /* askpw_flags= */ 0,
+ &pkey,
+ /* ret_pin_used= */ NULL);
if (r < 0)
return r;
- pkey = X509_get0_pubkey(cert);
- if (!pkey)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate.");
-
- r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size);
+ r = pkey_generate_volume_keys(pkey, &decrypted_key, &decrypted_key_size, &saved_key, &saved_key_size);
if (r < 0)
- return log_error_errno(r, "Failed to determine RSA public key size.");
-
- log_debug("Generating %zu bytes random key.", decrypted_key_size);
-
- decrypted_key = malloc(decrypted_key_size);
- if (!decrypted_key)
- return log_oom();
-
- r = crypto_random_bytes(decrypted_key, decrypted_key_size);
- if (r < 0)
- return log_error_errno(r, "Failed to generate random key: %m");
-
- r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
- if (r < 0)
- return log_error_errno(r, "Failed to encrypt key: %m");
+ return log_error_errno(r, "Failed to generate volume keys: %m");
/* Let's base64 encode the key to use, for compat with homed (and it's easier to type it in by
* keyboard, if that might ever end up being necessary.) */
@@ -69,7 +80,7 @@ int enroll_pkcs11(
if (r < 0)
return log_error_errno(r, "Failed to set minimal PBKDF: %m");
- keyslot = crypt_keyslot_add_by_volume_key(
+ int keyslot = crypt_keyslot_add_by_volume_key(
cd,
CRYPT_ANY_SLOT,
volume_key,
@@ -82,12 +93,19 @@ int enroll_pkcs11(
if (asprintf(&keyslot_as_string, "%i", keyslot) < 0)
return log_oom();
+ /* Change 'type=cert' or 'type=public' in the provided URI to 'type=private' before storing in
+ a LUKS2 header. This allows users to use output of some PKCS#11 tools directly without
+ modifications. */
+ r = uri_set_private_class(uri, &private_uri);
+ if (r < 0)
+ return r;
+
r = json_build(&v,
- JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-pkcs11")),
- JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
- JSON_BUILD_PAIR("pkcs11-uri", JSON_BUILD_STRING(uri)),
- JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size))));
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-pkcs11")),
+ JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
+ JSON_BUILD_PAIR("pkcs11-uri", JSON_BUILD_STRING(private_uri ?: uri)),
+ JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(saved_key, saved_key_size))));
if (r < 0)
return log_error_errno(r, "Failed to prepare PKCS#11 JSON token object: %m");
diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c
index 2d93e13..1ee3525 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"
@@ -25,7 +28,7 @@ static int search_policy_hash(
if (hash_size == 0)
return 0;
- for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token ++) {
+ for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_free_ void *thash = NULL;
size_t thash_size = 0;
@@ -51,7 +54,7 @@ static int search_policy_hash(
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"TPM2 token data lacks 'tpm2-policy-hash' field.");
- r = unhexmem(json_variant_string(w), SIZE_MAX, &thash, &thash_size);
+ r = unhexmem(json_variant_string(w), &thash, &thash_size);
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid base64 data in 'tpm2-policy-hash' field.");
@@ -84,28 +87,29 @@ static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) {
return log_error_errno(
SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up.");
+ AskPasswordRequest req = {
+ .message = "Please enter TPM2 PIN:",
+ .icon = "drive-harddisk",
+ .keyring = "tpm2-pin",
+ .credential = "cryptenroll.new-tpm2-pin",
+ };
+
pin = strv_free_erase(pin);
r = ask_password_auto(
- "Please enter TPM2 PIN:",
- "drive-harddisk",
- NULL,
- "tpm2-pin",
- "cryptenroll.tpm2-pin",
- USEC_INFINITY,
- 0,
+ &req,
+ /* until= */ USEC_INFINITY,
+ /* flags= */ 0,
&pin);
if (r < 0)
return log_error_errno(r, "Failed to ask for user pin: %m");
assert(strv_length(pin) == 1);
+ req.message = "Please enter TPM2 PIN (repeat):";
+
r = ask_password_auto(
- "Please enter TPM2 PIN (repeat):",
- "drive-harddisk",
- NULL,
- "tpm2-pin",
- "cryptenroll.tpm2-pin",
+ &req,
USEC_INFINITY,
- 0,
+ /* flags= */ 0,
&pin2);
if (r < 0)
return log_error_errno(r, "Failed to ask for user pin: %m");
@@ -129,6 +133,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,
+ "cryptenroll.tpm2-pin",
+ /* askpw_flags= */ 0,
+ &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,
@@ -137,22 +249,22 @@ int enroll_tpm2(struct crypt_device *cd,
const char *device_key,
Tpm2PCRValue *hash_pcr_values,
size_t n_hash_pcr_values,
- const char *pubkey_path,
+ const char *pcr_pubkey_path,
+ bool load_pcr_pubkey,
uint32_t pubkey_pcr_mask,
const char *signature_path,
bool use_pin,
- const char *pcrlock_path) {
+ const char *pcrlock_path,
+ int *ret_slot_to_wipe) {
- _cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *signature_json = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
- _cleanup_free_ void *srk_buf = NULL;
- size_t secret_size, blob_size, pubkey_size = 0, srk_buf_size = 0;
- _cleanup_free_ void *blob = NULL, *pubkey = NULL;
+ _cleanup_(iovec_done) struct iovec srk = {}, blob = {}, pubkey = {};
+ _cleanup_(iovec_done_erase) struct iovec secret = {};
const char *node;
_cleanup_(erase_and_freep) char *pin_str = NULL;
ssize_t base64_encoded_size;
- int r, keyslot;
+ int r, keyslot, slot_to_wipe = -1;
TPM2Flags flags = 0;
uint8_t binary_salt[SHA256_DIGEST_SIZE] = {};
/*
@@ -168,6 +280,7 @@ int enroll_tpm2(struct crypt_device *cd,
assert(volume_key_size > 0);
assert(tpm2_pcr_values_valid(hash_pcr_values, n_hash_pcr_values));
assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask));
+ assert(ret_slot_to_wipe);
assert_se(node = crypt_get_device_name(cd));
@@ -194,27 +307,33 @@ int enroll_tpm2(struct crypt_device *cd,
}
TPM2B_PUBLIC public = {};
- r = tpm2_load_pcr_public_key(pubkey_path, &pubkey, &pubkey_size);
- if (r < 0) {
- if (pubkey_path || signature_path || r != -ENOENT)
- return log_error_errno(r, "Failed to read TPM PCR public key: %m");
-
- log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
- pubkey_pcr_mask = 0;
- } else {
- r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public);
- if (r < 0)
- return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
+ /* Load the PCR public key if specified explicitly, or if no pcrlock policy was specified and
+ * automatic loading of PCR public keys wasn't disabled explicitly. The reason we turn this off when
+ * pcrlock is configured is simply that we currently not support both in combination. */
+ if (pcr_pubkey_path || (load_pcr_pubkey && !pcrlock_path)) {
+ r = tpm2_load_pcr_public_key(pcr_pubkey_path, &pubkey.iov_base, &pubkey.iov_len);
+ if (r < 0) {
+ if (pcr_pubkey_path || signature_path || r != -ENOENT)
+ return log_error_errno(r, "Failed to read TPM PCR public key: %m");
+
+ log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
+ pubkey_pcr_mask = 0;
+ } else {
+ r = tpm2_tpm2b_public_from_pem(pubkey.iov_base, pubkey.iov_len, &public);
+ if (r < 0)
+ return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
- if (signature_path) {
- /* Also try to load the signature JSON object, to verify that our enrollment will work.
- * This is optional however, skip it if it's not explicitly provided. */
+ if (signature_path) {
+ /* Also try to load the signature JSON object, to verify that our enrollment will work.
+ * This is optional however, skip it if it's not explicitly provided. */
- r = tpm2_load_pcr_signature(signature_path, &signature_json);
- if (r < 0)
- return log_debug_errno(r, "Failed to read TPM PCR signature: %m");
+ r = tpm2_load_pcr_signature(signature_path, &signature_json);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read TPM PCR signature: %m");
+ }
}
- }
+ } else
+ pubkey_pcr_mask = 0;
bool any_pcr_value_specified = tpm2_pcr_values_has_any_values(hash_pcr_values, n_hash_pcr_values);
@@ -223,6 +342,8 @@ int enroll_tpm2(struct crypt_device *cd,
r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy);
if (r < 0)
return r;
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Couldn't find pcrlock policy %s.", pcrlock_path);
any_pcr_value_specified = true;
flags |= TPM2_FLAGS_USE_PCRLOCK;
@@ -252,8 +373,10 @@ int enroll_tpm2(struct crypt_device *cd,
uint16_t hash_pcr_bank = 0;
uint32_t hash_pcr_mask = 0;
+
if (n_hash_pcr_values > 0) {
size_t hash_count;
+
r = tpm2_pcr_values_hash_count(hash_pcr_values, n_hash_pcr_values, &hash_count);
if (r < 0)
return log_error_errno(r, "Could not get hash count: %m");
@@ -261,17 +384,28 @@ int enroll_tpm2(struct crypt_device *cd,
if (hash_count > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected.");
+ /* If we use a literal PCR value policy, derive the bank to use from the algorithm specified on the hash values */
hash_pcr_bank = hash_pcr_values[0].hash;
r = tpm2_pcr_values_to_mask(hash_pcr_values, n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask);
if (r < 0)
return log_error_errno(r, "Could not get hash mask: %m");
+ } else if (pubkey_pcr_mask != 0) {
+
+ /* If no literal PCR value policy is used, then let's determine the mask to use automatically
+ * from the measurements of the TPM. */
+ r = tpm2_get_best_pcr_bank(
+ tpm2_context,
+ pubkey_pcr_mask,
+ &hash_pcr_bank);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine best PCR bank: %m");
}
TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
r = tpm2_calculate_sealing_policy(
hash_pcr_values,
n_hash_pcr_values,
- pubkey ? &public : NULL,
+ iovec_is_set(&pubkey) ? &public : NULL,
use_pin,
pcrlock_path ? &pcrlock_policy : NULL,
&policy);
@@ -283,21 +417,21 @@ int enroll_tpm2(struct crypt_device *cd,
seal_key_handle,
&device_key_public,
/* attributes= */ NULL,
- /* secret= */ NULL, /* secret_size= */ 0,
+ /* secret= */ NULL,
&policy,
pin_str,
- &secret, &secret_size,
- &blob, &blob_size,
- &srk_buf, &srk_buf_size);
+ &secret,
+ &blob,
+ &srk);
else
r = tpm2_seal(tpm2_context,
seal_key_handle,
&policy,
pin_str,
- &secret, &secret_size,
- &blob, &blob_size,
+ &secret,
+ &blob,
/* ret_primary_alg= */ NULL,
- &srk_buf, &srk_buf_size);
+ &srk);
if (r < 0)
return log_error_errno(r, "Failed to seal to TPM2: %m");
@@ -307,39 +441,42 @@ int enroll_tpm2(struct crypt_device *cd,
log_debug_errno(r, "PCR policy hash not yet enrolled, enrolling now.");
else if (r < 0)
return r;
- else {
+ else if (use_pin) {
+ log_debug("This PCR set is already enrolled, re-enrolling anyway to update PIN.");
+ slot_to_wipe = r;
+ } else {
log_info("This PCR set is already enrolled, executing no operation.");
+ *ret_slot_to_wipe = slot_to_wipe;
return r; /* return existing keyslot, so that wiping won't kill it */
}
/* If possible, verify the sealed data object. */
- if ((!pubkey || signature_json) && !any_pcr_value_specified && !device_key) {
- _cleanup_(erase_and_freep) void *secret2 = NULL;
- size_t secret2_size;
+ if ((!iovec_is_set(&pubkey) || signature_json) && !any_pcr_value_specified && !device_key) {
+ _cleanup_(iovec_done_erase) struct iovec secret2 = {};
log_debug("Unsealing for verification...");
r = tpm2_unseal(tpm2_context,
hash_pcr_mask,
hash_pcr_bank,
- pubkey, pubkey_size,
+ &pubkey,
pubkey_pcr_mask,
signature_json,
pin_str,
pcrlock_path ? &pcrlock_policy : NULL,
/* primary_alg= */ 0,
- blob, blob_size,
- policy.buffer, policy.size,
- srk_buf, srk_buf_size,
- &secret2, &secret2_size);
+ &blob,
+ &IOVEC_MAKE(policy.buffer, policy.size),
+ &srk,
+ &secret2);
if (r < 0)
return log_error_errno(r, "Failed to unseal secret using TPM2: %m");
- if (memcmp_nn(secret, secret_size, secret2, secret2_size) != 0)
+ if (iovec_memcmp(&secret, &secret2) != 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 seal/unseal verification failed.");
}
/* let's base64 encode the key to use, for compat with homed (and it's easier to every type it in by keyboard, if that might end up being necessary. */
- base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+ base64_encoded_size = base64mem(secret.iov_base, secret.iov_len, &base64_encoded);
if (base64_encoded_size < 0)
return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
@@ -361,14 +498,14 @@ int enroll_tpm2(struct crypt_device *cd,
keyslot,
hash_pcr_mask,
hash_pcr_bank,
- pubkey, pubkey_size,
+ &pubkey,
pubkey_pcr_mask,
/* primary_alg= */ 0,
- blob, blob_size,
- policy.buffer, policy.size,
- use_pin ? binary_salt : NULL,
- use_pin ? sizeof(binary_salt) : 0,
- srk_buf, srk_buf_size,
+ &blob,
+ &IOVEC_MAKE(policy.buffer, policy.size),
+ use_pin ? &IOVEC_MAKE(binary_salt, sizeof(binary_salt)) : NULL,
+ &srk,
+ pcrlock_path ? &pcrlock_policy.nv_handle : NULL,
flags,
&v);
if (r < 0)
@@ -379,5 +516,7 @@ int enroll_tpm2(struct crypt_device *cd,
return log_error_errno(r, "Failed to add TPM2 JSON token to LUKS2 header: %m");
log_info("New TPM2 token enrolled as key slot %i.", keyslot);
+
+ *ret_slot_to_wipe = slot_to_wipe;
return keyslot;
}
diff --git a/src/cryptenroll/cryptenroll-tpm2.h b/src/cryptenroll/cryptenroll-tpm2.h
index 2fbcdd4..d722ed6 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 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 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_pcr_values, size_t n_hash_pcr_values, const char *pubkey_path, bool load_pcr_pubkey, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path, int *ret_slot_to_wipe);
#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_pcr_values, size_t n_hash_pcr_values, const char *pubkey_path, bool load_pcr_pubkey, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path, int *ret_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 1cb6652..04352bf 100644
--- a/src/cryptenroll/cryptenroll.c
+++ b/src/cryptenroll/cryptenroll.c
@@ -1,8 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
+#include <sys/mman.h>
#include "ask-password-api.h"
+#include "blockdev-util.h"
#include "build.h"
#include "cryptenroll-fido2.h"
#include "cryptenroll-list.h"
@@ -13,6 +15,7 @@
#include "cryptenroll-wipe.h"
#include "cryptenroll.h"
#include "cryptsetup-util.h"
+#include "devnum-util.h"
#include "env-util.h"
#include "escape.h"
#include "fileio.h"
@@ -33,6 +36,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;
@@ -42,6 +46,7 @@ static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL;
static size_t arg_tpm2_n_hash_pcr_values = 0;
static bool arg_tpm2_pin = false;
static char *arg_tpm2_public_key = NULL;
+static bool arg_tpm2_load_public_key = true;
static uint32_t arg_tpm2_public_key_pcr_mask = 0;
static char *arg_tpm2_signature = NULL;
static char *arg_tpm2_pcrlock = NULL;
@@ -61,6 +66,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);
@@ -98,6 +104,66 @@ static const char *const luks2_token_type_table[_ENROLL_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(luks2_token_type, EnrollType);
+static int determine_default_node(void) {
+ int r;
+
+ /* If no device is specified we'll default to the backing device of /var/.
+ *
+ * Why /var/ and not just / you ask?
+ *
+ * On most systems /var/ is going to be on the root fs, hence the outcome is usually the same.
+ *
+ * However, on systems where / and /var/ are separate it makes more sense to default to /var/ because
+ * that's where the persistent and variable data is placed (i.e. where LUKS should be used) while /
+ * doesn't really have to be variable and could as well be immutable or ephemeral. Hence /var/ should
+ * be a better default.
+ *
+ * Or to say this differently: it makes sense to support well systems with /var/ being on /. It also
+ * makes sense to support well systems with them being separate, and /var/ being variable and
+ * persistent. But any other kind of system appears much less interesting to support, and in that
+ * case people should just specify the device name explicitly. */
+
+ dev_t devno;
+ r = get_block_device("/var", &devno);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine block device backing /var/: %m");
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
+ "File system /var/ is on not backed by a (single) whole block device.");
+
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ r = sd_device_new_from_devnum(&dev, 'b', devno);
+ if (r < 0)
+ return log_error_errno(r, "Unable to access backing block device for /var/: %m");
+
+ const char *dm_uuid;
+ r = sd_device_get_property_value(dev, "DM_UUID", &dm_uuid);
+ if (r == -ENOENT)
+ return log_error_errno(r, "Backing block device of /var/ is not a DM device: %m");
+ if (r < 0)
+ return log_error_errno(r, "Unable to query DM_UUID udev property of backing block device for /var/: %m");
+
+ if (!startswith(dm_uuid, "CRYPT-LUKS2-"))
+ return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "Block device backing /var/ is not a LUKS2 device.");
+
+ _cleanup_(sd_device_unrefp) sd_device *origin = NULL;
+ r = block_device_get_originating(dev, &origin);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get originating device of LUKS2 device backing /var/: %m");
+
+ const char *dp;
+ r = sd_device_get_devname(origin, &dp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get device path for LUKS2 device backing /var/: %m");
+
+ r = free_and_strdup_warn(&arg_node, dp);
+ if (r < 0)
+ return r;
+
+ log_info("No device specified, defaulting to '%s'.", arg_node);
+ return 0;
+}
+
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
@@ -106,7 +172,7 @@ static int help(void) {
if (r < 0)
return log_oom();
- printf("%1$s [OPTIONS...] BLOCK-DEVICE\n\n"
+ printf("%1$s [OPTIONS...] [BLOCK-DEVICE]\n\n"
"%5$sEnroll a security token or authentication credential to a LUKS volume.%6$s\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
@@ -117,6 +183,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"
@@ -172,6 +240,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,
@@ -197,6 +266,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 },
@@ -304,6 +374,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;
@@ -409,9 +499,17 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_TPM2_PUBLIC_KEY:
+ /* an empty argument disables loading a public key */
+ if (isempty(optarg)) {
+ arg_tpm2_load_public_key = false;
+ arg_tpm2_public_key = mfree(arg_tpm2_public_key);
+ break;
+ }
+
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_public_key);
if (r < 0)
return r;
+ arg_tpm2_load_public_key = true;
break;
@@ -507,17 +605,23 @@ static int parse_argv(int argc, char *argv[]) {
}
}
- if (optind >= argc)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "No block device node specified, refusing.");
-
if (argc > optind+1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Too many arguments, refusing.");
- r = parse_path_argument(argv[optind], false, &arg_node);
- if (r < 0)
- return r;
+ if (optind < argc) {
+ r = parse_path_argument(argv[optind], false, &arg_node);
+ if (r < 0)
+ return r;
+ } else {
+ if (wipe_requested())
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Wiping requested and no block device node specified, refusing.");
+
+ r = determine_default_node();
+ if (r < 0)
+ return r;
+ }
if (arg_enroll_type == ENROLL_FIDO2) {
@@ -533,31 +637,33 @@ static int parse_argv(int argc, char *argv[]) {
}
}
- if (auto_pcrlock) {
- assert(!arg_tpm2_pcrlock);
+ if (arg_enroll_type == ENROLL_TPM2) {
+ if (auto_pcrlock) {
+ assert(!arg_tpm2_pcrlock);
- r = tpm2_pcrlock_search_file(NULL, NULL, &arg_tpm2_pcrlock);
- if (r < 0) {
- if (r != -ENOENT)
- log_warning_errno(r, "Search for pcrlock.json failed, assuming it does not exist: %m");
- } else
- log_info("Automatically using pcrlock policy '%s'.", arg_tpm2_pcrlock);
- }
+ r = tpm2_pcrlock_search_file(NULL, NULL, &arg_tpm2_pcrlock);
+ if (r < 0) {
+ if (r != -ENOENT)
+ log_warning_errno(r, "Search for pcrlock.json failed, assuming it does not exist: %m");
+ } else
+ log_info("Automatically using pcrlock policy '%s'.", arg_tpm2_pcrlock);
+ }
- if (auto_public_key_pcr_mask) {
- assert(arg_tpm2_public_key_pcr_mask == 0);
- arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT);
- }
+ if (auto_public_key_pcr_mask) {
+ assert(arg_tpm2_public_key_pcr_mask == 0);
+ arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT);
+ }
- if (auto_hash_pcr_values && !arg_tpm2_pcrlock) { /* Only lock to PCR 7 by default if no pcrlock policy is around (which is a better replacement) */
- assert(arg_tpm2_n_hash_pcr_values == 0);
+ if (auto_hash_pcr_values && !arg_tpm2_pcrlock) { /* Only lock to PCR 7 by default if no pcrlock policy is around (which is a better replacement) */
+ assert(arg_tpm2_n_hash_pcr_values == 0);
- if (!GREEDY_REALLOC_APPEND(
- arg_tpm2_hash_pcr_values,
- arg_tpm2_n_hash_pcr_values,
- &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}),
- 1))
- return log_oom();
+ if (!GREEDY_REALLOC_APPEND(
+ arg_tpm2_hash_pcr_values,
+ arg_tpm2_n_hash_pcr_values,
+ &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}),
+ 1))
+ return log_oom();
+ }
}
return 1;
@@ -571,7 +677,7 @@ static int check_for_homed(struct crypt_device *cd) {
/* Politely refuse operating on homed volumes. The enrolled tokens for the user record and the LUKS2
* volume should not get out of sync. */
- for (int token = 0; token < crypt_token_max(CRYPT_LUKS2); token ++) {
+ for (int token = 0; token < crypt_token_max(CRYPT_LUKS2); token++) {
r = cryptsetup_get_token_as_json(cd, token, "systemd-homed", NULL);
if (IN_SET(r, -ENOENT, -EINVAL, -EMEDIUMTYPE))
continue;
@@ -644,7 +750,7 @@ static int prepare_luks(
r = crypt_load(cd, CRYPT_LUKS2, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to load LUKS2 superblock: %m");
+ return log_error_errno(r, "Failed to load LUKS2 superblock of %s: %m", arg_node);
r = check_for_homed(cd);
if (r < 0)
@@ -666,6 +772,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;
@@ -674,8 +784,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:
@@ -696,16 +806,17 @@ static int run(int argc, char *argv[]) {
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
_cleanup_(erase_and_freep) void *vk = NULL;
size_t vks;
- int slot, r;
+ int slot, slot_to_wipe, r;
- log_show_color(true);
- log_parse_environment();
- log_open();
+ log_setup();
r = parse_argv(argc, argv);
if (r <= 0)
return r;
+ /* A delicious drop of snake oil */
+ (void) mlockall(MCL_FUTURE);
+
cryptsetup_enable_logging(NULL);
if (arg_enroll_type < 0)
@@ -734,9 +845,21 @@ static int run(int argc, char *argv[]) {
break;
case ENROLL_TPM2:
- slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_seal_key_handle, arg_tpm2_device_key, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin, arg_tpm2_pcrlock);
+ slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_seal_key_handle, arg_tpm2_device_key, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, arg_tpm2_public_key, arg_tpm2_load_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin, arg_tpm2_pcrlock, &slot_to_wipe);
+
+ if (slot >= 0 && slot_to_wipe >= 0) {
+ /* Updating PIN on an existing enrollment */
+ r = wipe_slots(
+ cd,
+ &slot_to_wipe,
+ /* n_explicit_slots= */ 1,
+ WIPE_EXPLICIT,
+ /* by_mask= */ 0,
+ /* except_slot= */ -1);
+ if (r < 0)
+ return r;
+ }
break;
-
case _ENROLL_TYPE_INVALID:
/* List enrolled slots if we are called without anything to enroll or wipe */
if (!wipe_requested())
diff --git a/src/cryptenroll/cryptenroll.h b/src/cryptenroll/cryptenroll.h
index 335d9cc..08ded3e 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;