1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "ask-password-api.h"
7 #include "cryptenroll-fido2.h"
8 #include "cryptenroll-list.h"
9 #include "cryptenroll-password.h"
10 #include "cryptenroll-pkcs11.h"
11 #include "cryptenroll-recovery.h"
12 #include "cryptenroll-tpm2.h"
13 #include "cryptenroll-wipe.h"
14 #include "cryptenroll.h"
15 #include "cryptsetup-util.h"
19 #include "libfido2-util.h"
20 #include "main-func.h"
21 #include "memory-util.h"
22 #include "parse-argument.h"
23 #include "parse-util.h"
24 #include "path-util.h"
25 #include "pkcs11-util.h"
26 #include "pretty-print.h"
27 #include "string-table.h"
29 #include "terminal-util.h"
31 #include "tpm2-util.h"
33 static EnrollType arg_enroll_type
= _ENROLL_TYPE_INVALID
;
34 static char *arg_unlock_keyfile
= NULL
;
35 static UnlockType arg_unlock_type
= UNLOCK_PASSWORD
;
36 static char *arg_unlock_fido2_device
= NULL
;
37 static char *arg_pkcs11_token_uri
= NULL
;
38 static char *arg_fido2_device
= NULL
;
39 static char *arg_tpm2_device
= NULL
;
40 static uint32_t arg_tpm2_pcr_mask
= UINT32_MAX
;
41 static bool arg_tpm2_pin
= false;
42 static char *arg_tpm2_public_key
= NULL
;
43 static uint32_t arg_tpm2_public_key_pcr_mask
= UINT32_MAX
;
44 static char *arg_tpm2_signature
= NULL
;
45 static char *arg_node
= NULL
;
46 static int *arg_wipe_slots
= NULL
;
47 static size_t arg_n_wipe_slots
= 0;
48 static WipeScope arg_wipe_slots_scope
= WIPE_EXPLICIT
;
49 static unsigned arg_wipe_slots_mask
= 0; /* Bitmask of (1U << EnrollType), for wiping all slots of specific types */
50 static Fido2EnrollFlags arg_fido2_lock_with
= FIDO2ENROLL_PIN
| FIDO2ENROLL_UP
;
52 static int arg_fido2_cred_alg
= COSE_ES256
;
54 static int arg_fido2_cred_alg
= 0;
57 assert_cc(sizeof(arg_wipe_slots_mask
) * 8 >= _ENROLL_TYPE_MAX
);
59 STATIC_DESTRUCTOR_REGISTER(arg_unlock_keyfile
, freep
);
60 STATIC_DESTRUCTOR_REGISTER(arg_unlock_fido2_device
, freep
);
61 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri
, freep
);
62 STATIC_DESTRUCTOR_REGISTER(arg_fido2_device
, freep
);
63 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device
, freep
);
64 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key
, freep
);
65 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature
, freep
);
66 STATIC_DESTRUCTOR_REGISTER(arg_node
, freep
);
67 STATIC_DESTRUCTOR_REGISTER(arg_wipe_slots
, freep
);
69 static bool wipe_requested(void) {
70 return arg_n_wipe_slots
> 0 ||
71 arg_wipe_slots_scope
!= WIPE_EXPLICIT
||
72 arg_wipe_slots_mask
!= 0;
75 static const char* const enroll_type_table
[_ENROLL_TYPE_MAX
] = {
76 [ENROLL_PASSWORD
] = "password",
77 [ENROLL_RECOVERY
] = "recovery",
78 [ENROLL_PKCS11
] = "pkcs11",
79 [ENROLL_FIDO2
] = "fido2",
80 [ENROLL_TPM2
] = "tpm2",
83 DEFINE_STRING_TABLE_LOOKUP(enroll_type
, EnrollType
);
85 static const char *const luks2_token_type_table
[_ENROLL_TYPE_MAX
] = {
86 /* ENROLL_PASSWORD has no entry here, as slots of this type do not have a token in the LUKS2 header */
87 [ENROLL_RECOVERY
] = "systemd-recovery",
88 [ENROLL_PKCS11
] = "systemd-pkcs11",
89 [ENROLL_FIDO2
] = "systemd-fido2",
90 [ENROLL_TPM2
] = "systemd-tpm2",
93 DEFINE_STRING_TABLE_LOOKUP(luks2_token_type
, EnrollType
);
95 static int help(void) {
96 _cleanup_free_
char *link
= NULL
;
99 r
= terminal_urlify_man("systemd-cryptenroll", "1", &link
);
103 printf("%s [OPTIONS...] BLOCK-DEVICE\n"
104 "\n%sEnroll a security token or authentication credential to a LUKS volume.%s\n\n"
105 " -h --help Show this help\n"
106 " --version Show package version\n"
107 " --password Enroll a user-supplied password\n"
108 " --recovery-key Enroll a recovery key\n"
109 " --unlock-key-file=PATH\n"
110 " Use a file to unlock the volume\n"
111 " --unlock-fido2-device=PATH\n"
112 " Use a FIDO2 device to unlock the volume\n"
113 " --pkcs11-token-uri=URI\n"
114 " Specify PKCS#11 security token URI\n"
115 " --fido2-credential-algorithm=STRING\n"
116 " Specify COSE algorithm for FIDO2 credential\n"
117 " --fido2-device=PATH\n"
118 " Enroll a FIDO2-HMAC security token\n"
119 " --fido2-with-client-pin=BOOL\n"
120 " Whether to require entering a PIN to unlock the volume\n"
121 " --fido2-with-user-presence=BOOL\n"
122 " Whether to require user presence to unlock the volume\n"
123 " --fido2-with-user-verification=BOOL\n"
124 " Whether to require user verification to unlock the volume\n"
125 " --tpm2-device=PATH\n"
126 " Enroll a TPM2 device\n"
127 " --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
128 " Specify TPM2 PCRs to seal against\n"
129 " --tpm2-public-key=PATH\n"
130 " Enroll signed TPM2 PCR policy against PEM public key\n"
131 " --tpm2-public-key-pcrs=PCR1+PCR2+PCR3+…\n"
132 " Enroll signed TPM2 PCR policy for specified TPM2 PCRs\n"
133 " --tpm2-signature=PATH\n"
134 " Validate public key enrollment works with JSON signature\n"
136 " --tpm2-with-pin=BOOL\n"
137 " Whether to require entering a PIN to unlock the volume\n"
138 " --wipe-slot=SLOT1,SLOT2,…\n"
139 " Wipe specified slots\n"
140 "\nSee the %s for details.\n",
141 program_invocation_short_name
,
149 static int parse_argv(int argc
, char *argv
[]) {
156 ARG_UNLOCK_FIDO2_DEVICE
,
157 ARG_PKCS11_TOKEN_URI
,
162 ARG_TPM2_PUBLIC_KEY_PCRS
,
172 static const struct option options
[] = {
173 { "help", no_argument
, NULL
, 'h' },
174 { "version", no_argument
, NULL
, ARG_VERSION
},
175 { "password", no_argument
, NULL
, ARG_PASSWORD
},
176 { "recovery-key", no_argument
, NULL
, ARG_RECOVERY_KEY
},
177 { "unlock-key-file", required_argument
, NULL
, ARG_UNLOCK_KEYFILE
},
178 { "unlock-fido2-device", required_argument
, NULL
, ARG_UNLOCK_FIDO2_DEVICE
},
179 { "pkcs11-token-uri", required_argument
, NULL
, ARG_PKCS11_TOKEN_URI
},
180 { "fido2-credential-algorithm", required_argument
, NULL
, ARG_FIDO2_CRED_ALG
},
181 { "fido2-device", required_argument
, NULL
, ARG_FIDO2_DEVICE
},
182 { "fido2-with-client-pin", required_argument
, NULL
, ARG_FIDO2_WITH_PIN
},
183 { "fido2-with-user-presence", required_argument
, NULL
, ARG_FIDO2_WITH_UP
},
184 { "fido2-with-user-verification", required_argument
, NULL
, ARG_FIDO2_WITH_UV
},
185 { "tpm2-device", required_argument
, NULL
, ARG_TPM2_DEVICE
},
186 { "tpm2-pcrs", required_argument
, NULL
, ARG_TPM2_PCRS
},
187 { "tpm2-public-key", required_argument
, NULL
, ARG_TPM2_PUBLIC_KEY
},
188 { "tpm2-public-key-pcrs", required_argument
, NULL
, ARG_TPM2_PUBLIC_KEY_PCRS
},
189 { "tpm2-signature", required_argument
, NULL
, ARG_TPM2_SIGNATURE
},
190 { "tpm2-with-pin", required_argument
, NULL
, ARG_TPM2_PIN
},
191 { "wipe-slot", required_argument
, NULL
, ARG_WIPE_SLOT
},
200 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0) {
210 case ARG_FIDO2_WITH_PIN
:
211 r
= parse_boolean_argument("--fido2-with-client-pin=", optarg
, NULL
);
215 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_PIN
, r
);
218 case ARG_FIDO2_WITH_UP
:
219 r
= parse_boolean_argument("--fido2-with-user-presence=", optarg
, NULL
);
223 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UP
, r
);
226 case ARG_FIDO2_WITH_UV
:
227 r
= parse_boolean_argument("--fido2-with-user-verification=", optarg
, NULL
);
231 SET_FLAG(arg_fido2_lock_with
, FIDO2ENROLL_UV
, r
);
235 if (arg_enroll_type
>= 0)
236 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
237 "Multiple operations specified at once, refusing.");
239 arg_enroll_type
= ENROLL_PASSWORD
;
242 case ARG_RECOVERY_KEY
:
243 if (arg_enroll_type
>= 0)
244 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
245 "Multiple operations specified at once, refusing.");
247 arg_enroll_type
= ENROLL_RECOVERY
;
250 case ARG_UNLOCK_KEYFILE
:
251 if (arg_unlock_type
!= UNLOCK_PASSWORD
)
252 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
253 "Multiple unlock methods specified at once, refusing.");
255 r
= parse_path_argument(optarg
, /* suppress_root= */ true, &arg_unlock_keyfile
);
259 arg_unlock_type
= UNLOCK_KEYFILE
;
262 case ARG_UNLOCK_FIDO2_DEVICE
: {
263 _cleanup_free_
char *device
= NULL
;
265 if (arg_unlock_type
!= UNLOCK_PASSWORD
)
266 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
267 "Multiple unlock methods specified at once, refusing.");
269 assert(!arg_unlock_fido2_device
);
271 if (!streq(optarg
, "auto")) {
272 device
= strdup(optarg
);
277 arg_unlock_type
= UNLOCK_FIDO2
;
278 arg_unlock_fido2_device
= TAKE_PTR(device
);
282 case ARG_PKCS11_TOKEN_URI
: {
283 _cleanup_free_
char *uri
= NULL
;
285 if (streq(optarg
, "list"))
286 return pkcs11_list_tokens();
288 if (arg_enroll_type
>= 0 || arg_pkcs11_token_uri
)
289 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
290 "Multiple operations specified at once, refusing.");
292 if (streq(optarg
, "auto")) {
293 r
= pkcs11_find_token_auto(&uri
);
297 if (!pkcs11_uri_valid(optarg
))
298 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Not a valid PKCS#11 URI: %s", optarg
);
300 uri
= strdup(optarg
);
305 arg_enroll_type
= ENROLL_PKCS11
;
306 arg_pkcs11_token_uri
= TAKE_PTR(uri
);
310 case ARG_FIDO2_CRED_ALG
:
311 r
= parse_fido2_algorithm(optarg
, &arg_fido2_cred_alg
);
313 return log_error_errno(r
, "Failed to parse COSE algorithm: %s", optarg
);
316 case ARG_FIDO2_DEVICE
: {
317 _cleanup_free_
char *device
= NULL
;
319 if (streq(optarg
, "list"))
320 return fido2_list_devices();
322 if (arg_enroll_type
>= 0 || arg_fido2_device
)
323 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
324 "Multiple operations specified at once, refusing.");
326 if (!streq(optarg
, "auto")) {
327 device
= strdup(optarg
);
332 arg_enroll_type
= ENROLL_FIDO2
;
333 arg_fido2_device
= TAKE_PTR(device
);
337 case ARG_TPM2_DEVICE
: {
338 _cleanup_free_
char *device
= NULL
;
340 if (streq(optarg
, "list"))
341 return tpm2_list_devices();
343 if (arg_enroll_type
>= 0 || arg_tpm2_device
)
344 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
345 "Multiple operations specified at once, refusing.");
347 if (!streq(optarg
, "auto")) {
348 device
= strdup(optarg
);
353 arg_enroll_type
= ENROLL_TPM2
;
354 arg_tpm2_device
= TAKE_PTR(device
);
359 r
= tpm2_parse_pcr_argument_to_mask(optarg
, &arg_tpm2_pcr_mask
);
366 r
= parse_boolean_argument("--tpm2-with-pin=", optarg
, &arg_tpm2_pin
);
372 case ARG_TPM2_PUBLIC_KEY
:
373 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_tpm2_public_key
);
379 case ARG_TPM2_PUBLIC_KEY_PCRS
:
380 r
= tpm2_parse_pcr_argument_to_mask(optarg
, &arg_tpm2_public_key_pcr_mask
);
386 case ARG_TPM2_SIGNATURE
:
387 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_tpm2_signature
);
393 case ARG_WIPE_SLOT
: {
394 const char *p
= optarg
;
396 if (isempty(optarg
)) {
397 arg_wipe_slots_mask
= 0;
398 arg_wipe_slots_scope
= WIPE_EXPLICIT
;
403 _cleanup_free_
char *slot
= NULL
;
406 r
= extract_first_word(&p
, &slot
, ",", EXTRACT_DONT_COALESCE_SEPARATORS
);
410 return log_error_errno(r
, "Failed to parse slot list: %s", optarg
);
412 if (streq(slot
, "all"))
413 arg_wipe_slots_scope
= WIPE_ALL
;
414 else if (streq(slot
, "empty")) {
415 if (arg_wipe_slots_scope
!= WIPE_ALL
) /* if "all" was specified before, that wins */
416 arg_wipe_slots_scope
= WIPE_EMPTY_PASSPHRASE
;
417 } else if (streq(slot
, "password"))
418 arg_wipe_slots_mask
|= 1U << ENROLL_PASSWORD
;
419 else if (streq(slot
, "recovery"))
420 arg_wipe_slots_mask
|= 1U << ENROLL_RECOVERY
;
421 else if (streq(slot
, "pkcs11"))
422 arg_wipe_slots_mask
|= 1U << ENROLL_PKCS11
;
423 else if (streq(slot
, "fido2"))
424 arg_wipe_slots_mask
|= 1U << ENROLL_FIDO2
;
425 else if (streq(slot
, "tpm2"))
426 arg_wipe_slots_mask
|= 1U << ENROLL_TPM2
;
430 r
= safe_atou(slot
, &n
);
432 return log_error_errno(r
, "Failed to parse slot index: %s", slot
);
434 return log_error_errno(SYNTHETIC_ERRNO(ERANGE
), "Slot index out of range: %u", n
);
436 a
= reallocarray(arg_wipe_slots
, sizeof(int), arg_n_wipe_slots
+ 1);
441 arg_wipe_slots
[arg_n_wipe_slots
++] = (int) n
;
451 assert_not_reached();
455 if ((arg_enroll_type
== ENROLL_FIDO2
&& arg_unlock_type
== UNLOCK_FIDO2
)
456 && !(arg_fido2_device
&& arg_unlock_fido2_device
))
457 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
458 "When both enrolling and unlocking with FIDO2 tokens, automatic discovery is unsupported. "
459 "Please specify device paths for enrolling and unlocking respectively.");
461 if (arg_enroll_type
== ENROLL_FIDO2
&& !arg_fido2_device
) {
462 r
= fido2_find_device_auto(&arg_fido2_device
);
468 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
469 "No block device node specified, refusing.");
472 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
473 "Too many arguments, refusing.");
475 r
= parse_path_argument(argv
[optind
], false, &arg_node
);
479 if (arg_tpm2_pcr_mask
== UINT32_MAX
)
480 arg_tpm2_pcr_mask
= TPM2_PCR_MASK_DEFAULT
;
481 if (arg_tpm2_public_key_pcr_mask
== UINT32_MAX
)
482 arg_tpm2_public_key_pcr_mask
= UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE
;
487 static int check_for_homed(struct crypt_device
*cd
) {
492 /* Politely refuse operating on homed volumes. The enrolled tokens for the user record and the LUKS2
493 * volume should not get out of sync. */
495 for (int token
= 0; token
< crypt_token_max(CRYPT_LUKS2
); token
++) {
496 r
= cryptsetup_get_token_as_json(cd
, token
, "systemd-homed", NULL
);
497 if (IN_SET(r
, -ENOENT
, -EINVAL
, -EMEDIUMTYPE
))
500 return log_error_errno(r
, "Failed to read JSON token data off disk: %m");
502 return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN
),
503 "LUKS2 volume is managed by systemd-homed, please use homectl to enroll tokens.");
509 static int load_volume_key_keyfile(
510 struct crypt_device
*cd
,
514 _cleanup_(erase_and_freep
) char *password
= NULL
;
522 r
= read_full_file_full(
527 READ_FULL_FILE_SECURE
|READ_FULL_FILE_WARN_WORLD_READABLE
|READ_FULL_FILE_CONNECT_SOCKET
,
532 return log_error_errno(r
, "Reading keyfile %s failed: %m", arg_unlock_keyfile
);
534 r
= crypt_volume_key_get(
542 return log_error_errno(r
, "Unlocking via keyfile failed: %m");
547 static int prepare_luks(
548 struct crypt_device
**ret_cd
,
549 void **ret_volume_key
,
550 size_t *ret_volume_key_size
) {
552 _cleanup_(crypt_freep
) struct crypt_device
*cd
= NULL
;
553 _cleanup_(erase_and_freep
) void *vk
= NULL
;
558 assert(!ret_volume_key
== !ret_volume_key_size
);
560 r
= crypt_init(&cd
, arg_node
);
562 return log_error_errno(r
, "Failed to allocate libcryptsetup context: %m");
564 cryptsetup_enable_logging(cd
);
566 r
= crypt_load(cd
, CRYPT_LUKS2
, NULL
);
568 return log_error_errno(r
, "Failed to load LUKS2 superblock: %m");
570 r
= check_for_homed(cd
);
574 if (!ret_volume_key
) {
575 *ret_cd
= TAKE_PTR(cd
);
579 r
= crypt_get_volume_key_size(cd
);
581 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to determine LUKS volume key size");
588 switch (arg_unlock_type
) {
591 r
= load_volume_key_keyfile(cd
, vk
, &vks
);
595 r
= load_volume_key_fido2(cd
, arg_node
, arg_unlock_fido2_device
, vk
, &vks
);
598 case UNLOCK_PASSWORD
:
599 r
= load_volume_key_password(cd
, arg_node
, vk
, &vks
);
603 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unknown LUKS unlock method");
609 *ret_cd
= TAKE_PTR(cd
);
610 *ret_volume_key
= TAKE_PTR(vk
);
611 *ret_volume_key_size
= vks
;
616 static int run(int argc
, char *argv
[]) {
617 _cleanup_(crypt_freep
) struct crypt_device
*cd
= NULL
;
618 _cleanup_(erase_and_freep
) void *vk
= NULL
;
622 log_show_color(true);
623 log_parse_environment();
626 r
= parse_argv(argc
, argv
);
630 cryptsetup_enable_logging(NULL
);
632 if (arg_enroll_type
< 0)
633 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 */
635 r
= prepare_luks(&cd
, &vk
, &vks
);
639 switch (arg_enroll_type
) {
641 case ENROLL_PASSWORD
:
642 slot
= enroll_password(cd
, vk
, vks
);
645 case ENROLL_RECOVERY
:
646 slot
= enroll_recovery(cd
, vk
, vks
);
650 slot
= enroll_pkcs11(cd
, vk
, vks
, arg_pkcs11_token_uri
);
654 slot
= enroll_fido2(cd
, vk
, vks
, arg_fido2_device
, arg_fido2_lock_with
, arg_fido2_cred_alg
);
658 slot
= enroll_tpm2(cd
, vk
, vks
, arg_tpm2_device
, arg_tpm2_pcr_mask
, arg_tpm2_public_key
, arg_tpm2_public_key_pcr_mask
, arg_tpm2_signature
, arg_tpm2_pin
);
661 case _ENROLL_TYPE_INVALID
:
662 /* List enrolled slots if we are called without anything to enroll or wipe */
663 if (!wipe_requested())
664 return list_enrolled(cd
);
666 /* Only slot wiping selected */
667 return wipe_slots(cd
, arg_wipe_slots
, arg_n_wipe_slots
, arg_wipe_slots_scope
, arg_wipe_slots_mask
, -1);
670 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Operation not implemented yet.");
675 /* After we completed enrolling, remove user selected slots */
676 r
= wipe_slots(cd
, arg_wipe_slots
, arg_n_wipe_slots
, arg_wipe_slots_scope
, arg_wipe_slots_mask
, slot
);
683 DEFINE_MAIN_FUNCTION(run
);