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 char *opt_hash = NULL;
41 static unsigned opt_tries = 0;
42 static bool opt_readonly = false;
43 static bool opt_verify = false;
44 static usec_t opt_timeout = DEFAULT_TIMEOUT_USEC;
46 /* Options Debian's crypttab knows we don't:
58 static int parse_one_option(const char *option) {
61 /* Handled outside of this tool */
62 if (streq(option, "noauto"))
65 if (startswith(option, "cipher=")) {
68 if (!(t = strdup(option+7)))
74 } else if (startswith(option, "size=")) {
76 if (safe_atou(option+5, &opt_key_size) < 0) {
77 log_error("size= parse failure, ignoring.");
81 } else if (startswith(option, "hash=")) {
84 if (!(t = strdup(option+5)))
90 } else if (startswith(option, "tries=")) {
92 if (safe_atou(option+6, &opt_tries) < 0) {
93 log_error("tries= parse failure, ignoring.");
97 } else if (streq(option, "readonly"))
99 else if (streq(option, "verify"))
101 else if (streq(option, "luks"))
102 opt_type = CRYPT_LUKS1;
103 else if (streq(option, "plain") ||
104 streq(option, "swap") ||
105 streq(option, "tmp"))
106 opt_type = CRYPT_PLAIN;
107 else if (startswith(option, "timeout=")) {
109 if (parse_usec(option+8, &opt_timeout) < 0) {
110 log_error("timeout= parse failure, ignoring.");
114 } else if (!streq(option, "none"))
115 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
120 static int parse_options(const char *options) {
127 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
131 if (!(o = strndup(w, l)))
134 r = parse_one_option(o);
144 static void log_glue(int level, const char *msg, void *usrptr) {
145 log_debug("%s", msg);
148 static char *disk_description(const char *path) {
149 struct udev *udev = NULL;
150 struct udev_device *device = NULL;
152 char *description = NULL;
157 if (stat(path, &st) < 0)
160 if (!S_ISBLK(st.st_mode))
163 if (!(udev = udev_new()))
166 if (!(device = udev_device_new_from_devnum(udev, 'b', st.st_rdev)))
169 if ((model = udev_device_get_property_value(device, "ID_MODEL_FROM_DATABASE")) ||
170 (model = udev_device_get_property_value(device, "ID_MODEL")) ||
171 (model = udev_device_get_property_value(device, "DM_NAME")))
172 description = strdup(model);
176 udev_device_unref(device);
184 static char *disk_mount_point(const char *label) {
185 char *mp = NULL, *device = NULL;
189 /* Yeah, we don't support native systemd unit files here for now */
191 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
194 f = setmntent("/etc/fstab", "r");
198 while ((m = getmntent(f)))
199 if (path_equal(m->mnt_fsname, device)) {
200 mp = strdup(m->mnt_dir);
213 static int help(void) {
215 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
216 "%s detach VOLUME\n\n"
217 "Attaches or detaches an encrypted block device.\n",
218 program_invocation_short_name,
219 program_invocation_short_name);
224 int main(int argc, char *argv[]) {
225 int r = EXIT_FAILURE;
226 struct crypt_device *cd = NULL;
227 char **passwords = NULL, *truncated_cipher = NULL;
228 const char *cipher = NULL, *cipher_mode = NULL, *hash = NULL, *name = NULL;
229 char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
230 unsigned keyfile_size = 0;
238 log_error("This program requires at least two arguments.");
242 log_set_target(LOG_TARGET_AUTO);
243 log_parse_environment();
248 if (streq(argv[1], "attach")) {
252 const char *key_file = NULL;
254 crypt_status_info status;
256 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
259 log_error("attach requires at least two arguments.");
265 !streq(argv[4], "-") &&
266 !streq(argv[4], "none")) {
268 if (!path_is_absolute(argv[4]))
269 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
274 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-"))
275 parse_options(argv[5]);
277 /* A delicious drop of snake oil */
278 mlockall(MCL_FUTURE);
280 description = disk_description(argv[3]);
281 mount_point = disk_mount_point(argv[2]);
283 if (description && streq(argv[2], description)) {
284 /* If the description string is simply the
285 * volume name, then let's not show this
291 if (mount_point && description)
292 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
293 else if (mount_point)
294 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
295 else if (description)
296 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
298 name = name_buffer ? name_buffer : argv[2];
300 if ((k = crypt_init(&cd, argv[3]))) {
301 log_error("crypt_init() failed: %s", strerror(-k));
305 crypt_set_log_callback(cd, log_glue, NULL);
307 status = crypt_status(cd, argv[2]);
308 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
309 log_info("Volume %s already active.", argv[2]);
315 flags |= CRYPT_ACTIVATE_READONLY;
318 until = now(CLOCK_MONOTONIC) + opt_timeout;
322 opt_tries = opt_tries > 0 ? opt_tries : 3;
323 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
324 hash = opt_hash ? opt_hash : "ripemd160";
329 l = strcspn(opt_cipher, "-");
331 if (!(truncated_cipher = strndup(opt_cipher, l))) {
332 log_error("Out of memory");
336 cipher = truncated_cipher;
337 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
340 cipher_mode = "cbc-essiv:sha256";
343 for (try = 0; try < opt_tries; try++) {
344 bool pass_volume_key = false;
346 strv_free(passwords);
353 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
354 log_error("Out of memory");
358 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
362 log_error("Failed to query password: %s", strerror(-k));
367 char **passwords2 = NULL;
369 assert(strv_length(passwords) == 1);
371 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
372 log_error("Out of memory");
376 k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
380 log_error("Failed to query verification password: %s", strerror(-k));
384 assert(strv_length(passwords2) == 1);
386 if (!streq(passwords[0], passwords2[0])) {
387 log_warning("Passwords did not match, retrying.");
388 strv_free(passwords2);
392 strv_free(passwords2);
395 strv_uniq(passwords);
397 STRV_FOREACH(p, passwords) {
400 if (strlen(*p)+1 >= opt_key_size)
403 /* Pad password if necessary */
404 if (!(c = new(char, opt_key_size))) {
405 log_error("Out of memory.");
409 strncpy(c, *p, opt_key_size);
417 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
418 k = crypt_load(cd, CRYPT_LUKS1, NULL);
420 if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
421 struct crypt_params_plain params;
426 /* In contrast to what the name
427 * crypt_setup() might suggest this
428 * doesn't actually format anything,
429 * it just configures encryption
430 * parameters when used for plain
432 k = crypt_format(cd, CRYPT_PLAIN,
440 pass_volume_key = streq(hash, "plain");
442 /* for CRYPT_PLAIN limit reads
443 * from keyfile to key length */
444 keyfile_size = opt_key_size / 8;
448 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
452 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
453 crypt_get_cipher(cd),
454 crypt_get_cipher_mode(cd),
455 crypt_get_volume_key_size(cd)*8,
459 k = crypt_activate_by_keyfile(cd, argv[2], CRYPT_ANY_SLOT, key_file, keyfile_size, flags);
463 STRV_FOREACH(p, passwords) {
466 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
468 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
479 log_error("Failed to activate: %s", strerror(-k));
483 log_warning("Invalid passphrase.");
486 if (try >= opt_tries) {
487 log_error("Too many attempts.");
492 } else if (streq(argv[1], "detach")) {
495 if ((k = crypt_init_by_name(&cd, argv[2]))) {
496 log_error("crypt_init() failed: %s", strerror(-k));
500 crypt_set_log_callback(cd, log_glue, NULL);
502 if ((k = crypt_deactivate(cd, argv[2])) < 0) {
503 log_error("Failed to deactivate: %s", strerror(-k));
508 log_error("Unknown verb %s.", argv[1]);
522 free(truncated_cipher);
524 strv_free(passwords);