]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/cryptenroll/cryptenroll.c
Merge pull request #32434 from poettering/cryptenroll-prefer-var
[thirdparty/systemd.git] / src / cryptenroll / cryptenroll.c
index b2e4c0a5f5e01e90d9980773df4c0be31e237621..77b31477f120c93a5ee9eb5d39b43ff84db94bcd 100644 (file)
@@ -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"
 #include "string-table.h"
 #include "strv.h"
 #include "terminal-util.h"
-#include "tpm-pcr.h"
-#include "tpm2-util.h"
+#include "tpm2-pcr.h"
 
 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;
-static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
+static uint32_t arg_tpm2_seal_key_handle = 0;
+static char *arg_tpm2_device_key = NULL;
+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 uint32_t arg_tpm2_public_key_pcr_mask = UINT32_MAX;
+static uint32_t arg_tpm2_public_key_pcr_mask = 0;
 static char *arg_tpm2_signature = NULL;
+static char *arg_tpm2_pcrlock = NULL;
 static char *arg_node = NULL;
 static int *arg_wipe_slots = NULL;
 static size_t arg_n_wipe_slots = 0;
@@ -58,11 +65,15 @@ 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);
+STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device_key, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_wipe_slots, freep);
 
@@ -75,9 +86,9 @@ static bool wipe_requested(void) {
 static const char* const enroll_type_table[_ENROLL_TYPE_MAX] = {
         [ENROLL_PASSWORD] = "password",
         [ENROLL_RECOVERY] = "recovery",
-        [ENROLL_PKCS11] = "pkcs11",
-        [ENROLL_FIDO2] = "fido2",
-        [ENROLL_TPM2] = "tpm2",
+        [ENROLL_PKCS11]   = "pkcs11",
+        [ENROLL_FIDO2]    = "fido2",
+        [ENROLL_TPM2]     = "tpm2",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(enroll_type, EnrollType);
@@ -85,13 +96,73 @@ DEFINE_STRING_TABLE_LOOKUP(enroll_type, EnrollType);
 static const char *const luks2_token_type_table[_ENROLL_TYPE_MAX] = {
         /* ENROLL_PASSWORD has no entry here, as slots of this type do not have a token in the LUKS2 header */
         [ENROLL_RECOVERY] = "systemd-recovery",
-        [ENROLL_PKCS11] = "systemd-pkcs11",
-        [ENROLL_FIDO2] = "systemd-fido2",
-        [ENROLL_TPM2] = "systemd-tpm2",
+        [ENROLL_PKCS11]   = "systemd-pkcs11",
+        [ENROLL_FIDO2]    = "systemd-fido2",
+        [ENROLL_TPM2]     = "systemd-tpm2",
 };
 
 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: %m");
+
+        _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;
@@ -100,30 +171,43 @@ static int help(void) {
         if (r < 0)
                 return log_oom();
 
-        printf("%s [OPTIONS...] BLOCK-DEVICE\n"
-               "\n%sEnroll a security token or authentication credential to a LUKS volume.%s\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"
-               "     --password        Enroll a user-supplied password\n"
-               "     --recovery-key    Enroll a recovery key\n"
+               "     --wipe-slot=SLOT1,SLOT2,…\n"
+               "                       Wipe specified slots\n"
+               "\n%3$sUnlocking:%4$s\n"
                "     --unlock-key-file=PATH\n"
                "                       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"
+               "\n%3$sPKCS11 Enrollment:%4$s\n"
                "     --pkcs11-token-uri=URI\n"
                "                       Specify PKCS#11 security token URI\n"
-               "     --fido2-credential-algorithm=STRING\n"
-               "                       Specify COSE algorithm for FIDO2 credential\n"
+               "\n%3$sFIDO2 Enrollment:%4$s\n"
                "     --fido2-device=PATH\n"
                "                       Enroll a FIDO2-HMAC security token\n"
+               "     --fido2-credential-algorithm=STRING\n"
+               "                       Specify COSE algorithm for FIDO2 credential\n"
                "     --fido2-with-client-pin=BOOL\n"
                "                       Whether to require entering a PIN to unlock the volume\n"
                "     --fido2-with-user-presence=BOOL\n"
                "                       Whether to require user presence to unlock the volume\n"
                "     --fido2-with-user-verification=BOOL\n"
                "                       Whether to require user verification to unlock the volume\n"
+               "\n%3$sTPM2 Enrollment:%4$s\n"
                "     --tpm2-device=PATH\n"
                "                       Enroll a TPM2 device\n"
+               "     --tpm2-device-key=PATH\n"
+               "                       Enroll a TPM2 device using its public key\n"
+               "     --tpm2-seal-key-handle=HANDLE\n"
+               "                       Specify handle of key to use for sealing\n"
                "     --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
                "                       Specify TPM2 PCRs to seal against\n"
                "     --tpm2-public-key=PATH\n"
@@ -133,35 +217,40 @@ static int help(void) {
                "     --tpm2-signature=PATH\n"
                "                       Validate public key enrollment works with JSON signature\n"
                "                       file\n"
+               "     --tpm2-pcrlock=PATH\n"
+               "                       Specify pcrlock policy to lock against\n"
                "     --tpm2-with-pin=BOOL\n"
                "                       Whether to require entering a PIN to unlock the volume\n"
-               "     --wipe-slot=SLOT1,SLOT2,…\n"
-               "                       Wipe specified slots\n"
-               "\nSee the %s for details.\n",
+               "\nSee the %2$s for details.\n",
                program_invocation_short_name,
-               ansi_highlight(),
+               link,
+               ansi_underline(),
                ansi_normal(),
-               link);
+               ansi_highlight(),
+               ansi_normal());
 
         return 0;
 }
 
 static int parse_argv(int argc, char *argv[]) {
-
         enum {
                 ARG_VERSION = 0x100,
                 ARG_PASSWORD,
                 ARG_RECOVERY_KEY,
                 ARG_UNLOCK_KEYFILE,
                 ARG_UNLOCK_FIDO2_DEVICE,
+                ARG_UNLOCK_TPM2_DEVICE,
                 ARG_PKCS11_TOKEN_URI,
                 ARG_FIDO2_DEVICE,
                 ARG_TPM2_DEVICE,
+                ARG_TPM2_DEVICE_KEY,
+                ARG_TPM2_SEAL_KEY_HANDLE,
                 ARG_TPM2_PCRS,
                 ARG_TPM2_PUBLIC_KEY,
                 ARG_TPM2_PUBLIC_KEY_PCRS,
                 ARG_TPM2_SIGNATURE,
-                ARG_TPM2_PIN,
+                ARG_TPM2_PCRLOCK,
+                ARG_TPM2_WITH_PIN,
                 ARG_WIPE_SLOT,
                 ARG_FIDO2_WITH_PIN,
                 ARG_FIDO2_WITH_UP,
@@ -176,6 +265,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          },
@@ -183,15 +273,19 @@ static int parse_argv(int argc, char *argv[]) {
                 { "fido2-with-user-presence",     required_argument, NULL, ARG_FIDO2_WITH_UP         },
                 { "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV         },
                 { "tpm2-device",                  required_argument, NULL, ARG_TPM2_DEVICE           },
+                { "tpm2-device-key",              required_argument, NULL, ARG_TPM2_DEVICE_KEY       },
+                { "tpm2-seal-key-handle",         required_argument, NULL, ARG_TPM2_SEAL_KEY_HANDLE  },
                 { "tpm2-pcrs",                    required_argument, NULL, ARG_TPM2_PCRS             },
                 { "tpm2-public-key",              required_argument, NULL, ARG_TPM2_PUBLIC_KEY       },
                 { "tpm2-public-key-pcrs",         required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS  },
                 { "tpm2-signature",               required_argument, NULL, ARG_TPM2_SIGNATURE        },
-                { "tpm2-with-pin",                required_argument, NULL, ARG_TPM2_PIN              },
+                { "tpm2-pcrlock",                 required_argument, NULL, ARG_TPM2_PCRLOCK          },
+                { "tpm2-with-pin",                required_argument, NULL, ARG_TPM2_WITH_PIN         },
                 { "wipe-slot",                    required_argument, NULL, ARG_WIPE_SLOT             },
                 {}
         };
 
+        bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true, auto_pcrlock = true;
         int c, r;
 
         assert(argc >= 0);
@@ -279,6 +373,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;
 
@@ -355,15 +469,29 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
-                case ARG_TPM2_PCRS:
-                        r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_pcr_mask);
+                case ARG_TPM2_DEVICE_KEY:
+                        if (arg_enroll_type >= 0 || arg_tpm2_device_key)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Multiple operations specified at once, refusing.");
+
+
+                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_device_key);
                         if (r < 0)
                                 return r;
 
+                        arg_enroll_type = ENROLL_TPM2;
                         break;
 
-                case ARG_TPM2_PIN:
-                        r = parse_boolean_argument("--tpm2-with-pin=", optarg, &arg_tpm2_pin);
+                case ARG_TPM2_SEAL_KEY_HANDLE:
+                        r = safe_atou32_full(optarg, 16, &arg_tpm2_seal_key_handle);
+                        if (r < 0)
+                                return log_error_errno(r, "Could not parse TPM2 seal key handle index '%s': %m", optarg);
+
+                        break;
+
+                case ARG_TPM2_PCRS:
+                        auto_hash_pcr_values = false;
+                        r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values);
                         if (r < 0)
                                 return r;
 
@@ -377,6 +505,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_TPM2_PUBLIC_KEY_PCRS:
+                        auto_public_key_pcr_mask = false;
                         r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask);
                         if (r < 0)
                                 return r;
