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