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