]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
cryptsetup-tokens: fix pin asserts
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-tokens / cryptsetup-token-systemd-tpm2.c
CommitLineData
d1ae38d8
OK
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <errno.h>
4#include <libcryptsetup.h>
5
6#include "cryptsetup-token.h"
7#include "cryptsetup-token-util.h"
8#include "hexdecoct.h"
1f895ada 9#include "json.h"
d1ae38d8
OK
10#include "luks2-tpm2.h"
11#include "memory-util.h"
1f895ada 12#include "strv.h"
d1ae38d8
OK
13#include "tpm2-util.h"
14#include "version.h"
15
16#define TOKEN_NAME "systemd-tpm2"
17#define TOKEN_VERSION_MAJOR "1"
18#define TOKEN_VERSION_MINOR "0"
19
20/* for libcryptsetup debug purpose */
21_public_ const char *cryptsetup_token_version(void) {
22
ea2a57be 23 return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" PROJECT_VERSION_FULL " (" GIT_VERSION ")";
d1ae38d8
OK
24}
25
26static int log_debug_open_error(struct crypt_device *cd, int r) {
b6fd88a5
LP
27 if (r == -EAGAIN)
28 return crypt_log_debug_errno(cd, r, "TPM2 device not found.");
29 if (r == -ENXIO)
30 return crypt_log_debug_errno(cd, r, "No matching TPM2 token data found.");
d1ae38d8
OK
31
32 return crypt_log_debug_errno(cd, r, TOKEN_NAME " open failed: %m.");
33}
34
35ba2b4f 35_public_ int cryptsetup_token_open_pin(
d1ae38d8
OK
36 struct crypt_device *cd, /* is always LUKS2 context */
37 int token /* is always >= 0 */,
35ba2b4f
JW
38 const char *pin,
39 size_t pin_size,
75a9681e
LP
40 char **ret_password, /* freed by cryptsetup_token_buffer_free */
41 size_t *ret_password_len,
d1ae38d8
OK
42 void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
43
75a9681e 44 _cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL;
d37c312b 45 _cleanup_(iovec_done) struct iovec blob = {}, pubkey = {}, policy_hash = {}, salt = {}, srk = {}, pcrlock_nv = {};
8d042bc4 46 _cleanup_(iovec_done_erase) struct iovec decrypted_key = {};
75a9681e
LP
47 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
48 uint32_t hash_pcr_mask, pubkey_pcr_mask;
d1ae38d8
OK
49 systemd_tpm2_plugin_params params = {
50 .search_pcr_mask = UINT32_MAX
51 };
75a9681e 52 uint16_t pcr_bank, primary_alg;
5e476b85 53 ssize_t base64_encoded_size;
75a9681e
LP
54 TPM2Flags flags = 0;
55 const char *json;
56 int r;
d1ae38d8 57
d1ae38d8 58 assert(token >= 0);
5cef6b53 59 assert(pin || pin_size == 0);
75a9681e
LP
60 assert(ret_password);
61 assert(ret_password_len);
d1ae38d8
OK
62
63 /* This must not fail at this moment (internal error) */
64 r = crypt_token_json_get(cd, token, &json);
65 assert(token == r);
66 assert(json);
67
35ba2b4f
JW
68 r = crypt_normalize_pin(pin, pin_size, &pin_string);
69 if (r < 0)
cee60fc3 70 return crypt_log_debug_errno(cd, r, "Cannot normalize PIN: %m");
35ba2b4f 71
d1ae38d8
OK
72 if (usrptr)
73 params = *(systemd_tpm2_plugin_params *)usrptr;
74
75a9681e 75 r = json_parse(json, 0, &v, NULL, NULL);
d1ae38d8 76 if (r < 0)
75a9681e
LP
77 return crypt_log_debug_errno(cd, r, "Failed to parse token JSON data: %m");
78
79 r = tpm2_parse_luks2_json(
80 v,
8d042bc4 81 /* ret_keyslot= */ NULL,
75a9681e
LP
82 &hash_pcr_mask,
83 &pcr_bank,
84 &pubkey,
75a9681e
LP
85 &pubkey_pcr_mask,
86 &primary_alg,
87 &blob,
75a9681e 88 &policy_hash,
aae6eb96 89 &salt,
8d042bc4 90 &srk,
d37c312b 91 &pcrlock_nv,
75a9681e 92 &flags);
d1ae38d8
OK
93 if (r < 0)
94 return log_debug_open_error(cd, r);
95
75a9681e
LP
96 if (params.search_pcr_mask != UINT32_MAX && hash_pcr_mask != params.search_pcr_mask)
97 return crypt_log_debug_errno(cd, ENXIO, "PCR mask doesn't match expectation (%" PRIu32 " vs. %" PRIu32 ")", hash_pcr_mask, params.search_pcr_mask);
d1ae38d8
OK
98
99 r = acquire_luks2_key(
75a9681e
LP
100 params.device,
101 hash_pcr_mask,
b98855d9 102 pcr_bank,
8d042bc4 103 &pubkey,
75a9681e
LP
104 pubkey_pcr_mask,
105 params.signature_path,
106 pin_string,
404aea78 107 params.pcrlock_path,
2b92a672 108 primary_alg,
8d042bc4
LP
109 &blob,
110 &policy_hash,
111 &salt,
112 &srk,
d37c312b 113 &pcrlock_nv,
1f895ada 114 flags,
8d042bc4 115 &decrypted_key);
d1ae38d8
OK
116 if (r < 0)
117 return log_debug_open_error(cd, r);
118
119 /* Before using this key as passphrase we base64 encode it, for compat with homed */
8d042bc4 120 base64_encoded_size = base64mem(decrypted_key.iov_base, decrypted_key.iov_len, &base64_encoded);
5e476b85
LP
121 if (base64_encoded_size < 0)
122 return log_debug_open_error(cd, base64_encoded_size);
d1ae38d8 123
bdbb61f6 124 /* free'd automatically by libcryptsetup */
75a9681e 125 *ret_password = TAKE_PTR(base64_encoded);
5e476b85 126 *ret_password_len = base64_encoded_size;
d1ae38d8
OK
127
128 return 0;
129}
130
35ba2b4f
JW
131/*
132 * This function is called from within following libcryptsetup calls
133 * provided conditions further below are met:
134 *
135 * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-tpm2'):
136 *
137 * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
138 * (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
139 * and token is assigned to at least single keyslot).
140 *
141 * - if plugin defines validate function (see cryptsetup_token_validate below) it must have
142 * passed the check (aka return 0)
143 */
144_public_ int cryptsetup_token_open(
145 struct crypt_device *cd, /* is always LUKS2 context */
146 int token /* is always >= 0 */,
75a9681e
LP
147 char **ret_password, /* freed by cryptsetup_token_buffer_free */
148 size_t *ret_password_len,
35ba2b4f
JW
149 void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
150
75a9681e 151 return cryptsetup_token_open_pin(cd, token, NULL, 0, ret_password, ret_password_len, usrptr);
35ba2b4f
JW
152}
153
d1ae38d8
OK
154/*
155 * libcryptsetup callback for memory deallocation of 'password' parameter passed in
156 * any crypt_token_open_* plugin function
157 */
158_public_ void cryptsetup_token_buffer_free(void *buffer, size_t buffer_len) {
159 erase_and_free(buffer);
160}
161
162/*
163 * prints systemd-tpm2 token content in crypt_dump().
164 * 'type' and 'keyslots' fields are printed by libcryptsetup
165 */
166_public_ void cryptsetup_token_dump(
167 struct crypt_device *cd /* is always LUKS2 context */,
168 const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
169
75a9681e 170 _cleanup_free_ char *hash_pcrs_str = NULL, *pubkey_pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL, *pubkey_str = NULL;
d37c312b 171 _cleanup_(iovec_done) struct iovec blob = {}, pubkey = {}, policy_hash = {}, salt = {}, srk = {}, pcrlock_nv = {};
75a9681e 172 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
75a9681e 173 uint32_t hash_pcr_mask, pubkey_pcr_mask;
2b92a672 174 uint16_t pcr_bank, primary_alg;
75a9681e
LP
175 TPM2Flags flags = 0;
176 int r;
d1ae38d8
OK
177
178 assert(json);
179
75a9681e
LP
180 r = json_parse(json, 0, &v, NULL, NULL);
181 if (r < 0)
182 return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON object: %m");
183
184 r = tpm2_parse_luks2_json(
185 v,
186 NULL,
187 &hash_pcr_mask,
188 &pcr_bank,
189 &pubkey,
75a9681e
LP
190 &pubkey_pcr_mask,
191 &primary_alg,
192 &blob,
75a9681e 193 &policy_hash,
aae6eb96 194 &salt,
8d042bc4 195 &srk,
d37c312b 196 &pcrlock_nv,
75a9681e
LP
197 &flags);
198 if (r < 0)
199 return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON fields: %m");
200
c69bd0ab
DS
201 hash_pcrs_str = tpm2_pcr_mask_to_string(hash_pcr_mask);
202 if (!hash_pcrs_str)
203 return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m");
d1ae38d8 204
c69bd0ab
DS
205 pubkey_pcrs_str = tpm2_pcr_mask_to_string(pubkey_pcr_mask);
206 if (!pubkey_pcrs_str)
207 return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m");
d1ae38d8 208
8d042bc4 209 r = crypt_dump_buffer_to_hex_string(blob.iov_base, blob.iov_len, &blob_str);
d1ae38d8 210 if (r < 0)
cee60fc3 211 return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
d1ae38d8 212
8d042bc4 213 r = crypt_dump_buffer_to_hex_string(pubkey.iov_base, pubkey.iov_len, &pubkey_str);
d1ae38d8 214 if (r < 0)
cee60fc3 215 return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
d1ae38d8 216
8d042bc4 217 r = crypt_dump_buffer_to_hex_string(policy_hash.iov_base, policy_hash.iov_len, &policy_hash_str);
d1ae38d8 218 if (r < 0)
cee60fc3 219 return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
d1ae38d8 220
75a9681e 221 crypt_log(cd, "\ttpm2-hash-pcrs: %s\n", strna(hash_pcrs_str));
7bfe0a48 222 crypt_log(cd, "\ttpm2-pcr-bank: %s\n", strna(tpm2_hash_alg_to_string(pcr_bank)));
75a9681e
LP
223 crypt_log(cd, "\ttpm2-pubkey:" CRYPT_DUMP_LINE_SEP "%s\n", pubkey_str);
224 crypt_log(cd, "\ttpm2-pubkey-pcrs: %s\n", strna(pubkey_pcrs_str));
7bfe0a48 225 crypt_log(cd, "\ttpm2-primary-alg: %s\n", strna(tpm2_asym_alg_to_string(primary_alg)));
75a9681e 226 crypt_log(cd, "\ttpm2-blob: %s\n", blob_str);
dfba4518 227 crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
75a9681e 228 crypt_log(cd, "\ttpm2-pin: %s\n", true_false(flags & TPM2_FLAGS_USE_PIN));
404aea78 229 crypt_log(cd, "\ttpm2-pcrlock: %s\n", true_false(flags & TPM2_FLAGS_USE_PCRLOCK));
8d042bc4
LP
230 crypt_log(cd, "\ttpm2-salt: %s\n", true_false(iovec_is_set(&salt)));
231 crypt_log(cd, "\ttpm2-srk: %s\n", true_false(iovec_is_set(&srk)));
d37c312b 232 crypt_log(cd, "\ttpm2-pcrlock-nv: %s\n", true_false(iovec_is_set(&pcrlock_nv)));
d1ae38d8
OK
233}
234
235/*
236 * Note:
237 * If plugin is available in library path, it's called in before following libcryptsetup calls:
238 *
239 * crypt_token_json_set, crypt_dump, any crypt_activate_by_token_* flavour
240 */
241_public_ int cryptsetup_token_validate(
242 struct crypt_device *cd, /* is always LUKS2 context */
243 const char *json /* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-tpm2' */) {
244
245 int r;
246 JsonVariant *w, *e;
247 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
248
249 assert(json);
250
251 r = json_parse(json, 0, &v, NULL, NULL);
252 if (r < 0)
253 return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m");
254
255 w = json_variant_by_key(v, "tpm2-pcrs");
256 if (!w || !json_variant_is_array(w)) {
257 crypt_log_debug(cd, "TPM2 token data lacks 'tpm2-pcrs' field.");
258 return 1;
259 }
260
261 JSON_VARIANT_ARRAY_FOREACH(e, w) {
718ca772 262 uint64_t u;
d1ae38d8
OK
263
264 if (!json_variant_is_number(e)) {
265 crypt_log_debug(cd, "TPM2 PCR is not a number.");
266 return 1;
267 }
268
269 u = json_variant_unsigned(e);
323eb480 270 if (!TPM2_PCR_INDEX_VALID(u)) {
d1ae38d8
OK
271 crypt_log_debug(cd, "TPM2 PCR number out of range.");
272 return 1;
273 }
274 }
275
2b92a672
LP
276 /* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded
277 * to SHA256. */
38a0aec6
OK
278 w = json_variant_by_key(v, "tpm2-pcr-bank");
279 if (w) {
280 /* The PCR bank field is optional */
281
282 if (!json_variant_is_string(w)) {
283 crypt_log_debug(cd, "TPM2 PCR bank is not a string.");
284 return 1;
285 }
286
7bfe0a48 287 if (tpm2_hash_alg_from_string(json_variant_string(w)) < 0) {
38a0aec6
OK
288 crypt_log_debug(cd, "TPM2 PCR bank invalid or not supported: %s.", json_variant_string(w));
289 return 1;
290 }
291 }
292
2b92a672
LP
293 /* The primary key algorithm field is optional, since it was also added in systemd 250 only. Before
294 * the algorithm was hardcoded to ECC. */
295 w = json_variant_by_key(v, "tpm2-primary-alg");
296 if (w) {
297 /* The primary key algorithm is optional */
298
299 if (!json_variant_is_string(w)) {
300 crypt_log_debug(cd, "TPM2 primary key algorithm is not a string.");
301 return 1;
302 }
303
7bfe0a48 304 if (tpm2_asym_alg_from_string(json_variant_string(w)) < 0) {
2b92a672
LP
305 crypt_log_debug(cd, "TPM2 primary key algorithm invalid or not supported: %s", json_variant_string(w));
306 return 1;
307 }
308 }
309
d1ae38d8
OK
310 w = json_variant_by_key(v, "tpm2-blob");
311 if (!w || !json_variant_is_string(w)) {
312 crypt_log_debug(cd, "TPM2 token data lacks 'tpm2-blob' field.");
313 return 1;
314 }
315
bdd2036e 316 r = unbase64mem(json_variant_string(w), NULL, NULL);
d1ae38d8
OK
317 if (r < 0)
318 return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-blob' field: %m");
319
320 w = json_variant_by_key(v, "tpm2-policy-hash");
321 if (!w || !json_variant_is_string(w)) {
322 crypt_log_debug(cd, "TPM2 token data lacks 'tpm2-policy-hash' field.");
323 return 1;
324 }
325
bdd2036e 326 r = unhexmem(json_variant_string(w), NULL, NULL);
d1ae38d8
OK
327 if (r < 0)
328 return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-policy-hash' field: %m");
329
1f895ada
GG
330 w = json_variant_by_key(v, "tpm2-pin");
331 if (w) {
332 if (!json_variant_is_boolean(w)) {
333 crypt_log_debug(cd, "TPM2 PIN policy is not a boolean.");
334 return 1;
335 }
336 }
337
d1ae38d8
OK
338 return 0;
339}