]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup.c
cryptsetup: fix OOM handling when parsing mount options
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup.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 <sys/mman.h>
25 #include <mntent.h>
26
27 #include <libcryptsetup.h>
28 #include <libudev.h>
29
30 #include "fileio.h"
31 #include "log.h"
32 #include "util.h"
33 #include "path-util.h"
34 #include "strv.h"
35 #include "ask-password-api.h"
36 #include "def.h"
37
38 static const char *opt_type = NULL; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
39 static char *opt_cipher = NULL;
40 static unsigned opt_key_size = 0;
41 static unsigned opt_keyfile_size = 0;
42 static unsigned opt_keyfile_offset = 0;
43 static char *opt_hash = NULL;
44 static unsigned opt_tries = 3;
45 static bool opt_readonly = false;
46 static bool opt_verify = false;
47 static bool opt_discards = false;
48 static bool opt_tcrypt_hidden = false;
49 static bool opt_tcrypt_system = false;
50 static char **opt_tcrypt_keyfiles = NULL;
51 static usec_t opt_timeout = 0;
52
53 /* Options Debian's crypttab knows we don't:
54
55 offset=
56 skip=
57 precheck=
58 check=
59 checkargs=
60 noearly=
61 loud=
62 keyscript=
63 */
64
65 static int parse_one_option(const char *option) {
66 assert(option);
67
68 /* Handled outside of this tool */
69 if (streq(option, "noauto") || streq(option, "nofail"))
70 return 0;
71
72 if (startswith(option, "cipher=")) {
73 char *t;
74
75 t = strdup(option+7);
76 if (!t)
77 return log_oom();
78
79 free(opt_cipher);
80 opt_cipher = t;
81
82 } else if (startswith(option, "size=")) {
83
84 if (safe_atou(option+5, &opt_key_size) < 0) {
85 log_error("size= parse failure, ignoring.");
86 return 0;
87 }
88
89 } else if (startswith(option, "tcrypt-keyfile=")) {
90
91 opt_type = CRYPT_TCRYPT;
92 if (path_is_absolute(option+15)) {
93 if (strv_extend(&opt_tcrypt_keyfiles, option + 15) < 0)
94 return log_oom();
95 } else
96 log_error("Key file path '%s' is not absolute. Ignoring.", option+15);
97
98 } else if (startswith(option, "keyfile-size=")) {
99
100 if (safe_atou(option+13, &opt_keyfile_size) < 0) {
101 log_error("keyfile-size= parse failure, ignoring.");
102 return 0;
103 }
104
105 } else if (startswith(option, "keyfile-offset=")) {
106
107 if (safe_atou(option+15, &opt_keyfile_offset) < 0) {
108 log_error("keyfile-offset= parse failure, ignoring.");
109 return 0;
110 }
111
112 } else if (startswith(option, "hash=")) {
113 char *t;
114
115 t = strdup(option+5);
116 if (!t)
117 return log_oom();
118
119 free(opt_hash);
120 opt_hash = t;
121
122 } else if (startswith(option, "tries=")) {
123
124 if (safe_atou(option+6, &opt_tries) < 0) {
125 log_error("tries= parse failure, ignoring.");
126 return 0;
127 }
128
129 } else if (streq(option, "readonly") || streq(option, "read-only"))
130 opt_readonly = true;
131 else if (streq(option, "verify"))
132 opt_verify = true;
133 else if (streq(option, "allow-discards") || streq(option, "discard"))
134 opt_discards = true;
135 else if (streq(option, "luks"))
136 opt_type = CRYPT_LUKS1;
137 else if (streq(option, "tcrypt"))
138 opt_type = CRYPT_TCRYPT;
139 else if (streq(option, "tcrypt-hidden")) {
140 opt_type = CRYPT_TCRYPT;
141 opt_tcrypt_hidden = true;
142 } else if (streq(option, "tcrypt-system")) {
143 opt_type = CRYPT_TCRYPT;
144 opt_tcrypt_system = true;
145 } else if (streq(option, "plain") ||
146 streq(option, "swap") ||
147 streq(option, "tmp"))
148 opt_type = CRYPT_PLAIN;
149 else if (startswith(option, "timeout=")) {
150
151 if (parse_sec(option+8, &opt_timeout) < 0) {
152 log_error("timeout= parse failure, ignoring.");
153 return 0;
154 }
155
156 } else if (!streq(option, "none"))
157 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
158
159 return 0;
160 }
161
162 static int parse_options(const char *options) {
163 char *state, *w;
164 size_t l;
165 int r;
166
167 assert(options);
168
169 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
170 _cleanup_free_ char *o;
171
172 o = strndup(w, l);
173 if (!o)
174 return -ENOMEM;
175 r = parse_one_option(o);
176 if (r < 0)
177 return r;
178 }
179
180 return 0;
181 }
182
183 static void log_glue(int level, const char *msg, void *usrptr) {
184 log_debug("%s", msg);
185 }
186
187 static char *disk_description(const char *path) {
188
189 static const char name_fields[] = {
190 "ID_PART_ENTRY_NAME\0"
191 "DM_NAME\0"
192 "ID_MODEL_FROM_DATABASE\0"
193 "ID_MODEL\0"
194 };
195
196 struct udev *udev = NULL;
197 struct udev_device *device = NULL;
198 struct stat st;
199 char *description = NULL;
200 const char *i;
201
202 assert(path);
203
204 if (stat(path, &st) < 0)
205 return NULL;
206
207 if (!S_ISBLK(st.st_mode))
208 return NULL;
209
210 udev = udev_new();
211 if (!udev)
212 return NULL;
213
214 device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
215 if (!device)
216 goto finish;
217
218 NULSTR_FOREACH(i, name_fields) {
219 const char *name;
220
221 name = udev_device_get_property_value(device, i);
222 if (!isempty(name)) {
223 description = strdup(name);
224 break;
225 }
226 }
227
228 finish:
229 if (device)
230 udev_device_unref(device);
231
232 if (udev)
233 udev_unref(udev);
234
235 return description;
236 }
237
238 static char *disk_mount_point(const char *label) {
239 char *mp = NULL;
240 _cleanup_free_ char *device = NULL;
241 FILE *f = NULL;
242 struct mntent *m;
243
244 /* Yeah, we don't support native systemd unit files here for now */
245
246 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
247 goto finish;
248
249 f = setmntent("/etc/fstab", "r");
250 if (!f)
251 goto finish;
252
253 while ((m = getmntent(f)))
254 if (path_equal(m->mnt_fsname, device)) {
255 mp = strdup(m->mnt_dir);
256 break;
257 }
258
259 finish:
260 if (f)
261 endmntent(f);
262
263 return mp;
264 }
265
266 static int get_password(const char *name, usec_t until, bool accept_cached, char ***passwords) {
267 int r;
268 char **p;
269 _cleanup_free_ char *text = NULL;
270
271 assert(name);
272 assert(passwords);
273
274 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
275 return log_oom();
276
277 r = ask_password_auto(text, "drive-harddisk", until, accept_cached, passwords);
278 if (r < 0) {
279 log_error("Failed to query password: %s", strerror(-r));
280 return r;
281 }
282
283 if (opt_verify) {
284 _cleanup_strv_free_ char **passwords2 = NULL;
285
286 assert(strv_length(*passwords) == 1);
287
288 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
289 return log_oom();
290
291 r = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
292 if (r < 0) {
293 log_error("Failed to query verification password: %s", strerror(-r));
294 return r;
295 }
296
297 assert(strv_length(passwords2) == 1);
298
299 if (!streq(*passwords[0], passwords2[0])) {
300 log_warning("Passwords did not match, retrying.");
301 return -EAGAIN;
302 }
303 }
304
305 strv_uniq(*passwords);
306
307 STRV_FOREACH(p, *passwords) {
308 char *c;
309
310 if (strlen(*p)+1 >= opt_key_size)
311 continue;
312
313 /* Pad password if necessary */
314 if (!(c = new(char, opt_key_size)))
315 return log_oom();
316
317 strncpy(c, *p, opt_key_size);
318 free(*p);
319 *p = c;
320 }
321
322 return 0;
323 }
324
325 static int attach_tcrypt(struct crypt_device *cd,
326 const char *name,
327 const char *key_file,
328 char **passwords,
329 uint32_t flags) {
330 int r = 0;
331 _cleanup_free_ char *passphrase = NULL;
332 struct crypt_params_tcrypt params = {
333 .flags = CRYPT_TCRYPT_LEGACY_MODES,
334 .keyfiles = (const char **)opt_tcrypt_keyfiles,
335 .keyfiles_count = strv_length(opt_tcrypt_keyfiles)
336 };
337
338 assert(cd);
339 assert(name);
340 assert(key_file || passwords);
341
342 if (opt_tcrypt_hidden)
343 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
344
345 if (opt_tcrypt_system)
346 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
347
348 if (key_file) {
349 r = read_one_line_file(key_file, &passphrase);
350 if (r < 0) {
351 log_error("Failed to read password file '%s': %s", key_file, strerror(-r));
352 return -EAGAIN;
353 }
354
355 params.passphrase = passphrase;
356 } else
357 params.passphrase = passwords[0];
358 params.passphrase_size = strlen(params.passphrase);
359
360 r = crypt_load(cd, CRYPT_TCRYPT, &params);
361 if (r < 0) {
362 if (key_file && r == -EPERM) {
363 log_error("Failed to activate using password file '%s'.", key_file);
364 return -EAGAIN;
365 }
366 return r;
367 }
368
369 return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);;
370 }
371
372 static int attach_luks_or_plain(struct crypt_device *cd,
373 const char *name,
374 const char *key_file,
375 char **passwords,
376 uint32_t flags) {
377 int r = 0;
378 bool pass_volume_key = false;
379
380 assert(cd);
381 assert(name);
382 assert(key_file || passwords);
383
384 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
385 r = crypt_load(cd, CRYPT_LUKS1, NULL);
386
387 if ((!opt_type && r < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
388 struct crypt_params_plain params = {};
389 const char *cipher, *cipher_mode;
390 _cleanup_free_ char *truncated_cipher = NULL;
391
392 if (opt_hash) {
393 /* plain isn't a real hash type. it just means "use no hash" */
394 if (!streq(opt_hash, "plain"))
395 params.hash = opt_hash;
396 } else
397 params.hash = "ripemd160";
398
399 if (opt_cipher) {
400 size_t l;
401
402 l = strcspn(opt_cipher, "-");
403 truncated_cipher = strndup(opt_cipher, l);
404 if (!truncated_cipher)
405 return log_oom();
406
407 cipher = truncated_cipher;
408 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
409 } else {
410 cipher = "aes";
411 cipher_mode = "cbc-essiv:sha256";
412 }
413
414 /* for CRYPT_PLAIN limit reads
415 * from keyfile to key length, and
416 * ignore keyfile-size */
417 opt_keyfile_size = opt_key_size / 8;
418
419 /* In contrast to what the name
420 * crypt_setup() might suggest this
421 * doesn't actually format anything,
422 * it just configures encryption
423 * parameters when used for plain
424 * mode. */
425 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode,
426 NULL, NULL, opt_keyfile_size, &params);
427
428 /* hash == NULL implies the user passed "plain" */
429 pass_volume_key = (params.hash == NULL);
430 }
431
432 if (r < 0) {
433 log_error("Loading of cryptographic parameters failed: %s", strerror(-r));
434 return r;
435 }
436
437 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
438 crypt_get_cipher(cd),
439 crypt_get_cipher_mode(cd),
440 crypt_get_volume_key_size(cd)*8,
441 crypt_get_device_name(cd));
442
443 if (key_file) {
444 r = crypt_activate_by_keyfile_offset(cd, name, CRYPT_ANY_SLOT,
445 key_file, opt_keyfile_size,
446 opt_keyfile_offset, flags);
447 if (r < 0) {
448 log_error("Failed to activate with key file '%s': %s", key_file, strerror(-r));
449 return -EAGAIN;
450 }
451 } else {
452 char **p;
453
454 STRV_FOREACH(p, passwords) {
455 if (pass_volume_key)
456 r = crypt_activate_by_volume_key(cd, name, *p, opt_key_size, flags);
457 else
458 r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, *p, strlen(*p), flags);
459
460 if (r >= 0)
461 break;
462 }
463 }
464
465 return r;
466 }
467
468 static int help(void) {
469
470 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
471 "%s detach VOLUME\n\n"
472 "Attaches or detaches an encrypted block device.\n",
473 program_invocation_short_name,
474 program_invocation_short_name);
475
476 return 0;
477 }
478
479 int main(int argc, char *argv[]) {
480 int r = EXIT_FAILURE;
481 struct crypt_device *cd = NULL;
482
483 if (argc <= 1) {
484 help();
485 return EXIT_SUCCESS;
486 }
487
488 if (argc < 3) {
489 log_error("This program requires at least two arguments.");
490 return EXIT_FAILURE;
491 }
492
493 log_set_target(LOG_TARGET_AUTO);
494 log_parse_environment();
495 log_open();
496
497 umask(0022);
498
499 if (streq(argv[1], "attach")) {
500 uint32_t flags = 0;
501 int k;
502 unsigned tries;
503 usec_t until;
504 crypt_status_info status;
505 const char *key_file = NULL, *name = NULL;
506 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
507
508 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
509
510 if (argc < 4) {
511 log_error("attach requires at least two arguments.");
512 goto finish;
513 }
514
515 if (argc >= 5 &&
516 argv[4][0] &&
517 !streq(argv[4], "-") &&
518 !streq(argv[4], "none")) {
519
520 if (!path_is_absolute(argv[4]))
521 log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]);
522 else
523 key_file = argv[4];
524 }
525
526 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
527 if (parse_options(argv[5]) < 0)
528 goto finish;
529 }
530
531 /* A delicious drop of snake oil */
532 mlockall(MCL_FUTURE);
533
534 description = disk_description(argv[3]);
535 mount_point = disk_mount_point(argv[2]);
536
537 if (description && streq(argv[2], description)) {
538 /* If the description string is simply the
539 * volume name, then let's not show this
540 * twice */
541 free(description);
542 description = NULL;
543 }
544
545 if (mount_point && description)
546 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
547 else if (mount_point)
548 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
549 else if (description)
550 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
551
552 name = name_buffer ? name_buffer : argv[2];
553
554 k = crypt_init(&cd, argv[3]);
555 if (k) {
556 log_error("crypt_init() failed: %s", strerror(-k));
557 goto finish;
558 }
559
560 crypt_set_log_callback(cd, log_glue, NULL);
561
562 status = crypt_status(cd, argv[2]);
563 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
564 log_info("Volume %s already active.", argv[2]);
565 r = EXIT_SUCCESS;
566 goto finish;
567 }
568
569 if (opt_readonly)
570 flags |= CRYPT_ACTIVATE_READONLY;
571
572 if (opt_discards)
573 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
574
575 if (opt_timeout > 0)
576 until = now(CLOCK_MONOTONIC) + opt_timeout;
577 else
578 until = 0;
579
580 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
581
582 if (key_file) {
583 struct stat st;
584
585 /* Ideally we'd do this on the open fd, but since this is just a
586 * warning it's OK to do this in two steps. */
587 if (stat(key_file, &st) >= 0 && (st.st_mode & 0005))
588 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
589 }
590
591 for (tries = 0; opt_tries == 0 || tries < opt_tries; tries++) {
592 _cleanup_strv_free_ char **passwords = NULL;
593
594 if (!key_file) {
595 k = get_password(name, until, tries == 0 && !opt_verify, &passwords);
596 if (k == -EAGAIN)
597 continue;
598 else if (k < 0)
599 goto finish;
600 }
601
602 if (streq_ptr(opt_type, CRYPT_TCRYPT))
603 k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
604 else
605 k = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags);
606 if (k >= 0)
607 break;
608 else if (k == -EAGAIN) {
609 key_file = NULL;
610 continue;
611 } else if (k != -EPERM) {
612 log_error("Failed to activate: %s", strerror(-k));
613 goto finish;
614 }
615
616 log_warning("Invalid passphrase.");
617 }
618
619 if (opt_tries != 0 && tries >= opt_tries) {
620 log_error("Too many attempts; giving up.");
621 r = EXIT_FAILURE;
622 goto finish;
623 }
624
625 } else if (streq(argv[1], "detach")) {
626 int k;
627
628 k = crypt_init_by_name(&cd, argv[2]);
629 if (k) {
630 log_error("crypt_init() failed: %s", strerror(-k));
631 goto finish;
632 }
633
634 crypt_set_log_callback(cd, log_glue, NULL);
635
636 k = crypt_deactivate(cd, argv[2]);
637 if (k < 0) {
638 log_error("Failed to deactivate: %s", strerror(-k));
639 goto finish;
640 }
641
642 } else {
643 log_error("Unknown verb %s.", argv[1]);
644 goto finish;
645 }
646
647 r = EXIT_SUCCESS;
648
649 finish:
650
651 if (cd)
652 crypt_free(cd);
653
654 free(opt_cipher);
655 free(opt_hash);
656 strv_free(opt_tcrypt_keyfiles);
657
658 return r;
659 }