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