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