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"
35 #include "sd-device.h"
36 #include "device-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_device_unref_ sd_device *device = NULL;
234 if (stat(path, &st) < 0)
237 if (!S_ISBLK(st.st_mode))
240 r = sd_device_new_from_devnum(&device, 'b', st.st_rdev);
244 NULSTR_FOREACH(i, name_fields) {
247 r = sd_device_get_property_value(device, i, &name);
248 if (r >= 0 && !isempty(name))
255 static char *disk_mount_point(const char *label) {
256 _cleanup_free_ char *device = NULL;
257 _cleanup_endmntent_ FILE *f = NULL;
260 /* Yeah, we don't support native systemd unit files here for now */
262 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
265 f = setmntent("/etc/fstab", "r");
269 while ((m = getmntent(f)))
270 if (path_equal(m->mnt_fsname, device))
271 return strdup(m->mnt_dir);
276 static int get_password(const char *name, usec_t until, bool accept_cached, char ***passwords) {
279 _cleanup_free_ char *text = NULL;
280 _cleanup_free_ char *escaped_name = NULL;
286 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
289 escaped_name = cescape(name);
293 id = strjoina("cryptsetup:", escaped_name);
295 r = ask_password_auto(text, "drive-harddisk", id, until, accept_cached, passwords);
297 return log_error_errno(r, "Failed to query password: %m");
300 _cleanup_strv_free_ char **passwords2 = NULL;
302 assert(strv_length(*passwords) == 1);
304 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
307 id = strjoina("cryptsetup-verification:", escaped_name);
309 r = ask_password_auto(text, "drive-harddisk", id, until, false, &passwords2);
311 return log_error_errno(r, "Failed to query verification password: %m");
313 assert(strv_length(passwords2) == 1);
315 if (!streq(*passwords[0], passwords2[0])) {
316 log_warning("Passwords did not match, retrying.");
321 strv_uniq(*passwords);
323 STRV_FOREACH(p, *passwords) {
326 if (strlen(*p)+1 >= arg_key_size)
329 /* Pad password if necessary */
330 if (!(c = new(char, arg_key_size)))
333 strncpy(c, *p, arg_key_size);
341 static int attach_tcrypt(struct crypt_device *cd,
343 const char *key_file,
347 _cleanup_free_ char *passphrase = NULL;
348 struct crypt_params_tcrypt params = {
349 .flags = CRYPT_TCRYPT_LEGACY_MODES,
350 .keyfiles = (const char **)arg_tcrypt_keyfiles,
351 .keyfiles_count = strv_length(arg_tcrypt_keyfiles)
356 assert(key_file || (passwords && passwords[0]));
358 if (arg_tcrypt_hidden)
359 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
361 if (arg_tcrypt_system)
362 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
365 r = read_one_line_file(key_file, &passphrase);
367 log_error_errno(r, "Failed to read password file '%s': %m", key_file);
371 params.passphrase = passphrase;
373 params.passphrase = passwords[0];
374 params.passphrase_size = strlen(params.passphrase);
376 r = crypt_load(cd, CRYPT_TCRYPT, ¶ms);
378 if (key_file && r == -EPERM) {
379 log_error("Failed to activate using password file '%s'.", key_file);
385 return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
388 static int attach_luks_or_plain(struct crypt_device *cd,
390 const char *key_file,
391 const char *data_device,
395 bool pass_volume_key = false;
399 assert(key_file || passwords);
401 if (!arg_type || streq(arg_type, CRYPT_LUKS1)) {
402 r = crypt_load(cd, CRYPT_LUKS1, NULL);
404 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd));
409 r = crypt_set_data_device(cd, data_device);
412 if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) {
413 struct crypt_params_plain params = {};
414 const char *cipher, *cipher_mode;
415 _cleanup_free_ char *truncated_cipher = NULL;
418 /* plain isn't a real hash type. it just means "use no hash" */
419 if (!streq(arg_hash, "plain"))
420 params.hash = arg_hash;
421 } else if (!key_file)
422 /* for CRYPT_PLAIN, the behaviour of cryptsetup
423 * package is to not hash when a key file is provided */
424 params.hash = "ripemd160";
429 l = strcspn(arg_cipher, "-");
430 truncated_cipher = strndup(arg_cipher, l);
431 if (!truncated_cipher)
434 cipher = truncated_cipher;
435 cipher_mode = arg_cipher[l] ? arg_cipher+l+1 : "plain";
438 cipher_mode = "cbc-essiv:sha256";
441 /* for CRYPT_PLAIN limit reads
442 * from keyfile to key length, and
443 * ignore keyfile-size */
444 arg_keyfile_size = arg_key_size;
446 /* In contrast to what the name
447 * crypt_setup() might suggest this
448 * doesn't actually format anything,
449 * it just configures encryption
450 * parameters when used for plain
452 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode,
453 NULL, NULL, arg_keyfile_size, ¶ms);
455 /* hash == NULL implies the user passed "plain" */
456 pass_volume_key = (params.hash == NULL);
460 return log_error_errno(r, "Loading of cryptographic parameters failed: %m");
462 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
463 crypt_get_cipher(cd),
464 crypt_get_cipher_mode(cd),
465 crypt_get_volume_key_size(cd)*8,
466 crypt_get_device_name(cd));
469 r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot,
470 key_file, arg_keyfile_size,
471 arg_keyfile_offset, flags);
473 log_error_errno(r, "Failed to activate with key file '%s': %m", key_file);
479 STRV_FOREACH(p, passwords) {
481 r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
483 r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
493 static int help(void) {
495 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
496 "%s detach VOLUME\n\n"
497 "Attaches or detaches an encrypted block device.\n",
498 program_invocation_short_name,
499 program_invocation_short_name);
504 int main(int argc, char *argv[]) {
505 int r = EXIT_FAILURE;
506 struct crypt_device *cd = NULL;
514 log_error("This program requires at least two arguments.");
518 log_set_target(LOG_TARGET_AUTO);
519 log_parse_environment();
524 if (streq(argv[1], "attach")) {
529 crypt_status_info status;
530 const char *key_file = NULL, *name = NULL;
531 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
533 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
536 log_error("attach requires at least two arguments.");
542 !streq(argv[4], "-") &&
543 !streq(argv[4], "none")) {
545 if (!path_is_absolute(argv[4]))
546 log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]);
551 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
552 if (parse_options(argv[5]) < 0)
556 /* A delicious drop of snake oil */
557 mlockall(MCL_FUTURE);
559 description = disk_description(argv[3]);
560 mount_point = disk_mount_point(argv[2]);
562 if (description && streq(argv[2], description)) {
563 /* If the description string is simply the
564 * volume name, then let's not show this
571 if (mount_point && description)
572 k = asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
573 else if (mount_point)
574 k = asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
575 else if (description)
576 k = asprintf(&name_buffer, "%s (%s)", description, argv[2]);
582 name = name_buffer ? name_buffer : argv[2];
585 log_debug("LUKS header: %s", arg_header);
586 k = crypt_init(&cd, arg_header);
588 k = crypt_init(&cd, argv[3]);
591 log_error_errno(k, "crypt_init() failed: %m");
595 crypt_set_log_callback(cd, log_glue, NULL);
597 status = crypt_status(cd, argv[2]);
598 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
599 log_info("Volume %s already active.", argv[2]);
605 flags |= CRYPT_ACTIVATE_READONLY;
608 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
611 until = now(CLOCK_MONOTONIC) + arg_timeout;
615 arg_key_size = (arg_key_size > 0 ? arg_key_size : (256 / 8));
620 /* Ideally we'd do this on the open fd, but since this is just a
621 * warning it's OK to do this in two steps. */
622 if (stat(key_file, &st) >= 0 && S_ISREG(st.st_mode) && (st.st_mode & 0005))
623 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
626 for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) {
627 _cleanup_strv_free_ char **passwords = NULL;
630 k = get_password(name, until, tries == 0 && !arg_verify, &passwords);
637 if (streq_ptr(arg_type, CRYPT_TCRYPT))
638 k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
640 k = attach_luks_or_plain(cd,
643 arg_header ? argv[3] : NULL,
648 else if (k == -EAGAIN) {
651 } else if (k != -EPERM) {
652 log_error_errno(k, "Failed to activate: %m");
656 log_warning("Invalid passphrase.");
659 if (arg_tries != 0 && tries >= arg_tries) {
660 log_error("Too many attempts; giving up.");
665 } else if (streq(argv[1], "detach")) {
668 k = crypt_init_by_name(&cd, argv[2]);
670 log_error_errno(k, "crypt_init() failed: %m");
674 crypt_set_log_callback(cd, log_glue, NULL);
676 k = crypt_deactivate(cd, argv[2]);
678 log_error_errno(k, "Failed to deactivate: %m");
683 log_error("Unknown verb %s.", argv[1]);
697 strv_free(arg_tcrypt_keyfiles);