]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptsetup: Add optional support for linking volume key in keyring.
authorOndrej Kozina <okozina@redhat.com>
Wed, 31 Jan 2024 12:11:21 +0000 (13:11 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 13 Feb 2024 08:45:08 +0000 (09:45 +0100)
cryptsetup 2.7.0 adds feature to link effective volume key in custom
kernel keyring during device activation. It can be used later to pass
linked volume key to other services.

For example: kdump enabled systems installed on LUKS2 device.
This feature allows it to store volume key linked in a kernel keyring
to the kdump reserved memory and reuse it to reactivate LUKS2 device
in case of kernel crash.

man/crypttab.xml
meson.build
src/cryptsetup/cryptsetup.c

index 8994e4f0d22e47de9e5e947e974cdbcdea667dac..1d92745eb42ad9529f347193f0ebc45b74e0df78 100644 (file)
         <xi:include href="version-info.xml" xpointer="v243"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>link-volume-key=</option></term>
+
+        <listitem><para>Specifies the kernel keyring and key description
+        (see <citerefentry project='man-pages'><refentrytitle>keyrings</refentrytitle><manvolnum>7</manvolnum></citerefentry>)
+        where LUKS2 volume key gets linked during device activation. The kernel keyring
+        description and key description must be separated by <literal>::</literal>.</para>
+
+        <para>The kernel keyring part can be a string description or a predefined
+        kernel keyring prefixed with <literal>@</literal> (e.g.: to use <literal>@s</literal> session or
+        <literal>@u</literal> user keyring directly). The type prefix text in the kernel keyring description
+        is not required. The specified kernel keyring must already exist at the time of device activation.</para>
+
+        <para>The key part is a string description optionally prefixed by a <literal>%key_type:</literal>.
+        If no type is specified, the <literal>user</literal> type key is linked by default. See
+        <citerefentry project='man-pages'><refentrytitle>keyctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        for more information on key descriptions (KEY IDENTIFIERS section).</para>
+
+        <para>Note that the linked volume key is not cleaned up automatically when the device is detached.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>luks</option></term>
 
index 4ebe46dd2342bb9ed99dd41143dc56c10458fa9d..c031db78a7a7a8b729b9324dc4c903a209e4bf70 100644 (file)
@@ -1260,7 +1260,8 @@ foreach ident : ['crypt_set_metadata_size',
                  'crypt_token_max',
                  'crypt_reencrypt_init_by_passphrase',
                  'crypt_reencrypt',
-                 'crypt_set_data_offset']
+                 'crypt_set_data_offset',
+                 'crypt_set_keyring_to_link']
         have_ident = have and cc.has_function(
                 ident,
                 prefix : '#include <libcryptsetup.h>',
index 1a027300371fbf8e330e4b9d6d8dbfae0497eb55..a10b49dda0f390f7e599f3221c83a23fd7759fd5 100644 (file)
@@ -105,6 +105,9 @@ static bool arg_headless = false;
 static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC;
 static unsigned arg_tpm2_measure_pcr = UINT_MAX; /* This and the following field is about measuring the unlocked volume key to the local TPM */
 static char **arg_tpm2_measure_banks = NULL;
+static char *arg_link_keyring = NULL;
+static char *arg_link_key_type = NULL;
+static char *arg_link_key_description = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
@@ -118,6 +121,9 @@ STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_measure_banks, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_link_keyring, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_link_key_type, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_link_key_description, freep);
 
 static const char* const passphrase_type_table[_PASSPHRASE_TYPE_MAX] = {
         [PASSPHRASE_REGULAR] = "passphrase",
@@ -508,6 +514,56 @@ static int parse_one_option(const char *option) {
                 if (r < 0)
                         log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
 
+        } else if ((val = startswith(option, "link-volume-key="))) {
+#ifdef HAVE_CRYPT_SET_KEYRING_TO_LINK
+                const char *sep, *c;
+                _cleanup_free_ char *keyring = NULL, *key_type = NULL, *key_description = NULL;
+
+                /* Stick with cryptsetup --link-vk-to-keyring format
+                 * <keyring_description>::%<key_type>:<key_description>,
+                 * where %<key_type> is optional and defaults to 'user'.
+                 */
+                if (!(sep = strstr(val, "::")))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse link-volume-key= option value: %m");
+
+                /* cryptsetup (cli) supports <keyring_description> passed in various formats:
+                 * - well-known keyrings prefixed with '@' (@u user, @s session, etc)
+                 * - text descriptions prefixed with "%:" or "%keyring:".
+                 * - text desription with no prefix.
+                 * - numeric keyring id (ignored in current patch set). */
+                if (*val == '@' || *val == '%')
+                        keyring = strndup(val, sep - val);
+                else
+                        /* add type prefix if missing (crypt_set_keyring_to_link() expects it) */
+                        keyring = strnappend("%:", val, sep - val);
+                if (!keyring)
+                        return log_oom();
+
+                sep += 2;
+
+                /* %<key_type> is optional (and defaults to 'user') */
+                if (*sep == '%') {
+                        /* must be separated by colon */
+                        if (!(c = strchr(sep, ':')))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse link-volume-key= option value: %m");
+
+                        key_type = strndup(sep + 1, c - sep - 1);
+                        if (!key_type)
+                                return log_oom();
+
+                        sep = c + 1;
+                }
+
+                key_description = strdup(sep);
+                if (!key_description)
+                        return log_oom();
+
+                free_and_replace(arg_link_keyring, keyring);
+                free_and_replace(arg_link_key_type, key_type);
+                free_and_replace(arg_link_key_description, key_description);
+#else
+                log_error("Build lacks libcryptsetup support for linking volume keys in user specified kernel keyrings upon device activation, ignoring: %s", option);
+#endif
         } else if (!streq(option, "x-initrd.attach"))
                 log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
 
@@ -2287,6 +2343,15 @@ static int run(int argc, char *argv[]) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to load LUKS superblock on device %s: %m", crypt_get_device_name(cd));
 
+/* since cryptsetup 2.7.0 (Jan 2024) */
+#if HAVE_CRYPT_SET_KEYRING_TO_LINK
+                        if (arg_link_key_description) {
+                                r = crypt_set_keyring_to_link(cd, arg_link_key_description, NULL, arg_link_key_type, arg_link_keyring);
+                                if (r < 0)
+                                        log_warning_errno(r, "Failed to set keyring or key description to link volume key in, ignoring: %m");
+                        }
+#endif
+
                         if (arg_header) {
                                 r = crypt_set_data_device(cd, source);
                                 if (r < 0)