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 "alloc-util.h"
31 #include "ask-password-api.h"
32 #include "device-util.h"
36 #include "mount-util.h"
37 #include "parse-util.h"
38 #include "path-util.h"
39 #include "string-util.h"
43 static const char *arg_type
= NULL
; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
44 static char *arg_cipher
= NULL
;
45 static unsigned arg_key_size
= 0;
46 static int arg_key_slot
= CRYPT_ANY_SLOT
;
47 static unsigned arg_keyfile_size
= 0;
48 static unsigned arg_keyfile_offset
= 0;
49 static char *arg_hash
= NULL
;
50 static char *arg_header
= NULL
;
51 static unsigned arg_tries
= 3;
52 static bool arg_readonly
= false;
53 static bool arg_verify
= false;
54 static bool arg_discards
= false;
55 static bool arg_tcrypt_hidden
= false;
56 static bool arg_tcrypt_system
= false;
57 static char **arg_tcrypt_keyfiles
= NULL
;
58 static uint64_t arg_offset
= 0;
59 static uint64_t arg_skip
= 0;
60 static usec_t arg_timeout
= 0;
62 /* Options Debian's crypttab knows we don't:
72 static int parse_one_option(const char *option
) {
75 /* Handled outside of this tool */
76 if (STR_IN_SET(option
, "noauto", "auto", "nofail", "fail"))
79 if (startswith(option
, "cipher=")) {
89 } else if (startswith(option
, "size=")) {
91 if (safe_atou(option
+5, &arg_key_size
) < 0) {
92 log_error("size= parse failure, ignoring.");
96 if (arg_key_size
% 8) {
97 log_error("size= not a multiple of 8, ignoring.");
103 } else if (startswith(option
, "key-slot=")) {
105 arg_type
= CRYPT_LUKS1
;
106 if (safe_atoi(option
+9, &arg_key_slot
) < 0) {
107 log_error("key-slot= parse failure, ignoring.");
111 } else if (startswith(option
, "tcrypt-keyfile=")) {
113 arg_type
= CRYPT_TCRYPT
;
114 if (path_is_absolute(option
+15)) {
115 if (strv_extend(&arg_tcrypt_keyfiles
, option
+ 15) < 0)
118 log_error("Key file path '%s' is not absolute. Ignoring.", option
+15);
120 } else if (startswith(option
, "keyfile-size=")) {
122 if (safe_atou(option
+13, &arg_keyfile_size
) < 0) {
123 log_error("keyfile-size= parse failure, ignoring.");
127 } else if (startswith(option
, "keyfile-offset=")) {
129 if (safe_atou(option
+15, &arg_keyfile_offset
) < 0) {
130 log_error("keyfile-offset= parse failure, ignoring.");
134 } else if (startswith(option
, "hash=")) {
137 t
= strdup(option
+5);
144 } else if (startswith(option
, "header=")) {
145 arg_type
= CRYPT_LUKS1
;
147 if (!path_is_absolute(option
+7)) {
148 log_error("Header path '%s' is not absolute, refusing.", option
+7);
153 log_error("Duplicate header= options, refusing.");
157 arg_header
= strdup(option
+7);
161 } else if (startswith(option
, "tries=")) {
163 if (safe_atou(option
+6, &arg_tries
) < 0) {
164 log_error("tries= parse failure, ignoring.");
168 } else if (STR_IN_SET(option
, "readonly", "read-only"))
170 else if (streq(option
, "verify"))
172 else if (STR_IN_SET(option
, "allow-discards", "discard"))
174 else if (streq(option
, "luks"))
175 arg_type
= CRYPT_LUKS1
;
176 else if (streq(option
, "tcrypt"))
177 arg_type
= CRYPT_TCRYPT
;
178 else if (streq(option
, "tcrypt-hidden")) {
179 arg_type
= CRYPT_TCRYPT
;
180 arg_tcrypt_hidden
= true;
181 } else if (streq(option
, "tcrypt-system")) {
182 arg_type
= CRYPT_TCRYPT
;
183 arg_tcrypt_system
= true;
184 } else if (STR_IN_SET(option
, "plain", "swap", "tmp"))
185 arg_type
= CRYPT_PLAIN
;
186 else if (startswith(option
, "timeout=")) {
188 if (parse_sec(option
+8, &arg_timeout
) < 0) {
189 log_error("timeout= parse failure, ignoring.");
193 } else if (startswith(option
, "offset=")) {
195 if (safe_atou64(option
+7, &arg_offset
) < 0) {
196 log_error("offset= parse failure, refusing.");
200 } else if (startswith(option
, "skip=")) {
202 if (safe_atou64(option
+5, &arg_skip
) < 0) {
203 log_error("skip= parse failure, refusing.");
207 } else if (!streq(option
, "none"))
208 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
213 static int parse_options(const char *options
) {
214 const char *word
, *state
;
220 FOREACH_WORD_SEPARATOR(word
, l
, options
, ",", state
) {
221 _cleanup_free_
char *o
;
223 o
= strndup(word
, l
);
226 r
= parse_one_option(o
);
231 /* sanity-check options */
232 if (arg_type
!= NULL
&& !streq(arg_type
, CRYPT_PLAIN
)) {
234 log_warning("offset= ignored with type %s", arg_type
);
236 log_warning("skip= ignored with type %s", arg_type
);
242 static void log_glue(int level
, const char *msg
, void *usrptr
) {
243 log_debug("%s", msg
);
246 static int disk_major_minor(const char *path
, char **ret
) {
251 if (stat(path
, &st
) < 0)
254 if (!S_ISBLK(st
.st_mode
))
257 if (asprintf(ret
, "/dev/block/%d:%d", major(st
.st_rdev
), minor(st
.st_rdev
)) < 0)
263 static char* disk_description(const char *path
) {
265 static const char name_fields
[] =
266 "ID_PART_ENTRY_NAME\0"
268 "ID_MODEL_FROM_DATABASE\0"
271 _cleanup_device_unref_ sd_device
*device
= NULL
;
278 if (stat(path
, &st
) < 0)
281 if (!S_ISBLK(st
.st_mode
))
284 r
= sd_device_new_from_devnum(&device
, 'b', st
.st_rdev
);
288 NULSTR_FOREACH(i
, name_fields
) {
291 r
= sd_device_get_property_value(device
, i
, &name
);
292 if (r
>= 0 && !isempty(name
))
299 static char *disk_mount_point(const char *label
) {
300 _cleanup_free_
char *device
= NULL
;
301 _cleanup_endmntent_
FILE *f
= NULL
;
304 /* Yeah, we don't support native systemd unit files here for now */
306 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
309 f
= setmntent("/etc/fstab", "r");
313 while ((m
= getmntent(f
)))
314 if (path_equal(m
->mnt_fsname
, device
))
315 return strdup(m
->mnt_dir
);
320 static int get_password(const char *vol
, const char *src
, usec_t until
, bool accept_cached
, char ***ret
) {
321 _cleanup_free_
char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
, *maj_min
= NULL
, *text
= NULL
, *escaped_name
= NULL
;
322 _cleanup_strv_free_erase_
char **passwords
= NULL
;
323 const char *name
= NULL
;
331 description
= disk_description(src
);
332 mount_point
= disk_mount_point(vol
);
334 if (description
&& streq(vol
, description
))
335 /* If the description string is simply the
336 * volume name, then let's not show this
338 description
= mfree(description
);
340 if (mount_point
&& description
)
341 r
= asprintf(&name_buffer
, "%s (%s) on %s", description
, vol
, mount_point
);
342 else if (mount_point
)
343 r
= asprintf(&name_buffer
, "%s on %s", vol
, mount_point
);
344 else if (description
)
345 r
= asprintf(&name_buffer
, "%s (%s)", description
, vol
);
350 name
= name_buffer
? name_buffer
: vol
;
352 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0)
356 (void) disk_major_minor(src
, &maj_min
);
359 escaped_name
= maj_min
;
362 escaped_name
= cescape(name
);
367 id
= strjoina("cryptsetup:", escaped_name
);
369 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
,
370 ASK_PASSWORD_PUSH_CACHE
| (accept_cached
*ASK_PASSWORD_ACCEPT_CACHED
),
373 return log_error_errno(r
, "Failed to query password: %m");
376 _cleanup_strv_free_erase_
char **passwords2
= NULL
;
378 assert(strv_length(passwords
) == 1);
380 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0)
383 id
= strjoina("cryptsetup-verification:", escaped_name
);
385 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
, ASK_PASSWORD_PUSH_CACHE
, &passwords2
);
387 return log_error_errno(r
, "Failed to query verification password: %m");
389 assert(strv_length(passwords2
) == 1);
391 if (!streq(passwords
[0], passwords2
[0])) {
392 log_warning("Passwords did not match, retrying.");
397 strv_uniq(passwords
);
399 STRV_FOREACH(p
, passwords
) {
402 if (strlen(*p
)+1 >= arg_key_size
)
405 /* Pad password if necessary */
406 c
= new(char, arg_key_size
);
410 strncpy(c
, *p
, arg_key_size
);
421 static int attach_tcrypt(
422 struct crypt_device
*cd
,
424 const char *key_file
,
429 _cleanup_free_
char *passphrase
= NULL
;
430 struct crypt_params_tcrypt params
= {
431 .flags
= CRYPT_TCRYPT_LEGACY_MODES
,
432 .keyfiles
= (const char **)arg_tcrypt_keyfiles
,
433 .keyfiles_count
= strv_length(arg_tcrypt_keyfiles
)
438 assert(key_file
|| (passwords
&& passwords
[0]));
440 if (arg_tcrypt_hidden
)
441 params
.flags
|= CRYPT_TCRYPT_HIDDEN_HEADER
;
443 if (arg_tcrypt_system
)
444 params
.flags
|= CRYPT_TCRYPT_SYSTEM_HEADER
;
447 r
= read_one_line_file(key_file
, &passphrase
);
449 log_error_errno(r
, "Failed to read password file '%s': %m", key_file
);
453 params
.passphrase
= passphrase
;
455 params
.passphrase
= passwords
[0];
456 params
.passphrase_size
= strlen(params
.passphrase
);
458 r
= crypt_load(cd
, CRYPT_TCRYPT
, ¶ms
);
460 if (key_file
&& r
== -EPERM
) {
461 log_error("Failed to activate using password file '%s'.", key_file
);
467 return crypt_activate_by_volume_key(cd
, name
, NULL
, 0, flags
);
470 static int attach_luks_or_plain(struct crypt_device
*cd
,
472 const char *key_file
,
473 const char *data_device
,
477 bool pass_volume_key
= false;
481 assert(key_file
|| passwords
);
483 if (!arg_type
|| streq(arg_type
, CRYPT_LUKS1
)) {
484 r
= crypt_load(cd
, CRYPT_LUKS1
, NULL
);
486 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd
));
491 r
= crypt_set_data_device(cd
, data_device
);
494 if ((!arg_type
&& r
< 0) || streq_ptr(arg_type
, CRYPT_PLAIN
)) {
495 struct crypt_params_plain params
= {
496 .offset
= arg_offset
,
499 const char *cipher
, *cipher_mode
;
500 _cleanup_free_
char *truncated_cipher
= NULL
;
503 /* plain isn't a real hash type. it just means "use no hash" */
504 if (!streq(arg_hash
, "plain"))
505 params
.hash
= arg_hash
;
506 } else if (!key_file
)
507 /* for CRYPT_PLAIN, the behaviour of cryptsetup
508 * package is to not hash when a key file is provided */
509 params
.hash
= "ripemd160";
514 l
= strcspn(arg_cipher
, "-");
515 truncated_cipher
= strndup(arg_cipher
, l
);
516 if (!truncated_cipher
)
519 cipher
= truncated_cipher
;
520 cipher_mode
= arg_cipher
[l
] ? arg_cipher
+l
+1 : "plain";
523 cipher_mode
= "cbc-essiv:sha256";
526 /* for CRYPT_PLAIN limit reads
527 * from keyfile to key length, and
528 * ignore keyfile-size */
529 arg_keyfile_size
= arg_key_size
;
531 /* In contrast to what the name
532 * crypt_setup() might suggest this
533 * doesn't actually format anything,
534 * it just configures encryption
535 * parameters when used for plain
537 r
= crypt_format(cd
, CRYPT_PLAIN
, cipher
, cipher_mode
, NULL
, NULL
, arg_keyfile_size
, ¶ms
);
539 /* hash == NULL implies the user passed "plain" */
540 pass_volume_key
= (params
.hash
== NULL
);
544 return log_error_errno(r
, "Loading of cryptographic parameters failed: %m");
546 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
547 crypt_get_cipher(cd
),
548 crypt_get_cipher_mode(cd
),
549 crypt_get_volume_key_size(cd
)*8,
550 crypt_get_device_name(cd
));
553 r
= crypt_activate_by_keyfile_offset(cd
, name
, arg_key_slot
, key_file
, arg_keyfile_size
, arg_keyfile_offset
, flags
);
555 log_error_errno(r
, "Failed to activate with key file '%s': %m", key_file
);
561 STRV_FOREACH(p
, passwords
) {
563 r
= crypt_activate_by_volume_key(cd
, name
, *p
, arg_key_size
, flags
);
565 r
= crypt_activate_by_passphrase(cd
, name
, arg_key_slot
, *p
, strlen(*p
), flags
);
575 static int help(void) {
577 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
578 "%s detach VOLUME\n\n"
579 "Attaches or detaches an encrypted block device.\n",
580 program_invocation_short_name
,
581 program_invocation_short_name
);
586 int main(int argc
, char *argv
[]) {
587 int r
= EXIT_FAILURE
;
588 struct crypt_device
*cd
= NULL
;
596 log_error("This program requires at least two arguments.");
600 log_set_target(LOG_TARGET_AUTO
);
601 log_parse_environment();
606 if (streq(argv
[1], "attach")) {
611 crypt_status_info status
;
612 const char *key_file
= NULL
;
614 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
617 log_error("attach requires at least two arguments.");
623 !streq(argv
[4], "-") &&
624 !streq(argv
[4], "none")) {
626 if (!path_is_absolute(argv
[4]))
627 log_error("Password file path '%s' is not absolute. Ignoring.", argv
[4]);
632 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-")) {
633 if (parse_options(argv
[5]) < 0)
637 /* A delicious drop of snake oil */
638 mlockall(MCL_FUTURE
);
641 log_debug("LUKS header: %s", arg_header
);
642 k
= crypt_init(&cd
, arg_header
);
644 k
= crypt_init(&cd
, argv
[3]);
646 log_error_errno(k
, "crypt_init() failed: %m");
650 crypt_set_log_callback(cd
, log_glue
, NULL
);
652 status
= crypt_status(cd
, argv
[2]);
653 if (status
== CRYPT_ACTIVE
|| status
== CRYPT_BUSY
) {
654 log_info("Volume %s already active.", argv
[2]);
660 flags
|= CRYPT_ACTIVATE_READONLY
;
663 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
666 until
= now(CLOCK_MONOTONIC
) + arg_timeout
;
670 arg_key_size
= (arg_key_size
> 0 ? arg_key_size
: (256 / 8));
675 /* Ideally we'd do this on the open fd, but since this is just a
676 * warning it's OK to do this in two steps. */
677 if (stat(key_file
, &st
) >= 0 && S_ISREG(st
.st_mode
) && (st
.st_mode
& 0005))
678 log_warning("Key file %s is world-readable. This is not a good idea!", key_file
);
681 for (tries
= 0; arg_tries
== 0 || tries
< arg_tries
; tries
++) {
682 _cleanup_strv_free_erase_
char **passwords
= NULL
;
685 k
= get_password(argv
[2], argv
[3], until
, tries
== 0 && !arg_verify
, &passwords
);
692 if (streq_ptr(arg_type
, CRYPT_TCRYPT
))
693 k
= attach_tcrypt(cd
, argv
[2], key_file
, passwords
, flags
);
695 k
= attach_luks_or_plain(cd
,
698 arg_header
? argv
[3] : NULL
,
703 else if (k
== -EAGAIN
) {
706 } else if (k
!= -EPERM
) {
707 log_error_errno(k
, "Failed to activate: %m");
711 log_warning("Invalid passphrase.");
714 if (arg_tries
!= 0 && tries
>= arg_tries
) {
715 log_error("Too many attempts; giving up.");
720 } else if (streq(argv
[1], "detach")) {
723 k
= crypt_init_by_name(&cd
, argv
[2]);
725 log_error_errno(k
, "crypt_init() failed: %m");
729 crypt_set_log_callback(cd
, log_glue
, NULL
);
731 k
= crypt_deactivate(cd
, argv
[2]);
733 log_error_errno(k
, "Failed to deactivate: %m");
738 log_error("Unknown verb %s.", argv
[1]);
752 strv_free(arg_tcrypt_keyfiles
);