]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptenroll/cryptenroll-tpm2.c
Merge pull request #18044 from weblate/weblate-systemd-master
[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 "cryptenroll-tpm2.h"
5 #include "hexdecoct.h"
6 #include "json.h"
7 #include "memory-util.h"
8 #include "tpm2-util.h"
9
10 static int search_policy_hash(
11 struct crypt_device *cd,
12 const void *hash,
13 size_t hash_size) {
14
15 int r;
16
17 assert(cd);
18 assert(hash || hash_size == 0);
19
20 if (hash_size == 0)
21 return 0;
22
23 for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token ++) {
24 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
25 _cleanup_free_ void *thash = NULL;
26 size_t thash_size = 0;
27 int keyslot;
28 JsonVariant *w;
29
30 r = cryptsetup_get_token_as_json(cd, token, "systemd-tpm2", &v);
31 if (IN_SET(r, -ENOENT, -EINVAL, -EMEDIUMTYPE))
32 continue;
33 if (r < 0)
34 return log_error_errno(r, "Failed to read JSON token data off disk: %m");
35
36 keyslot = cryptsetup_get_keyslot_from_token(v);
37 if (keyslot < 0)
38 return log_error_errno(keyslot, "Failed to determine keyslot of JSON token: %m");
39
40 w = json_variant_by_key(v, "tpm2-policy-hash");
41 if (!w || !json_variant_is_string(w))
42 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
43 "TPM2 token data lacks 'tpm2-policy-hash' field.");
44
45 r = unhexmem(json_variant_string(w), (size_t) -1, &thash, &thash_size);
46 if (r < 0)
47 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
48 "Invalid base64 data in 'tpm2-policy-hash' field.");
49
50 if (memcmp_nn(hash, hash_size, thash, thash_size) == 0)
51 return keyslot; /* Found entry with same hash. */
52 }
53
54 return -ENOENT; /* Not found */
55 }
56
57 int enroll_tpm2(struct crypt_device *cd,
58 const void *volume_key,
59 size_t volume_key_size,
60 const char *device,
61 uint32_t pcr_mask) {
62
63 _cleanup_(erase_and_freep) void *secret = NULL, *secret2 = NULL;
64 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
65 _cleanup_(erase_and_freep) char *base64_encoded = NULL;
66 size_t secret_size, secret2_size, blob_size, hash_size;
67 _cleanup_free_ void *blob = NULL, *hash = NULL;
68 const char *node;
69 int r, keyslot;
70
71 assert(cd);
72 assert(volume_key);
73 assert(volume_key_size > 0);
74 assert(pcr_mask < (1U << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
75
76 assert_se(node = crypt_get_device_name(cd));
77
78 r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size);
79 if (r < 0)
80 return r;
81
82 /* Let's see if we already have this specific PCR policy hash enrolled, if so, exit early. */
83 r = search_policy_hash(cd, hash, hash_size);
84 if (r == -ENOENT)
85 log_debug_errno(r, "PCR policy hash not yet enrolled, enrolling now.");
86 else if (r < 0)
87 return r;
88 else {
89 log_info("This PCR set is already enrolled, executing no operation.");
90 return r; /* return existing keyslot, so that wiping won't kill it */
91 }
92
93 /* Quick verification that everything is in order, we are not in a hurry after all. */
94 log_debug("Unsealing for verification...");
95 r = tpm2_unseal(device, pcr_mask, blob, blob_size, hash, hash_size, &secret2, &secret2_size);
96 if (r < 0)
97 return r;
98
99 if (memcmp_nn(secret, secret_size, secret2, secret2_size) != 0)
100 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 seal/unseal verification failed.");
101
102 /* 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. */
103 r = base64mem(secret, secret_size, &base64_encoded);
104 if (r < 0)
105 return log_error_errno(r, "Failed to base64 encode secret key: %m");
106
107 r = cryptsetup_set_minimal_pbkdf(cd);
108 if (r < 0)
109 return log_error_errno(r, "Failed to set minimal PBKDF: %m");
110
111 keyslot = crypt_keyslot_add_by_volume_key(
112 cd,
113 CRYPT_ANY_SLOT,
114 volume_key,
115 volume_key_size,
116 base64_encoded,
117 strlen(base64_encoded));
118 if (keyslot < 0)
119 return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
120
121 r = tpm2_make_luks2_json(keyslot, pcr_mask, blob, blob_size, hash, hash_size, &v);
122 if (r < 0)
123 return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
124
125 r = cryptsetup_add_token_json(cd, v);
126 if (r < 0)
127 return log_error_errno(r, "Failed to add TPM2 JSON token to LUKS2 header: %m");
128
129 log_info("New TPM2 token enrolled as key slot %i.", keyslot);
130 return keyslot;
131 }