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"))
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);
345 hash = opt_hash ? opt_hash : "ripemd160";
350 l = strcspn(opt_cipher, "-");
352 if (!(truncated_cipher = strndup(opt_cipher, l))) {
357 cipher = truncated_cipher;
358 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
361 cipher_mode = "cbc-essiv:sha256";
364 for (try = 0; try < opt_tries; try++) {
365 bool pass_volume_key = false;
367 strv_free(passwords);
374 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
379 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
383 log_error("Failed to query password: %s", strerror(-k));
388 char **passwords2 = NULL;
390 assert(strv_length(passwords) == 1);
392 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
397 k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
401 log_error("Failed to query verification password: %s", strerror(-k));
405 assert(strv_length(passwords2) == 1);
407 if (!streq(passwords[0], passwords2[0])) {
408 log_warning("Passwords did not match, retrying.");
409 strv_free(passwords2);
413 strv_free(passwords2);
416 strv_uniq(passwords);
418 STRV_FOREACH(p, passwords) {
421 if (strlen(*p)+1 >= opt_key_size)
424 /* Pad password if necessary */
425 if (!(c = new(char, opt_key_size))) {
430 strncpy(c, *p, opt_key_size);
438 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
439 k = crypt_load(cd, CRYPT_LUKS1, NULL);
441 if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
442 struct crypt_params_plain params;
447 /* for CRYPT_PLAIN limit reads
448 * from keyfile to key length, and
449 * ignore keyfile-size */
450 opt_keyfile_size = opt_key_size / 8;
452 /* In contrast to what the name
453 * crypt_setup() might suggest this
454 * doesn't actually format anything,
455 * it just configures encryption
456 * parameters when used for plain
458 k = crypt_format(cd, CRYPT_PLAIN,
466 pass_volume_key = streq(hash, "plain");
470 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
474 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
475 crypt_get_cipher(cd),
476 crypt_get_cipher_mode(cd),
477 crypt_get_volume_key_size(cd)*8,
481 k = crypt_activate_by_keyfile_offset(cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_keyfile_size,
482 opt_keyfile_offset, flags);
486 STRV_FOREACH(p, passwords) {
489 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
491 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
502 log_error("Failed to activate: %s", strerror(-k));
506 log_warning("Invalid passphrase.");
509 if (try >= opt_tries) {
510 log_error("Too many attempts.");
515 } else if (streq(argv[1], "detach")) {
518 if ((k = crypt_init_by_name(&cd, argv[2]))) {
519 log_error("crypt_init() failed: %s", strerror(-k));
523 crypt_set_log_callback(cd, log_glue, NULL);
525 if ((k = crypt_deactivate(cd, argv[2])) < 0) {
526 log_error("Failed to deactivate: %s", strerror(-k));
531 log_error("Unknown verb %s.", argv[1]);
545 free(truncated_cipher);
547 strv_free(passwords);