cryptsetup-generator.c revision 2b68185ac97a98bf9d6f31b2ac1fddbaaffaa887
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos/***
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos This file is part of systemd.
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos Copyright 2010 Lennart Poettering
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos systemd is free software; you can redistribute it and/or modify it
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos under the terms of the GNU Lesser General Public License as published by
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos the Free Software Foundation; either version 2.1 of the License, or
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos (at your option) any later version.
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos systemd is distributed in the hope that it will be useful, but
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos WITHOUT ANY WARRANTY; without even the implied warranty of
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos Lesser General Public License for more details.
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos You should have received a copy of the GNU Lesser General Public License
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos along with systemd; If not, see <http://www.gnu.org/licenses/>.
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos***/
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos#include <string.h>
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos#include <errno.h>
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos#include <unistd.h>
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos#include "log.h"
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos#include "util.h"
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos#include "unit-name.h"
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos#include "mkdir.h"
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos#include "virt.h"
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos#include "strv.h"
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos#include "fileio.h"
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordosstatic const char *arg_dest = "/tmp";
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordosstatic bool arg_enabled = true;
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordosstatic bool arg_read_crypttab = true;
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
f2f8138695e157f9ce4a11fa2f93d7d44a36d243Laszlo Hordosstatic bool has_option(const char *haystack, const char *needle) {
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos const char *f = haystack;
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos size_t l;
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos assert(needle);
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos if (!haystack)
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos return false;
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos l = strlen(needle);
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch while ((f = strstr(f, needle))) {
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos if (f > haystack && f[-1] != ',') {
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos f++;
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch continue;
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos }
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos if (f[l] != 0 && f[l] != ',') {
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch f++;
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch continue;
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch }
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch return true;
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch }
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch return false;
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch}
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branch
7cffd25e51581b0461c6cf208995b8c5d66cb166Jon Branchstatic int create_disk(
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos const char *name,
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos const char *device,
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos const char *password,
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos const char *options) {
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos _cleanup_fclose_ FILE *f = NULL;
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici bool noauto, nofail;
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos assert(name);
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos assert(device);
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos noauto = has_option(options, "noauto");
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos nofail = has_option(options, "nofail");
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici if (!n)
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici return log_oom();
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos p = strjoin(arg_dest, "/", n, NULL);
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos if (!p)
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos return log_oom();
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos u = fstab_node_to_udev_node(device);
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos if (!u)
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos return log_oom();
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos d = unit_name_from_path(u, ".device");
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos if (!d)
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos return log_oom();
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos f = fopen(p, "wxe");
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos if (!f) {
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos log_error("Failed to create unit file %s: %m", p);
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos return -errno;
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos }
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos fputs(
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos "# Automatically generated by systemd-cryptsetup-generator\n\n"
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos "[Unit]\n"
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici "Description=Cryptography Setup for %I\n"
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos "Documentation=man:systemd-cryptsetup@.service(8) man:crypttab(5)\n"
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos "SourcePath=/etc/crypttab\n"
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos "Conflicts=umount.target\n"
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos "DefaultDependencies=no\n"
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos "BindsTo=dev-mapper-%i.device\n"
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos "After=systemd-readahead-collect.service systemd-readahead-replay.service\n",
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos f);
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos if (!nofail)
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos fprintf(f,
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos "Before=cryptsetup.target\n");
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos if (password) {
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos if (streq(password, "/dev/urandom") ||
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos streq(password, "/dev/random") ||
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos streq(password, "/dev/hw_random"))
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos fputs("After=systemd-random-seed-load.service\n", f);
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos else if (!streq(password, "-") &&
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos !streq(password, "none"))
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici fprintf(f,
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici "RequiresMountsFor=%s\n",
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici password);
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici }
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici if (is_device_path(u))
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici fprintf(f,
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici "BindsTo=%s\n"
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici "After=%s\n"
521378a7df33486a3b37e90752117143cd7bd2a8Alin Brici "Before=umount.target\n",
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos d, d);
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos else
b1ad84aa02aa5c8ee457e35d790d2e91691da517Laszlo Hordos fprintf(f,
963cc96f97623aac2218f625e558cff1ddaea8c1Laszlo Hordos "RequiresMountsFor=%s\n",
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos u);
0fdda69ce3627d501e4bb3103765f676bb1ab061Laszlo Hordos
fprintf(f,
"\n[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"TimeoutSec=0\n" /* the binary handles timeouts anyway */
"ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
"ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
name, u, strempty(password), strempty(options),
name);
if (has_option(options, "tmp"))
fprintf(f,
"ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
name);
if (has_option(options, "swap"))
fprintf(f,
"ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
name);
fflush(f);
if (ferror(f)) {
log_error("Failed to write file %s: %m", p);
return -errno;
}
if (asprintf(&from, "../%s", n) < 0)
return log_oom();
if (!noauto) {
to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
if (!to)
return log_oom();
mkdir_parents_label(to, 0755);
if (symlink(from, to) < 0) {
log_error("Failed to create symlink %s: %m", to);
return -errno;
}
free(to);
if (!nofail)
to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
else
to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
if (!to)
return log_oom();
mkdir_parents_label(to, 0755);
if (symlink(from, to) < 0) {
log_error("Failed to create symlink %s: %m", to);
return -errno;
}
}
e = unit_name_escape(name);
if (!e)
return log_oom();
free(to);
to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
if (!to)
return log_oom();
mkdir_parents_label(to, 0755);
if (symlink(from, to) < 0) {
log_error("Failed to create symlink %s: %m", to);
return -errno;
}
if (!noauto && !nofail) {
int r;
free(p);
p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
if (!p)
return log_oom();
mkdir_parents_label(p, 0755);
r = write_string_file(p,
"# Automatically generated by systemd-cryptsetup-generator\n\n"
"[Unit]\n"
"JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
if (r)
return r;
}
return 0;
}
static int parse_proc_cmdline(char ***arg_proc_cmdline_disks, char **arg_proc_cmdline_keyfile) {
_cleanup_free_ char *line = NULL;
char *w = NULL, *state = NULL;
int r;
size_t l;
if (detect_container(NULL) > 0)
return 0;
r = read_one_line_file("/proc/cmdline", &line);
if (r < 0) {
log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
return 0;
}
FOREACH_WORD_QUOTED(w, l, line, state) {
_cleanup_free_ char *word = NULL;
word = strndup(w, l);
if (!word)
return log_oom();
if (startswith(word, "luks=")) {
r = parse_boolean(word + 5);
if (r < 0)
log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
else
arg_enabled = r;
} else if (startswith(word, "rd.luks=")) {
if (in_initrd()) {
r = parse_boolean(word + 8);
if (r < 0)
log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
else
arg_enabled = r;
}
} else if (startswith(word, "luks.crypttab=")) {
r = parse_boolean(word + 14);
if (r < 0)
log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
else
arg_read_crypttab = r;
} else if (startswith(word, "rd.luks.crypttab=")) {
if (in_initrd()) {
r = parse_boolean(word + 17);
if (r < 0)
log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
else
arg_read_crypttab = r;
}
} else if (startswith(word, "luks.uuid=")) {
if (strv_extend(arg_proc_cmdline_disks, word + 10) < 0)
return log_oom();
} else if (startswith(word, "rd.luks.uuid=")) {
if (in_initrd()) {
if (strv_extend(arg_proc_cmdline_disks, word + 13) < 0)
return log_oom();
}
} else if (startswith(word, "luks.key=")) {
*arg_proc_cmdline_keyfile = strdup(word + 9);
if (!*arg_proc_cmdline_keyfile)
return log_oom();
} else if (startswith(word, "rd.luks.key=")) {
if (in_initrd()) {
if (*arg_proc_cmdline_keyfile)
free(*arg_proc_cmdline_keyfile);
*arg_proc_cmdline_keyfile = strdup(word + 12);
if (!*arg_proc_cmdline_keyfile)
return log_oom();
}
} else if (startswith(word, "luks.") ||
(in_initrd() && startswith(word, "rd.luks."))) {
log_warning("Unknown kernel switch %s. Ignoring.", word);
}
}
strv_uniq(*arg_proc_cmdline_disks);
return 0;
}
int main(int argc, char *argv[]) {
_cleanup_strv_free_ char **arg_proc_cmdline_disks_done = NULL;
_cleanup_strv_free_ char **arg_proc_cmdline_disks = NULL;
_cleanup_free_ char *arg_proc_cmdline_keyfile = NULL;
_cleanup_fclose_ FILE *f = NULL;
unsigned n = 0;
int r = EXIT_SUCCESS;
char **i;
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
return EXIT_FAILURE;
}
if (argc > 1)
arg_dest = argv[1];
log_set_target(LOG_TARGET_SAFE);
log_parse_environment();
log_open();
umask(0022);
if (parse_proc_cmdline(&arg_proc_cmdline_disks, &arg_proc_cmdline_keyfile) < 0)
return EXIT_FAILURE;
if (!arg_enabled)
return EXIT_SUCCESS;
if (arg_read_crypttab) {
struct stat st;
f = fopen("/etc/crypttab", "re");
if (!f) {
if (errno == ENOENT)
r = EXIT_SUCCESS;
else {
r = EXIT_FAILURE;
log_error("Failed to open /etc/crypttab: %m");
}
goto next;
}
if (fstat(fileno(f), &st) < 0) {
log_error("Failed to stat /etc/crypttab: %m");
r = EXIT_FAILURE;
goto next;
}
/* If we readd support for specifying passphrases
* directly in crypttabe we should upgrade the warning
* below, though possibly only if a passphrase is
* specified directly. */
if (st.st_mode & 0005)
log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
for (;;) {
char line[LINE_MAX], *l;
_cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
int k;
if (!fgets(line, sizeof(line), f))
break;
n++;
l = strstrip(line);
if (*l == '#' || *l == 0)
continue;
k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
if (k < 2 || k > 4) {
log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
r = EXIT_FAILURE;
continue;
}
if (arg_proc_cmdline_disks) {
/*
If luks UUIDs are specified on the kernel command line, use them as a filter
for /etc/crypttab and only generate units for those.
*/
STRV_FOREACH(i, arg_proc_cmdline_disks) {
_cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
const char *p = *i;
if (startswith(p, "luks-"))
p += 5;
proc_name = strappend("luks-", p);
proc_device = strappend("UUID=", p);
if (!proc_name || !proc_device)
return log_oom();
if (streq(proc_device, device) || streq(proc_name, name)) {
if (create_disk(name, device, password, options) < 0)
r = EXIT_FAILURE;
if (strv_extend(&arg_proc_cmdline_disks_done, p) < 0)
return log_oom();
}
}
} else {
if (create_disk(name, device, password, options) < 0)
r = EXIT_FAILURE;
}
}
}
next:
STRV_FOREACH(i, arg_proc_cmdline_disks) {
/*
Generate units for those UUIDs, which were specified
on the kernel command line and not yet written.
*/
_cleanup_free_ char *name = NULL, *device = NULL;
const char *p = *i;
if (startswith(p, "luks-"))
p += 5;
if (strv_contains(arg_proc_cmdline_disks_done, p))
continue;
name = strappend("luks-", p);
device = strappend("UUID=", p);
if (!name || !device)
return log_oom();
if (create_disk(name, device, arg_proc_cmdline_keyfile, "timeout=0") < 0)
r = EXIT_FAILURE;
}
return r;
}