36e8a3
From 0977e6b34fb5f28fc94f1df32261742881fa9bbe Mon Sep 17 00:00:00 2001
36e8a3
From: Michal Sekletar <msekleta@redhat.com>
36e8a3
Date: Thu, 30 Aug 2018 08:45:11 +0000
36e8a3
Subject: [PATCH] cryptsetup-generator: introduce basic keydev support
36e8a3
36e8a3
Dracut has a support for unlocking encrypted drives with keyfile stored
36e8a3
on the external drive. This support is included in the generated initrd
36e8a3
only if systemd module is not included.
36e8a3
36e8a3
When systemd is used in initrd then attachment of encrypted drives is
36e8a3
handled by systemd-cryptsetup tools. Our generator has support for
36e8a3
keyfile, however, it didn't support keyfile on the external block
36e8a3
device (keydev).
36e8a3
36e8a3
This commit introduces basic keydev support. Keydev can be specified per
36e8a3
luks.uuid on the kernel command line. Keydev is automatically mounted
36e8a3
during boot and we look for keyfile in the keydev
36e8a3
mountpoint (i.e. keyfile path is prefixed with the keydev mount point
36e8a3
path). After crypt device is attached we automatically unmount
36e8a3
where keyfile resides.
36e8a3
36e8a3
Example:
36e8a3
        rd.luks.key=70bc876b-f627-4038-9049-3080d79d2165=/key:LABEL=KEYDEV
36e8a3
36e8a3
(cherry-picked from commit 70f5f48eb891b12e969577b464de61e15a2593da)
36e8a3
36e8a3
Resolves: #1656869
36e8a3
---
36e8a3
 man/systemd-cryptsetup-generator.xml  |  14 ++++
36e8a3
 src/cryptsetup/cryptsetup-generator.c | 105 +++++++++++++++++++++++++-
36e8a3
 2 files changed, 115 insertions(+), 4 deletions(-)
36e8a3
36e8a3
diff --git a/man/systemd-cryptsetup-generator.xml b/man/systemd-cryptsetup-generator.xml
4bff0a
index c37ee76b87..e30d69bfe7 100644
36e8a3
--- a/man/systemd-cryptsetup-generator.xml
36e8a3
+++ b/man/systemd-cryptsetup-generator.xml
36e8a3
@@ -144,6 +144,20 @@
36e8a3
         to the one specified by <varname>rd.luks.key=</varname> or
36e8a3
         <varname>luks.key=</varname> of the corresponding UUID, or the
36e8a3
         password file that was specified without a UUID.</para>
36e8a3
+
36e8a3
+        <para>It is also possible to specify an external device which
36e8a3
+        should be mounted before we attempt to unlock the LUKS device.
36e8a3
+        systemd-cryptsetup will use password file stored on that
36e8a3
+        device. Device containing password file is specified by
36e8a3
+        appending colon and a device identifier to the password file
36e8a3
+        path. For example,
36e8a3
+        <varname>rd.luks.uuid=</varname>b40f1abf-2a53-400a-889a-2eccc27eaa40
36e8a3
+        <varname>rd.luks.key=</varname>b40f1abf-2a53-400a-889a-2eccc27eaa40=/keyfile:LABEL=keydev.
36e8a3
+        Hence, in this case, we will attempt to mount file system
36e8a3
+        residing on the block device with label <literal>keydev</literal>.
36e8a3
+        This syntax is for now only supported on a per-device basis,
36e8a3
+        i.e. you have to specify LUKS device UUID.</para>
36e8a3
+
36e8a3
         <para><varname>rd.luks.key=</varname>
36e8a3
         is honored only by initial RAM disk
36e8a3
         (initrd) while
