]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cryptsetup-tpm2.c
ask-password: rework how we pass request meta info when asking passwords
[thirdparty/systemd.git] / src / shared / cryptsetup-tpm2.c
CommitLineData
18843ecc
LP
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include "alloc-util.h"
bea344a1 4#include "ask-password-api.h"
18843ecc 5#include "cryptsetup-tpm2.h"
bea344a1 6#include "env-util.h"
18843ecc
LP
7#include "fileio.h"
8#include "hexdecoct.h"
9#include "json.h"
10#include "parse-util.h"
11#include "random-util.h"
aae6eb96 12#include "sha256.h"
18843ecc
LP
13#include "tpm2-util.h"
14
bea344a1 15static int get_pin(usec_t until, AskPasswordFlags ask_password_flags, bool headless, char **ret_pin_str) {
a3b46c6b 16 _cleanup_(erase_and_freep) char *pin_str = NULL;
bea344a1
GG
17 _cleanup_strv_free_erase_ char **pin = NULL;
18 int r;
19
20 assert(ret_pin_str);
21
22 r = getenv_steal_erase("PIN", &pin_str);
23 if (r < 0)
24 return log_error_errno(r, "Failed to acquire PIN from environment: %m");
25 if (!r) {
26 if (headless)
27 return log_error_errno(
28 SYNTHETIC_ERRNO(ENOPKG),
29 "PIN querying disabled via 'headless' option. "
30 "Use the '$PIN' environment variable.");
31
d08fd4c3
LP
32 static const AskPasswordRequest req = {
33 .message = "Please enter TPM2 PIN:",
34 .icon = "drive-harddisk",
35 .keyring = "tpm2-pin",
36 .credential = "cryptsetup.tpm2-pin",
37 };
38
bea344a1
GG
39 pin = strv_free_erase(pin);
40 r = ask_password_auto(
d08fd4c3 41 &req,
bea344a1
GG
42 until,
43 ask_password_flags,
44 &pin);
45 if (r < 0)
46 return log_error_errno(r, "Failed to ask for user pin: %m");
47 assert(strv_length(pin) == 1);
48
49 pin_str = strdup(pin[0]);
50 if (!pin_str)
51 return log_oom();
52 }
53
54 *ret_pin_str = TAKE_PTR(pin_str);
55
56 return r;
57}
58
18843ecc
LP
59int acquire_tpm2_key(
60 const char *volume_name,
61 const char *device,
d9b5841d 62 uint32_t hash_pcr_mask,
07697bfe 63 uint16_t pcr_bank,
8d042bc4 64 const struct iovec *pubkey,
dc63b2c9
LP
65 uint32_t pubkey_pcr_mask,
66 const char *signature_path,
404aea78 67 const char *pcrlock_path,
2b92a672 68 uint16_t primary_alg,
18843ecc
LP
69 const char *key_file,
70 size_t key_file_size,
71 uint64_t key_file_offset,
8d042bc4
LP
72 const struct iovec *key_data,
73 const struct iovec *policy_hash,
74 const struct iovec *salt,
75 const struct iovec *srk,
d37c312b 76 const struct iovec *pcrlock_nv,
bea344a1
GG
77 TPM2Flags flags,
78 usec_t until,
79 bool headless,
80 AskPasswordFlags ask_password_flags,
8d042bc4 81 struct iovec *ret_decrypted_key) {
18843ecc 82
dc63b2c9 83 _cleanup_(json_variant_unrefp) JsonVariant *signature_json = NULL;
18843ecc
LP
84 _cleanup_free_ void *loaded_blob = NULL;
85 _cleanup_free_ char *auto_device = NULL;
8d042bc4 86 struct iovec blob;
18843ecc
LP
87 int r;
88
8d042bc4 89 assert(iovec_is_valid(salt));
504d0acf 90
18843ecc 91 if (!device) {
f9a0ee75 92 r = tpm2_find_device_auto(&auto_device);
18843ecc
LP
93 if (r == -ENODEV)
94 return -EAGAIN; /* Tell the caller to wait for a TPM2 device to show up */
95 if (r < 0)
f9a0ee75 96 return log_error_errno(r, "Could not find TPM2 device: %m");
18843ecc
LP
97
98 device = auto_device;
99 }
100
8d042bc4
LP
101 if (iovec_is_set(key_data))
102 blob = *key_data;
103 else {
18843ecc
LP
104 _cleanup_free_ char *bindname = NULL;
105
106 /* If we read the salt via AF_UNIX, make this client recognizable */
107 if (asprintf(&bindname, "@%" PRIx64"/cryptsetup-tpm2/%s", random_u64(), volume_name) < 0)
108 return log_oom();
109
110 r = read_full_file_full(
111 AT_FDCWD, key_file,
112 key_file_offset == 0 ? UINT64_MAX : key_file_offset,
113 key_file_size == 0 ? SIZE_MAX : key_file_size,
114 READ_FULL_FILE_CONNECT_SOCKET,
115 bindname,
8d042bc4 116 (char**) &loaded_blob, &blob.iov_len);
18843ecc
LP
117 if (r < 0)
118 return r;
119
8d042bc4 120 blob.iov_base = loaded_blob;
18843ecc
LP
121 }
122
dc63b2c9
LP
123 if (pubkey_pcr_mask != 0) {
124 r = tpm2_load_pcr_signature(signature_path, &signature_json);
125 if (r < 0)
f9a0ee75 126 return log_error_errno(r, "Failed to load pcr signature: %m");
dc63b2c9
LP
127 }
128
404aea78
LP
129 _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
130
131 if (FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK)) {
132 r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy);
133 if (r < 0)
134 return r;
d37c312b
LP
135 if (r == 0) {
136 /* Not found? Then search among passed credentials */
137 r = tpm2_pcrlock_policy_from_credentials(srk, pcrlock_nv, &pcrlock_policy);
138 if (r < 0)
139 return r;
140 if (r == 0)
141 return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Couldn't find pcrlock policy for volume.");
142 }
404aea78
LP
143 }
144
db7fdf15
DS
145 _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
146 r = tpm2_context_new(device, &tpm2_context);
147 if (r < 0)
148 return log_error_errno(r, "Failed to create TPM2 context: %m");
149
f9a0ee75 150 if (!(flags & TPM2_FLAGS_USE_PIN)) {
db7fdf15 151 r = tpm2_unseal(tpm2_context,
d9b5841d 152 hash_pcr_mask,
bea344a1 153 pcr_bank,
8d042bc4 154 pubkey,
dc63b2c9
LP
155 pubkey_pcr_mask,
156 signature_json,
d9b5841d 157 /* pin= */ NULL,
404aea78 158 FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL,
bea344a1 159 primary_alg,
8d042bc4 160 &blob,
bea344a1 161 policy_hash,
8d042bc4
LP
162 srk,
163 ret_decrypted_key);
f9a0ee75
DS
164 if (r < 0)
165 return log_error_errno(r, "Failed to unseal secret using TPM2: %m");
166
167 return r;
168 }
bea344a1
GG
169
170 for (int i = 5;; i--) {
aae6eb96 171 _cleanup_(erase_and_freep) char *pin_str = NULL, *b64_salted_pin = NULL;
bea344a1
GG
172
173 if (i <= 0)
174 return -EACCES;
175
176 r = get_pin(until, ask_password_flags, headless, &pin_str);
177 if (r < 0)
178 return r;
179
8d042bc4 180 if (iovec_is_set(salt)) {
aae6eb96
WR
181 uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
182 CLEANUP_ERASE(salted_pin);
183
8d042bc4 184 r = tpm2_util_pbkdf2_hmac_sha256(pin_str, strlen(pin_str), salt->iov_base, salt->iov_len, salted_pin);
aae6eb96
WR
185 if (r < 0)
186 return log_error_errno(r, "Failed to perform PBKDF2: %m");
187
188 r = base64mem(salted_pin, sizeof(salted_pin), &b64_salted_pin);
189 if (r < 0)
190 return log_error_errno(r, "Failed to base64 encode salted pin: %m");
191 } else
192 /* no salting needed, backwards compat with non-salted pins */
193 b64_salted_pin = TAKE_PTR(pin_str);
194
db7fdf15 195 r = tpm2_unseal(tpm2_context,
d9b5841d 196 hash_pcr_mask,
bea344a1 197 pcr_bank,
8d042bc4 198 pubkey,
dc63b2c9
LP
199 pubkey_pcr_mask,
200 signature_json,
aae6eb96 201 b64_salted_pin,
404aea78 202 pcrlock_path ? &pcrlock_policy : NULL,
bea344a1 203 primary_alg,
8d042bc4 204 &blob,
bea344a1 205 policy_hash,
8d042bc4
LP
206 srk,
207 ret_decrypted_key);
f9a0ee75
DS
208 if (r < 0) {
209 log_error_errno(r, "Failed to unseal secret using TPM2: %m");
210
211 /* We get this error in case there is an authentication policy mismatch. This should
212 * not happen, but this avoids confusing behavior, just in case. */
213 if (!IN_SET(r, -EPERM, -ENOLCK))
214 continue;
215 }
bea344a1
GG
216
217 return r;
218 }
18843ecc
LP
219}
220
221int find_tpm2_auto_data(
222 struct crypt_device *cd,
223 uint32_t search_pcr_mask,
224 int start_token,
dc63b2c9 225 uint32_t *ret_hash_pcr_mask,
07697bfe 226 uint16_t *ret_pcr_bank,
8d042bc4 227 struct iovec *ret_pubkey,
dc63b2c9 228 uint32_t *ret_pubkey_pcr_mask,
2b92a672 229 uint16_t *ret_primary_alg,
8d042bc4
LP
230 struct iovec *ret_blob,
231 struct iovec *ret_policy_hash,
232 struct iovec *ret_salt,
233 struct iovec *ret_srk,
d37c312b 234 struct iovec *ret_pcrlock_nv,
fdf6c27c 235 TPM2Flags *ret_flags,
18843ecc 236 int *ret_keyslot,
fdf6c27c 237 int *ret_token) {
18843ecc 238
fdf6c27c 239 int r, token;
18843ecc
LP
240
241 assert(cd);
242
3c2c8e62 243 for (token = start_token; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
d37c312b 244 _cleanup_(iovec_done) struct iovec blob = {}, policy_hash = {}, pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
18843ecc 245 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
fdf6c27c
LP
246 uint32_t hash_pcr_mask, pubkey_pcr_mask;
247 uint16_t pcr_bank, primary_alg;
248 TPM2Flags flags;
249 int keyslot;
18843ecc
LP
250
251 r = cryptsetup_get_token_as_json(cd, token, "systemd-tpm2", &v);
252 if (IN_SET(r, -ENOENT, -EINVAL, -EMEDIUMTYPE))
253 continue;
254 if (r < 0)
255 return log_error_errno(r, "Failed to read JSON token data off disk: %m");
256
fdf6c27c
LP
257 r = tpm2_parse_luks2_json(
258 v,
259 &keyslot,
260 &hash_pcr_mask,
261 &pcr_bank,
8d042bc4 262 &pubkey,
fdf6c27c
LP
263 &pubkey_pcr_mask,
264 &primary_alg,
8d042bc4
LP
265 &blob,
266 &policy_hash,
267 &salt,
268 &srk,
d37c312b 269 &pcrlock_nv,
fdf6c27c
LP
270 &flags);
271 if (r == -EUCLEAN) /* Gracefully handle issues in JSON fields not owned by us */
1641c2b1 272 continue;
18843ecc 273 if (r < 0)
fdf6c27c
LP
274 return log_error_errno(r, "Failed to parse TPM2 JSON data: %m");
275
276 if (search_pcr_mask == UINT32_MAX ||
277 search_pcr_mask == hash_pcr_mask) {
278
279 if (start_token <= 0)
280 log_info("Automatically discovered security TPM2 token unlocks volume.");
281
282 *ret_hash_pcr_mask = hash_pcr_mask;
283 *ret_pcr_bank = pcr_bank;
8d042bc4 284 *ret_pubkey = TAKE_STRUCT(pubkey);
fdf6c27c
LP
285 *ret_pubkey_pcr_mask = pubkey_pcr_mask;
286 *ret_primary_alg = primary_alg;
8d042bc4
LP
287 *ret_blob = TAKE_STRUCT(blob);
288 *ret_policy_hash = TAKE_STRUCT(policy_hash);
289 *ret_salt = TAKE_STRUCT(salt);
fdf6c27c
LP
290 *ret_keyslot = keyslot;
291 *ret_token = token;
8d042bc4 292 *ret_srk = TAKE_STRUCT(srk);
d37c312b 293 *ret_pcrlock_nv = TAKE_STRUCT(pcrlock_nv);
fdf6c27c
LP
294 *ret_flags = flags;
295 return 0;
bea344a1
GG
296 }
297
fdf6c27c 298 /* PCR mask doesn't match what is configured, ignore this entry, let's see next */
18843ecc
LP
299 }
300
fdf6c27c 301 return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "No valid TPM2 token data found.");
18843ecc 302}