]> git.ipfire.org Git - thirdparty/systemd.git/blame - 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
CommitLineData
e23a0ce8
LP
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
5430f7f2
LP
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
e23a0ce8
LP
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
5430f7f2 16 Lesser General Public License for more details.
e23a0ce8 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
e23a0ce8
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
e23a0ce8 22#include <errno.h>
e23a0ce8 23
0fa9e53d 24#include "dropin.h"
07630cea 25#include "fstab-util.h"
0fa9e53d
JJ
26#include "generator.h"
27#include "hashmap.h"
e23a0ce8 28#include "log.h"
49e942b2 29#include "mkdir.h"
bde29068 30#include "path-util.h"
07630cea 31#include "string-util.h"
0fa9e53d
JJ
32#include "strv.h"
33#include "unit-name.h"
34#include "util.h"
35
36typedef struct crypto_device {
37 char *uuid;
6cd5b12a 38 char *keyfile;
baade8cc 39 char *name;
0fa9e53d
JJ
40 char *options;
41 bool create;
42} crypto_device;
e23a0ce8 43
66a78c2b
LP
44static const char *arg_dest = "/tmp";
45static bool arg_enabled = true;
46static bool arg_read_crypttab = true;
0fa9e53d
JJ
47static bool arg_whitelist = false;
48static Hashmap *arg_disks = NULL;
49static char *arg_default_options = NULL;
50static char *arg_default_keyfile = NULL;
141a79f4 51
e23a0ce8
LP
52static int create_disk(
53 const char *name,
54 const char *device,
55 const char *password,
56 const char *options) {
57
8eea8687
ZJS
58 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *to = NULL, *e = NULL,
59 *filtered = NULL;
7fd1b19b 60 _cleanup_fclose_ FILE *f = NULL;
8c11d3c1 61 bool noauto, nofail, tmp, swap;
744198e9
LP
62 char *from;
63 int r;
e23a0ce8
LP
64
65 assert(name);
66 assert(device);
67
b9f111b9
ZJS
68 noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
69 nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
a6dba978
ZJS
70 tmp = fstab_test_option(options, "tmp\0");
71 swap = fstab_test_option(options, "swap\0");
8c11d3c1
TG
72
73 if (tmp && swap) {
74 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
75 return -EINVAL;
76 }
155da457 77
744198e9
LP
78 e = unit_name_escape(name);
79 if (!e)
80 return log_oom();
81
7410616c
LP
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");
e23a0ce8 85
b7def684 86 p = strjoin(arg_dest, "/", n, NULL);
24a988e9
HH
87 if (!p)
88 return log_oom();
e23a0ce8 89
f7f21d33 90 u = fstab_node_to_udev_node(device);
24a988e9
HH
91 if (!u)
92 return log_oom();
e23a0ce8 93
7410616c
LP
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");
e23a0ce8 97
f7f21d33 98 f = fopen(p, "wxe");
4a62c710
MS
99 if (!f)
100 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
e23a0ce8 101
9ece938a 102 fputs(
6b1dc2bd 103 "# Automatically generated by systemd-cryptsetup-generator\n\n"
e23a0ce8 104 "[Unit]\n"
9ece938a 105 "Description=Cryptography Setup for %I\n"
c3834f9b 106 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
1b64d026 107 "SourcePath=/etc/crypttab\n"
e23a0ce8 108 "DefaultDependencies=no\n"
744198e9 109 "Conflicts=umount.target\n"
9ece938a 110 "BindsTo=dev-mapper-%i.device\n"
4469ff4a 111 "IgnoreOnIsolate=true\n"
d6bc8348 112 "After=cryptsetup-pre.target\n",
9ece938a 113 f);
e23a0ce8 114
155da457
LP
115 if (!nofail)
116 fprintf(f,
117 "Before=cryptsetup.target\n");
118
ceca9501 119 if (password) {
744198e9 120 if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
a0f70805 121 fputs("After=systemd-random-seed.service\n", f);
66a5dbdf 122 else if (!streq(password, "-") && !streq(password, "none")) {
744198e9
LP
123 _cleanup_free_ char *uu;
124
125 uu = fstab_node_to_udev_node(password);
126 if (!uu)
66a5dbdf
DR
127 return log_oom();
128
bde29068 129 if (!path_equal(uu, "/dev/null")) {
744198e9 130
bde29068 131 if (is_device_path(uu)) {
7410616c 132 _cleanup_free_ char *dd = NULL;
66a5dbdf 133
7410616c
LP
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");
bde29068
LP
137
138 fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
139 } else
140 fprintf(f, "RequiresMountsFor=%s\n", password);
141 }
66a5dbdf 142 }
ceca9501 143 }
e23a0ce8 144
9ece938a
TW
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
8eea8687
ZJS
156 r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
157 if (r < 0)
158 return r;
159
e23a0ce8
LP
160 fprintf(f,
161 "\n[Service]\n"
162 "Type=oneshot\n"
163 "RemainAfterExit=yes\n"
90724929 164 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
260ab287 165 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
7f4e0805 166 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
8eea8687 167 name, u, strempty(password), strempty(filtered),
e23a0ce8
LP
168 name);
169
8c11d3c1 170 if (tmp)
e23a0ce8 171 fprintf(f,
50109038 172 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
1d3399e6 173 name);
e23a0ce8 174
8c11d3c1 175 if (swap)
e23a0ce8 176 fprintf(f,
50109038 177 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
1d3399e6 178 name);
e23a0ce8 179
4652c56c
ZJS
180 r = fflush_and_check(f);
181 if (r < 0)
182 return log_error_errno(r, "Failed to write file %s: %m", p);
e23a0ce8 183
63c372cb 184 from = strjoina("../", n);
74715b82 185
155da457 186 if (!noauto) {
e23a0ce8 187
b7def684 188 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
24a988e9
HH
189 if (!to)
190 return log_oom();
e23a0ce8 191
d2e54fae 192 mkdir_parents_label(to, 0755);
4a62c710
MS
193 if (symlink(from, to) < 0)
194 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
2f8cd170
LP
195
196 free(to);
155da457 197 if (!nofail)
b7def684 198 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
155da457 199 else
b7def684 200 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
24a988e9
HH
201 if (!to)
202 return log_oom();
2f8cd170 203
d2e54fae 204 mkdir_parents_label(to, 0755);
4a62c710
MS
205 if (symlink(from, to) < 0)
206 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
f7f21d33 207 }
74715b82 208
608d41f3 209 free(to);
b7def684 210 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
24a988e9
HH
211 if (!to)
212 return log_oom();
74715b82 213
d2e54fae 214 mkdir_parents_label(to, 0755);
4a62c710
MS
215 if (symlink(from, to) < 0)
216 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
74715b82 217
68395007 218 if (!noauto && !nofail) {
a6fb0dc1
HG
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",
8eea8687
ZJS
225 "# Automatically generated by systemd-cryptsetup-generator \n\n"
226 "[Unit]\nJobTimeoutSec=0");
23bbb0de
MS
227 if (r < 0)
228 return log_error_errno(r, "Failed to write device drop-in: %m");
68395007
HH
229 }
230
24a988e9 231 return 0;
e23a0ce8
LP
232}
233
0fa9e53d
JJ
234static void free_arg_disks(void) {
235 crypto_device *d;
236
237 while ((d = hashmap_steal_first(arg_disks))) {
238 free(d->uuid);
6cd5b12a 239 free(d->keyfile);
baade8cc 240 free(d->name);
0fa9e53d
JJ
241 free(d->options);
242 free(d);
243 }
244
245 hashmap_free(arg_disks);
246}
247
248static 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;
baade8cc 261 d->keyfile = d->options = d->name = NULL;
0fa9e53d
JJ
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
059cb385 280static int parse_proc_cmdline_item(const char *key, const char *value) {
74df0fca 281 int r;
0fa9e53d
JJ
282 crypto_device *d;
283 _cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
66a78c2b 284
059cb385
LP
285 if (STR_IN_SET(key, "luks", "rd.luks") && value) {
286
287 r = parse_boolean(value);
141a79f4 288 if (r < 0)
059cb385 289 log_warning("Failed to parse luks switch %s. Ignoring.", value);
141a79f4
ZJS
290 else
291 arg_enabled = r;
66a78c2b 292
059cb385 293 } else if (STR_IN_SET(key, "luks.crypttab", "rd.luks.crypttab") && value) {
66a78c2b 294
059cb385 295 r = parse_boolean(value);
141a79f4 296 if (r < 0)
059cb385 297 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value);
141a79f4
ZJS
298 else
299 arg_read_crypttab = r;
66a78c2b 300
059cb385 301 } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) {
66a78c2b 302
0fa9e53d
JJ
303 d = get_crypto_device(startswith(value, "luks-") ? value+5 : value);
304 if (!d)
141a79f4 305 return log_oom();
66a78c2b 306
0fa9e53d
JJ
307 d->create = arg_whitelist = true;
308
059cb385 309 } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) {
66a78c2b 310
0fa9e53d
JJ
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)
141a79f4 321 return log_oom();
66a78c2b 322
059cb385 323 } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) {
66a78c2b 324
6cd5b12a
JJ
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;
c802a730 334 } else if (free_and_strdup(&arg_default_keyfile, value) < 0)
141a79f4 335 return log_oom();
7ab064a6 336
baade8cc
JJ
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
85013844 353 }
66a78c2b 354
24a988e9 355 return 0;
66a78c2b
LP
356}
357
0fa9e53d
JJ
358static int add_crypttab_devices(void) {
359 struct stat st;
360 unsigned crypttab_line = 0;
7fd1b19b 361 _cleanup_fclose_ FILE *f = NULL;
e23a0ce8 362
0fa9e53d
JJ
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;
e23a0ce8
LP
371 }
372
0fa9e53d
JJ
373 if (fstat(fileno(f), &st) < 0) {
374 log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
375 return 0;
376 }
e23a0ce8 377
0fa9e53d
JJ
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;
4c12626c 383
0fa9e53d
JJ
384 if (!fgets(line, sizeof(line), f))
385 break;
66a78c2b 386
0fa9e53d 387 crypttab_line++;
141a79f4 388
0fa9e53d
JJ
389 l = strstrip(line);
390 if (*l == '#' || *l == 0)
391 continue;
66a78c2b 392
0fa9e53d
JJ
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 }
66a78c2b 398
0fa9e53d
JJ
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);
8973790e 406
0fa9e53d
JJ
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;
8973790e
LP
410 }
411
0fa9e53d
JJ
412 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
413 if (r < 0)
414 return r;
8973790e 415
0fa9e53d
JJ
416 if (d)
417 d->create = false;
418 }
8973790e 419
0fa9e53d
JJ
420 return 0;
421}
66a78c2b 422
0fa9e53d
JJ
423static int add_proc_cmdline_devices(void) {
424 int r;
425 Iterator i;
426 crypto_device *d;
66a78c2b 427
0fa9e53d
JJ
428 HASHMAP_FOREACH(d, arg_disks, i) {
429 const char *options;
baade8cc 430 _cleanup_free_ char *device = NULL;
66a78c2b 431
0fa9e53d
JJ
432 if (!d->create)
433 continue;
e23a0ce8 434
baade8cc
JJ
435 if (!d->name) {
436 d->name = strappend("luks-", d->uuid);
437 if (!d->name)
438 return log_oom();
439 }
e23a0ce8 440
0fa9e53d
JJ
441 device = strappend("UUID=", d->uuid);
442 if (!device)
443 return log_oom();
7ab064a6 444
0fa9e53d
JJ
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";
141a79f4 451
baade8cc 452 r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
0fa9e53d
JJ
453 if (r < 0)
454 return r;
e23a0ce8
LP
455 }
456
0fa9e53d
JJ
457 return 0;
458}
e23a0ce8 459
0fa9e53d
JJ
460int main(int argc, char *argv[]) {
461 int r = EXIT_FAILURE;
e23a0ce8 462
0fa9e53d
JJ
463 if (argc > 1 && argc != 4) {
464 log_error("This program takes three or no arguments.");
465 return EXIT_FAILURE;
466 }
e23a0ce8 467
0fa9e53d
JJ
468 if (argc > 1)
469 arg_dest = argv[1];
e23a0ce8 470
0fa9e53d
JJ
471 log_set_target(LOG_TARGET_SAFE);
472 log_parse_environment();
473 log_open();
e2cb60fa 474
0fa9e53d 475 umask(0022);
e23a0ce8 476
0fa9e53d
JJ
477 arg_disks = hashmap_new(&string_hash_ops);
478 if (!arg_disks)
479 goto cleanup;
7ab064a6 480
0fa9e53d
JJ
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 }
7ab064a6 486
0fa9e53d
JJ
487 if (!arg_enabled) {
488 r = EXIT_SUCCESS;
489 goto cleanup;
e23a0ce8
LP
490 }
491
0fa9e53d
JJ
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;
141a79f4
ZJS
499
500cleanup:
0fa9e53d
JJ
501 free_arg_disks();
502 free(arg_default_options);
503 free(arg_default_keyfile);
141a79f4 504
0fa9e53d 505 return r;
e23a0ce8 506}