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