@@ -390,6 +519,21 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_TPM2_PCRLOCK:
+                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_pcrlock);
+                        if (r < 0)
+                                return r;
+
+                        auto_pcrlock = false;
+                        break;
+
+                case ARG_TPM2_WITH_PIN:
+                        r = parse_boolean_argument("--tpm2-with-pin=", optarg, &arg_tpm2_pin);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
                 case ARG_WIPE_SLOT: {
                         const char *p = optarg;
 
@@ -433,7 +577,7 @@ static int parse_argv(int argc, char *argv[]) {
                                         if (n > INT_MAX)
                                                 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Slot index out of range: %u", n);
 
-                                        a = reallocarray(arg_wipe_slots, sizeof(int), arg_n_wipe_slots + 1);
+                                        a = reallocarray(arg_wipe_slots, arg_n_wipe_slots + 1, sizeof(int));
                                         if (!a)
                                                 return log_oom();
 
@@ -452,34 +596,64 @@ static int parse_argv(int argc, char *argv[]) {
                 }
         }
 
-        if ((arg_enroll_type == ENROLL_FIDO2 && arg_unlock_type == UNLOCK_FIDO2)
-                        && !(arg_fido2_device && arg_unlock_fido2_device))
+        if (argc > optind+1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "When both enrolling and unlocking with FIDO2 tokens, automatic discovery is unsupported. "
-                                       "Please specify device paths for enrolling and unlocking respectively.");
+                                       "Too many arguments, refusing.");
+
+        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.");
 
