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