]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-generator.c
Merge pull request #7379 from yuwata/follow-up-7309
[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(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);
345 }
346
347 return 0;
348 }
349
350 static int add_crypttab_devices(void) {
351 struct stat st;
352 unsigned crypttab_line = 0;
353 _cleanup_fclose_ FILE *f = NULL;
354
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;
363 }
364
365 if (fstat(fileno(f), &st) < 0) {
366 log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
367 return 0;
368 }
369
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;
375
376 if (!fgets(line, sizeof(line), f))
377 break;
378
379 crypttab_line++;
380
381 l = strstrip(line);
382 if (IN_SET(*l, 0, '#'))
383 continue;
384
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 }
390
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);
398
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;
402 }
403
404 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
405 if (r < 0)
406 return r;
407
408 if (d)
409 d->create = false;
410 }
411
412 return 0;
413 }
414
415 static int add_proc_cmdline_devices(void) {
416 int r;
417 Iterator i;
418 crypto_device *d;
419
420 HASHMAP_FOREACH(d, arg_disks, i) {
421 const char *options;
422 _cleanup_free_ char *device = NULL;
423
424 if (!d->create)
425 continue;
426
427 if (!d->name) {
428 d->name = strappend("luks-", d->uuid);
429 if (!d->name)
430 return log_oom();
431 }
432
433 device = strappend("UUID=", d->uuid);
434 if (!device)
435 return log_oom();
436
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";
443
444 r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
445 if (r < 0)
446 return r;
447 }
448
449 return 0;
450 }
451
452 int main(int argc, char *argv[]) {
453 int r;
454
455 if (argc > 1 && argc != 4) {
456 log_error("This program takes three or no arguments.");
457 return EXIT_FAILURE;
458 }
459
460 if (argc > 1)
461 arg_dest = argv[1];
462
463 log_set_target(LOG_TARGET_SAFE);
464 log_parse_environment();
465 log_open();
466
467 umask(0022);
468
469 arg_disks = hashmap_new(&string_hash_ops);
470 if (!arg_disks) {
471 r = log_oom();
472 goto finish;
473 }
474
475 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
476 if (r < 0) {
477 log_warning_errno(r, "Failed to parse kernel command line: %m");
478 goto finish;
479 }
480
481 if (!arg_enabled) {
482 r = 0;
483 goto finish;
484 }
485
486 r = add_crypttab_devices();
487 if (r < 0)
488 goto finish;
489
490 r = add_proc_cmdline_devices();
491 if (r < 0)
492 goto finish;
493
494 r = 0;
495
496 finish:
497 free_arg_disks();
498 free(arg_default_options);
499 free(arg_default_keyfile);
500
501 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
502 }