]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-generator.c
generators: be more careful when writing unit settings that support specifier expansion
[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"
98bad05e 35#include "specifier.h"
07630cea 36#include "string-util.h"
0fa9e53d
JJ
37#include "strv.h"
38#include "unit-name.h"
39#include "util.h"
40
41typedef struct crypto_device {
42 char *uuid;
6cd5b12a 43 char *keyfile;
baade8cc 44 char *name;
0fa9e53d
JJ
45 char *options;
46 bool create;
47} crypto_device;
e23a0ce8 48
66a78c2b
LP
49static const char *arg_dest = "/tmp";
50static bool arg_enabled = true;
51static bool arg_read_crypttab = true;
0fa9e53d
JJ
52static bool arg_whitelist = false;
53static Hashmap *arg_disks = NULL;
54static char *arg_default_options = NULL;
55static char *arg_default_keyfile = NULL;
141a79f4 56
e23a0ce8
LP
57static int create_disk(
58 const char *name,
59 const char *device,
60 const char *password,
61 const char *options) {
62
b559616f 63 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *e = NULL,
98bad05e 64 *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL;
7fd1b19b 65 _cleanup_fclose_ FILE *f = NULL;
b559616f 66 const char *dmname;
b001ad61 67 bool noauto, nofail, tmp, swap, netdev;
744198e9 68 int r;
e23a0ce8
LP
69
70 assert(name);
71 assert(device);
72
b9f111b9
ZJS
73 noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
74 nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
a6dba978
ZJS
75 tmp = fstab_test_option(options, "tmp\0");
76 swap = fstab_test_option(options, "swap\0");
b001ad61 77 netdev = fstab_test_option(options, "_netdev\0");
8c11d3c1
TG
78
79 if (tmp && swap) {
80 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
81 return -EINVAL;
82 }
155da457 83
98bad05e
LP
84 name_escaped = specifier_escape(name);
85 if (!name_escaped)
86 return log_oom();
87
744198e9
LP
88 e = unit_name_escape(name);
89 if (!e)
90 return log_oom();
91
7410616c
LP
92 r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
93 if (r < 0)
94 return log_error_errno(r, "Failed to generate unit name: %m");
e23a0ce8 95
605405c6 96 p = strjoin(arg_dest, "/", n);
24a988e9
HH
97 if (!p)
98 return log_oom();
e23a0ce8 99
f7f21d33 100 u = fstab_node_to_udev_node(device);
24a988e9
HH
101 if (!u)
102 return log_oom();
e23a0ce8 103
98bad05e
LP
104 u_escaped = specifier_escape(u);
105 if (!u_escaped)
106 return log_oom();
107
7410616c
LP
108 r = unit_name_from_path(u, ".device", &d);
109 if (r < 0)
110 return log_error_errno(r, "Failed to generate unit name: %m");
e23a0ce8 111
98bad05e
LP
112 password_escaped = specifier_escape(password);
113 if (!password_escaped)
114 return log_oom();
115
f7f21d33 116 f = fopen(p, "wxe");
4a62c710
MS
117 if (!f)
118 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
e23a0ce8 119
b001ad61
ZJS
120 fprintf(f,
121 "# Automatically generated by systemd-cryptsetup-generator\n\n"
122 "[Unit]\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"
129 "After=%s\n",
a0dd2097 130 netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
e23a0ce8 131
155da457
LP
132 if (!nofail)
133 fprintf(f,
b001ad61
ZJS
134 "Before=%s\n",
135 netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
155da457 136
ceca9501 137 if (password) {
744198e9 138 if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
4b61c875 139 fputs_unlocked("After=systemd-random-seed.service\n", f);
b559616f 140 else if (!STR_IN_SET(password, "-", "none")) {
744198e9
LP
141 _cleanup_free_ char *uu;
142
143 uu = fstab_node_to_udev_node(password);
144 if (!uu)
66a5dbdf
DR
145 return log_oom();
146
bde29068 147 if (!path_equal(uu, "/dev/null")) {
744198e9 148
048dd629 149 if (path_startswith(uu, "/dev/")) {
7410616c 150 _cleanup_free_ char *dd = NULL;
66a5dbdf 151
7410616c
LP
152 r = unit_name_from_path(uu, ".device", &dd);
153 if (r < 0)
154 return log_error_errno(r, "Failed to generate unit name: %m");
bde29068
LP
155
156 fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
157 } else
98bad05e 158 fprintf(f, "RequiresMountsFor=%s\n", password_escaped);
bde29068 159 }
66a5dbdf 160 }
ceca9501 161 }
e23a0ce8 162
048dd629 163 if (path_startswith(u, "/dev/")) {
9ece938a
TW
164 fprintf(f,
165 "BindsTo=%s\n"
166 "After=%s\n"
167 "Before=umount.target\n",
168 d, d);
a6f8786a
MFO
169
170 if (swap)
4b61c875
LP
171 fputs_unlocked("Before=dev-mapper-%i.swap\n",
172 f);
a6f8786a 173 } else
9ece938a
TW
174 fprintf(f,
175 "RequiresMountsFor=%s\n",
98bad05e
LP
176 u_escaped);
177
9ece938a 178
8eea8687
ZJS
179 r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
180 if (r < 0)
181 return r;
182
98bad05e
LP
183 filtered_escaped = specifier_escape(filtered);
184 if (!filtered_escaped)
185 return log_oom();
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)
210 return log_error_errno(r, "Failed to write file %s: %m", p);
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
0fa9e53d
JJ
375 if (fstat(fileno(f), &st) < 0) {
376 log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
377 return 0;
378 }
e23a0ce8 379
0fa9e53d
JJ
380 for (;;) {
381 int r, k;
382 char line[LINE_MAX], *l, *uuid;
383 crypto_device *d = NULL;
384 _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
4c12626c 385
0fa9e53d
JJ
386 if (!fgets(line, sizeof(line), f))
387 break;
66a78c2b 388
0fa9e53d 389 crypttab_line++;
141a79f4 390
0fa9e53d 391 l = strstrip(line);
4c701096 392 if (IN_SET(*l, 0, '#'))
0fa9e53d 393 continue;
66a78c2b 394
0fa9e53d
JJ
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);
398 continue;
399 }
66a78c2b 400
0fa9e53d
JJ
401 uuid = startswith(device, "UUID=");
402 if (!uuid)
403 uuid = path_startswith(device, "/dev/disk/by-uuid/");
404 if (!uuid)
405 uuid = startswith(name, "luks-");
406 if (uuid)
407 d = hashmap_get(arg_disks, uuid);
8973790e 408
0fa9e53d
JJ
409 if (arg_whitelist && !d) {
410 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
411 continue;
8973790e
LP
412 }
413
0fa9e53d
JJ
414 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
415 if (r < 0)
416 return r;
8973790e 417
0fa9e53d
JJ
418 if (d)
419 d->create = false;
420 }
8973790e 421
0fa9e53d
JJ
422 return 0;
423}
66a78c2b 424
0fa9e53d
JJ
425static int add_proc_cmdline_devices(void) {
426 int r;
427 Iterator i;
428 crypto_device *d;
66a78c2b 429
0fa9e53d
JJ
430 HASHMAP_FOREACH(d, arg_disks, i) {
431 const char *options;
baade8cc 432 _cleanup_free_ char *device = NULL;
66a78c2b 433
0fa9e53d
JJ
434 if (!d->create)
435 continue;
e23a0ce8 436
baade8cc
JJ
437 if (!d->name) {
438 d->name = strappend("luks-", d->uuid);
439 if (!d->name)
440 return log_oom();
441 }
e23a0ce8 442
0fa9e53d
JJ
443 device = strappend("UUID=", d->uuid);
444 if (!device)
445 return log_oom();
7ab064a6 446
0fa9e53d
JJ
447 if (d->options)
448 options = d->options;
449 else if (arg_default_options)
450 options = arg_default_options;
451 else
452 options = "timeout=0";
141a79f4 453
baade8cc 454 r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
0fa9e53d
JJ
455 if (r < 0)
456 return r;
e23a0ce8
LP
457 }
458
0fa9e53d
JJ
459 return 0;
460}
e23a0ce8 461
0fa9e53d 462int main(int argc, char *argv[]) {
5f4bfe56 463 int r;
e23a0ce8 464
0fa9e53d
JJ
465 if (argc > 1 && argc != 4) {
466 log_error("This program takes three or no arguments.");
467 return EXIT_FAILURE;
468 }
e23a0ce8 469
0fa9e53d
JJ
470 if (argc > 1)
471 arg_dest = argv[1];
e23a0ce8 472
0fa9e53d
JJ
473 log_set_target(LOG_TARGET_SAFE);
474 log_parse_environment();
475 log_open();
e2cb60fa 476
0fa9e53d 477 umask(0022);
e23a0ce8 478
0fa9e53d 479 arg_disks = hashmap_new(&string_hash_ops);
5f4bfe56
LP
480 if (!arg_disks) {
481 r = log_oom();
482 goto finish;
483 }
7ab064a6 484
1d84ad94 485 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
0fa9e53d 486 if (r < 0) {
5f4bfe56
LP
487 log_warning_errno(r, "Failed to parse kernel command line: %m");
488 goto finish;
0fa9e53d 489 }
7ab064a6 490
0fa9e53d 491 if (!arg_enabled) {
5f4bfe56
LP
492 r = 0;
493 goto finish;
e23a0ce8
LP
494 }
495
5f4bfe56
LP
496 r = add_crypttab_devices();
497 if (r < 0)
498 goto finish;
0fa9e53d 499
5f4bfe56
LP
500 r = add_proc_cmdline_devices();
501 if (r < 0)
502 goto finish;
0fa9e53d 503
5f4bfe56 504 r = 0;
141a79f4 505
5f4bfe56 506finish:
6dd1c368 507 hashmap_free_with_destructor(arg_disks, crypt_device_free);
0fa9e53d
JJ
508 free(arg_default_options);
509 free(arg_default_keyfile);
141a79f4 510
5f4bfe56 511 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
e23a0ce8 512}