]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptenroll/cryptenroll-tpm2.c
cryptenroll: Support rotating PIN on an existing TPM2 enrollment
[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
90 pin = strv_free_erase(pin);
91 r = ask_password_auto(
92 "Please enter TPM2 PIN:",
93 "drive-harddisk",
94 NULL,
95 "tpm2-pin",
96 "cryptenroll.tpm2-pin",
97 USEC_INFINITY,
98 0,
99 &pin);
100 if (r < 0)
101 return log_error_errno(r, "Failed to ask for user pin: %m");
102 assert(strv_length(pin) == 1);
103
104 r = ask_password_auto(
105 "Please enter TPM2 PIN (repeat):",
106 "drive-harddisk",
107 NULL,
108 "tpm2-pin",
109 "cryptenroll.tpm2-pin",
110 USEC_INFINITY,
111 0,
112 &pin2);
113 if (r < 0)
114 return log_error_errno(r, "Failed to ask for user pin: %m");
115 assert(strv_length(pin) == 1);
116
117 if (strv_equal(pin, pin2)) {
118 pin_str = strdup(*pin);
119 if (!pin_str)
120 return log_oom();
121 flags |= TPM2_FLAGS_USE_PIN;
122 break;
123 }
124
125 log_error("PINs didn't match, please try again!");
126 }
127 }
128
129 *ret_flags = flags;
130 *ret_pin_str = TAKE_PTR(pin_str);
131
132 return 0;
133}
134
631cf7f0
GAP
135int load_volume_key_tpm2(
136 struct crypt_device *cd,
137 const char *cd_node,
138 const char *device,
139 void *ret_vk,
140 size_t *ret_vks) {
141
142 _cleanup_(iovec_done_erase) struct iovec decrypted_key = {};
143 _cleanup_(erase_and_freep) char *passphrase = NULL;
144 ssize_t passphrase_size;
145 int r;
146
147 assert_se(cd);
148 assert_se(cd_node);
149 assert_se(ret_vk);
150 assert_se(ret_vks);
151
152 bool found_some = false;
153 int token = 0; /* first token to look at */
154
155 for (;;) {
156 _cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
157 _cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {};
158 uint32_t hash_pcr_mask, pubkey_pcr_mask;
159 uint16_t pcr_bank, primary_alg;
160 TPM2Flags tpm2_flags;
161 int keyslot;
162
163 r = find_tpm2_auto_data(
164 cd,
165 UINT32_MAX,
166 token,
167 &hash_pcr_mask,
168 &pcr_bank,
169 &pubkey,
170 &pubkey_pcr_mask,
171 &primary_alg,
172 &blob,
173 &policy_hash,
174 &salt,
175 &srk,
176 &pcrlock_nv,
177 &tpm2_flags,
178 &keyslot,
179 &token);
180 if (r == -ENXIO)
181 return log_full_errno(LOG_NOTICE,
182 SYNTHETIC_ERRNO(EAGAIN),
183 found_some
184 ? "No TPM2 metadata matching the current system state found in LUKS2 header."
185 : "No TPM2 metadata enrolled in LUKS2 header.");
186 if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
187 /* TPM2 support not compiled in? */
188 return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 support not available.");
189 if (r < 0)
190 return r;
191
192 found_some = true;
193
194 r = acquire_tpm2_key(
195 cd_node,
196 device,
197 hash_pcr_mask,
198 pcr_bank,
199 &pubkey,
200 pubkey_pcr_mask,
201 /* signature_path= */ NULL,
202 /* pcrlock_path= */ NULL,
203 primary_alg,
204 /* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */
205 &blob,
206 &policy_hash,
207 &salt,
208 &srk,
209 &pcrlock_nv,
210 tpm2_flags,
211 /* until= */ 0,
212 /* headless= */ false,
213 /* ask_password_flags */ false,
214 &decrypted_key);
215 if (IN_SET(r, -EACCES, -ENOLCK))
216 return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 PIN unlock failed");
217 if (r != -EPERM)
218 break;
219
220 token++; /* try a different token next time */
221 }
222
223 if (r < 0)
224 return log_error_errno(r, "Unlocking via TPM2 device failed: %m");
225
226 passphrase_size = base64mem(decrypted_key.iov_base, decrypted_key.iov_len, &passphrase);
227 if (passphrase_size < 0)
228 return log_oom();
229
230 r = crypt_volume_key_get(
231 cd,
232 CRYPT_ANY_SLOT,
233 ret_vk,
234 ret_vks,
235 passphrase,
236 passphrase_size);
237 if (r < 0)
238 return log_error_errno(r, "Unlocking via TPM2 device failed: %m");
239
240 return r;
241}
242
5e521624
LP
243int enroll_tpm2(struct crypt_device *cd,
244 const void *volume_key,
245 size_t volume_key_size,
246 const char *device,
382bfd90 247 uint32_t seal_key_handle,
c3a2a681 248 const char *device_key,
9e437994
DS
249 Tpm2PCRValue *hash_pcr_values,
250 size_t n_hash_pcr_values,
f0f4fcae
LP
251 const char *pubkey_path,
252 uint32_t pubkey_pcr_mask,
253 const char *signature_path,
404aea78 254 bool use_pin,
47ec2c8a
GAP
255 const char *pcrlock_path,
256 int *ret_slot_to_wipe) {
5e521624 257
f0f4fcae 258 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *signature_json = NULL;
5e521624 259 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
8d042bc4
LP
260 _cleanup_(iovec_done) struct iovec srk = {}, blob = {}, pubkey = {};
261 _cleanup_(iovec_done_erase) struct iovec secret = {};
5e521624 262 const char *node;
6c7a1681 263 _cleanup_(erase_and_freep) char *pin_str = NULL;
5e476b85 264 ssize_t base64_encoded_size;
47ec2c8a 265 int r, keyslot, slot_to_wipe = -1;
6c7a1681 266 TPM2Flags flags = 0;
aae6eb96
WR
267 uint8_t binary_salt[SHA256_DIGEST_SIZE] = {};
268 /*
269 * erase the salt, we'd rather attempt to not have this in a coredump
270 * as an attacker would have all the parameters but pin used to create
271 * the session key. This problem goes away when we move to a trusted
272 * primary key, aka the SRK.
273 */
274 CLEANUP_ERASE(binary_salt);
5e521624
LP
275
276 assert(cd);
277 assert(volume_key);
278 assert(volume_key_size > 0);
cc1a78d5 279 assert(tpm2_pcr_values_valid(hash_pcr_values, n_hash_pcr_values));
f0f4fcae 280 assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask));
47ec2c8a 281 assert(ret_slot_to_wipe);
5e521624
LP
282
283 assert_se(node = crypt_get_device_name(cd));
284
6c7a1681
GG
285 if (use_pin) {
286 r = get_pin(&pin_str, &flags);
287 if (r < 0)
288 return r;
aae6eb96
WR
289
290 r = crypto_random_bytes(binary_salt, sizeof(binary_salt));
291 if (r < 0)
292 return log_error_errno(r, "Failed to acquire random salt: %m");
293
294 uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
295 CLEANUP_ERASE(salted_pin);
296 r = tpm2_util_pbkdf2_hmac_sha256(pin_str, strlen(pin_str), binary_salt, sizeof(binary_salt), salted_pin);
297 if (r < 0)
298 return log_error_errno(r, "Failed to perform PBKDF2: %m");
299
300 pin_str = erase_and_free(pin_str);
301 /* re-stringify pin_str */
302 base64_encoded_size = base64mem(salted_pin, sizeof(salted_pin), &pin_str);
303 if (base64_encoded_size < 0)
304 return log_error_errno(base64_encoded_size, "Failed to base64 encode salted pin: %m");
6c7a1681
GG
305 }
306
a4e9f3d3 307 TPM2B_PUBLIC public = {};
8d042bc4 308 r = tpm2_load_pcr_public_key(pubkey_path, &pubkey.iov_base, &pubkey.iov_len);
f0f4fcae
LP
309 if (r < 0) {
310 if (pubkey_path || signature_path || r != -ENOENT)
cb7aabf1 311 return log_error_errno(r, "Failed to read TPM PCR public key: %m");
f0f4fcae
LP
312
313 log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
314 pubkey_pcr_mask = 0;
a4e9f3d3 315 } else {
8d042bc4 316 r = tpm2_tpm2b_public_from_pem(pubkey.iov_base, pubkey.iov_len, &public);
645063d1 317 if (r < 0)
a4e9f3d3
LP
318 return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
319
320 if (signature_path) {
321 /* Also try to load the signature JSON object, to verify that our enrollment will work.
322 * This is optional however, skip it if it's not explicitly provided. */
323
324 r = tpm2_load_pcr_signature(signature_path, &signature_json);
325 if (r < 0)
326 return log_debug_errno(r, "Failed to read TPM PCR signature: %m");
327 }
f0f4fcae
LP
328 }
329
c3a2a681
DS
330 bool any_pcr_value_specified = tpm2_pcr_values_has_any_values(hash_pcr_values, n_hash_pcr_values);
331
404aea78
LP
332 _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
333 if (pcrlock_path) {
334 r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy);
335 if (r < 0)
336 return r;
337
c3a2a681 338 any_pcr_value_specified = true;
404aea78
LP
339 flags |= TPM2_FLAGS_USE_PCRLOCK;
340 }
341
9e437994 342 _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
c3a2a681
DS
343 TPM2B_PUBLIC device_key_public = {};
344 if (device_key) {
a8d8d34b 345 r = tpm2_load_public_key_file(device_key, &device_key_public);
c3a2a681 346 if (r < 0)
a8d8d34b 347 return r;
c3a2a681
DS
348
349 if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values))
350 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
351 "Must provide all PCR values when using TPM2 device key.");
352 } else {
353 r = tpm2_context_new(device, &tpm2_context);
354 if (r < 0)
355 return log_error_errno(r, "Failed to create TPM2 context: %m");
356
357 if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values)) {
358 r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values);
359 if (r < 0)
360 return log_error_errno(r, "Could not read pcr values: %m");
361 }
362 }
9e437994
DS
363
364 uint16_t hash_pcr_bank = 0;
365 uint32_t hash_pcr_mask = 0;
366 if (n_hash_pcr_values > 0) {
367 size_t hash_count;
368 r = tpm2_pcr_values_hash_count(hash_pcr_values, n_hash_pcr_values, &hash_count);
369 if (r < 0)
370 return log_error_errno(r, "Could not get hash count: %m");
371
372 if (hash_count > 1)
373 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected.");
374
375 hash_pcr_bank = hash_pcr_values[0].hash;
376 r = tpm2_pcr_values_to_mask(hash_pcr_values, n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask);
377 if (r < 0)
378 return log_error_errno(r, "Could not get hash mask: %m");
379 }
380
9e437994
DS
381 TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
382 r = tpm2_calculate_sealing_policy(
383 hash_pcr_values,
384 n_hash_pcr_values,
8d042bc4 385 iovec_is_set(&pubkey) ? &public : NULL,
9e437994 386 use_pin,
404aea78 387 pcrlock_path ? &pcrlock_policy : NULL,
9e437994
DS
388 &policy);
389 if (r < 0)
390 return r;
391
c3a2a681
DS
392 if (device_key)
393 r = tpm2_calculate_seal(
394 seal_key_handle,
395 &device_key_public,
396 /* attributes= */ NULL,
8d042bc4 397 /* secret= */ NULL,
c3a2a681
DS
398 &policy,
399 pin_str,
8d042bc4
LP
400 &secret,
401 &blob,
402 &srk);
c3a2a681
DS
403 else
404 r = tpm2_seal(tpm2_context,
405 seal_key_handle,
406 &policy,
407 pin_str,
8d042bc4
LP
408 &secret,
409 &blob,
c3a2a681 410 /* ret_primary_alg= */ NULL,
8d042bc4 411 &srk);
5e521624 412 if (r < 0)
f9a0ee75 413 return log_error_errno(r, "Failed to seal to TPM2: %m");
5e521624
LP
414
415 /* Let's see if we already have this specific PCR policy hash enrolled, if so, exit early. */
9e437994 416 r = search_policy_hash(cd, policy.buffer, policy.size);
5e521624
LP
417 if (r == -ENOENT)
418 log_debug_errno(r, "PCR policy hash not yet enrolled, enrolling now.");
419 else if (r < 0)
420 return r;
47ec2c8a
GAP
421 else if (use_pin) {
422 log_debug("This PCR set is already enrolled, re-enrolling anyway to update PIN.");
423 slot_to_wipe = r;
424 } else {
5e521624 425 log_info("This PCR set is already enrolled, executing no operation.");
47ec2c8a 426 *ret_slot_to_wipe = slot_to_wipe;
5e521624
LP
427 return r; /* return existing keyslot, so that wiping won't kill it */
428 }
429
c3a2a681 430 /* If possible, verify the sealed data object. */
8d042bc4
LP
431 if ((!iovec_is_set(&pubkey) || signature_json) && !any_pcr_value_specified && !device_key) {
432 _cleanup_(iovec_done_erase) struct iovec secret2 = {};
f0f4fcae
LP
433
434 log_debug("Unsealing for verification...");
db7fdf15 435 r = tpm2_unseal(tpm2_context,
f0f4fcae 436 hash_pcr_mask,
9e437994 437 hash_pcr_bank,
8d042bc4 438 &pubkey,
f0f4fcae
LP
439 pubkey_pcr_mask,
440 signature_json,
441 pin_str,
404aea78 442 pcrlock_path ? &pcrlock_policy : NULL,
9e437994 443 /* primary_alg= */ 0,
8d042bc4
LP
444 &blob,
445 &IOVEC_MAKE(policy.buffer, policy.size),
446 &srk,
447 &secret2);
f0f4fcae 448 if (r < 0)
f9a0ee75 449 return log_error_errno(r, "Failed to unseal secret using TPM2: %m");
5e521624 450
8d042bc4 451 if (iovec_memcmp(&secret, &secret2) != 0)
f0f4fcae
LP
452 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 seal/unseal verification failed.");
453 }
5e521624
LP
454
455 /* 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 456 base64_encoded_size = base64mem(secret.iov_base, secret.iov_len, &base64_encoded);
5e476b85
LP
457 if (base64_encoded_size < 0)
458 return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
5e521624
LP
459
460 r = cryptsetup_set_minimal_pbkdf(cd);
461 if (r < 0)
462 return log_error_errno(r, "Failed to set minimal PBKDF: %m");
463
464 keyslot = crypt_keyslot_add_by_volume_key(
465 cd,
466 CRYPT_ANY_SLOT,
467 volume_key,
468 volume_key_size,
469 base64_encoded,
5e476b85 470 base64_encoded_size);
5e521624
LP
471 if (keyslot < 0)
472 return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
473
f0f4fcae
LP
474 r = tpm2_make_luks2_json(
475 keyslot,
476 hash_pcr_mask,
9e437994 477 hash_pcr_bank,
8d042bc4 478 &pubkey,
f0f4fcae 479 pubkey_pcr_mask,
9e437994 480 /* primary_alg= */ 0,
8d042bc4
LP
481 &blob,
482 &IOVEC_MAKE(policy.buffer, policy.size),
483 use_pin ? &IOVEC_MAKE(binary_salt, sizeof(binary_salt)) : NULL,
484 &srk,
d37c312b 485 pcrlock_path ? &pcrlock_policy.nv_handle : NULL,
f0f4fcae
LP
486 flags,
487 &v);
5e521624
LP
488 if (r < 0)
489 return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
490
491 r = cryptsetup_add_token_json(cd, v);
492 if (r < 0)
493 return log_error_errno(r, "Failed to add TPM2 JSON token to LUKS2 header: %m");
494
495 log_info("New TPM2 token enrolled as key slot %i.", keyslot);
47ec2c8a
GAP
496
497 *ret_slot_to_wipe = slot_to_wipe;
5e521624
LP
498 return keyslot;
499}