]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-generator.c
Merge pull request #7476 from jhxie/ycm-meson-backend
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-generator.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22
23 #include "alloc-util.h"
24 #include "dropin.h"
25 #include "fd-util.h"
26 #include "fileio.h"
27 #include "fstab-util.h"
28 #include "generator.h"
29 #include "hashmap.h"
30 #include "log.h"
31 #include "mkdir.h"
32 #include "parse-util.h"
33 #include "path-util.h"
34 #include "proc-cmdline.h"
35 #include "specifier.h"
36 #include "string-util.h"
37 #include "strv.h"
38 #include "unit-name.h"
39 #include "util.h"
40
41 typedef struct crypto_device {
42 char *uuid;
43 char *keyfile;
44 char *name;
45 char *options;
46 bool create;
47 } crypto_device;
48
49 static const char *arg_dest = "/tmp";
50 static bool arg_enabled = true;
51 static bool arg_read_crypttab = true;
52 static bool arg_whitelist = false;
53 static Hashmap *arg_disks = NULL;
54 static char *arg_default_options = NULL;
55 static char *arg_default_keyfile = NULL;
56
57 static int create_disk(
58 const char *name,
59 const char *device,
60 const char *password,
61 const char *options) {
62
63 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *e = NULL,
64 *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL;
65 _cleanup_fclose_ FILE *f = NULL;
66 const char *dmname;
67 bool noauto, nofail, tmp, swap, netdev;
68 int r;
69
70 assert(name);
71 assert(device);
72
73 noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
74 nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
75 tmp = fstab_test_option(options, "tmp\0");
76 swap = fstab_test_option(options, "swap\0");
77 netdev = fstab_test_option(options, "_netdev\0");
78
79 if (tmp && swap) {
80 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
81 return -EINVAL;
82 }
83
84 name_escaped = specifier_escape(name);
85 if (!name_escaped)
86 return log_oom();
87
88 e = unit_name_escape(name);
89 if (!e)
90 return log_oom();
91
92 r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
93 if (r < 0)
94 return log_error_errno(r, "Failed to generate unit name: %m");
95
96 p = strjoin(arg_dest, "/", n);
97 if (!p)
98 return log_oom();
99
100 u = fstab_node_to_udev_node(device);
101 if (!u)
102 return log_oom();
103
104 u_escaped = specifier_escape(u);
105 if (!u_escaped)
106 return log_oom();
107
108 r = unit_name_from_path(u, ".device", &d);
109 if (r < 0)
110 return log_error_errno(r, "Failed to generate unit name: %m");
111
112 password_escaped = specifier_escape(password);
113 if (!password_escaped)
114 return log_oom();
115
116 f = fopen(p, "wxe");
117 if (!f)
118 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
119
120 fprintf(f,
121 "# Automatically generated by systemd-cryptsetup-generator\n\n"
122 "[Unit]\n"
123 "Description=Cryptography Setup for %%I\n"
124 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
125 "SourcePath=/etc/crypttab\n"
126 "DefaultDependencies=no\n"
127 "Conflicts=umount.target\n"
128 "IgnoreOnIsolate=true\n"
129 "After=%s\n",
130 netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
131
132 if (!nofail)
133 fprintf(f,
134 "Before=%s\n",
135 netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
136
137 if (password) {
138 if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
139 fputs_unlocked("After=systemd-random-seed.service\n", f);
140 else if (!STR_IN_SET(password, "-", "none")) {
141 _cleanup_free_ char *uu;
142
143 uu = fstab_node_to_udev_node(password);
144 if (!uu)
145 return log_oom();
146
147 if (!path_equal(uu, "/dev/null")) {
148
149 if (path_startswith(uu, "/dev/")) {
150 _cleanup_free_ char *dd = NULL;
151
152 r = unit_name_from_path(uu, ".device", &dd);
153 if (r < 0)
154 return log_error_errno(r, "Failed to generate unit name: %m");
155
156 fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
157 } else
158 fprintf(f, "RequiresMountsFor=%s\n", password_escaped);
159 }
160 }
161 }
162
163 if (path_startswith(u, "/dev/")) {
164 fprintf(f,
165 "BindsTo=%s\n"
166 "After=%s\n"
167 "Before=umount.target\n",
168 d, d);
169
170 if (swap)
171 fputs_unlocked("Before=dev-mapper-%i.swap\n",
172 f);
173 } else
174 fprintf(f,
175 "RequiresMountsFor=%s\n",
176 u_escaped);
177
178
179 r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
180 if (r < 0)
181 return r;
182
183 filtered_escaped = specifier_escape(filtered);
184 if (!filtered_escaped)
185 return log_oom();
186
187 fprintf(f,
188 "\n[Service]\n"
189 "Type=oneshot\n"
190 "RemainAfterExit=yes\n"
191 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
192 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
193 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
194 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
195 name_escaped, u_escaped, strempty(password_escaped), strempty(filtered_escaped),
196 name_escaped);
197
198 if (tmp)
199 fprintf(f,
200 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
201 name_escaped);
202
203 if (swap)
204 fprintf(f,
205 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
206 name_escaped);
207
208 r = fflush_and_check(f);
209 if (r < 0)
210 return log_error_errno(r, "Failed to write file %s: %m", p);
211
212 if (!noauto) {
213 r = generator_add_symlink(arg_dest, d, "wants", n);
214 if (r < 0)
215 return r;
216
217 r = generator_add_symlink(arg_dest,
218 netdev ? "remote-cryptsetup.target" : "cryptsetup.target",
219 nofail ? "wants" : "requires", n);
220 if (r < 0)
221 return r;
222 }
223
224 dmname = strjoina("dev-mapper-", e, ".device");
225 r = generator_add_symlink(arg_dest, dmname, "requires", n);
226 if (r < 0)
227 return r;
228
229 if (!noauto && !nofail) {
230 r = write_drop_in(arg_dest, dmname, 90, "device-timeout",
231 "# Automatically generated by systemd-cryptsetup-generator \n\n"
232 "[Unit]\nJobTimeoutSec=0");
233 if (r < 0)
234 return log_error_errno(r, "Failed to write device drop-in: %m");
235 }
236
237 return 0;
238 }
239
240 static void crypt_device_free(crypto_device *d) {
241 free(d->uuid);
242 free(d->keyfile);
243 free(d->name);
244 free(d->options);
245 free(d);
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 return mfree(d);
266
267 r = hashmap_put(arg_disks, d->uuid, d);
268 if (r < 0) {
269 free(d->uuid);
270 return mfree(d);
271 }
272 }
273
274 return d;
275 }
276
277 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
278 _cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
279 crypto_device *d;
280 int r;
281
282 if (streq(key, "luks")) {
283
284 r = value ? parse_boolean(value) : 1;
285 if (r < 0)
286 log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value);
287 else
288 arg_enabled = r;
289
290 } else if (streq(key, "luks.crypttab")) {
291
292 r = value ? parse_boolean(value) : 1;
293 if (r < 0)
294 log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value);
295 else
296 arg_read_crypttab = r;
297
298 } else if (streq(key, "luks.uuid")) {
299
300 if (proc_cmdline_value_missing(key, value))
301 return 0;
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 (streq(key, "luks.options")) {
310
311 if (proc_cmdline_value_missing(key, value))
312 return 0;
313
314 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
315 if (r == 2) {
316 d = get_crypto_device(uuid);
317 if (!d)
318 return log_oom();
319
320 free_and_replace(d->options, uuid_value);
321 } else if (free_and_strdup(&arg_default_options, value) < 0)
322 return log_oom();
323
324 } else if (streq(key, "luks.key")) {
325
326 if (proc_cmdline_value_missing(key, value))
327 return 0;
328
329 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
330 if (r == 2) {
331 d = get_crypto_device(uuid);
332 if (!d)
333 return log_oom();
334
335 free_and_replace(d->keyfile, uuid_value);
336 } else if (free_and_strdup(&arg_default_keyfile, value) < 0)
337 return log_oom();
338
339 } else if (streq(key, "luks.name")) {
340
341 if (proc_cmdline_value_missing(key, value))
342 return 0;
343
344 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
345 if (r == 2) {
346 d = get_crypto_device(uuid);
347 if (!d)
348 return log_oom();
349
350 d->create = arg_whitelist = true;
351
352 free_and_replace(d->name, uuid_value);
353 } else
354 log_warning("Failed to parse luks name switch %s. Ignoring.", value);
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 (IN_SET(*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;
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 r = log_oom();
482 goto finish;
483 }
484
485 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
486 if (r < 0) {
487 log_warning_errno(r, "Failed to parse kernel command line: %m");
488 goto finish;
489 }
490
491 if (!arg_enabled) {
492 r = 0;
493 goto finish;
494 }
495
496 r = add_crypttab_devices();
497 if (r < 0)
498 goto finish;
499
500 r = add_proc_cmdline_devices();
501 if (r < 0)
502 goto finish;
503
504 r = 0;
505
506 finish:
507 hashmap_free_with_destructor(arg_disks, crypt_device_free);
508 free(arg_default_options);
509 free(arg_default_keyfile);
510
511 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
512 }