1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
23 #include <libcryptsetup.h>
28 #include "sd-device.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 static const char *arg_type
= NULL
; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
43 static char *arg_cipher
= NULL
;
44 static unsigned arg_key_size
= 0;
45 static int arg_key_slot
= CRYPT_ANY_SLOT
;
46 static unsigned arg_keyfile_size
= 0;
47 static unsigned arg_keyfile_offset
= 0;
48 static char *arg_hash
= NULL
;
49 static char *arg_header
= NULL
;
50 static unsigned arg_tries
= 3;
51 static bool arg_readonly
= false;
52 static bool arg_verify
= false;
53 static bool arg_discards
= false;
54 static bool arg_tcrypt_hidden
= false;
55 static bool arg_tcrypt_system
= false;
56 static char **arg_tcrypt_keyfiles
= NULL
;
57 static uint64_t arg_offset
= 0;
58 static uint64_t arg_skip
= 0;
59 static usec_t arg_timeout
= 0;
61 /* Options Debian's crypttab knows we don't:
71 static int parse_one_option(const char *option
) {
74 /* Handled outside of this tool */
75 if (STR_IN_SET(option
, "noauto", "auto", "nofail", "fail"))
78 if (startswith(option
, "cipher=")) {
88 } else if (startswith(option
, "size=")) {
90 if (safe_atou(option
+5, &arg_key_size
) < 0) {
91 log_error("size= parse failure, ignoring.");
95 if (arg_key_size
% 8) {
96 log_error("size= not a multiple of 8, ignoring.");
102 } else if (startswith(option
, "key-slot=")) {
104 arg_type
= CRYPT_LUKS1
;
105 if (safe_atoi(option
+9, &arg_key_slot
) < 0) {
106 log_error("key-slot= parse failure, ignoring.");
110 } else if (startswith(option
, "tcrypt-keyfile=")) {
112 arg_type
= CRYPT_TCRYPT
;
113 if (path_is_absolute(option
+15)) {
114 if (strv_extend(&arg_tcrypt_keyfiles
, option
+ 15) < 0)
117 log_error("Key file path '%s' is not absolute. Ignoring.", option
+15);
119 } else if (startswith(option
, "keyfile-size=")) {
121 if (safe_atou(option
+13, &arg_keyfile_size
) < 0) {
122 log_error("keyfile-size= parse failure, ignoring.");
126 } else if (startswith(option
, "keyfile-offset=")) {
128 if (safe_atou(option
+15, &arg_keyfile_offset
) < 0) {
129 log_error("keyfile-offset= parse failure, ignoring.");
133 } else if (startswith(option
, "hash=")) {
136 t
= strdup(option
+5);
143 } else if (startswith(option
, "header=")) {
144 arg_type
= CRYPT_LUKS1
;
146 if (!path_is_absolute(option
+7)) {
147 log_error("Header path '%s' is not absolute, refusing.", option
+7);
152 log_error("Duplicate header= options, refusing.");
156 arg_header
= strdup(option
+7);
160 } else if (startswith(option
, "tries=")) {
162 if (safe_atou(option
+6, &arg_tries
) < 0) {
163 log_error("tries= parse failure, ignoring.");
167 } else if (STR_IN_SET(option
, "readonly", "read-only"))
169 else if (streq(option
, "verify"))
171 else if (STR_IN_SET(option
, "allow-discards", "discard"))
173 else if (streq(option
, "luks"))
174 arg_type
= CRYPT_LUKS1
;
175 else if (streq(option
, "tcrypt"))
176 arg_type
= CRYPT_TCRYPT
;
177 else if (streq(option
, "tcrypt-hidden")) {
178 arg_type
= CRYPT_TCRYPT
;
179 arg_tcrypt_hidden
= true;
180 } else if (streq(option
, "tcrypt-system")) {
181 arg_type
= CRYPT_TCRYPT
;
182 arg_tcrypt_system
= true;
183 } else if (STR_IN_SET(option
, "plain", "swap", "tmp"))
184 arg_type
= CRYPT_PLAIN
;
185 else if (startswith(option
, "timeout=")) {
187 if (parse_sec(option
+8, &arg_timeout
) < 0) {
188 log_error("timeout= parse failure, ignoring.");
192 } else if (startswith(option
, "offset=")) {
194 if (safe_atou64(option
+7, &arg_offset
) < 0) {
195 log_error("offset= parse failure, refusing.");
199 } else if (startswith(option
, "skip=")) {
201 if (safe_atou64(option
+5, &arg_skip
) < 0) {
202 log_error("skip= parse failure, refusing.");
206 } else if (!streq(option
, "none"))
207 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
212 static int parse_options(const char *options
) {
213 const char *word
, *state
;
219 FOREACH_WORD_SEPARATOR(word
, l
, options
, ",", state
) {
220 _cleanup_free_
char *o
;
222 o
= strndup(word
, l
);
225 r
= parse_one_option(o
);
230 /* sanity-check options */
231 if (arg_type
!= NULL
&& !streq(arg_type
, CRYPT_PLAIN
)) {
233 log_warning("offset= ignored with type %s", arg_type
);
235 log_warning("skip= ignored with type %s", arg_type
);
241 static void log_glue(int level
, const char *msg
, void *usrptr
) {
242 log_debug("%s", msg
);
245 static int disk_major_minor(const char *path
, char **ret
) {
250 if (stat(path
, &st
) < 0)
253 if (!S_ISBLK(st
.st_mode
))
256 if (asprintf(ret
, "/dev/block/%d:%d", major(st
.st_rdev
), minor(st
.st_rdev
)) < 0)
262 static char* disk_description(const char *path
) {
264 static const char name_fields
[] =
265 "ID_PART_ENTRY_NAME\0"
267 "ID_MODEL_FROM_DATABASE\0"
270 _cleanup_device_unref_ sd_device
*device
= NULL
;
277 if (stat(path
, &st
) < 0)
280 if (!S_ISBLK(st
.st_mode
))
283 r
= sd_device_new_from_devnum(&device
, 'b', st
.st_rdev
);
287 NULSTR_FOREACH(i
, name_fields
) {
290 r
= sd_device_get_property_value(device
, i
, &name
);
291 if (r
>= 0 && !isempty(name
))
298 static char *disk_mount_point(const char *label
) {
299 _cleanup_free_
char *device
= NULL
;
300 _cleanup_endmntent_
FILE *f
= NULL
;
303 /* Yeah, we don't support native systemd unit files here for now */
305 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
308 f
= setmntent("/etc/fstab", "r");
312 while ((m
= getmntent(f
)))
313 if (path_equal(m
->mnt_fsname
, device
))
314 return strdup(m
->mnt_dir
);
319 static int get_password(const char *vol
, const char *src
, usec_t until
, bool accept_cached
, char ***ret
) {
320 _cleanup_free_
char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
, *maj_min
= NULL
, *text
= NULL
, *escaped_name
= NULL
;
321 _cleanup_strv_free_erase_
char **passwords
= NULL
;
322 const char *name
= NULL
;
330 description
= disk_description(src
);
331 mount_point
= disk_mount_point(vol
);
333 if (description
&& streq(vol
, description
))
334 /* If the description string is simply the
335 * volume name, then let's not show this
337 description
= mfree(description
);
339 if (mount_point
&& description
)
340 r
= asprintf(&name_buffer
, "%s (%s) on %s", description
, vol
, mount_point
);
341 else if (mount_point
)
342 r
= asprintf(&name_buffer
, "%s on %s", vol
, mount_point
);
343 else if (description
)
344 r
= asprintf(&name_buffer
, "%s (%s)", description
, vol
);
349 name
= name_buffer
? name_buffer
: vol
;
351 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0)
355 (void) disk_major_minor(src
, &maj_min
);
358 escaped_name
= maj_min
;
361 escaped_name
= cescape(name
);
366 id
= strjoina("cryptsetup:", escaped_name
);
368 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
,
369 ASK_PASSWORD_PUSH_CACHE
| (accept_cached
*ASK_PASSWORD_ACCEPT_CACHED
),
372 return log_error_errno(r
, "Failed to query password: %m");
375 _cleanup_strv_free_erase_
char **passwords2
= NULL
;
377 assert(strv_length(passwords
) == 1);
379 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0)
382 id
= strjoina("cryptsetup-verification:", escaped_name
);
384 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
, ASK_PASSWORD_PUSH_CACHE
, &passwords2
);
386 return log_error_errno(r
, "Failed to query verification password: %m");
388 assert(strv_length(passwords2
) == 1);
390 if (!streq(passwords
[0], passwords2
[0])) {
391 log_warning("Passwords did not match, retrying.");
396 strv_uniq(passwords
);
398 STRV_FOREACH(p
, passwords
) {
401 if (strlen(*p
)+1 >= arg_key_size
)
404 /* Pad password if necessary */
405 c
= new(char, arg_key_size
);
409 strncpy(c
, *p
, arg_key_size
);
420 static int attach_tcrypt(
421 struct crypt_device
*cd
,
423 const char *key_file
,
428 _cleanup_free_
char *passphrase
= NULL
;
429 struct crypt_params_tcrypt params
= {
430 .flags
= CRYPT_TCRYPT_LEGACY_MODES
,
431 .keyfiles
= (const char **)arg_tcrypt_keyfiles
,
432 .keyfiles_count
= strv_length(arg_tcrypt_keyfiles
)
437 assert(key_file
|| (passwords
&& passwords
[0]));
439 if (arg_tcrypt_hidden
)
440 params
.flags
|= CRYPT_TCRYPT_HIDDEN_HEADER
;
442 if (arg_tcrypt_system
)
443 params
.flags
|= CRYPT_TCRYPT_SYSTEM_HEADER
;
446 r
= read_one_line_file(key_file
, &passphrase
);
448 log_error_errno(r
, "Failed to read password file '%s': %m", key_file
);
452 params
.passphrase
= passphrase
;
454 params
.passphrase
= passwords
[0];
455 params
.passphrase_size
= strlen(params
.passphrase
);
457 r
= crypt_load(cd
, CRYPT_TCRYPT
, ¶ms
);
459 if (key_file
&& r
== -EPERM
) {
460 log_error("Failed to activate using password file '%s'.", key_file
);
466 return crypt_activate_by_volume_key(cd
, name
, NULL
, 0, flags
);
469 static int attach_luks_or_plain(struct crypt_device
*cd
,
471 const char *key_file
,
472 const char *data_device
,
476 bool pass_volume_key
= false;
480 assert(key_file
|| passwords
);
482 if (!arg_type
|| streq(arg_type
, CRYPT_LUKS1
)) {
483 r
= crypt_load(cd
, CRYPT_LUKS1
, NULL
);
485 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd
));
490 r
= crypt_set_data_device(cd
, data_device
);
493 if ((!arg_type
&& r
< 0) || streq_ptr(arg_type
, CRYPT_PLAIN
)) {
494 struct crypt_params_plain params
= {
495 .offset
= arg_offset
,
498 const char *cipher
, *cipher_mode
;
499 _cleanup_free_
char *truncated_cipher
= NULL
;
502 /* plain isn't a real hash type. it just means "use no hash" */
503 if (!streq(arg_hash
, "plain"))
504 params
.hash
= arg_hash
;
505 } else if (!key_file
)
506 /* for CRYPT_PLAIN, the behaviour of cryptsetup
507 * package is to not hash when a key file is provided */
508 params
.hash
= "ripemd160";
513 l
= strcspn(arg_cipher
, "-");
514 truncated_cipher
= strndup(arg_cipher
, l
);
515 if (!truncated_cipher
)
518 cipher
= truncated_cipher
;
519 cipher_mode
= arg_cipher
[l
] ? arg_cipher
+l
+1 : "plain";
522 cipher_mode
= "cbc-essiv:sha256";
525 /* for CRYPT_PLAIN limit reads
526 * from keyfile to key length, and
527 * ignore keyfile-size */
528 arg_keyfile_size
= arg_key_size
;
530 /* In contrast to what the name
531 * crypt_setup() might suggest this
532 * doesn't actually format anything,
533 * it just configures encryption
534 * parameters when used for plain
536 r
= crypt_format(cd
, CRYPT_PLAIN
, cipher
, cipher_mode
, NULL
, NULL
, arg_keyfile_size
, ¶ms
);
538 /* hash == NULL implies the user passed "plain" */
539 pass_volume_key
= (params
.hash
== NULL
);
543 return log_error_errno(r
, "Loading of cryptographic parameters failed: %m");
545 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
546 crypt_get_cipher(cd
),
547 crypt_get_cipher_mode(cd
),
548 crypt_get_volume_key_size(cd
)*8,
549 crypt_get_device_name(cd
));
552 r
= crypt_activate_by_keyfile_offset(cd
, name
, arg_key_slot
, key_file
, arg_keyfile_size
, arg_keyfile_offset
, flags
);
554 log_error_errno(r
, "Failed to activate with key file '%s': %m", key_file
);
560 STRV_FOREACH(p
, passwords
) {
562 r
= crypt_activate_by_volume_key(cd
, name
, *p
, arg_key_size
, flags
);
564 r
= crypt_activate_by_passphrase(cd
, name
, arg_key_slot
, *p
, strlen(*p
), flags
);
574 static int help(void) {
576 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
577 "%s detach VOLUME\n\n"
578 "Attaches or detaches an encrypted block device.\n",
579 program_invocation_short_name
,
580 program_invocation_short_name
);
585 int main(int argc
, char *argv
[]) {
586 int r
= EXIT_FAILURE
;
587 struct crypt_device
*cd
= NULL
;
595 log_error("This program requires at least two arguments.");
599 log_set_target(LOG_TARGET_AUTO
);
600 log_parse_environment();
605 if (streq(argv
[1], "attach")) {
610 crypt_status_info status
;
611 const char *key_file
= NULL
;
613 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
616 log_error("attach requires at least two arguments.");
622 !streq(argv
[4], "-") &&
623 !streq(argv
[4], "none")) {
625 if (!path_is_absolute(argv
[4]))
626 log_error("Password file path '%s' is not absolute. Ignoring.", argv
[4]);
631 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-")) {
632 if (parse_options(argv
[5]) < 0)
636 /* A delicious drop of snake oil */
637 mlockall(MCL_FUTURE
);
640 log_debug("LUKS header: %s", arg_header
);
641 k
= crypt_init(&cd
, arg_header
);
643 k
= crypt_init(&cd
, argv
[3]);
645 log_error_errno(k
, "crypt_init() failed: %m");
649 crypt_set_log_callback(cd
, log_glue
, NULL
);
651 status
= crypt_status(cd
, argv
[2]);
652 if (status
== CRYPT_ACTIVE
|| status
== CRYPT_BUSY
) {
653 log_info("Volume %s already active.", argv
[2]);
659 flags
|= CRYPT_ACTIVATE_READONLY
;
662 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
665 until
= now(CLOCK_MONOTONIC
) + arg_timeout
;
669 arg_key_size
= (arg_key_size
> 0 ? arg_key_size
: (256 / 8));
674 /* Ideally we'd do this on the open fd, but since this is just a
675 * warning it's OK to do this in two steps. */
676 if (stat(key_file
, &st
) >= 0 && S_ISREG(st
.st_mode
) && (st
.st_mode
& 0005))
677 log_warning("Key file %s is world-readable. This is not a good idea!", key_file
);
680 for (tries
= 0; arg_tries
== 0 || tries
< arg_tries
; tries
++) {
681 _cleanup_strv_free_erase_
char **passwords
= NULL
;
684 k
= get_password(argv
[2], argv
[3], until
, tries
== 0 && !arg_verify
, &passwords
);
691 if (streq_ptr(arg_type
, CRYPT_TCRYPT
))
692 k
= attach_tcrypt(cd
, argv
[2], key_file
, passwords
, flags
);
694 k
= attach_luks_or_plain(cd
,
697 arg_header
? argv
[3] : NULL
,
702 else if (k
== -EAGAIN
) {
705 } else if (k
!= -EPERM
) {
706 log_error_errno(k
, "Failed to activate: %m");
710 log_warning("Invalid passphrase.");
713 if (arg_tries
!= 0 && tries
>= arg_tries
) {
714 log_error("Too many attempts; giving up.");
719 } else if (streq(argv
[1], "detach")) {
722 k
= crypt_init_by_name(&cd
, argv
[2]);
724 log_error_errno(k
, "crypt_init() failed: %m");
728 crypt_set_log_callback(cd
, log_glue
, NULL
);
730 k
= crypt_deactivate(cd
, argv
[2]);
732 log_error_errno(k
, "Failed to deactivate: %m");
737 log_error("Unknown verb %s.", argv
[1]);
751 strv_free(arg_tcrypt_keyfiles
);