]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-generator.c
fstab-util: detect out-of-range pri= assignments
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-generator.c
CommitLineData
e23a0ce8
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
e23a0ce8
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
e23a0ce8 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
e23a0ce8
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
e23a0ce8 22#include <errno.h>
0fa9e53d 23#include <string.h>
e23a0ce8
LP
24#include <unistd.h>
25
0fa9e53d
JJ
26#include "dropin.h"
27#include "fileio.h"
28#include "generator.h"
29#include "hashmap.h"
e23a0ce8 30#include "log.h"
49e942b2 31#include "mkdir.h"
bde29068 32#include "path-util.h"
0fa9e53d
JJ
33#include "strv.h"
34#include "unit-name.h"
35#include "util.h"
36
37typedef struct crypto_device {
38 char *uuid;
6cd5b12a 39 char *keyfile;
baade8cc 40 char *name;
0fa9e53d
JJ
41 char *options;
42 bool create;
43} crypto_device;
e23a0ce8 44
66a78c2b
LP
45static const char *arg_dest = "/tmp";
46static bool arg_enabled = true;
47static bool arg_read_crypttab = true;
0fa9e53d
JJ
48static bool arg_whitelist = false;
49static Hashmap *arg_disks = NULL;
50static char *arg_default_options = NULL;
51static char *arg_default_keyfile = NULL;
141a79f4 52
e23a0ce8
LP
53static bool has_option(const char *haystack, const char *needle) {
54 const char *f = haystack;
55 size_t l;
56
f653f683
LP
57 assert(needle);
58
59 if (!haystack)
60 return false;
61
e23a0ce8
LP
62 l = strlen(needle);
63
64 while ((f = strstr(f, needle))) {
65
66 if (f > haystack && f[-1] != ',') {
67 f++;
68 continue;
69 }
70
aae5220d 71 if (f[l] != 0 && f[l] != ',') {
e23a0ce8
LP
72 f++;
73 continue;
74 }
75
76 return true;
77 }
78
79 return false;
80}
81
82static int create_disk(
83 const char *name,
84 const char *device,
85 const char *password,
86 const char *options) {
87
8eea8687
ZJS
88 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *to = NULL, *e = NULL,
89 *filtered = NULL;
7fd1b19b 90 _cleanup_fclose_ FILE *f = NULL;
8c11d3c1 91 bool noauto, nofail, tmp, swap;
744198e9
LP
92 char *from;
93 int r;
e23a0ce8
LP
94
95 assert(name);
96 assert(device);
97
155da457
LP
98 noauto = has_option(options, "noauto");
99 nofail = has_option(options, "nofail");
8c11d3c1
TG
100 tmp = has_option(options, "tmp");
101 swap = has_option(options, "swap");
102
103 if (tmp && swap) {
104 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
105 return -EINVAL;
106 }
155da457 107
744198e9
LP
108 e = unit_name_escape(name);
109 if (!e)
110 return log_oom();
111
112 n = unit_name_build("systemd-cryptsetup", e, ".service");
24a988e9
HH
113 if (!n)
114 return log_oom();
e23a0ce8 115
b7def684 116 p = strjoin(arg_dest, "/", n, NULL);
24a988e9
HH
117 if (!p)
118 return log_oom();
e23a0ce8 119
f7f21d33 120 u = fstab_node_to_udev_node(device);
24a988e9
HH
121 if (!u)
122 return log_oom();
e23a0ce8 123
f7f21d33 124 d = unit_name_from_path(u, ".device");
24a988e9
HH
125 if (!d)
126 return log_oom();
e23a0ce8 127
f7f21d33 128 f = fopen(p, "wxe");
4a62c710
MS
129 if (!f)
130 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
e23a0ce8 131
9ece938a 132 fputs(
6b1dc2bd 133 "# Automatically generated by systemd-cryptsetup-generator\n\n"
e23a0ce8 134 "[Unit]\n"
9ece938a 135 "Description=Cryptography Setup for %I\n"
c3834f9b 136 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
1b64d026 137 "SourcePath=/etc/crypttab\n"
e23a0ce8 138 "DefaultDependencies=no\n"
744198e9 139 "Conflicts=umount.target\n"
9ece938a 140 "BindsTo=dev-mapper-%i.device\n"
4469ff4a 141 "IgnoreOnIsolate=true\n"
d6bc8348 142 "After=cryptsetup-pre.target\n",
9ece938a 143 f);
e23a0ce8 144
155da457
LP
145 if (!nofail)
146 fprintf(f,
147 "Before=cryptsetup.target\n");
148
ceca9501 149 if (password) {
744198e9 150 if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
a0f70805 151 fputs("After=systemd-random-seed.service\n", f);
66a5dbdf 152 else if (!streq(password, "-") && !streq(password, "none")) {
744198e9
LP
153 _cleanup_free_ char *uu;
154
155 uu = fstab_node_to_udev_node(password);
156 if (!uu)
66a5dbdf
DR
157 return log_oom();
158
bde29068 159 if (!path_equal(uu, "/dev/null")) {
744198e9 160
bde29068
LP
161 if (is_device_path(uu)) {
162 _cleanup_free_ char *dd;
66a5dbdf 163
bde29068
LP
164 dd = unit_name_from_path(uu, ".device");
165 if (!dd)
166 return log_oom();
167
168 fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
169 } else
170 fprintf(f, "RequiresMountsFor=%s\n", password);
171 }
66a5dbdf 172 }
ceca9501 173 }
e23a0ce8 174
9ece938a
TW
175 if (is_device_path(u))
176 fprintf(f,
177 "BindsTo=%s\n"
178 "After=%s\n"
179 "Before=umount.target\n",
180 d, d);
181 else
182 fprintf(f,
183 "RequiresMountsFor=%s\n",
184 u);
185
8eea8687
ZJS
186 r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
187 if (r < 0)
188 return r;
189
e23a0ce8
LP
190 fprintf(f,
191 "\n[Service]\n"
192 "Type=oneshot\n"
193 "RemainAfterExit=yes\n"
90724929 194 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
260ab287 195 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
7f4e0805 196 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
8eea8687 197 name, u, strempty(password), strempty(filtered),
e23a0ce8
LP
198 name);
199
8c11d3c1 200 if (tmp)
e23a0ce8 201 fprintf(f,
50109038 202 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
1d3399e6 203 name);
e23a0ce8 204
8c11d3c1 205 if (swap)
e23a0ce8 206 fprintf(f,
50109038 207 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
1d3399e6 208 name);
e23a0ce8
LP
209
210 fflush(f);
4a62c710
MS
211 if (ferror(f))
212 return log_error_errno(errno, "Failed to write file %s: %m", p);
e23a0ce8 213
744198e9 214 from = strappenda("../", n);
74715b82 215
155da457 216 if (!noauto) {
e23a0ce8 217
b7def684 218 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
24a988e9
HH
219 if (!to)
220 return log_oom();
e23a0ce8 221
d2e54fae 222 mkdir_parents_label(to, 0755);
4a62c710
MS
223 if (symlink(from, to) < 0)
224 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
2f8cd170
LP
225
226 free(to);
155da457 227 if (!nofail)
b7def684 228 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
155da457 229 else
b7def684 230 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
24a988e9
HH
231 if (!to)
232 return log_oom();
2f8cd170 233
d2e54fae 234 mkdir_parents_label(to, 0755);
4a62c710
MS
235 if (symlink(from, to) < 0)
236 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
f7f21d33 237 }
74715b82 238
608d41f3 239 free(to);
b7def684 240 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
24a988e9
HH
241 if (!to)
242 return log_oom();
74715b82 243
d2e54fae 244 mkdir_parents_label(to, 0755);
4a62c710
MS
245 if (symlink(from, to) < 0)
246 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
74715b82 247
68395007 248 if (!noauto && !nofail) {
a6fb0dc1
HG
249 _cleanup_free_ char *dmname;
250 dmname = strjoin("dev-mapper-", e, ".device", NULL);
251 if (!dmname)
252 return log_oom();
253
254 r = write_drop_in(arg_dest, dmname, 90, "device-timeout",
8eea8687
ZJS
255 "# Automatically generated by systemd-cryptsetup-generator \n\n"
256 "[Unit]\nJobTimeoutSec=0");
23bbb0de
MS
257 if (r < 0)
258 return log_error_errno(r, "Failed to write device drop-in: %m");
68395007
HH
259 }
260
24a988e9 261 return 0;
e23a0ce8
LP
262}
263
0fa9e53d
JJ
264static void free_arg_disks(void) {
265 crypto_device *d;
266
267 while ((d = hashmap_steal_first(arg_disks))) {
268 free(d->uuid);
6cd5b12a 269 free(d->keyfile);
baade8cc 270 free(d->name);
0fa9e53d
JJ
271 free(d->options);
272 free(d);
273 }
274
275 hashmap_free(arg_disks);
276}
277
278static crypto_device *get_crypto_device(const char *uuid) {
279 int r;
280 crypto_device *d;
281
282 assert(uuid);
283
284 d = hashmap_get(arg_disks, uuid);
285 if (!d) {
286 d = new0(struct crypto_device, 1);
287 if (!d)
288 return NULL;
289
290 d->create = false;
baade8cc 291 d->keyfile = d->options = d->name = NULL;
0fa9e53d
JJ
292
293 d->uuid = strdup(uuid);
294 if (!d->uuid) {
295 free(d);
296 return NULL;
297 }
298
299 r = hashmap_put(arg_disks, d->uuid, d);
300 if (r < 0) {
301 free(d->uuid);
302 free(d);
303 return NULL;
304 }
305 }
306
307 return d;
308}
309
059cb385 310static int parse_proc_cmdline_item(const char *key, const char *value) {
74df0fca 311 int r;
0fa9e53d
JJ
312 crypto_device *d;
313 _cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
66a78c2b 314
059cb385
LP
315 if (STR_IN_SET(key, "luks", "rd.luks") && value) {
316
317 r = parse_boolean(value);
141a79f4 318 if (r < 0)
059cb385 319 log_warning("Failed to parse luks switch %s. Ignoring.", value);
141a79f4
ZJS
320 else
321 arg_enabled = r;
66a78c2b 322
059cb385 323 } else if (STR_IN_SET(key, "luks.crypttab", "rd.luks.crypttab") && value) {
66a78c2b 324
059cb385 325 r = parse_boolean(value);
141a79f4 326 if (r < 0)
059cb385 327 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value);
141a79f4
ZJS
328 else
329 arg_read_crypttab = r;
66a78c2b 330
059cb385 331 } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) {
66a78c2b 332
0fa9e53d
JJ
333 d = get_crypto_device(startswith(value, "luks-") ? value+5 : value);
334 if (!d)
141a79f4 335 return log_oom();
66a78c2b 336
0fa9e53d
JJ
337 d->create = arg_whitelist = true;
338
059cb385 339 } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) {
66a78c2b 340
0fa9e53d
JJ
341 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
342 if (r == 2) {
343 d = get_crypto_device(uuid);
344 if (!d)
345 return log_oom();
346
347 free(d->options);
348 d->options = uuid_value;
349 uuid_value = NULL;
350 } else if (free_and_strdup(&arg_default_options, value) < 0)
141a79f4 351 return log_oom();
66a78c2b 352
059cb385 353 } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) {
66a78c2b 354
6cd5b12a
JJ
355 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
356 if (r == 2) {
357 d = get_crypto_device(uuid);
358 if (!d)
359 return log_oom();
360
361 free(d->keyfile);
362 d->keyfile = uuid_value;
363 uuid_value = NULL;
364 } else if (free_and_strdup(&arg_default_keyfile, value))
141a79f4 365 return log_oom();
7ab064a6 366
baade8cc
JJ
367 } else if (STR_IN_SET(key, "luks.name", "rd.luks.name") && value) {
368
369 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
370 if (r == 2) {
371 d = get_crypto_device(uuid);
372 if (!d)
373 return log_oom();
374
375 d->create = arg_whitelist = true;
376
377 free(d->name);
378 d->name = uuid_value;
379 uuid_value = NULL;
380 } else
381 log_warning("Failed to parse luks name switch %s. Ignoring.", value);
382
85013844 383 }
66a78c2b 384
24a988e9 385 return 0;
66a78c2b
LP
386}
387
0fa9e53d
JJ
388static int add_crypttab_devices(void) {
389 struct stat st;
390 unsigned crypttab_line = 0;
7fd1b19b 391 _cleanup_fclose_ FILE *f = NULL;
e23a0ce8 392
0fa9e53d
JJ
393 if (!arg_read_crypttab)
394 return 0;
395
396 f = fopen("/etc/crypttab", "re");
397 if (!f) {
398 if (errno != ENOENT)
399 log_error_errno(errno, "Failed to open /etc/crypttab: %m");
400 return 0;
e23a0ce8
LP
401 }
402
0fa9e53d
JJ
403 if (fstat(fileno(f), &st) < 0) {
404 log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
405 return 0;
406 }
e23a0ce8 407
0fa9e53d
JJ
408 /* If we readd support for specifying passphrases
409 * directly in crypttab we should upgrade the warning
410 * below, though possibly only if a passphrase is
411 * specified directly. */
412 if (st.st_mode & 0005)
413 log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
e23a0ce8 414
0fa9e53d
JJ
415 for (;;) {
416 int r, k;
417 char line[LINE_MAX], *l, *uuid;
418 crypto_device *d = NULL;
419 _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
4c12626c 420
0fa9e53d
JJ
421 if (!fgets(line, sizeof(line), f))
422 break;
66a78c2b 423
0fa9e53d 424 crypttab_line++;
141a79f4 425
0fa9e53d
JJ
426 l = strstrip(line);
427 if (*l == '#' || *l == 0)
428 continue;
66a78c2b 429
0fa9e53d
JJ
430 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
431 if (k < 2 || k > 4) {
432 log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
433 continue;
434 }
66a78c2b 435
0fa9e53d
JJ
436 uuid = startswith(device, "UUID=");
437 if (!uuid)
438 uuid = path_startswith(device, "/dev/disk/by-uuid/");
439 if (!uuid)
440 uuid = startswith(name, "luks-");
441 if (uuid)
442 d = hashmap_get(arg_disks, uuid);
8973790e 443
0fa9e53d
JJ
444 if (arg_whitelist && !d) {
445 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
446 continue;
8973790e
LP
447 }
448
0fa9e53d
JJ
449 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
450 if (r < 0)
451 return r;
8973790e 452
0fa9e53d
JJ
453 if (d)
454 d->create = false;
455 }
8973790e 456
0fa9e53d
JJ
457 return 0;
458}
66a78c2b 459
0fa9e53d
JJ
460static int add_proc_cmdline_devices(void) {
461 int r;
462 Iterator i;
463 crypto_device *d;
66a78c2b 464
0fa9e53d
JJ
465 HASHMAP_FOREACH(d, arg_disks, i) {
466 const char *options;
baade8cc 467 _cleanup_free_ char *device = NULL;
66a78c2b 468
0fa9e53d
JJ
469 if (!d->create)
470 continue;
e23a0ce8 471
baade8cc
JJ
472 if (!d->name) {
473 d->name = strappend("luks-", d->uuid);
474 if (!d->name)
475 return log_oom();
476 }
e23a0ce8 477
0fa9e53d
JJ
478 device = strappend("UUID=", d->uuid);
479 if (!device)
480 return log_oom();
7ab064a6 481
0fa9e53d
JJ
482 if (d->options)
483 options = d->options;
484 else if (arg_default_options)
485 options = arg_default_options;
486 else
487 options = "timeout=0";
141a79f4 488
baade8cc 489 r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
0fa9e53d
JJ
490 if (r < 0)
491 return r;
e23a0ce8
LP
492 }
493
0fa9e53d
JJ
494 return 0;
495}
e23a0ce8 496
0fa9e53d
JJ
497int main(int argc, char *argv[]) {
498 int r = EXIT_FAILURE;
e23a0ce8 499
0fa9e53d
JJ
500 if (argc > 1 && argc != 4) {
501 log_error("This program takes three or no arguments.");
502 return EXIT_FAILURE;
503 }
e23a0ce8 504
0fa9e53d
JJ
505 if (argc > 1)
506 arg_dest = argv[1];
e23a0ce8 507
0fa9e53d
JJ
508 log_set_target(LOG_TARGET_SAFE);
509 log_parse_environment();
510 log_open();
e2cb60fa 511
0fa9e53d 512 umask(0022);
e23a0ce8 513
0fa9e53d
JJ
514 arg_disks = hashmap_new(&string_hash_ops);
515 if (!arg_disks)
516 goto cleanup;
7ab064a6 517
0fa9e53d
JJ
518 r = parse_proc_cmdline(parse_proc_cmdline_item);
519 if (r < 0) {
520 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
521 r = EXIT_FAILURE;
522 }
7ab064a6 523
0fa9e53d
JJ
524 if (!arg_enabled) {
525 r = EXIT_SUCCESS;
526 goto cleanup;
e23a0ce8
LP
527 }
528
0fa9e53d
JJ
529 if (add_crypttab_devices() < 0)
530 goto cleanup;
531
532 if (add_proc_cmdline_devices() < 0)
533 goto cleanup;
534
535 r = EXIT_SUCCESS;
141a79f4
ZJS
536
537cleanup:
0fa9e53d
JJ
538 free_arg_disks();
539 free(arg_default_options);
540 free(arg_default_keyfile);
141a79f4 541
0fa9e53d 542 return r;
e23a0ce8 543}