]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-generator.c
log: remove LOG_TARGET_SAFE pseudo log target
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-generator.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
e23a0ce8
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
e23a0ce8
LP
10 (at your option) any later version.
11
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
5430f7f2 15 Lesser General Public License for more details.
e23a0ce8 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
e23a0ce8
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
e23a0ce8 21#include <errno.h>
0d536673 22#include <stdio_ext.h>
e23a0ce8 23
b5efdb8a 24#include "alloc-util.h"
0fa9e53d 25#include "dropin.h"
3ffd4af2 26#include "fd-util.h"
0d39fa9c 27#include "fileio.h"
07630cea 28#include "fstab-util.h"
0fa9e53d
JJ
29#include "generator.h"
30#include "hashmap.h"
e23a0ce8 31#include "log.h"
49e942b2 32#include "mkdir.h"
6bedfcbb 33#include "parse-util.h"
bde29068 34#include "path-util.h"
4e731273 35#include "proc-cmdline.h"
98bad05e 36#include "specifier.h"
07630cea 37#include "string-util.h"
0fa9e53d
JJ
38#include "strv.h"
39#include "unit-name.h"
40#include "util.h"
41
42typedef struct crypto_device {
43 char *uuid;
6cd5b12a 44 char *keyfile;
baade8cc 45 char *name;
0fa9e53d
JJ
46 char *options;
47 bool create;
48} crypto_device;
e23a0ce8 49
66a78c2b
LP
50static const char *arg_dest = "/tmp";
51static bool arg_enabled = true;
52static bool arg_read_crypttab = true;
0fa9e53d
JJ
53static bool arg_whitelist = false;
54static Hashmap *arg_disks = NULL;
55static char *arg_default_options = NULL;
56static char *arg_default_keyfile = NULL;
141a79f4 57
e23a0ce8
LP
58static int create_disk(
59 const char *name,
60 const char *device,
61 const char *password,
62 const char *options) {
63
fb883e75 64 _cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
98bad05e 65 *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL;
7fd1b19b 66 _cleanup_fclose_ FILE *f = NULL;
b559616f 67 const char *dmname;
b001ad61 68 bool noauto, nofail, tmp, swap, netdev;
744198e9 69 int r;
e23a0ce8
LP
70
71 assert(name);
72 assert(device);
73
b9f111b9
ZJS
74 noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
75 nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
a6dba978
ZJS
76 tmp = fstab_test_option(options, "tmp\0");
77 swap = fstab_test_option(options, "swap\0");
b001ad61 78 netdev = fstab_test_option(options, "_netdev\0");
8c11d3c1
TG
79
80 if (tmp && swap) {
81 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
82 return -EINVAL;
83 }
155da457 84
98bad05e
LP
85 name_escaped = specifier_escape(name);
86 if (!name_escaped)
87 return log_oom();
88
744198e9
LP
89 e = unit_name_escape(name);
90 if (!e)
91 return log_oom();
92
f7f21d33 93 u = fstab_node_to_udev_node(device);
24a988e9
HH
94 if (!u)
95 return log_oom();
e23a0ce8 96
fb883e75
ZJS
97 r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
98 if (r < 0)
99 return log_error_errno(r, "Failed to generate unit name: %m");
100
98bad05e
LP
101 u_escaped = specifier_escape(u);
102 if (!u_escaped)
103 return log_oom();
104
7410616c
LP
105 r = unit_name_from_path(u, ".device", &d);
106 if (r < 0)
107 return log_error_errno(r, "Failed to generate unit name: %m");
e23a0ce8 108
d31eb24f
LP
109 if (password) {
110 password_escaped = specifier_escape(password);
111 if (!password_escaped)
112 return log_oom();
113 }
98bad05e 114
fb883e75
ZJS
115 r = generator_open_unit_file(arg_dest, NULL, n, &f);
116 if (r < 0)
117 return r;
0d536673 118
b001ad61 119 fprintf(f,
b001ad61
ZJS
120 "[Unit]\n"
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"
127 "After=%s\n",
a0dd2097 128 netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
e23a0ce8 129
155da457
LP
130 if (!nofail)
131 fprintf(f,
b001ad61
ZJS
132 "Before=%s\n",
133 netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
155da457 134
ceca9501 135 if (password) {
744198e9 136 if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
0d536673 137 fputs("After=systemd-random-seed.service\n", f);
b559616f 138 else if (!STR_IN_SET(password, "-", "none")) {
744198e9
LP
139 _cleanup_free_ char *uu;
140
141 uu = fstab_node_to_udev_node(password);
142 if (!uu)
66a5dbdf
DR
143 return log_oom();
144
bde29068 145 if (!path_equal(uu, "/dev/null")) {
744198e9 146
048dd629 147 if (path_startswith(uu, "/dev/")) {
7410616c 148 _cleanup_free_ char *dd = NULL;
66a5dbdf 149
7410616c
LP
150 r = unit_name_from_path(uu, ".device", &dd);
151 if (r < 0)
152 return log_error_errno(r, "Failed to generate unit name: %m");
bde29068
LP
153
154 fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
155 } else
98bad05e 156 fprintf(f, "RequiresMountsFor=%s\n", password_escaped);
bde29068 157 }
66a5dbdf 158 }
ceca9501 159 }
e23a0ce8 160
048dd629 161 if (path_startswith(u, "/dev/")) {
9ece938a
TW
162 fprintf(f,
163 "BindsTo=%s\n"
164 "After=%s\n"
165 "Before=umount.target\n",
166 d, d);
a6f8786a
MFO
167
168 if (swap)
0d536673
LP
169 fputs("Before=dev-mapper-%i.swap\n",
170 f);
a6f8786a 171 } else
9ece938a
TW
172 fprintf(f,
173 "RequiresMountsFor=%s\n",
98bad05e
LP
174 u_escaped);
175
9ece938a 176
8eea8687
ZJS
177 r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
178 if (r < 0)
179 return r;
180
d31eb24f
LP
181 if (filtered) {
182 filtered_escaped = specifier_escape(filtered);
183 if (!filtered_escaped)
184 return log_oom();
185 }
98bad05e 186
e23a0ce8
LP
187 fprintf(f,
188 "\n[Service]\n"
189 "Type=oneshot\n"
190 "RemainAfterExit=yes\n"
90724929 191 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
0b1f68ac 192 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
260ab287 193 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
7f4e0805 194 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
98bad05e
LP
195 name_escaped, u_escaped, strempty(password_escaped), strempty(filtered_escaped),
196 name_escaped);
e23a0ce8 197
8c11d3c1 198 if (tmp)
e23a0ce8 199 fprintf(f,
50109038 200 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
98bad05e 201 name_escaped);
e23a0ce8 202
8c11d3c1 203 if (swap)
e23a0ce8 204 fprintf(f,
50109038 205 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
98bad05e 206 name_escaped);
e23a0ce8 207
4652c56c
ZJS
208 r = fflush_and_check(f);
209 if (r < 0)
fb883e75 210 return log_error_errno(r, "Failed to write unit file %s: %m", n);
e23a0ce8 211
155da457 212 if (!noauto) {
b559616f
ZJS
213 r = generator_add_symlink(arg_dest, d, "wants", n);
214 if (r < 0)
215 return r;
e23a0ce8 216
b001ad61
ZJS
217 r = generator_add_symlink(arg_dest,
218 netdev ? "remote-cryptsetup.target" : "cryptsetup.target",
b559616f
ZJS
219 nofail ? "wants" : "requires", n);
220 if (r < 0)
221 return r;
f7f21d33 222 }
74715b82 223
b559616f
ZJS
224 dmname = strjoina("dev-mapper-", e, ".device");
225 r = generator_add_symlink(arg_dest, dmname, "requires", n);
226 if (r < 0)
227 return r;
74715b82 228
68395007 229 if (!noauto && !nofail) {
a6fb0dc1 230 r = write_drop_in(arg_dest, dmname, 90, "device-timeout",
8eea8687
ZJS
231 "# Automatically generated by systemd-cryptsetup-generator \n\n"
232 "[Unit]\nJobTimeoutSec=0");
23bbb0de
MS
233 if (r < 0)
234 return log_error_errno(r, "Failed to write device drop-in: %m");
68395007
HH
235 }
236
24a988e9 237 return 0;
e23a0ce8
LP
238}
239
6dd1c368
ZJS
240static void crypt_device_free(crypto_device *d) {
241 free(d->uuid);
242 free(d->keyfile);
243 free(d->name);
244 free(d->options);
245 free(d);
0fa9e53d
JJ
246}
247
248static crypto_device *get_crypto_device(const char *uuid) {
249 int r;
250 crypto_device *d;
251
252 assert(uuid);
253
254 d = hashmap_get(arg_disks, uuid);
255 if (!d) {
256 d = new0(struct crypto_device, 1);
257 if (!d)
258 return NULL;
259
260 d->create = false;
baade8cc 261 d->keyfile = d->options = d->name = NULL;
0fa9e53d
JJ
262
263 d->uuid = strdup(uuid);
6b430fdb
ZJS
264 if (!d->uuid)
265 return mfree(d);
0fa9e53d
JJ
266
267 r = hashmap_put(arg_disks, d->uuid, d);
268 if (r < 0) {
269 free(d->uuid);
6b430fdb 270 return mfree(d);
0fa9e53d
JJ
271 }
272 }
273
274 return d;
275}
276
96287a49 277static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
0fa9e53d 278 _cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
1d84ad94
LP
279 crypto_device *d;
280 int r;
66a78c2b 281
1d84ad94 282 if (streq(key, "luks")) {
059cb385 283
1d84ad94 284 r = value ? parse_boolean(value) : 1;
141a79f4 285 if (r < 0)
1d84ad94 286 log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value);
141a79f4
ZJS
287 else
288 arg_enabled = r;
66a78c2b 289
1d84ad94 290 } else if (streq(key, "luks.crypttab")) {
66a78c2b 291
1d84ad94 292 r = value ? parse_boolean(value) : 1;
141a79f4 293 if (r < 0)
1d84ad94 294 log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value);
141a79f4
ZJS
295 else
296 arg_read_crypttab = r;
66a78c2b 297
1d84ad94
LP
298 } else if (streq(key, "luks.uuid")) {
299
300 if (proc_cmdline_value_missing(key, value))
301 return 0;
66a78c2b 302
0fa9e53d
JJ
303 d = get_crypto_device(startswith(value, "luks-") ? value+5 : value);
304 if (!d)
141a79f4 305 return log_oom();
66a78c2b 306
0fa9e53d
JJ
307 d->create = arg_whitelist = true;
308
1d84ad94
LP
309 } else if (streq(key, "luks.options")) {
310
311 if (proc_cmdline_value_missing(key, value))
312 return 0;
66a78c2b 313
0fa9e53d
JJ
314 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
315 if (r == 2) {
316 d = get_crypto_device(uuid);
317 if (!d)
318 return log_oom();
319
1d84ad94 320 free_and_replace(d->options, uuid_value);
0fa9e53d 321 } else if (free_and_strdup(&arg_default_options, value) < 0)
141a79f4 322 return log_oom();
66a78c2b 323
1d84ad94
LP
324 } else if (streq(key, "luks.key")) {
325
326 if (proc_cmdline_value_missing(key, value))
327 return 0;
66a78c2b 328
6cd5b12a
JJ
329 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
330 if (r == 2) {
331 d = get_crypto_device(uuid);
332 if (!d)
333 return log_oom();
334
1d84ad94 335 free_and_replace(d->keyfile, uuid_value);
c802a730 336 } else if (free_and_strdup(&arg_default_keyfile, value) < 0)
141a79f4 337 return log_oom();
7ab064a6 338
1d84ad94
LP
339 } else if (streq(key, "luks.name")) {
340
341 if (proc_cmdline_value_missing(key, value))
342 return 0;
baade8cc
JJ
343
344 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
345 if (r == 2) {
346 d = get_crypto_device(uuid);
347 if (!d)
348 return log_oom();
349
350 d->create = arg_whitelist = true;
351
f9ecfd3b 352 free_and_replace(d->name, uuid_value);
baade8cc
JJ
353 } else
354 log_warning("Failed to parse luks name switch %s. Ignoring.", value);
85013844 355 }
66a78c2b 356
24a988e9 357 return 0;
66a78c2b
LP
358}
359
0fa9e53d
JJ
360static int add_crypttab_devices(void) {
361 struct stat st;
362 unsigned crypttab_line = 0;
7fd1b19b 363 _cleanup_fclose_ FILE *f = NULL;
e23a0ce8 364
0fa9e53d
JJ
365 if (!arg_read_crypttab)
366 return 0;
367
368 f = fopen("/etc/crypttab", "re");
369 if (!f) {
370 if (errno != ENOENT)
371 log_error_errno(errno, "Failed to open /etc/crypttab: %m");
372 return 0;
e23a0ce8
LP
373 }
374
0d536673
LP
375 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
376
0fa9e53d
JJ
377 if (fstat(fileno(f), &st) < 0) {
378 log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
379 return 0;
380 }
e23a0ce8 381
0fa9e53d
JJ
382 for (;;) {
383 int r, k;
384 char line[LINE_MAX], *l, *uuid;
385 crypto_device *d = NULL;
386 _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
4c12626c 387
0fa9e53d
JJ
388 if (!fgets(line, sizeof(line), f))
389 break;
66a78c2b 390
0fa9e53d 391 crypttab_line++;
141a79f4 392
0fa9e53d 393 l = strstrip(line);
4c701096 394 if (IN_SET(*l, 0, '#'))
0fa9e53d 395 continue;
66a78c2b 396
0fa9e53d
JJ
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);
400 continue;
401 }
66a78c2b 402
0fa9e53d
JJ
403 uuid = startswith(device, "UUID=");
404 if (!uuid)
405 uuid = path_startswith(device, "/dev/disk/by-uuid/");
406 if (!uuid)
407 uuid = startswith(name, "luks-");
408 if (uuid)
409 d = hashmap_get(arg_disks, uuid);
8973790e 410
0fa9e53d
JJ
411 if (arg_whitelist && !d) {
412 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
413 continue;
8973790e
LP
414 }
415
0fa9e53d
JJ
416 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
417 if (r < 0)
418 return r;
8973790e 419
0fa9e53d
JJ
420 if (d)
421 d->create = false;
422 }
8973790e 423
0fa9e53d
JJ
424 return 0;
425}
66a78c2b 426
0fa9e53d
JJ
427static int add_proc_cmdline_devices(void) {
428 int r;
429 Iterator i;
430 crypto_device *d;
66a78c2b 431
0fa9e53d
JJ
432 HASHMAP_FOREACH(d, arg_disks, i) {
433 const char *options;
baade8cc 434 _cleanup_free_ char *device = NULL;
66a78c2b 435
0fa9e53d
JJ
436 if (!d->create)
437 continue;
e23a0ce8 438
baade8cc
JJ
439 if (!d->name) {
440 d->name = strappend("luks-", d->uuid);
441 if (!d->name)
442 return log_oom();
443 }
e23a0ce8 444
0fa9e53d
JJ
445 device = strappend("UUID=", d->uuid);
446 if (!device)
447 return log_oom();
7ab064a6 448
0fa9e53d
JJ
449 if (d->options)
450 options = d->options;
451 else if (arg_default_options)
452 options = arg_default_options;
453 else
454 options = "timeout=0";
141a79f4 455
baade8cc 456 r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
0fa9e53d
JJ
457 if (r < 0)
458 return r;
e23a0ce8
LP
459 }
460
0fa9e53d
JJ
461 return 0;
462}
e23a0ce8 463
0fa9e53d 464int main(int argc, char *argv[]) {
5f4bfe56 465 int r;
e23a0ce8 466
0fa9e53d
JJ
467 if (argc > 1 && argc != 4) {
468 log_error("This program takes three or no arguments.");
469 return EXIT_FAILURE;
470 }
e23a0ce8 471
0fa9e53d
JJ
472 if (argc > 1)
473 arg_dest = argv[1];
e23a0ce8 474
6c347d50
LP
475 log_set_prohibit_ipc(true);
476 log_set_target(LOG_TARGET_AUTO);
0fa9e53d
JJ
477 log_parse_environment();
478 log_open();
e2cb60fa 479
0fa9e53d 480 umask(0022);
e23a0ce8 481
0fa9e53d 482 arg_disks = hashmap_new(&string_hash_ops);
5f4bfe56
LP
483 if (!arg_disks) {
484 r = log_oom();
485 goto finish;
486 }
7ab064a6 487
1d84ad94 488 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
0fa9e53d 489 if (r < 0) {
5f4bfe56
LP
490 log_warning_errno(r, "Failed to parse kernel command line: %m");
491 goto finish;
0fa9e53d 492 }
7ab064a6 493
0fa9e53d 494 if (!arg_enabled) {
5f4bfe56
LP
495 r = 0;
496 goto finish;
e23a0ce8
LP
497 }
498
5f4bfe56
LP
499 r = add_crypttab_devices();
500 if (r < 0)
501 goto finish;
0fa9e53d 502
5f4bfe56
LP
503 r = add_proc_cmdline_devices();
504 if (r < 0)
505 goto finish;
0fa9e53d 506
5f4bfe56 507 r = 0;
141a79f4 508
5f4bfe56 509finish:
6dd1c368 510 hashmap_free_with_destructor(arg_disks, crypt_device_free);
0fa9e53d
JJ
511 free(arg_default_options);
512 free(arg_default_keyfile);
141a79f4 513
5f4bfe56 514 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
e23a0ce8 515}