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/>.
27 #include <libcryptsetup.h>
32 #include "path-util.h"
34 #include "ask-password-api.h"
35 #include "sd-device.h"
36 #include "device-util.h"
38 static const char *arg_type
= NULL
; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
39 static char *arg_cipher
= NULL
;
40 static unsigned arg_key_size
= 0;
41 static int arg_key_slot
= CRYPT_ANY_SLOT
;
42 static unsigned arg_keyfile_size
= 0;
43 static unsigned arg_keyfile_offset
= 0;
44 static char *arg_hash
= NULL
;
45 static char *arg_header
= NULL
;
46 static unsigned arg_tries
= 3;
47 static bool arg_readonly
= false;
48 static bool arg_verify
= false;
49 static bool arg_discards
= false;
50 static bool arg_tcrypt_hidden
= false;
51 static bool arg_tcrypt_system
= false;
52 static char **arg_tcrypt_keyfiles
= NULL
;
53 static uint64_t arg_offset
= 0;
54 static uint64_t arg_skip
= 0;
55 static usec_t arg_timeout
= 0;
57 /* Options Debian's crypttab knows we don't:
67 static int parse_one_option(const char *option
) {
70 /* Handled outside of this tool */
71 if (STR_IN_SET(option
, "noauto", "auto", "nofail", "fail"))
74 if (startswith(option
, "cipher=")) {
84 } else if (startswith(option
, "size=")) {
86 if (safe_atou(option
+5, &arg_key_size
) < 0) {
87 log_error("size= parse failure, ignoring.");
91 if (arg_key_size
% 8) {
92 log_error("size= not a multiple of 8, ignoring.");
98 } else if (startswith(option
, "key-slot=")) {
100 arg_type
= CRYPT_LUKS1
;
101 if (safe_atoi(option
+9, &arg_key_slot
) < 0) {
102 log_error("key-slot= parse failure, ignoring.");
106 } else if (startswith(option
, "tcrypt-keyfile=")) {
108 arg_type
= CRYPT_TCRYPT
;
109 if (path_is_absolute(option
+15)) {
110 if (strv_extend(&arg_tcrypt_keyfiles
, option
+ 15) < 0)
113 log_error("Key file path '%s' is not absolute. Ignoring.", option
+15);
115 } else if (startswith(option
, "keyfile-size=")) {
117 if (safe_atou(option
+13, &arg_keyfile_size
) < 0) {
118 log_error("keyfile-size= parse failure, ignoring.");
122 } else if (startswith(option
, "keyfile-offset=")) {
124 if (safe_atou(option
+15, &arg_keyfile_offset
) < 0) {
125 log_error("keyfile-offset= parse failure, ignoring.");
129 } else if (startswith(option
, "hash=")) {
132 t
= strdup(option
+5);
139 } else if (startswith(option
, "header=")) {
140 arg_type
= CRYPT_LUKS1
;
142 if (!path_is_absolute(option
+7)) {
143 log_error("Header path '%s' is not absolute, refusing.", option
+7);
148 log_error("Duplicate header= options, refusing.");
152 arg_header
= strdup(option
+7);
156 } else if (startswith(option
, "tries=")) {
158 if (safe_atou(option
+6, &arg_tries
) < 0) {
159 log_error("tries= parse failure, ignoring.");
163 } else if (STR_IN_SET(option
, "readonly", "read-only"))
165 else if (streq(option
, "verify"))
167 else if (STR_IN_SET(option
, "allow-discards", "discard"))
169 else if (streq(option
, "luks"))
170 arg_type
= CRYPT_LUKS1
;
171 else if (streq(option
, "tcrypt"))
172 arg_type
= CRYPT_TCRYPT
;
173 else if (streq(option
, "tcrypt-hidden")) {
174 arg_type
= CRYPT_TCRYPT
;
175 arg_tcrypt_hidden
= true;
176 } else if (streq(option
, "tcrypt-system")) {
177 arg_type
= CRYPT_TCRYPT
;
178 arg_tcrypt_system
= true;
179 } else if (STR_IN_SET(option
, "plain", "swap", "tmp"))
180 arg_type
= CRYPT_PLAIN
;
181 else if (startswith(option
, "timeout=")) {
183 if (parse_sec(option
+8, &arg_timeout
) < 0) {
184 log_error("timeout= parse failure, ignoring.");
188 } else if (startswith(option
, "offset=")) {
190 if (safe_atou64(option
+7, &arg_offset
) < 0) {
191 log_error("offset= parse failure, refusing.");
195 } else if (startswith(option
, "skip=")) {
197 if (safe_atou64(option
+5, &arg_skip
) < 0) {
198 log_error("skip= parse failure, refusing.");
202 } else if (!streq(option
, "none"))
203 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
208 static int parse_options(const char *options
) {
209 const char *word
, *state
;
215 FOREACH_WORD_SEPARATOR(word
, l
, options
, ",", state
) {
216 _cleanup_free_
char *o
;
218 o
= strndup(word
, l
);
221 r
= parse_one_option(o
);
226 /* sanity-check options */
227 if (arg_type
!= NULL
&& !streq(arg_type
, CRYPT_PLAIN
)) {
229 log_warning("offset= ignored with type %s", arg_type
);
231 log_warning("skip= ignored with type %s", arg_type
);
237 static void log_glue(int level
, const char *msg
, void *usrptr
) {
238 log_debug("%s", msg
);
241 static int disk_major_minor(const char *path
, char **ret
) {
246 if (stat(path
, &st
) < 0)
249 if (!S_ISBLK(st
.st_mode
))
252 if (asprintf(ret
, "/dev/block/%d:%d", major(st
.st_rdev
), minor(st
.st_rdev
)) < 0)
258 static char* disk_description(const char *path
) {
260 static const char name_fields
[] =
261 "ID_PART_ENTRY_NAME\0"
263 "ID_MODEL_FROM_DATABASE\0"
266 _cleanup_device_unref_ sd_device
*device
= NULL
;
273 if (stat(path
, &st
) < 0)
276 if (!S_ISBLK(st
.st_mode
))
279 r
= sd_device_new_from_devnum(&device
, 'b', st
.st_rdev
);
283 NULSTR_FOREACH(i
, name_fields
) {
286 r
= sd_device_get_property_value(device
, i
, &name
);
287 if (r
>= 0 && !isempty(name
))
294 static char *disk_mount_point(const char *label
) {
295 _cleanup_free_
char *device
= NULL
;
296 _cleanup_endmntent_
FILE *f
= NULL
;
299 /* Yeah, we don't support native systemd unit files here for now */
301 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
304 f
= setmntent("/etc/fstab", "r");
308 while ((m
= getmntent(f
)))
309 if (path_equal(m
->mnt_fsname
, device
))
310 return strdup(m
->mnt_dir
);
315 static int get_password(const char *vol
, const char *src
, usec_t until
, bool accept_cached
, char ***ret
) {
316 _cleanup_free_
char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
, *maj_min
= NULL
, *text
= NULL
, *escaped_name
= NULL
;
317 _cleanup_strv_free_
char **passwords
= NULL
, **passwords2
= NULL
;
318 const char *name
= NULL
;
326 description
= disk_description(src
);
327 mount_point
= disk_mount_point(vol
);
329 if (description
&& streq(vol
, description
))
330 /* If the description string is simply the
331 * volume name, then let's not show this
333 description
= mfree(description
);
335 if (mount_point
&& description
)
336 r
= asprintf(&name_buffer
, "%s (%s) on %s", description
, vol
, mount_point
);
337 else if (mount_point
)
338 r
= asprintf(&name_buffer
, "%s on %s", vol
, mount_point
);
339 else if (description
)
340 r
= asprintf(&name_buffer
, "%s (%s)", description
, vol
);
345 name
= name_buffer
? name_buffer
: vol
;
347 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0)
351 (void) disk_major_minor(src
, &maj_min
);
354 escaped_name
= maj_min
;
357 escaped_name
= cescape(name
);
362 id
= strjoina("cryptsetup:", escaped_name
);
364 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
, ASK_PASSWORD_PUSH_CACHE
|(accept_cached
? ASK_PASSWORD_ACCEPT_CACHED
: 0), &passwords
);
366 return log_error_errno(r
, "Failed to query password: %m");
369 assert(strv_length(passwords
) == 1);
371 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0) {
376 id
= strjoina("cryptsetup-verification:", escaped_name
);
378 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
, ASK_PASSWORD_PUSH_CACHE
, &passwords2
);
380 log_error_errno(r
, "Failed to query verification password: %m");
384 assert(strv_length(passwords2
) == 1);
386 if (!streq(passwords
[0], passwords2
[0])) {
387 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
);
408 strncpy(c
, *p
, arg_key_size
);
419 strv_erase(passwords
);
420 strv_erase(passwords2
);
425 static int attach_tcrypt(
426 struct crypt_device
*cd
,
428 const char *key_file
,
433 _cleanup_free_
char *passphrase
= NULL
;
434 struct crypt_params_tcrypt params
= {
435 .flags
= CRYPT_TCRYPT_LEGACY_MODES
,
436 .keyfiles
= (const char **)arg_tcrypt_keyfiles
,
437 .keyfiles_count
= strv_length(arg_tcrypt_keyfiles
)
442 assert(key_file
|| (passwords
&& passwords
[0]));
444 if (arg_tcrypt_hidden
)
445 params
.flags
|= CRYPT_TCRYPT_HIDDEN_HEADER
;
447 if (arg_tcrypt_system
)
448 params
.flags
|= CRYPT_TCRYPT_SYSTEM_HEADER
;
451 r
= read_one_line_file(key_file
, &passphrase
);
453 log_error_errno(r
, "Failed to read password file '%s': %m", key_file
);
457 params
.passphrase
= passphrase
;
459 params
.passphrase
= passwords
[0];
460 params
.passphrase_size
= strlen(params
.passphrase
);
462 r
= crypt_load(cd
, CRYPT_TCRYPT
, ¶ms
);
464 if (key_file
&& r
== -EPERM
) {
465 log_error("Failed to activate using password file '%s'.", key_file
);
471 return crypt_activate_by_volume_key(cd
, name
, NULL
, 0, flags
);
474 static int attach_luks_or_plain(struct crypt_device
*cd
,
476 const char *key_file
,
477 const char *data_device
,
481 bool pass_volume_key
= false;
485 assert(key_file
|| passwords
);
487 if (!arg_type
|| streq(arg_type
, CRYPT_LUKS1
)) {
488 r
= crypt_load(cd
, CRYPT_LUKS1
, NULL
);
490 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd
));
495 r
= crypt_set_data_device(cd
, data_device
);
498 if ((!arg_type
&& r
< 0) || streq_ptr(arg_type
, CRYPT_PLAIN
)) {
499 struct crypt_params_plain params
= {
500 .offset
= arg_offset
,
503 const char *cipher
, *cipher_mode
;
504 _cleanup_free_
char *truncated_cipher
= NULL
;
507 /* plain isn't a real hash type. it just means "use no hash" */
508 if (!streq(arg_hash
, "plain"))
509 params
.hash
= arg_hash
;
510 } else if (!key_file
)
511 /* for CRYPT_PLAIN, the behaviour of cryptsetup
512 * package is to not hash when a key file is provided */
513 params
.hash
= "ripemd160";
518 l
= strcspn(arg_cipher
, "-");
519 truncated_cipher
= strndup(arg_cipher
, l
);
520 if (!truncated_cipher
)
523 cipher
= truncated_cipher
;
524 cipher_mode
= arg_cipher
[l
] ? arg_cipher
+l
+1 : "plain";
527 cipher_mode
= "cbc-essiv:sha256";
530 /* for CRYPT_PLAIN limit reads
531 * from keyfile to key length, and
532 * ignore keyfile-size */
533 arg_keyfile_size
= arg_key_size
;
535 /* In contrast to what the name
536 * crypt_setup() might suggest this
537 * doesn't actually format anything,
538 * it just configures encryption
539 * parameters when used for plain
541 r
= crypt_format(cd
, CRYPT_PLAIN
, cipher
, cipher_mode
, NULL
, NULL
, arg_keyfile_size
, ¶ms
);
543 /* hash == NULL implies the user passed "plain" */
544 pass_volume_key
= (params
.hash
== NULL
);
548 return log_error_errno(r
, "Loading of cryptographic parameters failed: %m");
550 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
551 crypt_get_cipher(cd
),
552 crypt_get_cipher_mode(cd
),
553 crypt_get_volume_key_size(cd
)*8,
554 crypt_get_device_name(cd
));
557 r
= crypt_activate_by_keyfile_offset(cd
, name
, arg_key_slot
, key_file
, arg_keyfile_size
, arg_keyfile_offset
, flags
);
559 log_error_errno(r
, "Failed to activate with key file '%s': %m", key_file
);
565 STRV_FOREACH(p
, passwords
) {
567 r
= crypt_activate_by_volume_key(cd
, name
, *p
, arg_key_size
, flags
);
569 r
= crypt_activate_by_passphrase(cd
, name
, arg_key_slot
, *p
, strlen(*p
), flags
);
579 static int help(void) {
581 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
582 "%s detach VOLUME\n\n"
583 "Attaches or detaches an encrypted block device.\n",
584 program_invocation_short_name
,
585 program_invocation_short_name
);
590 int main(int argc
, char *argv
[]) {
591 int r
= EXIT_FAILURE
;
592 struct crypt_device
*cd
= NULL
;
600 log_error("This program requires at least two arguments.");
604 log_set_target(LOG_TARGET_AUTO
);
605 log_parse_environment();
610 if (streq(argv
[1], "attach")) {
615 crypt_status_info status
;
616 const char *key_file
= NULL
;
618 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
621 log_error("attach requires at least two arguments.");
627 !streq(argv
[4], "-") &&
628 !streq(argv
[4], "none")) {
630 if (!path_is_absolute(argv
[4]))
631 log_error("Password file path '%s' is not absolute. Ignoring.", argv
[4]);
636 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-")) {
637 if (parse_options(argv
[5]) < 0)
641 /* A delicious drop of snake oil */
642 mlockall(MCL_FUTURE
);
645 log_debug("LUKS header: %s", arg_header
);
646 k
= crypt_init(&cd
, arg_header
);
648 k
= crypt_init(&cd
, argv
[3]);
650 log_error_errno(k
, "crypt_init() failed: %m");
654 crypt_set_log_callback(cd
, log_glue
, NULL
);
656 status
= crypt_status(cd
, argv
[2]);
657 if (status
== CRYPT_ACTIVE
|| status
== CRYPT_BUSY
) {
658 log_info("Volume %s already active.", argv
[2]);
664 flags
|= CRYPT_ACTIVATE_READONLY
;
667 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
670 until
= now(CLOCK_MONOTONIC
) + arg_timeout
;
674 arg_key_size
= (arg_key_size
> 0 ? arg_key_size
: (256 / 8));
679 /* Ideally we'd do this on the open fd, but since this is just a
680 * warning it's OK to do this in two steps. */
681 if (stat(key_file
, &st
) >= 0 && S_ISREG(st
.st_mode
) && (st
.st_mode
& 0005))
682 log_warning("Key file %s is world-readable. This is not a good idea!", key_file
);
685 for (tries
= 0; arg_tries
== 0 || tries
< arg_tries
; tries
++) {
686 _cleanup_strv_free_
char **passwords
= NULL
;
689 k
= get_password(argv
[2], argv
[3], until
, tries
== 0 && !arg_verify
, &passwords
);
696 if (streq_ptr(arg_type
, CRYPT_TCRYPT
))
697 k
= attach_tcrypt(cd
, argv
[2], key_file
, passwords
, flags
);
699 k
= attach_luks_or_plain(cd
,
702 arg_header
? argv
[3] : NULL
,
705 strv_erase(passwords
);
708 else if (k
== -EAGAIN
) {
711 } else if (k
!= -EPERM
) {
712 log_error_errno(k
, "Failed to activate: %m");
716 log_warning("Invalid passphrase.");
719 if (arg_tries
!= 0 && tries
>= arg_tries
) {
720 log_error("Too many attempts; giving up.");
725 } else if (streq(argv
[1], "detach")) {
728 k
= crypt_init_by_name(&cd
, argv
[2]);
730 log_error_errno(k
, "crypt_init() failed: %m");
734 crypt_set_log_callback(cd
, log_glue
, NULL
);
736 k
= crypt_deactivate(cd
, argv
[2]);
738 log_error_errno(k
, "Failed to deactivate: %m");
743 log_error("Unknown verb %s.", argv
[1]);
757 strv_free(arg_tcrypt_keyfiles
);