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 } else if (startswith(option, "key-slot=")) {
93 arg_type = CRYPT_LUKS1;
94 if (safe_atoi(option+9, &arg_key_slot) < 0) {
95 log_error("key-slot= parse failure, ignoring.");
99 } else if (startswith(option, "tcrypt-keyfile=")) {
101 arg_type = CRYPT_TCRYPT;
102 if (path_is_absolute(option+15)) {
103 if (strv_extend(&arg_tcrypt_keyfiles, option + 15) < 0)
106 log_error("Key file path '%s' is not absolute. Ignoring.", option+15);
108 } else if (startswith(option, "keyfile-size=")) {
110 if (safe_atou(option+13, &arg_keyfile_size) < 0) {
111 log_error("keyfile-size= parse failure, ignoring.");
115 } else if (startswith(option, "keyfile-offset=")) {
117 if (safe_atou(option+15, &arg_keyfile_offset) < 0) {
118 log_error("keyfile-offset= parse failure, ignoring.");
122 } else if (startswith(option, "hash=")) {
125 t = strdup(option+5);
132 } else if (startswith(option, "tries=")) {
134 if (safe_atou(option+6, &arg_tries) < 0) {
135 log_error("tries= parse failure, ignoring.");
139 } else if (STR_IN_SET(option, "readonly", "read-only"))
141 else if (streq(option, "verify"))
143 else if (STR_IN_SET(option, "allow-discards", "discard"))
145 else if (streq(option, "luks"))
146 arg_type = CRYPT_LUKS1;
147 else if (streq(option, "tcrypt"))
148 arg_type = CRYPT_TCRYPT;
149 else if (streq(option, "tcrypt-hidden")) {
150 arg_type = CRYPT_TCRYPT;
151 arg_tcrypt_hidden = true;
152 } else if (streq(option, "tcrypt-system")) {
153 arg_type = CRYPT_TCRYPT;
154 arg_tcrypt_system = true;
155 } else if (STR_IN_SET(option, "plain", "swap", "tmp"))
156 arg_type = CRYPT_PLAIN;
157 else if (startswith(option, "timeout=")) {
159 if (parse_sec(option+8, &arg_timeout) < 0) {
160 log_error("timeout= parse failure, ignoring.");
164 } else if (!streq(option, "none"))
165 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
170 static int parse_options(const char *options) {
177 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
178 _cleanup_free_ char *o;
183 r = parse_one_option(o);
191 static void log_glue(int level, const char *msg, void *usrptr) {
192 log_debug("%s", msg);
195 static char* disk_description(const char *path) {
197 static const char name_fields[] =
198 "ID_PART_ENTRY_NAME\0"
200 "ID_MODEL_FROM_DATABASE\0"
203 _cleanup_udev_unref_ struct udev *udev = NULL;
204 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
210 if (stat(path, &st) < 0)
213 if (!S_ISBLK(st.st_mode))
220 device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
224 NULSTR_FOREACH(i, name_fields) {
227 name = udev_device_get_property_value(device, i);
235 static char *disk_mount_point(const char *label) {
236 _cleanup_free_ char *device = NULL;
237 _cleanup_endmntent_ FILE *f = NULL;
240 /* Yeah, we don't support native systemd unit files here for now */
242 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
245 f = setmntent("/etc/fstab", "r");
249 while ((m = getmntent(f)))
250 if (path_equal(m->mnt_fsname, device))
251 return strdup(m->mnt_dir);
256 static int get_password(const char *name, usec_t until, bool accept_cached, char ***passwords) {
259 _cleanup_free_ char *text = NULL;
264 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
267 r = ask_password_auto(text, "drive-harddisk", until, accept_cached, passwords);
269 log_error("Failed to query password: %s", strerror(-r));
274 _cleanup_strv_free_ char **passwords2 = NULL;
276 assert(strv_length(*passwords) == 1);
278 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
281 r = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
283 log_error("Failed to query verification password: %s", strerror(-r));
287 assert(strv_length(passwords2) == 1);
289 if (!streq(*passwords[0], passwords2[0])) {
290 log_warning("Passwords did not match, retrying.");
295 strv_uniq(*passwords);
297 STRV_FOREACH(p, *passwords) {
300 if (strlen(*p)+1 >= arg_key_size)
303 /* Pad password if necessary */
304 if (!(c = new(char, arg_key_size)))
307 strncpy(c, *p, arg_key_size);
315 static int attach_tcrypt(struct crypt_device *cd,
317 const char *key_file,
321 _cleanup_free_ char *passphrase = NULL;
322 struct crypt_params_tcrypt params = {
323 .flags = CRYPT_TCRYPT_LEGACY_MODES,
324 .keyfiles = (const char **)arg_tcrypt_keyfiles,
325 .keyfiles_count = strv_length(arg_tcrypt_keyfiles)
330 assert(key_file || passwords);
332 if (arg_tcrypt_hidden)
333 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
335 if (arg_tcrypt_system)
336 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
339 r = read_one_line_file(key_file, &passphrase);
341 log_error("Failed to read password file '%s': %s", key_file, strerror(-r));
345 params.passphrase = passphrase;
347 params.passphrase = passwords[0];
348 params.passphrase_size = strlen(params.passphrase);
350 r = crypt_load(cd, CRYPT_TCRYPT, ¶ms);
352 if (key_file && r == -EPERM) {
353 log_error("Failed to activate using password file '%s'.", key_file);
359 return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
362 static int attach_luks_or_plain(struct crypt_device *cd,
364 const char *key_file,
368 bool pass_volume_key = false;
372 assert(key_file || passwords);
374 if (!arg_type || streq(arg_type, CRYPT_LUKS1))
375 r = crypt_load(cd, CRYPT_LUKS1, NULL);
377 if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) {
378 struct crypt_params_plain params = {};
379 const char *cipher, *cipher_mode;
380 _cleanup_free_ char *truncated_cipher = NULL;
383 /* plain isn't a real hash type. it just means "use no hash" */
384 if (!streq(arg_hash, "plain"))
385 params.hash = arg_hash;
387 params.hash = "ripemd160";
392 l = strcspn(arg_cipher, "-");
393 truncated_cipher = strndup(arg_cipher, l);
394 if (!truncated_cipher)
397 cipher = truncated_cipher;
398 cipher_mode = arg_cipher[l] ? arg_cipher+l+1 : "plain";
401 cipher_mode = "cbc-essiv:sha256";
404 /* for CRYPT_PLAIN limit reads
405 * from keyfile to key length, and
406 * ignore keyfile-size */
407 arg_keyfile_size = arg_key_size / 8;
409 /* In contrast to what the name
410 * crypt_setup() might suggest this
411 * doesn't actually format anything,
412 * it just configures encryption
413 * parameters when used for plain
415 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode,
416 NULL, NULL, arg_keyfile_size, ¶ms);
418 /* hash == NULL implies the user passed "plain" */
419 pass_volume_key = (params.hash == NULL);
423 log_error("Loading of cryptographic parameters failed: %s", strerror(-r));
427 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
428 crypt_get_cipher(cd),
429 crypt_get_cipher_mode(cd),
430 crypt_get_volume_key_size(cd)*8,
431 crypt_get_device_name(cd));
434 r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot,
435 key_file, arg_keyfile_size,
436 arg_keyfile_offset, flags);
438 log_error("Failed to activate with key file '%s': %s", key_file, strerror(-r));
444 STRV_FOREACH(p, passwords) {
446 r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
448 r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
458 static int help(void) {
460 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
461 "%s detach VOLUME\n\n"
462 "Attaches or detaches an encrypted block device.\n",
463 program_invocation_short_name,
464 program_invocation_short_name);
469 int main(int argc, char *argv[]) {
470 int r = EXIT_FAILURE;
471 struct crypt_device *cd = NULL;
479 log_error("This program requires at least two arguments.");
483 log_set_target(LOG_TARGET_AUTO);
484 log_parse_environment();
489 if (streq(argv[1], "attach")) {
494 crypt_status_info status;
495 const char *key_file = NULL, *name = NULL;
496 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
498 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
501 log_error("attach requires at least two arguments.");
507 !streq(argv[4], "-") &&
508 !streq(argv[4], "none")) {
510 if (!path_is_absolute(argv[4]))
511 log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]);
516 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
517 if (parse_options(argv[5]) < 0)
521 /* A delicious drop of snake oil */
522 mlockall(MCL_FUTURE);
524 description = disk_description(argv[3]);
525 mount_point = disk_mount_point(argv[2]);
527 if (description && streq(argv[2], description)) {
528 /* If the description string is simply the
529 * volume name, then let's not show this
535 if (mount_point && description)
536 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
537 else if (mount_point)
538 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
539 else if (description)
540 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
542 name = name_buffer ? name_buffer : argv[2];
544 k = crypt_init(&cd, argv[3]);
546 log_error("crypt_init() failed: %s", strerror(-k));
550 crypt_set_log_callback(cd, log_glue, NULL);
552 status = crypt_status(cd, argv[2]);
553 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
554 log_info("Volume %s already active.", argv[2]);
560 flags |= CRYPT_ACTIVATE_READONLY;
563 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
566 until = now(CLOCK_MONOTONIC) + arg_timeout;
570 arg_key_size = (arg_key_size > 0 ? arg_key_size : 256);
575 /* Ideally we'd do this on the open fd, but since this is just a
576 * warning it's OK to do this in two steps. */
577 if (stat(key_file, &st) >= 0 && (st.st_mode & 0005))
578 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
581 for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) {
582 _cleanup_strv_free_ char **passwords = NULL;
585 k = get_password(name, until, tries == 0 && !arg_verify, &passwords);
592 if (streq_ptr(arg_type, CRYPT_TCRYPT))
593 k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
595 k = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags);
598 else if (k == -EAGAIN) {
601 } else if (k != -EPERM) {
602 log_error("Failed to activate: %s", strerror(-k));
606 log_warning("Invalid passphrase.");
609 if (arg_tries != 0 && tries >= arg_tries) {
610 log_error("Too many attempts; giving up.");
615 } else if (streq(argv[1], "detach")) {
618 k = crypt_init_by_name(&cd, argv[2]);
620 log_error("crypt_init() failed: %s", strerror(-k));
624 crypt_set_log_callback(cd, log_glue, NULL);
626 k = crypt_deactivate(cd, argv[2]);
628 log_error("Failed to deactivate: %s", strerror(-k));
633 log_error("Unknown verb %s.", argv[1]);
646 strv_free(arg_tcrypt_keyfiles);