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