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