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 "path-util.h"
36 #include "string-util.h"
40 static const char *arg_type
= NULL
; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
41 static char *arg_cipher
= NULL
;
42 static unsigned arg_key_size
= 0;
43 static int arg_key_slot
= CRYPT_ANY_SLOT
;
44 static unsigned arg_keyfile_size
= 0;
45 static unsigned arg_keyfile_offset
= 0;
46 static char *arg_hash
= NULL
;
47 static char *arg_header
= NULL
;
48 static unsigned arg_tries
= 3;
49 static bool arg_readonly
= false;
50 static bool arg_verify
= false;
51 static bool arg_discards
= false;
52 static bool arg_tcrypt_hidden
= false;
53 static bool arg_tcrypt_system
= false;
54 static char **arg_tcrypt_keyfiles
= NULL
;
55 static uint64_t arg_offset
= 0;
56 static uint64_t arg_skip
= 0;
57 static usec_t arg_timeout
= 0;
59 /* Options Debian's crypttab knows we don't:
69 static int parse_one_option(const char *option
) {
72 /* Handled outside of this tool */
73 if (STR_IN_SET(option
, "noauto", "auto", "nofail", "fail"))
76 if (startswith(option
, "cipher=")) {
86 } else if (startswith(option
, "size=")) {
88 if (safe_atou(option
+5, &arg_key_size
) < 0) {
89 log_error("size= parse failure, ignoring.");
93 if (arg_key_size
% 8) {
94 log_error("size= not a multiple of 8, ignoring.");
100 } else if (startswith(option
, "key-slot=")) {
102 arg_type
= CRYPT_LUKS1
;
103 if (safe_atoi(option
+9, &arg_key_slot
) < 0) {
104 log_error("key-slot= parse failure, ignoring.");
108 } else if (startswith(option
, "tcrypt-keyfile=")) {
110 arg_type
= CRYPT_TCRYPT
;
111 if (path_is_absolute(option
+15)) {
112 if (strv_extend(&arg_tcrypt_keyfiles
, option
+ 15) < 0)
115 log_error("Key file path '%s' is not absolute. Ignoring.", option
+15);
117 } else if (startswith(option
, "keyfile-size=")) {
119 if (safe_atou(option
+13, &arg_keyfile_size
) < 0) {
120 log_error("keyfile-size= parse failure, ignoring.");
124 } else if (startswith(option
, "keyfile-offset=")) {
126 if (safe_atou(option
+15, &arg_keyfile_offset
) < 0) {
127 log_error("keyfile-offset= parse failure, ignoring.");
131 } else if (startswith(option
, "hash=")) {
134 t
= strdup(option
+5);
141 } else if (startswith(option
, "header=")) {
142 arg_type
= CRYPT_LUKS1
;
144 if (!path_is_absolute(option
+7)) {
145 log_error("Header path '%s' is not absolute, refusing.", option
+7);
150 log_error("Duplicate header= options, refusing.");
154 arg_header
= strdup(option
+7);
158 } else if (startswith(option
, "tries=")) {
160 if (safe_atou(option
+6, &arg_tries
) < 0) {
161 log_error("tries= parse failure, ignoring.");
165 } else if (STR_IN_SET(option
, "readonly", "read-only"))
167 else if (streq(option
, "verify"))
169 else if (STR_IN_SET(option
, "allow-discards", "discard"))
171 else if (streq(option
, "luks"))
172 arg_type
= CRYPT_LUKS1
;
173 else if (streq(option
, "tcrypt"))
174 arg_type
= CRYPT_TCRYPT
;
175 else if (streq(option
, "tcrypt-hidden")) {
176 arg_type
= CRYPT_TCRYPT
;
177 arg_tcrypt_hidden
= true;
178 } else if (streq(option
, "tcrypt-system")) {
179 arg_type
= CRYPT_TCRYPT
;
180 arg_tcrypt_system
= true;
181 } else if (STR_IN_SET(option
, "plain", "swap", "tmp"))
182 arg_type
= CRYPT_PLAIN
;
183 else if (startswith(option
, "timeout=")) {
185 if (parse_sec(option
+8, &arg_timeout
) < 0) {
186 log_error("timeout= parse failure, ignoring.");
190 } else if (startswith(option
, "offset=")) {
192 if (safe_atou64(option
+7, &arg_offset
) < 0) {
193 log_error("offset= parse failure, refusing.");
197 } else if (startswith(option
, "skip=")) {
199 if (safe_atou64(option
+5, &arg_skip
) < 0) {
200 log_error("skip= parse failure, refusing.");
204 } else if (!streq(option
, "none"))
205 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
210 static int parse_options(const char *options
) {
211 const char *word
, *state
;
217 FOREACH_WORD_SEPARATOR(word
, l
, options
, ",", state
) {
218 _cleanup_free_
char *o
;
220 o
= strndup(word
, l
);
223 r
= parse_one_option(o
);
228 /* sanity-check options */
229 if (arg_type
!= NULL
&& !streq(arg_type
, CRYPT_PLAIN
)) {
231 log_warning("offset= ignored with type %s", arg_type
);
233 log_warning("skip= ignored with type %s", arg_type
);
239 static void log_glue(int level
, const char *msg
, void *usrptr
) {
240 log_debug("%s", msg
);
243 static int disk_major_minor(const char *path
, char **ret
) {
248 if (stat(path
, &st
) < 0)
251 if (!S_ISBLK(st
.st_mode
))
254 if (asprintf(ret
, "/dev/block/%d:%d", major(st
.st_rdev
), minor(st
.st_rdev
)) < 0)
260 static char* disk_description(const char *path
) {
262 static const char name_fields
[] =
263 "ID_PART_ENTRY_NAME\0"
265 "ID_MODEL_FROM_DATABASE\0"
268 _cleanup_device_unref_ sd_device
*device
= NULL
;
275 if (stat(path
, &st
) < 0)
278 if (!S_ISBLK(st
.st_mode
))
281 r
= sd_device_new_from_devnum(&device
, 'b', st
.st_rdev
);
285 NULSTR_FOREACH(i
, name_fields
) {
288 r
= sd_device_get_property_value(device
, i
, &name
);
289 if (r
>= 0 && !isempty(name
))
296 static char *disk_mount_point(const char *label
) {
297 _cleanup_free_
char *device
= NULL
;
298 _cleanup_endmntent_
FILE *f
= NULL
;
301 /* Yeah, we don't support native systemd unit files here for now */
303 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
306 f
= setmntent("/etc/fstab", "r");
310 while ((m
= getmntent(f
)))
311 if (path_equal(m
->mnt_fsname
, device
))
312 return strdup(m
->mnt_dir
);
317 static int get_password(const char *vol
, const char *src
, usec_t until
, bool accept_cached
, char ***ret
) {
318 _cleanup_free_
char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
, *maj_min
= NULL
, *text
= NULL
, *escaped_name
= NULL
;
319 _cleanup_strv_free_erase_
char **passwords
= NULL
;
320 const char *name
= NULL
;
328 description
= disk_description(src
);
329 mount_point
= disk_mount_point(vol
);
331 if (description
&& streq(vol
, description
))
332 /* If the description string is simply the
333 * volume name, then let's not show this
335 description
= mfree(description
);
337 if (mount_point
&& description
)
338 r
= asprintf(&name_buffer
, "%s (%s) on %s", description
, vol
, mount_point
);
339 else if (mount_point
)
340 r
= asprintf(&name_buffer
, "%s on %s", vol
, mount_point
);
341 else if (description
)
342 r
= asprintf(&name_buffer
, "%s (%s)", description
, vol
);
347 name
= name_buffer
? name_buffer
: vol
;
349 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0)
353 (void) disk_major_minor(src
, &maj_min
);
356 escaped_name
= maj_min
;
359 escaped_name
= cescape(name
);
364 id
= strjoina("cryptsetup:", escaped_name
);
366 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
,
367 ASK_PASSWORD_PUSH_CACHE
| (accept_cached
*ASK_PASSWORD_ACCEPT_CACHED
),
370 return log_error_errno(r
, "Failed to query password: %m");
373 _cleanup_strv_free_erase_
char **passwords2
= NULL
;
375 assert(strv_length(passwords
) == 1);
377 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0)
380 id
= strjoina("cryptsetup-verification:", escaped_name
);
382 r
= ask_password_auto(text
, "drive-harddisk", id
, "cryptsetup", until
, ASK_PASSWORD_PUSH_CACHE
, &passwords2
);
384 return log_error_errno(r
, "Failed to query verification password: %m");
386 assert(strv_length(passwords2
) == 1);
388 if (!streq(passwords
[0], passwords2
[0])) {
389 log_warning("Passwords did not match, retrying.");
394 strv_uniq(passwords
);
396 STRV_FOREACH(p
, passwords
) {
399 if (strlen(*p
)+1 >= arg_key_size
)
402 /* Pad password if necessary */
403 c
= new(char, arg_key_size
);
407 strncpy(c
, *p
, arg_key_size
);
418 static int attach_tcrypt(
419 struct crypt_device
*cd
,
421 const char *key_file
,
426 _cleanup_free_
char *passphrase
= NULL
;
427 struct crypt_params_tcrypt params
= {
428 .flags
= CRYPT_TCRYPT_LEGACY_MODES
,
429 .keyfiles
= (const char **)arg_tcrypt_keyfiles
,
430 .keyfiles_count
= strv_length(arg_tcrypt_keyfiles
)
435 assert(key_file
|| (passwords
&& passwords
[0]));
437 if (arg_tcrypt_hidden
)
438 params
.flags
|= CRYPT_TCRYPT_HIDDEN_HEADER
;
440 if (arg_tcrypt_system
)
441 params
.flags
|= CRYPT_TCRYPT_SYSTEM_HEADER
;
444 r
= read_one_line_file(key_file
, &passphrase
);
446 log_error_errno(r
, "Failed to read password file '%s': %m", key_file
);
450 params
.passphrase
= passphrase
;
452 params
.passphrase
= passwords
[0];
453 params
.passphrase_size
= strlen(params
.passphrase
);
455 r
= crypt_load(cd
, CRYPT_TCRYPT
, ¶ms
);
457 if (key_file
&& r
== -EPERM
) {
458 log_error("Failed to activate using password file '%s'.", key_file
);
464 return crypt_activate_by_volume_key(cd
, name
, NULL
, 0, flags
);
467 static int attach_luks_or_plain(struct crypt_device
*cd
,
469 const char *key_file
,
470 const char *data_device
,
474 bool pass_volume_key
= false;
478 assert(key_file
|| passwords
);
480 if (!arg_type
|| streq(arg_type
, CRYPT_LUKS1
)) {
481 r
= crypt_load(cd
, CRYPT_LUKS1
, NULL
);
483 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd
));
488 r
= crypt_set_data_device(cd
, data_device
);
491 if ((!arg_type
&& r
< 0) || streq_ptr(arg_type
, CRYPT_PLAIN
)) {
492 struct crypt_params_plain params
= {
493 .offset
= arg_offset
,
496 const char *cipher
, *cipher_mode
;
497 _cleanup_free_
char *truncated_cipher
= NULL
;
500 /* plain isn't a real hash type. it just means "use no hash" */
501 if (!streq(arg_hash
, "plain"))
502 params
.hash
= arg_hash
;
503 } else if (!key_file
)
504 /* for CRYPT_PLAIN, the behaviour of cryptsetup
505 * package is to not hash when a key file is provided */
506 params
.hash
= "ripemd160";
511 l
= strcspn(arg_cipher
, "-");
512 truncated_cipher
= strndup(arg_cipher
, l
);
513 if (!truncated_cipher
)
516 cipher
= truncated_cipher
;
517 cipher_mode
= arg_cipher
[l
] ? arg_cipher
+l
+1 : "plain";
520 cipher_mode
= "cbc-essiv:sha256";
523 /* for CRYPT_PLAIN limit reads
524 * from keyfile to key length, and
525 * ignore keyfile-size */
526 arg_keyfile_size
= arg_key_size
;
528 /* In contrast to what the name
529 * crypt_setup() might suggest this
530 * doesn't actually format anything,
531 * it just configures encryption
532 * parameters when used for plain
534 r
= crypt_format(cd
, CRYPT_PLAIN
, cipher
, cipher_mode
, NULL
, NULL
, arg_keyfile_size
, ¶ms
);
536 /* hash == NULL implies the user passed "plain" */
537 pass_volume_key
= (params
.hash
== NULL
);
541 return log_error_errno(r
, "Loading of cryptographic parameters failed: %m");
543 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
544 crypt_get_cipher(cd
),
545 crypt_get_cipher_mode(cd
),
546 crypt_get_volume_key_size(cd
)*8,
547 crypt_get_device_name(cd
));
550 r
= crypt_activate_by_keyfile_offset(cd
, name
, arg_key_slot
, key_file
, arg_keyfile_size
, arg_keyfile_offset
, flags
);
552 log_error_errno(r
, "Failed to activate with key file '%s': %m", key_file
);
558 STRV_FOREACH(p
, passwords
) {
560 r
= crypt_activate_by_volume_key(cd
, name
, *p
, arg_key_size
, flags
);
562 r
= crypt_activate_by_passphrase(cd
, name
, arg_key_slot
, *p
, strlen(*p
), flags
);
572 static int help(void) {
574 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
575 "%s detach VOLUME\n\n"
576 "Attaches or detaches an encrypted block device.\n",
577 program_invocation_short_name
,
578 program_invocation_short_name
);
583 int main(int argc
, char *argv
[]) {
584 int r
= EXIT_FAILURE
;
585 struct crypt_device
*cd
= NULL
;
593 log_error("This program requires at least two arguments.");
597 log_set_target(LOG_TARGET_AUTO
);
598 log_parse_environment();
603 if (streq(argv
[1], "attach")) {
608 crypt_status_info status
;
609 const char *key_file
= NULL
;
611 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
614 log_error("attach requires at least two arguments.");
620 !streq(argv
[4], "-") &&
621 !streq(argv
[4], "none")) {
623 if (!path_is_absolute(argv
[4]))
624 log_error("Password file path '%s' is not absolute. Ignoring.", argv
[4]);
629 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-")) {
630 if (parse_options(argv
[5]) < 0)
634 /* A delicious drop of snake oil */
635 mlockall(MCL_FUTURE
);
638 log_debug("LUKS header: %s", arg_header
);
639 k
= crypt_init(&cd
, arg_header
);
641 k
= crypt_init(&cd
, argv
[3]);
643 log_error_errno(k
, "crypt_init() failed: %m");
647 crypt_set_log_callback(cd
, log_glue
, NULL
);
649 status
= crypt_status(cd
, argv
[2]);
650 if (status
== CRYPT_ACTIVE
|| status
== CRYPT_BUSY
) {
651 log_info("Volume %s already active.", argv
[2]);
657 flags
|= CRYPT_ACTIVATE_READONLY
;
660 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
663 until
= now(CLOCK_MONOTONIC
) + arg_timeout
;
667 arg_key_size
= (arg_key_size
> 0 ? arg_key_size
: (256 / 8));
672 /* Ideally we'd do this on the open fd, but since this is just a
673 * warning it's OK to do this in two steps. */
674 if (stat(key_file
, &st
) >= 0 && S_ISREG(st
.st_mode
) && (st
.st_mode
& 0005))
675 log_warning("Key file %s is world-readable. This is not a good idea!", key_file
);
678 for (tries
= 0; arg_tries
== 0 || tries
< arg_tries
; tries
++) {
679 _cleanup_strv_free_erase_
char **passwords
= NULL
;
682 k
= get_password(argv
[2], argv
[3], until
, tries
== 0 && !arg_verify
, &passwords
);
689 if (streq_ptr(arg_type
, CRYPT_TCRYPT
))
690 k
= attach_tcrypt(cd
, argv
[2], key_file
, passwords
, flags
);
692 k
= attach_luks_or_plain(cd
,
695 arg_header
? argv
[3] : NULL
,
700 else if (k
== -EAGAIN
) {
703 } else if (k
!= -EPERM
) {
704 log_error_errno(k
, "Failed to activate: %m");
708 log_warning("Invalid passphrase.");
711 if (arg_tries
!= 0 && tries
>= arg_tries
) {
712 log_error("Too many attempts; giving up.");
717 } else if (streq(argv
[1], "detach")) {
720 k
= crypt_init_by_name(&cd
, argv
[2]);
722 log_error_errno(k
, "crypt_init() failed: %m");
726 crypt_set_log_callback(cd
, log_glue
, NULL
);
728 k
= crypt_deactivate(cd
, argv
[2]);
730 log_error_errno(k
, "Failed to deactivate: %m");
735 log_error("Unknown verb %s.", argv
[1]);
749 strv_free(arg_tcrypt_keyfiles
);