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