]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptenroll/cryptenroll.c
strv: make iterator in STRV_FOREACH() declaread in the loop
[thirdparty/systemd.git] / src / cryptenroll / cryptenroll.c
CommitLineData
8710a681
LP
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <getopt.h>
4
5#include "ask-password-api.h"
6#include "cryptenroll-fido2.h"
d2fafc42 7#include "cryptenroll-list.h"
8710a681
LP
8#include "cryptenroll-password.h"
9#include "cryptenroll-pkcs11.h"
10#include "cryptenroll-recovery.h"
5e521624 11#include "cryptenroll-tpm2.h"
d2fafc42
LP
12#include "cryptenroll-wipe.h"
13#include "cryptenroll.h"
8710a681 14#include "cryptsetup-util.h"
7a6abbe9 15#include "env-util.h"
8710a681
LP
16#include "escape.h"
17#include "libfido2-util.h"
18#include "main-func.h"
19#include "memory-util.h"
614b022c 20#include "parse-argument.h"
5e521624 21#include "parse-util.h"
8710a681
LP
22#include "path-util.h"
23#include "pkcs11-util.h"
24#include "pretty-print.h"
d2fafc42 25#include "string-table.h"
8710a681
LP
26#include "strv.h"
27#include "terminal-util.h"
5e521624 28#include "tpm2-util.h"
8710a681 29
8710a681
LP
30static EnrollType arg_enroll_type = _ENROLL_TYPE_INVALID;
31static char *arg_pkcs11_token_uri = NULL;
32static char *arg_fido2_device = NULL;
33static char *arg_tpm2_device = NULL;
5e521624 34static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
6c7a1681 35static bool arg_tpm2_pin = false;
8710a681 36static char *arg_node = NULL;
d2fafc42
LP
37static int *arg_wipe_slots = NULL;
38static size_t arg_n_wipe_slots = 0;
39static WipeScope arg_wipe_slots_scope = WIPE_EXPLICIT;
40static unsigned arg_wipe_slots_mask = 0; /* Bitmask of (1U << EnrollType), for wiping all slots of specific types */
06f08719 41static Fido2EnrollFlags arg_fido2_lock_with = FIDO2ENROLL_PIN | FIDO2ENROLL_UP;
d2fafc42
LP
42
43assert_cc(sizeof(arg_wipe_slots_mask) * 8 >= _ENROLL_TYPE_MAX);
8710a681
LP
44
45STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, freep);
46STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, freep);
47STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
48STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
49
d2fafc42
LP
50static bool wipe_requested(void) {
51 return arg_n_wipe_slots > 0 ||
52 arg_wipe_slots_scope != WIPE_EXPLICIT ||
53 arg_wipe_slots_mask != 0;
54}
55
56static const char* const enroll_type_table[_ENROLL_TYPE_MAX] = {
57 [ENROLL_PASSWORD] = "password",
58 [ENROLL_RECOVERY] = "recovery",
59 [ENROLL_PKCS11] = "pkcs11",
60 [ENROLL_FIDO2] = "fido2",
61 [ENROLL_TPM2] = "tpm2",
62};
63
64DEFINE_STRING_TABLE_LOOKUP(enroll_type, EnrollType);
65
66static const char *const luks2_token_type_table[_ENROLL_TYPE_MAX] = {
67 /* ENROLL_PASSWORD has no entry here, as slots of this type do not have a token in the LUKS2 header */
68 [ENROLL_RECOVERY] = "systemd-recovery",
69 [ENROLL_PKCS11] = "systemd-pkcs11",
70 [ENROLL_FIDO2] = "systemd-fido2",
71 [ENROLL_TPM2] = "systemd-tpm2",
72};
73
74DEFINE_STRING_TABLE_LOOKUP(luks2_token_type, EnrollType);
75
8710a681
LP
76static int help(void) {
77 _cleanup_free_ char *link = NULL;
78 int r;
79
80 r = terminal_urlify_man("systemd-cryptenroll", "1", &link);
81 if (r < 0)
82 return log_oom();
83
84 printf("%s [OPTIONS...] BLOCK-DEVICE\n"
85 "\n%sEnroll a security token or authentication credential to a LUKS volume.%s\n\n"
86 " -h --help Show this help\n"
87 " --version Show package version\n"
88 " --password Enroll a user-supplied password\n"
89 " --recovery-key Enroll a recovery key\n"
90 " --pkcs11-token-uri=URI\n"
91 " Specify PKCS#11 security token URI\n"
92 " --fido2-device=PATH\n"
93 " Enroll a FIDO2-HMAC security token\n"
cde2f860
LB
94 " --fido2-with-client-pin=BOOL\n"
95 " Whether to require entering a PIN to unlock the volume\n"
06f08719
LB
96 " --fido2-with-user-presence=BOOL\n"
97 " Whether to require user presence to unlock the volume\n"
896cc0da
LB
98 " --fido2-with-user-verification=BOOL\n"
99 " Whether to require user verification to unlock the volume\n"
5e521624
LP
100 " --tpm2-device=PATH\n"
101 " Enroll a TPM2 device\n"
6e766d98 102 " --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
45861042 103 " Specify TPM2 PCRs to seal against\n"
6c7a1681
GG
104 " --tpm2-with-pin=BOOL\n"
105 " Whether to require entering a PIN to unlock the volume\n"
d2fafc42
LP
106 " --wipe-slot=SLOT1,SLOT2,…\n"
107 " Wipe specified slots\n"
bc556335
DDM
108 "\nSee the %s for details.\n",
109 program_invocation_short_name,
110 ansi_highlight(),
111 ansi_normal(),
112 link);
8710a681
LP
113
114 return 0;
115}
116
117static int parse_argv(int argc, char *argv[]) {
118
119 enum {
120 ARG_VERSION = 0x100,
121 ARG_PASSWORD,
122 ARG_RECOVERY_KEY,
123 ARG_PKCS11_TOKEN_URI,
124 ARG_FIDO2_DEVICE,
5e521624
LP
125 ARG_TPM2_DEVICE,
126 ARG_TPM2_PCRS,
6c7a1681 127 ARG_TPM2_PIN,
d2fafc42 128 ARG_WIPE_SLOT,
cde2f860 129 ARG_FIDO2_WITH_PIN,
06f08719 130 ARG_FIDO2_WITH_UP,
896cc0da 131 ARG_FIDO2_WITH_UV,
8710a681
LP
132 };
133
134 static const struct option options[] = {
cde2f860
LB
135 { "help", no_argument, NULL, 'h' },
136 { "version", no_argument, NULL, ARG_VERSION },
137 { "password", no_argument, NULL, ARG_PASSWORD },
138 { "recovery-key", no_argument, NULL, ARG_RECOVERY_KEY },
139 { "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
140 { "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE },
141 { "fido2-with-client-pin", required_argument, NULL, ARG_FIDO2_WITH_PIN },
06f08719 142 { "fido2-with-user-presence", required_argument, NULL, ARG_FIDO2_WITH_UP },
896cc0da 143 { "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV },
cde2f860
LB
144 { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
145 { "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
6c7a1681 146 { "tpm2-with-pin", required_argument, NULL, ARG_TPM2_PIN },
cde2f860 147 { "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT },
8710a681
LP
148 {}
149 };
150
151 int c, r;
152
153 assert(argc >= 0);
154 assert(argv);
155
156 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
157
158 switch (c) {
159
160 case 'h':
161 return help();
162
163 case ARG_VERSION:
164 return version();
165
cde2f860
LB
166 case ARG_FIDO2_WITH_PIN: {
167 bool lock_with_pin;
168
169 r = parse_boolean_argument("--fido2-with-client-pin=", optarg, &lock_with_pin);
170 if (r < 0)
171 return r;
172
173 SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_PIN, lock_with_pin);
cde2f860
LB
174 break;
175 }
176
06f08719
LB
177 case ARG_FIDO2_WITH_UP: {
178 bool lock_with_up;
179
180 r = parse_boolean_argument("--fido2-with-user-presence=", optarg, &lock_with_up);
181 if (r < 0)
182 return r;
183
184 SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UP, lock_with_up);
06f08719
LB
185 break;
186 }
187
896cc0da
LB
188 case ARG_FIDO2_WITH_UV: {
189 bool lock_with_uv;
190
191 r = parse_boolean_argument("--fido2-with-user-verification=", optarg, &lock_with_uv);
192 if (r < 0)
193 return r;
194
195 SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UV, lock_with_uv);
896cc0da
LB
196 break;
197 }
198
8710a681
LP
199 case ARG_PASSWORD:
200 if (arg_enroll_type >= 0)
201 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
202 "Multiple operations specified at once, refusing.");
203
204 arg_enroll_type = ENROLL_PASSWORD;
205 break;
206
207 case ARG_RECOVERY_KEY:
208 if (arg_enroll_type >= 0)
209 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
210 "Multiple operations specified at once, refusing.");
211
212 arg_enroll_type = ENROLL_RECOVERY;
213 break;
214
215 case ARG_PKCS11_TOKEN_URI: {
216 _cleanup_free_ char *uri = NULL;
217
218 if (streq(optarg, "list"))
219 return pkcs11_list_tokens();
220
221 if (arg_enroll_type >= 0 || arg_pkcs11_token_uri)
222 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
223 "Multiple operations specified at once, refusing.");
224
225 if (streq(optarg, "auto")) {
226 r = pkcs11_find_token_auto(&uri);
227 if (r < 0)
228 return r;
229 } else {
230 if (!pkcs11_uri_valid(optarg))
231 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg);
232
233 uri = strdup(optarg);
234 if (!uri)
235 return log_oom();
236 }
237
238 arg_enroll_type = ENROLL_PKCS11;
239 arg_pkcs11_token_uri = TAKE_PTR(uri);
240 break;
241 }
242
243 case ARG_FIDO2_DEVICE: {
244 _cleanup_free_ char *device = NULL;
245
246 if (streq(optarg, "list"))
247 return fido2_list_devices();
248
249 if (arg_enroll_type >= 0 || arg_fido2_device)
250 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
251 "Multiple operations specified at once, refusing.");
252
253 if (streq(optarg, "auto")) {
254 r = fido2_find_device_auto(&device);
255 if (r < 0)
256 return r;
257 } else {
258 device = strdup(optarg);
259 if (!device)
260 return log_oom();
261 }
262
263 arg_enroll_type = ENROLL_FIDO2;
264 arg_fido2_device = TAKE_PTR(device);
265 break;
266 }
267
5e521624
LP
268 case ARG_TPM2_DEVICE: {
269 _cleanup_free_ char *device = NULL;
270
271 if (streq(optarg, "list"))
272 return tpm2_list_devices();
273
274 if (arg_enroll_type >= 0 || arg_tpm2_device)
275 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
276 "Multiple operations specified at once, refusing.");
277
278 if (!streq(optarg, "auto")) {
279 device = strdup(optarg);
280 if (!device)
281 return log_oom();
282 }
283
284 arg_enroll_type = ENROLL_TPM2;
285 arg_tpm2_device = TAKE_PTR(device);
286 break;
287 }
288
289 case ARG_TPM2_PCRS: {
290 uint32_t mask;
291
292 if (isempty(optarg)) {
293 arg_tpm2_pcr_mask = 0;
294 break;
295 }
296
297 r = tpm2_parse_pcrs(optarg, &mask);
298 if (r < 0)
299 return r;
300
301 if (arg_tpm2_pcr_mask == UINT32_MAX)
302 arg_tpm2_pcr_mask = mask;
303 else
304 arg_tpm2_pcr_mask |= mask;
305
306 break;
307 }
308
6c7a1681
GG
309 case ARG_TPM2_PIN: {
310 r = parse_boolean_argument("--tpm2-with-pin=", optarg, &arg_tpm2_pin);
311 if (r < 0)
312 return r;
313
314 break;
315 }
316
d2fafc42
LP
317 case ARG_WIPE_SLOT: {
318 const char *p = optarg;
319
320 if (isempty(optarg)) {
321 arg_wipe_slots_mask = 0;
322 arg_wipe_slots_scope = WIPE_EXPLICIT;
323 break;
324 }
325
326 for (;;) {
327 _cleanup_free_ char *slot = NULL;
328 unsigned n;
329
330 r = extract_first_word(&p, &slot, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
331 if (r == 0)
332 break;
333 if (r < 0)
334 return log_error_errno(r, "Failed to parse slot list: %s", optarg);
335
336 if (streq(slot, "all"))
337 arg_wipe_slots_scope = WIPE_ALL;
338 else if (streq(slot, "empty")) {
339 if (arg_wipe_slots_scope != WIPE_ALL) /* if "all" was specified before, that wins */
340 arg_wipe_slots_scope = WIPE_EMPTY_PASSPHRASE;
341 } else if (streq(slot, "password"))
342 arg_wipe_slots_mask = 1U << ENROLL_PASSWORD;
343 else if (streq(slot, "recovery"))
344 arg_wipe_slots_mask = 1U << ENROLL_RECOVERY;
345 else if (streq(slot, "pkcs11"))
346 arg_wipe_slots_mask = 1U << ENROLL_PKCS11;
347 else if (streq(slot, "fido2"))
348 arg_wipe_slots_mask = 1U << ENROLL_FIDO2;
349 else if (streq(slot, "tpm2"))
350 arg_wipe_slots_mask = 1U << ENROLL_TPM2;
351 else {
352 int *a;
353
354 r = safe_atou(slot, &n);
355 if (r < 0)
356 return log_error_errno(r, "Failed to parse slot index: %s", slot);
357 if (n > INT_MAX)
358 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Slot index out of range: %u", n);
359
360 a = reallocarray(arg_wipe_slots, sizeof(int), arg_n_wipe_slots + 1);
361 if (!a)
362 return log_oom();
363
364 arg_wipe_slots = a;
365 arg_wipe_slots[arg_n_wipe_slots++] = (int) n;
366 }
367 }
368 break;
369 }
370
8710a681
LP
371 case '?':
372 return -EINVAL;
373
374 default:
04499a70 375 assert_not_reached();
8710a681
LP
376 }
377 }
378
8710a681
LP
379 if (optind >= argc)
380 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
381 "No block device node specified, refusing.");
382
383 if (argc > optind+1)
384 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
385 "Too many arguments, refusing.");
386
614b022c 387 r = parse_path_argument(argv[optind], false, &arg_node);
8710a681
LP
388 if (r < 0)
389 return r;
390
5e521624
LP
391 if (arg_tpm2_pcr_mask == UINT32_MAX)
392 arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT;
393
8710a681
LP
394 return 1;
395}
396
e0142d4f
LP
397static int check_for_homed(struct crypt_device *cd) {
398 int r;
399
400 assert_se(cd);
401
402 /* Politely refuse operating on homed volumes. The enrolled tokens for the user record and the LUKS2
403 * volume should not get out of sync. */
404
405 for (int token = 0; token < crypt_token_max(CRYPT_LUKS2); token ++) {
406 r = cryptsetup_get_token_as_json(cd, token, "systemd-homed", NULL);
407 if (IN_SET(r, -ENOENT, -EINVAL, -EMEDIUMTYPE))
408 continue;
409 if (r < 0)
410 return log_error_errno(r, "Failed to read JSON token data off disk: %m");
411
412 return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
413 "LUKS2 volume is managed by systemd-homed, please use homectl to enroll tokens.");
414 }
415
416 return 0;
417}
418
8710a681
LP
419static int prepare_luks(
420 struct crypt_device **ret_cd,
421 void **ret_volume_key,
422 size_t *ret_volume_key_size) {
423
424 _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
e99ca147 425 _cleanup_(erase_and_freep) char *envpw = NULL;
8710a681 426 _cleanup_(erase_and_freep) void *vk = NULL;
8710a681
LP
427 size_t vks;
428 int r;
429
430 assert(ret_cd);
431 assert(!ret_volume_key == !ret_volume_key_size);
432
433 r = crypt_init(&cd, arg_node);
434 if (r < 0)
435 return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
436
437 cryptsetup_enable_logging(cd);
438
439 r = crypt_load(cd, CRYPT_LUKS2, NULL);
440 if (r < 0)
441 return log_error_errno(r, "Failed to load LUKS2 superblock: %m");
442
e0142d4f
LP
443 r = check_for_homed(cd);
444 if (r < 0)
445 return r;
446
8710a681
LP
447 if (!ret_volume_key) {
448 *ret_cd = TAKE_PTR(cd);
449 return 0;
450 }
451
452 r = crypt_get_volume_key_size(cd);
453 if (r <= 0)
454 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size");
455 vks = (size_t) r;
456
457 vk = malloc(vks);
458 if (!vk)
459 return log_oom();
460
e99ca147
LP
461 r = getenv_steal_erase("PASSWORD", &envpw);
462 if (r < 0)
463 return log_error_errno(r, "Failed to acquire password from environment: %m");
464 if (r > 0) {
8710a681
LP
465 r = crypt_volume_key_get(
466 cd,
467 CRYPT_ANY_SLOT,
468 vk,
469 &vks,
e99ca147
LP
470 envpw,
471 strlen(envpw));
8710a681 472 if (r < 0)
45861042 473 return log_error_errno(r, "Password from environment variable $PASSWORD did not work.");
8710a681
LP
474 } else {
475 AskPasswordFlags ask_password_flags = ASK_PASSWORD_PUSH_CACHE|ASK_PASSWORD_ACCEPT_CACHED;
476 _cleanup_free_ char *question = NULL, *disk_path = NULL;
477 unsigned i = 5;
478 const char *id;
479
480 question = strjoin("Please enter current passphrase for disk ", arg_node, ":");
481 if (!question)
482 return log_oom();
483
484 disk_path = cescape(arg_node);
485 if (!disk_path)
486 return log_oom();
487
488 id = strjoina("cryptsetup:", disk_path);
489
490 for (;;) {
491 _cleanup_strv_free_erase_ char **passwords = NULL;
8710a681
LP
492
493 if (--i == 0)
494 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY),
495 "Too many attempts, giving up:");
496
497 r = ask_password_auto(
8806bb4b 498 question, "drive-harddisk", id, "cryptenroll", "cryptenroll.passphrase", USEC_INFINITY,
8710a681
LP
499 ask_password_flags,
500 &passwords);
501 if (r < 0)
502 return log_error_errno(r, "Failed to query password: %m");
503
504 r = -EPERM;
505 STRV_FOREACH(p, passwords) {
506 r = crypt_volume_key_get(
507 cd,
508 CRYPT_ANY_SLOT,
509 vk,
510 &vks,
511 *p,
512 strlen(*p));
513 if (r >= 0)
514 break;
515 }
516 if (r >= 0)
517 break;
518
519 log_error_errno(r, "Password not correct, please try again.");
520 ask_password_flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
521 }
522 }
523
524 *ret_cd = TAKE_PTR(cd);
525 *ret_volume_key = TAKE_PTR(vk);
526 *ret_volume_key_size = vks;
527
528 return 0;
529}
530
531static int run(int argc, char *argv[]) {
532 _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
533 _cleanup_(erase_and_freep) void *vk = NULL;
534 size_t vks;
d2fafc42 535 int slot, r;
8710a681
LP
536
537 log_show_color(true);
538 log_parse_environment();
539 log_open();
540
541 r = parse_argv(argc, argv);
542 if (r <= 0)
543 return r;
544
2f678640
LP
545 cryptsetup_enable_logging(NULL);
546
d2fafc42
LP
547 if (arg_enroll_type < 0)
548 r = prepare_luks(&cd, NULL, NULL); /* No need to unlock device if we don't need the volume key because we don't need to enroll anything */
549 else
550 r = prepare_luks(&cd, &vk, &vks);
8710a681
LP
551 if (r < 0)
552 return r;
553
554 switch (arg_enroll_type) {
555
556 case ENROLL_PASSWORD:
d2fafc42 557 slot = enroll_password(cd, vk, vks);
8710a681
LP
558 break;
559
560 case ENROLL_RECOVERY:
d2fafc42 561 slot = enroll_recovery(cd, vk, vks);
8710a681
LP
562 break;
563
564 case ENROLL_PKCS11:
d2fafc42 565 slot = enroll_pkcs11(cd, vk, vks, arg_pkcs11_token_uri);
8710a681
LP
566 break;
567
568 case ENROLL_FIDO2:
cde2f860 569 slot = enroll_fido2(cd, vk, vks, arg_fido2_device, arg_fido2_lock_with);
8710a681
LP
570 break;
571
5e521624 572 case ENROLL_TPM2:
6c7a1681 573 slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_pcr_mask, arg_tpm2_pin);
5e521624
LP
574 break;
575
d2fafc42
LP
576 case _ENROLL_TYPE_INVALID:
577 /* List enrolled slots if we are called without anything to enroll or wipe */
578 if (!wipe_requested())
579 return list_enrolled(cd);
580
581 /* Only slot wiping selected */
582 return wipe_slots(cd, arg_wipe_slots, arg_n_wipe_slots, arg_wipe_slots_scope, arg_wipe_slots_mask, -1);
583
8710a681
LP
584 default:
585 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Operation not implemented yet.");
586 }
d2fafc42
LP
587 if (slot < 0)
588 return slot;
8710a681 589
d2fafc42
LP
590 /* After we completed enrolling, remove user selected slots */
591 r = wipe_slots(cd, arg_wipe_slots, arg_n_wipe_slots, arg_wipe_slots_scope, arg_wipe_slots_mask, slot);
592 if (r < 0)
593 return r;
594
595 return 0;
8710a681
LP
596}
597
598DEFINE_MAIN_FUNCTION(run);