-        if (arg_enroll_type == ENROLL_FIDO2 && !arg_fido2_device) {
-                r = fido2_find_device_auto(&arg_fido2_device);
+                r = determine_default_node();
                 if (r < 0)
                         return r;
         }
 
-        if (optind >= argc)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "No block device node specified, refusing.");
+        if (arg_enroll_type == ENROLL_FIDO2) {
 
-        if (argc > optind+1)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Too many arguments, refusing.");
+                if (arg_unlock_type == UNLOCK_FIDO2 && !(arg_fido2_device && arg_unlock_fido2_device))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "When both enrolling and unlocking with FIDO2 tokens, automatic discovery is unsupported. "
+                                               "Please specify device paths for enrolling and unlocking respectively.");
 
-        r = parse_path_argument(argv[optind], false, &arg_node);
-        if (r < 0)
-                return r;
+                if (!arg_fido2_device) {
+                        r = fido2_find_device_auto(&arg_fido2_device);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        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);
+        }
+
+        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 (arg_tpm2_pcr_mask == UINT32_MAX)
-                arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT;
-        if (arg_tpm2_public_key_pcr_mask == UINT32_MAX)
-                arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE;
+        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();
+        }
 
         return 1;
 }
@@ -492,7 +666,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;
@@ -522,7 +696,7 @@ static int load_volume_key_keyfile(
         r = read_full_file_full(
                         AT_FDCWD,
                         arg_unlock_keyfile,
-                        0,
+                        UINT64_MAX,
                         SIZE_MAX,
                         READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
                         NULL,
@@ -565,7 +739,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)
@@ -587,6 +761,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;
@@ -595,8 +773,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:
@@ -617,7 +795,7 @@ 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();
@@ -627,6 +805,9 @@ static int run(int argc, char *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)
@@ -655,9 +836,21 @@ static int run(int argc, char *argv[]) {
                 break;
 
         case ENROLL_TPM2:
-                slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_pcr_mask, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin);
+                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_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())