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 "specifier.h"
36 #include "string-util.h"
38 #include "unit-name.h"
41 typedef struct crypto_device
{
49 static const char *arg_dest
= "/tmp";
50 static bool arg_enabled
= true;
51 static bool arg_read_crypttab
= true;
52 static bool arg_whitelist
= false;
53 static Hashmap
*arg_disks
= NULL
;
54 static char *arg_default_options
= NULL
;
55 static char *arg_default_keyfile
= NULL
;
57 static int create_disk(
61 const char *options
) {
63 _cleanup_free_
char *p
= NULL
, *n
= NULL
, *d
= NULL
, *u
= NULL
, *e
= NULL
,
64 *filtered
= NULL
, *u_escaped
= NULL
, *password_escaped
= NULL
, *filtered_escaped
= NULL
, *name_escaped
= NULL
;
65 _cleanup_fclose_
FILE *f
= NULL
;
67 bool noauto
, nofail
, tmp
, swap
, netdev
;
73 noauto
= fstab_test_yes_no_option(options
, "noauto\0" "auto\0");
74 nofail
= fstab_test_yes_no_option(options
, "nofail\0" "fail\0");
75 tmp
= fstab_test_option(options
, "tmp\0");
76 swap
= fstab_test_option(options
, "swap\0");
77 netdev
= fstab_test_option(options
, "_netdev\0");
80 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name
);
84 name_escaped
= specifier_escape(name
);
88 e
= unit_name_escape(name
);
92 r
= unit_name_build("systemd-cryptsetup", e
, ".service", &n
);
94 return log_error_errno(r
, "Failed to generate unit name: %m");
96 p
= strjoin(arg_dest
, "/", n
);
100 u
= fstab_node_to_udev_node(device
);
104 u_escaped
= specifier_escape(u
);
108 r
= unit_name_from_path(u
, ".device", &d
);
110 return log_error_errno(r
, "Failed to generate unit name: %m");
112 password_escaped
= specifier_escape(password
);
113 if (!password_escaped
)
118 return log_error_errno(errno
, "Failed to create unit file %s: %m", p
);
121 "# Automatically generated by systemd-cryptsetup-generator\n\n"
123 "Description=Cryptography Setup for %%I\n"
124 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
125 "SourcePath=/etc/crypttab\n"
126 "DefaultDependencies=no\n"
127 "Conflicts=umount.target\n"
128 "IgnoreOnIsolate=true\n"
130 netdev
? "remote-fs-pre.target" : "cryptsetup-pre.target");
135 netdev
? "remote-cryptsetup.target" : "cryptsetup.target");
138 if (STR_IN_SET(password
, "/dev/urandom", "/dev/random", "/dev/hw_random"))
139 fputs_unlocked("After=systemd-random-seed.service\n", f
);
140 else if (!STR_IN_SET(password
, "-", "none")) {
141 _cleanup_free_
char *uu
;
143 uu
= fstab_node_to_udev_node(password
);
147 if (!path_equal(uu
, "/dev/null")) {
149 if (path_startswith(uu
, "/dev/")) {
150 _cleanup_free_
char *dd
= NULL
;
152 r
= unit_name_from_path(uu
, ".device", &dd
);
154 return log_error_errno(r
, "Failed to generate unit name: %m");
156 fprintf(f
, "After=%1$s\nRequires=%1$s\n", dd
);
158 fprintf(f
, "RequiresMountsFor=%s\n", password_escaped
);
163 if (path_startswith(u
, "/dev/")) {
167 "Before=umount.target\n",
171 fputs_unlocked("Before=dev-mapper-%i.swap\n",
175 "RequiresMountsFor=%s\n",
179 r
= generator_write_timeouts(arg_dest
, device
, name
, options
, &filtered
);
183 filtered_escaped
= specifier_escape(filtered
);
184 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 file %s: %m", p
);
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 if (fstat(fileno(f
), &st
) < 0) {
376 log_error_errno(errno
, "Failed to stat /etc/crypttab: %m");
382 char line
[LINE_MAX
], *l
, *uuid
;
383 crypto_device
*d
= NULL
;
384 _cleanup_free_
char *name
= NULL
, *device
= NULL
, *keyfile
= NULL
, *options
= NULL
;
386 if (!fgets(line
, sizeof(line
), f
))
392 if (IN_SET(*l
, 0, '#'))
395 k
= sscanf(l
, "%ms %ms %ms %ms", &name
, &device
, &keyfile
, &options
);
396 if (k
< 2 || k
> 4) {
397 log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line
);
401 uuid
= startswith(device
, "UUID=");
403 uuid
= path_startswith(device
, "/dev/disk/by-uuid/");
405 uuid
= startswith(name
, "luks-");
407 d
= hashmap_get(arg_disks
, uuid
);
409 if (arg_whitelist
&& !d
) {
410 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name
);
414 r
= create_disk(name
, device
, keyfile
, (d
&& d
->options
) ? d
->options
: options
);
425 static int add_proc_cmdline_devices(void) {
430 HASHMAP_FOREACH(d
, arg_disks
, i
) {
432 _cleanup_free_
char *device
= NULL
;
438 d
->name
= strappend("luks-", d
->uuid
);
443 device
= strappend("UUID=", d
->uuid
);
448 options
= d
->options
;
449 else if (arg_default_options
)
450 options
= arg_default_options
;
452 options
= "timeout=0";
454 r
= create_disk(d
->name
, device
, d
->keyfile
?: arg_default_keyfile
, options
);
462 int main(int argc
, char *argv
[]) {
465 if (argc
> 1 && argc
!= 4) {
466 log_error("This program takes three or no arguments.");
473 log_set_target(LOG_TARGET_SAFE
);
474 log_parse_environment();
479 arg_disks
= hashmap_new(&string_hash_ops
);
485 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, PROC_CMDLINE_STRIP_RD_PREFIX
);
487 log_warning_errno(r
, "Failed to parse kernel command line: %m");
496 r
= add_crypttab_devices();
500 r
= add_proc_cmdline_devices();
507 hashmap_free_with_destructor(arg_disks
, crypt_device_free
);
508 free(arg_default_options
);
509 free(arg_default_keyfile
);
511 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;