]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-generator.c
Merge pull request #1234 from medhefgo/master
[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 r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
82 if (r < 0)
83 return log_error_errno(r, "Failed to generate unit name: %m");
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 r = unit_name_from_path(u, ".device", &d);
94 if (r < 0)
95 return log_error_errno(r, "Failed to generate unit name: %m");
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 = NULL;
132
133 r = unit_name_from_path(uu, ".device", &dd);
134 if (r < 0)
135 return log_error_errno(r, "Failed to generate unit name: %m");
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 r = fflush_and_check(f);
180 if (r < 0)
181 return log_error_errno(r, "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) < 0)
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 for (;;) {
378 int r, k;
379 char line[LINE_MAX], *l, *uuid;
380 crypto_device *d = NULL;
381 _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
382
383 if (!fgets(line, sizeof(line), f))
384 break;
385
386 crypttab_line++;
387
388 l = strstrip(line);
389 if (*l == '#' || *l == 0)
390 continue;
391
392 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
393 if (k < 2 || k > 4) {
394 log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
395 continue;
396 }
397
398 uuid = startswith(device, "UUID=");
399 if (!uuid)
400 uuid = path_startswith(device, "/dev/disk/by-uuid/");
401 if (!uuid)
402 uuid = startswith(name, "luks-");
403 if (uuid)
404 d = hashmap_get(arg_disks, uuid);
405
406 if (arg_whitelist && !d) {
407 log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
408 continue;
409 }
410
411 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
412 if (r < 0)
413 return r;
414
415 if (d)
416 d->create = false;
417 }
418
419 return 0;
420 }
421
422 static int add_proc_cmdline_devices(void) {
423 int r;
424 Iterator i;
425 crypto_device *d;
426
427 HASHMAP_FOREACH(d, arg_disks, i) {
428 const char *options;
429 _cleanup_free_ char *device = NULL;
430
431 if (!d->create)
432 continue;
433
434 if (!d->name) {
435 d->name = strappend("luks-", d->uuid);
436 if (!d->name)
437 return log_oom();
438 }
439
440 device = strappend("UUID=", d->uuid);
441 if (!device)
442 return log_oom();
443
444 if (d->options)
445 options = d->options;
446 else if (arg_default_options)
447 options = arg_default_options;
448 else
449 options = "timeout=0";
450
451 r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
452 if (r < 0)
453 return r;
454 }
455
456 return 0;
457 }
458
459 int main(int argc, char *argv[]) {
460 int r = EXIT_FAILURE;
461
462 if (argc > 1 && argc != 4) {
463 log_error("This program takes three or no arguments.");
464 return EXIT_FAILURE;
465 }
466
467 if (argc > 1)
468 arg_dest = argv[1];
469
470 log_set_target(LOG_TARGET_SAFE);
471 log_parse_environment();
472 log_open();
473
474 umask(0022);
475
476 arg_disks = hashmap_new(&string_hash_ops);
477 if (!arg_disks)
478 goto cleanup;
479
480 r = parse_proc_cmdline(parse_proc_cmdline_item);
481 if (r < 0) {
482 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
483 r = EXIT_FAILURE;
484 }
485
486 if (!arg_enabled) {
487 r = EXIT_SUCCESS;
488 goto cleanup;
489 }
490
491 if (add_crypttab_devices() < 0)
492 goto cleanup;
493
494 if (add_proc_cmdline_devices() < 0)
495 goto cleanup;
496
497 r = EXIT_SUCCESS;
498
499 cleanup:
500 free_arg_disks();
501 free(arg_default_options);
502 free(arg_default_keyfile);
503
504 return r;
505 }