chiark / gitweb /
bus: fix typo in systemd-bus-proxyd
[elogind.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 "strv.h"
31 #include "fileio.h"
32
33 static const char *arg_dest = "/tmp";
34 static bool arg_enabled = true;
35 static bool arg_read_crypttab = true;
36
37 static bool has_option(const char *haystack, const char *needle) {
38         const char *f = haystack;
39         size_t l;
40
41         assert(needle);
42
43         if (!haystack)
44                 return false;
45
46         l = strlen(needle);
47
48         while ((f = strstr(f, needle))) {
49
50                 if (f > haystack && f[-1] != ',') {
51                         f++;
52                         continue;
53                 }
54
55                 if (f[l] != 0 && f[l] != ',') {
56                         f++;
57                         continue;
58                 }
59
60                 return true;
61         }
62
63         return false;
64 }
65
66 static int create_disk(
67                 const char *name,
68                 const char *device,
69                 const char *password,
70                 const char *options) {
71
72         _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
73         _cleanup_fclose_ FILE *f = NULL;
74         bool noauto, nofail, tmp, swap;
75
76         assert(name);
77         assert(device);
78
79         noauto = has_option(options, "noauto");
80         nofail = has_option(options, "nofail");
81         tmp = has_option(options, "tmp");
82         swap = has_option(options, "swap");
83
84         if (tmp && swap) {
85                 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
86                 return -EINVAL;
87         }
88
89         n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
90         if (!n)
91                 return log_oom();
92
93         p = strjoin(arg_dest, "/", n, NULL);
94         if (!p)
95                 return log_oom();
96
97         u = fstab_node_to_udev_node(device);
98         if (!u)
99                 return log_oom();
100
101         d = unit_name_from_path(u, ".device");
102         if (!d)
103                 return log_oom();
104
105         f = fopen(p, "wxe");
106         if (!f) {
107                 log_error("Failed to create unit file %s: %m", p);
108                 return -errno;
109         }
110
111         fputs(
112                 "# Automatically generated by systemd-cryptsetup-generator\n\n"
113                 "[Unit]\n"
114                 "Description=Cryptography Setup for %I\n"
115                 "Documentation=man:systemd-cryptsetup@.service(8) man:crypttab(5)\n"
116                 "SourcePath=/etc/crypttab\n"
117                 "Conflicts=umount.target\n"
118                 "DefaultDependencies=no\n"
119                 "BindsTo=dev-mapper-%i.device\n"
120                 "IgnoreOnIsolate=true\n"
121                 "After=systemd-readahead-collect.service systemd-readahead-replay.service\n",
122                 f);
123
124         if (!nofail)
125                 fprintf(f,
126                         "Before=cryptsetup.target\n");
127
128         if (password) {
129                 if (streq(password, "/dev/urandom") ||
130                     streq(password, "/dev/random") ||
131                     streq(password, "/dev/hw_random"))
132                         fputs("After=systemd-random-seed.service\n", f);
133                 else if (!streq(password, "-") &&
134                          !streq(password, "none"))
135                         fprintf(f,
136                                 "RequiresMountsFor=%s\n",
137                                 password);
138         }
139
140         if (is_device_path(u))
141                 fprintf(f,
142                         "BindsTo=%s\n"
143                         "After=%s\n"
144                         "Before=umount.target\n",
145                         d, d);
146         else
147                 fprintf(f,
148                         "RequiresMountsFor=%s\n",
149                         u);
150
151         fprintf(f,
152                 "\n[Service]\n"
153                 "Type=oneshot\n"
154                 "RemainAfterExit=yes\n"
155                 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
156                 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
157                 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
158                 name, u, strempty(password), strempty(options),
159                 name);
160
161         if (tmp)
162                 fprintf(f,
163                         "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
164                         name);
165
166         if (swap)
167                 fprintf(f,
168                         "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
169                         name);
170
171         fflush(f);
172
173         if (ferror(f)) {
174                 log_error("Failed to write file %s: %m", p);
175                 return -errno;
176         }
177
178         if (asprintf(&from, "../%s", n) < 0)
179                 return log_oom();
180
181         if (!noauto) {
182
183                 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
184                 if (!to)
185                         return log_oom();
186
187                 mkdir_parents_label(to, 0755);
188                 if (symlink(from, to) < 0) {
189                         log_error("Failed to create symlink %s: %m", to);
190                         return -errno;
191                 }
192
193                 free(to);
194                 if (!nofail)
195                         to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
196                 else
197                         to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
198                 if (!to)
199                         return log_oom();
200
201                 mkdir_parents_label(to, 0755);
202                 if (symlink(from, to) < 0) {
203                         log_error("Failed to create symlink %s: %m", to);
204                         return -errno;
205                 }
206         }
207
208         e = unit_name_escape(name);
209         if (!e)
210                 return log_oom();
211
212         free(to);
213         to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
214         if (!to)
215                 return log_oom();
216
217         mkdir_parents_label(to, 0755);
218         if (symlink(from, to) < 0) {
219                 log_error("Failed to create symlink %s: %m", to);
220                 return -errno;
221         }
222
223         if (!noauto && !nofail) {
224                 int r;
225                 free(p);
226                 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
227                 if (!p)
228                         return log_oom();
229
230                 mkdir_parents_label(p, 0755);
231
232                 r = write_string_file(p,
233                                 "# Automatically generated by systemd-cryptsetup-generator\n\n"
234                                 "[Unit]\n"
235                                 "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
236                 if (r)
237                         return r;
238         }
239
240         return 0;
241 }
242
243 static int parse_proc_cmdline(
244                 char ***arg_proc_cmdline_disks,
245                 char ***arg_proc_cmdline_options,
246                 char **arg_proc_cmdline_keyfile) {
247
248         _cleanup_free_ char *line = NULL;
249         char *w = NULL, *state = NULL;
250         size_t l;
251         int r;
252
253         r = proc_cmdline(&line);
254         if (r < 0)
255                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
256         if (r <= 0)
257                 return 0;
258
259         FOREACH_WORD_QUOTED(w, l, line, state) {
260                 _cleanup_free_ char *word = NULL;
261
262                 word = strndup(w, l);
263                 if (!word)
264                         return log_oom();
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                         if (strv_extend(arg_proc_cmdline_disks, word + 10) < 0)
302                                 return log_oom();
303
304                 } else if (startswith(word, "rd.luks.uuid=")) {
305
306                         if (in_initrd()) {
307                                 if (strv_extend(arg_proc_cmdline_disks, word + 13) < 0)
308                                         return log_oom();
309                         }
310
311                 } else if (startswith(word, "luks.options=")) {
312                         if (strv_extend(arg_proc_cmdline_options, word + 13) < 0)
313                                 return log_oom();
314
315                 } else if (startswith(word, "rd.luks.options=")) {
316
317                         if (in_initrd()) {
318                                 if (strv_extend(arg_proc_cmdline_options, word + 16) < 0)
319                                         return log_oom();
320                         }
321
322                 } else if (startswith(word, "luks.key=")) {
323                         if (*arg_proc_cmdline_keyfile)
324                                 free(*arg_proc_cmdline_keyfile);
325                         *arg_proc_cmdline_keyfile = strdup(word + 9);
326                         if (!*arg_proc_cmdline_keyfile)
327                                 return log_oom();
328
329                 } else if (startswith(word, "rd.luks.key=")) {
330
331                         if (in_initrd()) {
332                                 if (*arg_proc_cmdline_keyfile)
333                                         free(*arg_proc_cmdline_keyfile);
334                                 *arg_proc_cmdline_keyfile = strdup(word + 12);
335                                 if (!*arg_proc_cmdline_keyfile)
336                                         return log_oom();
337                         }
338
339                 } else if (startswith(word, "luks.") ||
340                            (in_initrd() && startswith(word, "rd.luks."))) {
341
342                         log_warning("Unknown kernel switch %s. Ignoring.", word);
343                 }
344         }
345
346         strv_uniq(*arg_proc_cmdline_disks);
347
348         return 0;
349 }
350
351 int main(int argc, char *argv[]) {
352         _cleanup_strv_free_ char **arg_proc_cmdline_disks_done = NULL;
353         _cleanup_strv_free_ char **arg_proc_cmdline_disks = NULL;
354         _cleanup_strv_free_ char **arg_proc_cmdline_options = NULL;
355         _cleanup_free_ char *arg_proc_cmdline_keyfile = NULL;
356         _cleanup_fclose_ FILE *f = NULL;
357         unsigned n = 0;
358         int r = EXIT_SUCCESS;
359         char **i;
360
361         if (argc > 1 && argc != 4) {
362                 log_error("This program takes three or no arguments.");
363                 return EXIT_FAILURE;
364         }
365
366         if (argc > 1)
367                 arg_dest = argv[1];
368
369         log_set_target(LOG_TARGET_SAFE);
370         log_parse_environment();
371         log_open();
372
373         umask(0022);
374
375         if (parse_proc_cmdline(&arg_proc_cmdline_disks, &arg_proc_cmdline_options, &arg_proc_cmdline_keyfile) < 0)
376                 return EXIT_FAILURE;
377
378         if (!arg_enabled)
379                 return EXIT_SUCCESS;
380
381         if (arg_read_crypttab) {
382                 struct stat st;
383
384                 f = fopen("/etc/crypttab", "re");
385                 if (!f) {
386                         if (errno == ENOENT)
387                                 r = EXIT_SUCCESS;
388                         else {
389                                 r = EXIT_FAILURE;
390                                 log_error("Failed to open /etc/crypttab: %m");
391                         }
392
393                         goto next;
394                 }
395
396                 if (fstat(fileno(f), &st) < 0) {
397                         log_error("Failed to stat /etc/crypttab: %m");
398                         r = EXIT_FAILURE;
399                         goto next;
400                 }
401
402                 /* If we readd support for specifying passphrases
403                  * directly in crypttabe we should upgrade the warning
404                  * below, though possibly only if a passphrase is
405                  * specified directly. */
406                 if (st.st_mode & 0005)
407                         log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
408
409                 for (;;) {
410                         char line[LINE_MAX], *l;
411                         _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
412                         int k;
413
414                         if (!fgets(line, sizeof(line), f))
415                                 break;
416
417                         n++;
418
419                         l = strstrip(line);
420                         if (*l == '#' || *l == 0)
421                                 continue;
422
423                         k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
424                         if (k < 2 || k > 4) {
425                                 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
426                                 r = EXIT_FAILURE;
427                                 continue;
428                         }
429
430                         if (arg_proc_cmdline_options) {
431                                 /*
432                                   If options are specified on the kernel commandline, let them override
433                                   the ones from crypttab.
434                                 */
435                                 STRV_FOREACH(i, arg_proc_cmdline_options) {
436                                         _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
437                                         const char *p = *i;
438
439                                         k = sscanf(p, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
440                                         if (k == 2 && streq(proc_uuid, device + 5)) {
441                                                 if (options)
442                                                         free(options);
443                                                 options = strdup(p);
444                                                 if (!proc_options)
445                                                         return log_oom();
446                                         }
447                                 }
448                         }
449
450                         if (arg_proc_cmdline_disks) {
451                                 /*
452                                   If luks UUIDs are specified on the kernel command line, use them as a filter
453                                   for /etc/crypttab and only generate units for those.
454                                 */
455                                 STRV_FOREACH(i, arg_proc_cmdline_disks) {
456                                         _cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
457                                         const char *p = *i;
458
459                                         if (startswith(p, "luks-"))
460                                                 p += 5;
461
462                                         proc_name = strappend("luks-", p);
463                                         proc_device = strappend("UUID=", p);
464
465                                         if (!proc_name || !proc_device)
466                                                 return log_oom();
467
468                                         if (streq(proc_device, device) || streq(proc_name, name)) {
469                                                 if (create_disk(name, device, password, options) < 0)
470                                                         r = EXIT_FAILURE;
471
472                                                 if (strv_extend(&arg_proc_cmdline_disks_done, p) < 0)
473                                                         return log_oom();
474                                         }
475                                 }
476                         } else {
477                                 if (create_disk(name, device, password, options) < 0)
478                                         r = EXIT_FAILURE;
479                         }
480                 }
481         }
482
483 next:
484         STRV_FOREACH(i, arg_proc_cmdline_disks) {
485                 /*
486                   Generate units for those UUIDs, which were specified
487                   on the kernel command line and not yet written.
488                 */
489
490                 _cleanup_free_ char *name = NULL, *device = NULL, *options = NULL;
491                 const char *p = *i;
492
493                 if (startswith(p, "luks-"))
494                         p += 5;
495
496                 if (strv_contains(arg_proc_cmdline_disks_done, p))
497                         continue;
498
499                 name = strappend("luks-", p);
500                 device = strappend("UUID=", p);
501
502                 if (!name || !device)
503                         return log_oom();
504
505                 if (arg_proc_cmdline_options) {
506                         /*
507                           If options are specified on the kernel commandline, use them.
508                         */
509                         char **j;
510
511                         STRV_FOREACH(j, arg_proc_cmdline_options) {
512                                 _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
513                                 const char *s = *j;
514                                 int k;
515
516                                 k = sscanf(s, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
517                                 if (k == 2) {
518                                         if (streq(proc_uuid, device + 5)) {
519                                                 if (options)
520                                                         free(options);
521                                                 options = strdup(proc_options);
522                                                 if (!options)
523                                                         return log_oom();
524                                         }
525                                 } else if (!options) {
526                                         /*
527                                           Fall back to options without a specified UUID
528                                         */
529                                         options = strdup(s);
530                                         if (!options)
531                                                 return log_oom();
532                                 }
533                         }
534                 }
535
536                 if (!options) {
537                         options = strdup("timeout=0");
538                         if (!options)
539                                 return log_oom();
540                 }
541
542                 if (create_disk(name, device, arg_proc_cmdline_keyfile, options) < 0)
543                         r = EXIT_FAILURE;
544         }
545
546         return r;
547 }