|
|
ddca0b |
From 0f7a4f49a7ce95e87061afe03ac40662a1eb0e2d Mon Sep 17 00:00:00 2001
|
|
|
ddca0b |
From: shinygold <10763595+shinygold@users.noreply.github.com>
|
|
|
ddca0b |
Date: Tue, 16 Jul 2019 13:06:16 +0200
|
|
|
ddca0b |
Subject: [PATCH] cryptsetup: add keyfile-timeout to allow a keydev timeout and
|
|
|
ddca0b |
allow to fallback to a password if it fails.
|
|
|
ddca0b |
|
|
|
ddca0b |
(cherry picked from commit 50d2eba27b9bfc77ef6b40e5721713846815418b)
|
|
|
ddca0b |
|
|
|
ddca0b |
Resolves: #1763155
|
|
|
ddca0b |
---
|
|
|
ddca0b |
src/cryptsetup/cryptsetup-generator.c | 119 ++++++++++++++++++--------
|
|
|
ddca0b |
src/cryptsetup/cryptsetup.c | 5 +-
|
|
|
ddca0b |
2 files changed, 89 insertions(+), 35 deletions(-)
|
|
|
ddca0b |
|
|
|
ddca0b |
diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c
|
|
|
ddca0b |
index 52c1262728..1e8e3ba00d 100644
|
|
|
ddca0b |
--- a/src/cryptsetup/cryptsetup-generator.c
|
|
|
ddca0b |
+++ b/src/cryptsetup/cryptsetup-generator.c
|
|
|
ddca0b |
@@ -40,10 +40,39 @@ static Hashmap *arg_disks = NULL;
|
|
|
ddca0b |
static char *arg_default_options = NULL;
|
|
|
ddca0b |
static char *arg_default_keyfile = NULL;
|
|
|
ddca0b |
|
|
|
ddca0b |
-static int generate_keydev_mount(const char *name, const char *keydev, char **unit, char **mount) {
|
|
|
ddca0b |
- _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *name_escaped = NULL;
|
|
|
ddca0b |
+static int split_keyspec(const char *keyspec, char **keyfile, char **keydev) {
|
|
|
ddca0b |
+ _cleanup_free_ char *kfile = NULL, *kdev = NULL;
|
|
|
ddca0b |
+ char *c;
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+ assert(keyspec);
|
|
|
ddca0b |
+ assert(keyfile);
|
|
|
ddca0b |
+ assert(keydev);
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+ c = strrchr(keyspec, ':');
|
|
|
ddca0b |
+ if (c) {
|
|
|
ddca0b |
+ kfile = strndup(keyspec, c-keyspec);
|
|
|
ddca0b |
+ kdev = strdup(c + 1);
|
|
|
ddca0b |
+ if (!*kfile || !*kdev)
|
|
|
ddca0b |
+ return log_oom();
|
|
|
ddca0b |
+ } else {
|
|
|
ddca0b |
+ /* No keydev specified */
|
|
|
ddca0b |
+ kfile = strdup(keyspec);
|
|
|
ddca0b |
+ kdev = NULL;
|
|
|
ddca0b |
+ if (!*kfile)
|
|
|
ddca0b |
+ return log_oom();
|
|
|
ddca0b |
+ }
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+ *keyfile = TAKE_PTR(kfile);
|
|
|
ddca0b |
+ *keydev = TAKE_PTR(kdev);
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+ return 0;
|
|
|
ddca0b |
+}
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+static int generate_keydev_mount(const char *name, const char *keydev, const char *keydev_timeout, bool canfail, char **unit, char **mount) {
|
|
|
ddca0b |
+ _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL;
|
|
|
ddca0b |
_cleanup_fclose_ FILE *f = NULL;
|
|
|
ddca0b |
int r;
|
|
|
ddca0b |
+ usec_t timeout_us;
|
|
|
ddca0b |
|
|
|
ddca0b |
assert(name);
|
|
|
ddca0b |
assert(keydev);
|
|
|
ddca0b |
@@ -88,7 +117,25 @@ static int generate_keydev_mount(const char *name, const char *keydev, char **un
|
|
|
ddca0b |
"[Mount]\n"
|
|
|
ddca0b |
"What=%s\n"
|
|
|
ddca0b |
"Where=%s\n"
|
|
|
ddca0b |
- "Options=ro\n", what, where);
|
|
|
ddca0b |
+ "Options=ro%s\n", what, where, canfail ? ",nofail" : "");
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+ if (keydev_timeout) {
|
|
|
ddca0b |
+ r = parse_sec_fix_0(keydev_timeout, &timeout_us);
|
|
|
ddca0b |
+ if (r >= 0) {
|
|
|
ddca0b |
+ r = unit_name_from_path(what, ".device", &device_unit);
|
|
|
ddca0b |
+ if (r < 0)
|
|
|
ddca0b |
+ return log_error_errno(r, "Failed to generate unit name: %m");
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+ r = write_drop_in_format(arg_dest, device_unit, 90, "device-timeout",
|
|
|
ddca0b |
+ "# Automatically generated by systemd-cryptsetup-generator \n\n"
|
|
|
ddca0b |
+ "[Unit]\nJobRunningTimeoutSec=%s", keydev_timeout);
|
|
|
ddca0b |
+ if (r < 0)
|
|
|
ddca0b |
+ return log_error_errno(r, "Failed to write device drop-in: %m");
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+ } else
|
|
|
ddca0b |
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", keydev_timeout);
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+ }
|
|
|
ddca0b |
|
|
|
ddca0b |
r = fflush_and_check(f);
|
|
|
ddca0b |
if (r < 0)
|
|
|
ddca0b |
@@ -103,16 +150,17 @@ static int generate_keydev_mount(const char *name, const char *keydev, char **un
|
|
|
ddca0b |
static int create_disk(
|
|
|
ddca0b |
const char *name,
|
|
|
ddca0b |
const char *device,
|
|
|
ddca0b |
- const char *keydev,
|
|
|
ddca0b |
const char *password,
|
|
|
ddca0b |
+ const char *keydev,
|
|
|
ddca0b |
const char *options) {
|
|
|
ddca0b |
|
|
|
ddca0b |
_cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
|
|
|
ddca0b |
- *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *keydev_mount = NULL;
|
|
|
ddca0b |
+ *keydev_mount = NULL, *keyfile_timeout_value = NULL, *password_escaped = NULL,
|
|
|
ddca0b |
+ *filtered = NULL, *u_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL;
|
|
|
ddca0b |
_cleanup_fclose_ FILE *f = NULL;
|
|
|
ddca0b |
const char *dmname;
|
|
|
ddca0b |
bool noauto, nofail, tmp, swap, netdev;
|
|
|
ddca0b |
- int r;
|
|
|
ddca0b |
+ int r, keyfile_can_timeout;
|
|
|
ddca0b |
|
|
|
ddca0b |
assert(name);
|
|
|
ddca0b |
assert(device);
|
|
|
ddca0b |
@@ -123,6 +171,10 @@ static int create_disk(
|
|
|
ddca0b |
swap = fstab_test_option(options, "swap\0");
|
|
|
ddca0b |
netdev = fstab_test_option(options, "_netdev\0");
|
|
|
ddca0b |
|
|
|
ddca0b |
+ keyfile_can_timeout = fstab_filter_options(options, "keyfile-timeout\0", NULL, &keyfile_timeout_value, NULL);
|
|
|
ddca0b |
+ if (keyfile_can_timeout < 0)
|
|
|
ddca0b |
+ return log_error_errno(keyfile_can_timeout, "Failed to parse keyfile-timeout= option value: %m");
|
|
|
ddca0b |
+
|
|
|
ddca0b |
if (tmp && swap) {
|
|
|
ddca0b |
log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
|
|
|
ddca0b |
return -EINVAL;
|
|
|
ddca0b |
@@ -152,12 +204,6 @@ static int create_disk(
|
|
|
ddca0b |
if (r < 0)
|
|
|
ddca0b |
return log_error_errno(r, "Failed to generate unit name: %m");
|
|
|
ddca0b |
|
|
|
ddca0b |
- if (password) {
|
|
|
ddca0b |
- password_escaped = specifier_escape(password);
|
|
|
ddca0b |
- if (!password_escaped)
|
|
|
ddca0b |
- return log_oom();
|
|
|
ddca0b |
- }
|
|
|
ddca0b |
-
|
|
|
ddca0b |
if (keydev && !password) {
|
|
|
ddca0b |
log_error("Key device is specified, but path to the password file is missing.");
|
|
|
ddca0b |
return -EINVAL;
|
|
|
ddca0b |
@@ -178,10 +224,16 @@ static int create_disk(
|
|
|
ddca0b |
"After=%s\n",
|
|
|
ddca0b |
netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
|
|
|
ddca0b |
|
|
|
ddca0b |
+ if (password) {
|
|
|
ddca0b |
+ password_escaped = specifier_escape(password);
|
|
|
ddca0b |
+ if (!password_escaped)
|
|
|
ddca0b |
+ return log_oom();
|
|
|
ddca0b |
+ }
|
|
|
ddca0b |
+
|
|
|
ddca0b |
if (keydev) {
|
|
|
ddca0b |
_cleanup_free_ char *unit = NULL, *p = NULL;
|
|
|
ddca0b |
|
|
|
ddca0b |
- r = generate_keydev_mount(name, keydev, &unit, &keydev_mount);
|
|
|
ddca0b |
+ r = generate_keydev_mount(name, keydev, keyfile_timeout_value, keyfile_can_timeout > 0, &unit, &keydev_mount);
|
|
|
ddca0b |
if (r < 0)
|
|
|
ddca0b |
return log_error_errno(r, "Failed to generate keydev mount unit: %m");
|
|
|
ddca0b |
|
|
|
ddca0b |
@@ -190,6 +242,12 @@ static int create_disk(
|
|
|
ddca0b |
return log_oom();
|
|
|
ddca0b |
|
|
|
ddca0b |
free_and_replace(password_escaped, p);
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+ fprintf(f, "After=%s\n", unit);
|
|
|
ddca0b |
+ if (keyfile_can_timeout > 0)
|
|
|
ddca0b |
+ fprintf(f, "Wants=%s\n", unit);
|
|
|
ddca0b |
+ else
|
|
|
ddca0b |
+ fprintf(f, "Requires=%s\n", unit);
|
|
|
ddca0b |
}
|
|
|
ddca0b |
|
|
|
ddca0b |
if (!nofail)
|
|
|
ddca0b |
@@ -197,7 +255,7 @@ static int create_disk(
|
|
|
ddca0b |
"Before=%s\n",
|
|
|
ddca0b |
netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
|
|
|
ddca0b |
|
|
|
ddca0b |
- if (password) {
|
|
|
ddca0b |
+ if (password && !keydev) {
|
|
|
ddca0b |
if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
|
|
|
ddca0b |
fputs("After=systemd-random-seed.service\n", f);
|
|
|
ddca0b |
else if (!STR_IN_SET(password, "-", "none")) {
|
|
|
ddca0b |
@@ -271,7 +329,7 @@ static int create_disk(
|
|
|
ddca0b |
|
|
|
ddca0b |
if (keydev)
|
|
|
ddca0b |
fprintf(f,
|
|
|
ddca0b |
- "ExecStartPost=" UMOUNT_PATH " %s\n\n",
|
|
|
ddca0b |
+ "ExecStartPost=-" UMOUNT_PATH " %s\n\n",
|
|
|
ddca0b |
keydev_mount);
|
|
|
ddca0b |
|
|
|
ddca0b |
r = fflush_and_check(f);
|
|
|
ddca0b |
@@ -394,7 +452,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
|
|
|
ddca0b |
} else if (streq(key, "luks.key")) {
|
|
|
ddca0b |
size_t n;
|
|
|
ddca0b |
_cleanup_free_ char *keyfile = NULL, *keydev = NULL;
|
|
|
ddca0b |
- char *c;
|
|
|
ddca0b |
const char *keyspec;
|
|
|
ddca0b |
|
|
|
ddca0b |
if (proc_cmdline_value_missing(key, value))
|
|
|
ddca0b |
@@ -421,23 +478,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
|
|
|
ddca0b |
return log_oom();
|
|
|
ddca0b |
|
|
|
ddca0b |
keyspec = value + n + 1;
|
|
|
ddca0b |
- c = strrchr(keyspec, ':');
|
|
|
ddca0b |
- if (c) {
|
|
|
ddca0b |
- *c = '\0';
|
|
|
ddca0b |
- keyfile = strdup(keyspec);
|
|
|
ddca0b |
- keydev = strdup(c + 1);
|
|
|
ddca0b |
-
|
|
|
ddca0b |
- if (!keyfile || !keydev)
|
|
|
ddca0b |
- return log_oom();
|
|
|
ddca0b |
- } else {
|
|
|
ddca0b |
- /* No keydev specified */
|
|
|
ddca0b |
- keyfile = strdup(keyspec);
|
|
|
ddca0b |
- if (!keyfile)
|
|
|
ddca0b |
- return log_oom();
|
|
|
ddca0b |
- }
|
|
|
ddca0b |
+ r = split_keyspec(keyspec, &keyfile, &keydev);
|
|
|
ddca0b |
+ if (r < 0)
|
|
|
ddca0b |
+ return r;
|
|
|
ddca0b |
|
|
|
ddca0b |
free_and_replace(d->keyfile, keyfile);
|
|
|
ddca0b |
free_and_replace(d->keydev, keydev);
|
|
|
ddca0b |
+
|
|
|
ddca0b |
} else if (streq(key, "luks.name")) {
|
|
|
ddca0b |
|
|
|
ddca0b |
if (proc_cmdline_value_missing(key, value))
|
|
|
ddca0b |
@@ -485,7 +532,7 @@ static int add_crypttab_devices(void) {
|
|
|
ddca0b |
int r, k;
|
|
|
ddca0b |
char line[LINE_MAX], *l, *uuid;
|
|
|
ddca0b |
crypto_device *d = NULL;
|
|
|
ddca0b |
- _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
|
|
|
ddca0b |
+ _cleanup_free_ char *name = NULL, *device = NULL, *keydev = NULL, *keyfile = NULL, *keyspec = NULL, *options = NULL;
|
|
|
ddca0b |
|
|
|
ddca0b |
if (!fgets(line, sizeof(line), f))
|
|
|
ddca0b |
break;
|
|
|
ddca0b |
@@ -496,7 +543,7 @@ static int add_crypttab_devices(void) {
|
|
|
ddca0b |
if (IN_SET(*l, 0, '#'))
|
|
|
ddca0b |
continue;
|
|
|
ddca0b |
|
|
|
ddca0b |
- k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
|
|
|
ddca0b |
+ k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyspec, &options);
|
|
|
ddca0b |
if (k < 2 || k > 4) {
|
|
|
ddca0b |
log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
|
|
|
ddca0b |
continue;
|
|
|
ddca0b |
@@ -515,7 +562,11 @@ static int add_crypttab_devices(void) {
|
|
|
ddca0b |
continue;
|
|
|
ddca0b |
}
|
|
|
ddca0b |
|
|
|
ddca0b |
- r = create_disk(name, device, NULL, keyfile, (d && d->options) ? d->options : options);
|
|
|
ddca0b |
+ r = split_keyspec(keyspec, &keyfile, &keydev);
|
|
|
ddca0b |
+ if (r < 0)
|
|
|
ddca0b |
+ return r;
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+ r = create_disk(name, device, keyfile, keydev, (d && d->options) ? d->options : options);
|
|
|
ddca0b |
if (r < 0)
|
|
|
ddca0b |
return r;
|
|
|
ddca0b |
|
|
|
ddca0b |
@@ -555,7 +606,7 @@ static int add_proc_cmdline_devices(void) {
|
|
|
ddca0b |
else
|
|
|
ddca0b |
options = "timeout=0";
|
|
|
ddca0b |
|
|
|
ddca0b |
- r = create_disk(d->name, device, d->keydev, d->keyfile ?: arg_default_keyfile, options);
|
|
|
ddca0b |
+ r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, d->keydev, options);
|
|
|
ddca0b |
if (r < 0)
|
|
|
ddca0b |
return r;
|
|
|
ddca0b |
}
|
|
|
ddca0b |
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
|
|
|
ddca0b |
index 9071126c2e..0881aea915 100644
|
|
|
ddca0b |
--- a/src/cryptsetup/cryptsetup.c
|
|
|
ddca0b |
+++ b/src/cryptsetup/cryptsetup.c
|
|
|
ddca0b |
@@ -69,7 +69,10 @@ static int parse_one_option(const char *option) {
|
|
|
ddca0b |
assert(option);
|
|
|
ddca0b |
|
|
|
ddca0b |
/* Handled outside of this tool */
|
|
|
ddca0b |
- if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail", "_netdev"))
|
|
|
ddca0b |
+ if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail", "_netdev", "keyfile-timeout"))
|
|
|
ddca0b |
+ return 0;
|
|
|
ddca0b |
+
|
|
|
ddca0b |
+ if (startswith(option, "keyfile-timeout="))
|
|
|
ddca0b |
return 0;
|
|
|
ddca0b |
|
|
|
ddca0b |
if ((val = startswith(option, "cipher="))) {
|