]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup.c
CommitLineData
e23a0ce8
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
e23a0ce8
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
e23a0ce8 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
e23a0ce8
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <string.h>
7f4e0805 23#include <errno.h>
b853f6e9 24#include <sys/mman.h>
b61e476f 25#include <mntent.h>
7f4e0805
LP
26
27#include <libcryptsetup.h>
b1a2da0a 28#include <libudev.h>
e23a0ce8
LP
29
30#include "log.h"
31#include "util.h"
21bc923a 32#include "strv.h"
7f4e0805 33#include "ask-password-api.h"
f6a6225e 34#include "def.h"
7f4e0805 35
260ab287 36static const char *opt_type = NULL; /* LUKS1 or PLAIN */
7f4e0805 37static char *opt_cipher = NULL;
260ab287 38static unsigned opt_key_size = 0;
7f4e0805 39static char *opt_hash = NULL;
260ab287 40static unsigned opt_tries = 0;
7f4e0805
LP
41static bool opt_readonly = false;
42static bool opt_verify = false;
90724929 43static usec_t opt_timeout = DEFAULT_TIMEOUT_USEC;
7f4e0805 44
1fc76335
LP
45/* Options Debian's crypttab knows we don't:
46
47 offset=
48 skip=
49 precheck=
50 check=
51 checkargs=
52 noearly=
53 loud=
54 keyscript=
55*/
56
7f4e0805
LP
57static int parse_one_option(const char *option) {
58 assert(option);
59
60 /* Handled outside of this tool */
260ab287 61 if (streq(option, "noauto"))
7f4e0805
LP
62 return 0;
63
64 if (startswith(option, "cipher=")) {
65 char *t;
66
67 if (!(t = strdup(option+7)))
68 return -ENOMEM;
69
70 free(opt_cipher);
71 opt_cipher = t;
72
73 } else if (startswith(option, "size=")) {
74
260ab287 75 if (safe_atou(option+5, &opt_key_size) < 0) {
7f4e0805
LP
76 log_error("size= parse failure, ignoring.");
77 return 0;
78 }
79
80 } else if (startswith(option, "hash=")) {
81 char *t;
82
83 if (!(t = strdup(option+5)))
84 return -ENOMEM;
85
86 free(opt_hash);
87 opt_hash = t;
88
89 } else if (startswith(option, "tries=")) {
90
91 if (safe_atou(option+6, &opt_tries) < 0) {
92 log_error("tries= parse failure, ignoring.");
93 return 0;
94 }
95
96 } else if (streq(option, "readonly"))
97 opt_readonly = true;
98 else if (streq(option, "verify"))
99 opt_verify = true;
260ab287
LP
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;
7f4e0805
LP
106 else if (startswith(option, "timeout=")) {
107
260ab287 108 if (parse_usec(option+8, &opt_timeout) < 0) {
7f4e0805
LP
109 log_error("timeout= parse failure, ignoring.");
110 return 0;
111 }
112
41e6f28a 113 } else if (!streq(option, "none"))
7f4e0805
LP
114 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
115
116 return 0;
117}
118
119static int parse_options(const char *options) {
120 char *state;
121 char *w;
122 size_t l;
123
124 assert(options);
125
126 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
127 char *o;
128 int r;
129
130 if (!(o = strndup(w, l)))
131 return -ENOMEM;
132
133 r = parse_one_option(o);
134 free(o);
135
136 if (r < 0)
137 return r;
138 }
139
140 return 0;
141}
142
143static void log_glue(int level, const char *msg, void *usrptr) {
260ab287 144 log_debug("%s", msg);
7f4e0805 145}
e23a0ce8 146
b1a2da0a
LP
147static char *disk_description(const char *path) {
148 struct udev *udev = NULL;
149 struct udev_device *device = NULL;
150 struct stat st;
151 char *description = NULL;
152 const char *model;
153
154 assert(path);
155
156 if (stat(path, &st) < 0)
157 return NULL;
158
159 if (!S_ISBLK(st.st_mode))
160 return NULL;
161
162 if (!(udev = udev_new()))
163 return NULL;
164
165 if (!(device = udev_device_new_from_devnum(udev, 'b', st.st_rdev)))
166 goto finish;
167
168 if ((model = udev_device_get_property_value(device, "ID_MODEL_FROM_DATABASE")) ||
b61e476f
LP
169 (model = udev_device_get_property_value(device, "ID_MODEL")) ||
170 (model = udev_device_get_property_value(device, "DM_NAME")))
b1a2da0a
LP
171 description = strdup(model);
172
173finish:
174 if (device)
175 udev_device_unref(device);
176
177 if (udev)
178 udev_unref(udev);
179
180 return description;
181}
182
b61e476f
LP
183static char *disk_mount_point(const char *label) {
184 char *mp = NULL, *device = NULL;
185 FILE *f = NULL;
186 struct mntent *m;
187
188 /* Yeah, we don't support native systemd unit files here for now */
189
190 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
191 goto finish;
192
193 if (!(f = setmntent("/etc/fstab", "r")))
194 goto finish;
195
196 while ((m = getmntent(f)))
197 if (path_equal(m->mnt_fsname, device)) {
198 mp = strdup(m->mnt_dir);
199 break;
200 }
201
202finish:
203 if (f)
204 endmntent(f);
205
206 free(device);
207
208 return mp;
209}
210
dd5e696d
LP
211static int help(void) {
212
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);
218
219 return 0;
220}
221
e23a0ce8 222int main(int argc, char *argv[]) {
7f4e0805
LP
223 int r = EXIT_FAILURE;
224 struct crypt_device *cd = NULL;
21bc923a 225 char **passwords = NULL, *truncated_cipher = NULL;
b1a2da0a 226 const char *cipher = NULL, *cipher_mode = NULL, *hash = NULL, *name = NULL;
b61e476f 227 char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
2d745456 228 unsigned keyfile_size = 0;
e23a0ce8 229
dd5e696d
LP
230 if (argc <= 1) {
231 help();
232 return EXIT_SUCCESS;
233 }
234
e23a0ce8
LP
235 if (argc < 3) {
236 log_error("This program requires at least two arguments.");
237 return EXIT_FAILURE;
238 }
239
bb7df0da 240 log_set_target(LOG_TARGET_AUTO);
e23a0ce8
LP
241 log_parse_environment();
242 log_open();
243
4c12626c
LP
244 umask(0022);
245
260ab287 246 if (streq(argv[1], "attach")) {
7f4e0805
LP
247 uint32_t flags = 0;
248 int k;
260ab287 249 unsigned try;
7f4e0805 250 const char *key_file = NULL;
260ab287 251 usec_t until;
e2d480b9 252 crypt_status_info status;
7f4e0805 253
b61e476f
LP
254 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
255
7f4e0805
LP
256 if (argc < 4) {
257 log_error("attach requires at least two arguments.");
258 goto finish;
259 }
260
1fc76335
LP
261 if (argc >= 5 &&
262 argv[4][0] &&
263 !streq(argv[4], "-") &&
264 !streq(argv[4], "none")) {
7f4e0805
LP
265
266 if (!path_is_absolute(argv[4]))
267 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
268 else
269 key_file = argv[4];
270 }
271
272 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-"))
273 parse_options(argv[5]);
e23a0ce8 274
b853f6e9
LP
275 /* A delicious drop of snake oil */
276 mlockall(MCL_FUTURE);
277
b1a2da0a 278 description = disk_description(argv[3]);
b61e476f
LP
279 mount_point = disk_mount_point(argv[2]);
280
281 if (description && streq(argv[2], description)) {
282 /* If the description string is simply the
283 * volume name, then let's not show this
284 * twice */
285 free(description);
286 description = NULL;
287 }
288
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]);
295
296 name = name_buffer ? name_buffer : argv[2];
b1a2da0a 297
7f4e0805
LP
298 if ((k = crypt_init(&cd, argv[3]))) {
299 log_error("crypt_init() failed: %s", strerror(-k));
300 goto finish;
301 }
e23a0ce8 302
7f4e0805 303 crypt_set_log_callback(cd, log_glue, NULL);
7f4e0805 304
260ab287
LP
305 status = crypt_status(cd, argv[2]);
306 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
307 log_info("Volume %s already active.", argv[2]);
308 r = EXIT_SUCCESS;
7f4e0805
LP
309 goto finish;
310 }
311
312 if (opt_readonly)
313 flags |= CRYPT_ACTIVATE_READONLY;
314
7dcda352
LP
315 if (opt_timeout > 0)
316 until = now(CLOCK_MONOTONIC) + opt_timeout;
317 else
318 until = 0;
260ab287
LP
319
320 opt_tries = opt_tries > 0 ? opt_tries : 3;
321 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
260ab287
LP
322 hash = opt_hash ? opt_hash : "ripemd160";
323
e2d480b9
LP
324 if (opt_cipher) {
325 size_t l;
326
327 l = strcspn(opt_cipher, "-");
328
329 if (!(truncated_cipher = strndup(opt_cipher, l))) {
330 log_error("Out of memory");
331 goto finish;
332 }
333
334 cipher = truncated_cipher;
335 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
336 } else {
337 cipher = "aes";
338 cipher_mode = "cbc-essiv:sha256";
339 }
340
260ab287
LP
341 for (try = 0; try < opt_tries; try++) {
342 bool pass_volume_key = false;
343
21bc923a
LP
344 strv_free(passwords);
345 passwords = NULL;
260ab287
LP
346
347 if (!key_file) {
494856b5 348 char *text;
21bc923a 349 char **p;
260ab287 350
1cd4a9f0 351 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
494856b5
LP
352 log_error("Out of memory");
353 goto finish;
354 }
355
21bc923a 356 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
494856b5
LP
357 free(text);
358
359 if (k < 0) {
260ab287
LP
360 log_error("Failed to query password: %s", strerror(-k));
361 goto finish;
362 }
363
364 if (opt_verify) {
21bc923a
LP
365 char **passwords2 = NULL;
366
367 assert(strv_length(passwords) == 1);
260ab287 368
1cd4a9f0 369 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
494856b5
LP
370 log_error("Out of memory");
371 goto finish;
372 }
373
21bc923a 374 k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
494856b5
LP
375 free(text);
376
377 if (k < 0) {
260ab287
LP
378 log_error("Failed to query verification password: %s", strerror(-k));
379 goto finish;
380 }
381
21bc923a
LP
382 assert(strv_length(passwords2) == 1);
383
384 if (!streq(passwords[0], passwords2[0])) {
260ab287 385 log_warning("Passwords did not match, retrying.");
21bc923a 386 strv_free(passwords2);
260ab287
LP
387 continue;
388 }
389
21bc923a 390 strv_free(passwords2);
260ab287
LP
391 }
392
b61e476f
LP
393 strv_uniq(passwords);
394
21bc923a 395 STRV_FOREACH(p, passwords) {
260ab287
LP
396 char *c;
397
21bc923a
LP
398 if (strlen(*p)+1 >= opt_key_size)
399 continue;
260ab287 400
21bc923a 401 /* Pad password if necessary */
260ab287
LP
402 if (!(c = new(char, opt_key_size))) {
403 log_error("Out of memory.");
404 goto finish;
405 }
406
21bc923a
LP
407 strncpy(c, *p, opt_key_size);
408 free(*p);
409 *p = c;
260ab287
LP
410 }
411 }
412
7dcda352
LP
413 k = 0;
414
260ab287
LP
415 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
416 k = crypt_load(cd, CRYPT_LUKS1, NULL);
417
418 if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
419 struct crypt_params_plain params;
420
421 zero(params);
422 params.hash = hash;
423
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
429 * mode. */
430 k = crypt_format(cd, CRYPT_PLAIN,
431 cipher,
432 cipher_mode,
433 NULL,
434 NULL,
435 opt_key_size / 8,
436 &params);
437
438 pass_volume_key = streq(hash, "plain");
2d745456
MB
439
440 /* for CRYPT_PLAIN limit reads
441 * from keyfile to key length */
442 keyfile_size = opt_key_size / 8;
260ab287
LP
443 }
444
445 if (k < 0) {
446 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
447 goto finish;
448 }
449
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,
454 argv[3]);
455
456 if (key_file)
2d745456 457 k = crypt_activate_by_keyfile(cd, argv[2], CRYPT_ANY_SLOT, key_file, keyfile_size, flags);
21bc923a
LP
458 else {
459 char **p;
460
461 STRV_FOREACH(p, passwords) {
462
463 if (pass_volume_key)
464 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
465 else
466 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
467
468 if (k >= 0)
469 break;
470 }
471 }
260ab287
LP
472
473 if (k >= 0)
474 break;
475
476 if (k != -EPERM) {
477 log_error("Failed to activate: %s", strerror(-k));
478 goto finish;
479 }
480
481 log_warning("Invalid passphrase.");
7f4e0805
LP
482 }
483
260ab287
LP
484 if (try >= opt_tries) {
485 log_error("Too many attempts.");
486 r = EXIT_FAILURE;
bd40a2d8 487 goto finish;
7f4e0805
LP
488 }
489
490 } else if (streq(argv[1], "detach")) {
491 int k;
492
493 if ((k = crypt_init_by_name(&cd, argv[2]))) {
494 log_error("crypt_init() failed: %s", strerror(-k));
495 goto finish;
496 }
497
498 crypt_set_log_callback(cd, log_glue, NULL);
499
500 if ((k = crypt_deactivate(cd, argv[2])) < 0) {
501 log_error("Failed to deactivate: %s", strerror(-k));
502 goto finish;
503 }
e23a0ce8
LP
504
505 } else {
506 log_error("Unknown verb %s.", argv[1]);
507 goto finish;
508 }
509
7f4e0805
LP
510 r = EXIT_SUCCESS;
511
e23a0ce8 512finish:
7f4e0805
LP
513
514 if (cd)
515 crypt_free(cd);
516
260ab287 517 free(opt_cipher);
260ab287
LP
518 free(opt_hash);
519
e2d480b9
LP
520 free(truncated_cipher);
521
21bc923a 522 strv_free(passwords);
260ab287 523
b1a2da0a 524 free(description);
b61e476f
LP
525 free(mount_point);
526 free(name_buffer);
b1a2da0a 527
e23a0ce8
LP
528 return r;
529}