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