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