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"
37 #include "udev-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 usec_t arg_timeout
= 0;
56 /* 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 (!streq(option
, "none"))
190 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
195 static int parse_options(const char *options
) {
196 const char *word
, *state
;
202 FOREACH_WORD_SEPARATOR(word
, l
, options
, ",", state
) {
203 _cleanup_free_
char *o
;
205 o
= strndup(word
, l
);
208 r
= parse_one_option(o
);
216 static void log_glue(int level
, const char *msg
, void *usrptr
) {
217 log_debug("%s", msg
);
220 static char* disk_description(const char *path
) {
222 static const char name_fields
[] =
223 "ID_PART_ENTRY_NAME\0"
225 "ID_MODEL_FROM_DATABASE\0"
228 _cleanup_udev_unref_
struct udev
*udev
= NULL
;
229 _cleanup_udev_device_unref_
struct udev_device
*device
= NULL
;
235 if (stat(path
, &st
) < 0)
238 if (!S_ISBLK(st
.st_mode
))
245 device
= udev_device_new_from_devnum(udev
, 'b', st
.st_rdev
);
249 NULSTR_FOREACH(i
, name_fields
) {
252 name
= udev_device_get_property_value(device
, i
);
260 static char *disk_mount_point(const char *label
) {
261 _cleanup_free_
char *device
= NULL
;
262 _cleanup_endmntent_
FILE *f
= NULL
;
265 /* Yeah, we don't support native systemd unit files here for now */
267 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
270 f
= setmntent("/etc/fstab", "r");
274 while ((m
= getmntent(f
)))
275 if (path_equal(m
->mnt_fsname
, device
))
276 return strdup(m
->mnt_dir
);
281 static int get_password(const char *name
, usec_t until
, bool accept_cached
, char ***passwords
) {
284 _cleanup_free_
char *text
= NULL
;
285 _cleanup_free_
char *escaped_name
= NULL
;
291 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0)
294 escaped_name
= cescape(name
);
298 id
= strjoina("cryptsetup:", escaped_name
);
300 r
= ask_password_auto(text
, "drive-harddisk", id
, until
, accept_cached
, passwords
);
302 return log_error_errno(r
, "Failed to query password: %m");
305 _cleanup_strv_free_
char **passwords2
= NULL
;
307 assert(strv_length(*passwords
) == 1);
309 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0)
312 id
= strjoina("cryptsetup-verification:", escaped_name
);
314 r
= ask_password_auto(text
, "drive-harddisk", id
, until
, false, &passwords2
);
316 return log_error_errno(r
, "Failed to query verification password: %m");
318 assert(strv_length(passwords2
) == 1);
320 if (!streq(*passwords
[0], passwords2
[0])) {
321 log_warning("Passwords did not match, retrying.");
326 strv_uniq(*passwords
);
328 STRV_FOREACH(p
, *passwords
) {
331 if (strlen(*p
)+1 >= arg_key_size
)
334 /* Pad password if necessary */
335 if (!(c
= new(char, arg_key_size
)))
338 strncpy(c
, *p
, arg_key_size
);
346 static int attach_tcrypt(struct crypt_device
*cd
,
348 const char *key_file
,
352 _cleanup_free_
char *passphrase
= NULL
;
353 struct crypt_params_tcrypt params
= {
354 .flags
= CRYPT_TCRYPT_LEGACY_MODES
,
355 .keyfiles
= (const char **)arg_tcrypt_keyfiles
,
356 .keyfiles_count
= strv_length(arg_tcrypt_keyfiles
)
361 assert(key_file
|| (passwords
&& passwords
[0]));
363 if (arg_tcrypt_hidden
)
364 params
.flags
|= CRYPT_TCRYPT_HIDDEN_HEADER
;
366 if (arg_tcrypt_system
)
367 params
.flags
|= CRYPT_TCRYPT_SYSTEM_HEADER
;
370 r
= read_one_line_file(key_file
, &passphrase
);
372 log_error_errno(r
, "Failed to read password file '%s': %m", key_file
);
376 params
.passphrase
= passphrase
;
378 params
.passphrase
= passwords
[0];
379 params
.passphrase_size
= strlen(params
.passphrase
);
381 r
= crypt_load(cd
, CRYPT_TCRYPT
, ¶ms
);
383 if (key_file
&& r
== -EPERM
) {
384 log_error("Failed to activate using password file '%s'.", key_file
);
390 return crypt_activate_by_volume_key(cd
, name
, NULL
, 0, flags
);
393 static int attach_luks_or_plain(struct crypt_device
*cd
,
395 const char *key_file
,
396 const char *data_device
,
400 bool pass_volume_key
= false;
404 assert(key_file
|| passwords
);
406 if (!arg_type
|| streq(arg_type
, CRYPT_LUKS1
)) {
407 r
= crypt_load(cd
, CRYPT_LUKS1
, NULL
);
409 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd
));
414 r
= crypt_set_data_device(cd
, data_device
);
417 if ((!arg_type
&& r
< 0) || streq_ptr(arg_type
, CRYPT_PLAIN
)) {
418 struct crypt_params_plain params
= {};
419 const char *cipher
, *cipher_mode
;
420 _cleanup_free_
char *truncated_cipher
= NULL
;
423 /* plain isn't a real hash type. it just means "use no hash" */
424 if (!streq(arg_hash
, "plain"))
425 params
.hash
= arg_hash
;
426 } else if (!key_file
)
427 /* for CRYPT_PLAIN, the behaviour of cryptsetup
428 * package is to not hash when a key file is provided */
429 params
.hash
= "ripemd160";
434 l
= strcspn(arg_cipher
, "-");
435 truncated_cipher
= strndup(arg_cipher
, l
);
436 if (!truncated_cipher
)
439 cipher
= truncated_cipher
;
440 cipher_mode
= arg_cipher
[l
] ? arg_cipher
+l
+1 : "plain";
443 cipher_mode
= "cbc-essiv:sha256";
446 /* for CRYPT_PLAIN limit reads
447 * from keyfile to key length, and
448 * ignore keyfile-size */
449 arg_keyfile_size
= arg_key_size
;
451 /* In contrast to what the name
452 * crypt_setup() might suggest this
453 * doesn't actually format anything,
454 * it just configures encryption
455 * parameters when used for plain
457 r
= crypt_format(cd
, CRYPT_PLAIN
, cipher
, cipher_mode
,
458 NULL
, NULL
, arg_keyfile_size
, ¶ms
);
460 /* hash == NULL implies the user passed "plain" */
461 pass_volume_key
= (params
.hash
== NULL
);
465 return log_error_errno(r
, "Loading of cryptographic parameters failed: %m");
467 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
468 crypt_get_cipher(cd
),
469 crypt_get_cipher_mode(cd
),
470 crypt_get_volume_key_size(cd
)*8,
471 crypt_get_device_name(cd
));
474 r
= crypt_activate_by_keyfile_offset(cd
, name
, arg_key_slot
,
475 key_file
, arg_keyfile_size
,
476 arg_keyfile_offset
, flags
);
478 log_error_errno(r
, "Failed to activate with key file '%s': %m", key_file
);
484 STRV_FOREACH(p
, passwords
) {
486 r
= crypt_activate_by_volume_key(cd
, name
, *p
, arg_key_size
, flags
);
488 r
= crypt_activate_by_passphrase(cd
, name
, arg_key_slot
, *p
, strlen(*p
), flags
);
498 static int help(void) {
500 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
501 "%s detach VOLUME\n\n"
502 "Attaches or detaches an encrypted block device.\n",
503 program_invocation_short_name
,
504 program_invocation_short_name
);
509 int main(int argc
, char *argv
[]) {
510 int r
= EXIT_FAILURE
;
511 struct crypt_device
*cd
= NULL
;
519 log_error("This program requires at least two arguments.");
523 log_set_target(LOG_TARGET_AUTO
);
524 log_parse_environment();
529 if (streq(argv
[1], "attach")) {
534 crypt_status_info status
;
535 const char *key_file
= NULL
, *name
= NULL
;
536 _cleanup_free_
char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
;
538 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
541 log_error("attach requires at least two arguments.");
547 !streq(argv
[4], "-") &&
548 !streq(argv
[4], "none")) {
550 if (!path_is_absolute(argv
[4]))
551 log_error("Password file path '%s' is not absolute. Ignoring.", argv
[4]);
556 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-")) {
557 if (parse_options(argv
[5]) < 0)
561 /* A delicious drop of snake oil */
562 mlockall(MCL_FUTURE
);
564 description
= disk_description(argv
[3]);
565 mount_point
= disk_mount_point(argv
[2]);
567 if (description
&& streq(argv
[2], description
)) {
568 /* If the description string is simply the
569 * volume name, then let's not show this
576 if (mount_point
&& description
)
577 k
= asprintf(&name_buffer
, "%s (%s) on %s", description
, argv
[2], mount_point
);
578 else if (mount_point
)
579 k
= asprintf(&name_buffer
, "%s on %s", argv
[2], mount_point
);
580 else if (description
)
581 k
= asprintf(&name_buffer
, "%s (%s)", description
, argv
[2]);
587 name
= name_buffer
? name_buffer
: argv
[2];
590 log_debug("LUKS header: %s", arg_header
);
591 k
= crypt_init(&cd
, arg_header
);
593 k
= crypt_init(&cd
, argv
[3]);
596 log_error_errno(k
, "crypt_init() failed: %m");
600 crypt_set_log_callback(cd
, log_glue
, NULL
);
602 status
= crypt_status(cd
, argv
[2]);
603 if (status
== CRYPT_ACTIVE
|| status
== CRYPT_BUSY
) {
604 log_info("Volume %s already active.", argv
[2]);
610 flags
|= CRYPT_ACTIVATE_READONLY
;
613 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
616 until
= now(CLOCK_MONOTONIC
) + arg_timeout
;
620 arg_key_size
= (arg_key_size
> 0 ? arg_key_size
: (256 / 8));
625 /* Ideally we'd do this on the open fd, but since this is just a
626 * warning it's OK to do this in two steps. */
627 if (stat(key_file
, &st
) >= 0 && S_ISREG(st
.st_mode
) && (st
.st_mode
& 0005))
628 log_warning("Key file %s is world-readable. This is not a good idea!", key_file
);
631 for (tries
= 0; arg_tries
== 0 || tries
< arg_tries
; tries
++) {
632 _cleanup_strv_free_
char **passwords
= NULL
;
635 k
= get_password(name
, until
, tries
== 0 && !arg_verify
, &passwords
);
642 if (streq_ptr(arg_type
, CRYPT_TCRYPT
))
643 k
= attach_tcrypt(cd
, argv
[2], key_file
, passwords
, flags
);
645 k
= attach_luks_or_plain(cd
,
648 arg_header
? argv
[3] : NULL
,
653 else if (k
== -EAGAIN
) {
656 } else if (k
!= -EPERM
) {
657 log_error_errno(k
, "Failed to activate: %m");
661 log_warning("Invalid passphrase.");
664 if (arg_tries
!= 0 && tries
>= arg_tries
) {
665 log_error("Too many attempts; giving up.");
670 } else if (streq(argv
[1], "detach")) {
673 k
= crypt_init_by_name(&cd
, argv
[2]);
675 log_error_errno(k
, "crypt_init() failed: %m");
679 crypt_set_log_callback(cd
, log_glue
, NULL
);
681 k
= crypt_deactivate(cd
, argv
[2]);
683 log_error_errno(k
, "Failed to deactivate: %m");
688 log_error("Unknown verb %s.", argv
[1]);
702 strv_free(arg_tcrypt_keyfiles
);