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/>.
22 #include <stdio_ext.h>
24 #include "alloc-util.h"
28 #include "fstab-util.h"
29 #include "generator.h"
33 #include "parse-util.h"
34 #include "path-util.h"
35 #include "proc-cmdline.h"
36 #include "specifier.h"
37 #include "string-util.h"
39 #include "unit-name.h"
42 typedef struct crypto_device
{
50 static const char *arg_dest
= "/tmp";
51 static bool arg_enabled
= true;
52 static bool arg_read_crypttab
= true;
53 static bool arg_whitelist
= false;
54 static Hashmap
*arg_disks
= NULL
;
55 static char *arg_default_options
= NULL
;
56 static char *arg_default_keyfile
= NULL
;
58 static int create_disk(
62 const char *options
) {
64 _cleanup_free_
char *n
= NULL
, *d
= NULL
, *u
= NULL
, *e
= NULL
,
65 *filtered
= NULL
, *u_escaped
= NULL
, *password_escaped
= NULL
, *filtered_escaped
= NULL
, *name_escaped
= NULL
;
66 _cleanup_fclose_
FILE *f
= NULL
;
68 bool noauto
, nofail
, tmp
, swap
, netdev
;
74 noauto
= fstab_test_yes_no_option(options
, "noauto\0" "auto\0");
75 nofail
= fstab_test_yes_no_option(options
, "nofail\0" "fail\0");
76 tmp
= fstab_test_option(options
, "tmp\0");
77 swap
= fstab_test_option(options
, "swap\0");
78 netdev
= fstab_test_option(options
, "_netdev\0");
81 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name
);
85 name_escaped
= specifier_escape(name
);
89 e
= unit_name_escape(name
);
93 u
= fstab_node_to_udev_node(device
);
97 r
= unit_name_build("systemd-cryptsetup", e
, ".service", &n
);
99 return log_error_errno(r
, "Failed to generate unit name: %m");
101 u_escaped
= specifier_escape(u
);
105 r
= unit_name_from_path(u
, ".device", &d
);
107 return log_error_errno(r
, "Failed to generate unit name: %m");
110 password_escaped
= specifier_escape(password
);
111 if (!password_escaped
)
115 r
= generator_open_unit_file(arg_dest
, NULL
, n
, &f
);
121 "Description=Cryptography Setup for %%I\n"
122 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
123 "SourcePath=/etc/crypttab\n"
124 "DefaultDependencies=no\n"
125 "Conflicts=umount.target\n"
126 "IgnoreOnIsolate=true\n"
128 netdev
? "remote-fs-pre.target" : "cryptsetup-pre.target");
133 netdev
? "remote-cryptsetup.target" : "cryptsetup.target");
136 if (STR_IN_SET(password
, "/dev/urandom", "/dev/random", "/dev/hw_random"))
137 fputs("After=systemd-random-seed.service\n", f
);
138 else if (!STR_IN_SET(password
, "-", "none")) {
139 _cleanup_free_
char *uu
;
141 uu
= fstab_node_to_udev_node(password
);
145 if (!path_equal(uu
, "/dev/null")) {
147 if (path_startswith(uu
, "/dev/")) {
148 _cleanup_free_
char *dd
= NULL
;
150 r
= unit_name_from_path(uu
, ".device", &dd
);
152 return log_error_errno(r
, "Failed to generate unit name: %m");
154 fprintf(f
, "After=%1$s\nRequires=%1$s\n", dd
);
156 fprintf(f
, "RequiresMountsFor=%s\n", password_escaped
);
161 if (path_startswith(u
, "/dev/")) {
165 "Before=umount.target\n",
169 fputs("Before=dev-mapper-%i.swap\n",
173 "RequiresMountsFor=%s\n",
177 r
= generator_write_timeouts(arg_dest
, device
, name
, options
, &filtered
);
182 filtered_escaped
= specifier_escape(filtered
);
183 if (!filtered_escaped
)
190 "RemainAfterExit=yes\n"
191 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
192 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
193 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH
" attach '%s' '%s' '%s' '%s'\n"
194 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH
" detach '%s'\n",
195 name_escaped
, u_escaped
, strempty(password_escaped
), strempty(filtered_escaped
),
200 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
205 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
208 r
= fflush_and_check(f
);
210 return log_error_errno(r
, "Failed to write unit file %s: %m", n
);
213 r
= generator_add_symlink(arg_dest
, d
, "wants", n
);
217 r
= generator_add_symlink(arg_dest
,
218 netdev
? "remote-cryptsetup.target" : "cryptsetup.target",
219 nofail
? "wants" : "requires", n
);
224 dmname
= strjoina("dev-mapper-", e
, ".device");
225 r
= generator_add_symlink(arg_dest
, dmname
, "requires", n
);
229 if (!noauto
&& !nofail
) {
230 r
= write_drop_in(arg_dest
, dmname
, 90, "device-timeout",
231 "# Automatically generated by systemd-cryptsetup-generator \n\n"
232 "[Unit]\nJobTimeoutSec=0");
234 return log_error_errno(r
, "Failed to write device drop-in: %m");
240 static void crypt_device_free(crypto_device
*d
) {
248 static crypto_device
*get_crypto_device(const char *uuid
) {
254 d
= hashmap_get(arg_disks
, uuid
);
256 d
= new0(struct crypto_device
, 1);
261 d
->keyfile
= d
->options
= d
->name
= NULL
;
263 d
->uuid
= strdup(uuid
);
267 r
= hashmap_put(arg_disks
, d
->uuid
, d
);
277 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
278 _cleanup_free_
char *uuid
= NULL
, *uuid_value
= NULL
;
282 if (streq(key
, "luks")) {
284 r
= value
? parse_boolean(value
) : 1;
286 log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value
);
290 } else if (streq(key
, "luks.crypttab")) {
292 r
= value
? parse_boolean(value
) : 1;
294 log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value
);
296 arg_read_crypttab
= r
;
298 } else if (streq(key
, "luks.uuid")) {
300 if (proc_cmdline_value_missing(key
, value
))
303 d
= get_crypto_device(startswith(value
, "luks-") ? value
+5 : value
);
307 d
->create
= arg_whitelist
= true;
309 } else if (streq(key
, "luks.options")) {
311 if (proc_cmdline_value_missing(key
, value
))
314 r
= sscanf(value
, "%m[0-9a-fA-F-]=%ms", &uuid
, &uuid_value
);
316 d
= get_crypto_device(uuid
);
320 free_and_replace(d
->options
, uuid_value
);
321 } else if (free_and_strdup(&arg_default_options
, value
) < 0)
324 } else if (streq(key
, "luks.key")) {
326 if (proc_cmdline_value_missing(key
, value
))
329 r
= sscanf(value
, "%m[0-9a-fA-F-]=%ms", &uuid
, &uuid_value
);
331 d
= get_crypto_device(uuid
);
335 free_and_replace(d
->keyfile
, uuid_value
);
336 } else if (free_and_strdup(&arg_default_keyfile
, value
) < 0)
339 } else if (streq(key
, "luks.name")) {
341 if (proc_cmdline_value_missing(key
, value
))
344 r
= sscanf(value
, "%m[0-9a-fA-F-]=%ms", &uuid
, &uuid_value
);
346 d
= get_crypto_device(uuid
);
350 d
->create
= arg_whitelist
= true;
352 free_and_replace(d
->name
, uuid_value
);
354 log_warning("Failed to parse luks name switch %s. Ignoring.", value
);
360 static int add_crypttab_devices(void) {
362 unsigned crypttab_line
= 0;
363 _cleanup_fclose_
FILE *f
= NULL
;
365 if (!arg_read_crypttab
)
368 f
= fopen("/etc/crypttab", "re");
371 log_error_errno(errno
, "Failed to open /etc/crypttab: %m");
375 (void) __fsetlocking(f
, FSETLOCKING_BYCALLER
);
377 if (fstat(fileno(f
), &st
) < 0) {
378 log_error_errno(errno
, "Failed to stat /etc/crypttab: %m");
384 char line
[LINE_MAX
], *l
, *uuid
;
385 crypto_device
*d
= NULL
;
386 _cleanup_free_
char *name
= NULL
, *device
= NULL
, *keyfile
= NULL
, *options
= NULL
;
388 if (!fgets(line
, sizeof(line
), f
))
394 if (IN_SET(*l
, 0, '#'))
397 k
= sscanf(l
, "%ms %ms %ms %ms", &name
, &device
, &keyfile
, &options
);
398 if (k
< 2 || k
> 4) {
399 log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line
);
403 uuid
= startswith(device
, "UUID=");
405 uuid
= path_startswith(device
, "/dev/disk/by-uuid/");
407 uuid
= startswith(name
, "luks-");
409 d
= hashmap_get(arg_disks
, uuid
);
411 if (arg_whitelist
&& !d
) {
412 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name
);
416 r
= create_disk(name
, device
, keyfile
, (d
&& d
->options
) ? d
->options
: options
);
427 static int add_proc_cmdline_devices(void) {
432 HASHMAP_FOREACH(d
, arg_disks
, i
) {
434 _cleanup_free_
char *device
= NULL
;
440 d
->name
= strappend("luks-", d
->uuid
);
445 device
= strappend("UUID=", d
->uuid
);
450 options
= d
->options
;
451 else if (arg_default_options
)
452 options
= arg_default_options
;
454 options
= "timeout=0";
456 r
= create_disk(d
->name
, device
, d
->keyfile
?: arg_default_keyfile
, options
);
464 int main(int argc
, char *argv
[]) {
467 if (argc
> 1 && argc
!= 4) {
468 log_error("This program takes three or no arguments.");
475 log_set_prohibit_ipc(true);
476 log_set_target(LOG_TARGET_AUTO
);
477 log_parse_environment();
482 arg_disks
= hashmap_new(&string_hash_ops
);
488 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, PROC_CMDLINE_STRIP_RD_PREFIX
);
490 log_warning_errno(r
, "Failed to parse kernel command line: %m");
499 r
= add_crypttab_devices();
503 r
= add_proc_cmdline_devices();
510 hashmap_free_with_destructor(arg_disks
, crypt_device_free
);
511 free(arg_default_options
);
512 free(arg_default_keyfile
);
514 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;