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"
36 #include "udev-util.h"
38 static const char *arg_type = NULL; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
39 static char *arg_cipher = NULL;
40 static unsigned arg_key_size = 0;
41 static int arg_key_slot = CRYPT_ANY_SLOT;
42 static unsigned arg_keyfile_size = 0;
43 static unsigned arg_keyfile_offset = 0;
44 static char *arg_hash = NULL;
45 static char *arg_header = 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 (STR_IN_SET(option, "noauto", "auto", "nofail", "fail"))
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, "header=")) {
140 arg_type = CRYPT_LUKS1;
142 if (!path_is_absolute(option+7)) {
143 log_error("Header path '%s' is not absolute, refusing.", option+7);
148 log_error("Duplicate header= options, refusing.");
152 arg_header = strdup(option+7);
156 } else if (startswith(option, "tries=")) {
158 if (safe_atou(option+6, &arg_tries) < 0) {
159 log_error("tries= parse failure, ignoring.");
163 } else if (STR_IN_SET(option, "readonly", "read-only"))
165 else if (streq(option, "verify"))
167 else if (STR_IN_SET(option, "allow-discards", "discard"))
169 else if (streq(option, "luks"))
170 arg_type = CRYPT_LUKS1;
171 else if (streq(option, "tcrypt"))
172 arg_type = CRYPT_TCRYPT;
173 else if (streq(option, "tcrypt-hidden")) {
174 arg_type = CRYPT_TCRYPT;
175 arg_tcrypt_hidden = true;
176 } else if (streq(option, "tcrypt-system")) {
177 arg_type = CRYPT_TCRYPT;
178 arg_tcrypt_system = true;
179 } else if (STR_IN_SET(option, "plain", "swap", "tmp"))
180 arg_type = CRYPT_PLAIN;
181 else if (startswith(option, "timeout=")) {
183 if (parse_sec(option+8, &arg_timeout) < 0) {
184 log_error("timeout= parse failure, ignoring.");
188 } else if (!streq(option, "none"))
189 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
194 static int parse_options(const char *options) {
195 const char *word, *state;
201 FOREACH_WORD_SEPARATOR(word, l, options, ",", state) {
202 _cleanup_free_ char *o;
204 o = strndup(word, l);
207 r = parse_one_option(o);
215 static void log_glue(int level, const char *msg, void *usrptr) {
216 log_debug("%s", msg);
219 static char* disk_description(const char *path) {
221 static const char name_fields[] =
222 "ID_PART_ENTRY_NAME\0"
224 "ID_MODEL_FROM_DATABASE\0"
227 _cleanup_udev_unref_ struct udev *udev = NULL;
228 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
234 if (stat(path, &st) < 0)
237 if (!S_ISBLK(st.st_mode))
244 device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
248 NULSTR_FOREACH(i, name_fields) {
251 name = udev_device_get_property_value(device, i);
259 static char *disk_mount_point(const char *label) {
260 _cleanup_free_ char *device = NULL;
261 _cleanup_endmntent_ FILE *f = NULL;
264 /* Yeah, we don't support native systemd unit files here for now */
266 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
269 f = setmntent("/etc/fstab", "r");
273 while ((m = getmntent(f)))
274 if (path_equal(m->mnt_fsname, device))
275 return strdup(m->mnt_dir);
280 static int get_password(const char *name, usec_t until, bool accept_cached, char ***passwords) {
283 _cleanup_free_ char *text = NULL;
284 _cleanup_free_ char *escaped_name = NULL;
290 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
293 escaped_name = cescape(name);
297 id = strjoina("cryptsetup:", escaped_name);
299 r = ask_password_auto(text, "drive-harddisk", id, until, accept_cached, passwords);
301 return log_error_errno(r, "Failed to query password: %m");
304 _cleanup_strv_free_ char **passwords2 = NULL;
306 assert(strv_length(*passwords) == 1);
308 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
311 id = strjoina("cryptsetup-verification:", escaped_name);
313 r = ask_password_auto(text, "drive-harddisk", id, until, false, &passwords2);
315 return log_error_errno(r, "Failed to query verification password: %m");
317 assert(strv_length(passwords2) == 1);
319 if (!streq(*passwords[0], passwords2[0])) {
320 log_warning("Passwords did not match, retrying.");
325 strv_uniq(*passwords);
327 STRV_FOREACH(p, *passwords) {
330 if (strlen(*p)+1 >= arg_key_size)
333 /* Pad password if necessary */
334 if (!(c = new(char, arg_key_size)))
337 strncpy(c, *p, arg_key_size);
345 static int attach_tcrypt(struct crypt_device *cd,
347 const char *key_file,
351 _cleanup_free_ char *passphrase = NULL;
352 struct crypt_params_tcrypt params = {
353 .flags = CRYPT_TCRYPT_LEGACY_MODES,
354 .keyfiles = (const char **)arg_tcrypt_keyfiles,
355 .keyfiles_count = strv_length(arg_tcrypt_keyfiles)
360 assert(key_file || (passwords && passwords[0]));
362 if (arg_tcrypt_hidden)
363 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
365 if (arg_tcrypt_system)
366 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
369 r = read_one_line_file(key_file, &passphrase);
371 log_error_errno(r, "Failed to read password file '%s': %m", key_file);
375 params.passphrase = passphrase;
377 params.passphrase = passwords[0];
378 params.passphrase_size = strlen(params.passphrase);
380 r = crypt_load(cd, CRYPT_TCRYPT, ¶ms);
382 if (key_file && r == -EPERM) {
383 log_error("Failed to activate using password file '%s'.", key_file);
389 return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
392 static int attach_luks_or_plain(struct crypt_device *cd,
394 const char *key_file,
395 const char *data_device,
399 bool pass_volume_key = false;
403 assert(key_file || passwords);
405 if (!arg_type || streq(arg_type, CRYPT_LUKS1)) {
406 r = crypt_load(cd, CRYPT_LUKS1, NULL);
408 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd));
413 r = crypt_set_data_device(cd, data_device);
416 if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) {
417 struct crypt_params_plain params = {};
418 const char *cipher, *cipher_mode;
419 _cleanup_free_ char *truncated_cipher = NULL;
422 /* plain isn't a real hash type. it just means "use no hash" */
423 if (!streq(arg_hash, "plain"))
424 params.hash = arg_hash;
425 } else if (!key_file)
426 /* for CRYPT_PLAIN, the behaviour of cryptsetup
427 * package is to not hash when a key file is provided */
428 params.hash = "ripemd160";
433 l = strcspn(arg_cipher, "-");
434 truncated_cipher = strndup(arg_cipher, l);
435 if (!truncated_cipher)
438 cipher = truncated_cipher;
439 cipher_mode = arg_cipher[l] ? arg_cipher+l+1 : "plain";
442 cipher_mode = "cbc-essiv:sha256";
445 /* for CRYPT_PLAIN limit reads
446 * from keyfile to key length, and
447 * ignore keyfile-size */
448 arg_keyfile_size = arg_key_size;
450 /* In contrast to what the name
451 * crypt_setup() might suggest this
452 * doesn't actually format anything,
453 * it just configures encryption
454 * parameters when used for plain
456 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode,
457 NULL, NULL, arg_keyfile_size, ¶ms);
459 /* hash == NULL implies the user passed "plain" */
460 pass_volume_key = (params.hash == NULL);
464 return log_error_errno(r, "Loading of cryptographic parameters failed: %m");
466 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
467 crypt_get_cipher(cd),
468 crypt_get_cipher_mode(cd),
469 crypt_get_volume_key_size(cd)*8,
470 crypt_get_device_name(cd));
473 r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot,
474 key_file, arg_keyfile_size,
475 arg_keyfile_offset, flags);
477 log_error_errno(r, "Failed to activate with key file '%s': %m", key_file);
483 STRV_FOREACH(p, passwords) {
485 r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
487 r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
497 static int help(void) {
499 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
500 "%s detach VOLUME\n\n"
501 "Attaches or detaches an encrypted block device.\n",
502 program_invocation_short_name,
503 program_invocation_short_name);
508 int main(int argc, char *argv[]) {
509 int r = EXIT_FAILURE;
510 struct crypt_device *cd = NULL;
518 log_error("This program requires at least two arguments.");
522 log_set_target(LOG_TARGET_AUTO);
523 log_parse_environment();
528 if (streq(argv[1], "attach")) {
533 crypt_status_info status;
534 const char *key_file = NULL, *name = NULL;
535 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
537 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
540 log_error("attach requires at least two arguments.");
546 !streq(argv[4], "-") &&
547 !streq(argv[4], "none")) {
549 if (!path_is_absolute(argv[4]))
550 log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]);
555 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
556 if (parse_options(argv[5]) < 0)
560 /* A delicious drop of snake oil */
561 mlockall(MCL_FUTURE);
563 description = disk_description(argv[3]);
564 mount_point = disk_mount_point(argv[2]);
566 if (description && streq(argv[2], description)) {
567 /* If the description string is simply the
568 * volume name, then let's not show this
575 if (mount_point && description)
576 k = asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
577 else if (mount_point)
578 k = asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
579 else if (description)
580 k = asprintf(&name_buffer, "%s (%s)", description, argv[2]);
586 name = name_buffer ? name_buffer : argv[2];
589 log_debug("LUKS header: %s", arg_header);
590 k = crypt_init(&cd, arg_header);
592 k = crypt_init(&cd, argv[3]);
595 log_error_errno(k, "crypt_init() failed: %m");
599 crypt_set_log_callback(cd, log_glue, NULL);
601 status = crypt_status(cd, argv[2]);
602 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
603 log_info("Volume %s already active.", argv[2]);
609 flags |= CRYPT_ACTIVATE_READONLY;
612 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
615 until = now(CLOCK_MONOTONIC) + arg_timeout;
619 arg_key_size = (arg_key_size > 0 ? arg_key_size : (256 / 8));
624 /* Ideally we'd do this on the open fd, but since this is just a
625 * warning it's OK to do this in two steps. */
626 if (stat(key_file, &st) >= 0 && S_ISREG(st.st_mode) && (st.st_mode & 0005))
627 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
630 for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) {
631 _cleanup_strv_free_ char **passwords = NULL;
634 k = get_password(name, until, tries == 0 && !arg_verify, &passwords);
641 if (streq_ptr(arg_type, CRYPT_TCRYPT))
642 k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
644 k = attach_luks_or_plain(cd,
647 arg_header ? argv[3] : NULL,
652 else if (k == -EAGAIN) {
655 } else if (k != -EPERM) {
656 log_error_errno(k, "Failed to activate: %m");
660 log_warning("Invalid passphrase.");
663 if (arg_tries != 0 && tries >= arg_tries) {
664 log_error("Too many attempts; giving up.");
669 } else if (streq(argv[1], "detach")) {
672 k = crypt_init_by_name(&cd, argv[2]);
674 log_error_errno(k, "crypt_init() failed: %m");
678 crypt_set_log_callback(cd, log_glue, NULL);
680 k = crypt_deactivate(cd, argv[2]);
682 log_error_errno(k, "Failed to deactivate: %m");
687 log_error("Unknown verb %s.", argv[1]);
701 strv_free(arg_tcrypt_keyfiles);