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=")) {
78 } else if (startswith(option, "size=")) {
80 if (safe_atou(option+5, &opt_key_size) < 0) {
81 log_error("size= parse failure, ignoring.");
85 } else if (startswith(option, "keyfile-size=")) {
87 if (safe_atou(option+13, &opt_keyfile_size) < 0) {
88 log_error("keyfile-size= parse failure, ignoring.");
92 } else if (startswith(option, "keyfile-offset=")) {
94 if (safe_atou(option+15, &opt_keyfile_offset) < 0) {
95 log_error("keyfile-offset= parse failure, ignoring.");
99 } else if (startswith(option, "hash=")) {
102 t = strdup(option+5);
109 } else if (startswith(option, "tries=")) {
111 if (safe_atou(option+6, &opt_tries) < 0) {
112 log_error("tries= parse failure, ignoring.");
116 } else if (streq(option, "readonly") || streq(option, "read-only"))
118 else if (streq(option, "verify"))
120 else if (streq(option, "allow-discards"))
122 else if (streq(option, "luks"))
123 opt_type = CRYPT_LUKS1;
124 else if (streq(option, "plain") ||
125 streq(option, "swap") ||
126 streq(option, "tmp"))
127 opt_type = CRYPT_PLAIN;
128 else if (startswith(option, "timeout=")) {
130 if (parse_sec(option+8, &opt_timeout) < 0) {
131 log_error("timeout= parse failure, ignoring.");
135 } else if (!streq(option, "none"))
136 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
141 static int parse_options(const char *options) {
148 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
149 _cleanup_free_ char *o;
154 r = parse_one_option(o);
162 static void log_glue(int level, const char *msg, void *usrptr) {
163 log_debug("%s", msg);
166 static char *disk_description(const char *path) {
168 static const char name_fields[] = {
169 "ID_PART_ENTRY_NAME\0"
171 "ID_MODEL_FROM_DATABASE\0"
175 struct udev *udev = NULL;
176 struct udev_device *device = NULL;
178 char *description = NULL;
183 if (stat(path, &st) < 0)
186 if (!S_ISBLK(st.st_mode))
193 device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
197 NULSTR_FOREACH(i, name_fields) {
200 name = udev_device_get_property_value(device, i);
201 if (!isempty(name)) {
202 description = strdup(name);
209 udev_device_unref(device);
217 static char *disk_mount_point(const char *label) {
218 char *mp = NULL, *device = NULL;
222 /* Yeah, we don't support native systemd unit files here for now */
224 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
227 f = setmntent("/etc/fstab", "r");
231 while ((m = getmntent(f)))
232 if (path_equal(m->mnt_fsname, device)) {
233 mp = strdup(m->mnt_dir);
246 static int help(void) {
248 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
249 "%s detach VOLUME\n\n"
250 "Attaches or detaches an encrypted block device.\n",
251 program_invocation_short_name,
252 program_invocation_short_name);
257 int main(int argc, char *argv[]) {
258 int r = EXIT_FAILURE;
259 struct crypt_device *cd = NULL;
260 char **passwords = NULL, *truncated_cipher = NULL;
261 const char *cipher = NULL, *cipher_mode = NULL, *hash = NULL, *name = NULL;
262 char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
270 log_error("This program requires at least two arguments.");
274 log_set_target(LOG_TARGET_AUTO);
275 log_parse_environment();
280 if (streq(argv[1], "attach")) {
284 const char *key_file = NULL;
286 crypt_status_info status;
288 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
291 log_error("attach requires at least two arguments.");
297 !streq(argv[4], "-") &&
298 !streq(argv[4], "none")) {
300 if (!path_is_absolute(argv[4]))
301 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
306 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
307 if (parse_options(argv[5]) < 0)
311 /* A delicious drop of snake oil */
312 mlockall(MCL_FUTURE);
314 description = disk_description(argv[3]);
315 mount_point = disk_mount_point(argv[2]);
317 if (description && streq(argv[2], description)) {
318 /* If the description string is simply the
319 * volume name, then let's not show this
325 if (mount_point && description)
326 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
327 else if (mount_point)
328 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
329 else if (description)
330 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
332 name = name_buffer ? name_buffer : argv[2];
334 k = crypt_init(&cd, argv[3]);
336 log_error("crypt_init() failed: %s", strerror(-k));
340 crypt_set_log_callback(cd, log_glue, NULL);
342 status = crypt_status(cd, argv[2]);
343 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
344 log_info("Volume %s already active.", argv[2]);
350 flags |= CRYPT_ACTIVATE_READONLY;
353 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
356 until = now(CLOCK_MONOTONIC) + opt_timeout;
360 opt_tries = opt_tries > 0 ? opt_tries : 3;
361 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
363 /* plain isn't a real hash type. it just means "use no hash" */
364 if (!streq(opt_hash, "plain"))
372 l = strcspn(opt_cipher, "-");
373 truncated_cipher = strndup(opt_cipher, l);
375 if (!truncated_cipher) {
380 cipher = truncated_cipher;
381 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
384 cipher_mode = "cbc-essiv:sha256";
387 for (try = 0; try < opt_tries; try++) {
388 bool pass_volume_key = false;
390 strv_free(passwords);
396 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
401 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
405 log_error("Failed to query password: %s", strerror(-k));
410 char **passwords2 = NULL;
412 assert(strv_length(passwords) == 1);
414 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
419 k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
423 log_error("Failed to query verification password: %s", strerror(-k));
427 assert(strv_length(passwords2) == 1);
429 if (!streq(passwords[0], passwords2[0])) {
430 log_warning("Passwords did not match, retrying.");
431 strv_free(passwords2);
435 strv_free(passwords2);
438 strv_uniq(passwords);
440 STRV_FOREACH(p, passwords) {
443 if (strlen(*p)+1 >= opt_key_size)
446 /* Pad password if necessary */
447 if (!(c = new(char, opt_key_size))) {
452 strncpy(c, *p, opt_key_size);
460 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
461 k = crypt_load(cd, CRYPT_LUKS1, NULL);
463 if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
464 struct crypt_params_plain params = { .hash = hash };
466 /* for CRYPT_PLAIN limit reads
467 * from keyfile to key length, and
468 * ignore keyfile-size */
469 opt_keyfile_size = opt_key_size / 8;
471 /* In contrast to what the name
472 * crypt_setup() might suggest this
473 * doesn't actually format anything,
474 * it just configures encryption
475 * parameters when used for plain
477 k = crypt_format(cd, CRYPT_PLAIN,
485 /* hash == NULL implies the user passed "plain" */
486 pass_volume_key = (hash == NULL);
490 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
494 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
495 crypt_get_cipher(cd),
496 crypt_get_cipher_mode(cd),
497 crypt_get_volume_key_size(cd)*8,
501 k = crypt_activate_by_keyfile_offset(cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_keyfile_size,
502 opt_keyfile_offset, flags);
506 STRV_FOREACH(p, passwords) {
509 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
511 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
522 log_error("Failed to activate: %s", strerror(-k));
526 log_warning("Invalid passphrase.");
529 if (try >= opt_tries) {
530 log_error("Too many attempts.");
535 } else if (streq(argv[1], "detach")) {
538 k = crypt_init_by_name(&cd, argv[2]);
540 log_error("crypt_init() failed: %s", strerror(-k));
544 crypt_set_log_callback(cd, log_glue, NULL);
546 k = crypt_deactivate(cd, argv[2]);
548 log_error("Failed to deactivate: %s", strerror(-k));
553 log_error("Unknown verb %s.", argv[1]);
567 free(truncated_cipher);
569 strv_free(passwords);