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 = DEFAULT_TIMEOUT_USEC;
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=")) {
71 if (!(t = strdup(option+7)))
77 } else if (startswith(option, "size=")) {
79 if (safe_atou(option+5, &opt_key_size) < 0) {
80 log_error("size= parse failure, ignoring.");
84 } else if (startswith(option, "keyfile-size=")) {
86 if (safe_atou(option+13, &opt_keyfile_size) < 0) {
87 log_error("keyfile-size= parse failure, ignoring.");
91 } else if (startswith(option, "keyfile-offset=")) {
93 if (safe_atou(option+15, &opt_keyfile_offset) < 0) {
94 log_error("keyfile-offset= parse failure, ignoring.");
98 } else if (startswith(option, "hash=")) {
101 if (!(t = strdup(option+5)))
107 } else if (startswith(option, "tries=")) {
109 if (safe_atou(option+6, &opt_tries) < 0) {
110 log_error("tries= parse failure, ignoring.");
114 } else if (streq(option, "readonly"))
116 else if (streq(option, "verify"))
118 else if (streq(option, "allow-discards"))
120 else if (streq(option, "luks"))
121 opt_type = CRYPT_LUKS1;
122 else if (streq(option, "plain") ||
123 streq(option, "swap") ||
124 streq(option, "tmp"))
125 opt_type = CRYPT_PLAIN;
126 else if (startswith(option, "timeout=")) {
128 if (parse_usec(option+8, &opt_timeout) < 0) {
129 log_error("timeout= parse failure, ignoring.");
133 } else if (!streq(option, "none"))
134 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
139 static int parse_options(const char *options) {
146 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
150 if (!(o = strndup(w, l)))
153 r = parse_one_option(o);
163 static void log_glue(int level, const char *msg, void *usrptr) {
164 log_debug("%s", msg);
167 static char *disk_description(const char *path) {
168 struct udev *udev = NULL;
169 struct udev_device *device = NULL;
171 char *description = NULL;
176 if (stat(path, &st) < 0)
179 if (!S_ISBLK(st.st_mode))
182 if (!(udev = udev_new()))
185 if (!(device = udev_device_new_from_devnum(udev, 'b', st.st_rdev)))
188 if ((model = udev_device_get_property_value(device, "ID_MODEL_FROM_DATABASE")) ||
189 (model = udev_device_get_property_value(device, "ID_MODEL")) ||
190 (model = udev_device_get_property_value(device, "DM_NAME")))
191 description = strdup(model);
195 udev_device_unref(device);
203 static char *disk_mount_point(const char *label) {
204 char *mp = NULL, *device = NULL;
208 /* Yeah, we don't support native systemd unit files here for now */
210 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
213 f = setmntent("/etc/fstab", "r");
217 while ((m = getmntent(f)))
218 if (path_equal(m->mnt_fsname, device)) {
219 mp = strdup(m->mnt_dir);
232 static int help(void) {
234 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
235 "%s detach VOLUME\n\n"
236 "Attaches or detaches an encrypted block device.\n",
237 program_invocation_short_name,
238 program_invocation_short_name);
243 int main(int argc, char *argv[]) {
244 int r = EXIT_FAILURE;
245 struct crypt_device *cd = NULL;
246 char **passwords = NULL, *truncated_cipher = NULL;
247 const char *cipher = NULL, *cipher_mode = NULL, *hash = NULL, *name = NULL;
248 char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
256 log_error("This program requires at least two arguments.");
260 log_set_target(LOG_TARGET_AUTO);
261 log_parse_environment();
266 if (streq(argv[1], "attach")) {
270 const char *key_file = NULL;
272 crypt_status_info status;
274 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
277 log_error("attach requires at least two arguments.");
283 !streq(argv[4], "-") &&
284 !streq(argv[4], "none")) {
286 if (!path_is_absolute(argv[4]))
287 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
292 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-"))
293 parse_options(argv[5]);
295 /* A delicious drop of snake oil */
296 mlockall(MCL_FUTURE);
298 description = disk_description(argv[3]);
299 mount_point = disk_mount_point(argv[2]);
301 if (description && streq(argv[2], description)) {
302 /* If the description string is simply the
303 * volume name, then let's not show this
309 if (mount_point && description)
310 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
311 else if (mount_point)
312 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
313 else if (description)
314 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
316 name = name_buffer ? name_buffer : argv[2];
318 if ((k = crypt_init(&cd, argv[3]))) {
319 log_error("crypt_init() failed: %s", strerror(-k));
323 crypt_set_log_callback(cd, log_glue, NULL);
325 status = crypt_status(cd, argv[2]);
326 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
327 log_info("Volume %s already active.", argv[2]);
333 flags |= CRYPT_ACTIVATE_READONLY;
336 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
339 until = now(CLOCK_MONOTONIC) + opt_timeout;
343 opt_tries = opt_tries > 0 ? opt_tries : 3;
344 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
346 /* plain isn't a real hash type. it just means "use no hash" */
347 if (!streq(opt_hash, "plain"))
355 l = strcspn(opt_cipher, "-");
357 if (!(truncated_cipher = strndup(opt_cipher, l))) {
362 cipher = truncated_cipher;
363 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
366 cipher_mode = "cbc-essiv:sha256";
369 for (try = 0; try < opt_tries; try++) {
370 bool pass_volume_key = false;
372 strv_free(passwords);
379 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
384 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
388 log_error("Failed to query password: %s", strerror(-k));
393 char **passwords2 = NULL;
395 assert(strv_length(passwords) == 1);
397 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
402 k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
406 log_error("Failed to query verification password: %s", strerror(-k));
410 assert(strv_length(passwords2) == 1);
412 if (!streq(passwords[0], passwords2[0])) {
413 log_warning("Passwords did not match, retrying.");
414 strv_free(passwords2);
418 strv_free(passwords2);
421 strv_uniq(passwords);
423 STRV_FOREACH(p, passwords) {
426 if (strlen(*p)+1 >= opt_key_size)
429 /* Pad password if necessary */
430 if (!(c = new(char, opt_key_size))) {
435 strncpy(c, *p, opt_key_size);
443 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
444 k = crypt_load(cd, CRYPT_LUKS1, NULL);
446 if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
447 struct crypt_params_plain params;
452 /* for CRYPT_PLAIN limit reads
453 * from keyfile to key length, and
454 * ignore keyfile-size */
455 opt_keyfile_size = opt_key_size / 8;
457 /* In contrast to what the name
458 * crypt_setup() might suggest this
459 * doesn't actually format anything,
460 * it just configures encryption
461 * parameters when used for plain
463 k = crypt_format(cd, CRYPT_PLAIN,
471 /* hash == NULL implies the user passed "plain" */
472 pass_volume_key = (hash == NULL);
476 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
480 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
481 crypt_get_cipher(cd),
482 crypt_get_cipher_mode(cd),
483 crypt_get_volume_key_size(cd)*8,
487 k = crypt_activate_by_keyfile_offset(cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_keyfile_size,
488 opt_keyfile_offset, flags);
492 STRV_FOREACH(p, passwords) {
495 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
497 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
508 log_error("Failed to activate: %s", strerror(-k));
512 log_warning("Invalid passphrase.");
515 if (try >= opt_tries) {
516 log_error("Too many attempts.");
521 } else if (streq(argv[1], "detach")) {
524 if ((k = crypt_init_by_name(&cd, argv[2]))) {
525 log_error("crypt_init() failed: %s", strerror(-k));
529 crypt_set_log_callback(cd, log_glue, NULL);
531 if ((k = crypt_deactivate(cd, argv[2])) < 0) {
532 log_error("Failed to deactivate: %s", strerror(-k));
537 log_error("Unknown verb %s.", argv[1]);
551 free(truncated_cipher);
553 strv_free(passwords);