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 *opt_type = NULL; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
40 static char *opt_cipher = NULL;
41 static unsigned opt_key_size = 0;
42 static int opt_key_slot = CRYPT_ANY_SLOT;
43 static unsigned opt_keyfile_size = 0;
44 static unsigned opt_keyfile_offset = 0;
45 static char *opt_hash = NULL;
46 static unsigned opt_tries = 3;
47 static bool opt_readonly = false;
48 static bool opt_verify = false;
49 static bool opt_discards = false;
50 static bool opt_tcrypt_hidden = false;
51 static bool opt_tcrypt_system = false;
52 static char **opt_tcrypt_keyfiles = NULL;
53 static usec_t opt_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, &opt_key_size) < 0) {
87 log_error("size= parse failure, ignoring.");
91 } else if (startswith(option, "key-slot=")) {
93 opt_type = CRYPT_LUKS1;
94 if (safe_atoi(option+9, &opt_key_slot) < 0) {
95 log_error("key-slot= parse failure, ignoring.");
99 } else if (startswith(option, "tcrypt-keyfile=")) {
101 opt_type = CRYPT_TCRYPT;
102 if (path_is_absolute(option+15)) {
103 if (strv_extend(&opt_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, &opt_keyfile_size) < 0) {
111 log_error("keyfile-size= parse failure, ignoring.");
115 } else if (startswith(option, "keyfile-offset=")) {
117 if (safe_atou(option+15, &opt_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, &opt_tries) < 0) {
135 log_error("tries= parse failure, ignoring.");
139 } else if (streq(option, "readonly") || streq(option, "read-only"))
141 else if (streq(option, "verify"))
143 else if (streq(option, "allow-discards") || streq(option, "discard"))
145 else if (streq(option, "luks"))
146 opt_type = CRYPT_LUKS1;
147 else if (streq(option, "tcrypt"))
148 opt_type = CRYPT_TCRYPT;
149 else if (streq(option, "tcrypt-hidden")) {
150 opt_type = CRYPT_TCRYPT;
151 opt_tcrypt_hidden = true;
152 } else if (streq(option, "tcrypt-system")) {
153 opt_type = CRYPT_TCRYPT;
154 opt_tcrypt_system = true;
155 } else if (streq(option, "plain") ||
156 streq(option, "swap") ||
157 streq(option, "tmp"))
158 opt_type = CRYPT_PLAIN;
159 else if (startswith(option, "timeout=")) {
161 if (parse_sec(option+8, &opt_timeout) < 0) {
162 log_error("timeout= parse failure, ignoring.");
166 } else if (!streq(option, "none"))
167 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
172 static int parse_options(const char *options) {
179 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
180 _cleanup_free_ char *o;
185 r = parse_one_option(o);
193 static void log_glue(int level, const char *msg, void *usrptr) {
194 log_debug("%s", msg);
197 static char* disk_description(const char *path) {
199 static const char name_fields[] = {
200 "ID_PART_ENTRY_NAME\0"
202 "ID_MODEL_FROM_DATABASE\0"
206 _cleanup_udev_unref_ struct udev *udev = NULL;
207 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
213 if (stat(path, &st) < 0)
216 if (!S_ISBLK(st.st_mode))
223 device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
227 NULSTR_FOREACH(i, name_fields) {
230 name = udev_device_get_property_value(device, i);
238 static char *disk_mount_point(const char *label) {
239 _cleanup_free_ char *device = NULL;
240 _cleanup_endmntent_ FILE *f = 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 return strdup(m->mnt_dir);
259 static int get_password(const char *name, usec_t until, bool accept_cached, char ***passwords) {
262 _cleanup_free_ char *text = NULL;
267 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
270 r = ask_password_auto(text, "drive-harddisk", until, accept_cached, passwords);
272 log_error("Failed to query password: %s", strerror(-r));
277 _cleanup_strv_free_ char **passwords2 = NULL;
279 assert(strv_length(*passwords) == 1);
281 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
284 r = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
286 log_error("Failed to query verification password: %s", strerror(-r));
290 assert(strv_length(passwords2) == 1);
292 if (!streq(*passwords[0], passwords2[0])) {
293 log_warning("Passwords did not match, retrying.");
298 strv_uniq(*passwords);
300 STRV_FOREACH(p, *passwords) {
303 if (strlen(*p)+1 >= opt_key_size)
306 /* Pad password if necessary */
307 if (!(c = new(char, opt_key_size)))
310 strncpy(c, *p, opt_key_size);
318 static int attach_tcrypt(struct crypt_device *cd,
320 const char *key_file,
324 _cleanup_free_ char *passphrase = NULL;
325 struct crypt_params_tcrypt params = {
326 .flags = CRYPT_TCRYPT_LEGACY_MODES,
327 .keyfiles = (const char **)opt_tcrypt_keyfiles,
328 .keyfiles_count = strv_length(opt_tcrypt_keyfiles)
333 assert(key_file || passwords);
335 if (opt_tcrypt_hidden)
336 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
338 if (opt_tcrypt_system)
339 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
342 r = read_one_line_file(key_file, &passphrase);
344 log_error("Failed to read password file '%s': %s", key_file, strerror(-r));
348 params.passphrase = passphrase;
350 params.passphrase = passwords[0];
351 params.passphrase_size = strlen(params.passphrase);
353 r = crypt_load(cd, CRYPT_TCRYPT, ¶ms);
355 if (key_file && r == -EPERM) {
356 log_error("Failed to activate using password file '%s'.", key_file);
362 return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);;
365 static int attach_luks_or_plain(struct crypt_device *cd,
367 const char *key_file,
371 bool pass_volume_key = false;
375 assert(key_file || passwords);
377 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
378 r = crypt_load(cd, CRYPT_LUKS1, NULL);
380 if ((!opt_type && r < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
381 struct crypt_params_plain params = {};
382 const char *cipher, *cipher_mode;
383 _cleanup_free_ char *truncated_cipher = NULL;
386 /* plain isn't a real hash type. it just means "use no hash" */
387 if (!streq(opt_hash, "plain"))
388 params.hash = opt_hash;
390 params.hash = "ripemd160";
395 l = strcspn(opt_cipher, "-");
396 truncated_cipher = strndup(opt_cipher, l);
397 if (!truncated_cipher)
400 cipher = truncated_cipher;
401 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
404 cipher_mode = "cbc-essiv:sha256";
407 /* for CRYPT_PLAIN limit reads
408 * from keyfile to key length, and
409 * ignore keyfile-size */
410 opt_keyfile_size = opt_key_size / 8;
412 /* In contrast to what the name
413 * crypt_setup() might suggest this
414 * doesn't actually format anything,
415 * it just configures encryption
416 * parameters when used for plain
418 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode,
419 NULL, NULL, opt_keyfile_size, ¶ms);
421 /* hash == NULL implies the user passed "plain" */
422 pass_volume_key = (params.hash == NULL);
426 log_error("Loading of cryptographic parameters failed: %s", strerror(-r));
430 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
431 crypt_get_cipher(cd),
432 crypt_get_cipher_mode(cd),
433 crypt_get_volume_key_size(cd)*8,
434 crypt_get_device_name(cd));
437 r = crypt_activate_by_keyfile_offset(cd, name, opt_key_slot,
438 key_file, opt_keyfile_size,
439 opt_keyfile_offset, flags);
441 log_error("Failed to activate with key file '%s': %s", key_file, strerror(-r));
447 STRV_FOREACH(p, passwords) {
449 r = crypt_activate_by_volume_key(cd, name, *p, opt_key_size, flags);
451 r = crypt_activate_by_passphrase(cd, name, opt_key_slot, *p, strlen(*p), flags);
461 static int help(void) {
463 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
464 "%s detach VOLUME\n\n"
465 "Attaches or detaches an encrypted block device.\n",
466 program_invocation_short_name,
467 program_invocation_short_name);
472 int main(int argc, char *argv[]) {
473 int r = EXIT_FAILURE;
474 struct crypt_device *cd = NULL;
482 log_error("This program requires at least two arguments.");
486 log_set_target(LOG_TARGET_AUTO);
487 log_parse_environment();
492 if (streq(argv[1], "attach")) {
497 crypt_status_info status;
498 const char *key_file = NULL, *name = NULL;
499 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
501 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
504 log_error("attach requires at least two arguments.");
510 !streq(argv[4], "-") &&
511 !streq(argv[4], "none")) {
513 if (!path_is_absolute(argv[4]))
514 log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]);
519 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
520 if (parse_options(argv[5]) < 0)
524 /* A delicious drop of snake oil */
525 mlockall(MCL_FUTURE);
527 description = disk_description(argv[3]);
528 mount_point = disk_mount_point(argv[2]);
530 if (description && streq(argv[2], description)) {
531 /* If the description string is simply the
532 * volume name, then let's not show this
538 if (mount_point && description)
539 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
540 else if (mount_point)
541 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
542 else if (description)
543 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
545 name = name_buffer ? name_buffer : argv[2];
547 k = crypt_init(&cd, argv[3]);
549 log_error("crypt_init() failed: %s", strerror(-k));
553 crypt_set_log_callback(cd, log_glue, NULL);
555 status = crypt_status(cd, argv[2]);
556 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
557 log_info("Volume %s already active.", argv[2]);
563 flags |= CRYPT_ACTIVATE_READONLY;
566 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
569 until = now(CLOCK_MONOTONIC) + opt_timeout;
573 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
578 /* Ideally we'd do this on the open fd, but since this is just a
579 * warning it's OK to do this in two steps. */
580 if (stat(key_file, &st) >= 0 && (st.st_mode & 0005))
581 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
584 for (tries = 0; opt_tries == 0 || tries < opt_tries; tries++) {
585 _cleanup_strv_free_ char **passwords = NULL;
588 k = get_password(name, until, tries == 0 && !opt_verify, &passwords);
595 if (streq_ptr(opt_type, CRYPT_TCRYPT))
596 k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
598 k = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags);
601 else if (k == -EAGAIN) {
604 } else if (k != -EPERM) {
605 log_error("Failed to activate: %s", strerror(-k));
609 log_warning("Invalid passphrase.");
612 if (opt_tries != 0 && tries >= opt_tries) {
613 log_error("Too many attempts; giving up.");
618 } else if (streq(argv[1], "detach")) {
621 k = crypt_init_by_name(&cd, argv[2]);
623 log_error("crypt_init() failed: %s", strerror(-k));
627 crypt_set_log_callback(cd, log_glue, NULL);
629 k = crypt_deactivate(cd, argv[2]);
631 log_error("Failed to deactivate: %s", strerror(-k));
636 log_error("Unknown verb %s.", argv[1]);
649 strv_free(opt_tcrypt_keyfiles);