chiark / gitweb /
cryptsetup-generator: remove warning about crypttab access mode
[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 <errno.h>
23
24 #include "dropin.h"
25 #include "generator.h"
26 #include "hashmap.h"
27 #include "log.h"
28 #include "mkdir.h"
29 #include "path-util.h"
30 #include "fstab-util.h"
31 #include "strv.h"
32 #include "unit-name.h"
33 #include "util.h"
34
35 typedef struct crypto_device {
36         char *uuid;
37         char *keyfile;
38         char *name;
39         char *options;
40         bool create;
41 } crypto_device;
42
43 static const char *arg_dest = "/tmp";
44 static bool arg_enabled = true;
45 static bool arg_read_crypttab = true;
46 static bool arg_whitelist = false;
47 static Hashmap *arg_disks = NULL;
48 static char *arg_default_options = NULL;
49 static char *arg_default_keyfile = NULL;
50
51 static int create_disk(
52                 const char *name,
53                 const char *device,
54                 const char *password,
55                 const char *options) {
56
57         _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *to = NULL, *e = NULL,
58                 *filtered = NULL;
59         _cleanup_fclose_ FILE *f = NULL;
60         bool noauto, nofail, tmp, swap;
61         char *from;
62         int r;
63
64         assert(name);
65         assert(device);
66
67         noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
68         nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
69         tmp = fstab_test_option(options, "tmp\0");
70         swap = fstab_test_option(options, "swap\0");
71
72         if (tmp && swap) {
73                 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
74                 return -EINVAL;
75         }
76
77         e = unit_name_escape(name);
78         if (!e)
79                 return log_oom();
80
81         n = unit_name_build("systemd-cryptsetup", e, ".service");
82         if (!n)
83                 return log_oom();
84
85         p = strjoin(arg_dest, "/", n, NULL);
86         if (!p)
87                 return log_oom();
88
89         u = fstab_node_to_udev_node(device);
90         if (!u)
91                 return log_oom();
92
93         d = unit_name_from_path(u, ".device");
94         if (!d)
95                 return log_oom();
96
97         f = fopen(p, "wxe");
98         if (!f)
99                 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
100
101         fputs(
102                 "# Automatically generated by systemd-cryptsetup-generator\n\n"
103                 "[Unit]\n"
104                 "Description=Cryptography Setup for %I\n"
105                 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
106                 "SourcePath=/etc/crypttab\n"
107                 "DefaultDependencies=no\n"
108                 "Conflicts=umount.target\n"
109                 "BindsTo=dev-mapper-%i.device\n"
110                 "IgnoreOnIsolate=true\n"
111                 "After=cryptsetup-pre.target\n",
112                 f);
113
114         if (!nofail)
115                 fprintf(f,
116                         "Before=cryptsetup.target\n");
117
118         if (password) {
119                 if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
120                         fputs("After=systemd-random-seed.service\n", f);
121                 else if (!streq(password, "-") && !streq(password, "none")) {
122                         _cleanup_free_ char *uu;
123
124                         uu = fstab_node_to_udev_node(password);
125                         if (!uu)
126                                 return log_oom();
127
128                         if (!path_equal(uu, "/dev/null")) {
129
130                                 if (is_device_path(uu)) {
131                                         _cleanup_free_ char *dd;
132
133                                         dd = unit_name_from_path(uu, ".device");
134                                         if (!dd)
135                                                 return log_oom();
136
137                                         fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
138                                 } else
139                                         fprintf(f, "RequiresMountsFor=%s\n", password);
140                         }
141                 }
142         }
143
144         if (is_device_path(u))
145                 fprintf(f,
146                         "BindsTo=%s\n"
147                         "After=%s\n"
148                         "Before=umount.target\n",
149                         d, d);
150         else
151                 fprintf(f,
152                         "RequiresMountsFor=%s\n",
153                         u);
154
155         r = generator_write_timeouts(arg_dest, device, name, options, &filtered);
156         if (r < 0)
157                 return r;
158
159         fprintf(f,
160                 "\n[Service]\n"
161                 "Type=oneshot\n"
162                 "RemainAfterExit=yes\n"
163                 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
164                 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
165                 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
166                 name, u, strempty(password), strempty(filtered),
167                 name);
168
169         if (tmp)
170                 fprintf(f,
171                         "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
172                         name);
173
174         if (swap)
175                 fprintf(f,
176                         "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
177                         name);
178
179         fflush(f);
180         if (ferror(f))
181                 return log_error_errno(errno, "Failed to write file %s: %m", p);
182
183         from = strjoina("../", n);
184
185         if (!noauto) {
186
187                 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
188                 if (!to)
189                         return log_oom();
190
191                 mkdir_parents_label(to, 0755);
192                 if (symlink(from, to) < 0)
193                         return log_error_errno(errno, "Failed to create symlink %s: %m", to);
194
195                 free(to);
196                 if (!nofail)
197                         to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
198                 else
199                         to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
200                 if (!to)
201                         return log_oom();
202
203                 mkdir_parents_label(to, 0755);
204                 if (symlink(from, to) < 0)
205                         return log_error_errno(errno, "Failed to create symlink %s: %m", to);
206         }
207
208         free(to);
209         to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
210         if (!to)
211                 return log_oom();
212
213         mkdir_parents_label(to, 0755);
214         if (symlink(from, to) < 0)
215                 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
216
217         if (!noauto && !nofail) {
218                 _cleanup_free_ char *dmname;
219                 dmname = strjoin("dev-mapper-", e, ".device", NULL);
220                 if (!dmname)
221                         return log_oom();
222
223                 r = write_drop_in(arg_dest, dmname, 90, "device-timeout",
224                                   "# Automatically generated by systemd-cryptsetup-generator \n\n"
225                                   "[Unit]\nJobTimeoutSec=0");
226                 if (r < 0)
227                         return log_error_errno(r, "Failed to write device drop-in: %m");
228         }
229
230         return 0;
231 }
232
233 static void free_arg_disks(void) {
234         crypto_device *d;
235
236         while ((d = hashmap_steal_first(arg_disks))) {
237                 free(d->uuid);
238                 free(d->keyfile);
239                 free(d->name);
240                 free(d->options);
241                 free(d);
242         }
243
244         hashmap_free(arg_disks);
245 }
246
247 static crypto_device *get_crypto_device(const char *uuid) {
248         int r;
249         crypto_device *d;
250
251         assert(uuid);
252
253         d = hashmap_get(arg_disks, uuid);
254         if (!d) {
255                 d = new0(struct crypto_device, 1);
256                 if (!d)
257                         return NULL;
258
259                 d->create = false;
260                 d->keyfile = d->options = d->name = NULL;
261
262                 d->uuid = strdup(uuid);
263                 if (!d->uuid) {
264                         free(d);
265                         return NULL;
266                 }
267
268                 r = hashmap_put(arg_disks, d->uuid, d);
269                 if (r < 0) {
270                         free(d->uuid);
271                         free(d);
272                         return NULL;
273                 }
274         }
275
276         return d;
277 }
278
279 static int parse_proc_cmdline_item(const char *key, const char *value) {
280         int r;
281         crypto_device *d;
282         _cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
283
284         if (STR_IN_SET(key, "luks", "rd.luks") && value) {
285
286                 r = parse_boolean(value);
287                 if (r < 0)
288                         log_warning("Failed to parse luks switch %s. Ignoring.", value);
289                 else
290                         arg_enabled = r;
291
292         } else if (STR_IN_SET(key, "luks.crypttab", "rd.luks.crypttab") && value) {
293
294                 r = parse_boolean(value);
295                 if (r < 0)
296                         log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value);
297                 else
298                         arg_read_crypttab = r;
299
300         } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) {
301
302                 d = get_crypto_device(startswith(value, "luks-") ? value+5 : value);
303                 if (!d)
304                         return log_oom();
305
306                 d->create = arg_whitelist = true;
307
308         } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) {
309
310                 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
311                 if (r == 2) {
312                         d = get_crypto_device(uuid);
313                         if (!d)
314                                 return log_oom();
315
316                         free(d->options);
317                         d->options = uuid_value;
318                         uuid_value = NULL;
319                 } else if (free_and_strdup(&arg_default_options, value) < 0)
320                         return log_oom();
321
322         } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) {
323
324                 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
325                 if (r == 2) {
326                         d = get_crypto_device(uuid);
327                         if (!d)
328                                 return log_oom();
329
330                         free(d->keyfile);
331                         d->keyfile = uuid_value;
332                         uuid_value = NULL;
333                 } else if (free_and_strdup(&arg_default_keyfile, value))
334                         return log_oom();
335
336         } else if (STR_IN_SET(key, "luks.name", "rd.luks.name") && value) {
337
338                 r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
339                 if (r == 2) {
340                         d = get_crypto_device(uuid);
341                         if (!d)
342                                 return log_oom();
343
344                         d->create = arg_whitelist = true;
345
346                         free(d->name);
347                         d->name = uuid_value;
348                         uuid_value = NULL;
349                 } else
350                         log_warning("Failed to parse luks name switch %s. Ignoring.", value);
351
352         }
353
354         return 0;
355 }
356
357 static int add_crypttab_devices(void) {
358         struct stat st;
359         unsigned crypttab_line = 0;
360         _cleanup_fclose_ FILE *f = NULL;
361
362         if (!arg_read_crypttab)
363                 return 0;
364
365         f = fopen("/etc/crypttab", "re");
366         if (!f) {
367                 if (errno != ENOENT)
368                         log_error_errno(errno, "Failed to open /etc/crypttab: %m");
369                 return 0;
370         }
371
372         if (fstat(fileno(f), &st) < 0) {
373                 log_error_errno(errno, "Failed to stat /etc/crypttab: %m");
374                 return 0;
375         }
376
377         for (;;) {
378                 int r, k;
379                 char line[LINE_MAX], *l, *uuid;
380                 crypto_device *d = NULL;
381                 _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
382
383                 if (!fgets(line, sizeof(line), f))
384                         break;
385
386                 crypttab_line++;
387
388                 l = strstrip(line);
389                 if (*l == '#' || *l == 0)
390                         continue;
391
392                 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
393                 if (k < 2 || k > 4) {
394                         log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
395                         continue;
396                 }
397
398                 uuid = startswith(device, "UUID=");
399                 if (!uuid)
400                         uuid = path_startswith(device, "/dev/disk/by-uuid/");
401                 if (!uuid)
402                         uuid = startswith(name, "luks-");
403                 if (uuid)
404                         d = hashmap_get(arg_disks, uuid);
405
406                 if (arg_whitelist && !d) {
407                         log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
408                         continue;
409                 }
410
411                 r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options);
412                 if (r < 0)
413                         return r;
414
415                 if (d)
416                         d->create = false;
417         }
418
419         return 0;
420 }
421
422 static int add_proc_cmdline_devices(void) {
423         int r;
424         Iterator i;
425         crypto_device *d;
426
427         HASHMAP_FOREACH(d, arg_disks, i) {
428                 const char *options;
429                 _cleanup_free_ char *device = NULL;
430
431                 if (!d->create)
432                         continue;
433
434                 if (!d->name) {
435                         d->name = strappend("luks-", d->uuid);
436                         if (!d->name)
437                                 return log_oom();
438                 }
439
440                 device = strappend("UUID=", d->uuid);
441                 if (!device)
442                         return log_oom();
443
444                 if (d->options)
445                         options = d->options;
446                 else if (arg_default_options)
447                         options = arg_default_options;
448                 else
449                         options = "timeout=0";
450
451                 r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options);
452                 if (r < 0)
453                         return r;
454         }
455
456         return 0;
457 }
458
459 int main(int argc, char *argv[]) {
460         int r = EXIT_FAILURE;
461
462         if (argc > 1 && argc != 4) {
463                 log_error("This program takes three or no arguments.");
464                 return EXIT_FAILURE;
465         }
466
467         if (argc > 1)
468                 arg_dest = argv[1];
469
470         log_set_target(LOG_TARGET_SAFE);
471         log_parse_environment();
472         log_open();
473
474         umask(0022);
475
476         arg_disks = hashmap_new(&string_hash_ops);
477         if (!arg_disks)
478                 goto cleanup;
479
480         r = parse_proc_cmdline(parse_proc_cmdline_item);
481         if (r < 0) {
482                 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
483                 r = EXIT_FAILURE;
484         }
485
486         if (!arg_enabled) {
487                 r = EXIT_SUCCESS;
488                 goto cleanup;
489         }
490
491         if (add_crypttab_devices() < 0)
492                 goto cleanup;
493
494         if (add_proc_cmdline_devices() < 0)
495                 goto cleanup;
496
497         r = EXIT_SUCCESS;
498
499 cleanup:
500         free_arg_disks();
501         free(arg_default_options);
502         free(arg_default_keyfile);
503
504         return r;
505 }