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_offset
= 0;
41 static char *opt_hash
= NULL
;
42 static unsigned opt_tries
= 0;
43 static bool opt_readonly
= false;
44 static bool opt_verify
= false;
45 static bool opt_discards
= false;
46 static usec_t opt_timeout
= DEFAULT_TIMEOUT_USEC
;
48 /* Options Debian's crypttab knows we don't:
60 static int parse_one_option(const char *option
) {
63 /* Handled outside of this tool */
64 if (streq(option
, "noauto"))
67 if (startswith(option
, "cipher=")) {
70 if (!(t
= strdup(option
+7)))
76 } else if (startswith(option
, "size=")) {
78 if (safe_atou(option
+5, &opt_key_size
) < 0) {
79 log_error("size= parse failure, ignoring.");
83 } else if (startswith(option
, "keyfile-offset=")) {
85 if (safe_atou(option
+15, &opt_keyfile_offset
) < 0) {
86 log_error("keyfile-offset= parse failure, ignoring.");
90 } else if (startswith(option
, "hash=")) {
93 if (!(t
= strdup(option
+5)))
99 } else if (startswith(option
, "tries=")) {
101 if (safe_atou(option
+6, &opt_tries
) < 0) {
102 log_error("tries= parse failure, ignoring.");
106 } else if (streq(option
, "readonly"))
108 else if (streq(option
, "verify"))
110 else if (streq(option
, "allow-discards"))
112 else if (streq(option
, "luks"))
113 opt_type
= CRYPT_LUKS1
;
114 else if (streq(option
, "plain") ||
115 streq(option
, "swap") ||
116 streq(option
, "tmp"))
117 opt_type
= CRYPT_PLAIN
;
118 else if (startswith(option
, "timeout=")) {
120 if (parse_usec(option
+8, &opt_timeout
) < 0) {
121 log_error("timeout= parse failure, ignoring.");
125 } else if (!streq(option
, "none"))
126 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option
);
131 static int parse_options(const char *options
) {
138 FOREACH_WORD_SEPARATOR(w
, l
, options
, ",", state
) {
142 if (!(o
= strndup(w
, l
)))
145 r
= parse_one_option(o
);
155 static void log_glue(int level
, const char *msg
, void *usrptr
) {
156 log_debug("%s", msg
);
159 static char *disk_description(const char *path
) {
160 struct udev
*udev
= NULL
;
161 struct udev_device
*device
= NULL
;
163 char *description
= NULL
;
168 if (stat(path
, &st
) < 0)
171 if (!S_ISBLK(st
.st_mode
))
174 if (!(udev
= udev_new()))
177 if (!(device
= udev_device_new_from_devnum(udev
, 'b', st
.st_rdev
)))
180 if ((model
= udev_device_get_property_value(device
, "ID_MODEL_FROM_DATABASE")) ||
181 (model
= udev_device_get_property_value(device
, "ID_MODEL")) ||
182 (model
= udev_device_get_property_value(device
, "DM_NAME")))
183 description
= strdup(model
);
187 udev_device_unref(device
);
195 static char *disk_mount_point(const char *label
) {
196 char *mp
= NULL
, *device
= NULL
;
200 /* Yeah, we don't support native systemd unit files here for now */
202 if (asprintf(&device
, "/dev/mapper/%s", label
) < 0)
205 f
= setmntent("/etc/fstab", "r");
209 while ((m
= getmntent(f
)))
210 if (path_equal(m
->mnt_fsname
, device
)) {
211 mp
= strdup(m
->mnt_dir
);
224 static int help(void) {
226 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
227 "%s detach VOLUME\n\n"
228 "Attaches or detaches an encrypted block device.\n",
229 program_invocation_short_name
,
230 program_invocation_short_name
);
235 int main(int argc
, char *argv
[]) {
236 int r
= EXIT_FAILURE
;
237 struct crypt_device
*cd
= NULL
;
238 char **passwords
= NULL
, *truncated_cipher
= NULL
;
239 const char *cipher
= NULL
, *cipher_mode
= NULL
, *hash
= NULL
, *name
= NULL
;
240 char *description
= NULL
, *name_buffer
= NULL
, *mount_point
= NULL
;
241 unsigned keyfile_size
= 0;
249 log_error("This program requires at least two arguments.");
253 log_set_target(LOG_TARGET_AUTO
);
254 log_parse_environment();
259 if (streq(argv
[1], "attach")) {
263 const char *key_file
= NULL
;
265 crypt_status_info status
;
267 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
270 log_error("attach requires at least two arguments.");
276 !streq(argv
[4], "-") &&
277 !streq(argv
[4], "none")) {
279 if (!path_is_absolute(argv
[4]))
280 log_error("Password file path %s is not absolute. Ignoring.", argv
[4]);
285 if (argc
>= 6 && argv
[5][0] && !streq(argv
[5], "-"))
286 parse_options(argv
[5]);
288 /* A delicious drop of snake oil */
289 mlockall(MCL_FUTURE
);
291 description
= disk_description(argv
[3]);
292 mount_point
= disk_mount_point(argv
[2]);
294 if (description
&& streq(argv
[2], description
)) {
295 /* If the description string is simply the
296 * volume name, then let's not show this
302 if (mount_point
&& description
)
303 asprintf(&name_buffer
, "%s (%s) on %s", description
, argv
[2], mount_point
);
304 else if (mount_point
)
305 asprintf(&name_buffer
, "%s on %s", argv
[2], mount_point
);
306 else if (description
)
307 asprintf(&name_buffer
, "%s (%s)", description
, argv
[2]);
309 name
= name_buffer
? name_buffer
: argv
[2];
311 if ((k
= crypt_init(&cd
, argv
[3]))) {
312 log_error("crypt_init() failed: %s", strerror(-k
));
316 crypt_set_log_callback(cd
, log_glue
, NULL
);
318 status
= crypt_status(cd
, argv
[2]);
319 if (status
== CRYPT_ACTIVE
|| status
== CRYPT_BUSY
) {
320 log_info("Volume %s already active.", argv
[2]);
326 flags
|= CRYPT_ACTIVATE_READONLY
;
329 flags
|= CRYPT_ACTIVATE_ALLOW_DISCARDS
;
332 until
= now(CLOCK_MONOTONIC
) + opt_timeout
;
336 opt_tries
= opt_tries
> 0 ? opt_tries
: 3;
337 opt_key_size
= (opt_key_size
> 0 ? opt_key_size
: 256);
338 hash
= opt_hash
? opt_hash
: "ripemd160";
343 l
= strcspn(opt_cipher
, "-");
345 if (!(truncated_cipher
= strndup(opt_cipher
, l
))) {
346 log_error("Out of memory");
350 cipher
= truncated_cipher
;
351 cipher_mode
= opt_cipher
[l
] ? opt_cipher
+l
+1 : "plain";
354 cipher_mode
= "cbc-essiv:sha256";
357 for (try = 0; try < opt_tries
; try++) {
358 bool pass_volume_key
= false;
360 strv_free(passwords
);
367 if (asprintf(&text
, "Please enter passphrase for disk %s!", name
) < 0) {
368 log_error("Out of memory");
372 k
= ask_password_auto(text
, "drive-harddisk", until
, try == 0 && !opt_verify
, &passwords
);
376 log_error("Failed to query password: %s", strerror(-k
));
381 char **passwords2
= NULL
;
383 assert(strv_length(passwords
) == 1);
385 if (asprintf(&text
, "Please enter passphrase for disk %s! (verification)", name
) < 0) {
386 log_error("Out of memory");
390 k
= ask_password_auto(text
, "drive-harddisk", until
, false, &passwords2
);
394 log_error("Failed to query verification password: %s", strerror(-k
));
398 assert(strv_length(passwords2
) == 1);
400 if (!streq(passwords
[0], passwords2
[0])) {
401 log_warning("Passwords did not match, retrying.");
402 strv_free(passwords2
);
406 strv_free(passwords2
);
409 strv_uniq(passwords
);
411 STRV_FOREACH(p
, passwords
) {
414 if (strlen(*p
)+1 >= opt_key_size
)
417 /* Pad password if necessary */
418 if (!(c
= new(char, opt_key_size
))) {
419 log_error("Out of memory.");
423 strncpy(c
, *p
, opt_key_size
);
431 if (!opt_type
|| streq(opt_type
, CRYPT_LUKS1
))
432 k
= crypt_load(cd
, CRYPT_LUKS1
, NULL
);
434 if ((!opt_type
&& k
< 0) || streq_ptr(opt_type
, CRYPT_PLAIN
)) {
435 struct crypt_params_plain params
;
440 /* In contrast to what the name
441 * crypt_setup() might suggest this
442 * doesn't actually format anything,
443 * it just configures encryption
444 * parameters when used for plain
446 k
= crypt_format(cd
, CRYPT_PLAIN
,
454 pass_volume_key
= streq(hash
, "plain");
456 /* for CRYPT_PLAIN limit reads
457 * from keyfile to key length */
458 keyfile_size
= opt_key_size
/ 8;
462 log_error("Loading of cryptographic parameters failed: %s", strerror(-k
));
466 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
467 crypt_get_cipher(cd
),
468 crypt_get_cipher_mode(cd
),
469 crypt_get_volume_key_size(cd
)*8,
473 k
= crypt_activate_by_keyfile_offset(cd
, argv
[2], CRYPT_ANY_SLOT
, key_file
, keyfile_size
,
474 opt_keyfile_offset
, flags
);
478 STRV_FOREACH(p
, passwords
) {
481 k
= crypt_activate_by_volume_key(cd
, argv
[2], *p
, opt_key_size
, flags
);
483 k
= crypt_activate_by_passphrase(cd
, argv
[2], CRYPT_ANY_SLOT
, *p
, strlen(*p
), flags
);
494 log_error("Failed to activate: %s", strerror(-k
));
498 log_warning("Invalid passphrase.");
501 if (try >= opt_tries
) {
502 log_error("Too many attempts.");
507 } else if (streq(argv
[1], "detach")) {
510 if ((k
= crypt_init_by_name(&cd
, argv
[2]))) {
511 log_error("crypt_init() failed: %s", strerror(-k
));
515 crypt_set_log_callback(cd
, log_glue
, NULL
);
517 if ((k
= crypt_deactivate(cd
, argv
[2])) < 0) {
518 log_error("Failed to deactivate: %s", strerror(-k
));
523 log_error("Unknown verb %s.", argv
[1]);
537 free(truncated_cipher
);
539 strv_free(passwords
);