]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-generator.c
Merge pull request #7446 from poettering/efi-firmware-boot-fixes
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-generator.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
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.
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
15 Lesser General Public License for more details.
16
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/>.
19 ***/
20
21 #include <errno.h>
22
23 #include "alloc-util.h"
24 #include "dropin.h"
25 #include "fd-util.h"
26 #include "fileio.h"
27 #include "fstab-util.h"
28 #include "generator.h"
29 #include "hashmap.h"
30 #include "log.h"
31 #include "mkdir.h"
32 #include "parse-util.h"
33 #include "path-util.h"
34 #include "proc-cmdline.h"
35 #include "string-util.h"
36 #include "strv.h"
37 #include "unit-name.h"
38 #include "util.h"
39
40 typedef struct crypto_device {
41 char *uuid;
42 char *keyfile;
43 char *name;
44 char *options;
45 bool create;
46 } crypto_device;
47
48 static const char *arg_dest = "/tmp";
49 static bool arg_enabled = true;
50 static bool arg_read_crypttab = true;
51 static bool arg_whitelist = false;
52 static Hashmap *arg_disks = NULL;
53 static char *arg_default_options = NULL;
54 static char *arg_default_keyfile = NULL;
55
56 static int create_disk(
57 const char *name,
58 const char *device,
59 const char *password,
60 const char *options) {
61
62 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *e = NULL,
63 *filtered = NULL;
64 _cleanup_fclose_ FILE *f = NULL;
65 const char *dmname;
66 bool noauto, nofail, tmp, swap, netdev;
67 int r;
68
69 assert(name);
70 assert(device);
71
72 noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
73 nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
74 tmp = fstab_test_option(options, "tmp\0");
75 swap = fstab_test_option(options, "swap\0");
76 netdev = fstab_test_option(options, "_netdev\0");
77
78 if (tmp && swap) {
79 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
80 return -EINVAL;
81 }
82
83 e = unit_name_escape(name);
84 if (!e)
85 return log_oom();
86
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");
90
91 p = strjoin(arg_dest, "/", n);
92 if (!p)
93 return log_oom();
94
95 u = fstab_node_to_udev_node(device);
96 if (!u)
97 return log_oom();
98
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");
102
103 f = fopen(p, "wxe");
104 if (!f)
105 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
106
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",
117 netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
118
119 if (!nofail)
120 fprintf(f,
121 "Before=%s\n",
122 netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
123
124 if (password) {
125 if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
126 fputs_unlocked("After=systemd-random-seed.service\n", f);
127 else if (!STR_IN_SET(password, "-", "none")) {
128 _cleanup_free_ char *uu;
129
130 uu = fstab_node_to_udev_node(password);
131 if (!uu)
132 return log_oom();
133
134 if (!path_equal(uu, "/dev/null")) {
135
136 if (path_startswith(uu, "/dev/")) {
137 _cleanup_free_ char *dd = NULL;
138
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");
142
143 fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
144 } else
145 fprintf(f, "RequiresMountsFor=%s\n", password);
146 }
147 }
148 }
149
150 if (path_startswith(u, "/dev/")) {
151 fprintf(f,
152 "BindsTo=%s\n"
153 "After=%s\n"
154 "Before=umount.target\n",
155 d, d);
156
157 if (swap)
158 fputs_unlocked("Before=dev-mapper-%i.swap\n",
159 f);
160 } else
161 fprintf(f,
162 "RequiresMountsFor=%s\n",
163 u);
164
165 r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
166 if (r < 0)
167 return r;
168
169 fprintf(f,
170 "\n[Service]\n"
171 "Type=oneshot\n"
172 "RemainAfterExit=yes\n"
173 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
174 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
175 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
176 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
177 name, u, strempty(password), strempty(filtered),
178 name);
179
180 if (tmp)
181 fprintf(f,
182 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
183 name);
184
185 if (swap)
186 fprintf(f,
187 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
188 name);
189
190 r = fflush_and_check(f);
191 if (r < 0)
192 return log_error_errno(r, "Failed to write file %s: %m", p);
193
194 if (!noauto) {
195 r = generator_add_symlink(arg_dest, d, "wants", n);
196 if (r < 0)
197 return r;
198
199 r = generator_add_symlink(arg_dest,
200 netdev ? "remote-cryptsetup.target" : "cryptsetup.target",
201 nofail ? "wants" : "requires", n);
202 if (r < 0)
203 return r;
204 }
205
206 dmname = strjoina("dev-mapper-", e, ".device");
207 r = generator_add_symlink(arg_dest, dmname, "requires", n);
208 if (r < 0)
209 return r;
210
211 if (!noauto && !nofail) {
212 r = write_drop_in(arg_dest, dmname, 90, "device-timeout",
213 "# Automatically generated by systemd-cryptsetup-generator \n\n"
214 "[Unit]\nJobTimeoutSec=0");
215 if (r < 0)
216 return log_error_errno(r, "Failed to write device drop-in: %m");
217 }
218
219 return 0;
220 }
221
222 static void free_arg_disks(void) {
223 crypto_device *d;
224
225 while ((d = hashmap_steal_first(arg_disks))) {
226 free(d->uuid);
227 free(d->keyfile);
228 free(d->name);
229 free(d->options);
230 free(d);
231 }
232
233 hashmap_free(arg_disks);
234 }
235
236 static 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;
249 d->keyfile = d->options = d->name = NULL;
250
251 d->uuid = strdup(uuid);
252 if (!d->uuid)
253 return mfree(d);
254
255 r = hashmap_put(arg_disks, d->uuid, d);
256 if (r < 0) {
257 free(d->uuid);
258 return mfree(d);
259 }
260 }
261
262 return d;
263 }
264
265 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
266 _cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
267 crypto_device *d;
268 int r;
269
270 if (streq(key, "luks")) {
271
272 r = value ? parse_boolean(value) : 1;
273 if (r < 0)
274 log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value);
275 else
276 arg_enabled = r;
277
278 } else if (streq(key, "luks.crypttab")) {
279
280 r = value ? parse_boolean(value) : 1;
281 if (r < 0)
282 log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value);
283 else
284 arg_read_crypttab = r;
285
286 } else if (streq(key, "luks.uuid")) {
287
288 if (proc_cmdline_value_missing(key, value))
289 return 0;
290
291 d = get_crypto_device(startswith(value, "luks-") ? value+5 : value);
292 if (!d)
293 return log_oom();
294
295 d->create = arg_whitelist = true;
296
297 } else if (streq(key, "luks.options")) {
298
299 if (proc_cmdline_value_missing(key, value))
300 return 0;
301
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
308 free_and_replace(d->options, uuid_value);
309 } else if (free_and_strdup(&arg_default_options, value) < 0)
310 return log_oom();
311
312 } else if (streq(key, "luks.key")) {
313
314 if (proc_cmdline_value_missing(key, value))
315 return 0;
316
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
323 free_and_replace(d->keyfile, uuid_value);
324 } else if (free_and_strdup(&arg_default_keyfile, value) < 0)
325 return log_oom();
326
327 } else if (streq(key, "luks.name")) {
328
329 if (proc_cmdline_value_missing(key, value))
330 return 0;
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_and_replace(d->name, uuid_value);
341 } else
342 log_warning("Failed to parse luks name switch %s. Ignoring.", value);
343 }
344
345 return 0;
346 }
347
348 static int add_crypttab_devices(void) {
349 struct stat st;
350 unsigned crypttab_line = 0;
351 _cleanup_fclose_ FILE *f = NULL;
352
353 if (!arg_read_crypttab)
354 return 0;
355
356 f = fopen("/etc/crypttab", "re");
357 if (!f) {
358 if (errno != ENOENT)
359 log_error_errno(errno, "Failed to open /etc/crypttab: %m");
360 return 0;
361 }
362
363 if (fstat(fileno(f), &st) < 0) {
364 log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
365 return 0;
366 }
367
368 for (;;) {
369 int r, k;
370 char line[LINE_MAX], *l, *uuid;
371 crypto_device *d = NULL;
372 _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
373
374 if (!fgets(line, sizeof(line), f))
375 break;
376
377 crypttab_line++;
378
379 l = strstrip(line);
380 if (IN_SET(*l, 0, '#'))
381 continue;
382
383 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
384 if (k < 2 || k > 4) {
385 log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
386 continue;
387 }
388
389 uuid = startswith(device, "UUID=");
390 if (!uuid)
391 uuid = path_startswith(device, "/dev/disk/by-uuid/");
392 if (!uuid)
393 uuid = startswith(name, "luks-");
394 if (uuid)
395 d = hashmap_get(arg_disks, uuid);
396
397 if (arg_whitelist && !d) {
398 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
399 continue;
400 }
401
402 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
403 if (r < 0)
404 return r;
405
406 if (d)
407 d->create = false;
408 }
409
410 return 0;
411 }
412
413 static int add_proc_cmdline_devices(void) {
414 int r;
415 Iterator i;
416 crypto_device *d;
417
418 HASHMAP_FOREACH(d, arg_disks, i) {
419 const char *options;
420 _cleanup_free_ char *device = NULL;
421
422 if (!d->create)
423 continue;
424
425 if (!d->name) {
426 d->name = strappend("luks-", d->uuid);
427 if (!d->name)
428 return log_oom();
429 }
430
431 device = strappend("UUID=", d->uuid);
432 if (!device)
433 return log_oom();
434
435 if (d->options)
436 options = d->options;
437 else if (arg_default_options)
438 options = arg_default_options;
439 else
440 options = "timeout=0";
441
442 r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
443 if (r < 0)
444 return r;
445 }
446
447 return 0;
448 }
449
450 int main(int argc, char *argv[]) {
451 int r;
452
453 if (argc > 1 && argc != 4) {
454 log_error("This program takes three or no arguments.");
455 return EXIT_FAILURE;
456 }
457
458 if (argc > 1)
459 arg_dest = argv[1];
460
461 log_set_target(LOG_TARGET_SAFE);
462 log_parse_environment();
463 log_open();
464
465 umask(0022);
466
467 arg_disks = hashmap_new(&string_hash_ops);
468 if (!arg_disks) {
469 r = log_oom();
470 goto finish;
471 }
472
473 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
474 if (r < 0) {
475 log_warning_errno(r, "Failed to parse kernel command line: %m");
476 goto finish;
477 }
478
479 if (!arg_enabled) {
480 r = 0;
481 goto finish;
482 }
483
484 r = add_crypttab_devices();
485 if (r < 0)
486 goto finish;
487
488 r = add_proc_cmdline_devices();
489 if (r < 0)
490 goto finish;
491
492 r = 0;
493
494 finish:
495 free_arg_disks();
496 free(arg_default_options);
497 free(arg_default_keyfile);
498
499 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
500 }