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 "parse-util.h"
36 #include "path-util.h"
37 #include "string-util.h"
41 static const char *arg_type
= NULL
; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
42 static char *arg_cipher
= NULL
;
43 static unsigned arg_key_size
= 0;
44 static int arg_key_slot
= CRYPT_ANY_SLOT
;
45 static unsigned arg_keyfile_size
= 0;
46 static unsigned arg_keyfile_offset
= 0;
47 static char *arg_hash
= NULL
;
48 static char *arg_header
= NULL
;
49 static unsigned arg_tries
= 3;
50 static bool arg_readonly
= false;
51 static bool arg_verify
= false;
52 static bool arg_discards
= false;
53 static bool arg_tcrypt_hidden
= false;
54 static bool arg_tcrypt_system
= false;
55 static char **arg_tcrypt_keyfiles
= NULL
;
56 static uint64_t arg_offset
= 0;
57 static uint64_t arg_skip
= 0;
58 static usec_t arg_timeout
= 0;
60 /* Options Debian's crypttab knows we don't:
70 static int parse_one_option(const char *option
) {
73 /* Handled outside of this tool */
74 if (STR_IN_SET(option
, "noauto", "auto", "nofail", "fail"))
77 if (startswith(option
, "cipher=")) {
87 } else if (startswith(option
, "size=")) {
89 if (safe_atou(option
+5, &arg_key_size
) < 0) {
90 log_error("size= parse failure, ignoring.");
94 if (arg_key_size
% 8) {
95 log_error("size= not a multiple of 8, ignoring.");
101 } else if (startswith(option
, "key-slot=")) {
103 arg_type
= CRYPT_LUKS1
;
104 if (safe_atoi(option
+9, &arg_key_slot
) < 0) {
105 log_error("key-slot= parse failure, ignoring.");
109 } else if (startswith(option
, "tcrypt-keyfile=")) {
111 arg_type
= CRYPT_TCRYPT
;
112 if (path_is_absolute(option
+15)) {
113 if (strv_extend(&arg_tcrypt_keyfiles
, option
+ 15) < 0)
116 log_error("Key file path '%s' is not absolute. Ignoring.", option
+15);
118 } else if (startswith(option
, "keyfile-size=")) {
120 if (safe_atou(option
+13, &arg_keyfile_size
) < 0) {
121 log_error("keyfile-size= parse failure, ignoring.");
125 } else if (startswith(option
, "keyfile-offset=")) {
127 if (safe_atou(option
+15, &arg_keyfile_offset
) < 0) {
128 log_error("keyfile-offset= parse failure, ignoring.");
132 } else if (startswith(option
, "hash=")) {
135 t
= strdup(option
+5);
142 } else if (startswith(option
, "header=")) {
143 arg_type
= CRYPT_LUKS1
;
145 if (!path_is_absolute(option
+7)) {
146 log_error("Header path '%s' is not absolute, refusing.", option
+7);
151 log_error("Duplicate header= options, refusing.");
155 arg_header
= strdup(option
+7);
159 } else if (startswith(option
, "tries=")) {
161 if (safe_atou(option
+6, &arg_tries
) < 0) {
162 log_error("tries= parse failure, ignoring.");
166 } else if (STR_IN_SET(option
, "readonly", "read-only"))
168 else if (streq(option
, "verify"))
170 else if (STR_IN_SET(option
, "allow-discards", "discard"))
172 else if (streq(option
, "luks"))
173 arg_type
= CRYPT_LUKS1
;
174 else if (streq(option
, "tcrypt"))
175 arg_type
= CRYPT_TCRYPT
;
176 else if (streq(option
, "tcrypt-hidden")) {
177 arg_type
= CRYPT_TCRYPT
;
178 arg_tcrypt_hidden
= true;
179 } else if (streq(option
, "tcrypt-system")) {
180 arg_type
= CRYPT_TCRYPT
;
181 arg_tcrypt_system
= true;
182 } else if (STR_IN_SET(option
, "plain", "swap", "tmp"))
183 arg_type
= CRYPT_PLAIN
;
184 else if (startswith(option
, "timeout=")) {
186 if (parse_sec(option
+8, &arg_timeout
) < 0) {
187 log_error("timeout= parse failure, ignoring.");
191 } else if (startswith(option
, "offset=")) {
193 if (safe_atou64(option
+7, &arg_offset
) < 0) {
194 log_error("offset= parse failure, refusing.");
198 } else if (startswith(option
, "skip=")) {
200 if (safe_atou64(option
+5, &arg_skip
) < 0) {
201 log_error("skip= parse failure, refusing.");
205 } else if (!streq(option
, "none"))
206 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
211 static int parse_options(const char *options
) {
212 const char *word
, *state
;
218 FOREACH_WORD_SEPARATOR(word
, l
, options
, ",", state
) {
219 _cleanup_free_
char *o
;
221 o
= strndup(word
, l
);
224 r
= parse_one_option(o
);
229 /* sanity-check options */
230 if (arg_type
!= NULL
&& !streq(arg_type
, CRYPT_PLAIN
)) {
232 log_warning("offset= ignored with type %s", arg_type
);
234 log_warning("skip= ignored with type %s", arg_type
);
240 static void log_glue(int level
, const char *msg
, void *usrptr
) {
241 log_debug("%s", msg
);
244 static int disk_major_minor(const char *path
, char **ret
) {
249 if (stat(path
, &st
) < 0)
252 if (!S_ISBLK(st
.st_mode
))
255 if (asprintf(ret
, "/dev/block/%d:%d", major(st
.st_rdev
), minor(st
.st_rdev
)) < 0)
261 static char* disk_description(const char *path
) {
263 static const char name_fields
[] =
264 "ID_PART_ENTRY_NAME\0"
266 "ID_MODEL_FROM_DATABASE\0"
269 _cleanup_device_unref_ sd_device
*device
= NULL
;
276 if (stat(path
, &st
) < 0)
279 if (!S_ISBLK(st
.st_mode
))
282 r
= sd_device_new_from_devnum(&device
, 'b', st
.st_rdev
);
286 NULSTR_FOREACH(i
, name_fields
) {
289 r
= sd_device_get_property_value(device
, i
, &name
);
290 if (r
>= 0 && !isempty(name
))
297 static char *disk_mount_point(const char *label
) {
298 _cleanup_free_
char *device
= NULL
;
299 _cleanup_endmntent_
FILE *f
= NULL
;
302 /* Yeah, we don't support native systemd unit files here for now */
304 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
307 f
= setmntent("/etc/fstab", "r");
311 while ((m
= getmntent(f
)))
312 if (path_equal(m
->mnt_fsname
, device
))
313 return strdup(m
->mnt_dir
);
318 static int get_password(const char *vol
, const char *src
, usec_t until
, bool accept_cached
, char ***ret
) {
319 _cleanup_free_
char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
, *maj_min
= NULL
, *text
= NULL
, *escaped_name
= NULL
;
320 _cleanup_strv_free_erase_
char **passwords
= NULL
;
321 const char *name
= NULL
;
329 description
= disk_description(src
);
330 mount_point
= disk_mount_point(vol
);
332 if (description
&& streq(vol
, description
))
333 /* If the description string is simply the
334 * volume name, then let's not show this
336 description
= mfree(description
);
338 if (mount_point
&& description
)
339 r
= asprintf(&name_buffer
, "%s (%s) on %s", description
, vol
, mount_point
);
340 else if (mount_point
)
341 r
= asprintf(&name_buffer
, "%s on %s", vol
, mount_point
);
342 else if (description
)
343 r
= asprintf(&name_buffer
, "%s (%s)", description
, vol
);
348 name
= name_buffer
? name_buffer
: vol
;
350 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0)
354 (void) disk_major_minor(src
, &maj_min
);
357 escaped_name
= maj_min
;
360 escaped_name
= cescape(name
);
365 id
= strjoina("cryptsetup:", escaped_name
);
367 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
,
368 ASK_PASSWORD_PUSH_CACHE
| (accept_cached
*ASK_PASSWORD_ACCEPT_CACHED
),
371 return log_error_errno(r
, "Failed to query password: %m");
374 _cleanup_strv_free_erase_
char **passwords2
= NULL
;
376 assert(strv_length(passwords
) == 1);
378 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0)
381 id
= strjoina("cryptsetup-verification:", escaped_name
);
383 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
, ASK_PASSWORD_PUSH_CACHE
, &passwords2
);
385 return log_error_errno(r
, "Failed to query verification password: %m");
387 assert(strv_length(passwords2
) == 1);
389 if (!streq(passwords
[0], passwords2
[0])) {
390 log_warning("Passwords did not match, retrying.");
395 strv_uniq(passwords
);
397 STRV_FOREACH(p
, passwords
) {
400 if (strlen(*p
)+1 >= arg_key_size
)
403 /* Pad password if necessary */
404 c
= new(char, arg_key_size
);
408 strncpy(c
, *p
, arg_key_size
);
419 static int attach_tcrypt(
420 struct crypt_device
*cd
,
422 const char *key_file
,
427 _cleanup_free_
char *passphrase
= NULL
;
428 struct crypt_params_tcrypt params
= {
429 .flags
= CRYPT_TCRYPT_LEGACY_MODES
,
430 .keyfiles
= (const char **)arg_tcrypt_keyfiles
,
431 .keyfiles_count
= strv_length(arg_tcrypt_keyfiles
)
436 assert(key_file
|| (passwords
&& passwords
[0]));
438 if (arg_tcrypt_hidden
)
439 params
.flags
|= CRYPT_TCRYPT_HIDDEN_HEADER
;
441 if (arg_tcrypt_system
)
442 params
.flags
|= CRYPT_TCRYPT_SYSTEM_HEADER
;
445 r
= read_one_line_file(key_file
, &passphrase
);
447 log_error_errno(r
, "Failed to read password file '%s': %m", key_file
);
451 params
.passphrase
= passphrase
;
453 params
.passphrase
= passwords
[0];
454 params
.passphrase_size
= strlen(params
.passphrase
);
456 r
= crypt_load(cd
, CRYPT_TCRYPT
, ¶ms
);
458 if (key_file
&& r
== -EPERM
) {
459 log_error("Failed to activate using password file '%s'.", key_file
);
465 return crypt_activate_by_volume_key(cd
, name
, NULL
, 0, flags
);
468 static int attach_luks_or_plain(struct crypt_device
*cd
,
470 const char *key_file
,
471 const char *data_device
,
475 bool pass_volume_key
= false;
479 assert(key_file
|| passwords
);
481 if (!arg_type
|| streq(arg_type
, CRYPT_LUKS1
)) {
482 r
= crypt_load(cd
, CRYPT_LUKS1
, NULL
);
484 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd
));
489 r
= crypt_set_data_device(cd
, data_device
);
492 if ((!arg_type
&& r
< 0) || streq_ptr(arg_type
, CRYPT_PLAIN
)) {
493 struct crypt_params_plain params
= {
494 .offset
= arg_offset
,
497 const char *cipher
, *cipher_mode
;
498 _cleanup_free_
char *truncated_cipher
= NULL
;
501 /* plain isn't a real hash type. it just means "use no hash" */
502 if (!streq(arg_hash
, "plain"))
503 params
.hash
= arg_hash
;
504 } else if (!key_file
)
505 /* for CRYPT_PLAIN, the behaviour of cryptsetup
506 * package is to not hash when a key file is provided */
507 params
.hash
= "ripemd160";
512 l
= strcspn(arg_cipher
, "-");
513 truncated_cipher
= strndup(arg_cipher
, l
);
514 if (!truncated_cipher
)
517 cipher
= truncated_cipher
;
518 cipher_mode
= arg_cipher
[l
] ? arg_cipher
+l
+1 : "plain";
521 cipher_mode
= "cbc-essiv:sha256";
524 /* for CRYPT_PLAIN limit reads
525 * from keyfile to key length, and
526 * ignore keyfile-size */
527 arg_keyfile_size
= arg_key_size
;
529 /* In contrast to what the name
530 * crypt_setup() might suggest this
531 * doesn't actually format anything,
532 * it just configures encryption
533 * parameters when used for plain
535 r
= crypt_format(cd
, CRYPT_PLAIN
, cipher
, cipher_mode
, NULL
, NULL
, arg_keyfile_size
, ¶ms
);
537 /* hash == NULL implies the user passed "plain" */
538 pass_volume_key
= (params
.hash
== NULL
);
542 return log_error_errno(r
, "Loading of cryptographic parameters failed: %m");
544 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
545 crypt_get_cipher(cd
),
546 crypt_get_cipher_mode(cd
),
547 crypt_get_volume_key_size(cd
)*8,
548 crypt_get_device_name(cd
));
551 r
= crypt_activate_by_keyfile_offset(cd
, name
, arg_key_slot
, key_file
, arg_keyfile_size
, arg_keyfile_offset
, flags
);
553 log_error_errno(r
, "Failed to activate with key file '%s': %m", key_file
);
559 STRV_FOREACH(p
, passwords
) {
561 r
= crypt_activate_by_volume_key(cd
, name
, *p
, arg_key_size
, flags
);
563 r
= crypt_activate_by_passphrase(cd
, name
, arg_key_slot
, *p
, strlen(*p
), flags
);
573 static int help(void) {
575 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
576 "%s detach VOLUME\n\n"
577 "Attaches or detaches an encrypted block device.\n",
578 program_invocation_short_name
,
579 program_invocation_short_name
);
584 int main(int argc
, char *argv
[]) {
585 int r
= EXIT_FAILURE
;
586 struct crypt_device
*cd
= NULL
;
594 log_error("This program requires at least two arguments.");
598 log_set_target(LOG_TARGET_AUTO
);
599 log_parse_environment();
604 if (streq(argv
[1], "attach")) {
609 crypt_status_info status
;
610 const char *key_file
= NULL
;
612 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
615 log_error("attach requires at least two arguments.");
621 !streq(argv
[4], "-") &&
622 !streq(argv
[4], "none")) {
624 if (!path_is_absolute(argv
[4]))
625 log_error("Password file path '%s' is not absolute. Ignoring.", argv
[4]);
630 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-")) {
631 if (parse_options(argv
[5]) < 0)
635 /* A delicious drop of snake oil */
636 mlockall(MCL_FUTURE
);
639 log_debug("LUKS header: %s", arg_header
);
640 k
= crypt_init(&cd
, arg_header
);
642 k
= crypt_init(&cd
, argv
[3]);
644 log_error_errno(k
, "crypt_init() failed: %m");
648 crypt_set_log_callback(cd
, log_glue
, NULL
);
650 status
= crypt_status(cd
, argv
[2]);
651 if (status
== CRYPT_ACTIVE
|| status
== CRYPT_BUSY
) {
652 log_info("Volume %s already active.", argv
[2]);
658 flags
|= CRYPT_ACTIVATE_READONLY
;
661 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
664 until
= now(CLOCK_MONOTONIC
) + arg_timeout
;
668 arg_key_size
= (arg_key_size
> 0 ? arg_key_size
: (256 / 8));
673 /* Ideally we'd do this on the open fd, but since this is just a
674 * warning it's OK to do this in two steps. */
675 if (stat(key_file
, &st
) >= 0 && S_ISREG(st
.st_mode
) && (st
.st_mode
& 0005))
676 log_warning("Key file %s is world-readable. This is not a good idea!", key_file
);
679 for (tries
= 0; arg_tries
== 0 || tries
< arg_tries
; tries
++) {
680 _cleanup_strv_free_erase_
char **passwords
= NULL
;
683 k
= get_password(argv
[2], argv
[3], until
, tries
== 0 && !arg_verify
, &passwords
);
690 if (streq_ptr(arg_type
, CRYPT_TCRYPT
))
691 k
= attach_tcrypt(cd
, argv
[2], key_file
, passwords
, flags
);
693 k
= attach_luks_or_plain(cd
,
696 arg_header
? argv
[3] : NULL
,
701 else if (k
== -EAGAIN
) {
704 } else if (k
!= -EPERM
) {
705 log_error_errno(k
, "Failed to activate: %m");
709 log_warning("Invalid passphrase.");
712 if (arg_tries
!= 0 && tries
>= arg_tries
) {
713 log_error("Too many attempts; giving up.");
718 } else if (streq(argv
[1], "detach")) {
721 k
= crypt_init_by_name(&cd
, argv
[2]);
723 log_error_errno(k
, "crypt_init() failed: %m");
727 crypt_set_log_callback(cd
, log_glue
, NULL
);
729 k
= crypt_deactivate(cd
, argv
[2]);
731 log_error_errno(k
, "Failed to deactivate: %m");
736 log_error("Unknown verb %s.", argv
[1]);
750 strv_free(arg_tcrypt_keyfiles
);