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 static const char *opt_type = NULL; /* LUKS1 or PLAIN */
38 static char *opt_cipher = NULL;
39 static unsigned opt_key_size = 0;
40 static unsigned opt_keyfile_size = 0;
41 static unsigned opt_keyfile_offset = 0;
42 static char *opt_hash = NULL;
43 static unsigned opt_tries = 0;
44 static bool opt_readonly = false;
45 static bool opt_verify = false;
46 static bool opt_discards = false;
47 static usec_t opt_timeout = 0;
49 /* Options Debian's crypttab knows we don't:
61 static int parse_one_option(const char *option) {
64 /* Handled outside of this tool */
65 if (streq(option, "noauto") || streq(option, "nofail"))
68 if (startswith(option, "cipher=")) {
78 } else if (startswith(option, "size=")) {
80 if (safe_atou(option+5, &opt_key_size) < 0) {
81 log_error("size= parse failure, ignoring.");
85 } else if (startswith(option, "keyfile-size=")) {
87 if (safe_atou(option+13, &opt_keyfile_size) < 0) {
88 log_error("keyfile-size= parse failure, ignoring.");
92 } else if (startswith(option, "keyfile-offset=")) {
94 if (safe_atou(option+15, &opt_keyfile_offset) < 0) {
95 log_error("keyfile-offset= parse failure, ignoring.");
99 } else if (startswith(option, "hash=")) {
102 t = strdup(option+5);
109 } else if (startswith(option, "tries=")) {
111 if (safe_atou(option+6, &opt_tries) < 0) {
112 log_error("tries= parse failure, ignoring.");
116 } else if (streq(option, "readonly") || streq(option, "read-only"))
118 else if (streq(option, "verify"))
120 else if (streq(option, "allow-discards"))
122 else if (streq(option, "luks"))
123 opt_type = CRYPT_LUKS1;
124 else if (streq(option, "plain") ||
125 streq(option, "swap") ||
126 streq(option, "tmp"))
127 opt_type = CRYPT_PLAIN;
128 else if (startswith(option, "timeout=")) {
130 if (parse_sec(option+8, &opt_timeout) < 0) {
131 log_error("timeout= parse failure, ignoring.");
135 } else if (!streq(option, "none"))
136 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
141 static int parse_options(const char *options) {
148 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
149 _cleanup_free_ char *o;
154 r = parse_one_option(o);
162 static void log_glue(int level, const char *msg, void *usrptr) {
163 log_debug("%s", msg);
166 static char *disk_description(const char *path) {
168 static const char name_fields[] = {
169 "ID_PART_ENTRY_NAME\0"
171 "ID_MODEL_FROM_DATABASE\0"
175 struct udev *udev = NULL;
176 struct udev_device *device = NULL;
178 char *description = NULL;
183 if (stat(path, &st) < 0)
186 if (!S_ISBLK(st.st_mode))
193 device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
197 NULSTR_FOREACH(i, name_fields) {
200 name = udev_device_get_property_value(device, i);
201 if (!isempty(name)) {
202 description = strdup(name);
209 udev_device_unref(device);
217 static char *disk_mount_point(const char *label) {
219 _cleanup_free_ char *device = NULL;
223 /* Yeah, we don't support native systemd unit files here for now */
225 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
228 f = setmntent("/etc/fstab", "r");
232 while ((m = getmntent(f)))
233 if (path_equal(m->mnt_fsname, device)) {
234 mp = strdup(m->mnt_dir);
245 static int get_password(const char *name, usec_t until, bool accept_cached, char ***passwords) {
248 _cleanup_free_ char *text = NULL;
253 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
256 r = ask_password_auto(text, "drive-harddisk", until, accept_cached, passwords);
258 log_error("Failed to query password: %s", strerror(-r));
263 _cleanup_strv_free_ char **passwords2 = NULL;
265 assert(strv_length(*passwords) == 1);
267 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
270 r = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
272 log_error("Failed to query verification password: %s", strerror(-r));
276 assert(strv_length(passwords2) == 1);
278 if (!streq(*passwords[0], passwords2[0])) {
279 log_warning("Passwords did not match, retrying.");
284 strv_uniq(*passwords);
286 STRV_FOREACH(p, *passwords) {
289 if (strlen(*p)+1 >= opt_key_size)
292 /* Pad password if necessary */
293 if (!(c = new(char, opt_key_size)))
296 strncpy(c, *p, opt_key_size);
304 static int attach_luks_or_plain(struct crypt_device *cd,
306 const char *key_file,
310 bool pass_volume_key = false;
314 assert(key_file || passwords);
316 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
317 r = crypt_load(cd, CRYPT_LUKS1, NULL);
319 if ((!opt_type && r < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
320 struct crypt_params_plain params = {};
321 const char *cipher, *cipher_mode;
322 _cleanup_free_ char *truncated_cipher = NULL;
325 /* plain isn't a real hash type. it just means "use no hash" */
326 if (!streq(opt_hash, "plain"))
327 params.hash = opt_hash;
329 params.hash = "ripemd160";
334 l = strcspn(opt_cipher, "-");
335 truncated_cipher = strndup(opt_cipher, l);
336 if (!truncated_cipher)
339 cipher = truncated_cipher;
340 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
343 cipher_mode = "cbc-essiv:sha256";
346 /* for CRYPT_PLAIN limit reads
347 * from keyfile to key length, and
348 * ignore keyfile-size */
349 opt_keyfile_size = opt_key_size / 8;
351 /* In contrast to what the name
352 * crypt_setup() might suggest this
353 * doesn't actually format anything,
354 * it just configures encryption
355 * parameters when used for plain
357 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode,
358 NULL, NULL, opt_keyfile_size, ¶ms);
360 /* hash == NULL implies the user passed "plain" */
361 pass_volume_key = (params.hash == NULL);
365 log_error("Loading of cryptographic parameters failed: %s", strerror(-r));
369 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
370 crypt_get_cipher(cd),
371 crypt_get_cipher_mode(cd),
372 crypt_get_volume_key_size(cd)*8,
373 crypt_get_device_name(cd));
376 r = crypt_activate_by_keyfile_offset(cd, name, CRYPT_ANY_SLOT,
377 key_file, opt_keyfile_size,
378 opt_keyfile_offset, flags);
380 log_error("Failed to activate with key file '%s': %s", key_file, strerror(-r));
386 STRV_FOREACH(p, passwords) {
388 r = crypt_activate_by_volume_key(cd, name, *p, opt_key_size, flags);
390 r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, *p, strlen(*p), flags);
400 static int help(void) {
402 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
403 "%s detach VOLUME\n\n"
404 "Attaches or detaches an encrypted block device.\n",
405 program_invocation_short_name,
406 program_invocation_short_name);
411 int main(int argc, char *argv[]) {
412 int r = EXIT_FAILURE;
413 struct crypt_device *cd = NULL;
421 log_error("This program requires at least two arguments.");
425 log_set_target(LOG_TARGET_AUTO);
426 log_parse_environment();
431 if (streq(argv[1], "attach")) {
436 crypt_status_info status;
437 const char *key_file = NULL, *name = NULL;
438 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
440 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
443 log_error("attach requires at least two arguments.");
449 !streq(argv[4], "-") &&
450 !streq(argv[4], "none")) {
452 if (!path_is_absolute(argv[4]))
453 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
458 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
459 if (parse_options(argv[5]) < 0)
463 /* A delicious drop of snake oil */
464 mlockall(MCL_FUTURE);
466 description = disk_description(argv[3]);
467 mount_point = disk_mount_point(argv[2]);
469 if (description && streq(argv[2], description)) {
470 /* If the description string is simply the
471 * volume name, then let's not show this
477 if (mount_point && description)
478 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
479 else if (mount_point)
480 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
481 else if (description)
482 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
484 name = name_buffer ? name_buffer : argv[2];
486 k = crypt_init(&cd, argv[3]);
488 log_error("crypt_init() failed: %s", strerror(-k));
492 crypt_set_log_callback(cd, log_glue, NULL);
494 status = crypt_status(cd, argv[2]);
495 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
496 log_info("Volume %s already active.", argv[2]);
502 flags |= CRYPT_ACTIVATE_READONLY;
505 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
508 until = now(CLOCK_MONOTONIC) + opt_timeout;
512 opt_tries = opt_tries > 0 ? opt_tries : 3;
513 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
518 /* Ideally we'd do this on the open fd, but since this is just a
519 * warning it's OK to do this in two steps. */
520 if (stat(key_file, &st) >= 0 && (st.st_mode & 0005))
521 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
524 for (tries = 0; tries < opt_tries; tries++) {
525 _cleanup_strv_free_ char **passwords = NULL;
528 k = get_password(name, until, tries == 0 && !opt_verify, &passwords);
535 k = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags);
538 else if (k == -EAGAIN) {
541 } else if (k != -EPERM) {
542 log_error("Failed to activate: %s", strerror(-k));
546 log_warning("Invalid passphrase.");
549 if (tries >= opt_tries) {
550 log_error("Too many attempts; giving up.");
555 } else if (streq(argv[1], "detach")) {
558 k = crypt_init_by_name(&cd, argv[2]);
560 log_error("crypt_init() failed: %s", strerror(-k));
564 crypt_set_log_callback(cd, log_glue, NULL);
566 k = crypt_deactivate(cd, argv[2]);
568 log_error("Failed to deactivate: %s", strerror(-k));
573 log_error("Unknown verb %s.", argv[1]);