1 /* SPDX-License-Identifier: LGPL-2.1+ */
6 #include "alloc-util.h"
11 #include "fstab-util.h"
12 #include "generator.h"
14 #include "id128-util.h"
17 #include "parse-util.h"
18 #include "path-util.h"
19 #include "proc-cmdline.h"
20 #include "specifier.h"
21 #include "string-util.h"
23 #include "unit-name.h"
26 typedef struct crypto_device
{
35 static const char *arg_dest
= "/tmp";
36 static bool arg_enabled
= true;
37 static bool arg_read_crypttab
= true;
38 static bool arg_whitelist
= false;
39 static Hashmap
*arg_disks
= NULL
;
40 static char *arg_default_options
= NULL
;
41 static char *arg_default_keyfile
= NULL
;
43 static int generate_keydev_mount(const char *name
, const char *keydev
, char **unit
, char **mount
) {
44 _cleanup_free_
char *u
= NULL
, *what
= NULL
, *where
= NULL
, *name_escaped
= NULL
;
45 _cleanup_fclose_
FILE *f
= NULL
;
53 r
= mkdir_parents("/run/systemd/cryptsetup", 0755);
57 r
= mkdir("/run/systemd/cryptsetup", 0700);
58 if (r
< 0 && errno
!= EEXIST
)
61 name_escaped
= cescape(name
);
65 where
= strjoin("/run/systemd/cryptsetup/keydev-", name_escaped
);
69 r
= mkdir(where
, 0700);
70 if (r
< 0 && errno
!= EEXIST
)
73 r
= unit_name_from_path(where
, ".mount", &u
);
77 r
= generator_open_unit_file(arg_dest
, NULL
, u
, &f
);
81 what
= fstab_node_to_udev_node(keydev
);
87 "DefaultDependencies=no\n\n"
91 "Options=ro\n", what
, where
);
93 r
= fflush_and_check(f
);
98 *mount
= TAKE_PTR(where
);
103 static int create_disk(
107 const char *password
,
108 const char *options
) {
110 _cleanup_free_
char *n
= NULL
, *d
= NULL
, *u
= NULL
, *e
= NULL
,
111 *filtered
= NULL
, *u_escaped
= NULL
, *password_escaped
= NULL
, *filtered_escaped
= NULL
, *name_escaped
= NULL
, *keydev_mount
= NULL
;
112 _cleanup_fclose_
FILE *f
= NULL
;
114 bool noauto
, nofail
, tmp
, swap
, netdev
;
120 noauto
= fstab_test_yes_no_option(options
, "noauto\0" "auto\0");
121 nofail
= fstab_test_yes_no_option(options
, "nofail\0" "fail\0");
122 tmp
= fstab_test_option(options
, "tmp\0");
123 swap
= fstab_test_option(options
, "swap\0");
124 netdev
= fstab_test_option(options
, "_netdev\0");
127 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
128 "Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.",
131 name_escaped
= specifier_escape(name
);
135 e
= unit_name_escape(name
);
139 u
= fstab_node_to_udev_node(device
);
143 r
= unit_name_build("systemd-cryptsetup", e
, ".service", &n
);
145 return log_error_errno(r
, "Failed to generate unit name: %m");
147 u_escaped
= specifier_escape(u
);
151 r
= unit_name_from_path(u
, ".device", &d
);
153 return log_error_errno(r
, "Failed to generate unit name: %m");
156 password_escaped
= specifier_escape(password
);
157 if (!password_escaped
)
161 if (keydev
&& !password
)
162 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
163 "Key device is specified, but path to the password file is missing.");
165 r
= generator_open_unit_file(arg_dest
, NULL
, n
, &f
);
171 "Description=Cryptography Setup for %%I\n"
172 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
173 "SourcePath=/etc/crypttab\n"
174 "DefaultDependencies=no\n"
175 "Conflicts=umount.target\n"
176 "IgnoreOnIsolate=true\n"
178 netdev
? "remote-fs-pre.target" : "cryptsetup-pre.target");
181 _cleanup_free_
char *unit
= NULL
, *p
= NULL
;
183 r
= generate_keydev_mount(name
, keydev
, &unit
, &keydev_mount
);
185 return log_error_errno(r
, "Failed to generate keydev mount unit: %m");
187 p
= prefix_root(keydev_mount
, password_escaped
);
191 free_and_replace(password_escaped
, p
);
197 netdev
? "remote-cryptsetup.target" : "cryptsetup.target");
200 if (PATH_IN_SET(password
, "/dev/urandom", "/dev/random", "/dev/hw_random"))
201 fputs("After=systemd-random-seed.service\n", f
);
202 else if (!STR_IN_SET(password
, "-", "none")) {
203 _cleanup_free_
char *uu
;
205 uu
= fstab_node_to_udev_node(password
);
209 if (!path_equal(uu
, "/dev/null")) {
211 if (path_startswith(uu
, "/dev/")) {
212 _cleanup_free_
char *dd
= NULL
;
214 r
= unit_name_from_path(uu
, ".device", &dd
);
216 return log_error_errno(r
, "Failed to generate unit name: %m");
218 fprintf(f
, "After=%1$s\nRequires=%1$s\n", dd
);
220 fprintf(f
, "RequiresMountsFor=%s\n", password_escaped
);
225 if (path_startswith(u
, "/dev/")) {
229 "Before=umount.target\n",
233 fputs("Before=dev-mapper-%i.swap\n",
236 /* For loopback devices, add systemd-tmpfiles-setup-dev.service
237 dependency to ensure that loopback support is available in
238 the kernel (/dev/loop-control needs to exist) */
240 "RequiresMountsFor=%s\n"
241 "Requires=systemd-tmpfiles-setup-dev.service\n"
242 "After=systemd-tmpfiles-setup-dev.service\n",
245 r
= generator_write_timeouts(arg_dest
, device
, name
, options
, &filtered
);
250 filtered_escaped
= specifier_escape(filtered
);
251 if (!filtered_escaped
)
258 "RemainAfterExit=yes\n"
259 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
260 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
261 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH
" attach '%s' '%s' '%s' '%s'\n"
262 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH
" detach '%s'\n",
263 name_escaped
, u_escaped
, strempty(password_escaped
), strempty(filtered_escaped
),
268 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
273 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
278 "ExecStartPost=" UMOUNT_PATH
" %s\n\n",
281 r
= fflush_and_check(f
);
283 return log_error_errno(r
, "Failed to write unit file %s: %m", n
);
286 r
= generator_add_symlink(arg_dest
, d
, "wants", n
);
290 r
= generator_add_symlink(arg_dest
,
291 netdev
? "remote-cryptsetup.target" : "cryptsetup.target",
292 nofail
? "wants" : "requires", n
);
297 dmname
= strjoina("dev-mapper-", e
, ".device");
298 r
= generator_add_symlink(arg_dest
, dmname
, "requires", n
);
302 if (!noauto
&& !nofail
) {
303 r
= write_drop_in(arg_dest
, dmname
, 90, "device-timeout",
304 "# Automatically generated by systemd-cryptsetup-generator \n\n"
305 "[Unit]\nJobTimeoutSec=0");
307 return log_error_errno(r
, "Failed to write device drop-in: %m");
313 static void crypt_device_free(crypto_device
*d
) {
322 static crypto_device
*get_crypto_device(const char *uuid
) {
328 d
= hashmap_get(arg_disks
, uuid
);
330 d
= new0(struct crypto_device
, 1);
335 d
->keyfile
= d
->options
= d
->name
= NULL
;
337 d
->uuid
= strdup(uuid
);
341 r
= hashmap_put(arg_disks
, d
->uuid
, d
);
351 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
352 _cleanup_free_
char *uuid
= NULL
, *uuid_value
= NULL
;
356 if (streq(key
, "luks")) {
358 r
= value
? parse_boolean(value
) : 1;
360 log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value
);
364 } else if (streq(key
, "luks.crypttab")) {
366 r
= value
? parse_boolean(value
) : 1;
368 log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value
);
370 arg_read_crypttab
= r
;
372 } else if (streq(key
, "luks.uuid")) {
374 if (proc_cmdline_value_missing(key
, value
))
377 d
= get_crypto_device(startswith(value
, "luks-") ? value
+5 : value
);
381 d
->create
= arg_whitelist
= true;
383 } else if (streq(key
, "luks.options")) {
385 if (proc_cmdline_value_missing(key
, value
))
388 r
= sscanf(value
, "%m[0-9a-fA-F-]=%ms", &uuid
, &uuid_value
);
390 d
= get_crypto_device(uuid
);
394 free_and_replace(d
->options
, uuid_value
);
395 } else if (free_and_strdup(&arg_default_options
, value
) < 0)
398 } else if (streq(key
, "luks.key")) {
400 _cleanup_free_
char *keyfile
= NULL
, *keydev
= NULL
;
404 if (proc_cmdline_value_missing(key
, value
))
407 n
= strspn(value
, LETTERS DIGITS
"-");
408 if (value
[n
] != '=') {
409 if (free_and_strdup(&arg_default_keyfile
, value
) < 0)
414 uuid
= strndup(value
, n
);
418 if (!id128_is_valid(uuid
)) {
419 log_warning("Failed to parse luks.key= kernel command line switch. UUID is invalid, ignoring.");
423 d
= get_crypto_device(uuid
);
427 keyspec
= value
+ n
+ 1;
428 c
= strrchr(keyspec
, ':');
431 keyfile
= strdup(keyspec
);
432 keydev
= strdup(c
+ 1);
434 if (!keyfile
|| !keydev
)
437 /* No keydev specified */
438 keyfile
= strdup(keyspec
);
443 free_and_replace(d
->keyfile
, keyfile
);
444 free_and_replace(d
->keydev
, keydev
);
445 } else if (streq(key
, "luks.name")) {
447 if (proc_cmdline_value_missing(key
, value
))
450 r
= sscanf(value
, "%m[0-9a-fA-F-]=%ms", &uuid
, &uuid_value
);
452 d
= get_crypto_device(uuid
);
456 d
->create
= arg_whitelist
= true;
458 free_and_replace(d
->name
, uuid_value
);
460 log_warning("Failed to parse luks name switch %s. Ignoring.", value
);
466 static int add_crypttab_devices(void) {
467 _cleanup_fclose_
FILE *f
= NULL
;
468 unsigned crypttab_line
= 0;
472 if (!arg_read_crypttab
)
475 f
= fopen("/etc/crypttab", "re");
478 log_error_errno(errno
, "Failed to open /etc/crypttab: %m");
482 (void) __fsetlocking(f
, FSETLOCKING_BYCALLER
);
484 if (fstat(fileno(f
), &st
) < 0) {
485 log_error_errno(errno
, "Failed to stat /etc/crypttab: %m");
490 _cleanup_free_
char *line
= NULL
, *name
= NULL
, *device
= NULL
, *keyfile
= NULL
, *options
= NULL
;
491 crypto_device
*d
= NULL
;
495 r
= read_line(f
, LONG_LINE_MAX
, &line
);
497 return log_error_errno(r
, "Failed to read /etc/crypttab: %m");
504 if (IN_SET(l
[0], 0, '#'))
507 k
= sscanf(l
, "%ms %ms %ms %ms", &name
, &device
, &keyfile
, &options
);
508 if (k
< 2 || k
> 4) {
509 log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line
);
513 uuid
= startswith(device
, "UUID=");
515 uuid
= path_startswith(device
, "/dev/disk/by-uuid/");
517 uuid
= startswith(name
, "luks-");
519 d
= hashmap_get(arg_disks
, uuid
);
521 if (arg_whitelist
&& !d
) {
522 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name
);
526 r
= create_disk(name
, device
, NULL
, keyfile
, (d
&& d
->options
) ? d
->options
: options
);
537 static int add_proc_cmdline_devices(void) {
542 HASHMAP_FOREACH(d
, arg_disks
, i
) {
544 _cleanup_free_
char *device
= NULL
;
550 d
->name
= strappend("luks-", d
->uuid
);
555 device
= strappend("UUID=", d
->uuid
);
560 options
= d
->options
;
561 else if (arg_default_options
)
562 options
= arg_default_options
;
564 options
= "timeout=0";
566 r
= create_disk(d
->name
, device
, d
->keydev
, d
->keyfile
?: arg_default_keyfile
, options
);
574 int main(int argc
, char *argv
[]) {
577 if (argc
> 1 && argc
!= 4) {
578 log_error("This program takes three or no arguments.");
585 log_setup_generator();
587 arg_disks
= hashmap_new(&string_hash_ops
);
593 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, PROC_CMDLINE_STRIP_RD_PREFIX
);
595 log_warning_errno(r
, "Failed to parse kernel command line: %m");
604 r
= add_crypttab_devices();
608 r
= add_proc_cmdline_devices();
615 hashmap_free_with_destructor(arg_disks
, crypt_device_free
);
616 free(arg_default_options
);
617 free(arg_default_keyfile
);
619 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;