]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-generator.c
cryptsetup: fix escaping when generating cryptsetup units
[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 <string.h>
23 #include <errno.h>
24 #include <unistd.h>
25
26 #include "log.h"
27 #include "util.h"
28 #include "unit-name.h"
29 #include "mkdir.h"
30 #include "virt.h"
31 #include "strv.h"
32
33 static const char *arg_dest = "/tmp";
34 static bool arg_enabled = true;
35 static bool arg_read_crypttab = true;
36 static char **arg_proc_cmdline_disks = NULL;
37
38 static bool has_option(const char *haystack, const char *needle) {
39 const char *f = haystack;
40 size_t l;
41
42 assert(needle);
43
44 if (!haystack)
45 return false;
46
47 l = strlen(needle);
48
49 while ((f = strstr(f, needle))) {
50
51 if (f > haystack && f[-1] != ',') {
52 f++;
53 continue;
54 }
55
56 if (f[l] != 0 && f[l] != ',') {
57 f++;
58 continue;
59 }
60
61 return true;
62 }
63
64 return false;
65 }
66
67 static int create_disk(
68 const char *name,
69 const char *device,
70 const char *password,
71 const char *options) {
72
73 char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
74 int r;
75 FILE *f = NULL;
76 bool noauto, nofail;
77
78 assert(name);
79 assert(device);
80
81 noauto = has_option(options, "noauto");
82 nofail = has_option(options, "nofail");
83
84 n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
85 if (!n) {
86 r = -ENOMEM;
87 log_error("Failed to allocate unit name.");
88 goto fail;
89 }
90
91 p = join(arg_dest, "/", n, NULL);
92 if (!p) {
93 r = -ENOMEM;
94 log_error("Failed to allocate unit file name.");
95 goto fail;
96 }
97
98 u = fstab_node_to_udev_node(device);
99 if (!u) {
100 r = -ENOMEM;
101 log_error("Failed to allocate device node.");
102 goto fail;
103 }
104
105 d = unit_name_from_path(u, ".device");
106 if (!d) {
107 r = -ENOMEM;
108 log_error("Failed to allocate device name.");
109 goto fail;
110 }
111
112 f = fopen(p, "wxe");
113 if (!f) {
114 r = -errno;
115 log_error("Failed to create unit file: %m");
116 goto fail;
117 }
118
119 fprintf(f,
120 "# Automatically generated by systemd-cryptsetup-generator\n\n"
121 "[Unit]\n"
122 "Description=Cryptography Setup for %%I\n"
123 "SourcePath=/etc/crypttab\n"
124 "Conflicts=umount.target\n"
125 "DefaultDependencies=no\n"
126 "BindTo=%s dev-mapper-%%i.device\n"
127 "After=systemd-readahead-collect.service systemd-readahead-replay.service %s\n"
128 "Before=umount.target\n",
129 d, d);
130
131 if (!nofail)
132 fprintf(f,
133 "Before=cryptsetup.target\n");
134
135 if (password && (streq(password, "/dev/urandom") ||
136 streq(password, "/dev/random") ||
137 streq(password, "/dev/hw_random")))
138 fputs("After=systemd-random-seed-load.service\n", f);
139 else
140 fputs("Before=local-fs.target\n", f);
141
142 fprintf(f,
143 "\n[Service]\n"
144 "Type=oneshot\n"
145 "RemainAfterExit=yes\n"
146 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
147 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
148 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
149 name, u, strempty(password), strempty(options),
150 name);
151
152 if (has_option(options, "tmp"))
153 fprintf(f,
154 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
155 name);
156
157 if (has_option(options, "swap"))
158 fprintf(f,
159 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
160 name);
161
162 fflush(f);
163
164 if (ferror(f)) {
165 r = -errno;
166 log_error("Failed to write file: %m");
167 goto fail;
168 }
169
170 if (asprintf(&from, "../%s", n) < 0) {
171 r = -ENOMEM;
172 goto fail;
173 }
174
175 if (!noauto) {
176
177 to = join(arg_dest, "/", d, ".wants/", n, NULL);
178 if (!to) {
179 r = -ENOMEM;
180 goto fail;
181 }
182
183 mkdir_parents_label(to, 0755);
184 if (symlink(from, to) < 0) {
185 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
186 r = -errno;
187 goto fail;
188 }
189
190 free(to);
191
192 if (!nofail)
193 to = join(arg_dest, "/cryptsetup.target.requires/", n, NULL);
194 else
195 to = join(arg_dest, "/cryptsetup.target.wants/", n, NULL);
196 if (!to) {
197 r = -ENOMEM;
198 goto fail;
199 }
200
201 mkdir_parents_label(to, 0755);
202 if (symlink(from, to) < 0) {
203 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
204 r = -errno;
205 goto fail;
206 }
207
208 free(to);
209 to = NULL;
210 }
211
212 e = unit_name_escape(name);
213 to = join(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
214 if (!to) {
215 r = -ENOMEM;
216 goto fail;
217 }
218
219 mkdir_parents_label(to, 0755);
220 if (symlink(from, to) < 0) {
221 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
222 r = -errno;
223 goto fail;
224 }
225
226 r = 0;
227
228 fail:
229 free(p);
230 free(n);
231 free(d);
232 free(e);
233
234 free(from);
235 free(to);
236
237 if (f)
238 fclose(f);
239
240 return r;
241 }
242
243 static int parse_proc_cmdline(void) {
244 char *line, *w, *state;
245 int r;
246 size_t l;
247
248 if (detect_container(NULL) > 0)
249 return 0;
250
251 r = read_one_line_file("/proc/cmdline", &line);
252 if (r < 0) {
253 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
254 return 0;
255 }
256
257 FOREACH_WORD_QUOTED(w, l, line, state) {
258 char *word;
259
260 word = strndup(w, l);
261 if (!word) {
262 r = -ENOMEM;
263 goto finish;
264 }
265
266 if (startswith(word, "luks=")) {
267 r = parse_boolean(word + 5);
268 if (r < 0)
269 log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
270 else
271 arg_enabled = r;
272
273 } else if (startswith(word, "rd.luks=")) {
274
275 if (in_initrd()) {
276 r = parse_boolean(word + 8);
277 if (r < 0)
278 log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
279 else
280 arg_enabled = r;
281 }
282
283 } else if (startswith(word, "luks.crypttab=")) {
284 r = parse_boolean(word + 14);
285 if (r < 0)
286 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
287 else
288 arg_read_crypttab = r;
289
290 } else if (startswith(word, "rd.luks.crypttab=")) {
291
292 if (in_initrd()) {
293 r = parse_boolean(word + 17);
294 if (r < 0)
295 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
296 else
297 arg_read_crypttab = r;
298 }
299
300 } else if (startswith(word, "luks.uuid=")) {
301 char **t;
302
303 t = strv_append(arg_proc_cmdline_disks, word + 10);
304 if (!t) {
305 log_error("Out of memory");
306 r = -ENOMEM;
307 goto finish;
308 }
309 strv_free(arg_proc_cmdline_disks);
310 arg_proc_cmdline_disks = t;
311
312 } else if (startswith(word, "rd.luks.uuid=")) {
313
314 if (in_initrd()) {
315 char **t;
316
317 t = strv_append(arg_proc_cmdline_disks, word + 13);
318 if (!t) {
319 log_error("Out of memory");
320 r = -ENOMEM;
321 goto finish;
322 }
323 strv_free(arg_proc_cmdline_disks);
324 arg_proc_cmdline_disks = t;
325 }
326
327 } else if (startswith(word, "luks.") ||
328 (in_initrd() && startswith(word, "rd.luks."))) {
329
330 log_warning("Unknown kernel switch %s. Ignoring.", word);
331 }
332
333 free(word);
334 }
335
336 r = 0;
337
338 finish:
339 free(line);
340 return r;
341 }
342
343 int main(int argc, char *argv[]) {
344 FILE *f = NULL;
345 int r = EXIT_SUCCESS;
346 unsigned n = 0;
347 char **i;
348
349 if (argc > 1 && argc != 4) {
350 log_error("This program takes three or no arguments.");
351 return EXIT_FAILURE;
352 }
353
354 if (argc > 1)
355 arg_dest = argv[1];
356
357 log_set_target(LOG_TARGET_SAFE);
358 log_parse_environment();
359 log_open();
360
361 umask(0022);
362
363 if (parse_proc_cmdline() < 0)
364 return EXIT_FAILURE;
365
366 if (!arg_enabled) {
367 r = EXIT_SUCCESS;
368 goto finish;
369 }
370
371 STRV_FOREACH(i, arg_proc_cmdline_disks) {
372 char *name, *device;
373 const char *p = *i;
374
375 if (startswith(p, "luks-"))
376 p += 5;
377
378 name = strappend("luks-", *i);
379 device = strappend("UUID=", *i);
380
381 if (!name || !device) {
382 log_error("Out of memory");
383 r = EXIT_FAILURE;
384 free(name);
385 free(device);
386 goto finish;
387 }
388
389 if (create_disk(name, device, NULL, NULL) < 0)
390 r = EXIT_FAILURE;
391
392 free(name);
393 free(device);
394 }
395
396 if (!arg_read_crypttab)
397 return r;
398
399 f = fopen("/etc/crypttab", "re");
400 if (!f) {
401
402 if (errno == ENOENT)
403 r = EXIT_SUCCESS;
404 else {
405 r = EXIT_FAILURE;
406 log_error("Failed to open /etc/crypttab: %m");
407 }
408
409 goto finish;
410 }
411
412 for (;;) {
413 char line[LINE_MAX], *l;
414 char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
415 int k;
416
417 if (!fgets(line, sizeof(line), f))
418 break;
419
420 n++;
421
422 l = strstrip(line);
423 if (*l == '#' || *l == 0)
424 continue;
425
426 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
427 if (k < 2 || k > 4) {
428 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
429 r = EXIT_FAILURE;
430 goto next;
431 }
432
433 if (create_disk(name, device, password, options) < 0)
434 r = EXIT_FAILURE;
435
436 next:
437 free(name);
438 free(device);
439 free(password);
440 free(options);
441 }
442
443 finish:
444 if (f)
445 fclose(f);
446
447 strv_free(arg_proc_cmdline_disks);
448
449 return r;
450 }