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 help(void) {
306 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
307 "%s detach VOLUME\n\n"
308 "Attaches or detaches an encrypted block device.\n",
309 program_invocation_short_name,
310 program_invocation_short_name);
315 int main(int argc, char *argv[]) {
316 int r = EXIT_FAILURE;
317 struct crypt_device *cd = NULL;
325 log_error("This program requires at least two arguments.");
329 log_set_target(LOG_TARGET_AUTO);
330 log_parse_environment();
335 if (streq(argv[1], "attach")) {
340 crypt_status_info status;
341 const char *key_file = NULL, *cipher = NULL, *cipher_mode = NULL,
342 *hash = NULL, *name = NULL;
343 _cleanup_free_ char *description = NULL, *name_buffer = NULL,
344 *mount_point = NULL, *truncated_cipher = NULL;
346 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
349 log_error("attach requires at least two arguments.");
355 !streq(argv[4], "-") &&
356 !streq(argv[4], "none")) {
358 if (!path_is_absolute(argv[4]))
359 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
364 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
365 if (parse_options(argv[5]) < 0)
369 /* A delicious drop of snake oil */
370 mlockall(MCL_FUTURE);
372 description = disk_description(argv[3]);
373 mount_point = disk_mount_point(argv[2]);
375 if (description && streq(argv[2], description)) {
376 /* If the description string is simply the
377 * volume name, then let's not show this
383 if (mount_point && description)
384 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
385 else if (mount_point)
386 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
387 else if (description)
388 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
390 name = name_buffer ? name_buffer : argv[2];
392 k = crypt_init(&cd, argv[3]);
394 log_error("crypt_init() failed: %s", strerror(-k));
398 crypt_set_log_callback(cd, log_glue, NULL);
400 status = crypt_status(cd, argv[2]);
401 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
402 log_info("Volume %s already active.", argv[2]);
408 flags |= CRYPT_ACTIVATE_READONLY;
411 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
414 until = now(CLOCK_MONOTONIC) + opt_timeout;
418 opt_tries = opt_tries > 0 ? opt_tries : 3;
419 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
421 /* plain isn't a real hash type. it just means "use no hash" */
422 if (!streq(opt_hash, "plain"))
430 l = strcspn(opt_cipher, "-");
431 truncated_cipher = strndup(opt_cipher, l);
433 if (!truncated_cipher) {
438 cipher = truncated_cipher;
439 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
442 cipher_mode = "cbc-essiv:sha256";
445 for (try = 0; try < opt_tries; try++) {
446 bool pass_volume_key = false;
447 _cleanup_strv_free_ char **passwords = NULL;
450 k = get_password(name, until, try == 0 && !opt_verify, &passwords);
459 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
460 k = crypt_load(cd, CRYPT_LUKS1, NULL);
462 if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
463 struct crypt_params_plain params = { .hash = hash };
465 /* for CRYPT_PLAIN limit reads
466 * from keyfile to key length, and
467 * ignore keyfile-size */
468 opt_keyfile_size = opt_key_size / 8;
470 /* In contrast to what the name
471 * crypt_setup() might suggest this
472 * doesn't actually format anything,
473 * it just configures encryption
474 * parameters when used for plain
476 k = crypt_format(cd, CRYPT_PLAIN,
484 /* hash == NULL implies the user passed "plain" */
485 pass_volume_key = (hash == NULL);
489 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
493 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
494 crypt_get_cipher(cd),
495 crypt_get_cipher_mode(cd),
496 crypt_get_volume_key_size(cd)*8,
502 /* Ideally we'd do this on the open
503 * fd, but since this is just a
504 * warning it's OK to do this in two
506 if (stat(key_file, &st) >= 0 && (st.st_mode & 0005))
507 log_warning("Key file %s is world-readable. That's certainly not a good idea.", key_file);
509 k = crypt_activate_by_keyfile_offset(
510 cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_keyfile_size,
511 opt_keyfile_offset, flags);
513 log_error("Failed to activate with key file '%s': %s", key_file, strerror(-k));
520 STRV_FOREACH(p, passwords) {
523 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
525 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
536 log_error("Failed to activate: %s", strerror(-k));
540 log_warning("Invalid passphrase.");
543 if (try >= opt_tries) {
544 log_error("Too many attempts.");
549 } else if (streq(argv[1], "detach")) {
552 k = crypt_init_by_name(&cd, argv[2]);
554 log_error("crypt_init() failed: %s", strerror(-k));
558 crypt_set_log_callback(cd, log_glue, NULL);
560 k = crypt_deactivate(cd, argv[2]);
562 log_error("Failed to deactivate: %s", strerror(-k));
567 log_error("Unknown verb %s.", argv[1]);