aed857
From aafa651e44df825abeec061f295f227862aad6d9 Mon Sep 17 00:00:00 2001
aed857
From: Michal Sekletar <msekleta@redhat.com>
aed857
Date: Thu, 30 Aug 2018 08:45:11 +0000
aed857
Subject: [PATCH] cryptsetup-generator: introduce basic keydev support
aed857
aed857
Dracut has a support for unlocking encrypted drives with keyfile stored
aed857
on the external drive. This support is included in the generated initrd
aed857
only if systemd module is not included.
aed857
aed857
When systemd is used in initrd then attachment of encrypted drives is
aed857
handled by systemd-cryptsetup tools. Our generator has support for
aed857
keyfile, however, it didn't support keyfile on the external block
aed857
device (keydev).
aed857
aed857
This commit introduces basic keydev support. Keydev can be specified per
aed857
luks.uuid on the kernel command line. Keydev is automatically mounted
aed857
during boot and we look for keyfile in the keydev
aed857
mountpoint (i.e. keyfile path is prefixed with the keydev mount point
aed857
path). After crypt device is attached we automatically unmount
aed857
where keyfile resides.
aed857
aed857
Example:
aed857
        rd.luks.key=70bc876b-f627-4038-9049-3080d79d2165=/key:LABEL=KEYDEV
aed857
aed857
(cherry-picked from commit 70f5f48eb891b12e969577b464de61e15a2593da)
aed857
aed857
Resolves: #1619743
aed857
---
23b3cf
 man/systemd-cryptsetup-generator.xml  |  14 +++
23b3cf
 src/cryptsetup/cryptsetup-generator.c | 122 ++++++++++++++++++++++++--
aed857
 2 files changed, 131 insertions(+), 5 deletions(-)
aed857
aed857
diff --git a/man/systemd-cryptsetup-generator.xml b/man/systemd-cryptsetup-generator.xml
c62b8e
index b6270358ea..8cfd8b6a8a 100644
aed857
--- a/man/systemd-cryptsetup-generator.xml
aed857
+++ b/man/systemd-cryptsetup-generator.xml
aed857
@@ -168,6 +168,20 @@
aed857
         to the one specified by <varname>rd.luks.key=</varname> or
aed857
         <varname>luks.key=</varname> of the corresponding UUID, or the
aed857
         password file that was specified without a UUID.</para>
aed857
+
aed857
+        <para>It is also possible to specify an external device which
aed857
+        should be mounted before we attempt to unlock the LUKS device.
aed857
+        systemd-cryptsetup will use password file stored on that
aed857
+        device. Device containing password file is specified by
aed857
+        appending colon and a device identifier to the password file
aed857
+        path. For example,
aed857
+        <varname>rd.luks.uuid=</varname>b40f1abf-2a53-400a-889a-2eccc27eaa40
aed857
+        <varname>rd.luks.key=</varname>b40f1abf-2a53-400a-889a-2eccc27eaa40=/keyfile:LABEL=keydev.
aed857
+        Hence, in this case, we will attempt to mount file system
aed857
+        residing on the block device with label <literal>keydev</literal>.
aed857
+        This syntax is for now only supported on a per-device basis,
aed857
+        i.e. you have to specify LUKS device UUID.</para>
aed857
+
aed857
         <para><varname>rd.luks.key=</varname>
aed857
         is honored only by initial RAM disk
aed857
         (initrd) while
