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 opt_tcrypt_keyfiles
= strv_append(opt_tcrypt_keyfiles
, strdup(option
+15));
95 log_error("Key file path '%s' is not absolute. Ignoring.", option
+15);
97 } else if (startswith(option
, "keyfile-size=")) {
99 if (safe_atou(option
+13, &opt_keyfile_size
) < 0) {
100 log_error("keyfile-size= parse failure, ignoring.");
104 } else if (startswith(option
, "keyfile-offset=")) {
106 if (safe_atou(option
+15, &opt_keyfile_offset
) < 0) {
107 log_error("keyfile-offset= parse failure, ignoring.");
111 } else if (startswith(option
, "hash=")) {
114 t
= strdup(option
+5);
121 } else if (startswith(option
, "tries=")) {
123 if (safe_atou(option
+6, &opt_tries
) < 0) {
124 log_error("tries= parse failure, ignoring.");
128 } else if (streq(option
, "readonly") || streq(option
, "read-only"))
130 else if (streq(option
, "verify"))
132 else if (streq(option
, "allow-discards") || streq(option
, "discard"))
134 else if (streq(option
, "luks"))
135 opt_type
= CRYPT_LUKS1
;
136 else if (streq(option
, "tcrypt"))
137 opt_type
= CRYPT_TCRYPT
;
138 else if (streq(option
, "tcrypt-hidden")) {
139 opt_type
= CRYPT_TCRYPT
;
140 opt_tcrypt_hidden
= true;
141 } else if (streq(option
, "tcrypt-system")) {
142 opt_type
= CRYPT_TCRYPT
;
143 opt_tcrypt_system
= true;
144 } else if (streq(option
, "plain") ||
145 streq(option
, "swap") ||
146 streq(option
, "tmp"))
147 opt_type
= CRYPT_PLAIN
;
148 else if (startswith(option
, "timeout=")) {
150 if (parse_sec(option
+8, &opt_timeout
) < 0) {
151 log_error("timeout= parse failure, ignoring.");
155 } else if (!streq(option
, "none"))
156 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
161 static int parse_options(const char *options
) {
168 FOREACH_WORD_SEPARATOR(w
, l
, options
, ",", state
) {
169 _cleanup_free_
char *o
;
174 r
= parse_one_option(o
);
182 static void log_glue(int level
, const char *msg
, void *usrptr
) {
183 log_debug("%s", msg
);
186 static char *disk_description(const char *path
) {
188 static const char name_fields
[] = {
189 "ID_PART_ENTRY_NAME\0"
191 "ID_MODEL_FROM_DATABASE\0"
195 struct udev
*udev
= NULL
;
196 struct udev_device
*device
= NULL
;
198 char *description
= NULL
;
203 if (stat(path
, &st
) < 0)
206 if (!S_ISBLK(st
.st_mode
))
213 device
= udev_device_new_from_devnum(udev
, 'b', st
.st_rdev
);
217 NULSTR_FOREACH(i
, name_fields
) {
220 name
= udev_device_get_property_value(device
, i
);
221 if (!isempty(name
)) {
222 description
= strdup(name
);
229 udev_device_unref(device
);
237 static char *disk_mount_point(const char *label
) {
239 _cleanup_free_
char *device
= NULL
;
243 /* Yeah, we don't support native systemd unit files here for now */
245 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
248 f
= setmntent("/etc/fstab", "r");
252 while ((m
= getmntent(f
)))
253 if (path_equal(m
->mnt_fsname
, device
)) {
254 mp
= strdup(m
->mnt_dir
);
265 static int get_password(const char *name
, usec_t until
, bool accept_cached
, char ***passwords
) {
268 _cleanup_free_
char *text
= NULL
;
273 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0)
276 r
= ask_password_auto(text
, "drive-harddisk", until
, accept_cached
, passwords
);
278 log_error("Failed to query password: %s", strerror(-r
));
283 _cleanup_strv_free_
char **passwords2
= NULL
;
285 assert(strv_length(*passwords
) == 1);
287 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0)
290 r
= ask_password_auto(text
, "drive-harddisk", until
, false, &passwords2
);
292 log_error("Failed to query verification password: %s", strerror(-r
));
296 assert(strv_length(passwords2
) == 1);
298 if (!streq(*passwords
[0], passwords2
[0])) {
299 log_warning("Passwords did not match, retrying.");
304 strv_uniq(*passwords
);
306 STRV_FOREACH(p
, *passwords
) {
309 if (strlen(*p
)+1 >= opt_key_size
)
312 /* Pad password if necessary */
313 if (!(c
= new(char, opt_key_size
)))
316 strncpy(c
, *p
, opt_key_size
);
324 static int attach_tcrypt(struct crypt_device
*cd
,
326 const char *key_file
,
330 _cleanup_free_
char *passphrase
= NULL
;
331 struct crypt_params_tcrypt params
= {
332 .flags
= CRYPT_TCRYPT_LEGACY_MODES
,
333 .keyfiles
= (const char **)opt_tcrypt_keyfiles
,
334 .keyfiles_count
= strv_length(opt_tcrypt_keyfiles
)
339 assert(key_file
|| passwords
);
341 if (opt_tcrypt_hidden
)
342 params
.flags
|= CRYPT_TCRYPT_HIDDEN_HEADER
;
344 if (opt_tcrypt_system
)
345 params
.flags
|= CRYPT_TCRYPT_SYSTEM_HEADER
;
348 r
= read_one_line_file(key_file
, &passphrase
);
350 log_error("Failed to read password file '%s': %s", key_file
, strerror(-r
));
354 params
.passphrase
= passphrase
;
356 params
.passphrase
= passwords
[0];
357 params
.passphrase_size
= strlen(params
.passphrase
);
359 r
= crypt_load(cd
, CRYPT_TCRYPT
, ¶ms
);
361 if (key_file
&& r
== -EPERM
) {
362 log_error("Failed to activate using password file '%s'.", key_file
);
368 return crypt_activate_by_volume_key(cd
, name
, NULL
, 0, flags
);;
371 static int attach_luks_or_plain(struct crypt_device
*cd
,
373 const char *key_file
,
377 bool pass_volume_key
= false;
381 assert(key_file
|| passwords
);
383 if (!opt_type
|| streq(opt_type
, CRYPT_LUKS1
))
384 r
= crypt_load(cd
, CRYPT_LUKS1
, NULL
);
386 if ((!opt_type
&& r
< 0) || streq_ptr(opt_type
, CRYPT_PLAIN
)) {
387 struct crypt_params_plain params
= {};
388 const char *cipher
, *cipher_mode
;
389 _cleanup_free_
char *truncated_cipher
= NULL
;
392 /* plain isn't a real hash type. it just means "use no hash" */
393 if (!streq(opt_hash
, "plain"))
394 params
.hash
= opt_hash
;
396 params
.hash
= "ripemd160";
401 l
= strcspn(opt_cipher
, "-");
402 truncated_cipher
= strndup(opt_cipher
, l
);
403 if (!truncated_cipher
)
406 cipher
= truncated_cipher
;
407 cipher_mode
= opt_cipher
[l
] ? opt_cipher
+l
+1 : "plain";
410 cipher_mode
= "cbc-essiv:sha256";
413 /* for CRYPT_PLAIN limit reads
414 * from keyfile to key length, and
415 * ignore keyfile-size */
416 opt_keyfile_size
= opt_key_size
/ 8;
418 /* In contrast to what the name
419 * crypt_setup() might suggest this
420 * doesn't actually format anything,
421 * it just configures encryption
422 * parameters when used for plain
424 r
= crypt_format(cd
, CRYPT_PLAIN
, cipher
, cipher_mode
,
425 NULL
, NULL
, opt_keyfile_size
, ¶ms
);
427 /* hash == NULL implies the user passed "plain" */
428 pass_volume_key
= (params
.hash
== NULL
);
432 log_error("Loading of cryptographic parameters failed: %s", strerror(-r
));
436 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
437 crypt_get_cipher(cd
),
438 crypt_get_cipher_mode(cd
),
439 crypt_get_volume_key_size(cd
)*8,
440 crypt_get_device_name(cd
));
443 r
= crypt_activate_by_keyfile_offset(cd
, name
, CRYPT_ANY_SLOT
,
444 key_file
, opt_keyfile_size
,
445 opt_keyfile_offset
, flags
);
447 log_error("Failed to activate with key file '%s': %s", key_file
, strerror(-r
));
453 STRV_FOREACH(p
, passwords
) {
455 r
= crypt_activate_by_volume_key(cd
, name
, *p
, opt_key_size
, flags
);
457 r
= crypt_activate_by_passphrase(cd
, name
, CRYPT_ANY_SLOT
, *p
, strlen(*p
), flags
);
467 static int help(void) {
469 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
470 "%s detach VOLUME\n\n"
471 "Attaches or detaches an encrypted block device.\n",
472 program_invocation_short_name
,
473 program_invocation_short_name
);
478 int main(int argc
, char *argv
[]) {
479 int r
= EXIT_FAILURE
;
480 struct crypt_device
*cd
= NULL
;
488 log_error("This program requires at least two arguments.");
492 log_set_target(LOG_TARGET_AUTO
);
493 log_parse_environment();
498 if (streq(argv
[1], "attach")) {
503 crypt_status_info status
;
504 const char *key_file
= NULL
, *name
= NULL
;
505 _cleanup_free_
char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
;
507 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
510 log_error("attach requires at least two arguments.");
516 !streq(argv
[4], "-") &&
517 !streq(argv
[4], "none")) {
519 if (!path_is_absolute(argv
[4]))
520 log_error("Password file path '%s' is not absolute. Ignoring.", argv
[4]);
525 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-")) {
526 if (parse_options(argv
[5]) < 0)
530 /* A delicious drop of snake oil */
531 mlockall(MCL_FUTURE
);
533 description
= disk_description(argv
[3]);
534 mount_point
= disk_mount_point(argv
[2]);
536 if (description
&& streq(argv
[2], description
)) {
537 /* If the description string is simply the
538 * volume name, then let's not show this
544 if (mount_point
&& description
)
545 asprintf(&name_buffer
, "%s (%s) on %s", description
, argv
[2], mount_point
);
546 else if (mount_point
)
547 asprintf(&name_buffer
, "%s on %s", argv
[2], mount_point
);
548 else if (description
)
549 asprintf(&name_buffer
, "%s (%s)", description
, argv
[2]);
551 name
= name_buffer
? name_buffer
: argv
[2];
553 k
= crypt_init(&cd
, argv
[3]);
555 log_error("crypt_init() failed: %s", strerror(-k
));
559 crypt_set_log_callback(cd
, log_glue
, NULL
);
561 status
= crypt_status(cd
, argv
[2]);
562 if (status
== CRYPT_ACTIVE
|| status
== CRYPT_BUSY
) {
563 log_info("Volume %s already active.", argv
[2]);
569 flags
|= CRYPT_ACTIVATE_READONLY
;
572 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
575 until
= now(CLOCK_MONOTONIC
) + opt_timeout
;
579 opt_key_size
= (opt_key_size
> 0 ? opt_key_size
: 256);
584 /* Ideally we'd do this on the open fd, but since this is just a
585 * warning it's OK to do this in two steps. */
586 if (stat(key_file
, &st
) >= 0 && (st
.st_mode
& 0005))
587 log_warning("Key file %s is world-readable. This is not a good idea!", key_file
);
590 for (tries
= 0; opt_tries
== 0 || tries
< opt_tries
; tries
++) {
591 _cleanup_strv_free_
char **passwords
= NULL
;
594 k
= get_password(name
, until
, tries
== 0 && !opt_verify
, &passwords
);
601 if (streq_ptr(opt_type
, CRYPT_TCRYPT
))
602 k
= attach_tcrypt(cd
, argv
[2], key_file
, passwords
, flags
);
604 k
= attach_luks_or_plain(cd
, argv
[2], key_file
, passwords
, flags
);
607 else if (k
== -EAGAIN
) {
610 } else if (k
!= -EPERM
) {
611 log_error("Failed to activate: %s", strerror(-k
));
615 log_warning("Invalid passphrase.");
618 if (opt_tries
!= 0 && tries
>= opt_tries
) {
619 log_error("Too many attempts; giving up.");
624 } else if (streq(argv
[1], "detach")) {
627 k
= crypt_init_by_name(&cd
, argv
[2]);
629 log_error("crypt_init() failed: %s", strerror(-k
));
633 crypt_set_log_callback(cd
, log_glue
, NULL
);
635 k
= crypt_deactivate(cd
, argv
[2]);
637 log_error("Failed to deactivate: %s", strerror(-k
));
642 log_error("Unknown verb %s.", argv
[1]);
655 strv_free(opt_tcrypt_keyfiles
);