]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-generator.c
Merge pull request #1693 from ssahani/word
[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>
e23a0ce8 23
0fa9e53d 24#include "dropin.h"
3ffd4af2 25#include "fd-util.h"
07630cea 26#include "fstab-util.h"
0fa9e53d
JJ
27#include "generator.h"
28#include "hashmap.h"
e23a0ce8 29#include "log.h"
49e942b2 30#include "mkdir.h"
bde29068 31#include "path-util.h"
07630cea 32#include "string-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 int create_disk(
54 const char *name,
55 const char *device,
56 const char *password,
57 const char *options) {
58
8eea8687
ZJS
59 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *to = NULL, *e = NULL,
60 *filtered = NULL;
7fd1b19b 61 _cleanup_fclose_ FILE *f = NULL;
8c11d3c1 62 bool noauto, nofail, tmp, swap;
744198e9
LP
63 char *from;
64 int r;
e23a0ce8
LP
65
66 assert(name);
67 assert(device);
68
b9f111b9
ZJS
69 noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
70 nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
a6dba978
ZJS
71 tmp = fstab_test_option(options, "tmp\0");
72 swap = fstab_test_option(options, "swap\0");
8c11d3c1
TG
73
74 if (tmp && swap) {
75 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
76 return -EINVAL;
77 }
155da457 78
744198e9
LP
79 e = unit_name_escape(name);
80 if (!e)
81 return log_oom();
82
7410616c
LP
83 r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
84 if (r < 0)
85 return log_error_errno(r, "Failed to generate unit name: %m");
e23a0ce8 86
b7def684 87 p = strjoin(arg_dest, "/", n, NULL);
24a988e9
HH
88 if (!p)
89 return log_oom();
e23a0ce8 90
f7f21d33 91 u = fstab_node_to_udev_node(device);
24a988e9
HH
92 if (!u)
93 return log_oom();
e23a0ce8 94
7410616c
LP
95 r = unit_name_from_path(u, ".device", &d);
96 if (r < 0)
97 return log_error_errno(r, "Failed to generate unit name: %m");
e23a0ce8 98
f7f21d33 99 f = fopen(p, "wxe");
4a62c710
MS
100 if (!f)
101 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
e23a0ce8 102
9ece938a 103 fputs(
6b1dc2bd 104 "# Automatically generated by systemd-cryptsetup-generator\n\n"
e23a0ce8 105 "[Unit]\n"
9ece938a 106 "Description=Cryptography Setup for %I\n"
c3834f9b 107 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
1b64d026 108 "SourcePath=/etc/crypttab\n"
e23a0ce8 109 "DefaultDependencies=no\n"
744198e9 110 "Conflicts=umount.target\n"
9ece938a 111 "BindsTo=dev-mapper-%i.device\n"
4469ff4a 112 "IgnoreOnIsolate=true\n"
d6bc8348 113 "After=cryptsetup-pre.target\n",
9ece938a 114 f);
e23a0ce8 115
155da457
LP
116 if (!nofail)
117 fprintf(f,
118 "Before=cryptsetup.target\n");
119
ceca9501 120 if (password) {
744198e9 121 if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
a0f70805 122 fputs("After=systemd-random-seed.service\n", f);
66a5dbdf 123 else if (!streq(password, "-") && !streq(password, "none")) {
744198e9
LP
124 _cleanup_free_ char *uu;
125
126 uu = fstab_node_to_udev_node(password);
127 if (!uu)
66a5dbdf
DR
128 return log_oom();
129
bde29068 130 if (!path_equal(uu, "/dev/null")) {
744198e9 131
bde29068 132 if (is_device_path(uu)) {
7410616c 133 _cleanup_free_ char *dd = NULL;
66a5dbdf 134
7410616c
LP
135 r = unit_name_from_path(uu, ".device", &dd);
136 if (r < 0)
137 return log_error_errno(r, "Failed to generate unit name: %m");
bde29068
LP
138
139 fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
140 } else
141 fprintf(f, "RequiresMountsFor=%s\n", password);
142 }
66a5dbdf 143 }
ceca9501 144 }
e23a0ce8 145
9ece938a
TW
146 if (is_device_path(u))
147 fprintf(f,
148 "BindsTo=%s\n"
149 "After=%s\n"
150 "Before=umount.target\n",
151 d, d);
152 else
153 fprintf(f,
154 "RequiresMountsFor=%s\n",
155 u);
156
8eea8687
ZJS
157 r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
158 if (r < 0)
159 return r;
160
e23a0ce8
LP
161 fprintf(f,
162 "\n[Service]\n"
163 "Type=oneshot\n"
164 "RemainAfterExit=yes\n"
90724929 165 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
260ab287 166 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
7f4e0805 167 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
8eea8687 168 name, u, strempty(password), strempty(filtered),
e23a0ce8
LP
169 name);
170
8c11d3c1 171 if (tmp)
e23a0ce8 172 fprintf(f,
50109038 173 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
1d3399e6 174 name);
e23a0ce8 175
8c11d3c1 176 if (swap)
e23a0ce8 177 fprintf(f,
50109038 178 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
1d3399e6 179 name);
e23a0ce8 180
4652c56c
ZJS
181 r = fflush_and_check(f);
182 if (r < 0)
183 return log_error_errno(r, "Failed to write file %s: %m", p);
e23a0ce8 184
63c372cb 185 from = strjoina("../", n);
74715b82 186
155da457 187 if (!noauto) {
e23a0ce8 188
b7def684 189 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
24a988e9
HH
190 if (!to)
191 return log_oom();
e23a0ce8 192
d2e54fae 193 mkdir_parents_label(to, 0755);
4a62c710
MS
194 if (symlink(from, to) < 0)
195 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
2f8cd170
LP
196
197 free(to);
155da457 198 if (!nofail)
b7def684 199 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
155da457 200 else
b7def684 201 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
24a988e9
HH
202 if (!to)
203 return log_oom();
2f8cd170 204
d2e54fae 205 mkdir_parents_label(to, 0755);
4a62c710
MS
206 if (symlink(from, to) < 0)
207 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
f7f21d33 208 }
74715b82 209
608d41f3 210 free(to);
b7def684 211 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
24a988e9
HH
212 if (!to)
213 return log_oom();
74715b82 214
d2e54fae 215 mkdir_parents_label(to, 0755);
4a62c710
MS
216 if (symlink(from, to) < 0)
217 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
74715b82 218
68395007 219 if (!noauto && !nofail) {
a6fb0dc1
HG
220 _cleanup_free_ char *dmname;
221 dmname = strjoin("dev-mapper-", e, ".device", NULL);
222 if (!dmname)
223 return log_oom();
224
225 r = write_drop_in(arg_dest, dmname, 90, "device-timeout",
8eea8687
ZJS
226 "# Automatically generated by systemd-cryptsetup-generator \n\n"
227 "[Unit]\nJobTimeoutSec=0");
23bbb0de
MS
228 if (r < 0)
229 return log_error_errno(r, "Failed to write device drop-in: %m");
68395007
HH
230 }
231
24a988e9 232 return 0;
e23a0ce8
LP
233}
234
0fa9e53d
JJ
235static void free_arg_disks(void) {
236 crypto_device *d;
237
238 while ((d = hashmap_steal_first(arg_disks))) {
239 free(d->uuid);
6cd5b12a 240 free(d->keyfile);
baade8cc 241 free(d->name);
0fa9e53d
JJ
242 free(d->options);
243 free(d);
244 }
245
246 hashmap_free(arg_disks);
247}
248
249static crypto_device *get_crypto_device(const char *uuid) {
250 int r;
251 crypto_device *d;
252
253 assert(uuid);
254
255 d = hashmap_get(arg_disks, uuid);
256 if (!d) {
257 d = new0(struct crypto_device, 1);
258 if (!d)
259 return NULL;
260
261 d->create = false;
baade8cc 262 d->keyfile = d->options = d->name = NULL;
0fa9e53d
JJ
263
264 d->uuid = strdup(uuid);
265 if (!d->uuid) {
266 free(d);
267 return NULL;
268 }
269
270 r = hashmap_put(arg_disks, d->uuid, d);
271 if (r < 0) {
272 free(d->uuid);
273 free(d);
274 return NULL;
275 }
276 }
277
278 return d;
279}
280
059cb385 281static int parse_proc_cmdline_item(const char *key, const char *value) {
74df0fca 282 int r;
0fa9e53d
JJ
283 crypto_device *d;
284 _cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
66a78c2b 285
059cb385
LP
286 if (STR_IN_SET(key, "luks", "rd.luks") && value) {
287
288 r = parse_boolean(value);
141a79f4 289 if (r < 0)
059cb385 290 log_warning("Failed to parse luks switch %s. Ignoring.", value);
141a79f4
ZJS
291 else
292 arg_enabled = r;
66a78c2b 293
059cb385 294 } else if (STR_IN_SET(key, "luks.crypttab", "rd.luks.crypttab") && value) {
66a78c2b 295
059cb385 296 r = parse_boolean(value);
141a79f4 297 if (r < 0)
059cb385 298 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value);
141a79f4
ZJS
299 else
300 arg_read_crypttab = r;
66a78c2b 301
059cb385 302 } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) {
66a78c2b 303
0fa9e53d
JJ
304 d = get_crypto_device(startswith(value, "luks-") ? value+5 : value);
305 if (!d)
141a79f4 306 return log_oom();
66a78c2b 307
0fa9e53d
JJ
308 d->create = arg_whitelist = true;
309
059cb385 310 } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) {
66a78c2b 311
0fa9e53d
JJ
312 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
313 if (r == 2) {
314 d = get_crypto_device(uuid);
315 if (!d)
316 return log_oom();
317
318 free(d->options);
319 d->options = uuid_value;
320 uuid_value = NULL;
321 } else if (free_and_strdup(&arg_default_options, value) < 0)
141a79f4 322 return log_oom();
66a78c2b 323
059cb385 324 } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) {
66a78c2b 325
6cd5b12a
JJ
326 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
327 if (r == 2) {
328 d = get_crypto_device(uuid);
329 if (!d)
330 return log_oom();
331
332 free(d->keyfile);
333 d->keyfile = uuid_value;
334 uuid_value = NULL;
c802a730 335 } else if (free_and_strdup(&arg_default_keyfile, value) < 0)
141a79f4 336 return log_oom();
7ab064a6 337
baade8cc
JJ
338 } else if (STR_IN_SET(key, "luks.name", "rd.luks.name") && value) {
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(d->name);
349 d->name = uuid_value;
350 uuid_value = NULL;
351 } else
352 log_warning("Failed to parse luks name switch %s. Ignoring.", value);
353
85013844 354 }
66a78c2b 355
24a988e9 356 return 0;
66a78c2b
LP
357}
358
0fa9e53d
JJ
359static int add_crypttab_devices(void) {
360 struct stat st;
361 unsigned crypttab_line = 0;
7fd1b19b 362 _cleanup_fclose_ FILE *f = NULL;
e23a0ce8 363
0fa9e53d
JJ
364 if (!arg_read_crypttab)
365 return 0;
366
367 f = fopen("/etc/crypttab", "re");
368 if (!f) {
369 if (errno != ENOENT)
370 log_error_errno(errno, "Failed to open /etc/crypttab: %m");
371 return 0;
e23a0ce8
LP
372 }
373
0fa9e53d
JJ
374 if (fstat(fileno(f), &st) < 0) {
375 log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
376 return 0;
377 }
e23a0ce8 378
0fa9e53d
JJ
379 for (;;) {
380 int r, k;
381 char line[LINE_MAX], *l, *uuid;
382 crypto_device *d = NULL;
383 _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
4c12626c 384
0fa9e53d
JJ
385 if (!fgets(line, sizeof(line), f))
386 break;
66a78c2b 387
0fa9e53d 388 crypttab_line++;
141a79f4 389
0fa9e53d
JJ
390 l = strstrip(line);
391 if (*l == '#' || *l == 0)
392 continue;
66a78c2b 393
0fa9e53d
JJ
394 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
395 if (k < 2 || k > 4) {
396 log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
397 continue;
398 }
66a78c2b 399
0fa9e53d
JJ
400 uuid = startswith(device, "UUID=");
401 if (!uuid)
402 uuid = path_startswith(device, "/dev/disk/by-uuid/");
403 if (!uuid)
404 uuid = startswith(name, "luks-");
405 if (uuid)
406 d = hashmap_get(arg_disks, uuid);
8973790e 407
0fa9e53d
JJ
408 if (arg_whitelist && !d) {
409 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
410 continue;
8973790e
LP
411 }
412
0fa9e53d
JJ
413 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
414 if (r < 0)
415 return r;
8973790e 416
0fa9e53d
JJ
417 if (d)
418 d->create = false;
419 }
8973790e 420
0fa9e53d
JJ
421 return 0;
422}
66a78c2b 423
0fa9e53d
JJ
424static int add_proc_cmdline_devices(void) {
425 int r;
426 Iterator i;
427 crypto_device *d;
66a78c2b 428
0fa9e53d
JJ
429 HASHMAP_FOREACH(d, arg_disks, i) {
430 const char *options;
baade8cc 431 _cleanup_free_ char *device = NULL;
66a78c2b 432
0fa9e53d
JJ
433 if (!d->create)
434 continue;
e23a0ce8 435
baade8cc
JJ
436 if (!d->name) {
437 d->name = strappend("luks-", d->uuid);
438 if (!d->name)
439 return log_oom();
440 }
e23a0ce8 441
0fa9e53d
JJ
442 device = strappend("UUID=", d->uuid);
443 if (!device)
444 return log_oom();
7ab064a6 445
0fa9e53d
JJ
446 if (d->options)
447 options = d->options;
448 else if (arg_default_options)
449 options = arg_default_options;
450 else
451 options = "timeout=0";
141a79f4 452
baade8cc 453 r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
0fa9e53d
JJ
454 if (r < 0)
455 return r;
e23a0ce8
LP
456 }
457
0fa9e53d
JJ
458 return 0;
459}
e23a0ce8 460
0fa9e53d
JJ
461int main(int argc, char *argv[]) {
462 int r = EXIT_FAILURE;
e23a0ce8 463
0fa9e53d
JJ
464 if (argc > 1 && argc != 4) {
465 log_error("This program takes three or no arguments.");
466 return EXIT_FAILURE;
467 }
e23a0ce8 468
0fa9e53d
JJ
469 if (argc > 1)
470 arg_dest = argv[1];
e23a0ce8 471
0fa9e53d
JJ
472 log_set_target(LOG_TARGET_SAFE);
473 log_parse_environment();
474 log_open();
e2cb60fa 475
0fa9e53d 476 umask(0022);
e23a0ce8 477
0fa9e53d
JJ
478 arg_disks = hashmap_new(&string_hash_ops);
479 if (!arg_disks)
480 goto cleanup;
7ab064a6 481
0fa9e53d
JJ
482 r = parse_proc_cmdline(parse_proc_cmdline_item);
483 if (r < 0) {
484 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
485 r = EXIT_FAILURE;
486 }
7ab064a6 487
0fa9e53d
JJ
488 if (!arg_enabled) {
489 r = EXIT_SUCCESS;
490 goto cleanup;
e23a0ce8
LP
491 }
492
0fa9e53d
JJ
493 if (add_crypttab_devices() < 0)
494 goto cleanup;
495
496 if (add_proc_cmdline_devices() < 0)
497 goto cleanup;
498
499 r = EXIT_SUCCESS;
141a79f4
ZJS
500
501cleanup:
0fa9e53d
JJ
502 free_arg_disks();
503 free(arg_default_options);
504 free(arg_default_keyfile);
141a79f4 505
0fa9e53d 506 return r;
e23a0ce8 507}