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