]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptenroll/cryptenroll-tpm2.c
hexdecoct: make unbase64mem and unhexmem always use SIZE_MAX
[thirdparty/systemd.git] / src / cryptenroll / cryptenroll-tpm2.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "ask-password-api.h"
5 #include "cryptenroll-tpm2.h"
6 #include "env-util.h"
7 #include "fileio.h"
8 #include "hexdecoct.h"
9 #include "json.h"
10 #include "memory-util.h"
11 #include "random-util.h"
12 #include "sha256.h"
13 #include "tpm2-util.h"
14
15 static int search_policy_hash(
16 struct crypt_device *cd,
17 const void *hash,
18 size_t hash_size) {
19
20 int r;
21
22 assert(cd);
23 assert(hash || hash_size == 0);
24
25 if (hash_size == 0)
26 return 0;
27
28 for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
29 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
30 _cleanup_free_ void *thash = NULL;
31 size_t thash_size = 0;
32 int keyslot;
33 JsonVariant *w;
34
35 r = cryptsetup_get_token_as_json(cd, token, "systemd-tpm2", &v);
36 if (IN_SET(r, -ENOENT, -EINVAL, -EMEDIUMTYPE))
37 continue;
38 if (r < 0)
39 return log_error_errno(r, "Failed to read JSON token data off disk: %m");
40
41 keyslot = cryptsetup_get_keyslot_from_token(v);
42 if (keyslot < 0) {
43 /* Handle parsing errors of the keyslots field gracefully, since it's not 'owned' by
44 * us, but by the LUKS2 spec */
45 log_warning_errno(keyslot, "Failed to determine keyslot of JSON token %i, skipping: %m", token);
46 continue;
47 }
48
49 w = json_variant_by_key(v, "tpm2-policy-hash");
50 if (!w || !json_variant_is_string(w))
51 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
52 "TPM2 token data lacks 'tpm2-policy-hash' field.");
53
54 r = unhexmem(json_variant_string(w), &thash, &thash_size);
55 if (r < 0)
56 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
57 "Invalid base64 data in 'tpm2-policy-hash' field.");
58
59 if (memcmp_nn(hash, hash_size, thash, thash_size) == 0)
60 return keyslot; /* Found entry with same hash. */
61 }
62
63 return -ENOENT; /* Not found */
64 }
65
66 static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) {
67 _cleanup_(erase_and_freep) char *pin_str = NULL;
68 TPM2Flags flags = 0;
69 int r;
70
71 assert(ret_pin_str);
72 assert(ret_flags);
73
74 r = getenv_steal_erase("NEWPIN", &pin_str);
75 if (r < 0)
76 return log_error_errno(r, "Failed to acquire PIN from environment: %m");
77 if (r > 0)
78 flags |= TPM2_FLAGS_USE_PIN;
79 else {
80 for (size_t i = 5;; i--) {
81 _cleanup_strv_free_erase_ char **pin = NULL, **pin2 = NULL;
82
83 if (i <= 0)
84 return log_error_errno(
85 SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up.");
86
87 pin = strv_free_erase(pin);
88 r = ask_password_auto(
89 "Please enter TPM2 PIN:",
90 "drive-harddisk",
91 NULL,
92 "tpm2-pin",
93 "cryptenroll.tpm2-pin",
94 USEC_INFINITY,
95 0,
96 &pin);
97 if (r < 0)
98 return log_error_errno(r, "Failed to ask for user pin: %m");
99 assert(strv_length(pin) == 1);
100
101 r = ask_password_auto(
102 "Please enter TPM2 PIN (repeat):",
103 "drive-harddisk",
104 NULL,
105 "tpm2-pin",
106 "cryptenroll.tpm2-pin",
107 USEC_INFINITY,
108 0,
109 &pin2);
110 if (r < 0)
111 return log_error_errno(r, "Failed to ask for user pin: %m");
112 assert(strv_length(pin) == 1);
113
114 if (strv_equal(pin, pin2)) {
115 pin_str = strdup(*pin);
116 if (!pin_str)
117 return log_oom();
118 flags |= TPM2_FLAGS_USE_PIN;
119 break;
120 }
121
122 log_error("PINs didn't match, please try again!");
123 }
124 }
125
126 *ret_flags = flags;
127 *ret_pin_str = TAKE_PTR(pin_str);
128
129 return 0;
130 }
131
132 int enroll_tpm2(struct crypt_device *cd,
133 const void *volume_key,
134 size_t volume_key_size,
135 const char *device,
136 uint32_t seal_key_handle,
137 const char *device_key,
138 Tpm2PCRValue *hash_pcr_values,
139 size_t n_hash_pcr_values,
140 const char *pubkey_path,
141 uint32_t pubkey_pcr_mask,
142 const char *signature_path,
143 bool use_pin,
144 const char *pcrlock_path) {
145
146 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *signature_json = NULL;
147 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
148 _cleanup_(iovec_done) struct iovec srk = {}, blob = {}, pubkey = {};
149 _cleanup_(iovec_done_erase) struct iovec secret = {};
150 const char *node;
151 _cleanup_(erase_and_freep) char *pin_str = NULL;
152 ssize_t base64_encoded_size;
153 int r, keyslot;
154 TPM2Flags flags = 0;
155 uint8_t binary_salt[SHA256_DIGEST_SIZE] = {};
156 /*
157 * erase the salt, we'd rather attempt to not have this in a coredump
158 * as an attacker would have all the parameters but pin used to create
159 * the session key. This problem goes away when we move to a trusted
160 * primary key, aka the SRK.
161 */
162 CLEANUP_ERASE(binary_salt);
163
164 assert(cd);
165 assert(volume_key);
166 assert(volume_key_size > 0);
167 assert(tpm2_pcr_values_valid(hash_pcr_values, n_hash_pcr_values));
168 assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask));
169
170 assert_se(node = crypt_get_device_name(cd));
171
172 if (use_pin) {
173 r = get_pin(&pin_str, &flags);
174 if (r < 0)
175 return r;
176
177 r = crypto_random_bytes(binary_salt, sizeof(binary_salt));
178 if (r < 0)
179 return log_error_errno(r, "Failed to acquire random salt: %m");
180
181 uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
182 CLEANUP_ERASE(salted_pin);
183 r = tpm2_util_pbkdf2_hmac_sha256(pin_str, strlen(pin_str), binary_salt, sizeof(binary_salt), salted_pin);
184 if (r < 0)
185 return log_error_errno(r, "Failed to perform PBKDF2: %m");
186
187 pin_str = erase_and_free(pin_str);
188 /* re-stringify pin_str */
189 base64_encoded_size = base64mem(salted_pin, sizeof(salted_pin), &pin_str);
190 if (base64_encoded_size < 0)
191 return log_error_errno(base64_encoded_size, "Failed to base64 encode salted pin: %m");
192 }
193
194 TPM2B_PUBLIC public = {};
195 r = tpm2_load_pcr_public_key(pubkey_path, &pubkey.iov_base, &pubkey.iov_len);
196 if (r < 0) {
197 if (pubkey_path || signature_path || r != -ENOENT)
198 return log_error_errno(r, "Failed to read TPM PCR public key: %m");
199
200 log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
201 pubkey_pcr_mask = 0;
202 } else {
203 r = tpm2_tpm2b_public_from_pem(pubkey.iov_base, pubkey.iov_len, &public);
204 if (r < 0)
205 return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
206
207 if (signature_path) {
208 /* Also try to load the signature JSON object, to verify that our enrollment will work.
209 * This is optional however, skip it if it's not explicitly provided. */
210
211 r = tpm2_load_pcr_signature(signature_path, &signature_json);
212 if (r < 0)
213 return log_debug_errno(r, "Failed to read TPM PCR signature: %m");
214 }
215 }
216
217 bool any_pcr_value_specified = tpm2_pcr_values_has_any_values(hash_pcr_values, n_hash_pcr_values);
218
219 _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
220 if (pcrlock_path) {
221 r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy);
222 if (r < 0)
223 return r;
224
225 any_pcr_value_specified = true;
226 flags |= TPM2_FLAGS_USE_PCRLOCK;
227 }
228
229 _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
230 TPM2B_PUBLIC device_key_public = {};
231 if (device_key) {
232 r = tpm2_load_public_key_file(device_key, &device_key_public);
233 if (r < 0)
234 return r;
235
236 if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values))
237 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
238 "Must provide all PCR values when using TPM2 device key.");
239 } else {
240 r = tpm2_context_new(device, &tpm2_context);
241 if (r < 0)
242 return log_error_errno(r, "Failed to create TPM2 context: %m");
243
244 if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values)) {
245 r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values);
246 if (r < 0)
247 return log_error_errno(r, "Could not read pcr values: %m");
248 }
249 }
250
251 uint16_t hash_pcr_bank = 0;
252 uint32_t hash_pcr_mask = 0;
253 if (n_hash_pcr_values > 0) {
254 size_t hash_count;
255 r = tpm2_pcr_values_hash_count(hash_pcr_values, n_hash_pcr_values, &hash_count);
256 if (r < 0)
257 return log_error_errno(r, "Could not get hash count: %m");
258
259 if (hash_count > 1)
260 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected.");
261
262 hash_pcr_bank = hash_pcr_values[0].hash;
263 r = tpm2_pcr_values_to_mask(hash_pcr_values, n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask);
264 if (r < 0)
265 return log_error_errno(r, "Could not get hash mask: %m");
266 }
267
268 TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
269 r = tpm2_calculate_sealing_policy(
270 hash_pcr_values,
271 n_hash_pcr_values,
272 iovec_is_set(&pubkey) ? &public : NULL,
273 use_pin,
274 pcrlock_path ? &pcrlock_policy : NULL,
275 &policy);
276 if (r < 0)
277 return r;
278
279 if (device_key)
280 r = tpm2_calculate_seal(
281 seal_key_handle,
282 &device_key_public,
283 /* attributes= */ NULL,
284 /* secret= */ NULL,
285 &policy,
286 pin_str,
287 &secret,
288 &blob,
289 &srk);
290 else
291 r = tpm2_seal(tpm2_context,
292 seal_key_handle,
293 &policy,
294 pin_str,
295 &secret,
296 &blob,
297 /* ret_primary_alg= */ NULL,
298 &srk);
299 if (r < 0)
300 return log_error_errno(r, "Failed to seal to TPM2: %m");
301
302 /* Let's see if we already have this specific PCR policy hash enrolled, if so, exit early. */
303 r = search_policy_hash(cd, policy.buffer, policy.size);
304 if (r == -ENOENT)
305 log_debug_errno(r, "PCR policy hash not yet enrolled, enrolling now.");
306 else if (r < 0)
307 return r;
308 else {
309 log_info("This PCR set is already enrolled, executing no operation.");
310 return r; /* return existing keyslot, so that wiping won't kill it */
311 }
312
313 /* If possible, verify the sealed data object. */
314 if ((!iovec_is_set(&pubkey) || signature_json) && !any_pcr_value_specified && !device_key) {
315 _cleanup_(iovec_done_erase) struct iovec secret2 = {};
316
317 log_debug("Unsealing for verification...");
318 r = tpm2_unseal(tpm2_context,
319 hash_pcr_mask,
320 hash_pcr_bank,
321 &pubkey,
322 pubkey_pcr_mask,
323 signature_json,
324 pin_str,
325 pcrlock_path ? &pcrlock_policy : NULL,
326 /* primary_alg= */ 0,
327 &blob,
328 &IOVEC_MAKE(policy.buffer, policy.size),
329 &srk,
330 &secret2);
331 if (r < 0)
332 return log_error_errno(r, "Failed to unseal secret using TPM2: %m");
333
334 if (iovec_memcmp(&secret, &secret2) != 0)
335 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 seal/unseal verification failed.");
336 }
337
338 /* 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. */
339 base64_encoded_size = base64mem(secret.iov_base, secret.iov_len, &base64_encoded);
340 if (base64_encoded_size < 0)
341 return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
342
343 r = cryptsetup_set_minimal_pbkdf(cd);
344 if (r < 0)
345 return log_error_errno(r, "Failed to set minimal PBKDF: %m");
346
347 keyslot = crypt_keyslot_add_by_volume_key(
348 cd,
349 CRYPT_ANY_SLOT,
350 volume_key,
351 volume_key_size,
352 base64_encoded,
353 base64_encoded_size);
354 if (keyslot < 0)
355 return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
356
357 r = tpm2_make_luks2_json(
358 keyslot,
359 hash_pcr_mask,
360 hash_pcr_bank,
361 &pubkey,
362 pubkey_pcr_mask,
363 /* primary_alg= */ 0,
364 &blob,
365 &IOVEC_MAKE(policy.buffer, policy.size),
366 use_pin ? &IOVEC_MAKE(binary_salt, sizeof(binary_salt)) : NULL,
367 &srk,
368 flags,
369 &v);
370 if (r < 0)
371 return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
372
373 r = cryptsetup_add_token_json(cd, v);
374 if (r < 0)
375 return log_error_errno(r, "Failed to add TPM2 JSON token to LUKS2 header: %m");
376
377 log_info("New TPM2 token enrolled as key slot %i.", keyslot);
378 return keyslot;
379 }