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 unsigned opt_keyfile_size = 0;
43 static unsigned opt_keyfile_offset = 0;
44 static char *opt_hash = NULL;
45 static unsigned opt_tries = 3;
46 static bool opt_readonly = false;
47 static bool opt_verify = false;
48 static bool opt_discards = false;
49 static bool opt_tcrypt_hidden = false;
50 static bool opt_tcrypt_system = false;
51 static char **opt_tcrypt_keyfiles = NULL;
52 static usec_t opt_timeout = 0;
54 /* Options Debian's crypttab knows we don't:
66 static int parse_one_option(const char *option) {
69 /* Handled outside of this tool */
70 if (streq(option, "noauto") || streq(option, "nofail"))
73 if (startswith(option, "cipher=")) {
83 } else if (startswith(option, "size=")) {
85 if (safe_atou(option+5, &opt_key_size) < 0) {
86 log_error("size= parse failure, ignoring.");
90 } else if (startswith(option, "tcrypt-keyfile=")) {
92 opt_type = CRYPT_TCRYPT;
93 if (path_is_absolute(option+15)) {
94 if (strv_extend(&opt_tcrypt_keyfiles, option + 15) < 0)
97 log_error("Key file path '%s' is not absolute. Ignoring.", option+15);
99 } else if (startswith(option, "keyfile-size=")) {
101 if (safe_atou(option+13, &opt_keyfile_size) < 0) {
102 log_error("keyfile-size= parse failure, ignoring.");
106 } else if (startswith(option, "keyfile-offset=")) {
108 if (safe_atou(option+15, &opt_keyfile_offset) < 0) {
109 log_error("keyfile-offset= parse failure, ignoring.");
113 } else if (startswith(option, "hash=")) {
116 t = strdup(option+5);
123 } else if (startswith(option, "tries=")) {
125 if (safe_atou(option+6, &opt_tries) < 0) {
126 log_error("tries= parse failure, ignoring.");
130 } else if (streq(option, "readonly") || streq(option, "read-only"))
132 else if (streq(option, "verify"))
134 else if (streq(option, "allow-discards") || streq(option, "discard"))
136 else if (streq(option, "luks"))
137 opt_type = CRYPT_LUKS1;
138 else if (streq(option, "tcrypt"))
139 opt_type = CRYPT_TCRYPT;
140 else if (streq(option, "tcrypt-hidden")) {
141 opt_type = CRYPT_TCRYPT;
142 opt_tcrypt_hidden = true;
143 } else if (streq(option, "tcrypt-system")) {
144 opt_type = CRYPT_TCRYPT;
145 opt_tcrypt_system = true;
146 } else if (streq(option, "plain") ||
147 streq(option, "swap") ||
148 streq(option, "tmp"))
149 opt_type = CRYPT_PLAIN;
150 else if (startswith(option, "timeout=")) {
152 if (parse_sec(option+8, &opt_timeout) < 0) {
153 log_error("timeout= parse failure, ignoring.");
157 } else if (!streq(option, "none"))
158 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
163 static int parse_options(const char *options) {
170 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
171 _cleanup_free_ char *o;
176 r = parse_one_option(o);
184 static void log_glue(int level, const char *msg, void *usrptr) {
185 log_debug("%s", msg);
188 static char* disk_description(const char *path) {
190 static const char name_fields[] = {
191 "ID_PART_ENTRY_NAME\0"
193 "ID_MODEL_FROM_DATABASE\0"
197 _cleanup_udev_unref_ struct udev *udev = NULL;
198 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
204 if (stat(path, &st) < 0)
207 if (!S_ISBLK(st.st_mode))
214 device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
218 NULSTR_FOREACH(i, name_fields) {
221 name = udev_device_get_property_value(device, i);
229 static char *disk_mount_point(const char *label) {
230 _cleanup_free_ char *device = NULL;
231 _cleanup_endmntent_ FILE *f = NULL;
234 /* Yeah, we don't support native systemd unit files here for now */
236 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
239 f = setmntent("/etc/fstab", "r");
243 while ((m = getmntent(f)))
244 if (path_equal(m->mnt_fsname, device))
245 return strdup(m->mnt_dir);
250 static int get_password(const char *name, usec_t until, bool accept_cached, char ***passwords) {
253 _cleanup_free_ char *text = NULL;
258 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
261 r = ask_password_auto(text, "drive-harddisk", until, accept_cached, passwords);
263 log_error("Failed to query password: %s", strerror(-r));
268 _cleanup_strv_free_ char **passwords2 = NULL;
270 assert(strv_length(*passwords) == 1);
272 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
275 r = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
277 log_error("Failed to query verification password: %s", strerror(-r));
281 assert(strv_length(passwords2) == 1);
283 if (!streq(*passwords[0], passwords2[0])) {
284 log_warning("Passwords did not match, retrying.");
289 strv_uniq(*passwords);
291 STRV_FOREACH(p, *passwords) {
294 if (strlen(*p)+1 >= opt_key_size)
297 /* Pad password if necessary */
298 if (!(c = new(char, opt_key_size)))
301 strncpy(c, *p, opt_key_size);
309 static int attach_tcrypt(struct crypt_device *cd,
311 const char *key_file,
315 _cleanup_free_ char *passphrase = NULL;
316 struct crypt_params_tcrypt params = {
317 .flags = CRYPT_TCRYPT_LEGACY_MODES,
318 .keyfiles = (const char **)opt_tcrypt_keyfiles,
319 .keyfiles_count = strv_length(opt_tcrypt_keyfiles)
324 assert(key_file || passwords);
326 if (opt_tcrypt_hidden)
327 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
329 if (opt_tcrypt_system)
330 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
333 r = read_one_line_file(key_file, &passphrase);
335 log_error("Failed to read password file '%s': %s", key_file, strerror(-r));
339 params.passphrase = passphrase;
341 params.passphrase = passwords[0];
342 params.passphrase_size = strlen(params.passphrase);
344 r = crypt_load(cd, CRYPT_TCRYPT, ¶ms);
346 if (key_file && r == -EPERM) {
347 log_error("Failed to activate using password file '%s'.", key_file);
353 return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);;
356 static int attach_luks_or_plain(struct crypt_device *cd,
358 const char *key_file,
362 bool pass_volume_key = false;
366 assert(key_file || passwords);
368 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
369 r = crypt_load(cd, CRYPT_LUKS1, NULL);
371 if ((!opt_type && r < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
372 struct crypt_params_plain params = {};
373 const char *cipher, *cipher_mode;
374 _cleanup_free_ char *truncated_cipher = NULL;
377 /* plain isn't a real hash type. it just means "use no hash" */
378 if (!streq(opt_hash, "plain"))
379 params.hash = opt_hash;
381 params.hash = "ripemd160";
386 l = strcspn(opt_cipher, "-");
387 truncated_cipher = strndup(opt_cipher, l);
388 if (!truncated_cipher)
391 cipher = truncated_cipher;
392 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
395 cipher_mode = "cbc-essiv:sha256";
398 /* for CRYPT_PLAIN limit reads
399 * from keyfile to key length, and
400 * ignore keyfile-size */
401 opt_keyfile_size = opt_key_size / 8;
403 /* In contrast to what the name
404 * crypt_setup() might suggest this
405 * doesn't actually format anything,
406 * it just configures encryption
407 * parameters when used for plain
409 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode,
410 NULL, NULL, opt_keyfile_size, ¶ms);
412 /* hash == NULL implies the user passed "plain" */
413 pass_volume_key = (params.hash == NULL);
417 log_error("Loading of cryptographic parameters failed: %s", strerror(-r));
421 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
422 crypt_get_cipher(cd),
423 crypt_get_cipher_mode(cd),
424 crypt_get_volume_key_size(cd)*8,
425 crypt_get_device_name(cd));
428 r = crypt_activate_by_keyfile_offset(cd, name, CRYPT_ANY_SLOT,
429 key_file, opt_keyfile_size,
430 opt_keyfile_offset, flags);
432 log_error("Failed to activate with key file '%s': %s", key_file, strerror(-r));
438 STRV_FOREACH(p, passwords) {
440 r = crypt_activate_by_volume_key(cd, name, *p, opt_key_size, flags);
442 r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, *p, strlen(*p), flags);
452 static int help(void) {
454 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
455 "%s detach VOLUME\n\n"
456 "Attaches or detaches an encrypted block device.\n",
457 program_invocation_short_name,
458 program_invocation_short_name);
463 int main(int argc, char *argv[]) {
464 int r = EXIT_FAILURE;
465 struct crypt_device *cd = NULL;
473 log_error("This program requires at least two arguments.");
477 log_set_target(LOG_TARGET_AUTO);
478 log_parse_environment();
483 if (streq(argv[1], "attach")) {
488 crypt_status_info status;
489 const char *key_file = NULL, *name = NULL;
490 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
492 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
495 log_error("attach requires at least two arguments.");
501 !streq(argv[4], "-") &&
502 !streq(argv[4], "none")) {
504 if (!path_is_absolute(argv[4]))
505 log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]);
510 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
511 if (parse_options(argv[5]) < 0)
515 /* A delicious drop of snake oil */
516 mlockall(MCL_FUTURE);
518 description = disk_description(argv[3]);
519 mount_point = disk_mount_point(argv[2]);
521 if (description && streq(argv[2], description)) {
522 /* If the description string is simply the
523 * volume name, then let's not show this
529 if (mount_point && description)
530 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
531 else if (mount_point)
532 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
533 else if (description)
534 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
536 name = name_buffer ? name_buffer : argv[2];
538 k = crypt_init(&cd, argv[3]);
540 log_error("crypt_init() failed: %s", strerror(-k));
544 crypt_set_log_callback(cd, log_glue, NULL);
546 status = crypt_status(cd, argv[2]);
547 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
548 log_info("Volume %s already active.", argv[2]);
554 flags |= CRYPT_ACTIVATE_READONLY;
557 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
560 until = now(CLOCK_MONOTONIC) + opt_timeout;
564 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
569 /* Ideally we'd do this on the open fd, but since this is just a
570 * warning it's OK to do this in two steps. */
571 if (stat(key_file, &st) >= 0 && (st.st_mode & 0005))
572 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
575 for (tries = 0; opt_tries == 0 || tries < opt_tries; tries++) {
576 _cleanup_strv_free_ char **passwords = NULL;
579 k = get_password(name, until, tries == 0 && !opt_verify, &passwords);
586 if (streq_ptr(opt_type, CRYPT_TCRYPT))
587 k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
589 k = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags);
592 else if (k == -EAGAIN) {
595 } else if (k != -EPERM) {
596 log_error("Failed to activate: %s", strerror(-k));
600 log_warning("Invalid passphrase.");
603 if (opt_tries != 0 && tries >= opt_tries) {
604 log_error("Too many attempts; giving up.");
609 } else if (streq(argv[1], "detach")) {
612 k = crypt_init_by_name(&cd, argv[2]);
614 log_error("crypt_init() failed: %s", strerror(-k));
618 crypt_set_log_callback(cd, log_glue, NULL);
620 k = crypt_deactivate(cd, argv[2]);
622 log_error("Failed to deactivate: %s", strerror(-k));
627 log_error("Unknown verb %s.", argv[1]);
640 strv_free(opt_tcrypt_keyfiles);