]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup.c
Merge pull request #9811 from poettering/random-seed-tweaks
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <mntent.h>
5 #include <string.h>
6 #include <sys/mman.h>
7
8 #include "sd-device.h"
9
10 #include "alloc-util.h"
11 #include "ask-password-api.h"
12 #include "crypt-util.h"
13 #include "device-util.h"
14 #include "escape.h"
15 #include "fileio.h"
16 #include "log.h"
17 #include "mount-util.h"
18 #include "parse-util.h"
19 #include "path-util.h"
20 #include "string-util.h"
21 #include "strv.h"
22 #include "terminal-util.h"
23 #include "util.h"
24
25 /* internal helper */
26 #define ANY_LUKS "LUKS"
27
28 static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT or CRYPT_PLAIN */
29 static char *arg_cipher = NULL;
30 static unsigned arg_key_size = 0;
31 static int arg_key_slot = CRYPT_ANY_SLOT;
32 static unsigned arg_keyfile_size = 0;
33 static uint64_t arg_keyfile_offset = 0;
34 static char *arg_hash = NULL;
35 static char *arg_header = NULL;
36 static unsigned arg_tries = 3;
37 static bool arg_readonly = false;
38 static bool arg_verify = false;
39 static bool arg_discards = false;
40 static bool arg_tcrypt_hidden = false;
41 static bool arg_tcrypt_system = false;
42 #ifdef CRYPT_TCRYPT_VERA_MODES
43 static bool arg_tcrypt_veracrypt = false;
44 #endif
45 static char **arg_tcrypt_keyfiles = NULL;
46 static uint64_t arg_offset = 0;
47 static uint64_t arg_skip = 0;
48 static usec_t arg_timeout = USEC_INFINITY;
49
50 /* Options Debian's crypttab knows we don't:
51
52 precheck=
53 check=
54 checkargs=
55 noearly=
56 loud=
57 keyscript=
58 */
59
60 static int parse_one_option(const char *option) {
61 const char *val;
62 int r;
63
64 assert(option);
65
66 /* Handled outside of this tool */
67 if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail", "_netdev"))
68 return 0;
69
70 if ((val = startswith(option, "cipher="))) {
71 r = free_and_strdup(&arg_cipher, val);
72 if (r < 0)
73 return log_oom();
74
75 } else if ((val = startswith(option, "size="))) {
76
77 r = safe_atou(val, &arg_key_size);
78 if (r < 0) {
79 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
80 return 0;
81 }
82
83 if (arg_key_size % 8) {
84 log_error("size= not a multiple of 8, ignoring.");
85 return 0;
86 }
87
88 arg_key_size /= 8;
89
90 } else if ((val = startswith(option, "key-slot="))) {
91
92 arg_type = ANY_LUKS;
93 r = safe_atoi(val, &arg_key_slot);
94 if (r < 0) {
95 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
96 return 0;
97 }
98
99 } else if ((val = startswith(option, "tcrypt-keyfile="))) {
100
101 arg_type = CRYPT_TCRYPT;
102 if (path_is_absolute(val)) {
103 if (strv_extend(&arg_tcrypt_keyfiles, val) < 0)
104 return log_oom();
105 } else
106 log_error("Key file path \"%s\" is not absolute. Ignoring.", val);
107
108 } else if ((val = startswith(option, "keyfile-size="))) {
109
110 r = safe_atou(val, &arg_keyfile_size);
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, "keyfile-offset="))) {
117 uint64_t off;
118
119 r = safe_atou64(val, &off);
120 if (r < 0) {
121 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
122 return 0;
123 }
124
125 if ((size_t) off != off) {
126 /* https://gitlab.com/cryptsetup/cryptsetup/issues/359 */
127 log_error("keyfile-offset= value would truncated to %zu, ignoring.", (size_t) off);
128 return 0;
129 }
130
131 arg_keyfile_offset = off;
132
133 } else if ((val = startswith(option, "hash="))) {
134 r = free_and_strdup(&arg_hash, val);
135 if (r < 0)
136 return log_oom();
137
138 } else if ((val = startswith(option, "header="))) {
139 arg_type = ANY_LUKS;
140
141 if (!path_is_absolute(val)) {
142 log_error("Header path \"%s\" is not absolute, refusing.", val);
143 return -EINVAL;
144 }
145
146 if (arg_header) {
147 log_error("Duplicate header= option, refusing.");
148 return -EINVAL;
149 }
150
151 arg_header = strdup(val);
152 if (!arg_header)
153 return log_oom();
154
155 } else if ((val = startswith(option, "tries="))) {
156
157 r = safe_atou(val, &arg_tries);
158 if (r < 0) {
159 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
160 return 0;
161 }
162
163 } else if (STR_IN_SET(option, "readonly", "read-only"))
164 arg_readonly = true;
165 else if (streq(option, "verify"))
166 arg_verify = true;
167 else if (STR_IN_SET(option, "allow-discards", "discard"))
168 arg_discards = true;
169 else if (streq(option, "luks"))
170 arg_type = ANY_LUKS;
171 else if (streq(option, "tcrypt"))
172 arg_type = CRYPT_TCRYPT;
173 else if (streq(option, "tcrypt-hidden")) {
174 arg_type = CRYPT_TCRYPT;
175 arg_tcrypt_hidden = true;
176 } else if (streq(option, "tcrypt-system")) {
177 arg_type = CRYPT_TCRYPT;
178 arg_tcrypt_system = true;
179 } else if (streq(option, "tcrypt-veracrypt")) {
180 #ifdef CRYPT_TCRYPT_VERA_MODES
181 arg_type = CRYPT_TCRYPT;
182 arg_tcrypt_veracrypt = true;
183 #else
184 log_error("This version of cryptsetup does not support tcrypt-veracrypt; refusing.");
185 return -EINVAL;
186 #endif
187 } else if (STR_IN_SET(option, "plain", "swap", "tmp"))
188 arg_type = CRYPT_PLAIN;
189 else if ((val = startswith(option, "timeout="))) {
190
191 r = parse_sec_fix_0(val, &arg_timeout);
192 if (r < 0) {
193 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
194 return 0;
195 }
196
197 } else if ((val = startswith(option, "offset="))) {
198
199 r = safe_atou64(val, &arg_offset);
200 if (r < 0)
201 return log_error_errno(r, "Failed to parse %s: %m", option);
202
203 } else if ((val = startswith(option, "skip="))) {
204
205 r = safe_atou64(val, &arg_skip);
206 if (r < 0)
207 return log_error_errno(r, "Failed to parse %s: %m", option);
208
209 } else if (!streq(option, "none"))
210 log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
211
212 return 0;
213 }
214
215 static int parse_options(const char *options) {
216 const char *word, *state;
217 size_t l;
218 int r;
219
220 assert(options);
221
222 FOREACH_WORD_SEPARATOR(word, l, options, ",", state) {
223 _cleanup_free_ char *o;
224
225 o = strndup(word, l);
226 if (!o)
227 return -ENOMEM;
228 r = parse_one_option(o);
229 if (r < 0)
230 return r;
231 }
232
233 /* sanity-check options */
234 if (arg_type != NULL && !streq(arg_type, CRYPT_PLAIN)) {
235 if (arg_offset)
236 log_warning("offset= ignored with type %s", arg_type);
237 if (arg_skip)
238 log_warning("skip= ignored with type %s", arg_type);
239 }
240
241 return 0;
242 }
243
244 static char* disk_description(const char *path) {
245
246 static const char name_fields[] =
247 "ID_PART_ENTRY_NAME\0"
248 "DM_NAME\0"
249 "ID_MODEL_FROM_DATABASE\0"
250 "ID_MODEL\0";
251
252 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
253 struct stat st;
254 const char *i;
255 int r;
256
257 assert(path);
258
259 if (stat(path, &st) < 0)
260 return NULL;
261
262 if (!S_ISBLK(st.st_mode))
263 return NULL;
264
265 r = sd_device_new_from_devnum(&device, 'b', st.st_rdev);
266 if (r < 0)
267 return NULL;
268
269 NULSTR_FOREACH(i, name_fields) {
270 const char *name;
271
272 r = sd_device_get_property_value(device, i, &name);
273 if (r >= 0 && !isempty(name))
274 return strdup(name);
275 }
276
277 return NULL;
278 }
279
280 static char *disk_mount_point(const char *label) {
281 _cleanup_free_ char *device = NULL;
282 _cleanup_endmntent_ FILE *f = NULL;
283 struct mntent *m;
284
285 /* Yeah, we don't support native systemd unit files here for now */
286
287 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
288 return NULL;
289
290 f = setmntent("/etc/fstab", "re");
291 if (!f)
292 return NULL;
293
294 while ((m = getmntent(f)))
295 if (path_equal(m->mnt_fsname, device))
296 return strdup(m->mnt_dir);
297
298 return NULL;
299 }
300
301 static int get_password(const char *vol, const char *src, usec_t until, bool accept_cached, char ***ret) {
302 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL, *text = NULL, *disk_path = NULL;
303 _cleanup_strv_free_erase_ char **passwords = NULL;
304 const char *name = NULL;
305 char **p, *id;
306 int r = 0;
307
308 assert(vol);
309 assert(src);
310 assert(ret);
311
312 description = disk_description(src);
313 mount_point = disk_mount_point(vol);
314
315 disk_path = cescape(src);
316 if (!disk_path)
317 return log_oom();
318
319 if (description && streq(vol, description))
320 /* If the description string is simply the
321 * volume name, then let's not show this
322 * twice */
323 description = mfree(description);
324
325 if (mount_point && description)
326 r = asprintf(&name_buffer, "%s (%s) on %s", description, vol, mount_point);
327 else if (mount_point)
328 r = asprintf(&name_buffer, "%s on %s", vol, mount_point);
329 else if (description)
330 r = asprintf(&name_buffer, "%s (%s)", description, vol);
331
332 if (r < 0)
333 return log_oom();
334
335 name = name_buffer ? name_buffer : vol;
336
337 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
338 return log_oom();
339
340 id = strjoina("cryptsetup:", disk_path);
341
342 r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until,
343 ASK_PASSWORD_PUSH_CACHE | (accept_cached*ASK_PASSWORD_ACCEPT_CACHED),
344 &passwords);
345 if (r < 0)
346 return log_error_errno(r, "Failed to query password: %m");
347
348 if (arg_verify) {
349 _cleanup_strv_free_erase_ char **passwords2 = NULL;
350
351 assert(strv_length(passwords) == 1);
352
353 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
354 return log_oom();
355
356 id = strjoina("cryptsetup-verification:", disk_path);
357
358 r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE, &passwords2);
359 if (r < 0)
360 return log_error_errno(r, "Failed to query verification password: %m");
361
362 assert(strv_length(passwords2) == 1);
363
364 if (!streq(passwords[0], passwords2[0])) {
365 log_warning("Passwords did not match, retrying.");
366 return -EAGAIN;
367 }
368 }
369
370 strv_uniq(passwords);
371
372 STRV_FOREACH(p, passwords) {
373 char *c;
374
375 if (strlen(*p)+1 >= arg_key_size)
376 continue;
377
378 /* Pad password if necessary */
379 c = new(char, arg_key_size);
380 if (!c)
381 return log_oom();
382
383 strncpy(c, *p, arg_key_size);
384 free(*p);
385 *p = c;
386 }
387
388 *ret = TAKE_PTR(passwords);
389
390 return 0;
391 }
392
393 static int attach_tcrypt(
394 struct crypt_device *cd,
395 const char *name,
396 const char *key_file,
397 char **passwords,
398 uint32_t flags) {
399
400 int r = 0;
401 _cleanup_free_ char *passphrase = NULL;
402 struct crypt_params_tcrypt params = {
403 .flags = CRYPT_TCRYPT_LEGACY_MODES,
404 .keyfiles = (const char **)arg_tcrypt_keyfiles,
405 .keyfiles_count = strv_length(arg_tcrypt_keyfiles)
406 };
407
408 assert(cd);
409 assert(name);
410 assert(key_file || (passwords && passwords[0]));
411
412 if (arg_tcrypt_hidden)
413 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
414
415 if (arg_tcrypt_system)
416 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
417
418 #ifdef CRYPT_TCRYPT_VERA_MODES
419 if (arg_tcrypt_veracrypt)
420 params.flags |= CRYPT_TCRYPT_VERA_MODES;
421 #endif
422
423 if (key_file) {
424 r = read_one_line_file(key_file, &passphrase);
425 if (r < 0) {
426 log_error_errno(r, "Failed to read password file '%s': %m", key_file);
427 return -EAGAIN;
428 }
429
430 params.passphrase = passphrase;
431 } else
432 params.passphrase = passwords[0];
433 params.passphrase_size = strlen(params.passphrase);
434
435 r = crypt_load(cd, CRYPT_TCRYPT, &params);
436 if (r < 0) {
437 if (key_file && r == -EPERM) {
438 log_error("Failed to activate using password file '%s'.", key_file);
439 return -EAGAIN;
440 }
441 return r;
442 }
443
444 return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
445 }
446
447 static int attach_luks_or_plain(struct crypt_device *cd,
448 const char *name,
449 const char *key_file,
450 const char *data_device,
451 char **passwords,
452 uint32_t flags) {
453 int r = 0;
454 bool pass_volume_key = false;
455
456 assert(cd);
457 assert(name);
458 assert(key_file || passwords);
459
460 if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1)) {
461 r = crypt_load(cd, CRYPT_LUKS, NULL);
462 if (r < 0) {
463 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd));
464 return r;
465 }
466
467 if (data_device)
468 r = crypt_set_data_device(cd, data_device);
469 }
470
471 if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) {
472 struct crypt_params_plain params = {
473 .offset = arg_offset,
474 .skip = arg_skip,
475 };
476 const char *cipher, *cipher_mode;
477 _cleanup_free_ char *truncated_cipher = NULL;
478
479 if (arg_hash) {
480 /* plain isn't a real hash type. it just means "use no hash" */
481 if (!streq(arg_hash, "plain"))
482 params.hash = arg_hash;
483 } else if (!key_file)
484 /* for CRYPT_PLAIN, the behaviour of cryptsetup
485 * package is to not hash when a key file is provided */
486 params.hash = "ripemd160";
487
488 if (arg_cipher) {
489 size_t l;
490
491 l = strcspn(arg_cipher, "-");
492 truncated_cipher = strndup(arg_cipher, l);
493 if (!truncated_cipher)
494 return log_oom();
495
496 cipher = truncated_cipher;
497 cipher_mode = arg_cipher[l] ? arg_cipher+l+1 : "plain";
498 } else {
499 cipher = "aes";
500 cipher_mode = "cbc-essiv:sha256";
501 }
502
503 /* for CRYPT_PLAIN limit reads
504 * from keyfile to key length, and
505 * ignore keyfile-size */
506 arg_keyfile_size = arg_key_size;
507
508 /* In contrast to what the name
509 * crypt_setup() might suggest this
510 * doesn't actually format anything,
511 * it just configures encryption
512 * parameters when used for plain
513 * mode. */
514 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, arg_keyfile_size, &params);
515
516 /* hash == NULL implies the user passed "plain" */
517 pass_volume_key = (params.hash == NULL);
518 }
519
520 if (r < 0)
521 return log_error_errno(r, "Loading of cryptographic parameters failed: %m");
522
523 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
524 crypt_get_cipher(cd),
525 crypt_get_cipher_mode(cd),
526 crypt_get_volume_key_size(cd)*8,
527 crypt_get_device_name(cd));
528
529 if (key_file) {
530 r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags);
531 if (r < 0) {
532 log_error_errno(r, "Failed to activate with key file '%s': %m", key_file);
533 return -EAGAIN;
534 }
535 } else {
536 char **p;
537
538 STRV_FOREACH(p, passwords) {
539 if (pass_volume_key)
540 r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
541 else
542 r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
543
544 if (r >= 0)
545 break;
546 }
547 }
548
549 return r;
550 }
551
552 static int help(void) {
553 _cleanup_free_ char *link = NULL;
554 int r;
555
556 r = terminal_urlify_man("systemd-cryptsetup@.service", "8", &link);
557 if (r < 0)
558 return log_oom();
559
560 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
561 "%s detach VOLUME\n\n"
562 "Attaches or detaches an encrypted block device.\n"
563 "\nSee the %s for details.\n"
564 , program_invocation_short_name
565 , program_invocation_short_name
566 , link
567 );
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 }