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