36e8a3
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
4bff0a
index f5a81829b9..8c7a76e789 100644
36e8a3
--- a/src/cryptsetup/cryptsetup-generator.c
36e8a3
+++ b/src/cryptsetup/cryptsetup-generator.c
36e8a3
@@ -24,6 +24,7 @@
36e8a3
 typedef struct crypto_device {
36e8a3
         char *uuid;
36e8a3
         char *keyfile;
36e8a3
+        char *keydev;
36e8a3
         char *name;
36e8a3
         char *options;
36e8a3
         bool create;
36e8a3
@@ -37,14 +38,71 @@ static Hashmap *arg_disks = NULL;
36e8a3
 static char *arg_default_options = NULL;
36e8a3
 static char *arg_default_keyfile = NULL;
36e8a3
 
36e8a3
+static int generate_keydev_mount(const char *name, const char *keydev, char **unit, char **mount) {
36e8a3
+        _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL;
36e8a3
+        _cleanup_fclose_ FILE *f = NULL;
36e8a3
+        int r;
36e8a3
+
36e8a3
+        assert(name);
36e8a3
+        assert(keydev);
36e8a3
+        assert(unit);
36e8a3
+        assert(mount);
36e8a3
+
36e8a3
+        r = mkdir_parents("/run/systemd/cryptsetup", 0755);
36e8a3
+        if (r < 0)
36e8a3
+                return r;
36e8a3
+
36e8a3
+        r = mkdir("/run/systemd/cryptsetup", 0700);
36e8a3
+        if (r < 0)
36e8a3
+                return r;
36e8a3
+
36e8a3
+        where = strjoin("/run/systemd/cryptsetup/keydev-", name);
36e8a3
+        if (!where)
36e8a3
+                return -ENOMEM;
36e8a3
+
36e8a3
+        r = mkdir(where, 0700);
36e8a3
+        if (r < 0)
36e8a3
+                return r;
36e8a3
+
36e8a3
+        r = unit_name_from_path(where, ".mount", &u);
36e8a3
+        if (r < 0)
36e8a3
+                return r;
36e8a3
+
36e8a3
+        r = generator_open_unit_file(arg_dest, NULL, u, &f);
36e8a3
+        if (r < 0)
36e8a3
+                return r;
36e8a3
+
36e8a3
+        what = fstab_node_to_udev_node(keydev);
36e8a3
+        if (!what)
36e8a3
+                return -ENOMEM;
36e8a3
+
36e8a3
+        fprintf(f,
36e8a3
+                "[Unit]\n"
36e8a3
+                "DefaultDependencies=no\n\n"
36e8a3
+                "[Mount]\n"
36e8a3
+                "What=%s\n"
36e8a3
+                "Where=%s\n"
36e8a3
+                "Options=ro\n", what, where);
36e8a3
+
36e8a3
+        r = fflush_and_check(f);
36e8a3
+        if (r < 0)
36e8a3
+                return r;
36e8a3
+
36e8a3
+        *unit = TAKE_PTR(u);
36e8a3
+        *mount = TAKE_PTR(where);
36e8a3
+
36e8a3
+        return 0;
36e8a3
+}
36e8a3
+
36e8a3
 static int create_disk(
36e8a3
                 const char *name,
36e8a3
                 const char *device,
36e8a3
+                const char *keydev,
36e8a3
                 const char *password,
36e8a3
                 const char *options) {
36e8a3
 
36e8a3
         _cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
36e8a3
-                *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL;
36e8a3
+                *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *keydev_mount = NULL;
36e8a3
         _cleanup_fclose_ FILE *f = NULL;
36e8a3
         const char *dmname;
36e8a3
         bool noauto, nofail, tmp, swap, netdev;
36e8a3
@@ -94,6 +152,9 @@ static int create_disk(
36e8a3
                         return log_oom();
36e8a3
         }
36e8a3
 
36e8a3
+        if (keydev && !password)
36e8a3
+                return log_error_errno(-EINVAL, "Keydev is specified, but path to the password file is missing: %m");
36e8a3
+
36e8a3
         r = generator_open_unit_file(arg_dest, NULL, n, &f);
36e8a3
         if (r < 0)
36e8a3
                 return r;
36e8a3
@@ -109,6 +170,20 @@ static int create_disk(
36e8a3
                 "After=%s\n",
36e8a3
                 netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
36e8a3
 
36e8a3
+        if (keydev) {
36e8a3
+                _cleanup_free_ char *unit = NULL, *p = NULL;
36e8a3
+
36e8a3
+                r = generate_keydev_mount(name, keydev, &unit, &keydev_mount);
36e8a3
+                if (r < 0)
36e8a3
+                        return log_error_errno(r, "Failed to generate keydev mount unit: %m");
36e8a3
+
36e8a3
+                p = prefix_root(keydev_mount, password_escaped);
36e8a3
+                if (!p)
36e8a3
+                        return log_oom();
36e8a3
+
36e8a3
+                free_and_replace(password_escaped, p);
36e8a3
+        }
36e8a3
+
36e8a3
         if (!nofail)
36e8a3
                 fprintf(f,
36e8a3
                         "Before=%s\n",
36e8a3
@@ -186,6 +261,11 @@ static int create_disk(
36e8a3
                         "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
36e8a3
                         name_escaped);
36e8a3
 
36e8a3
+        if (keydev)
36e8a3
+                fprintf(f,
36e8a3
+                        "ExecStartPost=" UMOUNT_PATH " %s\n\n",
36e8a3
+                        keydev_mount);
36e8a3
+
36e8a3
         r = fflush_and_check(f);
36e8a3
         if (r < 0)
36e8a3
                 return log_error_errno(r, "Failed to write unit file %s: %m", n);
36e8a3
@@ -221,6 +301,7 @@ static int create_disk(
36e8a3
 static void crypt_device_free(crypto_device *d) {
36e8a3
         free(d->uuid);
36e8a3
         free(d->keyfile);
36e8a3
+        free(d->keydev);
36e8a3
         free(d->name);
36e8a3
         free(d->options);
36e8a3
         free(d);
36e8a3
@@ -309,11 +390,27 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
36e8a3
 
36e8a3
                 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
36e8a3
                 if (r == 2) {
36e8a3
+                        char *c;
36e8a3
+                        _cleanup_free_ char *keyfile = NULL, *keydev = NULL;
36e8a3
+
36e8a3
                         d = get_crypto_device(uuid);
36e8a3
                         if (!d)
36e8a3
                                 return log_oom();
36e8a3
 
36e8a3
-                        free_and_replace(d->keyfile, uuid_value);
36e8a3
+                        c = strrchr(uuid_value, ':');
36e8a3
+                        if (!c)
36e8a3
+                                /* No keydev specified */
36e8a3
+                                return free_and_replace(d->keyfile, uuid_value);
36e8a3
+
36e8a3
+                        *c = '\0';
36e8a3
+                        keyfile = strdup(uuid_value);
36e8a3
+                        keydev = strdup(++c);
36e8a3
+
36e8a3
+                        if (!keyfile || !keydev)
36e8a3
+                                return log_oom();
36e8a3
+
36e8a3
+                        free_and_replace(d->keyfile, keyfile);
36e8a3
+                        free_and_replace(d->keydev, keydev);
36e8a3
                 } else if (free_and_strdup(&arg_default_keyfile, value) < 0)
36e8a3
                         return log_oom();
36e8a3
 
36e8a3
@@ -394,7 +491,7 @@ static int add_crypttab_devices(void) {
36e8a3
                         continue;
36e8a3
                 }
36e8a3
 
36e8a3
-                r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
36e8a3
+                r = create_disk(name, device, NULL, keyfile, (d && d->options) ? d->options : options);
36e8a3
                 if (r < 0)
36e8a3
                         return r;
36e8a3
 
36e8a3
@@ -434,7 +531,7 @@ static int add_proc_cmdline_devices(void) {
36e8a3
                 else
36e8a3
                         options = "timeout=0";
36e8a3
 
36e8a3
-                r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
36e8a3
+                r = create_disk(d->name, device, d->keydev, d->keyfile ?: arg_default_keyfile, options);
36e8a3
                 if (r < 0)
36e8a3
                         return r;
36e8a3
         }