]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup.c
cryptsetup: when unlocking always put path to the object into Id
[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;
50static unsigned arg_keyfile_offset = 0;
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="))) {
880a599e 134
fb4650aa
ZJS
135 r = safe_atou(val, &arg_keyfile_offset);
136 if (r < 0) {
137 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
880a599e
TG
138 return 0;
139 }
140
fb4650aa
ZJS
141 } else if ((val = startswith(option, "hash="))) {
142 r = free_and_strdup(&arg_hash, val);
143 if (r < 0)
4b93637f 144 return log_oom();
7f4e0805 145
fb4650aa 146 } else if ((val = startswith(option, "header="))) {
b3b4ebab 147 arg_type = ANY_LUKS;
7376e835 148
fb4650aa
ZJS
149 if (!path_is_absolute(val)) {
150 log_error("Header path \"%s\" is not absolute, refusing.", val);
7376e835
AC
151 return -EINVAL;
152 }
153
154 if (arg_header) {
fb4650aa 155 log_error("Duplicate header= option, refusing.");
7376e835
AC
156 return -EINVAL;
157 }
158
fb4650aa 159 arg_header = strdup(val);
7376e835
AC
160 if (!arg_header)
161 return log_oom();
162
fb4650aa 163 } else if ((val = startswith(option, "tries="))) {
7f4e0805 164
fb4650aa
ZJS
165 r = safe_atou(val, &arg_tries);
166 if (r < 0) {
167 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
7f4e0805
LP
168 return 0;
169 }
170
f75cac37
LP
171 } else if (STR_IN_SET(option, "readonly", "read-only"))
172 arg_readonly = true;
7f4e0805 173 else if (streq(option, "verify"))
f75cac37
LP
174 arg_verify = true;
175 else if (STR_IN_SET(option, "allow-discards", "discard"))
176 arg_discards = true;
260ab287 177 else if (streq(option, "luks"))
b3b4ebab 178 arg_type = ANY_LUKS;
8cf3ca80 179 else if (streq(option, "tcrypt"))
f75cac37 180 arg_type = CRYPT_TCRYPT;
8cf3ca80 181 else if (streq(option, "tcrypt-hidden")) {
f75cac37
LP
182 arg_type = CRYPT_TCRYPT;
183 arg_tcrypt_hidden = true;
8cf3ca80 184 } else if (streq(option, "tcrypt-system")) {
f75cac37
LP
185 arg_type = CRYPT_TCRYPT;
186 arg_tcrypt_system = true;
52028838
GH
187 } else if (streq(option, "tcrypt-veracrypt")) {
188#ifdef CRYPT_TCRYPT_VERA_MODES
189 arg_type = CRYPT_TCRYPT;
190 arg_tcrypt_veracrypt = true;
191#else
192 log_error("This version of cryptsetup does not support tcrypt-veracrypt; refusing.");
193 return -EINVAL;
194#endif
f75cac37
LP
195 } else if (STR_IN_SET(option, "plain", "swap", "tmp"))
196 arg_type = CRYPT_PLAIN;
fb4650aa 197 else if ((val = startswith(option, "timeout="))) {
7f4e0805 198
0004f698 199 r = parse_sec_fix_0(val, &arg_timeout);
fb4650aa
ZJS
200 if (r < 0) {
201 log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
7f4e0805
LP
202 return 0;
203 }
204
fb4650aa 205 } else if ((val = startswith(option, "offset="))) {
4eac2773 206
fb4650aa
ZJS
207 r = safe_atou64(val, &arg_offset);
208 if (r < 0)
209 return log_error_errno(r, "Failed to parse %s: %m", option);
4eac2773 210
fb4650aa 211 } else if ((val = startswith(option, "skip="))) {
4eac2773 212
fb4650aa
ZJS
213 r = safe_atou64(val, &arg_skip);
214 if (r < 0)
215 return log_error_errno(r, "Failed to parse %s: %m", option);
4eac2773 216
41e6f28a 217 } else if (!streq(option, "none"))
fb4650aa 218 log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
7f4e0805
LP
219
220 return 0;
221}
222
223static int parse_options(const char *options) {
a2a5291b 224 const char *word, *state;
7f4e0805 225 size_t l;
74b1c371 226 int r;
7f4e0805
LP
227
228 assert(options);
229
a2a5291b 230 FOREACH_WORD_SEPARATOR(word, l, options, ",", state) {
74b1c371 231 _cleanup_free_ char *o;
7f4e0805 232
a2a5291b 233 o = strndup(word, l);
74b1c371 234 if (!o)
7f4e0805 235 return -ENOMEM;
7f4e0805 236 r = parse_one_option(o);
7f4e0805
LP
237 if (r < 0)
238 return r;
239 }
240
4eac2773
MP
241 /* sanity-check options */
242 if (arg_type != NULL && !streq(arg_type, CRYPT_PLAIN)) {
243 if (arg_offset)
244 log_warning("offset= ignored with type %s", arg_type);
245 if (arg_skip)
246 log_warning("skip= ignored with type %s", arg_type);
247 }
248
7f4e0805
LP
249 return 0;
250}
251
e51b9486
HH
252static int disk_major_minor(const char *path, char **ret) {
253 struct stat st;
254
255 assert(path);
256
257 if (stat(path, &st) < 0)
258 return -errno;
259
260 if (!S_ISBLK(st.st_mode))
261 return -EINVAL;
262
263 if (asprintf(ret, "/dev/block/%d:%d", major(st.st_rdev), minor(st.st_rdev)) < 0)
264 return -errno;
265
266 return 0;
267}
268
1ca208fb 269static char* disk_description(const char *path) {
74b1c371 270
f75cac37 271 static const char name_fields[] =
74b1c371
LP
272 "ID_PART_ENTRY_NAME\0"
273 "DM_NAME\0"
274 "ID_MODEL_FROM_DATABASE\0"
f75cac37 275 "ID_MODEL\0";
74b1c371 276
4afd3348 277 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
b1a2da0a 278 struct stat st;
74b1c371 279 const char *i;
9a97aaae 280 int r;
b1a2da0a
LP
281
282 assert(path);
283
284 if (stat(path, &st) < 0)
285 return NULL;
286
287 if (!S_ISBLK(st.st_mode))
288 return NULL;
289
9a97aaae
TG
290 r = sd_device_new_from_devnum(&device, 'b', st.st_rdev);
291 if (r < 0)
1ca208fb 292 return NULL;
b1a2da0a 293
74b1c371
LP
294 NULSTR_FOREACH(i, name_fields) {
295 const char *name;
296
9a97aaae
TG
297 r = sd_device_get_property_value(device, i, &name);
298 if (r >= 0 && !isempty(name))
1ca208fb 299 return strdup(name);
74b1c371 300 }
b1a2da0a 301
1ca208fb 302 return NULL;
b1a2da0a
LP
303}
304
b61e476f 305static char *disk_mount_point(const char *label) {
e7d90b71 306 _cleanup_free_ char *device = NULL;
5862d652 307 _cleanup_endmntent_ FILE *f = NULL;
b61e476f
LP
308 struct mntent *m;
309
310 /* Yeah, we don't support native systemd unit files here for now */
311
312 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
5862d652 313 return NULL;
b61e476f 314
9ffcff0e 315 f = setmntent("/etc/fstab", "re");
e0295d26 316 if (!f)
5862d652 317 return NULL;
b61e476f
LP
318
319 while ((m = getmntent(f)))
5862d652
ZJS
320 if (path_equal(m->mnt_fsname, device))
321 return strdup(m->mnt_dir);
b61e476f 322
5862d652 323 return NULL;
b61e476f
LP
324}
325
1602b008 326static int get_password(const char *vol, const char *src, usec_t until, bool accept_cached, char ***ret) {
e287086b 327 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL, *maj_min = NULL, *text = NULL, *escaped_name = NULL;
ab84f5b9 328 _cleanup_strv_free_erase_ char **passwords = NULL;
e51b9486 329 const char *name = NULL;
e287086b
LP
330 char **p, *id;
331 int r = 0;
e7d90b71 332
e51b9486
HH
333 assert(vol);
334 assert(src);
1602b008 335 assert(ret);
e7d90b71 336
e51b9486
HH
337 description = disk_description(src);
338 mount_point = disk_mount_point(vol);
339
ece174c5 340 if (description && streq(vol, description))
e51b9486
HH
341 /* If the description string is simply the
342 * volume name, then let's not show this
343 * twice */
97b11eed 344 description = mfree(description);
e51b9486
HH
345
346 if (mount_point && description)
347 r = asprintf(&name_buffer, "%s (%s) on %s", description, vol, mount_point);
348 else if (mount_point)
349 r = asprintf(&name_buffer, "%s on %s", vol, mount_point);
350 else if (description)
351 r = asprintf(&name_buffer, "%s (%s)", description, vol);
352
353 if (r < 0)
354 return log_oom();
355
356 name = name_buffer ? name_buffer : vol;
357
e7d90b71
JJ
358 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
359 return log_oom();
360
e51b9486
HH
361 if (src)
362 (void) disk_major_minor(src, &maj_min);
363
364 if (maj_min) {
365 escaped_name = maj_min;
366 maj_min = NULL;
367 } else
5a9f1b05 368 escaped_name = cescape(src);
e51b9486 369
9fa1de96
DH
370 if (!escaped_name)
371 return log_oom();
372
63c372cb 373 id = strjoina("cryptsetup:", escaped_name);
9fa1de96 374
ab84f5b9
ZJS
375 r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until,
376 ASK_PASSWORD_PUSH_CACHE | (accept_cached*ASK_PASSWORD_ACCEPT_CACHED),
377 &passwords);
23bbb0de
MS
378 if (r < 0)
379 return log_error_errno(r, "Failed to query password: %m");
e7d90b71 380
f75cac37 381 if (arg_verify) {
ab84f5b9
ZJS
382 _cleanup_strv_free_erase_ char **passwords2 = NULL;
383
1602b008 384 assert(strv_length(passwords) == 1);
e7d90b71 385
ab84f5b9
ZJS
386 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
387 return log_oom();
e7d90b71 388
63c372cb 389 id = strjoina("cryptsetup-verification:", escaped_name);
9fa1de96 390
e287086b 391 r = ask_password_auto(text, "drive-harddisk", id, "cryptsetup", until, ASK_PASSWORD_PUSH_CACHE, &passwords2);
ab84f5b9
ZJS
392 if (r < 0)
393 return log_error_errno(r, "Failed to query verification password: %m");
e7d90b71
JJ
394
395 assert(strv_length(passwords2) == 1);
396
1602b008 397 if (!streq(passwords[0], passwords2[0])) {
e7d90b71 398 log_warning("Passwords did not match, retrying.");
ab84f5b9 399 return -EAGAIN;
e7d90b71
JJ
400 }
401 }
402
1602b008 403 strv_uniq(passwords);
e7d90b71 404
1602b008 405 STRV_FOREACH(p, passwords) {
e7d90b71
JJ
406 char *c;
407
f75cac37 408 if (strlen(*p)+1 >= arg_key_size)
e7d90b71
JJ
409 continue;
410
411 /* Pad password if necessary */
1602b008 412 c = new(char, arg_key_size);
ab84f5b9
ZJS
413 if (!c)
414 return log_oom();
e7d90b71 415
f75cac37 416 strncpy(c, *p, arg_key_size);
e7d90b71
JJ
417 free(*p);
418 *p = c;
419 }
420
1602b008
LP
421 *ret = passwords;
422 passwords = NULL;
423
ab84f5b9 424 return 0;
e7d90b71
JJ
425}
426
1602b008
LP
427static int attach_tcrypt(
428 struct crypt_device *cd,
429 const char *name,
430 const char *key_file,
431 char **passwords,
432 uint32_t flags) {
433
8cf3ca80
JJ
434 int r = 0;
435 _cleanup_free_ char *passphrase = NULL;
436 struct crypt_params_tcrypt params = {
437 .flags = CRYPT_TCRYPT_LEGACY_MODES,
f75cac37
LP
438 .keyfiles = (const char **)arg_tcrypt_keyfiles,
439 .keyfiles_count = strv_length(arg_tcrypt_keyfiles)
8cf3ca80
JJ
440 };
441
442 assert(cd);
443 assert(name);
f268f57f 444 assert(key_file || (passwords && passwords[0]));
8cf3ca80 445
f75cac37 446 if (arg_tcrypt_hidden)
8cf3ca80
JJ
447 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
448
f75cac37 449 if (arg_tcrypt_system)
8cf3ca80
JJ
450 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
451
52028838
GH
452#ifdef CRYPT_TCRYPT_VERA_MODES
453 if (arg_tcrypt_veracrypt)
454 params.flags |= CRYPT_TCRYPT_VERA_MODES;
455#endif
456
8cf3ca80
JJ
457 if (key_file) {
458 r = read_one_line_file(key_file, &passphrase);
459 if (r < 0) {
da927ba9 460 log_error_errno(r, "Failed to read password file '%s': %m", key_file);
8cf3ca80
JJ
461 return -EAGAIN;
462 }
463
464 params.passphrase = passphrase;
465 } else
466 params.passphrase = passwords[0];
467 params.passphrase_size = strlen(params.passphrase);
468
469 r = crypt_load(cd, CRYPT_TCRYPT, &params);
470 if (r < 0) {
471 if (key_file && r == -EPERM) {
472 log_error("Failed to activate using password file '%s'.", key_file);
473 return -EAGAIN;
474 }
475 return r;
476 }
477
ac1a87b9 478 return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
8cf3ca80
JJ
479}
480
10fb4e35
JJ
481static int attach_luks_or_plain(struct crypt_device *cd,
482 const char *name,
483 const char *key_file,
7376e835 484 const char *data_device,
10fb4e35
JJ
485 char **passwords,
486 uint32_t flags) {
487 int r = 0;
488 bool pass_volume_key = false;
489
490 assert(cd);
491 assert(name);
492 assert(key_file || passwords);
493
b3b4ebab
OK
494 if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1)) {
495 r = crypt_load(cd, CRYPT_LUKS, NULL);
7376e835
AC
496 if (r < 0) {
497 log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd));
498 return r;
499 }
500
501 if (data_device)
502 r = crypt_set_data_device(cd, data_device);
503 }
10fb4e35 504
f75cac37 505 if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) {
4eac2773
MP
506 struct crypt_params_plain params = {
507 .offset = arg_offset,
508 .skip = arg_skip,
509 };
10fb4e35
JJ
510 const char *cipher, *cipher_mode;
511 _cleanup_free_ char *truncated_cipher = NULL;
512
f75cac37 513 if (arg_hash) {
10fb4e35 514 /* plain isn't a real hash type. it just means "use no hash" */
f75cac37
LP
515 if (!streq(arg_hash, "plain"))
516 params.hash = arg_hash;
8a52210c
ZJS
517 } else if (!key_file)
518 /* for CRYPT_PLAIN, the behaviour of cryptsetup
519 * package is to not hash when a key file is provided */
10fb4e35
JJ
520 params.hash = "ripemd160";
521
f75cac37 522 if (arg_cipher) {
10fb4e35
JJ
523 size_t l;
524
f75cac37
LP
525 l = strcspn(arg_cipher, "-");
526 truncated_cipher = strndup(arg_cipher, l);
10fb4e35
JJ
527 if (!truncated_cipher)
528 return log_oom();
529
530 cipher = truncated_cipher;
f75cac37 531 cipher_mode = arg_cipher[l] ? arg_cipher+l+1 : "plain";
10fb4e35
JJ
532 } else {
533 cipher = "aes";
534 cipher_mode = "cbc-essiv:sha256";
535 }
536
537 /* for CRYPT_PLAIN limit reads
538 * from keyfile to key length, and
539 * ignore keyfile-size */
6131a78b 540 arg_keyfile_size = arg_key_size;
10fb4e35
JJ
541
542 /* In contrast to what the name
543 * crypt_setup() might suggest this
544 * doesn't actually format anything,
545 * it just configures encryption
546 * parameters when used for plain
547 * mode. */
1602b008 548 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, arg_keyfile_size, &params);
10fb4e35
JJ
549
550 /* hash == NULL implies the user passed "plain" */
551 pass_volume_key = (params.hash == NULL);
552 }
553
23bbb0de
MS
554 if (r < 0)
555 return log_error_errno(r, "Loading of cryptographic parameters failed: %m");
10fb4e35
JJ
556
557 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
558 crypt_get_cipher(cd),
559 crypt_get_cipher_mode(cd),
560 crypt_get_volume_key_size(cd)*8,
561 crypt_get_device_name(cd));
562
563 if (key_file) {
1602b008 564 r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags);
10fb4e35 565 if (r < 0) {
da927ba9 566 log_error_errno(r, "Failed to activate with key file '%s': %m", key_file);
10fb4e35
JJ
567 return -EAGAIN;
568 }
569 } else {
570 char **p;
571
572 STRV_FOREACH(p, passwords) {
573 if (pass_volume_key)
f75cac37 574 r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
10fb4e35 575 else
f75cac37 576 r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
10fb4e35
JJ
577
578 if (r >= 0)
579 break;
580 }
581 }
582
583 return r;
584}
585
dd5e696d
LP
586static int help(void) {
587
588 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
589 "%s detach VOLUME\n\n"
590 "Attaches or detaches an encrypted block device.\n",
591 program_invocation_short_name,
592 program_invocation_short_name);
593
594 return 0;
595}
596
e23a0ce8 597int main(int argc, char *argv[]) {
294bd454 598 _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
81a6ac6c 599 int r = -EINVAL;
e23a0ce8 600
dd5e696d 601 if (argc <= 1) {
5f4bfe56
LP
602 r = help();
603 goto finish;
dd5e696d
LP
604 }
605
e23a0ce8
LP
606 if (argc < 3) {
607 log_error("This program requires at least two arguments.");
5f4bfe56 608 goto finish;
e23a0ce8
LP
609 }
610
bb7df0da 611 log_set_target(LOG_TARGET_AUTO);
e23a0ce8
LP
612 log_parse_environment();
613 log_open();
614
4c12626c
LP
615 umask(0022);
616
260ab287 617 if (streq(argv[1], "attach")) {
7f4e0805 618 uint32_t flags = 0;
10fb4e35 619 unsigned tries;
260ab287 620 usec_t until;
e2d480b9 621 crypt_status_info status;
e51b9486 622 const char *key_file = NULL;
7f4e0805 623
b61e476f
LP
624 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
625
7f4e0805
LP
626 if (argc < 4) {
627 log_error("attach requires at least two arguments.");
628 goto finish;
629 }
630
1fc76335
LP
631 if (argc >= 5 &&
632 argv[4][0] &&
633 !streq(argv[4], "-") &&
634 !streq(argv[4], "none")) {
7f4e0805
LP
635
636 if (!path_is_absolute(argv[4]))
8cf3ca80 637 log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]);
7f4e0805
LP
638 else
639 key_file = argv[4];
640 }
641
74b1c371
LP
642 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
643 if (parse_options(argv[5]) < 0)
644 goto finish;
645 }
e23a0ce8 646
b853f6e9
LP
647 /* A delicious drop of snake oil */
648 mlockall(MCL_FUTURE);
649
7376e835
AC
650 if (arg_header) {
651 log_debug("LUKS header: %s", arg_header);
5f4bfe56 652 r = crypt_init(&cd, arg_header);
7376e835 653 } else
5f4bfe56
LP
654 r = crypt_init(&cd, argv[3]);
655 if (r < 0) {
656 log_error_errno(r, "crypt_init() failed: %m");
7f4e0805
LP
657 goto finish;
658 }
e23a0ce8 659
691c2e2e 660 crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
7f4e0805 661
260ab287 662 status = crypt_status(cd, argv[2]);
3742095b 663 if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) {
260ab287 664 log_info("Volume %s already active.", argv[2]);
5f4bfe56 665 r = 0;
7f4e0805
LP
666 goto finish;
667 }
668
f75cac37 669 if (arg_readonly)
7f4e0805
LP
670 flags |= CRYPT_ACTIVATE_READONLY;
671
f75cac37 672 if (arg_discards)
2a2aab60
MM
673 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
674
0864d311 675 if (arg_timeout == USEC_INFINITY)
7dcda352 676 until = 0;
0864d311
AS
677 else
678 until = now(CLOCK_MONOTONIC) + arg_timeout;
260ab287 679
6131a78b 680 arg_key_size = (arg_key_size > 0 ? arg_key_size : (256 / 8));
e2d480b9 681
10fb4e35
JJ
682 if (key_file) {
683 struct stat st;
e2d480b9 684
10fb4e35
JJ
685 /* Ideally we'd do this on the open fd, but since this is just a
686 * warning it's OK to do this in two steps. */
3f4d56a0
MP
687 if (stat(key_file, &st) >= 0 && S_ISREG(st.st_mode) && (st.st_mode & 0005))
688 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
e2d480b9
LP
689 }
690
f75cac37 691 for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) {
ab84f5b9 692 _cleanup_strv_free_erase_ char **passwords = NULL;
260ab287
LP
693
694 if (!key_file) {
5f4bfe56
LP
695 r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
696 if (r == -EAGAIN)
e7d90b71 697 continue;
5f4bfe56 698 if (r < 0)
260ab287 699 goto finish;
260ab287
LP
700 }
701
f75cac37 702 if (streq_ptr(arg_type, CRYPT_TCRYPT))
5f4bfe56 703 r = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
8cf3ca80 704 else
5f4bfe56 705 r = attach_luks_or_plain(cd,
7376e835
AC
706 argv[2],
707 key_file,
708 arg_header ? argv[3] : NULL,
709 passwords,
710 flags);
5f4bfe56 711 if (r >= 0)
260ab287 712 break;
5f4bfe56 713 if (r == -EAGAIN) {
10fb4e35
JJ
714 key_file = NULL;
715 continue;
5f4bfe56
LP
716 }
717 if (r != -EPERM) {
718 log_error_errno(r, "Failed to activate: %m");
260ab287
LP
719 goto finish;
720 }
721
722 log_warning("Invalid passphrase.");
7f4e0805
LP
723 }
724
f75cac37 725 if (arg_tries != 0 && tries >= arg_tries) {
10fb4e35 726 log_error("Too many attempts; giving up.");
5f4bfe56 727 r = -EPERM;
bd40a2d8 728 goto finish;
7f4e0805
LP
729 }
730
731 } else if (streq(argv[1], "detach")) {
7f4e0805 732
5f4bfe56
LP
733 r = crypt_init_by_name(&cd, argv[2]);
734 if (r == -ENODEV) {
a0bfc9c2 735 log_info("Volume %s already inactive.", argv[2]);
5f4bfe56 736 r = 0;
a0bfc9c2 737 goto finish;
5f4bfe56
LP
738 }
739 if (r < 0) {
740 log_error_errno(r, "crypt_init_by_name() failed: %m");
7f4e0805
LP
741 goto finish;
742 }
743
691c2e2e 744 crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
7f4e0805 745
5f4bfe56
LP
746 r = crypt_deactivate(cd, argv[2]);
747 if (r < 0) {
748 log_error_errno(r, "Failed to deactivate: %m");
7f4e0805
LP
749 goto finish;
750 }
e23a0ce8
LP
751
752 } else {
753 log_error("Unknown verb %s.", argv[1]);
754 goto finish;
755 }
756
5f4bfe56 757 r = 0;
7f4e0805 758
e23a0ce8 759finish:
f75cac37
LP
760 free(arg_cipher);
761 free(arg_hash);
7376e835 762 free(arg_header);
f75cac37 763 strv_free(arg_tcrypt_keyfiles);
260ab287 764
5f4bfe56 765 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
e23a0ce8 766}