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