1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include "alloc-util.h"
27 #include "fstab-util.h"
28 #include "generator.h"
32 #include "parse-util.h"
33 #include "path-util.h"
34 #include "proc-cmdline.h"
35 #include "string-util.h"
37 #include "unit-name.h"
40 typedef struct crypto_device
{
48 static const char *arg_dest
= "/tmp";
49 static bool arg_enabled
= true;
50 static bool arg_read_crypttab
= true;
51 static bool arg_whitelist
= false;
52 static Hashmap
*arg_disks
= NULL
;
53 static char *arg_default_options
= NULL
;
54 static char *arg_default_keyfile
= NULL
;
56 static int create_disk(
60 const char *options
) {
62 _cleanup_free_
char *p
= NULL
, *n
= NULL
, *d
= NULL
, *u
= NULL
, *e
= NULL
,
64 _cleanup_fclose_
FILE *f
= NULL
;
66 bool noauto
, nofail
, tmp
, swap
, netdev
;
72 noauto
= fstab_test_yes_no_option(options
, "noauto\0" "auto\0");
73 nofail
= fstab_test_yes_no_option(options
, "nofail\0" "fail\0");
74 tmp
= fstab_test_option(options
, "tmp\0");
75 swap
= fstab_test_option(options
, "swap\0");
76 netdev
= fstab_test_option(options
, "_netdev\0");
79 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name
);
83 e
= unit_name_escape(name
);
87 r
= unit_name_build("systemd-cryptsetup", e
, ".service", &n
);
89 return log_error_errno(r
, "Failed to generate unit name: %m");
91 p
= strjoin(arg_dest
, "/", n
);
95 u
= fstab_node_to_udev_node(device
);
99 r
= unit_name_from_path(u
, ".device", &d
);
101 return log_error_errno(r
, "Failed to generate unit name: %m");
105 return log_error_errno(errno
, "Failed to create unit file %s: %m", p
);
108 "# Automatically generated by systemd-cryptsetup-generator\n\n"
110 "Description=Cryptography Setup for %%I\n"
111 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
112 "SourcePath=/etc/crypttab\n"
113 "DefaultDependencies=no\n"
114 "Conflicts=umount.target\n"
115 "IgnoreOnIsolate=true\n"
117 netdev
? "remote-fs-pre.target" : "cryptsetup-pre.target");
122 netdev
? "remote-cryptsetup.target" : "cryptsetup.target");
125 if (STR_IN_SET(password
, "/dev/urandom", "/dev/random", "/dev/hw_random"))
126 fputs_unlocked("After=systemd-random-seed.service\n", f
);
127 else if (!STR_IN_SET(password
, "-", "none")) {
128 _cleanup_free_
char *uu
;
130 uu
= fstab_node_to_udev_node(password
);
134 if (!path_equal(uu
, "/dev/null")) {
136 if (path_startswith(uu
, "/dev/")) {
137 _cleanup_free_
char *dd
= NULL
;
139 r
= unit_name_from_path(uu
, ".device", &dd
);
141 return log_error_errno(r
, "Failed to generate unit name: %m");
143 fprintf(f
, "After=%1$s\nRequires=%1$s\n", dd
);
145 fprintf(f
, "RequiresMountsFor=%s\n", password
);
150 if (path_startswith(u
, "/dev/")) {
154 "Before=umount.target\n",
158 fputs_unlocked("Before=dev-mapper-%i.swap\n",
162 "RequiresMountsFor=%s\n",
165 r
= generator_write_timeouts(arg_dest
, device
, name
, options
, &filtered
);
172 "RemainAfterExit=yes\n"
173 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
174 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
175 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH
" attach '%s' '%s' '%s' '%s'\n"
176 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH
" detach '%s'\n",
177 name
, u
, strempty(password
), strempty(filtered
),
182 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
187 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
190 r
= fflush_and_check(f
);
192 return log_error_errno(r
, "Failed to write file %s: %m", p
);
195 r
= generator_add_symlink(arg_dest
, d
, "wants", n
);
199 r
= generator_add_symlink(arg_dest
,
200 netdev
? "remote-cryptsetup.target" : "cryptsetup.target",
201 nofail
? "wants" : "requires", n
);
206 dmname
= strjoina("dev-mapper-", e
, ".device");
207 r
= generator_add_symlink(arg_dest
, dmname
, "requires", n
);
211 if (!noauto
&& !nofail
) {
212 r
= write_drop_in(arg_dest
, dmname
, 90, "device-timeout",
213 "# Automatically generated by systemd-cryptsetup-generator \n\n"
214 "[Unit]\nJobTimeoutSec=0");
216 return log_error_errno(r
, "Failed to write device drop-in: %m");
222 static void free_arg_disks(void) {
225 while ((d
= hashmap_steal_first(arg_disks
))) {
233 hashmap_free(arg_disks
);
236 static crypto_device
*get_crypto_device(const char *uuid
) {
242 d
= hashmap_get(arg_disks
, uuid
);
244 d
= new0(struct crypto_device
, 1);
249 d
->keyfile
= d
->options
= d
->name
= NULL
;
251 d
->uuid
= strdup(uuid
);
255 r
= hashmap_put(arg_disks
, d
->uuid
, d
);
265 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
266 _cleanup_free_
char *uuid
= NULL
, *uuid_value
= NULL
;
270 if (streq(key
, "luks")) {
272 r
= value
? parse_boolean(value
) : 1;
274 log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value
);
278 } else if (streq(key
, "luks.crypttab")) {
280 r
= value
? parse_boolean(value
) : 1;
282 log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value
);
284 arg_read_crypttab
= r
;
286 } else if (streq(key
, "luks.uuid")) {
288 if (proc_cmdline_value_missing(key
, value
))
291 d
= get_crypto_device(startswith(value
, "luks-") ? value
+5 : value
);
295 d
->create
= arg_whitelist
= true;
297 } else if (streq(key
, "luks.options")) {
299 if (proc_cmdline_value_missing(key
, value
))
302 r
= sscanf(value
, "%m[0-9a-fA-F-]=%ms", &uuid
, &uuid_value
);
304 d
= get_crypto_device(uuid
);
308 free_and_replace(d
->options
, uuid_value
);
309 } else if (free_and_strdup(&arg_default_options
, value
) < 0)
312 } else if (streq(key
, "luks.key")) {
314 if (proc_cmdline_value_missing(key
, value
))
317 r
= sscanf(value
, "%m[0-9a-fA-F-]=%ms", &uuid
, &uuid_value
);
319 d
= get_crypto_device(uuid
);
323 free_and_replace(d
->keyfile
, uuid_value
);
324 } else if (free_and_strdup(&arg_default_keyfile
, value
) < 0)
327 } else if (streq(key
, "luks.name")) {
329 if (proc_cmdline_value_missing(key
, value
))
332 r
= sscanf(value
, "%m[0-9a-fA-F-]=%ms", &uuid
, &uuid_value
);
334 d
= get_crypto_device(uuid
);
338 d
->create
= arg_whitelist
= true;
340 free_and_replace(d
->name
, uuid_value
);
342 log_warning("Failed to parse luks name switch %s. Ignoring.", value
);
348 static int add_crypttab_devices(void) {
350 unsigned crypttab_line
= 0;
351 _cleanup_fclose_
FILE *f
= NULL
;
353 if (!arg_read_crypttab
)
356 f
= fopen("/etc/crypttab", "re");
359 log_error_errno(errno
, "Failed to open /etc/crypttab: %m");
363 if (fstat(fileno(f
), &st
) < 0) {
364 log_error_errno(errno
, "Failed to stat /etc/crypttab: %m");
370 char line
[LINE_MAX
], *l
, *uuid
;
371 crypto_device
*d
= NULL
;
372 _cleanup_free_
char *name
= NULL
, *device
= NULL
, *keyfile
= NULL
, *options
= NULL
;
374 if (!fgets(line
, sizeof(line
), f
))
380 if (IN_SET(*l
, 0, '#'))
383 k
= sscanf(l
, "%ms %ms %ms %ms", &name
, &device
, &keyfile
, &options
);
384 if (k
< 2 || k
> 4) {
385 log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line
);
389 uuid
= startswith(device
, "UUID=");
391 uuid
= path_startswith(device
, "/dev/disk/by-uuid/");
393 uuid
= startswith(name
, "luks-");
395 d
= hashmap_get(arg_disks
, uuid
);
397 if (arg_whitelist
&& !d
) {
398 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name
);
402 r
= create_disk(name
, device
, keyfile
, (d
&& d
->options
) ? d
->options
: options
);
413 static int add_proc_cmdline_devices(void) {
418 HASHMAP_FOREACH(d
, arg_disks
, i
) {
420 _cleanup_free_
char *device
= NULL
;
426 d
->name
= strappend("luks-", d
->uuid
);
431 device
= strappend("UUID=", d
->uuid
);
436 options
= d
->options
;
437 else if (arg_default_options
)
438 options
= arg_default_options
;
440 options
= "timeout=0";
442 r
= create_disk(d
->name
, device
, d
->keyfile
?: arg_default_keyfile
, options
);
450 int main(int argc
, char *argv
[]) {
453 if (argc
> 1 && argc
!= 4) {
454 log_error("This program takes three or no arguments.");
461 log_set_target(LOG_TARGET_SAFE
);
462 log_parse_environment();
467 arg_disks
= hashmap_new(&string_hash_ops
);
473 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, PROC_CMDLINE_STRIP_RD_PREFIX
);
475 log_warning_errno(r
, "Failed to parse kernel command line: %m");
484 r
= add_crypttab_devices();
488 r
= add_proc_cmdline_devices();
496 free(arg_default_options
);
497 free(arg_default_keyfile
);
499 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;