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 if (!(f = setmntent("/etc/fstab", "r")))
196 while ((m = getmntent(f)))
197 if (path_equal(m->mnt_fsname, device)) {
198 mp = strdup(m->mnt_dir);
211 static int help(void) {
213 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
214 "%s detach VOLUME\n\n"
215 "Attaches or detaches an encrypted block device.\n",
216 program_invocation_short_name,
217 program_invocation_short_name);
222 int main(int argc, char *argv[]) {
223 int r = EXIT_FAILURE;
224 struct crypt_device *cd = NULL;
225 char **passwords = NULL, *truncated_cipher = NULL;
226 const char *cipher = NULL, *cipher_mode = NULL, *hash = NULL, *name = NULL;
227 char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
228 unsigned keyfile_size = 0;
236 log_error("This program requires at least two arguments.");
240 log_set_target(LOG_TARGET_AUTO);
241 log_parse_environment();
246 if (streq(argv[1], "attach")) {
250 const char *key_file = NULL;
252 crypt_status_info status;
254 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
257 log_error("attach requires at least two arguments.");
263 !streq(argv[4], "-") &&
264 !streq(argv[4], "none")) {
266 if (!path_is_absolute(argv[4]))
267 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
272 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-"))
273 parse_options(argv[5]);
275 /* A delicious drop of snake oil */
276 mlockall(MCL_FUTURE);
278 description = disk_description(argv[3]);
279 mount_point = disk_mount_point(argv[2]);
281 if (description && streq(argv[2], description)) {
282 /* If the description string is simply the
283 * volume name, then let's not show this
289 if (mount_point && description)
290 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
291 else if (mount_point)
292 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
293 else if (description)
294 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
296 name = name_buffer ? name_buffer : argv[2];
298 if ((k = crypt_init(&cd, argv[3]))) {
299 log_error("crypt_init() failed: %s", strerror(-k));
303 crypt_set_log_callback(cd, log_glue, NULL);
305 status = crypt_status(cd, argv[2]);
306 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
307 log_info("Volume %s already active.", argv[2]);
313 flags |= CRYPT_ACTIVATE_READONLY;
316 until = now(CLOCK_MONOTONIC) + opt_timeout;
320 opt_tries = opt_tries > 0 ? opt_tries : 3;
321 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
322 hash = opt_hash ? opt_hash : "ripemd160";
327 l = strcspn(opt_cipher, "-");
329 if (!(truncated_cipher = strndup(opt_cipher, l))) {
330 log_error("Out of memory");
334 cipher = truncated_cipher;
335 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
338 cipher_mode = "cbc-essiv:sha256";
341 for (try = 0; try < opt_tries; try++) {
342 bool pass_volume_key = false;
344 strv_free(passwords);
351 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
352 log_error("Out of memory");
356 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
360 log_error("Failed to query password: %s", strerror(-k));
365 char **passwords2 = NULL;
367 assert(strv_length(passwords) == 1);
369 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
370 log_error("Out of memory");
374 k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
378 log_error("Failed to query verification password: %s", strerror(-k));
382 assert(strv_length(passwords2) == 1);
384 if (!streq(passwords[0], passwords2[0])) {
385 log_warning("Passwords did not match, retrying.");
386 strv_free(passwords2);
390 strv_free(passwords2);
393 strv_uniq(passwords);
395 STRV_FOREACH(p, passwords) {
398 if (strlen(*p)+1 >= opt_key_size)
401 /* Pad password if necessary */
402 if (!(c = new(char, opt_key_size))) {
403 log_error("Out of memory.");
407 strncpy(c, *p, opt_key_size);
415 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
416 k = crypt_load(cd, CRYPT_LUKS1, NULL);
418 if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
419 struct crypt_params_plain params;
424 /* In contrast to what the name
425 * crypt_setup() might suggest this
426 * doesn't actually format anything,
427 * it just configures encryption
428 * parameters when used for plain
430 k = crypt_format(cd, CRYPT_PLAIN,
438 pass_volume_key = streq(hash, "plain");
440 /* for CRYPT_PLAIN limit reads
441 * from keyfile to key length */
442 keyfile_size = opt_key_size / 8;
446 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
450 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
451 crypt_get_cipher(cd),
452 crypt_get_cipher_mode(cd),
453 crypt_get_volume_key_size(cd)*8,
457 k = crypt_activate_by_keyfile(cd, argv[2], CRYPT_ANY_SLOT, key_file, keyfile_size, flags);
461 STRV_FOREACH(p, passwords) {
464 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
466 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
477 log_error("Failed to activate: %s", strerror(-k));
481 log_warning("Invalid passphrase.");
484 if (try >= opt_tries) {
485 log_error("Too many attempts.");
490 } else if (streq(argv[1], "detach")) {
493 if ((k = crypt_init_by_name(&cd, argv[2]))) {
494 log_error("crypt_init() failed: %s", strerror(-k));
498 crypt_set_log_callback(cd, log_glue, NULL);
500 if ((k = crypt_deactivate(cd, argv[2])) < 0) {
501 log_error("Failed to deactivate: %s", strerror(-k));
506 log_error("Unknown verb %s.", argv[1]);
520 free(truncated_cipher);
522 strv_free(passwords);