static const char *arg_dest = NULL;
static bool arg_enabled = true;
static bool arg_read_crypttab = true;
+static const char *arg_crypttab = NULL;
+static const char *arg_runtime_directory = NULL;
static bool arg_whitelist = false;
static Hashmap *arg_disks = NULL;
static char *arg_default_options = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_default_options, freep);
STATIC_DESTRUCTOR_REGISTER(arg_default_keyfile, freep);
-static int generate_keydev_mount(const char *name, const char *keydev, char **unit, char **mount) {
- _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *name_escaped = NULL;
+static int split_keyspec(const char *keyspec, char **ret_keyfile, char **ret_keydev) {
+ _cleanup_free_ char *keyfile = NULL, *keydev = NULL;
+ const char *c;
+
+ assert(ret_keyfile);
+ assert(ret_keydev);
+
+ if (!keyspec) {
+ *ret_keyfile = *ret_keydev = NULL;
+ return 0;
+ }
+
+ c = strrchr(keyspec, ':');
+ if (c) {
+ /* The keydev part has to be either an absolute path to device node (/dev/something,
+ * /dev/foo/something, or even possibly /dev/foo/something:part), or a fstab device
+ * specification starting with LABEL= or similar. The keyfile part has the same syntax.
+ *
+ * Let's try to guess if the second part looks like a keydev specification, or just part of a
+ * filename with a colon. fstab_node_to_udev_node() will convert the fstab device syntax to
+ * an absolute path. If we didn't get an absolute path, assume that it is just part of the
+ * first keyfile argument. */
+
+ keydev = fstab_node_to_udev_node(c + 1);
+ if (!keydev)
+ return log_oom();
+
+ if (path_is_absolute(keydev))
+ keyfile = strndup(keyspec, c-keyspec);
+ else {
+ log_debug("Keyspec argument contains a colon, but \"%s\" doesn't look like a device specification.\n"
+ "Assuming that \"%s\" is a single device specification.",
+ c + 1, keyspec);
+ keydev = mfree(keydev);
+ c = NULL;
+ }
+ }
+
+ if (!c)
+ /* No keydev specified */
+ keyfile = strdup(keyspec);
+
+ if (!keyfile)
+ return log_oom();
+
+ *ret_keyfile = TAKE_PTR(keyfile);
+ *ret_keydev = TAKE_PTR(keydev);
+
+ return 0;
+}
+
+static int generate_keydev_mount(const char *name, const char *keydev, const char *keydev_timeout, bool canfail, char **unit, char **mount) {
+ _cleanup_free_ char *u = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
+ usec_t timeout_us;
assert(name);
assert(keydev);
assert(unit);
assert(mount);
- r = mkdir_parents("/run/systemd/cryptsetup", 0755);
+ r = mkdir_parents(arg_runtime_directory, 0755);
if (r < 0)
return r;
- r = mkdir("/run/systemd/cryptsetup", 0700);
+ r = mkdir(arg_runtime_directory, 0700);
if (r < 0 && errno != EEXIST)
return -errno;
if (!name_escaped)
return -ENOMEM;
- where = strjoin("/run/systemd/cryptsetup/keydev-", name_escaped);
+ where = strjoin(arg_runtime_directory, "/keydev-", name_escaped);
if (!where)
return -ENOMEM;
if (r < 0)
return r;
- what = fstab_node_to_udev_node(keydev);
- if (!what)
- return -ENOMEM;
-
fprintf(f,
"[Unit]\n"
"DefaultDependencies=no\n\n"
"[Mount]\n"
"What=%s\n"
"Where=%s\n"
- "Options=ro\n", what, where);
+ "Options=ro%s\n", keydev, where, canfail ? ",nofail" : "");
+
+ if (keydev_timeout) {
+ r = parse_sec_fix_0(keydev_timeout, &timeout_us);
+ if (r >= 0) {
+ r = unit_name_from_path(keydev, ".device", &device_unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
+
+ r = write_drop_in_format(arg_dest, device_unit, 90, "device-timeout",
+ "# Automatically generated by systemd-cryptsetup-generator \n\n"
+ "[Unit]\nJobRunningTimeoutSec=%s", keydev_timeout);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write device drop-in: %m");
+
+ } else
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", keydev_timeout);
+
+ }
r = fflush_and_check(f);
if (r < 0)
return 0;
}
+static int print_dependencies(FILE *f, const char* device_path) {
+ int r;
+
+ if (STR_IN_SET(device_path, "-", "none"))
+ /* None, nothing to do */
+ return 0;
+
+ if (PATH_IN_SET(device_path, "/dev/urandom", "/dev/random", "/dev/hw_random")) {
+ /* RNG device, add random dep */
+ fputs("After=systemd-random-seed.service\n", f);
+ return 0;
+ }
+
+ _cleanup_free_ char *udev_node = fstab_node_to_udev_node(device_path);
+ if (!udev_node)
+ return log_oom();
+
+ if (path_equal(udev_node, "/dev/null"))
+ return 0;
+
+ if (path_startswith(udev_node, "/dev/")) {
+ /* We are dealing with a block device, add dependency for correspoding unit */
+ _cleanup_free_ char *unit = NULL;
+
+ r = unit_name_from_path(udev_node, ".device", &unit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name: %m");
+
+ fprintf(f, "After=%1$s\nRequires=%1$s\n", unit);
+ } else {
+ /* Regular file, add mount dependency */
+ _cleanup_free_ char *escaped_path = specifier_escape(device_path);
+ if (!escaped_path)
+ return log_oom();
+
+ fprintf(f, "RequiresMountsFor=%s\n", escaped_path);
+ }
+
+ return 0;
+}
+
static int create_disk(
const char *name,
const char *device,
- const char *keydev,
const char *password,
+ const char *keydev,
const char *options) {
_cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
- *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *keydev_mount = NULL;
+ *keydev_mount = NULL, *keyfile_timeout_value = NULL, *password_escaped = NULL,
+ *filtered = NULL, *u_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *header_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *dmname;
bool noauto, nofail, tmp, swap, netdev;
- int r;
+ int r, detached_header, keyfile_can_timeout;
assert(name);
assert(device);
swap = fstab_test_option(options, "swap\0");
netdev = fstab_test_option(options, "_netdev\0");
+ keyfile_can_timeout = fstab_filter_options(options, "keyfile-timeout\0", NULL, &keyfile_timeout_value, NULL);
+ if (keyfile_can_timeout < 0)
+ return log_error_errno(keyfile_can_timeout, "Failed to parse keyfile-timeout= option value: %m");
+
+ detached_header = fstab_filter_options(options, "header\0", NULL, &header_path, NULL);
+ if (detached_header < 0)
+ return log_error_errno(detached_header, "Failed to parse header= option value: %m");
+
if (tmp && swap)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.",
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- if (password) {
- password_escaped = specifier_escape(password);
- if (!password_escaped)
- return log_oom();
- }
-
if (keydev && !password)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Key device is specified, but path to the password file is missing.");
"[Unit]\n"
"Description=Cryptography Setup for %%I\n"
"Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
- "SourcePath=/etc/crypttab\n"
+ "SourcePath=%s\n"
"DefaultDependencies=no\n"
"Conflicts=umount.target\n"
"IgnoreOnIsolate=true\n"
"After=%s\n",
+ arg_crypttab,
netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
+ if (password) {
+ password_escaped = specifier_escape(password);
+ if (!password_escaped)
+ return log_oom();
+ }
+
if (keydev) {
_cleanup_free_ char *unit = NULL, *p = NULL;
- r = generate_keydev_mount(name, keydev, &unit, &keydev_mount);
+ r = generate_keydev_mount(name, keydev, keyfile_timeout_value, keyfile_can_timeout > 0, &unit, &keydev_mount);
if (r < 0)
return log_error_errno(r, "Failed to generate keydev mount unit: %m");
- p = prefix_root(keydev_mount, password_escaped);
+ p = path_join(keydev_mount, password_escaped);
if (!p)
return log_oom();
free_and_replace(password_escaped, p);
+
+ fprintf(f, "After=%s\n", unit);
+ if (keyfile_can_timeout > 0)
+ fprintf(f, "Wants=%s\n", unit);
+ else
+ fprintf(f, "Requires=%s\n", unit);
}
if (!nofail)
"Before=%s\n",
netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
- if (password) {
- if (PATH_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
- fputs("After=systemd-random-seed.service\n", f);
- else if (!STR_IN_SET(password, "-", "none")) {
- _cleanup_free_ char *uu;
-
- uu = fstab_node_to_udev_node(password);
- if (!uu)
- return log_oom();
-
- if (!path_equal(uu, "/dev/null")) {
-
- if (path_startswith(uu, "/dev/")) {
- _cleanup_free_ char *dd = NULL;
-
- r = unit_name_from_path(uu, ".device", &dd);
- if (r < 0)
- return log_error_errno(r, "Failed to generate unit name: %m");
+ if (password && !keydev) {
+ r = print_dependencies(f, password);
+ if (r < 0)
+ return r;
+ }
- fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
- } else
- fprintf(f, "RequiresMountsFor=%s\n", password_escaped);
- }
- }
+ /* Check if a header option was specified */
+ if (detached_header > 0) {
+ r = print_dependencies(f, header_path);
+ if (r < 0)
+ return r;
}
if (path_startswith(u, "/dev/")) {
if (keydev)
fprintf(f,
- "ExecStartPost=" UMOUNT_PATH " %s\n\n",
+ "ExecStartPost=-" UMOUNT_PATH " %s\n\n",
keydev_mount);
r = fflush_and_check(f);
if (!d)
return NULL;
- d->create = false;
- d->keyfile = d->options = d->name = NULL;
-
d->uuid = strdup(uuid);
if (!d->uuid)
return mfree(d);
} else if (streq(key, "luks.key")) {
size_t n;
_cleanup_free_ char *keyfile = NULL, *keydev = NULL;
- char *c;
const char *keyspec;
if (proc_cmdline_value_missing(key, value))
return log_oom();
keyspec = value + n + 1;
- c = strrchr(keyspec, ':');
- if (c) {
- *c = '\0';
- keyfile = strdup(keyspec);
- keydev = strdup(c + 1);
-
- if (!keyfile || !keydev)
- return log_oom();
- } else {
- /* No keydev specified */
- keyfile = strdup(keyspec);
- if (!keyfile)
- return log_oom();
- }
+ r = split_keyspec(keyspec, &keyfile, &keydev);
+ if (r < 0)
+ return r;
free_and_replace(d->keyfile, keyfile);
free_and_replace(d->keydev, keydev);
+
} else if (streq(key, "luks.name")) {
if (proc_cmdline_value_missing(key, value))
if (!arg_read_crypttab)
return 0;
- r = fopen_unlocked("/etc/crypttab", "re", &f);
+ r = fopen_unlocked(arg_crypttab, "re", &f);
if (r < 0) {
if (errno != ENOENT)
- log_error_errno(errno, "Failed to open /etc/crypttab: %m");
+ log_error_errno(errno, "Failed to open %s: %m", arg_crypttab);
return 0;
}
if (fstat(fileno(f), &st) < 0) {
- log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
+ log_error_errno(errno, "Failed to stat %s: %m", arg_crypttab);
return 0;
}
for (;;) {
- _cleanup_free_ char *line = NULL, *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
+ _cleanup_free_ char *line = NULL, *name = NULL, *device = NULL, *keyspec = NULL, *options = NULL, *keyfile = NULL, *keydev = NULL;
crypto_device *d = NULL;
char *l, *uuid;
int k;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
- return log_error_errno(r, "Failed to read /etc/crypttab: %m");
+ return log_error_errno(r, "Failed to read %s: %m", arg_crypttab);
if (r == 0)
break;
if (IN_SET(l[0], 0, '#'))
continue;
- k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
+ k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyspec, &options);
if (k < 2 || k > 4) {
- log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
+ log_error("Failed to parse %s:%u, ignoring.", arg_crypttab, crypttab_line);
continue;
}
- uuid = STARTSWITH_SET(device, "UUID=", "luks-");
+ uuid = startswith(device, "UUID=");
if (!uuid)
uuid = path_startswith(device, "/dev/disk/by-uuid/");
+ if (!uuid)
+ uuid = startswith(name, "luks-");
if (uuid)
d = hashmap_get(arg_disks, uuid);
continue;
}
- r = create_disk(name, device, NULL, keyfile, (d && d->options) ? d->options : options);
+ r = split_keyspec(keyspec, &keyfile, &keydev);
+ if (r < 0)
+ return r;
+
+ r = create_disk(name, device, keyfile, keydev, (d && d->options) ? d->options : options);
if (r < 0)
return r;
continue;
if (!d->name) {
- d->name = strappend("luks-", d->uuid);
+ d->name = strjoin("luks-", d->uuid);
if (!d->name)
return log_oom();
}
- device = strappend("UUID=", d->uuid);
+ device = strjoin("UUID=", d->uuid);
if (!device)
return log_oom();
else
options = "timeout=0";
- r = create_disk(d->name, device, d->keydev, d->keyfile ?: arg_default_keyfile, options);
+ r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, d->keydev, options);
if (r < 0)
return r;
}
assert_se(arg_dest = dest);
+ arg_crypttab = getenv("SYSTEMD_CRYPTTAB") ?: "/etc/crypttab";
+ arg_runtime_directory = getenv("RUNTIME_DIRECTORY") ?: "/run/systemd/cryptsetup";
+
arg_disks = hashmap_new(&crypt_device_hash_ops);
if (!arg_disks)
return log_oom();