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