]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup.c
cryptsetup: Add tcrypt support
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup.c
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
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.
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
16 Lesser General Public License for more details.
17
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/>.
20 ***/
21
22 #include <string.h>
23 #include <errno.h>
24 #include <sys/mman.h>
25 #include <mntent.h>
26
27 #include <libcryptsetup.h>
28 #include <libudev.h>
29
30 #include "fileio.h"
31 #include "log.h"
32 #include "util.h"
33 #include "path-util.h"
34 #include "strv.h"
35 #include "ask-password-api.h"
36 #include "def.h"
37
38 static const char *opt_type = NULL; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
39 static char *opt_cipher = NULL;
40 static unsigned opt_key_size = 0;
41 static unsigned opt_keyfile_size = 0;
42 static unsigned opt_keyfile_offset = 0;
43 static char *opt_hash = NULL;
44 static unsigned opt_tries = 0;
45 static bool opt_readonly = false;
46 static bool opt_verify = false;
47 static bool opt_discards = false;
48 static bool opt_tcrypt_hidden = false;
49 static bool opt_tcrypt_system = false;
50 static char **opt_tcrypt_keyfiles = NULL;
51 static usec_t opt_timeout = 0;
52
53 /* Options Debian's crypttab knows we don't:
54
55 offset=
56 skip=
57 precheck=
58 check=
59 checkargs=
60 noearly=
61 loud=
62 keyscript=
63 */
64
65 static int parse_one_option(const char *option) {
66 assert(option);
67
68 /* Handled outside of this tool */
69 if (streq(option, "noauto") || streq(option, "nofail"))
70 return 0;
71
72 if (startswith(option, "cipher=")) {
73 char *t;
74
75 t = strdup(option+7);
76 if (!t)
77 return -ENOMEM;
78
79 free(opt_cipher);
80 opt_cipher = t;
81
82 } else if (startswith(option, "size=")) {
83
84 if (safe_atou(option+5, &opt_key_size) < 0) {
85 log_error("size= parse failure, ignoring.");
86 return 0;
87 }
88
89 } else if (startswith(option, "tcrypt-keyfile=")) {
90
91 opt_type = CRYPT_TCRYPT;
92 if (path_is_absolute(option+15))
93 opt_tcrypt_keyfiles = strv_append(opt_tcrypt_keyfiles, strdup(option+15));
94 else
95 log_error("Key file path '%s' is not absolute. Ignoring.", option+15);
96
97 } else if (startswith(option, "keyfile-size=")) {
98
99 if (safe_atou(option+13, &opt_keyfile_size) < 0) {
100 log_error("keyfile-size= parse failure, ignoring.");
101 return 0;
102 }
103
104 } else if (startswith(option, "keyfile-offset=")) {
105
106 if (safe_atou(option+15, &opt_keyfile_offset) < 0) {
107 log_error("keyfile-offset= parse failure, ignoring.");
108 return 0;
109 }
110
111 } else if (startswith(option, "hash=")) {
112 char *t;
113
114 t = strdup(option+5);
115 if (!t)
116 return -ENOMEM;
117
118 free(opt_hash);
119 opt_hash = t;
120
121 } else if (startswith(option, "tries=")) {
122
123 if (safe_atou(option+6, &opt_tries) < 0) {
124 log_error("tries= parse failure, ignoring.");
125 return 0;
126 }
127
128 } else if (streq(option, "readonly") || streq(option, "read-only"))
129 opt_readonly = true;
130 else if (streq(option, "verify"))
131 opt_verify = true;
132 else if (streq(option, "allow-discards"))
133 opt_discards = true;
134 else if (streq(option, "luks"))
135 opt_type = CRYPT_LUKS1;
136 else if (streq(option, "tcrypt"))
137 opt_type = CRYPT_TCRYPT;
138 else if (streq(option, "tcrypt-hidden")) {
139 opt_type = CRYPT_TCRYPT;
140 opt_tcrypt_hidden = true;
141 } else if (streq(option, "tcrypt-system")) {
142 opt_type = CRYPT_TCRYPT;
143 opt_tcrypt_system = true;
144 } else if (streq(option, "plain") ||
145 streq(option, "swap") ||
146 streq(option, "tmp"))
147 opt_type = CRYPT_PLAIN;
148 else if (startswith(option, "timeout=")) {
149
150 if (parse_sec(option+8, &opt_timeout) < 0) {
151 log_error("timeout= parse failure, ignoring.");
152 return 0;
153 }
154
155 } else if (!streq(option, "none"))
156 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
157
158 return 0;
159 }
160
161 static int parse_options(const char *options) {
162 char *state, *w;
163 size_t l;
164 int r;
165
166 assert(options);
167
168 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
169 _cleanup_free_ char *o;
170
171 o = strndup(w, l);
172 if (!o)
173 return -ENOMEM;
174 r = parse_one_option(o);
175 if (r < 0)
176 return r;
177 }
178
179 return 0;
180 }
181
182 static void log_glue(int level, const char *msg, void *usrptr) {
183 log_debug("%s", msg);
184 }
185
186 static char *disk_description(const char *path) {
187
188 static const char name_fields[] = {
189 "ID_PART_ENTRY_NAME\0"
190 "DM_NAME\0"
191 "ID_MODEL_FROM_DATABASE\0"
192 "ID_MODEL\0"
193 };
194
195 struct udev *udev = NULL;
196 struct udev_device *device = NULL;
197 struct stat st;
198 char *description = NULL;
199 const char *i;
200
201 assert(path);
202
203 if (stat(path, &st) < 0)
204 return NULL;
205
206 if (!S_ISBLK(st.st_mode))
207 return NULL;
208
209 udev = udev_new();
210 if (!udev)
211 return NULL;
212
213 device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
214 if (!device)
215 goto finish;
216
217 NULSTR_FOREACH(i, name_fields) {
218 const char *name;
219
220 name = udev_device_get_property_value(device, i);
221 if (!isempty(name)) {
222 description = strdup(name);
223 break;
224 }
225 }
226
227 finish:
228 if (device)
229 udev_device_unref(device);
230
231 if (udev)
232 udev_unref(udev);
233
234 return description;
235 }
236
237 static char *disk_mount_point(const char *label) {
238 char *mp = NULL;
239 _cleanup_free_ char *device = NULL;
240 FILE *f = NULL;
241 struct mntent *m;
242
243 /* Yeah, we don't support native systemd unit files here for now */
244
245 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
246 goto finish;
247
248 f = setmntent("/etc/fstab", "r");
249 if (!f)
250 goto finish;
251
252 while ((m = getmntent(f)))
253 if (path_equal(m->mnt_fsname, device)) {
254 mp = strdup(m->mnt_dir);
255 break;
256 }
257
258 finish:
259 if (f)
260 endmntent(f);
261
262 return mp;
263 }
264
265 static int get_password(const char *name, usec_t until, bool accept_cached, char ***passwords) {
266 int r;
267 char **p;
268 _cleanup_free_ char *text = NULL;
269
270 assert(name);
271 assert(passwords);
272
273 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
274 return log_oom();
275
276 r = ask_password_auto(text, "drive-harddisk", until, accept_cached, passwords);
277 if (r < 0) {
278 log_error("Failed to query password: %s", strerror(-r));
279 return r;
280 }
281
282 if (opt_verify) {
283 _cleanup_strv_free_ char **passwords2 = NULL;
284
285 assert(strv_length(*passwords) == 1);
286
287 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
288 return log_oom();
289
290 r = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
291 if (r < 0) {
292 log_error("Failed to query verification password: %s", strerror(-r));
293 return r;
294 }
295
296 assert(strv_length(passwords2) == 1);
297
298 if (!streq(*passwords[0], passwords2[0])) {
299 log_warning("Passwords did not match, retrying.");
300 return -EAGAIN;
301 }
302 }
303
304 strv_uniq(*passwords);
305
306 STRV_FOREACH(p, *passwords) {
307 char *c;
308
309 if (strlen(*p)+1 >= opt_key_size)
310 continue;
311
312 /* Pad password if necessary */
313 if (!(c = new(char, opt_key_size)))
314 return log_oom();
315
316 strncpy(c, *p, opt_key_size);
317 free(*p);
318 *p = c;
319 }
320
321 return 0;
322 }
323
324 static int attach_tcrypt(struct crypt_device *cd,
325 const char *name,
326 const char *key_file,
327 char **passwords,
328 uint32_t flags) {
329 int r = 0;
330 _cleanup_free_ char *passphrase = NULL;
331 struct crypt_params_tcrypt params = {
332 .flags = CRYPT_TCRYPT_LEGACY_MODES,
333 .keyfiles = (const char **)opt_tcrypt_keyfiles,
334 .keyfiles_count = strv_length(opt_tcrypt_keyfiles)
335 };
336
337 assert(cd);
338 assert(name);
339 assert(key_file || passwords);
340
341 if (opt_tcrypt_hidden)
342 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
343
344 if (opt_tcrypt_system)
345 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
346
347 if (key_file) {
348 r = read_one_line_file(key_file, &passphrase);
349 if (r < 0) {
350 log_error("Failed to read password file '%s': %s", key_file, strerror(-r));
351 return -EAGAIN;
352 }
353
354 params.passphrase = passphrase;
355 } else
356 params.passphrase = passwords[0];
357 params.passphrase_size = strlen(params.passphrase);
358
359 r = crypt_load(cd, CRYPT_TCRYPT, &params);
360 if (r < 0) {
361 if (key_file && r == -EPERM) {
362 log_error("Failed to activate using password file '%s'.", key_file);
363 return -EAGAIN;
364 }
365 return r;
366 }
367
368 return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);;
369 }
370
371 static int attach_luks_or_plain(struct crypt_device *cd,
372 const char *name,
373 const char *key_file,
374 char **passwords,
375 uint32_t flags) {
376 int r = 0;
377 bool pass_volume_key = false;
378
379 assert(cd);
380 assert(name);
381 assert(key_file || passwords);
382
383 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
384 r = crypt_load(cd, CRYPT_LUKS1, NULL);
385
386 if ((!opt_type && r < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
387 struct crypt_params_plain params = {};
388 const char *cipher, *cipher_mode;
389 _cleanup_free_ char *truncated_cipher = NULL;
390
391 if (opt_hash) {
392 /* plain isn't a real hash type. it just means "use no hash" */
393 if (!streq(opt_hash, "plain"))
394 params.hash = opt_hash;
395 } else
396 params.hash = "ripemd160";
397
398 if (opt_cipher) {
399 size_t l;
400
401 l = strcspn(opt_cipher, "-");
402 truncated_cipher = strndup(opt_cipher, l);
403 if (!truncated_cipher)
404 return log_oom();
405
406 cipher = truncated_cipher;
407 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
408 } else {
409 cipher = "aes";
410 cipher_mode = "cbc-essiv:sha256";
411 }
412
413 /* for CRYPT_PLAIN limit reads
414 * from keyfile to key length, and
415 * ignore keyfile-size */
416 opt_keyfile_size = opt_key_size / 8;
417
418 /* In contrast to what the name
419 * crypt_setup() might suggest this
420 * doesn't actually format anything,
421 * it just configures encryption
422 * parameters when used for plain
423 * mode. */
424 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode,
425 NULL, NULL, opt_keyfile_size, &params);
426
427 /* hash == NULL implies the user passed "plain" */
428 pass_volume_key = (params.hash == NULL);
429 }
430
431 if (r < 0) {
432 log_error("Loading of cryptographic parameters failed: %s", strerror(-r));
433 return r;
434 }
435
436 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
437 crypt_get_cipher(cd),
438 crypt_get_cipher_mode(cd),
439 crypt_get_volume_key_size(cd)*8,
440 crypt_get_device_name(cd));
441
442 if (key_file) {
443 r = crypt_activate_by_keyfile_offset(cd, name, CRYPT_ANY_SLOT,
444 key_file, opt_keyfile_size,
445 opt_keyfile_offset, flags);
446 if (r < 0) {
447 log_error("Failed to activate with key file '%s': %s", key_file, strerror(-r));
448 return -EAGAIN;
449 }
450 } else {
451 char **p;
452
453 STRV_FOREACH(p, passwords) {
454 if (pass_volume_key)
455 r = crypt_activate_by_volume_key(cd, name, *p, opt_key_size, flags);
456 else
457 r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, *p, strlen(*p), flags);
458
459 if (r >= 0)
460 break;
461 }
462 }
463
464 return r;
465 }
466
467 static int help(void) {
468
469 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
470 "%s detach VOLUME\n\n"
471 "Attaches or detaches an encrypted block device.\n",
472 program_invocation_short_name,
473 program_invocation_short_name);
474
475 return 0;
476 }
477
478 int main(int argc, char *argv[]) {
479 int r = EXIT_FAILURE;
480 struct crypt_device *cd = NULL;
481
482 if (argc <= 1) {
483 help();
484 return EXIT_SUCCESS;
485 }
486
487 if (argc < 3) {
488 log_error("This program requires at least two arguments.");
489 return EXIT_FAILURE;
490 }
491
492 log_set_target(LOG_TARGET_AUTO);
493 log_parse_environment();
494 log_open();
495
496 umask(0022);
497
498 if (streq(argv[1], "attach")) {
499 uint32_t flags = 0;
500 int k;
501 unsigned tries;
502 usec_t until;
503 crypt_status_info status;
504 const char *key_file = NULL, *name = NULL;
505 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
506
507 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
508
509 if (argc < 4) {
510 log_error("attach requires at least two arguments.");
511 goto finish;
512 }
513
514 if (argc >= 5 &&
515 argv[4][0] &&
516 !streq(argv[4], "-") &&
517 !streq(argv[4], "none")) {
518
519 if (!path_is_absolute(argv[4]))
520 log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]);
521 else
522 key_file = argv[4];
523 }
524
525 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
526 if (parse_options(argv[5]) < 0)
527 goto finish;
528 }
529
530 /* A delicious drop of snake oil */
531 mlockall(MCL_FUTURE);
532
533 description = disk_description(argv[3]);
534 mount_point = disk_mount_point(argv[2]);
535
536 if (description && streq(argv[2], description)) {
537 /* If the description string is simply the
538 * volume name, then let's not show this
539 * twice */
540 free(description);
541 description = NULL;
542 }
543
544 if (mount_point && description)
545 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
546 else if (mount_point)
547 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
548 else if (description)
549 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
550
551 name = name_buffer ? name_buffer : argv[2];
552
553 k = crypt_init(&cd, argv[3]);
554 if (k) {
555 log_error("crypt_init() failed: %s", strerror(-k));
556 goto finish;
557 }
558
559 crypt_set_log_callback(cd, log_glue, NULL);
560
561 status = crypt_status(cd, argv[2]);
562 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
563 log_info("Volume %s already active.", argv[2]);
564 r = EXIT_SUCCESS;
565 goto finish;
566 }
567
568 if (opt_readonly)
569 flags |= CRYPT_ACTIVATE_READONLY;
570
571 if (opt_discards)
572 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
573
574 if (opt_timeout > 0)
575 until = now(CLOCK_MONOTONIC) + opt_timeout;
576 else
577 until = 0;
578
579 opt_tries = opt_tries > 0 ? opt_tries : 3;
580 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
581
582 if (key_file) {
583 struct stat st;
584
585 /* Ideally we'd do this on the open fd, but since this is just a
586 * warning it's OK to do this in two steps. */
587 if (stat(key_file, &st) >= 0 && (st.st_mode & 0005))
588 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
589 }
590
591 for (tries = 0; tries < opt_tries; tries++) {
592 _cleanup_strv_free_ char **passwords = NULL;
593
594 if (!key_file) {
595 k = get_password(name, until, tries == 0 && !opt_verify, &passwords);
596 if (k == -EAGAIN)
597 continue;
598 else if (k < 0)
599 goto finish;
600 }
601
602 if (streq_ptr(opt_type, CRYPT_TCRYPT))
603 k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
604 else
605 k = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags);
606 if (k >= 0)
607 break;
608 else if (k == -EAGAIN) {
609 key_file = NULL;
610 continue;
611 } else if (k != -EPERM) {
612 log_error("Failed to activate: %s", strerror(-k));
613 goto finish;
614 }
615
616 log_warning("Invalid passphrase.");
617 }
618
619 if (tries >= opt_tries) {
620 log_error("Too many attempts; giving up.");
621 r = EXIT_FAILURE;
622 goto finish;
623 }
624
625 } else if (streq(argv[1], "detach")) {
626 int k;
627
628 k = crypt_init_by_name(&cd, argv[2]);
629 if (k) {
630 log_error("crypt_init() failed: %s", strerror(-k));
631 goto finish;
632 }
633
634 crypt_set_log_callback(cd, log_glue, NULL);
635
636 k = crypt_deactivate(cd, argv[2]);
637 if (k < 0) {
638 log_error("Failed to deactivate: %s", strerror(-k));
639 goto finish;
640 }
641
642 } else {
643 log_error("Unknown verb %s.", argv[1]);
644 goto finish;
645 }
646
647 r = EXIT_SUCCESS;
648
649 finish:
650
651 if (cd)
652 crypt_free(cd);
653
654 free(opt_cipher);
655 free(opt_hash);
656 strv_free(opt_tcrypt_keyfiles);
657
658 return r;
659 }