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