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