]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-generator.c
cryptsetup: warn if /etc/crypttab is world-readable
[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
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"
49e942b2 29#include "mkdir.h"
66a78c2b
LP
30#include "virt.h"
31#include "strv.h"
a860325e 32#include "fileio.h"
e23a0ce8 33
66a78c2b
LP
34static const char *arg_dest = "/tmp";
35static bool arg_enabled = true;
36static bool arg_read_crypttab = true;
e23a0ce8
LP
37
38static bool has_option(const char *haystack, const char *needle) {
39 const char *f = haystack;
40 size_t l;
41
f653f683
LP
42 assert(needle);
43
44 if (!haystack)
45 return false;
46
e23a0ce8
LP
47 l = strlen(needle);
48
49 while ((f = strstr(f, needle))) {
50
51 if (f > haystack && f[-1] != ',') {
52 f++;
53 continue;
54 }
55
aae5220d 56 if (f[l] != 0 && f[l] != ',') {
e23a0ce8
LP
57 f++;
58 continue;
59 }
60
61 return true;
62 }
63
64 return false;
65}
66
67static int create_disk(
68 const char *name,
69 const char *device,
70 const char *password,
71 const char *options) {
72
7fd1b19b
HH
73 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
74 _cleanup_fclose_ FILE *f = NULL;
155da457 75 bool noauto, nofail;
e23a0ce8
LP
76
77 assert(name);
78 assert(device);
79
155da457
LP
80 noauto = has_option(options, "noauto");
81 nofail = has_option(options, "nofail");
82
35eb6b12 83 n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
24a988e9
HH
84 if (!n)
85 return log_oom();
e23a0ce8 86
b7def684 87 p = strjoin(arg_dest, "/", n, NULL);
24a988e9
HH
88 if (!p)
89 return log_oom();
e23a0ce8 90
f7f21d33 91 u = fstab_node_to_udev_node(device);
24a988e9
HH
92 if (!u)
93 return log_oom();
e23a0ce8 94
f7f21d33 95 d = unit_name_from_path(u, ".device");
24a988e9
HH
96 if (!d)
97 return log_oom();
e23a0ce8 98
f7f21d33
LP
99 f = fopen(p, "wxe");
100 if (!f) {
1cda32b8 101 log_error("Failed to create unit file %s: %m", p);
24a988e9 102 return -errno;
e23a0ce8
LP
103 }
104
9ece938a 105 fputs(
6b1dc2bd 106 "# Automatically generated by systemd-cryptsetup-generator\n\n"
e23a0ce8 107 "[Unit]\n"
9ece938a 108 "Description=Cryptography Setup for %I\n"
1c732700 109 "Documentation=man:systemd-cryptsetup@.service(8) man:crypttab(5)\n"
1b64d026 110 "SourcePath=/etc/crypttab\n"
155da457 111 "Conflicts=umount.target\n"
e23a0ce8 112 "DefaultDependencies=no\n"
9ece938a
TW
113 "BindsTo=dev-mapper-%i.device\n"
114 "After=systemd-readahead-collect.service systemd-readahead-replay.service\n",
115 f);
e23a0ce8 116
155da457
LP
117 if (!nofail)
118 fprintf(f,
119 "Before=cryptsetup.target\n");
120
ceca9501
TW
121 if (password) {
122 if (streq(password, "/dev/urandom") ||
123 streq(password, "/dev/random") ||
124 streq(password, "/dev/hw_random"))
125 fputs("After=systemd-random-seed-load.service\n", f);
126 else if (!streq(password, "-") &&
127 !streq(password, "none"))
128 fprintf(f,
129 "RequiresMountsFor=%s\n",
130 password);
131 }
e23a0ce8 132
9ece938a
TW
133 if (is_device_path(u))
134 fprintf(f,
135 "BindsTo=%s\n"
136 "After=%s\n"
137 "Before=umount.target\n",
138 d, d);
139 else
140 fprintf(f,
141 "RequiresMountsFor=%s\n",
142 u);
143
e23a0ce8
LP
144 fprintf(f,
145 "\n[Service]\n"
146 "Type=oneshot\n"
147 "RemainAfterExit=yes\n"
90724929 148 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
260ab287 149 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
7f4e0805 150 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
e23a0ce8
LP
151 name, u, strempty(password), strempty(options),
152 name);
153
f653f683 154 if (has_option(options, "tmp"))
e23a0ce8 155 fprintf(f,
50109038 156 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
1d3399e6 157 name);
e23a0ce8 158
f653f683 159 if (has_option(options, "swap"))
e23a0ce8 160 fprintf(f,
50109038 161 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
1d3399e6 162 name);
e23a0ce8
LP
163
164 fflush(f);
165
166 if (ferror(f)) {
1cda32b8 167 log_error("Failed to write file %s: %m", p);
24a988e9 168 return -errno;
e23a0ce8
LP
169 }
170
24a988e9
HH
171 if (asprintf(&from, "../%s", n) < 0)
172 return log_oom();
74715b82 173
155da457 174 if (!noauto) {
e23a0ce8 175
b7def684 176 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
24a988e9
HH
177 if (!to)
178 return log_oom();
e23a0ce8 179
d2e54fae 180 mkdir_parents_label(to, 0755);
e23a0ce8 181 if (symlink(from, to) < 0) {
c79bb9e4 182 log_error("Failed to create symlink %s: %m", to);
24a988e9 183 return -errno;
e23a0ce8 184 }
2f8cd170
LP
185
186 free(to);
155da457 187 if (!nofail)
b7def684 188 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
155da457 189 else
b7def684 190 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
24a988e9
HH
191 if (!to)
192 return log_oom();
2f8cd170 193
d2e54fae 194 mkdir_parents_label(to, 0755);
155da457 195 if (symlink(from, to) < 0) {
c79bb9e4 196 log_error("Failed to create symlink %s: %m", to);
24a988e9 197 return -errno;
2f8cd170 198 }
f7f21d33 199 }
74715b82
LP
200
201 e = unit_name_escape(name);
608d41f3
LP
202 if (!e)
203 return log_oom();
204
205 free(to);
b7def684 206 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
24a988e9
HH
207 if (!to)
208 return log_oom();
74715b82 209
d2e54fae 210 mkdir_parents_label(to, 0755);
74715b82 211 if (symlink(from, to) < 0) {
c79bb9e4 212 log_error("Failed to create symlink %s: %m", to);
24a988e9 213 return -errno;
74715b82
LP
214 }
215
68395007
HH
216 if (!noauto && !nofail) {
217 int r;
218 free(p);
219 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
220 if (!p)
221 return log_oom();
222
223 mkdir_parents_label(p, 0755);
224
225 r = write_string_file(p,
226 "# Automatically generated by systemd-cryptsetup-generator\n\n"
227 "[Unit]\n"
228 "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
229 if (r)
230 return r;
231 }
232
24a988e9 233 return 0;
e23a0ce8
LP
234}
235
951657bd 236static int parse_proc_cmdline(char ***arg_proc_cmdline_disks, char **arg_proc_cmdline_keyfile) {
7fd1b19b 237 _cleanup_free_ char *line = NULL;
24a988e9 238 char *w = NULL, *state = NULL;
66a78c2b
LP
239 int r;
240 size_t l;
241
242 if (detect_container(NULL) > 0)
243 return 0;
244
245 r = read_one_line_file("/proc/cmdline", &line);
246 if (r < 0) {
247 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
248 return 0;
249 }
250
251 FOREACH_WORD_QUOTED(w, l, line, state) {
7fd1b19b 252 _cleanup_free_ char *word = NULL;
66a78c2b
LP
253
254 word = strndup(w, l);
24a988e9
HH
255 if (!word)
256 return log_oom();
66a78c2b
LP
257
258 if (startswith(word, "luks=")) {
259 r = parse_boolean(word + 5);
260 if (r < 0)
261 log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
262 else
263 arg_enabled = r;
264
265 } else if (startswith(word, "rd.luks=")) {
266
267 if (in_initrd()) {
268 r = parse_boolean(word + 8);
269 if (r < 0)
270 log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
271 else
272 arg_enabled = r;
273 }
274
275 } else if (startswith(word, "luks.crypttab=")) {
276 r = parse_boolean(word + 14);
277 if (r < 0)
278 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
279 else
280 arg_read_crypttab = r;
281
282 } else if (startswith(word, "rd.luks.crypttab=")) {
283
284 if (in_initrd()) {
285 r = parse_boolean(word + 17);
286 if (r < 0)
287 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
288 else
289 arg_read_crypttab = r;
290 }
291
292 } else if (startswith(word, "luks.uuid=")) {
e32530cb 293 if (strv_extend(arg_proc_cmdline_disks, word + 10) < 0)
24a988e9
HH
294 return log_oom();
295
66a78c2b
LP
296 } else if (startswith(word, "rd.luks.uuid=")) {
297
298 if (in_initrd()) {
e32530cb 299 if (strv_extend(arg_proc_cmdline_disks, word + 13) < 0)
24a988e9 300 return log_oom();
66a78c2b
LP
301 }
302
951657bd
HH
303 } else if (startswith(word, "luks.key=")) {
304 *arg_proc_cmdline_keyfile = strdup(word + 9);
5a8e2178 305 if (!*arg_proc_cmdline_keyfile)
951657bd
HH
306 return log_oom();
307
308 } else if (startswith(word, "rd.luks.key=")) {
309
310 if (in_initrd()) {
311 if (*arg_proc_cmdline_keyfile)
312 free(*arg_proc_cmdline_keyfile);
313 *arg_proc_cmdline_keyfile = strdup(word + 12);
5a8e2178 314 if (!*arg_proc_cmdline_keyfile)
951657bd
HH
315 return log_oom();
316 }
317
66a78c2b
LP
318 } else if (startswith(word, "luks.") ||
319 (in_initrd() && startswith(word, "rd.luks."))) {
320
321 log_warning("Unknown kernel switch %s. Ignoring.", word);
322 }
66a78c2b
LP
323 }
324
24a988e9 325 strv_uniq(*arg_proc_cmdline_disks);
66a78c2b 326
24a988e9 327 return 0;
66a78c2b
LP
328}
329
e23a0ce8 330int main(int argc, char *argv[]) {
8973790e
LP
331 _cleanup_strv_free_ char **arg_proc_cmdline_disks_done = NULL;
332 _cleanup_strv_free_ char **arg_proc_cmdline_disks = NULL;
333 _cleanup_free_ char *arg_proc_cmdline_keyfile = NULL;
7fd1b19b 334 _cleanup_fclose_ FILE *f = NULL;
e23a0ce8 335 unsigned n = 0;
24a988e9 336 int r = EXIT_SUCCESS;
66a78c2b 337 char **i;
e23a0ce8 338
07719a21
LP
339 if (argc > 1 && argc != 4) {
340 log_error("This program takes three or no arguments.");
e23a0ce8
LP
341 return EXIT_FAILURE;
342 }
343
2a796654
LP
344 if (argc > 1)
345 arg_dest = argv[1];
e23a0ce8 346
a6903061 347 log_set_target(LOG_TARGET_SAFE);
e23a0ce8
LP
348 log_parse_environment();
349 log_open();
350
4c12626c
LP
351 umask(0022);
352
951657bd 353 if (parse_proc_cmdline(&arg_proc_cmdline_disks, &arg_proc_cmdline_keyfile) < 0)
66a78c2b
LP
354 return EXIT_FAILURE;
355
24a988e9
HH
356 if (!arg_enabled)
357 return EXIT_SUCCESS;
66a78c2b 358
e2cb60fa 359 if (arg_read_crypttab) {
8973790e 360 struct stat st;
66a78c2b 361
8973790e 362 f = fopen("/etc/crypttab", "re");
e2cb60fa
HH
363 if (!f) {
364 if (errno == ENOENT)
365 r = EXIT_SUCCESS;
366 else {
367 r = EXIT_FAILURE;
368 log_error("Failed to open /etc/crypttab: %m");
369 }
8973790e
LP
370
371 goto next;
372 }
373
374 if (fstat(fileno(f), &st) < 0) {
375 log_error("Failed to stat /etc/crypttab: %m");
376 r = EXIT_FAILURE;
377 goto next;
378 }
379
380 if (st.st_mode & 0005)
381 log_warning("/etc/crypttab is world-readable. This is usually not a good idea.");
382
383 for (;;) {
e2cb60fa 384 char line[LINE_MAX], *l;
7fd1b19b 385 _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
e2cb60fa 386 int k;
66a78c2b 387
e2cb60fa
HH
388 if (!fgets(line, sizeof(line), f))
389 break;
66a78c2b 390
e2cb60fa 391 n++;
66a78c2b 392
e2cb60fa
HH
393 l = strstrip(line);
394 if (*l == '#' || *l == 0)
395 continue;
e23a0ce8 396
e2cb60fa
HH
397 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
398 if (k < 2 || k > 4) {
399 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
400 r = EXIT_FAILURE;
24a988e9 401 continue;
e2cb60fa 402 }
e23a0ce8 403
e2cb60fa
HH
404 if (arg_proc_cmdline_disks) {
405 /*
406 If luks UUIDs are specified on the kernel command line, use them as a filter
407 for /etc/crypttab and only generate units for those.
408 */
409 STRV_FOREACH(i, arg_proc_cmdline_disks) {
7fd1b19b 410 _cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
e2cb60fa
HH
411 const char *p = *i;
412
413 if (startswith(p, "luks-"))
414 p += 5;
415
416 proc_name = strappend("luks-", p);
417 proc_device = strappend("UUID=", p);
418
24a988e9
HH
419 if (!proc_name || !proc_device)
420 return log_oom();
421
e2cb60fa 422 if (streq(proc_device, device) || streq(proc_name, name)) {
e2cb60fa
HH
423 if (create_disk(name, device, password, options) < 0)
424 r = EXIT_FAILURE;
425
e32530cb 426 if (strv_extend(&arg_proc_cmdline_disks_done, p) < 0)
24a988e9 427 return log_oom();
e2cb60fa 428 }
e2cb60fa
HH
429 }
430 } else {
431 if (create_disk(name, device, password, options) < 0)
432 r = EXIT_FAILURE;
433 }
e2cb60fa 434 }
e23a0ce8
LP
435 }
436
8973790e 437next:
e2cb60fa
HH
438 STRV_FOREACH(i, arg_proc_cmdline_disks) {
439 /*
440 Generate units for those UUIDs, which were specified
441 on the kernel command line and not yet written.
442 */
e23a0ce8 443
7fd1b19b 444 _cleanup_free_ char *name = NULL, *device = NULL;
e2cb60fa 445 const char *p = *i;
e23a0ce8 446
e2cb60fa
HH
447 if (startswith(p, "luks-"))
448 p += 5;
e23a0ce8 449
e2cb60fa 450 if (strv_contains(arg_proc_cmdline_disks_done, p))
e23a0ce8
LP
451 continue;
452
e2cb60fa
HH
453 name = strappend("luks-", p);
454 device = strappend("UUID=", p);
455
24a988e9
HH
456 if (!name || !device)
457 return log_oom();
e23a0ce8 458
951657bd 459 if (create_disk(name, device, arg_proc_cmdline_keyfile, "timeout=0") < 0)
e23a0ce8 460 r = EXIT_FAILURE;
e23a0ce8
LP
461 }
462
e23a0ce8
LP
463 return r;
464}