]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup.c
69bbf525fe06554286ec838bcfc21888ec52035a
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <mntent.h>
23 #include <string.h>
24 #include <sys/mman.h>
25
26 #include "sd-device.h"
27
28 #include "alloc-util.h"
29 #include "ask-password-api.h"
30 #include "crypt-util.h"
31 #include "device-util.h"
32 #include "escape.h"
33 #include "fileio.h"
34 #include "log.h"
35 #include "mount-util.h"
36 #include "parse-util.h"
37 #include "path-util.h"
38 #include "string-util.h"
39 #include "strv.h"
40 #include "util.h"
41
42 /* internal helper */
43 #define ANY_LUKS "LUKS"
44
45 static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT or CRYPT_PLAIN */
46 static char *arg_cipher = NULL;
47 static unsigned arg_key_size = 0;
48 static int arg_key_slot = CRYPT_ANY_SLOT;
49 static unsigned arg_keyfile_size = 0;
50 static uint64_t arg_keyfile_offset = 0;
51 static char *arg_hash = NULL;
52 static char *arg_header = NULL;
53 static unsigned arg_tries = 3;
54 static bool arg_readonly = false;
55 static bool arg_verify = false;
56 static bool arg_discards = false;
57 static bool arg_tcrypt_hidden = false;
58 static bool arg_tcrypt_system = false;
59 #ifdef CRYPT_TCRYPT_VERA_MODES
60 static bool arg_tcrypt_veracrypt = false;
61 #endif
62 static char **arg_tcrypt_keyfiles = NULL;
63 static uint64_t arg_offset = 0;
64 static uint64_t arg_skip = 0;
65 static usec_t arg_timeout = USEC_INFINITY;
66
67 /* Options Debian's crypttab knows we don't:
68
69 precheck=
70 check=
71 checkargs=
72 noearly=
73 loud=
74 keyscript=
75 */
76
77 static int parse_one_option(const char *option) {
78 const char *val;
79 int r;
80
81 assert(option);
82
83 /* Handled outside of this tool */
84 if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail", "_netdev"))
85 return 0;
86
87 if ((val = startswith(option, "cipher="))) {
88 r = free_and_strdup(&arg_cipher, val);
89 if (r < 0)
90 return log_oom();
91
92 } else if ((val = startswith(option, "size="))) {
93
94 r = safe_atou(val, &arg_key_size);
95 if (r < 0) {
96 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
97 return 0;
98 }
99
100 if (arg_key_size % 8) {
101 log_error("size= not a multiple of 8, ignoring.");
102 return 0;
103 }
104
105 arg_key_size /= 8;
106
107 } else if ((val = startswith(option, "key-slot="))) {
108
109 arg_type = ANY_LUKS;
110 r = safe_atoi(val, &arg_key_slot);
111 if (r < 0) {
112 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
113 return 0;
114 }
115
116 } else if ((val = startswith(option, "tcrypt-keyfile="))) {
117
118 arg_type = CRYPT_TCRYPT;
119 if (path_is_absolute(val)) {
120 if (strv_extend(&arg_tcrypt_keyfiles, val) < 0)
121 return log_oom();
122 } else
123 log_error("Key file path \"%s\" is not absolute. Ignoring.", val);
124
125 } else if ((val = startswith(option, "keyfile-size="))) {
126
127 r = safe_atou(val, &arg_keyfile_size);
128 if (r < 0) {
129 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
130 return 0;
131 }
132
133 } else if ((val = startswith(option, "keyfile-offset="))) {
134 uint64_t off;
135
136 r = safe_atou64(val, &off);
137 if (r < 0) {
138 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
139 return 0;
140 }
141
142 if ((size_t) off != off) {
143 /* https://gitlab.com/cryptsetup/cryptsetup/issues/359 */
144 log_error("keyfile-offset= value would truncated to %zu, ignoring.", (size_t) off);
145 return 0;
146 }
147
148 arg_keyfile_offset = off;
149
150 } else if ((val = startswith(option, "hash="))) {
151 r = free_and_strdup(&arg_hash, val);
152 if (r < 0)
153 return log_oom();
154
155 } else if ((val = startswith(option, "header="))) {
156 arg_type = ANY_LUKS;
157
158 if (!path_is_absolute(val)) {
159 log_error("Header path \"%s\" is not absolute, refusing.", val);
160 return -EINVAL;
161 }
162
163 if (arg_header) {
164 log_error("Duplicate header= option, refusing.");
165 return -EINVAL;
166 }
167
168 arg_header = strdup(val);
169 if (!arg_header)
170 return log_oom();
171
172 } else if ((val = startswith(option, "tries="))) {
173
174 r = safe_atou(val, &arg_tries);
175 if (r < 0) {
176 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
177 return 0;
178 }
179
180 } else if (STR_IN_SET(option, "readonly", "read-only"))
181 arg_readonly = true;
182 else if (streq(option, "verify"))
183 arg_verify = true;
184 else if (STR_IN_SET(option, "allow-discards", "discard"))
185 arg_discards = true;
186 else if (streq(option, "luks"))
187 arg_type = ANY_LUKS;
188 else if (streq(option, "tcrypt"))
189 arg_type = CRYPT_TCRYPT;
190 else if (streq(option, "tcrypt-hidden")) {
191 arg_type = CRYPT_TCRYPT;
192 arg_tcrypt_hidden = true;
193 } else if (streq(option, "tcrypt-system")) {
194 arg_type = CRYPT_TCRYPT;
195 arg_tcrypt_system = true;
196 } else if (streq(option, "tcrypt-veracrypt")) {
197 #ifdef CRYPT_TCRYPT_VERA_MODES
198 arg_type = CRYPT_TCRYPT;
199 arg_tcrypt_veracrypt = true;
200 #else
201 log_error("This version of cryptsetup does not support tcrypt-veracrypt; refusing.");
202 return -EINVAL;
203 #endif
204 } else if (STR_IN_SET(option, "plain", "swap", "tmp"))
205 arg_type = CRYPT_PLAIN;
206 else if ((val = startswith(option, "timeout="))) {
207
208 r = parse_sec_fix_0(val, &arg_timeout);
209 if (r < 0) {
210 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
211 return 0;
212 }
213
214 } else if ((val = startswith(option, "offset="))) {
215
216 r = safe_atou64(val, &arg_offset);
217 if (r < 0)
218 return log_error_errno(r, "Failed to parse %s: %m", option);
219
220 } else if ((val = startswith(option, "skip="))) {
221
222 r = safe_atou64(val, &arg_skip);
223 if (r < 0)
224 return log_error_errno(r, "Failed to parse %s: %m", option);
225
226 } else if (!streq(option, "none"))
227 log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
228
229 return 0;
230 }
231
232 static int parse_options(const char *options) {
233 const char *word, *state;
234 size_t l;
235 int r;
236
237 assert(options);
238
239 FOREACH_WORD_SEPARATOR(word, l, options, ",", state) {
240 _cleanup_free_ char *o;
241
242 o = strndup(word, l);
243 if (!o)
244 return -ENOMEM;
245 r = parse_one_option(o);
246 if (r < 0)
247 return r;
248 }
249
250 /* sanity-check options */
251 if (arg_type != NULL && !streq(arg_type, CRYPT_PLAIN)) {
252 if (arg_offset)
253 log_warning("offset= ignored with type %s", arg_type);
254 if (arg_skip)
255 log_warning("skip= ignored with type %s", arg_type);
256 }
257
258 return 0;
259 }
260
261 static char* disk_description(const char *path) {
262
263 static const char name_fields[] =
264 "ID_PART_ENTRY_NAME\0"
265 "DM_NAME\0"
266 "ID_MODEL_FROM_DATABASE\0"
267 "ID_MODEL\0";
268
269 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
270 struct stat st;
271 const char *i;
272 int r;
273
274 assert(path);
275
276 if (stat(path, &st) < 0)
277 return NULL;
278
279 if (!S_ISBLK(st.st_mode))
280 return NULL;
281
282 r = sd_device_new_from_devnum(&device, 'b', st.st_rdev);
283 if (r < 0)
284 return NULL;
285
286 NULSTR_FOREACH(i, name_fields) {
287 const char *name;
288
289 r = sd_device_get_property_value(device, i, &name);
290 if (r >= 0 && !isempty(name))
291 return strdup(name);
292 }
293
294 return NULL;
295 }
296
297 static char *disk_mount_point(const char *label) {
298 _cleanup_free_ char *device = NULL;
299 _cleanup_endmntent_ FILE *f = NULL;
300 struct mntent *m;
301
302 /* Yeah, we don't support native systemd unit files here for now */
303
304 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
305 return NULL;
306
307 f = setmntent("/etc/fstab", "re");
308 if (!f)
309 return NULL;
310
311 while ((m = getmntent(f)))
312 if (path_equal(m->mnt_fsname, device))
313 return strdup(m->mnt_dir);
314
315 return NULL;
316 }
317
318 static int get_password(const char *vol, const char *src, usec_t until, bool accept_cached, char ***ret) {
319 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL, *text = NULL, *disk_path = NULL;
320 _cleanup_strv_free_erase_ char **passwords = NULL;
321 const char *name = NULL;
322 char **p, *id;
323 int r = 0;
324
325 assert(vol);
326 assert(src);
327 assert(ret);
328
329 description = disk_description(src);
330 mount_point = disk_mount_point(vol);
331
332 disk_path = cescape(src);
333 if (!disk_path)
334 return log_oom();
335
336 if (description && streq(vol, description))
337 /* If the description string is simply the
338 * volume name, then let's not show this
339 * twice */
340 description = mfree(description);
341
342 if (mount_point && description)
343 r = asprintf(&name_buffer, "%s (%s) on %s", description, vol, mount_point);
344 else if (mount_point)
345 r = asprintf(&name_buffer, "%s on %s", vol, mount_point);
346 else if (description)
347 r = asprintf(&name_buffer, "%s (%s)", description, vol);
348
349 if (r < 0)
350 return log_oom();
351
352 name = name_buffer ? name_buffer : vol;
353
354 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
355 return log_oom();
356
357 id = strjoina("cryptsetup:", disk_path);
358
359 r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until,
360 ASK_PASSWORD_PUSH_CACHE | (accept_cached*ASK_PASSWORD_ACCEPT_CACHED),
361 &passwords);
362 if (r < 0)
363 return log_error_errno(r, "Failed to query password: %m");
364
365 if (arg_verify) {
366 _cleanup_strv_free_erase_ char **passwords2 = NULL;
367
368 assert(strv_length(passwords) == 1);
369
370 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
371 return log_oom();
372
373 id = strjoina("cryptsetup-verification:", disk_path);
374
375 r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE, &passwords2);
376 if (r < 0)
377 return log_error_errno(r, "Failed to query verification password: %m");
378
379 assert(strv_length(passwords2) == 1);
380
381 if (!streq(passwords[0], passwords2[0])) {
382 log_warning("Passwords did not match, retrying.");
383 return -EAGAIN;
384 }
385 }
386
387 strv_uniq(passwords);
388
389 STRV_FOREACH(p, passwords) {
390 char *c;
391
392 if (strlen(*p)+1 >= arg_key_size)
393 continue;
394
395 /* Pad password if necessary */
396 c = new(char, arg_key_size);
397 if (!c)
398 return log_oom();
399
400 strncpy(c, *p, arg_key_size);
401 free(*p);
402 *p = c;
403 }
404
405 *ret = TAKE_PTR(passwords);
406
407 return 0;
408 }
409
410 static int attach_tcrypt(
411 struct crypt_device *cd,
412 const char *name,
413 const char *key_file,
414 char **passwords,
415 uint32_t flags) {
416
417 int r = 0;
418 _cleanup_free_ char *passphrase = NULL;
419 struct crypt_params_tcrypt params = {
420 .flags = CRYPT_TCRYPT_LEGACY_MODES,
421 .keyfiles = (const char **)arg_tcrypt_keyfiles,
422 .keyfiles_count = strv_length(arg_tcrypt_keyfiles)
423 };
424
425 assert(cd);
426 assert(name);
427 assert(key_file || (passwords && passwords[0]));
428
429 if (arg_tcrypt_hidden)
430 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
431
432 if (arg_tcrypt_system)
433 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
434
435 #ifdef CRYPT_TCRYPT_VERA_MODES
436 if (arg_tcrypt_veracrypt)
437 params.flags |= CRYPT_TCRYPT_VERA_MODES;
438 #endif
439
440 if (key_file) {
441 r = read_one_line_file(key_file, &passphrase);
442 if (r < 0) {
443 log_error_errno(r, "Failed to read password file '%s': %m", key_file);
444 return -EAGAIN;
445 }
446
447 params.passphrase = passphrase;
448 } else
449 params.passphrase = passwords[0];
450 params.passphrase_size = strlen(params.passphrase);
451
452 r = crypt_load(cd, CRYPT_TCRYPT, &params);
453 if (r < 0) {
454 if (key_file && r == -EPERM) {
455 log_error("Failed to activate using password file '%s'.", key_file);
456 return -EAGAIN;
457 }
458 return r;
459 }
460
461 return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
462 }
463
464 static int attach_luks_or_plain(struct crypt_device *cd,
465 const char *name,
466 const char *key_file,
467 const char *data_device,
468 char **passwords,
469 uint32_t flags) {
470 int r = 0;
471 bool pass_volume_key = false;
472
473 assert(cd);
474 assert(name);
475 assert(key_file || passwords);
476
477 if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1)) {
478 r = crypt_load(cd, CRYPT_LUKS, NULL);
479 if (r < 0) {
480 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd));
481 return r;
482 }
483
484 if (data_device)
485 r = crypt_set_data_device(cd, data_device);
486 }
487
488 if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) {
489 struct crypt_params_plain params = {
490 .offset = arg_offset,
491 .skip = arg_skip,
492 };
493 const char *cipher, *cipher_mode;
494 _cleanup_free_ char *truncated_cipher = NULL;
495
496 if (arg_hash) {
497 /* plain isn't a real hash type. it just means "use no hash" */
498 if (!streq(arg_hash, "plain"))
499 params.hash = arg_hash;
500 } else if (!key_file)
501 /* for CRYPT_PLAIN, the behaviour of cryptsetup
502 * package is to not hash when a key file is provided */
503 params.hash = "ripemd160";
504
505 if (arg_cipher) {
506 size_t l;
507
508 l = strcspn(arg_cipher, "-");
509 truncated_cipher = strndup(arg_cipher, l);
510 if (!truncated_cipher)
511 return log_oom();
512
513 cipher = truncated_cipher;
514 cipher_mode = arg_cipher[l] ? arg_cipher+l+1 : "plain";
515 } else {
516 cipher = "aes";
517 cipher_mode = "cbc-essiv:sha256";
518 }
519
520 /* for CRYPT_PLAIN limit reads
521 * from keyfile to key length, and
522 * ignore keyfile-size */
523 arg_keyfile_size = arg_key_size;
524
525 /* In contrast to what the name
526 * crypt_setup() might suggest this
527 * doesn't actually format anything,
528 * it just configures encryption
529 * parameters when used for plain
530 * mode. */
531 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, arg_keyfile_size, &params);
532
533 /* hash == NULL implies the user passed "plain" */
534 pass_volume_key = (params.hash == NULL);
535 }
536
537 if (r < 0)
538 return log_error_errno(r, "Loading of cryptographic parameters failed: %m");
539
540 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
541 crypt_get_cipher(cd),
542 crypt_get_cipher_mode(cd),
543 crypt_get_volume_key_size(cd)*8,
544 crypt_get_device_name(cd));
545
546 if (key_file) {
547 r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags);
548 if (r < 0) {
549 log_error_errno(r, "Failed to activate with key file '%s': %m", key_file);
550 return -EAGAIN;
551 }
552 } else {
553 char **p;
554
555 STRV_FOREACH(p, passwords) {
556 if (pass_volume_key)
557 r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
558 else
559 r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
560
561 if (r >= 0)
562 break;
563 }
564 }
565
566 return r;
567 }
568
569 static int help(void) {
570
571 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
572 "%s detach VOLUME\n\n"
573 "Attaches or detaches an encrypted block device.\n",
574 program_invocation_short_name,
575 program_invocation_short_name);
576
577 return 0;
578 }
579
580 int main(int argc, char *argv[]) {
581 _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
582 int r = -EINVAL;
583
584 if (argc <= 1) {
585 r = help();
586 goto finish;
587 }
588
589 if (argc < 3) {
590 log_error("This program requires at least two arguments.");
591 goto finish;
592 }
593
594 log_set_target(LOG_TARGET_AUTO);
595 log_parse_environment();
596 log_open();
597
598 umask(0022);
599
600 if (streq(argv[1], "attach")) {
601 uint32_t flags = 0;
602 unsigned tries;
603 usec_t until;
604 crypt_status_info status;
605 const char *key_file = NULL;
606
607 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
608
609 if (argc < 4) {
610 log_error("attach requires at least two arguments.");
611 goto finish;
612 }
613
614 if (argc >= 5 &&
615 argv[4][0] &&
616 !streq(argv[4], "-") &&
617 !streq(argv[4], "none")) {
618
619 if (!path_is_absolute(argv[4]))
620 log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]);
621 else
622 key_file = argv[4];
623 }
624
625 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
626 if (parse_options(argv[5]) < 0)
627 goto finish;
628 }
629
630 /* A delicious drop of snake oil */
631 mlockall(MCL_FUTURE);
632
633 if (arg_header) {
634 log_debug("LUKS header: %s", arg_header);
635 r = crypt_init(&cd, arg_header);
636 } else
637 r = crypt_init(&cd, argv[3]);
638 if (r < 0) {
639 log_error_errno(r, "crypt_init() failed: %m");
640 goto finish;
641 }
642
643 crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
644
645 status = crypt_status(cd, argv[2]);
646 if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) {
647 log_info("Volume %s already active.", argv[2]);
648 r = 0;
649 goto finish;
650 }
651
652 if (arg_readonly)
653 flags |= CRYPT_ACTIVATE_READONLY;
654
655 if (arg_discards)
656 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
657
658 if (arg_timeout == USEC_INFINITY)
659 until = 0;
660 else
661 until = now(CLOCK_MONOTONIC) + arg_timeout;
662
663 arg_key_size = (arg_key_size > 0 ? arg_key_size : (256 / 8));
664
665 if (key_file) {
666 struct stat st;
667
668 /* Ideally we'd do this on the open fd, but since this is just a
669 * warning it's OK to do this in two steps. */
670 if (stat(key_file, &st) >= 0 && S_ISREG(st.st_mode) && (st.st_mode & 0005))
671 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
672 }
673
674 for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) {
675 _cleanup_strv_free_erase_ char **passwords = NULL;
676
677 if (!key_file) {
678 r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
679 if (r == -EAGAIN)
680 continue;
681 if (r < 0)
682 goto finish;
683 }
684
685 if (streq_ptr(arg_type, CRYPT_TCRYPT))
686 r = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
687 else
688 r = attach_luks_or_plain(cd,
689 argv[2],
690 key_file,
691 arg_header ? argv[3] : NULL,
692 passwords,
693 flags);
694 if (r >= 0)
695 break;
696 if (r == -EAGAIN) {
697 key_file = NULL;
698 continue;
699 }
700 if (r != -EPERM) {
701 log_error_errno(r, "Failed to activate: %m");
702 goto finish;
703 }
704
705 log_warning("Invalid passphrase.");
706 }
707
708 if (arg_tries != 0 && tries >= arg_tries) {
709 log_error("Too many attempts; giving up.");
710 r = -EPERM;
711 goto finish;
712 }
713
714 } else if (streq(argv[1], "detach")) {
715
716 r = crypt_init_by_name(&cd, argv[2]);
717 if (r == -ENODEV) {
718 log_info("Volume %s already inactive.", argv[2]);
719 r = 0;
720 goto finish;
721 }
722 if (r < 0) {
723 log_error_errno(r, "crypt_init_by_name() failed: %m");
724 goto finish;
725 }
726
727 crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
728
729 r = crypt_deactivate(cd, argv[2]);
730 if (r < 0) {
731 log_error_errno(r, "Failed to deactivate: %m");
732 goto finish;
733 }
734
735 } else {
736 log_error("Unknown verb %s.", argv[1]);
737 goto finish;
738 }
739
740 r = 0;
741
742 finish:
743 free(arg_cipher);
744 free(arg_hash);
745 free(arg_header);
746 strv_free(arg_tcrypt_keyfiles);
747
748 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
749 }