]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-generator.c
cryptsetup-generator: Don't mistake NULL input as OOM (#7688)
[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 password_escaped = specifier_escape(password);
110 if (password && !password_escaped)
111 return log_oom();
112
113 r = generator_open_unit_file(arg_dest, NULL, n, &f);
114 if (r < 0)
115 return r;
116
117 fprintf(f,
118 "[Unit]\n"
119 "Description=Cryptography Setup for %%I\n"
120 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
121 "SourcePath=/etc/crypttab\n"
122 "DefaultDependencies=no\n"
123 "Conflicts=umount.target\n"
124 "IgnoreOnIsolate=true\n"
125 "After=%s\n",
126 netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
127
128 if (!nofail)
129 fprintf(f,
130 "Before=%s\n",
131 netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
132
133 if (password) {
134 if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
135 fputs("After=systemd-random-seed.service\n", f);
136 else if (!STR_IN_SET(password, "-", "none")) {
137 _cleanup_free_ char *uu;
138
139 uu = fstab_node_to_udev_node(password);
140 if (!uu)
141 return log_oom();
142
143 if (!path_equal(uu, "/dev/null")) {
144
145 if (path_startswith(uu, "/dev/")) {
146 _cleanup_free_ char *dd = NULL;
147
148 r = unit_name_from_path(uu, ".device", &dd);
149 if (r < 0)
150 return log_error_errno(r, "Failed to generate unit name: %m");
151
152 fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
153 } else
154 fprintf(f, "RequiresMountsFor=%s\n", password_escaped);
155 }
156 }
157 }
158
159 if (path_startswith(u, "/dev/")) {
160 fprintf(f,
161 "BindsTo=%s\n"
162 "After=%s\n"
163 "Before=umount.target\n",
164 d, d);
165
166 if (swap)
167 fputs("Before=dev-mapper-%i.swap\n",
168 f);
169 } else
170 fprintf(f,
171 "RequiresMountsFor=%s\n",
172 u_escaped);
173
174
175 r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
176 if (r < 0)
177 return r;
178
179 filtered_escaped = specifier_escape(filtered);
180 if (filtered && !filtered_escaped)
181 return log_oom();
182
183 fprintf(f,
184 "\n[Service]\n"
185 "Type=oneshot\n"
186 "RemainAfterExit=yes\n"
187 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
188 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
189 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
190 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
191 name_escaped, u_escaped, strempty(password_escaped), strempty(filtered_escaped),
192 name_escaped);
193
194 if (tmp)
195 fprintf(f,
196 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
197 name_escaped);
198
199 if (swap)
200 fprintf(f,
201 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
202 name_escaped);
203
204 r = fflush_and_check(f);
205 if (r < 0)
206 return log_error_errno(r, "Failed to write unit file %s: %m", n);
207
208 if (!noauto) {
209 r = generator_add_symlink(arg_dest, d, "wants", n);
210 if (r < 0)
211 return r;
212
213 r = generator_add_symlink(arg_dest,
214 netdev ? "remote-cryptsetup.target" : "cryptsetup.target",
215 nofail ? "wants" : "requires", n);
216 if (r < 0)
217 return r;
218 }
219
220 dmname = strjoina("dev-mapper-", e, ".device");
221 r = generator_add_symlink(arg_dest, dmname, "requires", n);
222 if (r < 0)
223 return r;
224
225 if (!noauto && !nofail) {
226 r = write_drop_in(arg_dest, dmname, 90, "device-timeout",
227 "# Automatically generated by systemd-cryptsetup-generator \n\n"
228 "[Unit]\nJobTimeoutSec=0");
229 if (r < 0)
230 return log_error_errno(r, "Failed to write device drop-in: %m");
231 }
232
233 return 0;
234 }
235
236 static void crypt_device_free(crypto_device *d) {
237 free(d->uuid);
238 free(d->keyfile);
239 free(d->name);
240 free(d->options);
241 free(d);
242 }
243
244 static crypto_device *get_crypto_device(const char *uuid) {
245 int r;
246 crypto_device *d;
247
248 assert(uuid);
249
250 d = hashmap_get(arg_disks, uuid);
251 if (!d) {
252 d = new0(struct crypto_device, 1);
253 if (!d)
254 return NULL;
255
256 d->create = false;
257 d->keyfile = d->options = d->name = NULL;
258
259 d->uuid = strdup(uuid);
260 if (!d->uuid)
261 return mfree(d);
262
263 r = hashmap_put(arg_disks, d->uuid, d);
264 if (r < 0) {
265 free(d->uuid);
266 return mfree(d);
267 }
268 }
269
270 return d;
271 }
272
273 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
274 _cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
275 crypto_device *d;
276 int r;
277
278 if (streq(key, "luks")) {
279
280 r = value ? parse_boolean(value) : 1;
281 if (r < 0)
282 log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value);
283 else
284 arg_enabled = r;
285
286 } else if (streq(key, "luks.crypttab")) {
287
288 r = value ? parse_boolean(value) : 1;
289 if (r < 0)
290 log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value);
291 else
292 arg_read_crypttab = r;
293
294 } else if (streq(key, "luks.uuid")) {
295
296 if (proc_cmdline_value_missing(key, value))
297 return 0;
298
299 d = get_crypto_device(startswith(value, "luks-") ? value+5 : value);
300 if (!d)
301 return log_oom();
302
303 d->create = arg_whitelist = true;
304
305 } else if (streq(key, "luks.options")) {
306
307 if (proc_cmdline_value_missing(key, value))
308 return 0;
309
310 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
311 if (r == 2) {
312 d = get_crypto_device(uuid);
313 if (!d)
314 return log_oom();
315
316 free_and_replace(d->options, uuid_value);
317 } else if (free_and_strdup(&arg_default_options, value) < 0)
318 return log_oom();
319
320 } else if (streq(key, "luks.key")) {
321
322 if (proc_cmdline_value_missing(key, value))
323 return 0;
324
325 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
326 if (r == 2) {
327 d = get_crypto_device(uuid);
328 if (!d)
329 return log_oom();
330
331 free_and_replace(d->keyfile, uuid_value);
332 } else if (free_and_strdup(&arg_default_keyfile, value) < 0)
333 return log_oom();
334
335 } else if (streq(key, "luks.name")) {
336
337 if (proc_cmdline_value_missing(key, value))
338 return 0;
339
340 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
341 if (r == 2) {
342 d = get_crypto_device(uuid);
343 if (!d)
344 return log_oom();
345
346 d->create = arg_whitelist = true;
347
348 free_and_replace(d->name, uuid_value);
349 } else
350 log_warning("Failed to parse luks name switch %s. Ignoring.", value);
351 }
352
353 return 0;
354 }
355
356 static int add_crypttab_devices(void) {
357 struct stat st;
358 unsigned crypttab_line = 0;
359 _cleanup_fclose_ FILE *f = NULL;
360
361 if (!arg_read_crypttab)
362 return 0;
363
364 f = fopen("/etc/crypttab", "re");
365 if (!f) {
366 if (errno != ENOENT)
367 log_error_errno(errno, "Failed to open /etc/crypttab: %m");
368 return 0;
369 }
370
371 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
372
373 if (fstat(fileno(f), &st) < 0) {
374 log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
375 return 0;
376 }
377
378 for (;;) {
379 int r, k;
380 char line[LINE_MAX], *l, *uuid;
381 crypto_device *d = NULL;
382 _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
383
384 if (!fgets(line, sizeof(line), f))
385 break;
386
387 crypttab_line++;
388
389 l = strstrip(line);
390 if (IN_SET(*l, 0, '#'))
391 continue;
392
393 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
394 if (k < 2 || k > 4) {
395 log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
396 continue;
397 }
398
399 uuid = startswith(device, "UUID=");
400 if (!uuid)
401 uuid = path_startswith(device, "/dev/disk/by-uuid/");
402 if (!uuid)
403 uuid = startswith(name, "luks-");
404 if (uuid)
405 d = hashmap_get(arg_disks, uuid);
406
407 if (arg_whitelist && !d) {
408 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
409 continue;
410 }
411
412 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
413 if (r < 0)
414 return r;
415
416 if (d)
417 d->create = false;
418 }
419
420 return 0;
421 }
422
423 static int add_proc_cmdline_devices(void) {
424 int r;
425 Iterator i;
426 crypto_device *d;
427
428 HASHMAP_FOREACH(d, arg_disks, i) {
429 const char *options;
430 _cleanup_free_ char *device = NULL;
431
432 if (!d->create)
433 continue;
434
435 if (!d->name) {
436 d->name = strappend("luks-", d->uuid);
437 if (!d->name)
438 return log_oom();
439 }
440
441 device = strappend("UUID=", d->uuid);
442 if (!device)
443 return log_oom();
444
445 if (d->options)
446 options = d->options;
447 else if (arg_default_options)
448 options = arg_default_options;
449 else
450 options = "timeout=0";
451
452 r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
453 if (r < 0)
454 return r;
455 }
456
457 return 0;
458 }
459
460 int main(int argc, char *argv[]) {
461 int r;
462
463 if (argc > 1 && argc != 4) {
464 log_error("This program takes three or no arguments.");
465 return EXIT_FAILURE;
466 }
467
468 if (argc > 1)
469 arg_dest = argv[1];
470
471 log_set_target(LOG_TARGET_SAFE);
472 log_parse_environment();
473 log_open();
474
475 umask(0022);
476
477 arg_disks = hashmap_new(&string_hash_ops);
478 if (!arg_disks) {
479 r = log_oom();
480 goto finish;
481 }
482
483 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
484 if (r < 0) {
485 log_warning_errno(r, "Failed to parse kernel command line: %m");
486 goto finish;
487 }
488
489 if (!arg_enabled) {
490 r = 0;
491 goto finish;
492 }
493
494 r = add_crypttab_devices();
495 if (r < 0)
496 goto finish;
497
498 r = add_proc_cmdline_devices();
499 if (r < 0)
500 goto finish;
501
502 r = 0;
503
504 finish:
505 hashmap_free_with_destructor(arg_disks, crypt_device_free);
506 free(arg_default_options);
507 free(arg_default_keyfile);
508
509 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
510 }