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/>.
26 #include <libcryptsetup.h>
28 #include "sd-device.h"
30 #include "ask-password-api.h"
31 #include "device-util.h"
35 #include "path-util.h"
39 static const char *arg_type
= NULL
; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
40 static char *arg_cipher
= NULL
;
41 static unsigned arg_key_size
= 0;
42 static int arg_key_slot
= CRYPT_ANY_SLOT
;
43 static unsigned arg_keyfile_size
= 0;
44 static unsigned arg_keyfile_offset
= 0;
45 static char *arg_hash
= NULL
;
46 static char *arg_header
= NULL
;
47 static unsigned arg_tries
= 3;
48 static bool arg_readonly
= false;
49 static bool arg_verify
= false;
50 static bool arg_discards
= false;
51 static bool arg_tcrypt_hidden
= false;
52 static bool arg_tcrypt_system
= false;
53 static char **arg_tcrypt_keyfiles
= NULL
;
54 static uint64_t arg_offset
= 0;
55 static uint64_t arg_skip
= 0;
56 static usec_t arg_timeout
= 0;
58 /* Options Debian's crypttab knows we don't:
68 static int parse_one_option(const char *option
) {
71 /* Handled outside of this tool */
72 if (STR_IN_SET(option
, "noauto", "auto", "nofail", "fail"))
75 if (startswith(option
, "cipher=")) {
85 } else if (startswith(option
, "size=")) {
87 if (safe_atou(option
+5, &arg_key_size
) < 0) {
88 log_error("size= parse failure, ignoring.");
92 if (arg_key_size
% 8) {
93 log_error("size= not a multiple of 8, ignoring.");
99 } else if (startswith(option
, "key-slot=")) {
101 arg_type
= CRYPT_LUKS1
;
102 if (safe_atoi(option
+9, &arg_key_slot
) < 0) {
103 log_error("key-slot= parse failure, ignoring.");
107 } else if (startswith(option
, "tcrypt-keyfile=")) {
109 arg_type
= CRYPT_TCRYPT
;
110 if (path_is_absolute(option
+15)) {
111 if (strv_extend(&arg_tcrypt_keyfiles
, option
+ 15) < 0)
114 log_error("Key file path '%s' is not absolute. Ignoring.", option
+15);
116 } else if (startswith(option
, "keyfile-size=")) {
118 if (safe_atou(option
+13, &arg_keyfile_size
) < 0) {
119 log_error("keyfile-size= parse failure, ignoring.");
123 } else if (startswith(option
, "keyfile-offset=")) {
125 if (safe_atou(option
+15, &arg_keyfile_offset
) < 0) {
126 log_error("keyfile-offset= parse failure, ignoring.");
130 } else if (startswith(option
, "hash=")) {
133 t
= strdup(option
+5);
140 } else if (startswith(option
, "header=")) {
141 arg_type
= CRYPT_LUKS1
;
143 if (!path_is_absolute(option
+7)) {
144 log_error("Header path '%s' is not absolute, refusing.", option
+7);
149 log_error("Duplicate header= options, refusing.");
153 arg_header
= strdup(option
+7);
157 } else if (startswith(option
, "tries=")) {
159 if (safe_atou(option
+6, &arg_tries
) < 0) {
160 log_error("tries= parse failure, ignoring.");
164 } else if (STR_IN_SET(option
, "readonly", "read-only"))
166 else if (streq(option
, "verify"))
168 else if (STR_IN_SET(option
, "allow-discards", "discard"))
170 else if (streq(option
, "luks"))
171 arg_type
= CRYPT_LUKS1
;
172 else if (streq(option
, "tcrypt"))
173 arg_type
= CRYPT_TCRYPT
;
174 else if (streq(option
, "tcrypt-hidden")) {
175 arg_type
= CRYPT_TCRYPT
;
176 arg_tcrypt_hidden
= true;
177 } else if (streq(option
, "tcrypt-system")) {
178 arg_type
= CRYPT_TCRYPT
;
179 arg_tcrypt_system
= true;
180 } else if (STR_IN_SET(option
, "plain", "swap", "tmp"))
181 arg_type
= CRYPT_PLAIN
;
182 else if (startswith(option
, "timeout=")) {
184 if (parse_sec(option
+8, &arg_timeout
) < 0) {
185 log_error("timeout= parse failure, ignoring.");
189 } else if (startswith(option
, "offset=")) {
191 if (safe_atou64(option
+7, &arg_offset
) < 0) {
192 log_error("offset= parse failure, refusing.");
196 } else if (startswith(option
, "skip=")) {
198 if (safe_atou64(option
+5, &arg_skip
) < 0) {
199 log_error("skip= parse failure, refusing.");
203 } else if (!streq(option
, "none"))
204 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
209 static int parse_options(const char *options
) {
210 const char *word
, *state
;
216 FOREACH_WORD_SEPARATOR(word
, l
, options
, ",", state
) {
217 _cleanup_free_
char *o
;
219 o
= strndup(word
, l
);
222 r
= parse_one_option(o
);
227 /* sanity-check options */
228 if (arg_type
!= NULL
&& !streq(arg_type
, CRYPT_PLAIN
)) {
230 log_warning("offset= ignored with type %s", arg_type
);
232 log_warning("skip= ignored with type %s", arg_type
);
238 static void log_glue(int level
, const char *msg
, void *usrptr
) {
239 log_debug("%s", msg
);
242 static int disk_major_minor(const char *path
, char **ret
) {
247 if (stat(path
, &st
) < 0)
250 if (!S_ISBLK(st
.st_mode
))
253 if (asprintf(ret
, "/dev/block/%d:%d", major(st
.st_rdev
), minor(st
.st_rdev
)) < 0)
259 static char* disk_description(const char *path
) {
261 static const char name_fields
[] =
262 "ID_PART_ENTRY_NAME\0"
264 "ID_MODEL_FROM_DATABASE\0"
267 _cleanup_device_unref_ sd_device
*device
= NULL
;
274 if (stat(path
, &st
) < 0)
277 if (!S_ISBLK(st
.st_mode
))
280 r
= sd_device_new_from_devnum(&device
, 'b', st
.st_rdev
);
284 NULSTR_FOREACH(i
, name_fields
) {
287 r
= sd_device_get_property_value(device
, i
, &name
);
288 if (r
>= 0 && !isempty(name
))
295 static char *disk_mount_point(const char *label
) {
296 _cleanup_free_
char *device
= NULL
;
297 _cleanup_endmntent_
FILE *f
= NULL
;
300 /* Yeah, we don't support native systemd unit files here for now */
302 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
305 f
= setmntent("/etc/fstab", "r");
309 while ((m
= getmntent(f
)))
310 if (path_equal(m
->mnt_fsname
, device
))
311 return strdup(m
->mnt_dir
);
316 static int get_password(const char *vol
, const char *src
, usec_t until
, bool accept_cached
, char ***ret
) {
317 _cleanup_free_
char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
, *maj_min
= NULL
, *text
= NULL
, *escaped_name
= NULL
;
318 _cleanup_strv_free_erase_
char **passwords
= NULL
;
319 const char *name
= NULL
;
327 description
= disk_description(src
);
328 mount_point
= disk_mount_point(vol
);
330 if (description
&& streq(vol
, description
))
331 /* If the description string is simply the
332 * volume name, then let's not show this
334 description
= mfree(description
);
336 if (mount_point
&& description
)
337 r
= asprintf(&name_buffer
, "%s (%s) on %s", description
, vol
, mount_point
);
338 else if (mount_point
)
339 r
= asprintf(&name_buffer
, "%s on %s", vol
, mount_point
);
340 else if (description
)
341 r
= asprintf(&name_buffer
, "%s (%s)", description
, vol
);
346 name
= name_buffer
? name_buffer
: vol
;
348 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0)
352 (void) disk_major_minor(src
, &maj_min
);
355 escaped_name
= maj_min
;
358 escaped_name
= cescape(name
);
363 id
= strjoina("cryptsetup:", escaped_name
);
365 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
,
366 ASK_PASSWORD_PUSH_CACHE
| (accept_cached
*ASK_PASSWORD_ACCEPT_CACHED
),
369 return log_error_errno(r
, "Failed to query password: %m");
372 _cleanup_strv_free_erase_
char **passwords2
= NULL
;
374 assert(strv_length(passwords
) == 1);
376 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0)
379 id
= strjoina("cryptsetup-verification:", escaped_name
);
381 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
, ASK_PASSWORD_PUSH_CACHE
, &passwords2
);
383 return log_error_errno(r
, "Failed to query verification password: %m");
385 assert(strv_length(passwords2
) == 1);
387 if (!streq(passwords
[0], passwords2
[0])) {
388 log_warning("Passwords did not match, retrying.");
393 strv_uniq(passwords
);
395 STRV_FOREACH(p
, passwords
) {
398 if (strlen(*p
)+1 >= arg_key_size
)
401 /* Pad password if necessary */
402 c
= new(char, arg_key_size
);
406 strncpy(c
, *p
, arg_key_size
);
417 static int attach_tcrypt(
418 struct crypt_device
*cd
,
420 const char *key_file
,
425 _cleanup_free_
char *passphrase
= NULL
;
426 struct crypt_params_tcrypt params
= {
427 .flags
= CRYPT_TCRYPT_LEGACY_MODES
,
428 .keyfiles
= (const char **)arg_tcrypt_keyfiles
,
429 .keyfiles_count
= strv_length(arg_tcrypt_keyfiles
)
434 assert(key_file
|| (passwords
&& passwords
[0]));
436 if (arg_tcrypt_hidden
)
437 params
.flags
|= CRYPT_TCRYPT_HIDDEN_HEADER
;
439 if (arg_tcrypt_system
)
440 params
.flags
|= CRYPT_TCRYPT_SYSTEM_HEADER
;
443 r
= read_one_line_file(key_file
, &passphrase
);
445 log_error_errno(r
, "Failed to read password file '%s': %m", key_file
);
449 params
.passphrase
= passphrase
;
451 params
.passphrase
= passwords
[0];
452 params
.passphrase_size
= strlen(params
.passphrase
);
454 r
= crypt_load(cd
, CRYPT_TCRYPT
, ¶ms
);
456 if (key_file
&& r
== -EPERM
) {
457 log_error("Failed to activate using password file '%s'.", key_file
);
463 return crypt_activate_by_volume_key(cd
, name
, NULL
, 0, flags
);
466 static int attach_luks_or_plain(struct crypt_device
*cd
,
468 const char *key_file
,
469 const char *data_device
,
473 bool pass_volume_key
= false;
477 assert(key_file
|| passwords
);
479 if (!arg_type
|| streq(arg_type
, CRYPT_LUKS1
)) {
480 r
= crypt_load(cd
, CRYPT_LUKS1
, NULL
);
482 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd
));
487 r
= crypt_set_data_device(cd
, data_device
);
490 if ((!arg_type
&& r
< 0) || streq_ptr(arg_type
, CRYPT_PLAIN
)) {
491 struct crypt_params_plain params
= {
492 .offset
= arg_offset
,
495 const char *cipher
, *cipher_mode
;
496 _cleanup_free_
char *truncated_cipher
= NULL
;
499 /* plain isn't a real hash type. it just means "use no hash" */
500 if (!streq(arg_hash
, "plain"))
501 params
.hash
= arg_hash
;
502 } else if (!key_file
)
503 /* for CRYPT_PLAIN, the behaviour of cryptsetup
504 * package is to not hash when a key file is provided */
505 params
.hash
= "ripemd160";
510 l
= strcspn(arg_cipher
, "-");
511 truncated_cipher
= strndup(arg_cipher
, l
);
512 if (!truncated_cipher
)
515 cipher
= truncated_cipher
;
516 cipher_mode
= arg_cipher
[l
] ? arg_cipher
+l
+1 : "plain";
519 cipher_mode
= "cbc-essiv:sha256";
522 /* for CRYPT_PLAIN limit reads
523 * from keyfile to key length, and
524 * ignore keyfile-size */
525 arg_keyfile_size
= arg_key_size
;
527 /* In contrast to what the name
528 * crypt_setup() might suggest this
529 * doesn't actually format anything,
530 * it just configures encryption
531 * parameters when used for plain
533 r
= crypt_format(cd
, CRYPT_PLAIN
, cipher
, cipher_mode
, NULL
, NULL
, arg_keyfile_size
, ¶ms
);
535 /* hash == NULL implies the user passed "plain" */
536 pass_volume_key
= (params
.hash
== NULL
);
540 return log_error_errno(r
, "Loading of cryptographic parameters failed: %m");
542 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
543 crypt_get_cipher(cd
),
544 crypt_get_cipher_mode(cd
),
545 crypt_get_volume_key_size(cd
)*8,
546 crypt_get_device_name(cd
));
549 r
= crypt_activate_by_keyfile_offset(cd
, name
, arg_key_slot
, key_file
, arg_keyfile_size
, arg_keyfile_offset
, flags
);
551 log_error_errno(r
, "Failed to activate with key file '%s': %m", key_file
);
557 STRV_FOREACH(p
, passwords
) {
559 r
= crypt_activate_by_volume_key(cd
, name
, *p
, arg_key_size
, flags
);
561 r
= crypt_activate_by_passphrase(cd
, name
, arg_key_slot
, *p
, strlen(*p
), flags
);
571 static int help(void) {
573 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
574 "%s detach VOLUME\n\n"
575 "Attaches or detaches an encrypted block device.\n",
576 program_invocation_short_name
,
577 program_invocation_short_name
);
582 int main(int argc
, char *argv
[]) {
583 int r
= EXIT_FAILURE
;
584 struct crypt_device
*cd
= NULL
;
592 log_error("This program requires at least two arguments.");
596 log_set_target(LOG_TARGET_AUTO
);
597 log_parse_environment();
602 if (streq(argv
[1], "attach")) {
607 crypt_status_info status
;
608 const char *key_file
= NULL
;
610 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
613 log_error("attach requires at least two arguments.");
619 !streq(argv
[4], "-") &&
620 !streq(argv
[4], "none")) {
622 if (!path_is_absolute(argv
[4]))
623 log_error("Password file path '%s' is not absolute. Ignoring.", argv
[4]);
628 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-")) {
629 if (parse_options(argv
[5]) < 0)
633 /* A delicious drop of snake oil */
634 mlockall(MCL_FUTURE
);
637 log_debug("LUKS header: %s", arg_header
);
638 k
= crypt_init(&cd
, arg_header
);
640 k
= crypt_init(&cd
, argv
[3]);
642 log_error_errno(k
, "crypt_init() failed: %m");
646 crypt_set_log_callback(cd
, log_glue
, NULL
);
648 status
= crypt_status(cd
, argv
[2]);
649 if (status
== CRYPT_ACTIVE
|| status
== CRYPT_BUSY
) {
650 log_info("Volume %s already active.", argv
[2]);
656 flags
|= CRYPT_ACTIVATE_READONLY
;
659 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
662 until
= now(CLOCK_MONOTONIC
) + arg_timeout
;
666 arg_key_size
= (arg_key_size
> 0 ? arg_key_size
: (256 / 8));
671 /* Ideally we'd do this on the open fd, but since this is just a
672 * warning it's OK to do this in two steps. */
673 if (stat(key_file
, &st
) >= 0 && S_ISREG(st
.st_mode
) && (st
.st_mode
& 0005))
674 log_warning("Key file %s is world-readable. This is not a good idea!", key_file
);
677 for (tries
= 0; arg_tries
== 0 || tries
< arg_tries
; tries
++) {
678 _cleanup_strv_free_erase_
char **passwords
= NULL
;
681 k
= get_password(argv
[2], argv
[3], until
, tries
== 0 && !arg_verify
, &passwords
);
688 if (streq_ptr(arg_type
, CRYPT_TCRYPT
))
689 k
= attach_tcrypt(cd
, argv
[2], key_file
, passwords
, flags
);
691 k
= attach_luks_or_plain(cd
,
694 arg_header
? argv
[3] : NULL
,
699 else if (k
== -EAGAIN
) {
702 } else if (k
!= -EPERM
) {
703 log_error_errno(k
, "Failed to activate: %m");
707 log_warning("Invalid passphrase.");
710 if (arg_tries
!= 0 && tries
>= arg_tries
) {
711 log_error("Too many attempts; giving up.");
716 } else if (streq(argv
[1], "detach")) {
719 k
= crypt_init_by_name(&cd
, argv
[2]);
721 log_error_errno(k
, "crypt_init() failed: %m");
725 crypt_set_log_callback(cd
, log_glue
, NULL
);
727 k
= crypt_deactivate(cd
, argv
[2]);
729 log_error_errno(k
, "Failed to deactivate: %m");
734 log_error("Unknown verb %s.", argv
[1]);
748 strv_free(arg_tcrypt_keyfiles
);