1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
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.
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.
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/>.
22 #include <libcryptsetup.h>
27 #include "sd-device.h"
29 #include "alloc-util.h"
30 #include "ask-password-api.h"
31 #include "device-util.h"
35 #include "mount-util.h"
36 #include "parse-util.h"
37 #include "path-util.h"
38 #include "string-util.h"
42 /* libcryptsetup define for any LUKS version, compatible with libcryptsetup 1.x */
44 #define CRYPT_LUKS NULL
48 #define ANY_LUKS "LUKS"
50 static const char *arg_type
= NULL
; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT or CRYPT_PLAIN */
51 static char *arg_cipher
= NULL
;
52 static unsigned arg_key_size
= 0;
53 static int arg_key_slot
= CRYPT_ANY_SLOT
;
54 static unsigned arg_keyfile_size
= 0;
55 static unsigned arg_keyfile_offset
= 0;
56 static char *arg_hash
= NULL
;
57 static char *arg_header
= NULL
;
58 static unsigned arg_tries
= 3;
59 static bool arg_readonly
= false;
60 static bool arg_verify
= false;
61 static bool arg_discards
= false;
62 static bool arg_tcrypt_hidden
= false;
63 static bool arg_tcrypt_system
= false;
64 #ifdef CRYPT_TCRYPT_VERA_MODES
65 static bool arg_tcrypt_veracrypt
= false;
67 static char **arg_tcrypt_keyfiles
= NULL
;
68 static uint64_t arg_offset
= 0;
69 static uint64_t arg_skip
= 0;
70 static usec_t arg_timeout
= USEC_INFINITY
;
72 /* Options Debian's crypttab knows we don't:
82 static int parse_one_option(const char *option
) {
88 /* Handled outside of this tool */
89 if (STR_IN_SET(option
, "noauto", "auto", "nofail", "fail", "_netdev"))
92 if ((val
= startswith(option
, "cipher="))) {
93 r
= free_and_strdup(&arg_cipher
, val
);
97 } else if ((val
= startswith(option
, "size="))) {
99 r
= safe_atou(val
, &arg_key_size
);
101 log_error_errno(r
, "Failed to parse %s, ignoring: %m", option
);
105 if (arg_key_size
% 8) {
106 log_error("size= not a multiple of 8, ignoring.");
112 } else if ((val
= startswith(option
, "key-slot="))) {
115 r
= safe_atoi(val
, &arg_key_slot
);
117 log_error_errno(r
, "Failed to parse %s, ignoring: %m", option
);
121 } else if ((val
= startswith(option
, "tcrypt-keyfile="))) {
123 arg_type
= CRYPT_TCRYPT
;
124 if (path_is_absolute(val
)) {
125 if (strv_extend(&arg_tcrypt_keyfiles
, val
) < 0)
128 log_error("Key file path \"%s\" is not absolute. Ignoring.", val
);
130 } else if ((val
= startswith(option
, "keyfile-size="))) {
132 r
= safe_atou(val
, &arg_keyfile_size
);
134 log_error_errno(r
, "Failed to parse %s, ignoring: %m", option
);
138 } else if ((val
= startswith(option
, "keyfile-offset="))) {
140 r
= safe_atou(val
, &arg_keyfile_offset
);
142 log_error_errno(r
, "Failed to parse %s, ignoring: %m", option
);
146 } else if ((val
= startswith(option
, "hash="))) {
147 r
= free_and_strdup(&arg_hash
, val
);
151 } else if ((val
= startswith(option
, "header="))) {
154 if (!path_is_absolute(val
)) {
155 log_error("Header path \"%s\" is not absolute, refusing.", val
);
160 log_error("Duplicate header= option, refusing.");
164 arg_header
= strdup(val
);
168 } else if ((val
= startswith(option
, "tries="))) {
170 r
= safe_atou(val
, &arg_tries
);
172 log_error_errno(r
, "Failed to parse %s, ignoring: %m", option
);
176 } else if (STR_IN_SET(option
, "readonly", "read-only"))
178 else if (streq(option
, "verify"))
180 else if (STR_IN_SET(option
, "allow-discards", "discard"))
182 else if (streq(option
, "luks"))
184 else if (streq(option
, "tcrypt"))
185 arg_type
= CRYPT_TCRYPT
;
186 else if (streq(option
, "tcrypt-hidden")) {
187 arg_type
= CRYPT_TCRYPT
;
188 arg_tcrypt_hidden
= true;
189 } else if (streq(option
, "tcrypt-system")) {
190 arg_type
= CRYPT_TCRYPT
;
191 arg_tcrypt_system
= true;
192 } else if (streq(option
, "tcrypt-veracrypt")) {
193 #ifdef CRYPT_TCRYPT_VERA_MODES
194 arg_type
= CRYPT_TCRYPT
;
195 arg_tcrypt_veracrypt
= true;
197 log_error("This version of cryptsetup does not support tcrypt-veracrypt; refusing.");
200 } else if (STR_IN_SET(option
, "plain", "swap", "tmp"))
201 arg_type
= CRYPT_PLAIN
;
202 else if ((val
= startswith(option
, "timeout="))) {
204 r
= parse_sec_fix_0(val
, &arg_timeout
);
206 log_error_errno(r
, "Failed to parse %s, ignoring: %m", option
);
210 } else if ((val
= startswith(option
, "offset="))) {
212 r
= safe_atou64(val
, &arg_offset
);
214 return log_error_errno(r
, "Failed to parse %s: %m", option
);
216 } else if ((val
= startswith(option
, "skip="))) {
218 r
= safe_atou64(val
, &arg_skip
);
220 return log_error_errno(r
, "Failed to parse %s: %m", option
);
222 } else if (!streq(option
, "none"))
223 log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
228 static int parse_options(const char *options
) {
229 const char *word
, *state
;
235 FOREACH_WORD_SEPARATOR(word
, l
, options
, ",", state
) {
236 _cleanup_free_
char *o
;
238 o
= strndup(word
, l
);
241 r
= parse_one_option(o
);
246 /* sanity-check options */
247 if (arg_type
!= NULL
&& !streq(arg_type
, CRYPT_PLAIN
)) {
249 log_warning("offset= ignored with type %s", arg_type
);
251 log_warning("skip= ignored with type %s", arg_type
);
257 static void log_glue(int level
, const char *msg
, void *usrptr
) {
258 log_debug("%s", msg
);
261 static int disk_major_minor(const char *path
, char **ret
) {
266 if (stat(path
, &st
) < 0)
269 if (!S_ISBLK(st
.st_mode
))
272 if (asprintf(ret
, "/dev/block/%d:%d", major(st
.st_rdev
), minor(st
.st_rdev
)) < 0)
278 static char* disk_description(const char *path
) {
280 static const char name_fields
[] =
281 "ID_PART_ENTRY_NAME\0"
283 "ID_MODEL_FROM_DATABASE\0"
286 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
293 if (stat(path
, &st
) < 0)
296 if (!S_ISBLK(st
.st_mode
))
299 r
= sd_device_new_from_devnum(&device
, 'b', st
.st_rdev
);
303 NULSTR_FOREACH(i
, name_fields
) {
306 r
= sd_device_get_property_value(device
, i
, &name
);
307 if (r
>= 0 && !isempty(name
))
314 static char *disk_mount_point(const char *label
) {
315 _cleanup_free_
char *device
= NULL
;
316 _cleanup_endmntent_
FILE *f
= NULL
;
319 /* Yeah, we don't support native systemd unit files here for now */
321 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
324 f
= setmntent("/etc/fstab", "re");
328 while ((m
= getmntent(f
)))
329 if (path_equal(m
->mnt_fsname
, device
))
330 return strdup(m
->mnt_dir
);
335 static int get_password(const char *vol
, const char *src
, usec_t until
, bool accept_cached
, char ***ret
) {
336 _cleanup_free_
char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
, *maj_min
= NULL
, *text
= NULL
, *escaped_name
= NULL
;
337 _cleanup_strv_free_erase_
char **passwords
= NULL
;
338 const char *name
= NULL
;
346 description
= disk_description(src
);
347 mount_point
= disk_mount_point(vol
);
349 if (description
&& streq(vol
, description
))
350 /* If the description string is simply the
351 * volume name, then let's not show this
353 description
= mfree(description
);
355 if (mount_point
&& description
)
356 r
= asprintf(&name_buffer
, "%s (%s) on %s", description
, vol
, mount_point
);
357 else if (mount_point
)
358 r
= asprintf(&name_buffer
, "%s on %s", vol
, mount_point
);
359 else if (description
)
360 r
= asprintf(&name_buffer
, "%s (%s)", description
, vol
);
365 name
= name_buffer
? name_buffer
: vol
;
367 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0)
371 (void) disk_major_minor(src
, &maj_min
);
374 escaped_name
= maj_min
;
377 escaped_name
= cescape(name
);
382 id
= strjoina("cryptsetup:", escaped_name
);
384 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
,
385 ASK_PASSWORD_PUSH_CACHE
| (accept_cached
*ASK_PASSWORD_ACCEPT_CACHED
),
388 return log_error_errno(r
, "Failed to query password: %m");
391 _cleanup_strv_free_erase_
char **passwords2
= NULL
;
393 assert(strv_length(passwords
) == 1);
395 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0)
398 id
= strjoina("cryptsetup-verification:", escaped_name
);
400 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
, ASK_PASSWORD_PUSH_CACHE
, &passwords2
);
402 return log_error_errno(r
, "Failed to query verification password: %m");
404 assert(strv_length(passwords2
) == 1);
406 if (!streq(passwords
[0], passwords2
[0])) {
407 log_warning("Passwords did not match, retrying.");
412 strv_uniq(passwords
);
414 STRV_FOREACH(p
, passwords
) {
417 if (strlen(*p
)+1 >= arg_key_size
)
420 /* Pad password if necessary */
421 c
= new(char, arg_key_size
);
425 strncpy(c
, *p
, arg_key_size
);
436 static int attach_tcrypt(
437 struct crypt_device
*cd
,
439 const char *key_file
,
444 _cleanup_free_
char *passphrase
= NULL
;
445 struct crypt_params_tcrypt params
= {
446 .flags
= CRYPT_TCRYPT_LEGACY_MODES
,
447 .keyfiles
= (const char **)arg_tcrypt_keyfiles
,
448 .keyfiles_count
= strv_length(arg_tcrypt_keyfiles
)
453 assert(key_file
|| (passwords
&& passwords
[0]));
455 if (arg_tcrypt_hidden
)
456 params
.flags
|= CRYPT_TCRYPT_HIDDEN_HEADER
;
458 if (arg_tcrypt_system
)
459 params
.flags
|= CRYPT_TCRYPT_SYSTEM_HEADER
;
461 #ifdef CRYPT_TCRYPT_VERA_MODES
462 if (arg_tcrypt_veracrypt
)
463 params
.flags
|= CRYPT_TCRYPT_VERA_MODES
;
467 r
= read_one_line_file(key_file
, &passphrase
);
469 log_error_errno(r
, "Failed to read password file '%s': %m", key_file
);
473 params
.passphrase
= passphrase
;
475 params
.passphrase
= passwords
[0];
476 params
.passphrase_size
= strlen(params
.passphrase
);
478 r
= crypt_load(cd
, CRYPT_TCRYPT
, ¶ms
);
480 if (key_file
&& r
== -EPERM
) {
481 log_error("Failed to activate using password file '%s'.", key_file
);
487 return crypt_activate_by_volume_key(cd
, name
, NULL
, 0, flags
);
490 static int attach_luks_or_plain(struct crypt_device
*cd
,
492 const char *key_file
,
493 const char *data_device
,
497 bool pass_volume_key
= false;
501 assert(key_file
|| passwords
);
503 if (!arg_type
|| STR_IN_SET(arg_type
, ANY_LUKS
, CRYPT_LUKS1
)) {
504 r
= crypt_load(cd
, CRYPT_LUKS
, NULL
);
506 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd
));
511 r
= crypt_set_data_device(cd
, data_device
);
514 if ((!arg_type
&& r
< 0) || streq_ptr(arg_type
, CRYPT_PLAIN
)) {
515 struct crypt_params_plain params
= {
516 .offset
= arg_offset
,
519 const char *cipher
, *cipher_mode
;
520 _cleanup_free_
char *truncated_cipher
= NULL
;
523 /* plain isn't a real hash type. it just means "use no hash" */
524 if (!streq(arg_hash
, "plain"))
525 params
.hash
= arg_hash
;
526 } else if (!key_file
)
527 /* for CRYPT_PLAIN, the behaviour of cryptsetup
528 * package is to not hash when a key file is provided */
529 params
.hash
= "ripemd160";
534 l
= strcspn(arg_cipher
, "-");
535 truncated_cipher
= strndup(arg_cipher
, l
);
536 if (!truncated_cipher
)
539 cipher
= truncated_cipher
;
540 cipher_mode
= arg_cipher
[l
] ? arg_cipher
+l
+1 : "plain";
543 cipher_mode
= "cbc-essiv:sha256";
546 /* for CRYPT_PLAIN limit reads
547 * from keyfile to key length, and
548 * ignore keyfile-size */
549 arg_keyfile_size
= arg_key_size
;
551 /* In contrast to what the name
552 * crypt_setup() might suggest this
553 * doesn't actually format anything,
554 * it just configures encryption
555 * parameters when used for plain
557 r
= crypt_format(cd
, CRYPT_PLAIN
, cipher
, cipher_mode
, NULL
, NULL
, arg_keyfile_size
, ¶ms
);
559 /* hash == NULL implies the user passed "plain" */
560 pass_volume_key
= (params
.hash
== NULL
);
564 return log_error_errno(r
, "Loading of cryptographic parameters failed: %m");
566 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
567 crypt_get_cipher(cd
),
568 crypt_get_cipher_mode(cd
),
569 crypt_get_volume_key_size(cd
)*8,
570 crypt_get_device_name(cd
));
573 r
= crypt_activate_by_keyfile_offset(cd
, name
, arg_key_slot
, key_file
, arg_keyfile_size
, arg_keyfile_offset
, flags
);
575 log_error_errno(r
, "Failed to activate with key file '%s': %m", key_file
);
581 STRV_FOREACH(p
, passwords
) {
583 r
= crypt_activate_by_volume_key(cd
, name
, *p
, arg_key_size
, flags
);
585 r
= crypt_activate_by_passphrase(cd
, name
, arg_key_slot
, *p
, strlen(*p
), flags
);
595 static int help(void) {
597 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
598 "%s detach VOLUME\n\n"
599 "Attaches or detaches an encrypted block device.\n",
600 program_invocation_short_name
,
601 program_invocation_short_name
);
606 int main(int argc
, char *argv
[]) {
607 struct crypt_device
*cd
= NULL
;
616 log_error("This program requires at least two arguments.");
620 log_set_target(LOG_TARGET_AUTO
);
621 log_parse_environment();
626 if (streq(argv
[1], "attach")) {
630 crypt_status_info status
;
631 const char *key_file
= NULL
;
633 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
636 log_error("attach requires at least two arguments.");
642 !streq(argv
[4], "-") &&
643 !streq(argv
[4], "none")) {
645 if (!path_is_absolute(argv
[4]))
646 log_error("Password file path '%s' is not absolute. Ignoring.", argv
[4]);
651 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-")) {
652 if (parse_options(argv
[5]) < 0)
656 /* A delicious drop of snake oil */
657 mlockall(MCL_FUTURE
);
660 log_debug("LUKS header: %s", arg_header
);
661 r
= crypt_init(&cd
, arg_header
);
663 r
= crypt_init(&cd
, argv
[3]);
665 log_error_errno(r
, "crypt_init() failed: %m");
669 crypt_set_log_callback(cd
, log_glue
, NULL
);
671 status
= crypt_status(cd
, argv
[2]);
672 if (IN_SET(status
, CRYPT_ACTIVE
, CRYPT_BUSY
)) {
673 log_info("Volume %s already active.", argv
[2]);
679 flags
|= CRYPT_ACTIVATE_READONLY
;
682 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
684 if (arg_timeout
== USEC_INFINITY
)
687 until
= now(CLOCK_MONOTONIC
) + arg_timeout
;
689 arg_key_size
= (arg_key_size
> 0 ? arg_key_size
: (256 / 8));
694 /* Ideally we'd do this on the open fd, but since this is just a
695 * warning it's OK to do this in two steps. */
696 if (stat(key_file
, &st
) >= 0 && S_ISREG(st
.st_mode
) && (st
.st_mode
& 0005))
697 log_warning("Key file %s is world-readable. This is not a good idea!", key_file
);
700 for (tries
= 0; arg_tries
== 0 || tries
< arg_tries
; tries
++) {
701 _cleanup_strv_free_erase_
char **passwords
= NULL
;
704 r
= get_password(argv
[2], argv
[3], until
, tries
== 0 && !arg_verify
, &passwords
);
711 if (streq_ptr(arg_type
, CRYPT_TCRYPT
))
712 r
= attach_tcrypt(cd
, argv
[2], key_file
, passwords
, flags
);
714 r
= attach_luks_or_plain(cd
,
717 arg_header
? argv
[3] : NULL
,
727 log_error_errno(r
, "Failed to activate: %m");
731 log_warning("Invalid passphrase.");
734 if (arg_tries
!= 0 && tries
>= arg_tries
) {
735 log_error("Too many attempts; giving up.");
740 } else if (streq(argv
[1], "detach")) {
742 r
= crypt_init_by_name(&cd
, argv
[2]);
744 log_info("Volume %s already inactive.", argv
[2]);
749 log_error_errno(r
, "crypt_init_by_name() failed: %m");
753 crypt_set_log_callback(cd
, log_glue
, NULL
);
755 r
= crypt_deactivate(cd
, argv
[2]);
757 log_error_errno(r
, "Failed to deactivate: %m");
762 log_error("Unknown verb %s.", argv
[1]);
775 strv_free(arg_tcrypt_keyfiles
);
777 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;