]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptenroll/cryptenroll.c
NEWS: fix typo
[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;
412 _cleanup_(erase_and_freep) void *vk = NULL;
413 char *e = NULL;
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
448 e = getenv("PASSWORD");
449 if (e) {
450 _cleanup_(erase_and_freep) char *password = NULL;
451
452 password = strdup(e);
453 if (!password)
454 return log_oom();
455
7a6abbe9 456 assert_se(unsetenv_erase("PASSWORD") >= 0);
8710a681
LP
457
458 r = crypt_volume_key_get(
459 cd,
460 CRYPT_ANY_SLOT,
461 vk,
462 &vks,
463 password,
464 strlen(password));
465 if (r < 0)
45861042 466 return log_error_errno(r, "Password from environment variable $PASSWORD did not work.");
8710a681
LP
467 } else {
468 AskPasswordFlags ask_password_flags = ASK_PASSWORD_PUSH_CACHE|ASK_PASSWORD_ACCEPT_CACHED;
469 _cleanup_free_ char *question = NULL, *disk_path = NULL;
470 unsigned i = 5;
471 const char *id;
472
473 question = strjoin("Please enter current passphrase for disk ", arg_node, ":");
474 if (!question)
475 return log_oom();
476
477 disk_path = cescape(arg_node);
478 if (!disk_path)
479 return log_oom();
480
481 id = strjoina("cryptsetup:", disk_path);
482
483 for (;;) {
484 _cleanup_strv_free_erase_ char **passwords = NULL;
485 char **p;
486
487 if (--i == 0)
488 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY),
489 "Too many attempts, giving up:");
490
491 r = ask_password_auto(
8806bb4b 492 question, "drive-harddisk", id, "cryptenroll", "cryptenroll.passphrase", USEC_INFINITY,
8710a681
LP
493 ask_password_flags,
494 &passwords);
495 if (r < 0)
496 return log_error_errno(r, "Failed to query password: %m");
497
498 r = -EPERM;
499 STRV_FOREACH(p, passwords) {
500 r = crypt_volume_key_get(
501 cd,
502 CRYPT_ANY_SLOT,
503 vk,
504 &vks,
505 *p,
506 strlen(*p));
507 if (r >= 0)
508 break;
509 }
510 if (r >= 0)
511 break;
512
513 log_error_errno(r, "Password not correct, please try again.");
514 ask_password_flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
515 }
516 }
517
518 *ret_cd = TAKE_PTR(cd);
519 *ret_volume_key = TAKE_PTR(vk);
520 *ret_volume_key_size = vks;
521
522 return 0;
523}
524
525static int run(int argc, char *argv[]) {
526 _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
527 _cleanup_(erase_and_freep) void *vk = NULL;
528 size_t vks;
d2fafc42 529 int slot, r;
8710a681
LP
530
531 log_show_color(true);
532 log_parse_environment();
533 log_open();
534
535 r = parse_argv(argc, argv);
536 if (r <= 0)
537 return r;
538
2f678640
LP
539 cryptsetup_enable_logging(NULL);
540
d2fafc42
LP
541 if (arg_enroll_type < 0)
542 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 */
543 else
544 r = prepare_luks(&cd, &vk, &vks);
8710a681
LP
545 if (r < 0)
546 return r;
547
548 switch (arg_enroll_type) {
549
550 case ENROLL_PASSWORD:
d2fafc42 551 slot = enroll_password(cd, vk, vks);
8710a681
LP
552 break;
553
554 case ENROLL_RECOVERY:
d2fafc42 555 slot = enroll_recovery(cd, vk, vks);
8710a681
LP
556 break;
557
558 case ENROLL_PKCS11:
d2fafc42 559 slot = enroll_pkcs11(cd, vk, vks, arg_pkcs11_token_uri);
8710a681
LP
560 break;
561
562 case ENROLL_FIDO2:
cde2f860 563 slot = enroll_fido2(cd, vk, vks, arg_fido2_device, arg_fido2_lock_with);
8710a681
LP
564 break;
565
5e521624 566 case ENROLL_TPM2:
d2fafc42 567 slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_pcr_mask);
5e521624
LP
568 break;
569
d2fafc42
LP
570 case _ENROLL_TYPE_INVALID:
571 /* List enrolled slots if we are called without anything to enroll or wipe */
572 if (!wipe_requested())
573 return list_enrolled(cd);
574
575 /* Only slot wiping selected */
576 return wipe_slots(cd, arg_wipe_slots, arg_n_wipe_slots, arg_wipe_slots_scope, arg_wipe_slots_mask, -1);
577
8710a681
LP
578 default:
579 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Operation not implemented yet.");
580 }
d2fafc42
LP
581 if (slot < 0)
582 return slot;
8710a681 583
d2fafc42
LP
584 /* After we completed enrolling, remove user selected slots */
585 r = wipe_slots(cd, arg_wipe_slots, arg_n_wipe_slots, arg_wipe_slots_scope, arg_wipe_slots_mask, slot);
586 if (r < 0)
587 return r;
588
589 return 0;
8710a681
LP
590}
591
592DEFINE_MAIN_FUNCTION(run);