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