]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptsetup: implement cryptsetup_token_open_pin for systemd-tpm2 LUKS2 token
authorJonas Witschel <diabonas@archlinux.org>
Wed, 25 May 2022 12:06:12 +0000 (14:06 +0200)
committerJonas Witschel <diabonas@archlinux.org>
Fri, 5 Aug 2022 10:19:57 +0000 (12:19 +0200)
This finishes the implementation started in commit
1f895adac287b5f1b6b854caa586093616ccc172 ("cryptsetup: add libcryptsetup TPM2
PIN support").

Note that the previous implementation took a shortcut by returning EOPNOTSUPP
instead of the correct ENOANO as per the cryptsetup documentation. This meant
that systemd-cryptsetup fell back to the non-plugin implementation in order to
ask for the PIN. Since this does not happen any more when returning ENOANO, we
need to ask for the PIN in attach_luks2_by_tpm2_via_plugin() instead like
attach_luks2_by_fido2_via_plugin() does.

src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-util.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-util.h
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
src/cryptsetup/cryptsetup.c

index 23df9749994783cb9e576c48a7106580a3ab22d1..bbc8a39c98b7d37e0caebbaf48ba9ab0744d0a6c 100644 (file)
@@ -35,22 +35,11 @@ static int log_debug_open_error(struct crypt_device *cd, int r) {
         return crypt_log_debug_errno(cd, r, TOKEN_NAME " open failed: %m.");
 }
 
-/*
- * This function is called from within following libcryptsetup calls
- * provided conditions further below are met:
- *
- * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-tpm2'):
- *
- * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
- *   (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
- *    and token is assigned to at least single keyslot).
- *
- * - if plugin defines validate function (see cryptsetup_token_validate below) it must have
- *   passed the check (aka return 0)
- */
-_public_ int cryptsetup_token_open(
+_public_ int cryptsetup_token_open_pin(
                 struct crypt_device *cd, /* is always LUKS2 context */
                 int token /* is always >= 0 */,
+                const char *pin,
+                size_t pin_size,
                 char **password, /* freed by cryptsetup_token_buffer_free */
                 size_t *password_len,
                 void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
@@ -66,8 +55,9 @@ _public_ int cryptsetup_token_open(
         _cleanup_free_ void *blob = NULL, *policy_hash = NULL;
         _cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL;
         _cleanup_(erase_and_freep) void *decrypted_key = NULL;
-        _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+        _cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL;
 
+        assert(!pin || pin_size);
         assert(password);
         assert(password_len);
         assert(token >= 0);
@@ -77,6 +67,10 @@ _public_ int cryptsetup_token_open(
         assert(token == r);
         assert(json);
 
+        r = crypt_normalize_pin(pin, pin_size, &pin_string);
+        if (r < 0)
+                return crypt_log_debug_errno(cd, r, "Can not normalize PIN: %m");
+
         if (usrptr)
                 params = *(systemd_tpm2_plugin_params *)usrptr;
 
@@ -105,6 +99,7 @@ _public_ int cryptsetup_token_open(
                         policy_hash,
                         policy_hash_size,
                         flags,
+                        pin_string,
                         &decrypted_key,
                         &decrypted_key_size);
         if (r < 0)
@@ -122,6 +117,29 @@ _public_ int cryptsetup_token_open(
         return 0;
 }
 
+/*
+ * This function is called from within following libcryptsetup calls
+ * provided conditions further below are met:
+ *
+ * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-tpm2'):
+ *
+ * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
+ *   (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
+ *    and token is assigned to at least single keyslot).
+ *
+ * - if plugin defines validate function (see cryptsetup_token_validate below) it must have
+ *   passed the check (aka return 0)
+ */
+_public_ int cryptsetup_token_open(
+                struct crypt_device *cd, /* is always LUKS2 context */
+                int token /* is always >= 0 */,
+                char **password, /* freed by cryptsetup_token_buffer_free */
+                size_t *password_len,
+                void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
+
+        return cryptsetup_token_open_pin(cd, token, NULL, 0, password, password_len, usrptr);
+}
+
 /*
  * libcryptsetup callback for memory deallocation of 'password' parameter passed in
  * any crypt_token_open_* plugin function
index f4086ae367b415ec9f58d7c63ee95813b2d9fc68..e305d8ba7958f8676adfafa3a085b8a426dca9b2 100644 (file)
@@ -56,3 +56,29 @@ int crypt_dump_hex_string(const char *hex_str, char **ret_dump_str) {
 
         return 0;
 }
+
+int crypt_normalize_pin(const void *pin, size_t pin_size, char **ret_pin_string) {
+
+        _cleanup_free_ char *pin_string = NULL;
+
+        assert(pin || !pin_size);
+        assert(ret_pin_string);
+
+        if (!pin) {
+                *ret_pin_string = NULL;
+                return 0;
+        }
+
+        /* Refuse embedded NULL bytes, but allow trailing NULL */
+        if (memchr(pin, 0, pin_size - 1))
+                return -EINVAL;
+
+        /* Enforce trailing NULL byte if missing */
+        pin_string = memdup_suffix0(pin, pin_size);
+        if (!pin_string)
+                return -ENOMEM;
+
+        *ret_pin_string = TAKE_PTR(pin_string);
+
+        return 0;
+}
index 57ffca136f4740aa3528b4332b2d137c9c557b35..146beffb3054ce64bc7701af11ab914531591126 100644 (file)
@@ -36,3 +36,5 @@ int crypt_dump_buffer_to_hex_string(
                 char **ret_dump_str);
 
 int crypt_dump_hex_string(const char *hex_str, char **ret_dump_str);
+
+int crypt_normalize_pin(const void *pin, size_t pin_size, char **ret_pin_string);
index 0d6e4bc0f8c676a3ddf7a9a1f859701be27eb326..f0286ec1bf4f76864962a93e6fed17859c9b4c5b 100644 (file)
@@ -22,11 +22,11 @@ int acquire_luks2_key(
                 const void *policy_hash,
                 size_t policy_hash_size,
                 TPM2Flags flags,
+                const char *pin,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size) {
 
         _cleanup_free_ char *auto_device = NULL;
-        _cleanup_(erase_and_freep) char *pin_str = NULL;
         int r;
 
         assert(ret_decrypted_key);
@@ -42,22 +42,15 @@ int acquire_luks2_key(
                 device = auto_device;
         }
 
-        r = getenv_steal_erase("PIN", &pin_str);
-        if (r < 0)
-                return log_error_errno(r, "Failed to acquire PIN from environment: %m");
-        if (!r) {
-                /* PIN entry is not supported by plugin, let it fallback, possibly to sd-cryptsetup's
-                 * internal handling. */
-                if (flags & TPM2_FLAGS_USE_PIN)
-                        return -EOPNOTSUPP;
-        }
+        if ((flags & TPM2_FLAGS_USE_PIN) && !pin)
+                return -ENOANO;
 
         return tpm2_unseal(
                         device,
                         pcr_mask, pcr_bank,
                         primary_alg,
                         key_data, key_data_size,
-                        policy_hash, policy_hash_size, pin_str,
+                        policy_hash, policy_hash_size, pin,
                         ret_decrypted_key, ret_decrypted_key_size);
 }
 
index 34c93216eee18d14bf58a6244d4b8f9419fab406..5e3341802520354f227fb27484d1cba15655de36 100644 (file)
@@ -16,6 +16,7 @@ int acquire_luks2_key(
                 const void *policy_hash,
                 size_t policy_hash_size,
                 TPM2Flags flags,
+                const char *pin,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size);
 
index ea56f552f0d3ee18c22280565f92bc98a0a46a62..7a9878e6897be56bbbc716744d2835c21f7715cc 100644 (file)
@@ -1275,11 +1275,11 @@ static int make_tpm2_device_monitor(
 static int attach_luks2_by_tpm2_via_plugin(
                 struct crypt_device *cd,
                 const char *name,
+                usec_t until,
+                bool headless,
                 uint32_t flags) {
 
 #if HAVE_LIBCRYPTSETUP_PLUGINS
-        int r;
-
         systemd_tpm2_plugin_params params = {
                 .search_pcr_mask = arg_tpm2_pcr_mask,
                 .device = arg_tpm2_device
@@ -1289,11 +1289,17 @@ static int attach_luks2_by_tpm2_via_plugin(
                 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                        "Libcryptsetup has external plugins support disabled.");
 
-        r = crypt_activate_by_token_pin(cd, name, "systemd-tpm2", CRYPT_ANY_TOKEN, NULL, 0, &params, flags);
-        if (r > 0) /* returns unlocked keyslot id on success */
-                r = 0;
-
-        return r;
+        return crypt_activate_by_token_pin_ask_password(
+                        cd,
+                        name,
+                        "systemd-tpm2",
+                        until,
+                        headless,
+                        &params,
+                        flags,
+                        "Please enter TPM2 PIN:",
+                        "tpm2-pin",
+                        "cryptsetup.tpm2-pin");
 #else
         return -EOPNOTSUPP;
 #endif
@@ -1354,7 +1360,9 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                                 return -EAGAIN; /* Mangle error code: let's make any form of TPM2 failure non-fatal. */
                         }
                 } else {
-                        r = attach_luks2_by_tpm2_via_plugin(cd, name, flags);
+                        r = attach_luks2_by_tpm2_via_plugin(cd, name, until, arg_headless, flags);
+                        if (r >= 0)
+                                return 0;
                         /* EAGAIN     means: no tpm2 chip found
                          * EOPNOTSUPP means: no libcryptsetup plugins support */
                         if (r == -ENXIO)