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