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