Blame SOURCES/0661-cryptsetup-generator-introduce-basic-keydev-support.patch

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