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");
109 password_escaped
= specifier_escape(password
);
110 if (password
&& !password_escaped
)
113 r
= generator_open_unit_file(arg_dest
, NULL
, n
, &f
);
119 "Description=Cryptography Setup for %%I\n"
120 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
121 "SourcePath=/etc/crypttab\n"
122 "DefaultDependencies=no\n"
123 "Conflicts=umount.target\n"
124 "IgnoreOnIsolate=true\n"
126 netdev
? "remote-fs-pre.target" : "cryptsetup-pre.target");
131 netdev
? "remote-cryptsetup.target" : "cryptsetup.target");
134 if (STR_IN_SET(password
, "/dev/urandom", "/dev/random", "/dev/hw_random"))
135 fputs("After=systemd-random-seed.service\n", f
);
136 else if (!STR_IN_SET(password
, "-", "none")) {
137 _cleanup_free_
char *uu
;
139 uu
= fstab_node_to_udev_node(password
);
143 if (!path_equal(uu
, "/dev/null")) {
145 if (path_startswith(uu
, "/dev/")) {
146 _cleanup_free_
char *dd
= NULL
;
148 r
= unit_name_from_path(uu
, ".device", &dd
);
150 return log_error_errno(r
, "Failed to generate unit name: %m");
152 fprintf(f
, "After=%1$s\nRequires=%1$s\n", dd
);
154 fprintf(f
, "RequiresMountsFor=%s\n", password_escaped
);
159 if (path_startswith(u
, "/dev/")) {
163 "Before=umount.target\n",
167 fputs("Before=dev-mapper-%i.swap\n",
171 "RequiresMountsFor=%s\n",
175 r
= generator_write_timeouts(arg_dest
, device
, name
, options
, &filtered
);
179 filtered_escaped
= specifier_escape(filtered
);
180 if (filtered
&& !filtered_escaped
)
186 "RemainAfterExit=yes\n"
187 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
188 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
189 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH
" attach '%s' '%s' '%s' '%s'\n"
190 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH
" detach '%s'\n",
191 name_escaped
, u_escaped
, strempty(password_escaped
), strempty(filtered_escaped
),
196 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
201 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
204 r
= fflush_and_check(f
);
206 return log_error_errno(r
, "Failed to write unit file %s: %m", n
);
209 r
= generator_add_symlink(arg_dest
, d
, "wants", n
);
213 r
= generator_add_symlink(arg_dest
,
214 netdev
? "remote-cryptsetup.target" : "cryptsetup.target",
215 nofail
? "wants" : "requires", n
);
220 dmname
= strjoina("dev-mapper-", e
, ".device");
221 r
= generator_add_symlink(arg_dest
, dmname
, "requires", n
);
225 if (!noauto
&& !nofail
) {
226 r
= write_drop_in(arg_dest
, dmname
, 90, "device-timeout",
227 "# Automatically generated by systemd-cryptsetup-generator \n\n"
228 "[Unit]\nJobTimeoutSec=0");
230 return log_error_errno(r
, "Failed to write device drop-in: %m");
236 static void crypt_device_free(crypto_device
*d
) {
244 static crypto_device
*get_crypto_device(const char *uuid
) {
250 d
= hashmap_get(arg_disks
, uuid
);
252 d
= new0(struct crypto_device
, 1);
257 d
->keyfile
= d
->options
= d
->name
= NULL
;
259 d
->uuid
= strdup(uuid
);
263 r
= hashmap_put(arg_disks
, d
->uuid
, d
);
273 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
274 _cleanup_free_
char *uuid
= NULL
, *uuid_value
= NULL
;
278 if (streq(key
, "luks")) {
280 r
= value
? parse_boolean(value
) : 1;
282 log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value
);
286 } else if (streq(key
, "luks.crypttab")) {
288 r
= value
? parse_boolean(value
) : 1;
290 log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value
);
292 arg_read_crypttab
= r
;
294 } else if (streq(key
, "luks.uuid")) {
296 if (proc_cmdline_value_missing(key
, value
))
299 d
= get_crypto_device(startswith(value
, "luks-") ? value
+5 : value
);
303 d
->create
= arg_whitelist
= true;
305 } else if (streq(key
, "luks.options")) {
307 if (proc_cmdline_value_missing(key
, value
))
310 r
= sscanf(value
, "%m[0-9a-fA-F-]=%ms", &uuid
, &uuid_value
);
312 d
= get_crypto_device(uuid
);
316 free_and_replace(d
->options
, uuid_value
);
317 } else if (free_and_strdup(&arg_default_options
, value
) < 0)
320 } else if (streq(key
, "luks.key")) {
322 if (proc_cmdline_value_missing(key
, value
))
325 r
= sscanf(value
, "%m[0-9a-fA-F-]=%ms", &uuid
, &uuid_value
);
327 d
= get_crypto_device(uuid
);
331 free_and_replace(d
->keyfile
, uuid_value
);
332 } else if (free_and_strdup(&arg_default_keyfile
, value
) < 0)
335 } else if (streq(key
, "luks.name")) {
337 if (proc_cmdline_value_missing(key
, value
))
340 r
= sscanf(value
, "%m[0-9a-fA-F-]=%ms", &uuid
, &uuid_value
);
342 d
= get_crypto_device(uuid
);
346 d
->create
= arg_whitelist
= true;
348 free_and_replace(d
->name
, uuid_value
);
350 log_warning("Failed to parse luks name switch %s. Ignoring.", value
);
356 static int add_crypttab_devices(void) {
358 unsigned crypttab_line
= 0;
359 _cleanup_fclose_
FILE *f
= NULL
;
361 if (!arg_read_crypttab
)
364 f
= fopen("/etc/crypttab", "re");
367 log_error_errno(errno
, "Failed to open /etc/crypttab: %m");
371 (void) __fsetlocking(f
, FSETLOCKING_BYCALLER
);
373 if (fstat(fileno(f
), &st
) < 0) {
374 log_error_errno(errno
, "Failed to stat /etc/crypttab: %m");
380 char line
[LINE_MAX
], *l
, *uuid
;
381 crypto_device
*d
= NULL
;
382 _cleanup_free_
char *name
= NULL
, *device
= NULL
, *keyfile
= NULL
, *options
= NULL
;
384 if (!fgets(line
, sizeof(line
), f
))
390 if (IN_SET(*l
, 0, '#'))
393 k
= sscanf(l
, "%ms %ms %ms %ms", &name
, &device
, &keyfile
, &options
);
394 if (k
< 2 || k
> 4) {
395 log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line
);
399 uuid
= startswith(device
, "UUID=");
401 uuid
= path_startswith(device
, "/dev/disk/by-uuid/");
403 uuid
= startswith(name
, "luks-");
405 d
= hashmap_get(arg_disks
, uuid
);
407 if (arg_whitelist
&& !d
) {
408 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name
);
412 r
= create_disk(name
, device
, keyfile
, (d
&& d
->options
) ? d
->options
: options
);
423 static int add_proc_cmdline_devices(void) {
428 HASHMAP_FOREACH(d
, arg_disks
, i
) {
430 _cleanup_free_
char *device
= NULL
;
436 d
->name
= strappend("luks-", d
->uuid
);
441 device
= strappend("UUID=", d
->uuid
);
446 options
= d
->options
;
447 else if (arg_default_options
)
448 options
= arg_default_options
;
450 options
= "timeout=0";
452 r
= create_disk(d
->name
, device
, d
->keyfile
?: arg_default_keyfile
, options
);
460 int main(int argc
, char *argv
[]) {
463 if (argc
> 1 && argc
!= 4) {
464 log_error("This program takes three or no arguments.");
471 log_set_target(LOG_TARGET_SAFE
);
472 log_parse_environment();
477 arg_disks
= hashmap_new(&string_hash_ops
);
483 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, PROC_CMDLINE_STRIP_RD_PREFIX
);
485 log_warning_errno(r
, "Failed to parse kernel command line: %m");
494 r
= add_crypttab_devices();
498 r
= add_proc_cmdline_devices();
505 hashmap_free_with_destructor(arg_disks
, crypt_device_free
);
506 free(arg_default_options
);
507 free(arg_default_keyfile
);
509 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;