]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-generator.c
core: enable specifier expansion for What=/Where=/Type=/SourcePath= too
[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
6dd1c368
ZJS
222static void crypt_device_free(crypto_device *d) {
223 free(d->uuid);
224 free(d->keyfile);
225 free(d->name);
226 free(d->options);
227 free(d);
0fa9e53d
JJ
228}
229
230static crypto_device *get_crypto_device(const char *uuid) {
231 int r;
232 crypto_device *d;
233
234 assert(uuid);
235
236 d = hashmap_get(arg_disks, uuid);
237 if (!d) {
238 d = new0(struct crypto_device, 1);
239 if (!d)
240 return NULL;
241
242 d->create = false;
baade8cc 243 d->keyfile = d->options = d->name = NULL;
0fa9e53d
JJ
244
245 d->uuid = strdup(uuid);
6b430fdb
ZJS
246 if (!d->uuid)
247 return mfree(d);
0fa9e53d
JJ
248
249 r = hashmap_put(arg_disks, d->uuid, d);
250 if (r < 0) {
251 free(d->uuid);
6b430fdb 252 return mfree(d);
0fa9e53d
JJ
253 }
254 }
255
256 return d;
257}
258
96287a49 259static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
0fa9e53d 260 _cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
1d84ad94
LP
261 crypto_device *d;
262 int r;
66a78c2b 263
1d84ad94 264 if (streq(key, "luks")) {
059cb385 265
1d84ad94 266 r = value ? parse_boolean(value) : 1;
141a79f4 267 if (r < 0)
1d84ad94 268 log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value);
141a79f4
ZJS
269 else
270 arg_enabled = r;
66a78c2b 271
1d84ad94 272 } else if (streq(key, "luks.crypttab")) {
66a78c2b 273
1d84ad94 274 r = value ? parse_boolean(value) : 1;
141a79f4 275 if (r < 0)
1d84ad94 276 log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value);
141a79f4
ZJS
277 else
278 arg_read_crypttab = r;
66a78c2b 279
1d84ad94
LP
280 } else if (streq(key, "luks.uuid")) {
281
282 if (proc_cmdline_value_missing(key, value))
283 return 0;
66a78c2b 284
0fa9e53d
JJ
285 d = get_crypto_device(startswith(value, "luks-") ? value+5 : value);
286 if (!d)
141a79f4 287 return log_oom();
66a78c2b 288
0fa9e53d
JJ
289 d->create = arg_whitelist = true;
290
1d84ad94
LP
291 } else if (streq(key, "luks.options")) {
292
293 if (proc_cmdline_value_missing(key, value))
294 return 0;
66a78c2b 295
0fa9e53d
JJ
296 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
297 if (r == 2) {
298 d = get_crypto_device(uuid);
299 if (!d)
300 return log_oom();
301
1d84ad94 302 free_and_replace(d->options, uuid_value);
0fa9e53d 303 } else if (free_and_strdup(&arg_default_options, value) < 0)
141a79f4 304 return log_oom();
66a78c2b 305
1d84ad94
LP
306 } else if (streq(key, "luks.key")) {
307
308 if (proc_cmdline_value_missing(key, value))
309 return 0;
66a78c2b 310
6cd5b12a
JJ
311 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
312 if (r == 2) {
313 d = get_crypto_device(uuid);
314 if (!d)
315 return log_oom();
316
1d84ad94 317 free_and_replace(d->keyfile, uuid_value);
c802a730 318 } else if (free_and_strdup(&arg_default_keyfile, value) < 0)
141a79f4 319 return log_oom();
7ab064a6 320
1d84ad94
LP
321 } else if (streq(key, "luks.name")) {
322
323 if (proc_cmdline_value_missing(key, value))
324 return 0;
baade8cc
JJ
325
326 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
327 if (r == 2) {
328 d = get_crypto_device(uuid);
329 if (!d)
330 return log_oom();
331
332 d->create = arg_whitelist = true;
333
f9ecfd3b 334 free_and_replace(d->name, uuid_value);
baade8cc
JJ
335 } else
336 log_warning("Failed to parse luks name switch %s. Ignoring.", value);
85013844 337 }
66a78c2b 338
24a988e9 339 return 0;
66a78c2b
LP
340}
341
0fa9e53d
JJ
342static int add_crypttab_devices(void) {
343 struct stat st;
344 unsigned crypttab_line = 0;
7fd1b19b 345 _cleanup_fclose_ FILE *f = NULL;
e23a0ce8 346
0fa9e53d
JJ
347 if (!arg_read_crypttab)
348 return 0;
349
350 f = fopen("/etc/crypttab", "re");
351 if (!f) {
352 if (errno != ENOENT)
353 log_error_errno(errno, "Failed to open /etc/crypttab: %m");
354 return 0;
e23a0ce8
LP
355 }
356
0fa9e53d
JJ
357 if (fstat(fileno(f), &st) < 0) {
358 log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
359 return 0;
360 }
e23a0ce8 361
0fa9e53d
JJ
362 for (;;) {
363 int r, k;
364 char line[LINE_MAX], *l, *uuid;
365 crypto_device *d = NULL;
366 _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
4c12626c 367
0fa9e53d
JJ
368 if (!fgets(line, sizeof(line), f))
369 break;
66a78c2b 370
0fa9e53d 371 crypttab_line++;
141a79f4 372
0fa9e53d 373 l = strstrip(line);
4c701096 374 if (IN_SET(*l, 0, '#'))
0fa9e53d 375 continue;
66a78c2b 376
0fa9e53d
JJ
377 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
378 if (k < 2 || k > 4) {
379 log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
380 continue;
381 }
66a78c2b 382
0fa9e53d
JJ
383 uuid = startswith(device, "UUID=");
384 if (!uuid)
385 uuid = path_startswith(device, "/dev/disk/by-uuid/");
386 if (!uuid)
387 uuid = startswith(name, "luks-");
388 if (uuid)
389 d = hashmap_get(arg_disks, uuid);
8973790e 390
0fa9e53d
JJ
391 if (arg_whitelist && !d) {
392 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
393 continue;
8973790e
LP
394 }
395
0fa9e53d
JJ
396 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
397 if (r < 0)
398 return r;
8973790e 399
0fa9e53d
JJ
400 if (d)
401 d->create = false;
402 }
8973790e 403
0fa9e53d
JJ
404 return 0;
405}
66a78c2b 406
0fa9e53d
JJ
407static int add_proc_cmdline_devices(void) {
408 int r;
409 Iterator i;
410 crypto_device *d;
66a78c2b 411
0fa9e53d
JJ
412 HASHMAP_FOREACH(d, arg_disks, i) {
413 const char *options;
baade8cc 414 _cleanup_free_ char *device = NULL;
66a78c2b 415
0fa9e53d
JJ
416 if (!d->create)
417 continue;
e23a0ce8 418
baade8cc
JJ
419 if (!d->name) {
420 d->name = strappend("luks-", d->uuid);
421 if (!d->name)
422 return log_oom();
423 }
e23a0ce8 424
0fa9e53d
JJ
425 device = strappend("UUID=", d->uuid);
426 if (!device)
427 return log_oom();
7ab064a6 428
0fa9e53d
JJ
429 if (d->options)
430 options = d->options;
431 else if (arg_default_options)
432 options = arg_default_options;
433 else
434 options = "timeout=0";
141a79f4 435
baade8cc 436 r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
0fa9e53d
JJ
437 if (r < 0)
438 return r;
e23a0ce8
LP
439 }
440
0fa9e53d
JJ
441 return 0;
442}
e23a0ce8 443
0fa9e53d 444int main(int argc, char *argv[]) {
5f4bfe56 445 int r;
e23a0ce8 446
0fa9e53d
JJ
447 if (argc > 1 && argc != 4) {
448 log_error("This program takes three or no arguments.");
449 return EXIT_FAILURE;
450 }
e23a0ce8 451
0fa9e53d
JJ
452 if (argc > 1)
453 arg_dest = argv[1];
e23a0ce8 454
0fa9e53d
JJ
455 log_set_target(LOG_TARGET_SAFE);
456 log_parse_environment();
457 log_open();
e2cb60fa 458
0fa9e53d 459 umask(0022);
e23a0ce8 460
0fa9e53d 461 arg_disks = hashmap_new(&string_hash_ops);
5f4bfe56
LP
462 if (!arg_disks) {
463 r = log_oom();
464 goto finish;
465 }
7ab064a6 466
1d84ad94 467 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
0fa9e53d 468 if (r < 0) {
5f4bfe56
LP
469 log_warning_errno(r, "Failed to parse kernel command line: %m");
470 goto finish;
0fa9e53d 471 }
7ab064a6 472
0fa9e53d 473 if (!arg_enabled) {
5f4bfe56
LP
474 r = 0;
475 goto finish;
e23a0ce8
LP
476 }
477
5f4bfe56
LP
478 r = add_crypttab_devices();
479 if (r < 0)
480 goto finish;
0fa9e53d 481
5f4bfe56
LP
482 r = add_proc_cmdline_devices();
483 if (r < 0)
484 goto finish;
0fa9e53d 485
5f4bfe56 486 r = 0;
141a79f4 487
5f4bfe56 488finish:
6dd1c368 489 hashmap_free_with_destructor(arg_disks, crypt_device_free);
0fa9e53d
JJ
490 free(arg_default_options);
491 free(arg_default_keyfile);
141a79f4 492
5f4bfe56 493 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
e23a0ce8 494}