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