]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-generator.c
cryptsetup-generator: add a missing OOM check
[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 #include "fileio.h"
33
34 static const char *arg_dest = "/tmp";
35 static bool arg_enabled = true;
36 static bool arg_read_crypttab = true;
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 _cleanup_free_ *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
74 FILE _cleanup_fclose_ *f = NULL;
75 bool noauto, nofail;
76
77 assert(name);
78 assert(device);
79
80 noauto = has_option(options, "noauto");
81 nofail = has_option(options, "nofail");
82
83 n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
84 if (!n)
85 return log_oom();
86
87 p = strjoin(arg_dest, "/", n, NULL);
88 if (!p)
89 return log_oom();
90
91 u = fstab_node_to_udev_node(device);
92 if (!u)
93 return log_oom();
94
95 d = unit_name_from_path(u, ".device");
96 if (!d)
97 return log_oom();
98
99 f = fopen(p, "wxe");
100 if (!f) {
101 log_error("Failed to create unit file %s: %m", p);
102 return -errno;
103 }
104
105 fprintf(f,
106 "# Automatically generated by systemd-cryptsetup-generator\n\n"
107 "[Unit]\n"
108 "Description=Cryptography Setup for %%I\n"
109 "Documentation=man:systemd-cryptsetup@.service(8) man:crypttab(5)\n"
110 "SourcePath=/etc/crypttab\n"
111 "Conflicts=umount.target\n"
112 "DefaultDependencies=no\n"
113 "BindsTo=%s dev-mapper-%%i.device\n"
114 "After=systemd-readahead-collect.service systemd-readahead-replay.service %s\n"
115 "Before=umount.target\n",
116 d, d);
117
118 if (!nofail)
119 fprintf(f,
120 "Before=cryptsetup.target\n");
121
122 if (password && (streq(password, "/dev/urandom") ||
123 streq(password, "/dev/random") ||
124 streq(password, "/dev/hw_random")))
125 fputs("After=systemd-random-seed-load.service\n", f);
126 else
127 fputs("Before=local-fs.target\n", f);
128
129 fprintf(f,
130 "\n[Service]\n"
131 "Type=oneshot\n"
132 "RemainAfterExit=yes\n"
133 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
134 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
135 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
136 name, u, strempty(password), strempty(options),
137 name);
138
139 if (has_option(options, "tmp"))
140 fprintf(f,
141 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
142 name);
143
144 if (has_option(options, "swap"))
145 fprintf(f,
146 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
147 name);
148
149 fflush(f);
150
151 if (ferror(f)) {
152 log_error("Failed to write file %s: %m", p);
153 return -errno;
154 }
155
156 if (asprintf(&from, "../%s", n) < 0)
157 return log_oom();
158
159 if (!noauto) {
160
161 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
162 if (!to)
163 return log_oom();
164
165 mkdir_parents_label(to, 0755);
166 if (symlink(from, to) < 0) {
167 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
168 return -errno;
169 }
170
171 free(to);
172 if (!nofail)
173 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
174 else
175 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
176 if (!to)
177 return log_oom();
178
179 mkdir_parents_label(to, 0755);
180 if (symlink(from, to) < 0) {
181 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
182 return -errno;
183 }
184 }
185
186 e = unit_name_escape(name);
187 if (!e)
188 return log_oom();
189
190 free(to);
191 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
192 if (!to)
193 return log_oom();
194
195 mkdir_parents_label(to, 0755);
196 if (symlink(from, to) < 0) {
197 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
198 return -errno;
199 }
200
201 return 0;
202 }
203
204 static int parse_proc_cmdline(char ***arg_proc_cmdline_disks) {
205 char _cleanup_free_ *line = NULL;
206 char *w = NULL, *state = NULL;
207 int r;
208 size_t l;
209
210 if (detect_container(NULL) > 0)
211 return 0;
212
213 r = read_one_line_file("/proc/cmdline", &line);
214 if (r < 0) {
215 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
216 return 0;
217 }
218
219 FOREACH_WORD_QUOTED(w, l, line, state) {
220 char _cleanup_free_ *word = NULL;
221
222 word = strndup(w, l);
223 if (!word)
224 return log_oom();
225
226 if (startswith(word, "luks=")) {
227 r = parse_boolean(word + 5);
228 if (r < 0)
229 log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
230 else
231 arg_enabled = r;
232
233 } else if (startswith(word, "rd.luks=")) {
234
235 if (in_initrd()) {
236 r = parse_boolean(word + 8);
237 if (r < 0)
238 log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
239 else
240 arg_enabled = r;
241 }
242
243 } else if (startswith(word, "luks.crypttab=")) {
244 r = parse_boolean(word + 14);
245 if (r < 0)
246 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
247 else
248 arg_read_crypttab = r;
249
250 } else if (startswith(word, "rd.luks.crypttab=")) {
251
252 if (in_initrd()) {
253 r = parse_boolean(word + 17);
254 if (r < 0)
255 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
256 else
257 arg_read_crypttab = r;
258 }
259
260 } else if (startswith(word, "luks.uuid=")) {
261 char **t;
262
263 t = strv_append(*arg_proc_cmdline_disks, word + 10);
264 if (!t)
265 return log_oom();
266
267 strv_free(*arg_proc_cmdline_disks);
268 *arg_proc_cmdline_disks = t;
269
270 } else if (startswith(word, "rd.luks.uuid=")) {
271
272 if (in_initrd()) {
273 char **t;
274
275 t = strv_append(*arg_proc_cmdline_disks, word + 13);
276 if (!t)
277 return log_oom();
278
279 strv_free(*arg_proc_cmdline_disks);
280 *arg_proc_cmdline_disks = t;
281 }
282
283 } else if (startswith(word, "luks.") ||
284 (in_initrd() && startswith(word, "rd.luks."))) {
285
286 log_warning("Unknown kernel switch %s. Ignoring.", word);
287 }
288 }
289
290 strv_uniq(*arg_proc_cmdline_disks);
291
292 return 0;
293 }
294
295 int main(int argc, char *argv[]) {
296 FILE _cleanup_fclose_ *f = NULL;
297 unsigned n = 0;
298 int r = EXIT_SUCCESS;
299 char **i;
300 char _cleanup_strv_free_ **arg_proc_cmdline_disks_done = NULL;
301 char _cleanup_strv_free_ **arg_proc_cmdline_disks = NULL;
302
303 if (argc > 1 && argc != 4) {
304 log_error("This program takes three or no arguments.");
305 return EXIT_FAILURE;
306 }
307
308 if (argc > 1)
309 arg_dest = argv[1];
310
311 log_set_target(LOG_TARGET_SAFE);
312 log_parse_environment();
313 log_open();
314
315 umask(0022);
316
317 if (parse_proc_cmdline(&arg_proc_cmdline_disks) < 0)
318 return EXIT_FAILURE;
319
320 if (!arg_enabled)
321 return EXIT_SUCCESS;
322
323 if (arg_read_crypttab) {
324 f = fopen("/etc/crypttab", "re");
325
326 if (!f) {
327 if (errno == ENOENT)
328 r = EXIT_SUCCESS;
329 else {
330 r = EXIT_FAILURE;
331 log_error("Failed to open /etc/crypttab: %m");
332 }
333 } else for (;;) {
334 char line[LINE_MAX], *l;
335 char _cleanup_free_ *name = NULL, *device = NULL, *password = NULL, *options = NULL;
336 int k;
337
338 if (!fgets(line, sizeof(line), f))
339 break;
340
341 n++;
342
343 l = strstrip(line);
344 if (*l == '#' || *l == 0)
345 continue;
346
347 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
348 if (k < 2 || k > 4) {
349 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
350 r = EXIT_FAILURE;
351 continue;
352 }
353
354 if (arg_proc_cmdline_disks) {
355 /*
356 If luks UUIDs are specified on the kernel command line, use them as a filter
357 for /etc/crypttab and only generate units for those.
358 */
359 STRV_FOREACH(i, arg_proc_cmdline_disks) {
360 char _cleanup_free_ *proc_device = NULL, *proc_name = NULL;
361 const char *p = *i;
362
363 if (startswith(p, "luks-"))
364 p += 5;
365
366 proc_name = strappend("luks-", p);
367 proc_device = strappend("UUID=", p);
368
369 if (!proc_name || !proc_device)
370 return log_oom();
371
372 if (streq(proc_device, device) || streq(proc_name, name)) {
373 char **t;
374
375 if (create_disk(name, device, password, options) < 0)
376 r = EXIT_FAILURE;
377
378 t = strv_append(arg_proc_cmdline_disks_done, p);
379 if (!t)
380 return log_oom();
381
382 strv_free(arg_proc_cmdline_disks_done);
383 arg_proc_cmdline_disks_done = t;
384 }
385 }
386 } else {
387 if (create_disk(name, device, password, options) < 0)
388 r = EXIT_FAILURE;
389 }
390 }
391 }
392
393 STRV_FOREACH(i, arg_proc_cmdline_disks) {
394 /*
395 Generate units for those UUIDs, which were specified
396 on the kernel command line and not yet written.
397 */
398
399 char _cleanup_free_ *name = NULL, *device = NULL;
400 const char *p = *i;
401
402 if (startswith(p, "luks-"))
403 p += 5;
404
405 if (strv_contains(arg_proc_cmdline_disks_done, p))
406 continue;
407
408 name = strappend("luks-", p);
409 device = strappend("UUID=", p);
410
411 if (!name || !device)
412 return log_oom();
413
414 if (create_disk(name, device, NULL, "timeout=0") < 0)
415 r = EXIT_FAILURE;
416 }
417
418 return r;
419 }