]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-generator.c
set IgnoreOnIsolate=true for systemd-cryptsetup@.service
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-generator.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <string.h>
23 #include <errno.h>
24 #include <unistd.h>
25
26 #include "log.h"
27 #include "util.h"
28 #include "unit-name.h"
29 #include "mkdir.h"
30 #include "virt.h"
31 #include "strv.h"
32 #include "fileio.h"
33
34 static const char *arg_dest = "/tmp";
35 static bool arg_enabled = true;
36 static bool arg_read_crypttab = true;
37
38 static bool has_option(const char *haystack, const char *needle) {
39 const char *f = haystack;
40 size_t l;
41
42 assert(needle);
43
44 if (!haystack)
45 return false;
46
47 l = strlen(needle);
48
49 while ((f = strstr(f, needle))) {
50
51 if (f > haystack && f[-1] != ',') {
52 f++;
53 continue;
54 }
55
56 if (f[l] != 0 && f[l] != ',') {
57 f++;
58 continue;
59 }
60
61 return true;
62 }
63
64 return false;
65 }
66
67 static int create_disk(
68 const char *name,
69 const char *device,
70 const char *password,
71 const char *options) {
72
73 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
74 _cleanup_fclose_ FILE *f = NULL;
75 bool noauto, nofail, tmp, swap;
76
77 assert(name);
78 assert(device);
79
80 noauto = has_option(options, "noauto");
81 nofail = has_option(options, "nofail");
82 tmp = has_option(options, "tmp");
83 swap = has_option(options, "swap");
84
85 if (tmp && swap) {
86 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
87 return -EINVAL;
88 }
89
90 n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
91 if (!n)
92 return log_oom();
93
94 p = strjoin(arg_dest, "/", n, NULL);
95 if (!p)
96 return log_oom();
97
98 u = fstab_node_to_udev_node(device);
99 if (!u)
100 return log_oom();
101
102 d = unit_name_from_path(u, ".device");
103 if (!d)
104 return log_oom();
105
106 f = fopen(p, "wxe");
107 if (!f) {
108 log_error("Failed to create unit file %s: %m", p);
109 return -errno;
110 }
111
112 fputs(
113 "# Automatically generated by systemd-cryptsetup-generator\n\n"
114 "[Unit]\n"
115 "Description=Cryptography Setup for %I\n"
116 "Documentation=man:systemd-cryptsetup@.service(8) man:crypttab(5)\n"
117 "SourcePath=/etc/crypttab\n"
118 "Conflicts=umount.target\n"
119 "DefaultDependencies=no\n"
120 "BindsTo=dev-mapper-%i.device\n"
121 "IgnoreOnIsolate=true\n"
122 "After=systemd-readahead-collect.service systemd-readahead-replay.service\n",
123 f);
124
125 if (!nofail)
126 fprintf(f,
127 "Before=cryptsetup.target\n");
128
129 if (password) {
130 if (streq(password, "/dev/urandom") ||
131 streq(password, "/dev/random") ||
132 streq(password, "/dev/hw_random"))
133 fputs("After=systemd-random-seed.service\n", f);
134 else if (!streq(password, "-") &&
135 !streq(password, "none"))
136 fprintf(f,
137 "RequiresMountsFor=%s\n",
138 password);
139 }
140
141 if (is_device_path(u))
142 fprintf(f,
143 "BindsTo=%s\n"
144 "After=%s\n"
145 "Before=umount.target\n",
146 d, d);
147 else
148 fprintf(f,
149 "RequiresMountsFor=%s\n",
150 u);
151
152 fprintf(f,
153 "\n[Service]\n"
154 "Type=oneshot\n"
155 "RemainAfterExit=yes\n"
156 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
157 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
158 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
159 name, u, strempty(password), strempty(options),
160 name);
161
162 if (tmp)
163 fprintf(f,
164 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
165 name);
166
167 if (swap)
168 fprintf(f,
169 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
170 name);
171
172 fflush(f);
173
174 if (ferror(f)) {
175 log_error("Failed to write file %s: %m", p);
176 return -errno;
177 }
178
179 if (asprintf(&from, "../%s", n) < 0)
180 return log_oom();
181
182 if (!noauto) {
183
184 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
185 if (!to)
186 return log_oom();
187
188 mkdir_parents_label(to, 0755);
189 if (symlink(from, to) < 0) {
190 log_error("Failed to create symlink %s: %m", to);
191 return -errno;
192 }
193
194 free(to);
195 if (!nofail)
196 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
197 else
198 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
199 if (!to)
200 return log_oom();
201
202 mkdir_parents_label(to, 0755);
203 if (symlink(from, to) < 0) {
204 log_error("Failed to create symlink %s: %m", to);
205 return -errno;
206 }
207 }
208
209 e = unit_name_escape(name);
210 if (!e)
211 return log_oom();
212
213 free(to);
214 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
215 if (!to)
216 return log_oom();
217
218 mkdir_parents_label(to, 0755);
219 if (symlink(from, to) < 0) {
220 log_error("Failed to create symlink %s: %m", to);
221 return -errno;
222 }
223
224 if (!noauto && !nofail) {
225 int r;
226 free(p);
227 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
228 if (!p)
229 return log_oom();
230
231 mkdir_parents_label(p, 0755);
232
233 r = write_string_file(p,
234 "# Automatically generated by systemd-cryptsetup-generator\n\n"
235 "[Unit]\n"
236 "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
237 if (r)
238 return r;
239 }
240
241 return 0;
242 }
243
244 static int parse_proc_cmdline(char ***arg_proc_cmdline_disks, char ***arg_proc_cmdline_options, char **arg_proc_cmdline_keyfile) {
245 _cleanup_free_ char *line = NULL;
246 char *w = NULL, *state = NULL;
247 int r;
248 size_t l;
249
250 if (detect_container(NULL) > 0)
251 return 0;
252
253 r = read_one_line_file("/proc/cmdline", &line);
254 if (r < 0) {
255 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
256 return 0;
257 }
258
259 FOREACH_WORD_QUOTED(w, l, line, state) {
260 _cleanup_free_ char *word = NULL;
261
262 word = strndup(w, l);
263 if (!word)
264 return log_oom();
265
266 if (startswith(word, "luks=")) {
267 r = parse_boolean(word + 5);
268 if (r < 0)
269 log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
270 else
271 arg_enabled = r;
272
273 } else if (startswith(word, "rd.luks=")) {
274
275 if (in_initrd()) {
276 r = parse_boolean(word + 8);
277 if (r < 0)
278 log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
279 else
280 arg_enabled = r;
281 }
282
283 } else if (startswith(word, "luks.crypttab=")) {
284 r = parse_boolean(word + 14);
285 if (r < 0)
286 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
287 else
288 arg_read_crypttab = r;
289
290 } else if (startswith(word, "rd.luks.crypttab=")) {
291
292 if (in_initrd()) {
293 r = parse_boolean(word + 17);
294 if (r < 0)
295 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
296 else
297 arg_read_crypttab = r;
298 }
299
300 } else if (startswith(word, "luks.uuid=")) {
301 if (strv_extend(arg_proc_cmdline_disks, word + 10) < 0)
302 return log_oom();
303
304 } else if (startswith(word, "rd.luks.uuid=")) {
305
306 if (in_initrd()) {
307 if (strv_extend(arg_proc_cmdline_disks, word + 13) < 0)
308 return log_oom();
309 }
310
311 } else if (startswith(word, "luks.options=")) {
312 if (strv_extend(arg_proc_cmdline_options, word + 13) < 0)
313 return log_oom();
314
315 } else if (startswith(word, "rd.luks.options=")) {
316
317 if (in_initrd()) {
318 if (strv_extend(arg_proc_cmdline_options, word + 16) < 0)
319 return log_oom();
320 }
321
322 } else if (startswith(word, "luks.key=")) {
323 if (*arg_proc_cmdline_keyfile)
324 free(*arg_proc_cmdline_keyfile);
325 *arg_proc_cmdline_keyfile = strdup(word + 9);
326 if (!*arg_proc_cmdline_keyfile)
327 return log_oom();
328
329 } else if (startswith(word, "rd.luks.key=")) {
330
331 if (in_initrd()) {
332 if (*arg_proc_cmdline_keyfile)
333 free(*arg_proc_cmdline_keyfile);
334 *arg_proc_cmdline_keyfile = strdup(word + 12);
335 if (!*arg_proc_cmdline_keyfile)
336 return log_oom();
337 }
338
339 } else if (startswith(word, "luks.") ||
340 (in_initrd() && startswith(word, "rd.luks."))) {
341
342 log_warning("Unknown kernel switch %s. Ignoring.", word);
343 }
344 }
345
346 strv_uniq(*arg_proc_cmdline_disks);
347
348 return 0;
349 }
350
351 int main(int argc, char *argv[]) {
352 _cleanup_strv_free_ char **arg_proc_cmdline_disks_done = NULL;
353 _cleanup_strv_free_ char **arg_proc_cmdline_disks = NULL;
354 _cleanup_strv_free_ char **arg_proc_cmdline_options = NULL;
355 _cleanup_free_ char *arg_proc_cmdline_keyfile = NULL;
356 _cleanup_fclose_ FILE *f = NULL;
357 unsigned n = 0;
358 int r = EXIT_SUCCESS;
359 char **i;
360
361 if (argc > 1 && argc != 4) {
362 log_error("This program takes three or no arguments.");
363 return EXIT_FAILURE;
364 }
365
366 if (argc > 1)
367 arg_dest = argv[1];
368
369 log_set_target(LOG_TARGET_SAFE);
370 log_parse_environment();
371 log_open();
372
373 umask(0022);
374
375 if (parse_proc_cmdline(&arg_proc_cmdline_disks, &arg_proc_cmdline_options, &arg_proc_cmdline_keyfile) < 0)
376 return EXIT_FAILURE;
377
378 if (!arg_enabled)
379 return EXIT_SUCCESS;
380
381 if (arg_read_crypttab) {
382 struct stat st;
383
384 f = fopen("/etc/crypttab", "re");
385 if (!f) {
386 if (errno == ENOENT)
387 r = EXIT_SUCCESS;
388 else {
389 r = EXIT_FAILURE;
390 log_error("Failed to open /etc/crypttab: %m");
391 }
392
393 goto next;
394 }
395
396 if (fstat(fileno(f), &st) < 0) {
397 log_error("Failed to stat /etc/crypttab: %m");
398 r = EXIT_FAILURE;
399 goto next;
400 }
401
402 /* If we readd support for specifying passphrases
403 * directly in crypttabe we should upgrade the warning
404 * below, though possibly only if a passphrase is
405 * specified directly. */
406 if (st.st_mode & 0005)
407 log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
408
409 for (;;) {
410 char line[LINE_MAX], *l;
411 _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
412 int k;
413
414 if (!fgets(line, sizeof(line), f))
415 break;
416
417 n++;
418
419 l = strstrip(line);
420 if (*l == '#' || *l == 0)
421 continue;
422
423 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
424 if (k < 2 || k > 4) {
425 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
426 r = EXIT_FAILURE;
427 continue;
428 }
429
430 if (arg_proc_cmdline_options) {
431 /*
432 If options are specified on the kernel commandline, let them override
433 the ones from crypttab.
434 */
435 STRV_FOREACH(i, arg_proc_cmdline_options) {
436 _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
437 const char *p = *i;
438
439 k = sscanf(p, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
440 if (k == 2 && streq(proc_uuid, device + 5)) {
441 if (options)
442 free(options);
443 options = strdup(p);
444 if (!proc_options)
445 return log_oom();
446 }
447 }
448 }
449
450 if (arg_proc_cmdline_disks) {
451 /*
452 If luks UUIDs are specified on the kernel command line, use them as a filter
453 for /etc/crypttab and only generate units for those.
454 */
455 STRV_FOREACH(i, arg_proc_cmdline_disks) {
456 _cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
457 const char *p = *i;
458
459 if (startswith(p, "luks-"))
460 p += 5;
461
462 proc_name = strappend("luks-", p);
463 proc_device = strappend("UUID=", p);
464
465 if (!proc_name || !proc_device)
466 return log_oom();
467
468 if (streq(proc_device, device) || streq(proc_name, name)) {
469 if (create_disk(name, device, password, options) < 0)
470 r = EXIT_FAILURE;
471
472 if (strv_extend(&arg_proc_cmdline_disks_done, p) < 0)
473 return log_oom();
474 }
475 }
476 } else {
477 if (create_disk(name, device, password, options) < 0)
478 r = EXIT_FAILURE;
479 }
480 }
481 }
482
483 next:
484 STRV_FOREACH(i, arg_proc_cmdline_disks) {
485 /*
486 Generate units for those UUIDs, which were specified
487 on the kernel command line and not yet written.
488 */
489
490 _cleanup_free_ char *name = NULL, *device = NULL, *options = NULL;
491 const char *p = *i;
492
493 if (startswith(p, "luks-"))
494 p += 5;
495
496 if (strv_contains(arg_proc_cmdline_disks_done, p))
497 continue;
498
499 name = strappend("luks-", p);
500 device = strappend("UUID=", p);
501
502 if (!name || !device)
503 return log_oom();
504
505 if (arg_proc_cmdline_options) {
506 /*
507 If options are specified on the kernel commandline, use them.
508 */
509 char **j;
510
511 STRV_FOREACH(j, arg_proc_cmdline_options) {
512 _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
513 const char *s = *j;
514 int k;
515
516 k = sscanf(s, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
517 if (k == 2) {
518 if (streq(proc_uuid, device + 5)) {
519 if (options)
520 free(options);
521 options = strdup(proc_options);
522 if (!options)
523 return log_oom();
524 }
525 } else if (!options) {
526 /*
527 Fall back to options without a specified UUID
528 */
529 options = strdup(s);
530 if (!options)
531 return log_oom();
532 }
533 }
534 }
535
536 if (!options) {
537 options = strdup("timeout=0");
538 if (!options)
539 return log_oom();
540 }
541
542 if (create_disk(name, device, arg_proc_cmdline_keyfile, options) < 0)
543 r = EXIT_FAILURE;
544 }
545
546 return r;
547 }