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