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>
33 #include "path-util.h"
35 #include "ask-password-api.h"
38 static const char *opt_type
= NULL
; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
39 static char *opt_cipher
= NULL
;
40 static unsigned opt_key_size
= 0;
41 static unsigned opt_keyfile_size
= 0;
42 static unsigned opt_keyfile_offset
= 0;
43 static char *opt_hash
= NULL
;
44 static unsigned opt_tries
= 3;
45 static bool opt_readonly
= false;
46 static bool opt_verify
= false;
47 static bool opt_discards
= false;
48 static bool opt_tcrypt_hidden
= false;
49 static bool opt_tcrypt_system
= false;
50 static char **opt_tcrypt_keyfiles
= NULL
;
51 static usec_t opt_timeout
= 0;
53 /* Options Debian's crypttab knows we don't:
65 static int parse_one_option(const char *option
) {
68 /* Handled outside of this tool */
69 if (streq(option
, "noauto") || streq(option
, "nofail"))
72 if (startswith(option
, "cipher=")) {
82 } else if (startswith(option
, "size=")) {
84 if (safe_atou(option
+5, &opt_key_size
) < 0) {
85 log_error("size= parse failure, ignoring.");
89 } else if (startswith(option
, "tcrypt-keyfile=")) {
91 opt_type
= CRYPT_TCRYPT
;
92 if (path_is_absolute(option
+15)) {
93 if (strv_extend(&opt_tcrypt_keyfiles
, option
+ 15) < 0)
96 log_error("Key file path '%s' is not absolute. Ignoring.", option
+15);
98 } else if (startswith(option
, "keyfile-size=")) {
100 if (safe_atou(option
+13, &opt_keyfile_size
) < 0) {
101 log_error("keyfile-size= parse failure, ignoring.");
105 } else if (startswith(option
, "keyfile-offset=")) {
107 if (safe_atou(option
+15, &opt_keyfile_offset
) < 0) {
108 log_error("keyfile-offset= parse failure, ignoring.");
112 } else if (startswith(option
, "hash=")) {
115 t
= strdup(option
+5);
122 } else if (startswith(option
, "tries=")) {
124 if (safe_atou(option
+6, &opt_tries
) < 0) {
125 log_error("tries= parse failure, ignoring.");
129 } else if (streq(option
, "readonly") || streq(option
, "read-only"))
131 else if (streq(option
, "verify"))
133 else if (streq(option
, "allow-discards") || streq(option
, "discard"))
135 else if (streq(option
, "luks"))
136 opt_type
= CRYPT_LUKS1
;
137 else if (streq(option
, "tcrypt"))
138 opt_type
= CRYPT_TCRYPT
;
139 else if (streq(option
, "tcrypt-hidden")) {
140 opt_type
= CRYPT_TCRYPT
;
141 opt_tcrypt_hidden
= true;
142 } else if (streq(option
, "tcrypt-system")) {
143 opt_type
= CRYPT_TCRYPT
;
144 opt_tcrypt_system
= true;
145 } else if (streq(option
, "plain") ||
146 streq(option
, "swap") ||
147 streq(option
, "tmp"))
148 opt_type
= CRYPT_PLAIN
;
149 else if (startswith(option
, "timeout=")) {
151 if (parse_sec(option
+8, &opt_timeout
) < 0) {
152 log_error("timeout= parse failure, ignoring.");
156 } else if (!streq(option
, "none"))
157 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
162 static int parse_options(const char *options
) {
169 FOREACH_WORD_SEPARATOR(w
, l
, options
, ",", state
) {
170 _cleanup_free_
char *o
;
175 r
= parse_one_option(o
);
183 static void log_glue(int level
, const char *msg
, void *usrptr
) {
184 log_debug("%s", msg
);
187 static char *disk_description(const char *path
) {
189 static const char name_fields
[] = {
190 "ID_PART_ENTRY_NAME\0"
192 "ID_MODEL_FROM_DATABASE\0"
196 struct udev
*udev
= NULL
;
197 struct udev_device
*device
= NULL
;
199 char *description
= NULL
;
204 if (stat(path
, &st
) < 0)
207 if (!S_ISBLK(st
.st_mode
))
214 device
= udev_device_new_from_devnum(udev
, 'b', st
.st_rdev
);
218 NULSTR_FOREACH(i
, name_fields
) {
221 name
= udev_device_get_property_value(device
, i
);
222 if (!isempty(name
)) {
223 description
= strdup(name
);
230 udev_device_unref(device
);
238 static char *disk_mount_point(const char *label
) {
240 _cleanup_free_
char *device
= NULL
;
244 /* Yeah, we don't support native systemd unit files here for now */
246 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
249 f
= setmntent("/etc/fstab", "r");
253 while ((m
= getmntent(f
)))
254 if (path_equal(m
->mnt_fsname
, device
)) {
255 mp
= strdup(m
->mnt_dir
);
266 static int get_password(const char *name
, usec_t until
, bool accept_cached
, char ***passwords
) {
269 _cleanup_free_
char *text
= NULL
;
274 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0)
277 r
= ask_password_auto(text
, "drive-harddisk", until
, accept_cached
, passwords
);
279 log_error("Failed to query password: %s", strerror(-r
));
284 _cleanup_strv_free_
char **passwords2
= NULL
;
286 assert(strv_length(*passwords
) == 1);
288 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0)
291 r
= ask_password_auto(text
, "drive-harddisk", until
, false, &passwords2
);
293 log_error("Failed to query verification password: %s", strerror(-r
));
297 assert(strv_length(passwords2
) == 1);
299 if (!streq(*passwords
[0], passwords2
[0])) {
300 log_warning("Passwords did not match, retrying.");
305 strv_uniq(*passwords
);
307 STRV_FOREACH(p
, *passwords
) {
310 if (strlen(*p
)+1 >= opt_key_size
)
313 /* Pad password if necessary */
314 if (!(c
= new(char, opt_key_size
)))
317 strncpy(c
, *p
, opt_key_size
);
325 static int attach_tcrypt(struct crypt_device
*cd
,
327 const char *key_file
,
331 _cleanup_free_
char *passphrase
= NULL
;
332 struct crypt_params_tcrypt params
= {
333 .flags
= CRYPT_TCRYPT_LEGACY_MODES
,
334 .keyfiles
= (const char **)opt_tcrypt_keyfiles
,
335 .keyfiles_count
= strv_length(opt_tcrypt_keyfiles
)
340 assert(key_file
|| passwords
);
342 if (opt_tcrypt_hidden
)
343 params
.flags
|= CRYPT_TCRYPT_HIDDEN_HEADER
;
345 if (opt_tcrypt_system
)
346 params
.flags
|= CRYPT_TCRYPT_SYSTEM_HEADER
;
349 r
= read_one_line_file(key_file
, &passphrase
);
351 log_error("Failed to read password file '%s': %s", key_file
, strerror(-r
));
355 params
.passphrase
= passphrase
;
357 params
.passphrase
= passwords
[0];
358 params
.passphrase_size
= strlen(params
.passphrase
);
360 r
= crypt_load(cd
, CRYPT_TCRYPT
, ¶ms
);
362 if (key_file
&& r
== -EPERM
) {
363 log_error("Failed to activate using password file '%s'.", key_file
);
369 return crypt_activate_by_volume_key(cd
, name
, NULL
, 0, flags
);;
372 static int attach_luks_or_plain(struct crypt_device
*cd
,
374 const char *key_file
,
378 bool pass_volume_key
= false;
382 assert(key_file
|| passwords
);
384 if (!opt_type
|| streq(opt_type
, CRYPT_LUKS1
))
385 r
= crypt_load(cd
, CRYPT_LUKS1
, NULL
);
387 if ((!opt_type
&& r
< 0) || streq_ptr(opt_type
, CRYPT_PLAIN
)) {
388 struct crypt_params_plain params
= {};
389 const char *cipher
, *cipher_mode
;
390 _cleanup_free_
char *truncated_cipher
= NULL
;
393 /* plain isn't a real hash type. it just means "use no hash" */
394 if (!streq(opt_hash
, "plain"))
395 params
.hash
= opt_hash
;
397 params
.hash
= "ripemd160";
402 l
= strcspn(opt_cipher
, "-");
403 truncated_cipher
= strndup(opt_cipher
, l
);
404 if (!truncated_cipher
)
407 cipher
= truncated_cipher
;
408 cipher_mode
= opt_cipher
[l
] ? opt_cipher
+l
+1 : "plain";
411 cipher_mode
= "cbc-essiv:sha256";
414 /* for CRYPT_PLAIN limit reads
415 * from keyfile to key length, and
416 * ignore keyfile-size */
417 opt_keyfile_size
= opt_key_size
/ 8;
419 /* In contrast to what the name
420 * crypt_setup() might suggest this
421 * doesn't actually format anything,
422 * it just configures encryption
423 * parameters when used for plain
425 r
= crypt_format(cd
, CRYPT_PLAIN
, cipher
, cipher_mode
,
426 NULL
, NULL
, opt_keyfile_size
, ¶ms
);
428 /* hash == NULL implies the user passed "plain" */
429 pass_volume_key
= (params
.hash
== NULL
);
433 log_error("Loading of cryptographic parameters failed: %s", strerror(-r
));
437 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
438 crypt_get_cipher(cd
),
439 crypt_get_cipher_mode(cd
),
440 crypt_get_volume_key_size(cd
)*8,
441 crypt_get_device_name(cd
));
444 r
= crypt_activate_by_keyfile_offset(cd
, name
, CRYPT_ANY_SLOT
,
445 key_file
, opt_keyfile_size
,
446 opt_keyfile_offset
, flags
);
448 log_error("Failed to activate with key file '%s': %s", key_file
, strerror(-r
));
454 STRV_FOREACH(p
, passwords
) {
456 r
= crypt_activate_by_volume_key(cd
, name
, *p
, opt_key_size
, flags
);
458 r
= crypt_activate_by_passphrase(cd
, name
, CRYPT_ANY_SLOT
, *p
, strlen(*p
), flags
);
468 static int help(void) {
470 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
471 "%s detach VOLUME\n\n"
472 "Attaches or detaches an encrypted block device.\n",
473 program_invocation_short_name
,
474 program_invocation_short_name
);
479 int main(int argc
, char *argv
[]) {
480 int r
= EXIT_FAILURE
;
481 struct crypt_device
*cd
= NULL
;
489 log_error("This program requires at least two arguments.");
493 log_set_target(LOG_TARGET_AUTO
);
494 log_parse_environment();
499 if (streq(argv
[1], "attach")) {
504 crypt_status_info status
;
505 const char *key_file
= NULL
, *name
= NULL
;
506 _cleanup_free_
char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
;
508 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
511 log_error("attach requires at least two arguments.");
517 !streq(argv
[4], "-") &&
518 !streq(argv
[4], "none")) {
520 if (!path_is_absolute(argv
[4]))
521 log_error("Password file path '%s' is not absolute. Ignoring.", argv
[4]);
526 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-")) {
527 if (parse_options(argv
[5]) < 0)
531 /* A delicious drop of snake oil */
532 mlockall(MCL_FUTURE
);
534 description
= disk_description(argv
[3]);
535 mount_point
= disk_mount_point(argv
[2]);
537 if (description
&& streq(argv
[2], description
)) {
538 /* If the description string is simply the
539 * volume name, then let's not show this
545 if (mount_point
&& description
)
546 asprintf(&name_buffer
, "%s (%s) on %s", description
, argv
[2], mount_point
);
547 else if (mount_point
)
548 asprintf(&name_buffer
, "%s on %s", argv
[2], mount_point
);
549 else if (description
)
550 asprintf(&name_buffer
, "%s (%s)", description
, argv
[2]);
552 name
= name_buffer
? name_buffer
: argv
[2];
554 k
= crypt_init(&cd
, argv
[3]);
556 log_error("crypt_init() failed: %s", strerror(-k
));
560 crypt_set_log_callback(cd
, log_glue
, NULL
);
562 status
= crypt_status(cd
, argv
[2]);
563 if (status
== CRYPT_ACTIVE
|| status
== CRYPT_BUSY
) {
564 log_info("Volume %s already active.", argv
[2]);
570 flags
|= CRYPT_ACTIVATE_READONLY
;
573 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
576 until
= now(CLOCK_MONOTONIC
) + opt_timeout
;
580 opt_key_size
= (opt_key_size
> 0 ? opt_key_size
: 256);
585 /* Ideally we'd do this on the open fd, but since this is just a
586 * warning it's OK to do this in two steps. */
587 if (stat(key_file
, &st
) >= 0 && (st
.st_mode
& 0005))
588 log_warning("Key file %s is world-readable. This is not a good idea!", key_file
);
591 for (tries
= 0; opt_tries
== 0 || tries
< opt_tries
; tries
++) {
592 _cleanup_strv_free_
char **passwords
= NULL
;
595 k
= get_password(name
, until
, tries
== 0 && !opt_verify
, &passwords
);
602 if (streq_ptr(opt_type
, CRYPT_TCRYPT
))
603 k
= attach_tcrypt(cd
, argv
[2], key_file
, passwords
, flags
);
605 k
= attach_luks_or_plain(cd
, argv
[2], key_file
, passwords
, flags
);
608 else if (k
== -EAGAIN
) {
611 } else if (k
!= -EPERM
) {
612 log_error("Failed to activate: %s", strerror(-k
));
616 log_warning("Invalid passphrase.");
619 if (opt_tries
!= 0 && tries
>= opt_tries
) {
620 log_error("Too many attempts; giving up.");
625 } else if (streq(argv
[1], "detach")) {
628 k
= crypt_init_by_name(&cd
, argv
[2]);
630 log_error("crypt_init() failed: %s", strerror(-k
));
634 crypt_set_log_callback(cd
, log_glue
, NULL
);
636 k
= crypt_deactivate(cd
, argv
[2]);
638 log_error("Failed to deactivate: %s", strerror(-k
));
643 log_error("Unknown verb %s.", argv
[1]);
656 strv_free(opt_tcrypt_keyfiles
);