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