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>
33 #include "ask-password-api.h"
36 static const char *opt_type = NULL; /* LUKS1 or PLAIN */
37 static char *opt_cipher = NULL;
38 static unsigned opt_key_size = 0;
39 static char *opt_hash = NULL;
40 static unsigned opt_tries = 0;
41 static bool opt_readonly = false;
42 static bool opt_verify = false;
43 static usec_t opt_timeout = DEFAULT_TIMEOUT_USEC;
45 /* Options Debian's crypttab knows we don't:
57 static int parse_one_option(const char *option) {
60 /* Handled outside of this tool */
61 if (streq(option, "noauto"))
64 if (startswith(option, "cipher=")) {
67 if (!(t = strdup(option+7)))
73 } else if (startswith(option, "size=")) {
75 if (safe_atou(option+5, &opt_key_size) < 0) {
76 log_error("size= parse failure, ignoring.");
80 } else if (startswith(option, "hash=")) {
83 if (!(t = strdup(option+5)))
89 } else if (startswith(option, "tries=")) {
91 if (safe_atou(option+6, &opt_tries) < 0) {
92 log_error("tries= parse failure, ignoring.");
96 } else if (streq(option, "readonly"))
98 else if (streq(option, "verify"))
100 else if (streq(option, "luks"))
101 opt_type = CRYPT_LUKS1;
102 else if (streq(option, "plain") ||
103 streq(option, "swap") ||
104 streq(option, "tmp"))
105 opt_type = CRYPT_PLAIN;
106 else if (startswith(option, "timeout=")) {
108 if (parse_usec(option+8, &opt_timeout) < 0) {
109 log_error("timeout= parse failure, ignoring.");
113 } else if (!streq(option, "none"))
114 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
119 static int parse_options(const char *options) {
126 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
130 if (!(o = strndup(w, l)))
133 r = parse_one_option(o);
143 static void log_glue(int level, const char *msg, void *usrptr) {
144 log_debug("%s", msg);
147 static char *disk_description(const char *path) {
148 struct udev *udev = NULL;
149 struct udev_device *device = NULL;
151 char *description = NULL;
156 if (stat(path, &st) < 0)
159 if (!S_ISBLK(st.st_mode))
162 if (!(udev = udev_new()))
165 if (!(device = udev_device_new_from_devnum(udev, 'b', st.st_rdev)))
168 if ((model = udev_device_get_property_value(device, "ID_MODEL_FROM_DATABASE")) ||
169 (model = udev_device_get_property_value(device, "ID_MODEL")) ||
170 (model = udev_device_get_property_value(device, "DM_NAME")))
171 description = strdup(model);
175 udev_device_unref(device);
183 static char *disk_mount_point(const char *label) {
184 char *mp = NULL, *device = NULL;
188 /* Yeah, we don't support native systemd unit files here for now */
190 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
193 f = setmntent("/etc/fstab", "r");
197 while ((m = getmntent(f)))
198 if (path_equal(m->mnt_fsname, device)) {
199 mp = strdup(m->mnt_dir);
212 static int help(void) {
214 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
215 "%s detach VOLUME\n\n"
216 "Attaches or detaches an encrypted block device.\n",
217 program_invocation_short_name,
218 program_invocation_short_name);
223 int main(int argc, char *argv[]) {
224 int r = EXIT_FAILURE;
225 struct crypt_device *cd = NULL;
226 char **passwords = NULL, *truncated_cipher = NULL;
227 const char *cipher = NULL, *cipher_mode = NULL, *hash = NULL, *name = NULL;
228 char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
229 unsigned keyfile_size = 0;
237 log_error("This program requires at least two arguments.");
241 log_set_target(LOG_TARGET_AUTO);
242 log_parse_environment();
247 if (streq(argv[1], "attach")) {
251 const char *key_file = NULL;
253 crypt_status_info status;
255 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
258 log_error("attach requires at least two arguments.");
264 !streq(argv[4], "-") &&
265 !streq(argv[4], "none")) {
267 if (!path_is_absolute(argv[4]))
268 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
273 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-"))
274 parse_options(argv[5]);
276 /* A delicious drop of snake oil */
277 mlockall(MCL_FUTURE);
279 description = disk_description(argv[3]);
280 mount_point = disk_mount_point(argv[2]);
282 if (description && streq(argv[2], description)) {
283 /* If the description string is simply the
284 * volume name, then let's not show this
290 if (mount_point && description)
291 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
292 else if (mount_point)
293 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
294 else if (description)
295 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
297 name = name_buffer ? name_buffer : argv[2];
299 if ((k = crypt_init(&cd, argv[3]))) {
300 log_error("crypt_init() failed: %s", strerror(-k));
304 crypt_set_log_callback(cd, log_glue, NULL);
306 status = crypt_status(cd, argv[2]);
307 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
308 log_info("Volume %s already active.", argv[2]);
314 flags |= CRYPT_ACTIVATE_READONLY;
317 until = now(CLOCK_MONOTONIC) + opt_timeout;
321 opt_tries = opt_tries > 0 ? opt_tries : 3;
322 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
323 hash = opt_hash ? opt_hash : "ripemd160";
328 l = strcspn(opt_cipher, "-");
330 if (!(truncated_cipher = strndup(opt_cipher, l))) {
331 log_error("Out of memory");
335 cipher = truncated_cipher;
336 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
339 cipher_mode = "cbc-essiv:sha256";
342 for (try = 0; try < opt_tries; try++) {
343 bool pass_volume_key = false;
345 strv_free(passwords);
352 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
353 log_error("Out of memory");
357 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
361 log_error("Failed to query password: %s", strerror(-k));
366 char **passwords2 = NULL;
368 assert(strv_length(passwords) == 1);
370 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
371 log_error("Out of memory");
375 k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
379 log_error("Failed to query verification password: %s", strerror(-k));
383 assert(strv_length(passwords2) == 1);
385 if (!streq(passwords[0], passwords2[0])) {
386 log_warning("Passwords did not match, retrying.");
387 strv_free(passwords2);
391 strv_free(passwords2);
394 strv_uniq(passwords);
396 STRV_FOREACH(p, passwords) {
399 if (strlen(*p)+1 >= opt_key_size)
402 /* Pad password if necessary */
403 if (!(c = new(char, opt_key_size))) {
404 log_error("Out of memory.");
408 strncpy(c, *p, opt_key_size);
416 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
417 k = crypt_load(cd, CRYPT_LUKS1, NULL);
419 if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
420 struct crypt_params_plain params;
425 /* In contrast to what the name
426 * crypt_setup() might suggest this
427 * doesn't actually format anything,
428 * it just configures encryption
429 * parameters when used for plain
431 k = crypt_format(cd, CRYPT_PLAIN,
439 pass_volume_key = streq(hash, "plain");
441 /* for CRYPT_PLAIN limit reads
442 * from keyfile to key length */
443 keyfile_size = opt_key_size / 8;
447 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
451 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
452 crypt_get_cipher(cd),
453 crypt_get_cipher_mode(cd),
454 crypt_get_volume_key_size(cd)*8,
458 k = crypt_activate_by_keyfile(cd, argv[2], CRYPT_ANY_SLOT, key_file, keyfile_size, flags);
462 STRV_FOREACH(p, passwords) {
465 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
467 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
478 log_error("Failed to activate: %s", strerror(-k));
482 log_warning("Invalid passphrase.");
485 if (try >= opt_tries) {
486 log_error("Too many attempts.");
491 } else if (streq(argv[1], "detach")) {
494 if ((k = crypt_init_by_name(&cd, argv[2]))) {
495 log_error("crypt_init() failed: %s", strerror(-k));
499 crypt_set_log_callback(cd, log_glue, NULL);
501 if ((k = crypt_deactivate(cd, argv[2])) < 0) {
502 log_error("Failed to deactivate: %s", strerror(-k));
507 log_error("Unknown verb %s.", argv[1]);
521 free(truncated_cipher);
523 strv_free(passwords);