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 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 usec_t arg_timeout
= 0;
55 /* 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 (streq(option
, "noauto") || streq(option
, "nofail"))
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
, "tries=")) {
141 if (safe_atou(option
+6, &arg_tries
) < 0) {
142 log_error("tries= parse failure, ignoring.");
146 } else if (STR_IN_SET(option
, "readonly", "read-only"))
148 else if (streq(option
, "verify"))
150 else if (STR_IN_SET(option
, "allow-discards", "discard"))
152 else if (streq(option
, "luks"))
153 arg_type
= CRYPT_LUKS1
;
154 else if (streq(option
, "tcrypt"))
155 arg_type
= CRYPT_TCRYPT
;
156 else if (streq(option
, "tcrypt-hidden")) {
157 arg_type
= CRYPT_TCRYPT
;
158 arg_tcrypt_hidden
= true;
159 } else if (streq(option
, "tcrypt-system")) {
160 arg_type
= CRYPT_TCRYPT
;
161 arg_tcrypt_system
= true;
162 } else if (STR_IN_SET(option
, "plain", "swap", "tmp"))
163 arg_type
= CRYPT_PLAIN
;
164 else if (startswith(option
, "timeout=")) {
166 if (parse_sec(option
+8, &arg_timeout
) < 0) {
167 log_error("timeout= parse failure, ignoring.");
171 } else if (!streq(option
, "none"))
172 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
177 static int parse_options(const char *options
) {
178 const char *word
, *state
;
184 FOREACH_WORD_SEPARATOR(word
, l
, options
, ",", state
) {
185 _cleanup_free_
char *o
;
187 o
= strndup(word
, l
);
190 r
= parse_one_option(o
);
198 static void log_glue(int level
, const char *msg
, void *usrptr
) {
199 log_debug("%s", msg
);
202 static char* disk_description(const char *path
) {
204 static const char name_fields
[] =
205 "ID_PART_ENTRY_NAME\0"
207 "ID_MODEL_FROM_DATABASE\0"
210 _cleanup_udev_unref_
struct udev
*udev
= NULL
;
211 _cleanup_udev_device_unref_
struct udev_device
*device
= NULL
;
217 if (stat(path
, &st
) < 0)
220 if (!S_ISBLK(st
.st_mode
))
227 device
= udev_device_new_from_devnum(udev
, 'b', st
.st_rdev
);
231 NULSTR_FOREACH(i
, name_fields
) {
234 name
= udev_device_get_property_value(device
, i
);
242 static char *disk_mount_point(const char *label
) {
243 _cleanup_free_
char *device
= NULL
;
244 _cleanup_endmntent_
FILE *f
= NULL
;
247 /* Yeah, we don't support native systemd unit files here for now */
249 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
252 f
= setmntent("/etc/fstab", "r");
256 while ((m
= getmntent(f
)))
257 if (path_equal(m
->mnt_fsname
, device
))
258 return strdup(m
->mnt_dir
);
263 static int get_password(const char *name
, usec_t until
, bool accept_cached
, char ***passwords
) {
266 _cleanup_free_
char *text
= NULL
;
267 _cleanup_free_
char *escaped_name
= NULL
;
273 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0)
276 escaped_name
= cescape(name
);
280 id
= strappenda("cryptsetup:", escaped_name
);
282 r
= ask_password_auto(text
, "drive-harddisk", id
, until
, accept_cached
, passwords
);
284 return log_error_errno(r
, "Failed to query password: %m");
287 _cleanup_strv_free_
char **passwords2
= NULL
;
289 assert(strv_length(*passwords
) == 1);
291 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0)
294 id
= strappenda("cryptsetup-verification:", escaped_name
);
296 r
= ask_password_auto(text
, "drive-harddisk", id
, until
, false, &passwords2
);
298 return log_error_errno(r
, "Failed to query verification password: %m");
300 assert(strv_length(passwords2
) == 1);
302 if (!streq(*passwords
[0], passwords2
[0])) {
303 log_warning("Passwords did not match, retrying.");
308 strv_uniq(*passwords
);
310 STRV_FOREACH(p
, *passwords
) {
313 if (strlen(*p
)+1 >= arg_key_size
)
316 /* Pad password if necessary */
317 if (!(c
= new(char, arg_key_size
)))
320 strncpy(c
, *p
, arg_key_size
);
328 static int attach_tcrypt(struct crypt_device
*cd
,
330 const char *key_file
,
334 _cleanup_free_
char *passphrase
= NULL
;
335 struct crypt_params_tcrypt params
= {
336 .flags
= CRYPT_TCRYPT_LEGACY_MODES
,
337 .keyfiles
= (const char **)arg_tcrypt_keyfiles
,
338 .keyfiles_count
= strv_length(arg_tcrypt_keyfiles
)
343 assert(key_file
|| (passwords
&& passwords
[0]));
345 if (arg_tcrypt_hidden
)
346 params
.flags
|= CRYPT_TCRYPT_HIDDEN_HEADER
;
348 if (arg_tcrypt_system
)
349 params
.flags
|= CRYPT_TCRYPT_SYSTEM_HEADER
;
352 r
= read_one_line_file(key_file
, &passphrase
);
354 log_error_errno(r
, "Failed to read password file '%s': %m", key_file
);
358 params
.passphrase
= passphrase
;
360 params
.passphrase
= passwords
[0];
361 params
.passphrase_size
= strlen(params
.passphrase
);
363 r
= crypt_load(cd
, CRYPT_TCRYPT
, ¶ms
);
365 if (key_file
&& r
== -EPERM
) {
366 log_error("Failed to activate using password file '%s'.", key_file
);
372 return crypt_activate_by_volume_key(cd
, name
, NULL
, 0, flags
);
375 static int attach_luks_or_plain(struct crypt_device
*cd
,
377 const char *key_file
,
381 bool pass_volume_key
= false;
385 assert(key_file
|| passwords
);
387 if (!arg_type
|| streq(arg_type
, CRYPT_LUKS1
))
388 r
= crypt_load(cd
, CRYPT_LUKS1
, NULL
);
390 if ((!arg_type
&& r
< 0) || streq_ptr(arg_type
, CRYPT_PLAIN
)) {
391 struct crypt_params_plain params
= {};
392 const char *cipher
, *cipher_mode
;
393 _cleanup_free_
char *truncated_cipher
= NULL
;
396 /* plain isn't a real hash type. it just means "use no hash" */
397 if (!streq(arg_hash
, "plain"))
398 params
.hash
= arg_hash
;
399 } else if (!key_file
)
400 /* for CRYPT_PLAIN, the behaviour of cryptsetup
401 * package is to not hash when a key file is provided */
402 params
.hash
= "ripemd160";
407 l
= strcspn(arg_cipher
, "-");
408 truncated_cipher
= strndup(arg_cipher
, l
);
409 if (!truncated_cipher
)
412 cipher
= truncated_cipher
;
413 cipher_mode
= arg_cipher
[l
] ? arg_cipher
+l
+1 : "plain";
416 cipher_mode
= "cbc-essiv:sha256";
419 /* for CRYPT_PLAIN limit reads
420 * from keyfile to key length, and
421 * ignore keyfile-size */
422 arg_keyfile_size
= arg_key_size
;
424 /* In contrast to what the name
425 * crypt_setup() might suggest this
426 * doesn't actually format anything,
427 * it just configures encryption
428 * parameters when used for plain
430 r
= crypt_format(cd
, CRYPT_PLAIN
, cipher
, cipher_mode
,
431 NULL
, NULL
, arg_keyfile_size
, ¶ms
);
433 /* hash == NULL implies the user passed "plain" */
434 pass_volume_key
= (params
.hash
== NULL
);
438 return log_error_errno(r
, "Loading of cryptographic parameters failed: %m");
440 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
441 crypt_get_cipher(cd
),
442 crypt_get_cipher_mode(cd
),
443 crypt_get_volume_key_size(cd
)*8,
444 crypt_get_device_name(cd
));
447 r
= crypt_activate_by_keyfile_offset(cd
, name
, arg_key_slot
,
448 key_file
, arg_keyfile_size
,
449 arg_keyfile_offset
, flags
);
451 log_error_errno(r
, "Failed to activate with key file '%s': %m", key_file
);
457 STRV_FOREACH(p
, passwords
) {
459 r
= crypt_activate_by_volume_key(cd
, name
, *p
, arg_key_size
, flags
);
461 r
= crypt_activate_by_passphrase(cd
, name
, arg_key_slot
, *p
, strlen(*p
), flags
);
471 static int help(void) {
473 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
474 "%s detach VOLUME\n\n"
475 "Attaches or detaches an encrypted block device.\n",
476 program_invocation_short_name
,
477 program_invocation_short_name
);
482 int main(int argc
, char *argv
[]) {
483 int r
= EXIT_FAILURE
;
484 struct crypt_device
*cd
= NULL
;
492 log_error("This program requires at least two arguments.");
496 log_set_target(LOG_TARGET_AUTO
);
497 log_parse_environment();
502 if (streq(argv
[1], "attach")) {
507 crypt_status_info status
;
508 const char *key_file
= NULL
, *name
= NULL
;
509 _cleanup_free_
char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
;
511 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
514 log_error("attach requires at least two arguments.");
520 !streq(argv
[4], "-") &&
521 !streq(argv
[4], "none")) {
523 if (!path_is_absolute(argv
[4]))
524 log_error("Password file path '%s' is not absolute. Ignoring.", argv
[4]);
529 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-")) {
530 if (parse_options(argv
[5]) < 0)
534 /* A delicious drop of snake oil */
535 mlockall(MCL_FUTURE
);
537 description
= disk_description(argv
[3]);
538 mount_point
= disk_mount_point(argv
[2]);
540 if (description
&& streq(argv
[2], description
)) {
541 /* If the description string is simply the
542 * volume name, then let's not show this
549 if (mount_point
&& description
)
550 k
= asprintf(&name_buffer
, "%s (%s) on %s", description
, argv
[2], mount_point
);
551 else if (mount_point
)
552 k
= asprintf(&name_buffer
, "%s on %s", argv
[2], mount_point
);
553 else if (description
)
554 k
= asprintf(&name_buffer
, "%s (%s)", description
, argv
[2]);
560 name
= name_buffer
? name_buffer
: argv
[2];
562 k
= crypt_init(&cd
, argv
[3]);
564 log_error_errno(k
, "crypt_init() failed: %m");
568 crypt_set_log_callback(cd
, log_glue
, NULL
);
570 status
= crypt_status(cd
, argv
[2]);
571 if (status
== CRYPT_ACTIVE
|| status
== CRYPT_BUSY
) {
572 log_info("Volume %s already active.", argv
[2]);
578 flags
|= CRYPT_ACTIVATE_READONLY
;
581 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
584 until
= now(CLOCK_MONOTONIC
) + arg_timeout
;
588 arg_key_size
= (arg_key_size
> 0 ? arg_key_size
: (256 / 8));
593 /* Ideally we'd do this on the open fd, but since this is just a
594 * warning it's OK to do this in two steps. */
595 if (stat(key_file
, &st
) >= 0 && (st
.st_mode
& 0005))
596 log_warning("Key file %s is world-readable. This is not a good idea!", key_file
);
599 for (tries
= 0; arg_tries
== 0 || tries
< arg_tries
; tries
++) {
600 _cleanup_strv_free_
char **passwords
= NULL
;
603 k
= get_password(name
, until
, tries
== 0 && !arg_verify
, &passwords
);
610 if (streq_ptr(arg_type
, CRYPT_TCRYPT
))
611 k
= attach_tcrypt(cd
, argv
[2], key_file
, passwords
, flags
);
613 k
= attach_luks_or_plain(cd
, argv
[2], key_file
, passwords
, flags
);
616 else if (k
== -EAGAIN
) {
619 } else if (k
!= -EPERM
) {
620 log_error_errno(k
, "Failed to activate: %m");
624 log_warning("Invalid passphrase.");
627 if (arg_tries
!= 0 && tries
>= arg_tries
) {
628 log_error("Too many attempts; giving up.");
633 } else if (streq(argv
[1], "detach")) {
636 k
= crypt_init_by_name(&cd
, argv
[2]);
638 log_error_errno(k
, "crypt_init() failed: %m");
642 crypt_set_log_callback(cd
, log_glue
, NULL
);
644 k
= crypt_deactivate(cd
, argv
[2]);
646 log_error_errno(k
, "Failed to deactivate: %m");
651 log_error("Unknown verb %s.", argv
[1]);
664 strv_free(arg_tcrypt_keyfiles
);