Apart from tests, the new argument isn't used anywhere, so there should be no functional change. Note that the two arms of the big conditional are switched, so the diff is artificially inflated. The actual code change is rather small. I dropped the path which extracts ret_value manually, because it wasn't supporting unescaping of the escape character properly.
917 lines
31 KiB
C
917 lines
31 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "alloc-util.h"
|
|
#include "dropin.h"
|
|
#include "escape.h"
|
|
#include "fd-util.h"
|
|
#include "fileio.h"
|
|
#include "fstab-util.h"
|
|
#include "generator.h"
|
|
#include "hashmap.h"
|
|
#include "id128-util.h"
|
|
#include "log.h"
|
|
#include "mkdir.h"
|
|
#include "parse-util.h"
|
|
#include "path-util.h"
|
|
#include "proc-cmdline.h"
|
|
#include "specifier.h"
|
|
#include "string-util.h"
|
|
#include "strv.h"
|
|
#include "unit-name.h"
|
|
#include "util.h"
|
|
|
|
typedef struct crypto_device {
|
|
char *uuid;
|
|
char *keyfile;
|
|
char *keydev;
|
|
char *headerdev;
|
|
char *datadev;
|
|
char *name;
|
|
char *options;
|
|
bool create;
|
|
} crypto_device;
|
|
|
|
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_allow_list = false;
|
|
static Hashmap *arg_disks = NULL;
|
|
static char *arg_default_options = NULL;
|
|
static char *arg_default_keyfile = NULL;
|
|
|
|
STATIC_DESTRUCTOR_REGISTER(arg_disks, hashmap_freep);
|
|
STATIC_DESTRUCTOR_REGISTER(arg_default_options, freep);
|
|
STATIC_DESTRUCTOR_REGISTER(arg_default_keyfile, freep);
|
|
|
|
static int split_locationspec(const char *locationspec, char **ret_file, char **ret_device) {
|
|
_cleanup_free_ char *file = NULL, *device = NULL;
|
|
const char *c;
|
|
|
|
assert(ret_file);
|
|
assert(ret_device);
|
|
|
|
if (!locationspec) {
|
|
*ret_file = *ret_device = NULL;
|
|
return 0;
|
|
}
|
|
|
|
c = strrchr(locationspec, ':');
|
|
if (c) {
|
|
/* The device 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 file part has the same syntax.
|
|
*
|
|
* Let's try to guess if the second part looks like a device 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 file argument. */
|
|
|
|
device = fstab_node_to_udev_node(c + 1);
|
|
if (!device)
|
|
return log_oom();
|
|
|
|
if (path_is_absolute(device))
|
|
file = strndup(locationspec, c-locationspec);
|
|
else {
|
|
log_debug("Location specification argument contains a colon, but \"%s\" doesn't look like a device specification.\n"
|
|
"Assuming that \"%s\" is a single device specification.",
|
|
c + 1, locationspec);
|
|
device = mfree(device);
|
|
c = NULL;
|
|
}
|
|
}
|
|
|
|
if (!c)
|
|
/* No device specified */
|
|
file = strdup(locationspec);
|
|
|
|
if (!file)
|
|
return log_oom();
|
|
|
|
*ret_file = TAKE_PTR(file);
|
|
*ret_device = TAKE_PTR(device);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int generate_device_mount(
|
|
const char *name,
|
|
const char *device,
|
|
const char *type_prefix, /* "keydev" or "headerdev" */
|
|
const char *device_timeout,
|
|
bool canfail,
|
|
bool readonly,
|
|
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(device);
|
|
assert(unit);
|
|
assert(mount);
|
|
|
|
r = mkdir_parents(arg_runtime_directory, 0755);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = mkdir(arg_runtime_directory, 0700);
|
|
if (r < 0 && errno != EEXIST)
|
|
return -errno;
|
|
|
|
name_escaped = cescape(name);
|
|
if (!name_escaped)
|
|
return -ENOMEM;
|
|
|
|
where = strjoin(arg_runtime_directory, "/", type_prefix, "-", name_escaped);
|
|
if (!where)
|
|
return -ENOMEM;
|
|
|
|
r = mkdir(where, 0700);
|
|
if (r < 0 && errno != EEXIST)
|
|
return -errno;
|
|
|
|
r = unit_name_from_path(where, ".mount", &u);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = generator_open_unit_file(arg_dest, NULL, u, &f);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
fprintf(f,
|
|
"[Unit]\n"
|
|
"DefaultDependencies=no\n\n"
|
|
"[Mount]\n"
|
|
"What=%s\n"
|
|
"Where=%s\n"
|
|
"Options=%s%s\n", device, where, readonly ? "ro" : "rw", canfail ? ",nofail" : "");
|
|
|
|
if (device_timeout) {
|
|
r = parse_sec_fix_0(device_timeout, &timeout_us);
|
|
if (r >= 0) {
|
|
r = unit_name_from_path(device, ".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", device_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", device_timeout);
|
|
|
|
}
|
|
|
|
r = fflush_and_check(f);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
*unit = TAKE_PTR(u);
|
|
*mount = TAKE_PTR(where);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int generate_device_umount(const char *name,
|
|
const char *device_mount,
|
|
const char *type_prefix, /* "keydev" or "headerdev" */
|
|
char **ret_umount_unit) {
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
_cleanup_free_ char *u = NULL, *name_escaped = NULL, *mount = NULL;
|
|
int r;
|
|
|
|
assert(name);
|
|
assert(ret_umount_unit);
|
|
|
|
name_escaped = cescape(name);
|
|
if (!name_escaped)
|
|
return -ENOMEM;
|
|
|
|
u = strjoin(type_prefix, "-", name_escaped, "-umount.service");
|
|
if (!u)
|
|
return -ENOMEM;
|
|
|
|
r = unit_name_from_path(device_mount, ".mount", &mount);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = generator_open_unit_file(arg_dest, NULL, u, &f);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
fprintf(f,
|
|
"[Unit]\n"
|
|
"DefaultDependencies=no\n"
|
|
"After=%s\n\n"
|
|
"[Service]\n"
|
|
"ExecStart=-" UMOUNT_PATH " %s\n\n", mount, device_mount);
|
|
|
|
r = fflush_and_check(f);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
*ret_umount_unit = TAKE_PTR(u);
|
|
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",
|
|
"/dev/hwrng")) {
|
|
/* 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 corresponding 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\n"
|
|
"Requires=%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 *password,
|
|
const char *keydev,
|
|
const char *headerdev,
|
|
const char *options,
|
|
const char *source) {
|
|
|
|
_cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
|
|
*keydev_mount = NULL, *keyfile_timeout_value = NULL,
|
|
*filtered = NULL, *u_escaped = NULL, *name_escaped = NULL, *header_path = NULL, *password_buffer = NULL,
|
|
*tmp_fstype = NULL, *filtered_header = NULL, *headerdev_mount = NULL;
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
const char *dmname;
|
|
bool noauto, nofail, swap, netdev, attach_in_initrd;
|
|
int r, detached_header, keyfile_can_timeout, tmp;
|
|
|
|
assert(name);
|
|
assert(device);
|
|
|
|
noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
|
|
nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
|
|
swap = fstab_test_option(options, "swap\0");
|
|
netdev = fstab_test_option(options, "_netdev\0");
|
|
attach_in_initrd = fstab_test_option(options, "x-initrd.attach\0");
|
|
|
|
keyfile_can_timeout = fstab_filter_options(options,
|
|
"keyfile-timeout\0",
|
|
NULL, &keyfile_timeout_value, NULL, 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,
|
|
headerdev ? &filtered_header : NULL);
|
|
if (detached_header < 0)
|
|
return log_error_errno(detached_header, "Failed to parse header= option value: %m");
|
|
|
|
tmp = fstab_filter_options(options, "tmp\0", NULL, &tmp_fstype, NULL, NULL);
|
|
if (tmp < 0)
|
|
return log_error_errno(tmp, "Failed to parse tmp= option value: %m");
|
|
|
|
if (tmp && swap)
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
"Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.",
|
|
name);
|
|
|
|
name_escaped = specifier_escape(name);
|
|
if (!name_escaped)
|
|
return log_oom();
|
|
|
|
e = unit_name_escape(name);
|
|
if (!e)
|
|
return log_oom();
|
|
|
|
u = fstab_node_to_udev_node(device);
|
|
if (!u)
|
|
return log_oom();
|
|
|
|
r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to generate unit name: %m");
|
|
|
|
u_escaped = specifier_escape(u);
|
|
if (!u_escaped)
|
|
return log_oom();
|
|
|
|
r = unit_name_from_path(u, ".device", &d);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to generate unit name: %m");
|
|
|
|
if (keydev && !password)
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
"Key device is specified, but path to the password file is missing.");
|
|
|
|
r = generator_open_unit_file(arg_dest, NULL, n, &f);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = generator_write_cryptsetup_unit_section(f, source);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (netdev)
|
|
fprintf(f, "After=remote-fs-pre.target\n");
|
|
|
|
/* If initrd takes care of attaching the disk then it should also detach it during shutdown. */
|
|
if (!attach_in_initrd)
|
|
fprintf(f, "Conflicts=umount.target\n");
|
|
|
|
if (keydev) {
|
|
_cleanup_free_ char *unit = NULL, *umount_unit = NULL;
|
|
|
|
r = generate_device_mount(
|
|
name,
|
|
keydev,
|
|
"keydev",
|
|
keyfile_timeout_value,
|
|
/* canfail = */ keyfile_can_timeout > 0,
|
|
/* readonly= */ true,
|
|
&unit,
|
|
&keydev_mount);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to generate keydev mount unit: %m");
|
|
|
|
r = generate_device_umount(name, keydev_mount, "keydev", &umount_unit);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to generate keydev umount unit: %m");
|
|
|
|
password_buffer = path_join(keydev_mount, password);
|
|
if (!password_buffer)
|
|
return log_oom();
|
|
|
|
password = password_buffer;
|
|
|
|
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 (umount_unit)
|
|
fprintf(f,
|
|
"Wants=%s\n"
|
|
"Before=%s\n",
|
|
umount_unit,
|
|
umount_unit
|
|
);
|
|
}
|
|
|
|
if (headerdev) {
|
|
_cleanup_free_ char *unit = NULL, *umount_unit = NULL, *p = NULL;
|
|
|
|
r = generate_device_mount(
|
|
name,
|
|
headerdev,
|
|
"headerdev",
|
|
NULL,
|
|
/* canfail= */ false, /* header is always necessary */
|
|
/* readonly= */ false, /* LUKS2 recovery requires rw header access */
|
|
&unit,
|
|
&headerdev_mount);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to generate header device mount unit: %m");
|
|
|
|
r = generate_device_umount(name, headerdev_mount, "headerdev", &umount_unit);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to generate header device umount unit: %m");
|
|
|
|
p = path_join(headerdev_mount, header_path);
|
|
if (!p)
|
|
return log_oom();
|
|
|
|
free_and_replace(header_path, p);
|
|
|
|
if (isempty(filtered_header))
|
|
p = strjoin("header=", header_path);
|
|
else
|
|
p = strjoin(filtered_header, ",header=", header_path);
|
|
|
|
if (!p)
|
|
return log_oom();
|
|
|
|
free_and_replace(filtered_header, p);
|
|
options = filtered_header;
|
|
|
|
fprintf(f, "After=%s\n"
|
|
"Requires=%s\n", unit, unit);
|
|
|
|
if (umount_unit)
|
|
fprintf(f,
|
|
"Wants=%s\n"
|
|
"Before=%s\n",
|
|
umount_unit,
|
|
umount_unit
|
|
);
|
|
}
|
|
|
|
if (!nofail)
|
|
fprintf(f,
|
|
"Before=%s\n",
|
|
netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
|
|
|
|
if (password && !keydev) {
|
|
r = print_dependencies(f, password);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
/* Check if a header option was specified */
|
|
if (detached_header > 0 && !headerdev) {
|
|
r = print_dependencies(f, header_path);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
if (path_startswith(u, "/dev/"))
|
|
fprintf(f,
|
|
"BindsTo=%s\n"
|
|
"After=%s\n"
|
|
"Before=umount.target\n",
|
|
d, d);
|
|
else
|
|
/* For loopback devices, add systemd-tmpfiles-setup-dev.service
|
|
dependency to ensure that loopback support is available in
|
|
the kernel (/dev/loop-control needs to exist) */
|
|
fprintf(f,
|
|
"RequiresMountsFor=%s\n"
|
|
"Requires=systemd-tmpfiles-setup-dev.service\n"
|
|
"After=systemd-tmpfiles-setup-dev.service\n",
|
|
u_escaped);
|
|
|
|
r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
|
|
if (r < 0)
|
|
log_warning_errno(r, "Failed to write device timeout drop-in: %m");
|
|
|
|
r = generator_write_cryptsetup_service_section(f, name, u, password, filtered);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (tmp) {
|
|
_cleanup_free_ char *tmp_fstype_escaped = NULL;
|
|
|
|
if (tmp_fstype) {
|
|
tmp_fstype_escaped = specifier_escape(tmp_fstype);
|
|
if (!tmp_fstype_escaped)
|
|
return log_oom();
|
|
}
|
|
|
|
fprintf(f,
|
|
"ExecStartPost=" ROOTLIBEXECDIR "/systemd-makefs '%s' '/dev/mapper/%s'\n",
|
|
tmp_fstype_escaped ?: "ext4", name_escaped);
|
|
}
|
|
|
|
if (swap)
|
|
fprintf(f,
|
|
"ExecStartPost=" ROOTLIBEXECDIR "/systemd-makefs swap '/dev/mapper/%s'\n",
|
|
name_escaped);
|
|
|
|
r = fflush_and_check(f);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to write unit file %s: %m", n);
|
|
|
|
if (!noauto) {
|
|
r = generator_add_symlink(arg_dest,
|
|
netdev ? "remote-cryptsetup.target" : "cryptsetup.target",
|
|
nofail ? "wants" : "requires", n);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
dmname = strjoina("dev-mapper-", e, ".device");
|
|
r = generator_add_symlink(arg_dest, dmname, "requires", n);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (!noauto && !nofail) {
|
|
r = write_drop_in(arg_dest, dmname, 40, "device-timeout",
|
|
"# Automatically generated by systemd-cryptsetup-generator\n\n"
|
|
"[Unit]\nJobTimeoutSec=0");
|
|
if (r < 0)
|
|
log_warning_errno(r, "Failed to write device timeout drop-in: %m");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static crypto_device* crypt_device_free(crypto_device *d) {
|
|
if (!d)
|
|
return NULL;
|
|
|
|
free(d->uuid);
|
|
free(d->keyfile);
|
|
free(d->keydev);
|
|
free(d->name);
|
|
free(d->options);
|
|
return mfree(d);
|
|
}
|
|
|
|
static crypto_device *get_crypto_device(const char *uuid) {
|
|
int r;
|
|
crypto_device *d;
|
|
|
|
assert(uuid);
|
|
|
|
d = hashmap_get(arg_disks, uuid);
|
|
if (!d) {
|
|
d = new0(struct crypto_device, 1);
|
|
if (!d)
|
|
return NULL;
|
|
|
|
d->uuid = strdup(uuid);
|
|
if (!d->uuid)
|
|
return mfree(d);
|
|
|
|
r = hashmap_put(arg_disks, d->uuid, d);
|
|
if (r < 0) {
|
|
free(d->uuid);
|
|
return mfree(d);
|
|
}
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
static bool warn_uuid_invalid(const char *uuid, const char *key) {
|
|
assert(key);
|
|
|
|
if (!id128_is_valid(uuid)) {
|
|
log_warning("Failed to parse %s= kernel command line switch. UUID is invalid, ignoring.", key);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int filter_header_device(const char *options,
|
|
char **ret_headerdev,
|
|
char **ret_filtered_headerdev_options) {
|
|
int r;
|
|
_cleanup_free_ char *headerfile = NULL, *headerdev = NULL, *headerspec = NULL,
|
|
*filtered_headerdev = NULL, *filtered_headerspec = NULL;
|
|
|
|
assert(ret_headerdev);
|
|
assert(ret_filtered_headerdev_options);
|
|
|
|
r = fstab_filter_options(options, "header\0", NULL, &headerspec, NULL, &filtered_headerspec);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to parse header= option value: %m");
|
|
|
|
if (r > 0) {
|
|
r = split_locationspec(headerspec, &headerfile, &headerdev);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (isempty(filtered_headerspec))
|
|
filtered_headerdev = strjoin("header=", headerfile);
|
|
else
|
|
filtered_headerdev = strjoin(filtered_headerspec, ",header=", headerfile);
|
|
|
|
if (!filtered_headerdev)
|
|
return log_oom();
|
|
} else
|
|
filtered_headerdev = TAKE_PTR(filtered_headerspec);
|
|
|
|
*ret_filtered_headerdev_options = TAKE_PTR(filtered_headerdev);
|
|
*ret_headerdev = TAKE_PTR(headerdev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
|
|
_cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
|
|
crypto_device *d;
|
|
int r;
|
|
|
|
if (streq(key, "luks")) {
|
|
|
|
r = value ? parse_boolean(value) : 1;
|
|
if (r < 0)
|
|
log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value);
|
|
else
|
|
arg_enabled = r;
|
|
|
|
} else if (streq(key, "luks.crypttab")) {
|
|
|
|
r = value ? parse_boolean(value) : 1;
|
|
if (r < 0)
|
|
log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value);
|
|
else
|
|
arg_read_crypttab = r;
|
|
|
|
} else if (streq(key, "luks.uuid")) {
|
|
|
|
if (proc_cmdline_value_missing(key, value))
|
|
return 0;
|
|
|
|
d = get_crypto_device(startswith(value, "luks-") ?: value);
|
|
if (!d)
|
|
return log_oom();
|
|
|
|
d->create = arg_allow_list = true;
|
|
|
|
} else if (streq(key, "luks.options")) {
|
|
_cleanup_free_ char *headerdev = NULL, *filtered_headerdev_options = NULL;
|
|
|
|
if (proc_cmdline_value_missing(key, value))
|
|
return 0;
|
|
|
|
r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
|
|
if (r != 2)
|
|
return free_and_strdup_warn(&arg_default_options, value);
|
|
|
|
if (warn_uuid_invalid(uuid, key))
|
|
return 0;
|
|
|
|
d = get_crypto_device(uuid);
|
|
if (!d)
|
|
return log_oom();
|
|
|
|
r = filter_header_device(uuid_value, &headerdev, &filtered_headerdev_options);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
free_and_replace(d->options, filtered_headerdev_options);
|
|
free_and_replace(d->headerdev, headerdev);
|
|
} else if (streq(key, "luks.key")) {
|
|
size_t n;
|
|
_cleanup_free_ char *keyfile = NULL, *keydev = NULL;
|
|
const char *keyspec;
|
|
|
|
if (proc_cmdline_value_missing(key, value))
|
|
return 0;
|
|
|
|
n = strspn(value, ALPHANUMERICAL "-");
|
|
if (value[n] != '=')
|
|
return free_and_strdup_warn(&arg_default_keyfile, value);
|
|
|
|
uuid = strndup(value, n);
|
|
if (!uuid)
|
|
return log_oom();
|
|
|
|
if (warn_uuid_invalid(uuid, key))
|
|
return 0;
|
|
|
|
d = get_crypto_device(uuid);
|
|
if (!d)
|
|
return log_oom();
|
|
|
|
keyspec = value + n + 1;
|
|
r = split_locationspec(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.data")) {
|
|
size_t n;
|
|
_cleanup_free_ char *datadev = NULL;
|
|
|
|
if (proc_cmdline_value_missing(key, value))
|
|
return 0;
|
|
|
|
n = strspn(value, ALPHANUMERICAL "-");
|
|
if (value[n] != '=') {
|
|
log_warning("Failed to parse luks.data= kernel command line switch. UUID is invalid, ignoring.");
|
|
return 0;
|
|
}
|
|
|
|
uuid = strndup(value, n);
|
|
if (!uuid)
|
|
return log_oom();
|
|
|
|
if (warn_uuid_invalid(uuid, key))
|
|
return 0;
|
|
|
|
d = get_crypto_device(uuid);
|
|
if (!d)
|
|
return log_oom();
|
|
|
|
datadev = fstab_node_to_udev_node(value + n + 1);
|
|
if (!datadev)
|
|
return log_oom();
|
|
|
|
free_and_replace(d->datadev, datadev);
|
|
} else if (streq(key, "luks.name")) {
|
|
|
|
if (proc_cmdline_value_missing(key, value))
|
|
return 0;
|
|
|
|
r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
|
|
if (r == 2) {
|
|
d = get_crypto_device(uuid);
|
|
if (!d)
|
|
return log_oom();
|
|
|
|
d->create = arg_allow_list = true;
|
|
|
|
free_and_replace(d->name, uuid_value);
|
|
} else
|
|
log_warning("Failed to parse luks name switch %s. Ignoring.", value);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int add_crypttab_devices(void) {
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
unsigned crypttab_line = 0;
|
|
int r;
|
|
|
|
if (!arg_read_crypttab)
|
|
return 0;
|
|
|
|
r = fopen_unlocked(arg_crypttab, "re", &f);
|
|
if (r < 0) {
|
|
if (errno != ENOENT)
|
|
log_error_errno(errno, "Failed to open %s: %m", arg_crypttab);
|
|
return 0;
|
|
}
|
|
|
|
for (;;) {
|
|
_cleanup_free_ char *line = NULL, *name = NULL, *device = NULL, *keyspec = NULL, *options = NULL,
|
|
*keyfile = NULL, *keydev = NULL, *headerdev = NULL, *filtered_header = 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 %s: %m", arg_crypttab);
|
|
if (r == 0)
|
|
break;
|
|
|
|
crypttab_line++;
|
|
|
|
l = strstrip(line);
|
|
if (IN_SET(l[0], 0, '#'))
|
|
continue;
|
|
|
|
k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyspec, &options);
|
|
if (k < 2 || k > 4) {
|
|
log_error("Failed to parse %s:%u, ignoring.", arg_crypttab, crypttab_line);
|
|
continue;
|
|
}
|
|
|
|
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);
|
|
|
|
if (arg_allow_list && !d) {
|
|
log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
|
|
continue;
|
|
}
|
|
|
|
r = split_locationspec(keyspec, &keyfile, &keydev);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (options && (!d || !d->options)) {
|
|
r = filter_header_device(options, &headerdev, &filtered_header);
|
|
if (r < 0)
|
|
return r;
|
|
free_and_replace(options, filtered_header);
|
|
}
|
|
|
|
r = create_disk(name,
|
|
device,
|
|
keyfile,
|
|
keydev,
|
|
(d && d->options) ? d->headerdev : headerdev,
|
|
(d && d->options) ? d->options : options,
|
|
arg_crypttab);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (d)
|
|
d->create = false;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int add_proc_cmdline_devices(void) {
|
|
int r;
|
|
crypto_device *d;
|
|
|
|
HASHMAP_FOREACH(d, arg_disks) {
|
|
_cleanup_free_ char *device = NULL;
|
|
|
|
if (!d->create)
|
|
continue;
|
|
|
|
if (!d->name) {
|
|
d->name = strjoin("luks-", d->uuid);
|
|
if (!d->name)
|
|
return log_oom();
|
|
}
|
|
|
|
device = strjoin("UUID=", d->uuid);
|
|
if (!device)
|
|
return log_oom();
|
|
|
|
r = create_disk(d->name,
|
|
d->datadev ?: device,
|
|
d->keyfile ?: arg_default_keyfile,
|
|
d->keydev,
|
|
d->headerdev,
|
|
d->options ?: arg_default_options,
|
|
"/proc/cmdline");
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(crypt_device_hash_ops, char, string_hash_func, string_compare_func,
|
|
crypto_device, crypt_device_free);
|
|
|
|
static int run(const char *dest, const char *dest_early, const char *dest_late) {
|
|
int 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();
|
|
|
|
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
|
|
if (r < 0)
|
|
return log_warning_errno(r, "Failed to parse kernel command line: %m");
|
|
|
|
if (!arg_enabled)
|
|
return 0;
|
|
|
|
r = add_crypttab_devices();
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = add_proc_cmdline_devices();
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_MAIN_GENERATOR_FUNCTION(run);
|