aed857
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
c62b8e
index 5f29093f54..42c30c5ca9 100644
aed857
--- a/src/cryptsetup/cryptsetup-generator.c
aed857
+++ b/src/cryptsetup/cryptsetup-generator.c
aed857
@@ -38,6 +38,7 @@
aed857
 typedef struct crypto_device {
aed857
         char *uuid;
aed857
         char *keyfile;
aed857
+        char *keydev;
aed857
         char *name;
aed857
         char *options;
aed857
         bool create;
aed857
@@ -51,14 +52,79 @@ static Hashmap *arg_disks = NULL;
aed857
 static char *arg_default_options = NULL;
aed857
 static char *arg_default_keyfile = NULL;
aed857
 
aed857
+static int generate_keydev_mount(const char *name, const char *keydev, char **unit, char **mount) {
aed857
+        _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *p = NULL;
aed857
+        _cleanup_fclose_ FILE *f = NULL;
aed857
+        int r;
aed857
+
aed857
+        assert(name);
aed857
+        assert(keydev);
aed857
+        assert(unit);
aed857
+        assert(mount);
aed857
+
aed857
+        r = mkdir_parents("/run/systemd/cryptsetup", 0755);
aed857
+        if (r < 0)
aed857
+                return r;
aed857
+
aed857
+        r = mkdir("/run/systemd/cryptsetup", 0700);
aed857
+        if (r < 0)
aed857
+                return r;
aed857
+
aed857
+        where = strjoin("/run/systemd/cryptsetup/keydev-", name, NULL);
aed857
+        if (!where)
aed857
+                return -ENOMEM;
aed857
+
aed857
+        r = mkdir(where, 0700);
aed857
+        if (r < 0)
aed857
+                return r;
aed857
+
aed857
+        u = unit_name_from_path(where, ".mount");
aed857
+        if (!u)
aed857
+                return -ENOMEM;
aed857
+
aed857
+        what = fstab_node_to_udev_node(keydev);
aed857
+        if (!what)
aed857
+                return -ENOMEM;
aed857
+
aed857
+        p = strjoin(arg_dest, "/", u, NULL);
aed857
+        if (!p)
aed857
+                return log_oom();
aed857
+
aed857
+        f = fopen(p, "wxe");
aed857
+        if (!f)
aed857
+                return log_error_errno(errno, "Failed to create unit file %s: %m", p);
aed857
+
aed857
+        fprintf(f,
aed857
+                "# Automatically generated by systemd-cryptsetup-generator\n\n"
aed857
+                "[Unit]\n"
aed857
+                "DefaultDependencies=no\n\n"
aed857
+                "[Mount]\n"
aed857
+                "What=%s\n"
aed857
+                "Where=%s\n"
aed857
+                "Options=ro\n", what, where);
aed857
+
aed857
+        r = fflush_and_check(f);
aed857
+        if (r < 0)
aed857
+                return r;
aed857
+
aed857
+        *unit = u;
aed857
+        u = NULL;
aed857
+
aed857
+        *mount = where;
aed857
+        where = NULL;
aed857
+
aed857
+        return 0;
aed857
+}
aed857
+
aed857
 static int create_disk(
aed857
                 const char *name,
aed857
                 const char *device,
aed857
+                const char *keydev,
aed857
                 const char *password,
aed857
                 const char *options) {
aed857
 
aed857
         _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *to = NULL, *e = NULL,
aed857
-                *filtered = NULL;
aed857
+                *filtered = NULL, *keydev_mount = NULL, *keyfile_path = NULL;
aed857
         _cleanup_fclose_ FILE *f = NULL;
aed857
         bool noauto, nofail, tmp, swap, netdev;
aed857
         char *from;
aed857
@@ -98,6 +164,9 @@ static int create_disk(
aed857
         if (!d)
aed857
                 return log_oom();
aed857
 
aed857
+        if (keydev && !password)
aed857
+                return log_error_errno(-EINVAL, "Keydev is specified, but path to the password file is missing: %m");
aed857
+
aed857
         f = fopen(p, "wxe");
aed857
         if (!f)
aed857
                 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
aed857
@@ -115,6 +184,20 @@ static int create_disk(
aed857
                 "After=%s\n",
aed857
                 netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
aed857
 
aed857
+        if (keydev) {
aed857
+                _cleanup_free_ char *unit = NULL;
aed857
+
aed857
+                r = generate_keydev_mount(name, keydev, &unit, &keydev_mount);
aed857
+                if (r < 0)
aed857
+                        return log_error_errno(r, "Failed to generate keydev mount unit: %m");
aed857
+
aed857
+                keyfile_path = prefix_root(keydev_mount, password);
aed857
+                if (!keyfile_path)
aed857
+                        return log_oom();
aed857
+
aed857
+                password = keyfile_path;
aed857
+        }
aed857
+
aed857
         if (!nofail)
aed857
                 fprintf(f,
aed857
                         "Before=%s\n",
aed857
@@ -181,6 +264,11 @@ static int create_disk(
aed857
                         "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
aed857
                         name);
aed857
 
aed857
+        if (keydev)
aed857
+                fprintf(f,
aed857
+                        "ExecStartPost=/bin/umount '%s'\n\n",
aed857
+                        keydev_mount);
aed857
+
aed857
         fflush(f);
aed857
         if (ferror(f))
aed857
                 return log_error_errno(errno, "Failed to write file %s: %m", p);
aed857
@@ -248,6 +336,7 @@ static void free_arg_disks(void) {
aed857
         while ((d = hashmap_steal_first(arg_disks))) {
aed857
                 free(d->uuid);
aed857
                 free(d->keyfile);
aed857
+                free(d->keydev);
aed857
                 free(d->name);
aed857
                 free(d->options);
aed857
                 free(d);
aed857
@@ -335,13 +424,36 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
aed857
 
aed857
                 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
aed857
                 if (r == 2) {
aed857
+                        char *c;
aed857
+                        _cleanup_free_ char *keyfile = NULL, *keydev = NULL;
aed857
+
aed857
                         d = get_crypto_device(uuid);
aed857
                         if (!d)
aed857
                                 return log_oom();
aed857
 
aed857
+                        c = strrchr(uuid_value, ':');
aed857
+                        if (!c) {
aed857
+                                free(d->keyfile);
aed857
+                                d->keyfile = uuid_value;
aed857
+                                uuid_value = NULL;
aed857
+
aed857
+                                return 0;
aed857
+                        }
aed857
+
aed857
+                        *c = '\0';
aed857
+                        keyfile = strdup(uuid_value);
aed857
+                        keydev = strdup(++c);
aed857
+
aed857
+                        if (!keyfile || !keydev)
aed857
+                                return log_oom();
aed857
+
aed857
                         free(d->keyfile);
aed857
-                        d->keyfile = uuid_value;
aed857
-                        uuid_value = NULL;
aed857
+                        d->keyfile = keyfile;
aed857
+                        keyfile = NULL;
aed857
+
aed857
+                        free(d->keydev);
aed857
+                        d->keydev = keydev;
aed857
+                        keydev = NULL;
aed857
                 } else if (free_and_strdup(&arg_default_keyfile, value))
aed857
                         return log_oom();
aed857
 
aed857
@@ -420,7 +532,7 @@ static int add_crypttab_devices(void) {
aed857
                         continue;
aed857
                 }
aed857
 
aed857
-                r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
aed857
+                r = create_disk(name, device, NULL, keyfile, (d && d->options) ? d->options : options);
aed857
                 if (r < 0)
aed857
                         return r;
aed857
 
aed857
@@ -460,7 +572,7 @@ static int add_proc_cmdline_devices(void) {
aed857
                 else
aed857
                         options = "timeout=0";
aed857
 
aed857
-                r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
aed857
+                r = create_disk(d->name, device, d->keydev, d->keyfile ?: arg_default_keyfile, options);
aed857
                 if (r < 0)
aed857
                         return r;
aed857
         }