]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup.c
tree-wide: use startswith return value to avoid hardcoded offset
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <libcryptsetup.h>
22 #include <mntent.h>
23 #include <string.h>
24 #include <sys/mman.h>
25
26 #include "sd-device.h"
27
28 #include "alloc-util.h"
29 #include "ask-password-api.h"
30 #include "device-util.h"
31 #include "escape.h"
32 #include "fileio.h"
33 #include "log.h"
34 #include "mount-util.h"
35 #include "parse-util.h"
36 #include "path-util.h"
37 #include "string-util.h"
38 #include "strv.h"
39 #include "util.h"
40
41 static const char *arg_type = NULL; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
42 static char *arg_cipher = NULL;
43 static unsigned arg_key_size = 0;
44 static int arg_key_slot = CRYPT_ANY_SLOT;
45 static unsigned arg_keyfile_size = 0;
46 static unsigned arg_keyfile_offset = 0;
47 static char *arg_hash = NULL;
48 static char *arg_header = NULL;
49 static unsigned arg_tries = 3;
50 static bool arg_readonly = false;
51 static bool arg_verify = false;
52 static bool arg_discards = false;
53 static bool arg_tcrypt_hidden = false;
54 static bool arg_tcrypt_system = false;
55 static char **arg_tcrypt_keyfiles = NULL;
56 static uint64_t arg_offset = 0;
57 static uint64_t arg_skip = 0;
58 static usec_t arg_timeout = 0;
59
60 /* Options Debian's crypttab knows we don't:
61
62 precheck=
63 check=
64 checkargs=
65 noearly=
66 loud=
67 keyscript=
68 */
69
70 static int parse_one_option(const char *option) {
71 const char *val;
72 int r;
73
74 assert(option);
75
76 /* Handled outside of this tool */
77 if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail"))
78 return 0;
79
80 if ((val = startswith(option, "cipher="))) {
81 r = free_and_strdup(&arg_cipher, val);
82 if (r < 0)
83 return log_oom();
84
85 } else if ((val = startswith(option, "size="))) {
86
87 r = safe_atou(val, &arg_key_size);
88 if (r < 0) {
89 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
90 return 0;
91 }
92
93 if (arg_key_size % 8) {
94 log_error("size= not a multiple of 8, ignoring.");
95 return 0;
96 }
97
98 arg_key_size /= 8;
99
100 } else if ((val = startswith(option, "key-slot="))) {
101
102 arg_type = CRYPT_LUKS1;
103 r = safe_atoi(val, &arg_key_slot);
104 if (r < 0) {
105 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
106 return 0;
107 }
108
109 } else if ((val = startswith(option, "tcrypt-keyfile="))) {
110
111 arg_type = CRYPT_TCRYPT;
112 if (path_is_absolute(val)) {
113 if (strv_extend(&arg_tcrypt_keyfiles, val) < 0)
114 return log_oom();
115 } else
116 log_error("Key file path \"%s\" is not absolute. Ignoring.", val);
117
118 } else if ((val = startswith(option, "keyfile-size="))) {
119
120 r = safe_atou(val, &arg_keyfile_size);
121 if (r < 0) {
122 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
123 return 0;
124 }
125
126 } else if ((val = startswith(option, "keyfile-offset="))) {
127
128 r = safe_atou(val, &arg_keyfile_offset);
129 if (r < 0) {
130 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
131 return 0;
132 }
133
134 } else if ((val = startswith(option, "hash="))) {
135 r = free_and_strdup(&arg_hash, val);
136 if (r < 0)
137 return log_oom();
138
139 } else if ((val = startswith(option, "header="))) {
140 arg_type = CRYPT_LUKS1;
141
142 if (!path_is_absolute(val)) {
143 log_error("Header path \"%s\" is not absolute, refusing.", val);
144 return -EINVAL;
145 }
146
147 if (arg_header) {
148 log_error("Duplicate header= option, refusing.");
149 return -EINVAL;
150 }
151
152 arg_header = strdup(val);
153 if (!arg_header)
154 return log_oom();
155
156 } else if ((val = startswith(option, "tries="))) {
157
158 r = safe_atou(val, &arg_tries);
159 if (r < 0) {
160 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
161 return 0;
162 }
163
164 } else if (STR_IN_SET(option, "readonly", "read-only"))
165 arg_readonly = true;
166 else if (streq(option, "verify"))
167 arg_verify = true;
168 else if (STR_IN_SET(option, "allow-discards", "discard"))
169 arg_discards = true;
170 else if (streq(option, "luks"))
171 arg_type = CRYPT_LUKS1;
172 else if (streq(option, "tcrypt"))
173 arg_type = CRYPT_TCRYPT;
174 else if (streq(option, "tcrypt-hidden")) {
175 arg_type = CRYPT_TCRYPT;
176 arg_tcrypt_hidden = true;
177 } else if (streq(option, "tcrypt-system")) {
178 arg_type = CRYPT_TCRYPT;
179 arg_tcrypt_system = true;
180 } else if (STR_IN_SET(option, "plain", "swap", "tmp"))
181 arg_type = CRYPT_PLAIN;
182 else if ((val = startswith(option, "timeout="))) {
183
184 r = parse_sec(val, &arg_timeout);
185 if (r < 0) {
186 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
187 return 0;
188 }
189
190 } else if ((val = startswith(option, "offset="))) {
191
192 r = safe_atou64(val, &arg_offset);
193 if (r < 0)
194 return log_error_errno(r, "Failed to parse %s: %m", option);
195
196 } else if ((val = startswith(option, "skip="))) {
197
198 r = safe_atou64(val, &arg_skip);
199 if (r < 0)
200 return log_error_errno(r, "Failed to parse %s: %m", option);
201
202 } else if (!streq(option, "none"))
203 log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
204
205 return 0;
206 }
207
208 static int parse_options(const char *options) {
209 const char *word, *state;
210 size_t l;
211 int r;
212
213 assert(options);
214
215 FOREACH_WORD_SEPARATOR(word, l, options, ",", state) {
216 _cleanup_free_ char *o;
217
218 o = strndup(word, l);
219 if (!o)
220 return -ENOMEM;
221 r = parse_one_option(o);
222 if (r < 0)
223 return r;
224 }
225
226 /* sanity-check options */
227 if (arg_type != NULL && !streq(arg_type, CRYPT_PLAIN)) {
228 if (arg_offset)
229 log_warning("offset= ignored with type %s", arg_type);
230 if (arg_skip)
231 log_warning("skip= ignored with type %s", arg_type);
232 }
233
234 return 0;
235 }
236
237 static void log_glue(int level, const char *msg, void *usrptr) {
238 log_debug("%s", msg);
239 }
240
241 static int disk_major_minor(const char *path, char **ret) {
242 struct stat st;
243
244 assert(path);
245
246 if (stat(path, &st) < 0)
247 return -errno;
248
249 if (!S_ISBLK(st.st_mode))
250 return -EINVAL;
251
252 if (asprintf(ret, "/dev/block/%d:%d", major(st.st_rdev), minor(st.st_rdev)) < 0)
253 return -errno;
254
255 return 0;
256 }
257
258 static char* disk_description(const char *path) {
259
260 static const char name_fields[] =
261 "ID_PART_ENTRY_NAME\0"
262 "DM_NAME\0"
263 "ID_MODEL_FROM_DATABASE\0"
264 "ID_MODEL\0";
265
266 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
267 struct stat st;
268 const char *i;
269 int r;
270
271 assert(path);
272
273 if (stat(path, &st) < 0)
274 return NULL;
275
276 if (!S_ISBLK(st.st_mode))
277 return NULL;
278
279 r = sd_device_new_from_devnum(&device, 'b', st.st_rdev);
280 if (r < 0)
281 return NULL;
282
283 NULSTR_FOREACH(i, name_fields) {
284 const char *name;
285
286 r = sd_device_get_property_value(device, i, &name);
287 if (r >= 0 && !isempty(name))
288 return strdup(name);
289 }
290
291 return NULL;
292 }
293
294 static char *disk_mount_point(const char *label) {
295 _cleanup_free_ char *device = NULL;
296 _cleanup_endmntent_ FILE *f = NULL;
297 struct mntent *m;
298
299 /* Yeah, we don't support native systemd unit files here for now */
300
301 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
302 return NULL;
303
304 f = setmntent("/etc/fstab", "r");
305 if (!f)
306 return NULL;
307
308 while ((m = getmntent(f)))
309 if (path_equal(m->mnt_fsname, device))
310 return strdup(m->mnt_dir);
311
312 return NULL;
313 }
314
315 static int get_password(const char *vol, const char *src, usec_t until, bool accept_cached, char ***ret) {
316 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL, *maj_min = NULL, *text = NULL, *escaped_name = NULL;
317 _cleanup_strv_free_erase_ char **passwords = NULL;
318 const char *name = NULL;
319 char **p, *id;
320 int r = 0;
321
322 assert(vol);
323 assert(src);
324 assert(ret);
325
326 description = disk_description(src);
327 mount_point = disk_mount_point(vol);
328
329 if (description && streq(vol, description))
330 /* If the description string is simply the
331 * volume name, then let's not show this
332 * twice */
333 description = mfree(description);
334
335 if (mount_point && description)
336 r = asprintf(&name_buffer, "%s (%s) on %s", description, vol, mount_point);
337 else if (mount_point)
338 r = asprintf(&name_buffer, "%s on %s", vol, mount_point);
339 else if (description)
340 r = asprintf(&name_buffer, "%s (%s)", description, vol);
341
342 if (r < 0)
343 return log_oom();
344
345 name = name_buffer ? name_buffer : vol;
346
347 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
348 return log_oom();
349
350 if (src)
351 (void) disk_major_minor(src, &maj_min);
352
353 if (maj_min) {
354 escaped_name = maj_min;
355 maj_min = NULL;
356 } else
357 escaped_name = cescape(name);
358
359 if (!escaped_name)
360 return log_oom();
361
362 id = strjoina("cryptsetup:", escaped_name);
363
364 r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until,
365 ASK_PASSWORD_PUSH_CACHE | (accept_cached*ASK_PASSWORD_ACCEPT_CACHED),
366 &passwords);
367 if (r < 0)
368 return log_error_errno(r, "Failed to query password: %m");
369
370 if (arg_verify) {
371 _cleanup_strv_free_erase_ char **passwords2 = NULL;
372
373 assert(strv_length(passwords) == 1);
374
375 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
376 return log_oom();
377
378 id = strjoina("cryptsetup-verification:", escaped_name);
379
380 r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE, &passwords2);
381 if (r < 0)
382 return log_error_errno(r, "Failed to query verification password: %m");
383
384 assert(strv_length(passwords2) == 1);
385
386 if (!streq(passwords[0], passwords2[0])) {
387 log_warning("Passwords did not match, retrying.");
388 return -EAGAIN;
389 }
390 }
391
392 strv_uniq(passwords);
393
394 STRV_FOREACH(p, passwords) {
395 char *c;
396
397 if (strlen(*p)+1 >= arg_key_size)
398 continue;
399
400 /* Pad password if necessary */
401 c = new(char, arg_key_size);
402 if (!c)
403 return log_oom();
404
405 strncpy(c, *p, arg_key_size);
406 free(*p);
407 *p = c;
408 }
409
410 *ret = passwords;
411 passwords = NULL;
412
413 return 0;
414 }
415
416 static int attach_tcrypt(
417 struct crypt_device *cd,
418 const char *name,
419 const char *key_file,
420 char **passwords,
421 uint32_t flags) {
422
423 int r = 0;
424 _cleanup_free_ char *passphrase = NULL;
425 struct crypt_params_tcrypt params = {
426 .flags = CRYPT_TCRYPT_LEGACY_MODES,
427 .keyfiles = (const char **)arg_tcrypt_keyfiles,
428 .keyfiles_count = strv_length(arg_tcrypt_keyfiles)
429 };
430
431 assert(cd);
432 assert(name);
433 assert(key_file || (passwords && passwords[0]));
434
435 if (arg_tcrypt_hidden)
436 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
437
438 if (arg_tcrypt_system)
439 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
440
441 if (key_file) {
442 r = read_one_line_file(key_file, &passphrase);
443 if (r < 0) {
444 log_error_errno(r, "Failed to read password file '%s': %m", key_file);
445 return -EAGAIN;
446 }
447
448 params.passphrase = passphrase;
449 } else
450 params.passphrase = passwords[0];
451 params.passphrase_size = strlen(params.passphrase);
452
453 r = crypt_load(cd, CRYPT_TCRYPT, &params);
454 if (r < 0) {
455 if (key_file && r == -EPERM) {
456 log_error("Failed to activate using password file '%s'.", key_file);
457 return -EAGAIN;
458 }
459 return r;
460 }
461
462 return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
463 }
464
465 static int attach_luks_or_plain(struct crypt_device *cd,
466 const char *name,
467 const char *key_file,
468 const char *data_device,
469 char **passwords,
470 uint32_t flags) {
471 int r = 0;
472 bool pass_volume_key = false;
473
474 assert(cd);
475 assert(name);
476 assert(key_file || passwords);
477
478 if (!arg_type || streq(arg_type, CRYPT_LUKS1)) {
479 r = crypt_load(cd, CRYPT_LUKS1, NULL);
480 if (r < 0) {
481 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd));
482 return r;
483 }
484
485 if (data_device)
486 r = crypt_set_data_device(cd, data_device);
487 }
488
489 if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) {
490 struct crypt_params_plain params = {
491 .offset = arg_offset,
492 .skip = arg_skip,
493 };
494 const char *cipher, *cipher_mode;
495 _cleanup_free_ char *truncated_cipher = NULL;
496
497 if (arg_hash) {
498 /* plain isn't a real hash type. it just means "use no hash" */
499 if (!streq(arg_hash, "plain"))
500 params.hash = arg_hash;
501 } else if (!key_file)
502 /* for CRYPT_PLAIN, the behaviour of cryptsetup
503 * package is to not hash when a key file is provided */
504 params.hash = "ripemd160";
505
506 if (arg_cipher) {
507 size_t l;
508
509 l = strcspn(arg_cipher, "-");
510 truncated_cipher = strndup(arg_cipher, l);
511 if (!truncated_cipher)
512 return log_oom();
513
514 cipher = truncated_cipher;
515 cipher_mode = arg_cipher[l] ? arg_cipher+l+1 : "plain";
516 } else {
517 cipher = "aes";
518 cipher_mode = "cbc-essiv:sha256";
519 }
520
521 /* for CRYPT_PLAIN limit reads
522 * from keyfile to key length, and
523 * ignore keyfile-size */
524 arg_keyfile_size = arg_key_size;
525
526 /* In contrast to what the name
527 * crypt_setup() might suggest this
528 * doesn't actually format anything,
529 * it just configures encryption
530 * parameters when used for plain
531 * mode. */
532 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, arg_keyfile_size, &params);
533
534 /* hash == NULL implies the user passed "plain" */
535 pass_volume_key = (params.hash == NULL);
536 }
537
538 if (r < 0)
539 return log_error_errno(r, "Loading of cryptographic parameters failed: %m");
540
541 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
542 crypt_get_cipher(cd),
543 crypt_get_cipher_mode(cd),
544 crypt_get_volume_key_size(cd)*8,
545 crypt_get_device_name(cd));
546
547 if (key_file) {
548 r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags);
549 if (r < 0) {
550 log_error_errno(r, "Failed to activate with key file '%s': %m", key_file);
551 return -EAGAIN;
552 }
553 } else {
554 char **p;
555
556 STRV_FOREACH(p, passwords) {
557 if (pass_volume_key)
558 r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
559 else
560 r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
561
562 if (r >= 0)
563 break;
564 }
565 }
566
567 return r;
568 }
569
570 static int help(void) {
571
572 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
573 "%s detach VOLUME\n\n"
574 "Attaches or detaches an encrypted block device.\n",
575 program_invocation_short_name,
576 program_invocation_short_name);
577
578 return 0;
579 }
580
581 int main(int argc, char *argv[]) {
582 int r = EXIT_FAILURE;
583 struct crypt_device *cd = NULL;
584
585 if (argc <= 1) {
586 help();
587 return EXIT_SUCCESS;
588 }
589
590 if (argc < 3) {
591 log_error("This program requires at least two arguments.");
592 return EXIT_FAILURE;
593 }
594
595 log_set_target(LOG_TARGET_AUTO);
596 log_parse_environment();
597 log_open();
598
599 umask(0022);
600
601 if (streq(argv[1], "attach")) {
602 uint32_t flags = 0;
603 int k;
604 unsigned tries;
605 usec_t until;
606 crypt_status_info status;
607 const char *key_file = NULL;
608
609 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
610
611 if (argc < 4) {
612 log_error("attach requires at least two arguments.");
613 goto finish;
614 }
615
616 if (argc >= 5 &&
617 argv[4][0] &&
618 !streq(argv[4], "-") &&
619 !streq(argv[4], "none")) {
620
621 if (!path_is_absolute(argv[4]))
622 log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]);
623 else
624 key_file = argv[4];
625 }
626
627 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
628 if (parse_options(argv[5]) < 0)
629 goto finish;
630 }
631
632 /* A delicious drop of snake oil */
633 mlockall(MCL_FUTURE);
634
635 if (arg_header) {
636 log_debug("LUKS header: %s", arg_header);
637 k = crypt_init(&cd, arg_header);
638 } else
639 k = crypt_init(&cd, argv[3]);
640 if (k) {
641 log_error_errno(k, "crypt_init() failed: %m");
642 goto finish;
643 }
644
645 crypt_set_log_callback(cd, log_glue, NULL);
646
647 status = crypt_status(cd, argv[2]);
648 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
649 log_info("Volume %s already active.", argv[2]);
650 r = EXIT_SUCCESS;
651 goto finish;
652 }
653
654 if (arg_readonly)
655 flags |= CRYPT_ACTIVATE_READONLY;
656
657 if (arg_discards)
658 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
659
660 if (arg_timeout > 0)
661 until = now(CLOCK_MONOTONIC) + arg_timeout;
662 else
663 until = 0;
664
665 arg_key_size = (arg_key_size > 0 ? arg_key_size : (256 / 8));
666
667 if (key_file) {
668 struct stat st;
669
670 /* Ideally we'd do this on the open fd, but since this is just a
671 * warning it's OK to do this in two steps. */
672 if (stat(key_file, &st) >= 0 && S_ISREG(st.st_mode) && (st.st_mode & 0005))
673 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
674 }
675
676 for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) {
677 _cleanup_strv_free_erase_ char **passwords = NULL;
678
679 if (!key_file) {
680 k = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
681 if (k == -EAGAIN)
682 continue;
683 else if (k < 0)
684 goto finish;
685 }
686
687 if (streq_ptr(arg_type, CRYPT_TCRYPT))
688 k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
689 else
690 k = attach_luks_or_plain(cd,
691 argv[2],
692 key_file,
693 arg_header ? argv[3] : NULL,
694 passwords,
695 flags);
696 if (k >= 0)
697 break;
698 else if (k == -EAGAIN) {
699 key_file = NULL;
700 continue;
701 } else if (k != -EPERM) {
702 log_error_errno(k, "Failed to activate: %m");
703 goto finish;
704 }
705
706 log_warning("Invalid passphrase.");
707 }
708
709 if (arg_tries != 0 && tries >= arg_tries) {
710 log_error("Too many attempts; giving up.");
711 r = EXIT_FAILURE;
712 goto finish;
713 }
714
715 } else if (streq(argv[1], "detach")) {
716 int k;
717
718 k = crypt_init_by_name(&cd, argv[2]);
719 if (k == -ENODEV) {
720 log_info("Volume %s already inactive.", argv[2]);
721 r = EXIT_SUCCESS;
722 goto finish;
723 } else if (k) {
724 log_error_errno(k, "crypt_init_by_name() failed: %m");
725 goto finish;
726 }
727
728 crypt_set_log_callback(cd, log_glue, NULL);
729
730 k = crypt_deactivate(cd, argv[2]);
731 if (k < 0) {
732 log_error_errno(k, "Failed to deactivate: %m");
733 goto finish;
734 }
735
736 } else {
737 log_error("Unknown verb %s.", argv[1]);
738 goto finish;
739 }
740
741 r = EXIT_SUCCESS;
742
743 finish:
744
745 if (cd)
746 crypt_free(cd);
747
748 free(arg_cipher);
749 free(arg_hash);
750 free(arg_header);
751 strv_free(arg_tcrypt_keyfiles);
752
753 return r;
754 }