]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptenroll/cryptenroll-tpm2.c
cryptenroll: disable loading public key if --tpm2-public-key= is empty
[thirdparty/systemd.git] / src / cryptenroll / cryptenroll-tpm2.c
CommitLineData
5e521624
LP
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include "alloc-util.h"
6c7a1681 4#include "ask-password-api.h"
5e521624 5#include "cryptenroll-tpm2.h"
631cf7f0 6#include "cryptsetup-tpm2.h"
6c7a1681 7#include "env-util.h"
631cf7f0 8#include "errno-util.h"
f0f4fcae 9#include "fileio.h"
5e521624
LP
10#include "hexdecoct.h"
11#include "json.h"
631cf7f0 12#include "log.h"
5e521624 13#include "memory-util.h"
aae6eb96
WR
14#include "random-util.h"
15#include "sha256.h"
5e521624
LP
16#include "tpm2-util.h"
17
18static int search_policy_hash(
19 struct crypt_device *cd,
20 const void *hash,
21 size_t hash_size) {
22
23 int r;
24
25 assert(cd);
26 assert(hash || hash_size == 0);
27
28 if (hash_size == 0)
29 return 0;
30
b3a9d980 31 for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
5e521624
LP
32 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
33 _cleanup_free_ void *thash = NULL;
34 size_t thash_size = 0;
35 int keyslot;
36 JsonVariant *w;
37
38 r = cryptsetup_get_token_as_json(cd, token, "systemd-tpm2", &v);
39 if (IN_SET(r, -ENOENT, -EINVAL, -EMEDIUMTYPE))
40 continue;
41 if (r < 0)
42 return log_error_errno(r, "Failed to read JSON token data off disk: %m");
43
44 keyslot = cryptsetup_get_keyslot_from_token(v);
1641c2b1
LP
45 if (keyslot < 0) {
46 /* Handle parsing errors of the keyslots field gracefully, since it's not 'owned' by
47 * us, but by the LUKS2 spec */
48 log_warning_errno(keyslot, "Failed to determine keyslot of JSON token %i, skipping: %m", token);
49 continue;
50 }
5e521624
LP
51
52 w = json_variant_by_key(v, "tpm2-policy-hash");
53 if (!w || !json_variant_is_string(w))
54 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
55 "TPM2 token data lacks 'tpm2-policy-hash' field.");
56
bdd2036e 57 r = unhexmem(json_variant_string(w), &thash, &thash_size);
5e521624
LP
58 if (r < 0)
59 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
60 "Invalid base64 data in 'tpm2-policy-hash' field.");
61
62 if (memcmp_nn(hash, hash_size, thash, thash_size) == 0)
63 return keyslot; /* Found entry with same hash. */
64 }
65
66 return -ENOENT; /* Not found */
67}
68
6c7a1681 69static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) {
a3b46c6b 70 _cleanup_(erase_and_freep) char *pin_str = NULL;
6c7a1681 71 TPM2Flags flags = 0;
a3b46c6b 72 int r;
6c7a1681
GG
73
74 assert(ret_pin_str);
75 assert(ret_flags);
76
77 r = getenv_steal_erase("NEWPIN", &pin_str);
78 if (r < 0)
79 return log_error_errno(r, "Failed to acquire PIN from environment: %m");
80 if (r > 0)
81 flags |= TPM2_FLAGS_USE_PIN;
82 else {
83 for (size_t i = 5;; i--) {
84 _cleanup_strv_free_erase_ char **pin = NULL, **pin2 = NULL;
85
86 if (i <= 0)
87 return log_error_errno(
88 SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up.");
89
d08fd4c3
LP
90 AskPasswordRequest req = {
91 .message = "Please enter TPM2 PIN:",
92 .icon = "drive-harddisk",
93 .keyring = "tpm2-pin",
7252be60 94 .credential = "cryptenroll.new-tpm2-pin",
d08fd4c3
LP
95 };
96
6c7a1681
GG
97 pin = strv_free_erase(pin);
98 r = ask_password_auto(
d08fd4c3
LP
99 &req,
100 /* until= */ USEC_INFINITY,
101 /* flags= */ 0,
6c7a1681
GG
102 &pin);
103 if (r < 0)
104 return log_error_errno(r, "Failed to ask for user pin: %m");
105 assert(strv_length(pin) == 1);
106
d08fd4c3
LP
107 req.message = "Please enter TPM2 PIN (repeat):";
108
6c7a1681 109 r = ask_password_auto(
d08fd4c3 110 &req,
6c7a1681 111 USEC_INFINITY,
d08fd4c3 112 /* flags= */ 0,
6c7a1681
GG
113 &pin2);
114 if (r < 0)
115 return log_error_errno(r, "Failed to ask for user pin: %m");
116 assert(strv_length(pin) == 1);
117
118 if (strv_equal(pin, pin2)) {
119 pin_str = strdup(*pin);
120 if (!pin_str)
121 return log_oom();
122 flags |= TPM2_FLAGS_USE_PIN;
123 break;
124 }
125
126 log_error("PINs didn't match, please try again!");
127 }
128 }
129
130 *ret_flags = flags;
131 *ret_pin_str = TAKE_PTR(pin_str);
132
133 return 0;
134}
135
631cf7f0
GAP
136int load_volume_key_tpm2(
137 struct crypt_device *cd,
138 const char *cd_node,
139 const char *device,
140 void *ret_vk,
141 size_t *ret_vks) {
142
143 _cleanup_(iovec_done_erase) struct iovec decrypted_key = {};
144 _cleanup_(erase_and_freep) char *passphrase = NULL;
145 ssize_t passphrase_size;
146 int r;
147
148 assert_se(cd);
149 assert_se(cd_node);
150 assert_se(ret_vk);
151 assert_se(ret_vks);
152
153 bool found_some = false;
154 int token = 0; /* first token to look at */
155
156 for (;;) {
157 _cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
158 _cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {};
159 uint32_t hash_pcr_mask, pubkey_pcr_mask;
160 uint16_t pcr_bank, primary_alg;
161 TPM2Flags tpm2_flags;
162 int keyslot;
163
164 r = find_tpm2_auto_data(
165 cd,
166 UINT32_MAX,
167 token,
168 &hash_pcr_mask,
169 &pcr_bank,
170 &pubkey,
171 &pubkey_pcr_mask,
172 &primary_alg,
173 &blob,
174 &policy_hash,
175 &salt,
176 &srk,
177 &pcrlock_nv,
178 &tpm2_flags,
179 &keyslot,
180 &token);
181 if (r == -ENXIO)
182 return log_full_errno(LOG_NOTICE,
183 SYNTHETIC_ERRNO(EAGAIN),
184 found_some
185 ? "No TPM2 metadata matching the current system state found in LUKS2 header."
186 : "No TPM2 metadata enrolled in LUKS2 header.");
187 if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
188 /* TPM2 support not compiled in? */
189 return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 support not available.");
190 if (r < 0)
191 return r;
192
193 found_some = true;
194
195 r = acquire_tpm2_key(
196 cd_node,
197 device,
198 hash_pcr_mask,
199 pcr_bank,
200 &pubkey,
201 pubkey_pcr_mask,
202 /* signature_path= */ NULL,
203 /* pcrlock_path= */ NULL,
204 primary_alg,
205 /* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */
206 &blob,
207 &policy_hash,
208 &salt,
209 &srk,
210 &pcrlock_nv,
211 tpm2_flags,
212 /* until= */ 0,
b3a63584
LP
213 "cryptenroll.tpm2-pin",
214 /* askpw_flags= */ 0,
631cf7f0
GAP
215 &decrypted_key);
216 if (IN_SET(r, -EACCES, -ENOLCK))
217 return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 PIN unlock failed");
218 if (r != -EPERM)
219 break;
220
221 token++; /* try a different token next time */
222 }
223
224 if (r < 0)
225 return log_error_errno(r, "Unlocking via TPM2 device failed: %m");
226
227 passphrase_size = base64mem(decrypted_key.iov_base, decrypted_key.iov_len, &passphrase);
228 if (passphrase_size < 0)
229 return log_oom();
230
231 r = crypt_volume_key_get(
232 cd,
233 CRYPT_ANY_SLOT,
234 ret_vk,
235 ret_vks,
236 passphrase,
237 passphrase_size);
238 if (r < 0)
239 return log_error_errno(r, "Unlocking via TPM2 device failed: %m");
240
241 return r;
242}
243
5e521624
LP
244int enroll_tpm2(struct crypt_device *cd,
245 const void *volume_key,
246 size_t volume_key_size,
247 const char *device,
382bfd90 248 uint32_t seal_key_handle,
c3a2a681 249 const char *device_key,
9e437994
DS
250 Tpm2PCRValue *hash_pcr_values,
251 size_t n_hash_pcr_values,
f0f4fcae 252 const char *pubkey_path,
03e3b267 253 bool load_pubkey,
f0f4fcae
LP
254 uint32_t pubkey_pcr_mask,
255 const char *signature_path,
404aea78 256 bool use_pin,
47ec2c8a
GAP
257 const char *pcrlock_path,
258 int *ret_slot_to_wipe) {
5e521624 259
f0f4fcae 260 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *signature_json = NULL;
5e521624 261 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
8d042bc4
LP
262 _cleanup_(iovec_done) struct iovec srk = {}, blob = {}, pubkey = {};
263 _cleanup_(iovec_done_erase) struct iovec secret = {};
5e521624 264 const char *node;
6c7a1681 265 _cleanup_(erase_and_freep) char *pin_str = NULL;
5e476b85 266 ssize_t base64_encoded_size;
47ec2c8a 267 int r, keyslot, slot_to_wipe = -1;
6c7a1681 268 TPM2Flags flags = 0;
aae6eb96
WR
269 uint8_t binary_salt[SHA256_DIGEST_SIZE] = {};
270 /*
271 * erase the salt, we'd rather attempt to not have this in a coredump
272 * as an attacker would have all the parameters but pin used to create
273 * the session key. This problem goes away when we move to a trusted
274 * primary key, aka the SRK.
275 */
276 CLEANUP_ERASE(binary_salt);
5e521624
LP
277
278 assert(cd);
279 assert(volume_key);
280 assert(volume_key_size > 0);
cc1a78d5 281 assert(tpm2_pcr_values_valid(hash_pcr_values, n_hash_pcr_values));
f0f4fcae 282 assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask));
47ec2c8a 283 assert(ret_slot_to_wipe);
5e521624
LP
284
285 assert_se(node = crypt_get_device_name(cd));
286
6c7a1681
GG
287 if (use_pin) {
288 r = get_pin(&pin_str, &flags);
289 if (r < 0)
290 return r;
aae6eb96
WR
291
292 r = crypto_random_bytes(binary_salt, sizeof(binary_salt));
293 if (r < 0)
294 return log_error_errno(r, "Failed to acquire random salt: %m");
295
296 uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
297 CLEANUP_ERASE(salted_pin);
298 r = tpm2_util_pbkdf2_hmac_sha256(pin_str, strlen(pin_str), binary_salt, sizeof(binary_salt), salted_pin);
299 if (r < 0)
300 return log_error_errno(r, "Failed to perform PBKDF2: %m");
301
302 pin_str = erase_and_free(pin_str);
303 /* re-stringify pin_str */
304 base64_encoded_size = base64mem(salted_pin, sizeof(salted_pin), &pin_str);
305 if (base64_encoded_size < 0)
306 return log_error_errno(base64_encoded_size, "Failed to base64 encode salted pin: %m");
6c7a1681
GG
307 }
308
a4e9f3d3 309 TPM2B_PUBLIC public = {};
03e3b267
SL
310 if (load_pubkey) {
311 r = tpm2_load_pcr_public_key(pubkey_path, &pubkey.iov_base, &pubkey.iov_len);
312 if (r < 0) {
313 if (pubkey_path || signature_path || r != -ENOENT)
314 return log_error_errno(r, "Failed to read TPM PCR public key: %m");
315
316 log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
317 pubkey_pcr_mask = 0;
318 } else {
319 r = tpm2_tpm2b_public_from_pem(pubkey.iov_base, pubkey.iov_len, &public);
320 if (r < 0)
321 return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
a4e9f3d3 322
03e3b267
SL
323 if (signature_path) {
324 /* Also try to load the signature JSON object, to verify that our enrollment will work.
325 * This is optional however, skip it if it's not explicitly provided. */
a4e9f3d3 326
03e3b267
SL
327 r = tpm2_load_pcr_signature(signature_path, &signature_json);
328 if (r < 0)
329 return log_debug_errno(r, "Failed to read TPM PCR signature: %m");
330 }
a4e9f3d3 331 }
f0f4fcae
LP
332 }
333
c3a2a681
DS
334 bool any_pcr_value_specified = tpm2_pcr_values_has_any_values(hash_pcr_values, n_hash_pcr_values);
335
404aea78
LP
336 _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
337 if (pcrlock_path) {
338 r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy);
339 if (r < 0)
340 return r;
341
c3a2a681 342 any_pcr_value_specified = true;
404aea78
LP
343 flags |= TPM2_FLAGS_USE_PCRLOCK;
344 }
345
9e437994 346 _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
c3a2a681
DS
347 TPM2B_PUBLIC device_key_public = {};
348 if (device_key) {
a8d8d34b 349 r = tpm2_load_public_key_file(device_key, &device_key_public);
c3a2a681 350 if (r < 0)
a8d8d34b 351 return r;
c3a2a681
DS
352
353 if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values))
354 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
355 "Must provide all PCR values when using TPM2 device key.");
356 } else {
21a3bc6b 357 r = tpm2_context_new_or_warn(device, &tpm2_context);
c3a2a681 358 if (r < 0)
21a3bc6b 359 return r;
c3a2a681
DS
360
361 if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values)) {
362 r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values);
363 if (r < 0)
364 return log_error_errno(r, "Could not read pcr values: %m");
365 }
366 }
9e437994
DS
367
368 uint16_t hash_pcr_bank = 0;
369 uint32_t hash_pcr_mask = 0;
370 if (n_hash_pcr_values > 0) {
371 size_t hash_count;
372 r = tpm2_pcr_values_hash_count(hash_pcr_values, n_hash_pcr_values, &hash_count);
373 if (r < 0)
374 return log_error_errno(r, "Could not get hash count: %m");
375
376 if (hash_count > 1)
377 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected.");
378
379 hash_pcr_bank = hash_pcr_values[0].hash;
380 r = tpm2_pcr_values_to_mask(hash_pcr_values, n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask);
381 if (r < 0)
382 return log_error_errno(r, "Could not get hash mask: %m");
383 }
384
9e437994
DS
385 TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
386 r = tpm2_calculate_sealing_policy(
387 hash_pcr_values,
388 n_hash_pcr_values,
8d042bc4 389 iovec_is_set(&pubkey) ? &public : NULL,
9e437994 390 use_pin,
404aea78 391 pcrlock_path ? &pcrlock_policy : NULL,
9e437994
DS
392 &policy);
393 if (r < 0)
394 return r;
395
c3a2a681
DS
396 if (device_key)
397 r = tpm2_calculate_seal(
398 seal_key_handle,
399 &device_key_public,
400 /* attributes= */ NULL,
8d042bc4 401 /* secret= */ NULL,
c3a2a681
DS
402 &policy,
403 pin_str,
8d042bc4
LP
404 &secret,
405 &blob,
406 &srk);
c3a2a681
DS
407 else
408 r = tpm2_seal(tpm2_context,
409 seal_key_handle,
410 &policy,
411 pin_str,
8d042bc4
LP
412 &secret,
413 &blob,
c3a2a681 414 /* ret_primary_alg= */ NULL,
8d042bc4 415 &srk);
5e521624 416 if (r < 0)
f9a0ee75 417 return log_error_errno(r, "Failed to seal to TPM2: %m");
5e521624
LP
418
419 /* Let's see if we already have this specific PCR policy hash enrolled, if so, exit early. */
9e437994 420 r = search_policy_hash(cd, policy.buffer, policy.size);
5e521624
LP
421 if (r == -ENOENT)
422 log_debug_errno(r, "PCR policy hash not yet enrolled, enrolling now.");
423 else if (r < 0)
424 return r;
47ec2c8a
GAP
425 else if (use_pin) {
426 log_debug("This PCR set is already enrolled, re-enrolling anyway to update PIN.");
427 slot_to_wipe = r;
428 } else {
5e521624 429 log_info("This PCR set is already enrolled, executing no operation.");
47ec2c8a 430 *ret_slot_to_wipe = slot_to_wipe;
5e521624
LP
431 return r; /* return existing keyslot, so that wiping won't kill it */
432 }
433
c3a2a681 434 /* If possible, verify the sealed data object. */
8d042bc4
LP
435 if ((!iovec_is_set(&pubkey) || signature_json) && !any_pcr_value_specified && !device_key) {
436 _cleanup_(iovec_done_erase) struct iovec secret2 = {};
f0f4fcae
LP
437
438 log_debug("Unsealing for verification...");
db7fdf15 439 r = tpm2_unseal(tpm2_context,
f0f4fcae 440 hash_pcr_mask,
9e437994 441 hash_pcr_bank,
8d042bc4 442 &pubkey,
f0f4fcae
LP
443 pubkey_pcr_mask,
444 signature_json,
445 pin_str,
404aea78 446 pcrlock_path ? &pcrlock_policy : NULL,
9e437994 447 /* primary_alg= */ 0,
8d042bc4
LP
448 &blob,
449 &IOVEC_MAKE(policy.buffer, policy.size),
450 &srk,
451 &secret2);
f0f4fcae 452 if (r < 0)
f9a0ee75 453 return log_error_errno(r, "Failed to unseal secret using TPM2: %m");
5e521624 454
8d042bc4 455 if (iovec_memcmp(&secret, &secret2) != 0)
f0f4fcae
LP
456 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 seal/unseal verification failed.");
457 }
5e521624
LP
458
459 /* let's base64 encode the key to use, for compat with homed (and it's easier to every type it in by keyboard, if that might end up being necessary. */
8d042bc4 460 base64_encoded_size = base64mem(secret.iov_base, secret.iov_len, &base64_encoded);
5e476b85
LP
461 if (base64_encoded_size < 0)
462 return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
5e521624
LP
463
464 r = cryptsetup_set_minimal_pbkdf(cd);
465 if (r < 0)
466 return log_error_errno(r, "Failed to set minimal PBKDF: %m");
467
468 keyslot = crypt_keyslot_add_by_volume_key(
469 cd,
470 CRYPT_ANY_SLOT,
471 volume_key,
472 volume_key_size,
473 base64_encoded,
5e476b85 474 base64_encoded_size);
5e521624
LP
475 if (keyslot < 0)
476 return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
477
f0f4fcae
LP
478 r = tpm2_make_luks2_json(
479 keyslot,
480 hash_pcr_mask,
9e437994 481 hash_pcr_bank,
8d042bc4 482 &pubkey,
f0f4fcae 483 pubkey_pcr_mask,
9e437994 484 /* primary_alg= */ 0,
8d042bc4
LP
485 &blob,
486 &IOVEC_MAKE(policy.buffer, policy.size),
487 use_pin ? &IOVEC_MAKE(binary_salt, sizeof(binary_salt)) : NULL,
488 &srk,
d37c312b 489 pcrlock_path ? &pcrlock_policy.nv_handle : NULL,
f0f4fcae
LP
490 flags,
491 &v);
5e521624
LP
492 if (r < 0)
493 return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
494
495 r = cryptsetup_add_token_json(cd, v);
496 if (r < 0)
497 return log_error_errno(r, "Failed to add TPM2 JSON token to LUKS2 header: %m");
498
499 log_info("New TPM2 token enrolled as key slot %i.", keyslot);
47ec2c8a
GAP
500
501 *ret_slot_to_wipe = slot_to_wipe;
5e521624
LP
502 return keyslot;
503}