• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14895667988

07 May 2025 08:57PM UTC coverage: 72.225% (-0.007%) from 72.232%
14895667988

push

github

yuwata
network: log_link_message_debug_errno() automatically append %m if necessary

Follow-up for d28746ef5.
Fixes CID#1609753.

0 of 1 new or added line in 1 file covered. (0.0%)

20297 existing lines in 338 files now uncovered.

297407 of 411780 relevant lines covered (72.22%)

695716.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

54.49
/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <libcryptsetup.h>
5

6
#include "alloc-util.h"
7
#include "cryptsetup-token.h"
8
#include "cryptsetup-token-util.h"
9
#include "hexdecoct.h"
10
#include "json-util.h"
11
#include "luks2-tpm2.h"
12
#include "memory-util.h"
13
#include "strv.h"
14
#include "tpm2-util.h"
15
#include "version.h"
16

17
#define TOKEN_NAME "systemd-tpm2"
18
#define TOKEN_VERSION_MAJOR "1"
19
#define TOKEN_VERSION_MINOR "0"
20

21
/* for libcryptsetup debug purpose */
22
_public_ const char *cryptsetup_token_version(void) {
63✔
23

24
        return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" PROJECT_VERSION_FULL " (" GIT_VERSION ")";
63✔
25
}
26

27
static int log_debug_open_error(struct crypt_device *cd, int r) {
28✔
28
        if (r == -EAGAIN)
28✔
UNCOV
29
                return crypt_log_debug_errno(cd, r, "TPM2 device not found.");
×
30
        if (r == -ENXIO)
28✔
UNCOV
31
                return crypt_log_debug_errno(cd, r, "No matching TPM2 token data found.");
×
32

33
        return crypt_log_debug_errno(cd, r, TOKEN_NAME " open failed: %m.");
28✔
34
}
35

36
_public_ int cryptsetup_token_open_pin(
57✔
37
                struct crypt_device *cd, /* is always LUKS2 context */
38
                int token /* is always >= 0 */,
39
                const char *pin,
40
                size_t pin_size,
41
                char **ret_password, /* freed by cryptsetup_token_buffer_free */
42
                size_t *ret_password_len,
43
                void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
44

45
        _cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL;
57✔
46
        _cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
57✔
UNCOV
47
        _cleanup_(iovec_done_erase) struct iovec decrypted_key = {};
×
48
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
57✔
49
        uint32_t hash_pcr_mask, pubkey_pcr_mask;
57✔
50
        systemd_tpm2_plugin_params params = {
57✔
51
                .search_pcr_mask = UINT32_MAX
52
        };
53
        uint16_t pcr_bank, primary_alg;
57✔
54
        ssize_t base64_encoded_size;
57✔
55
        TPM2Flags flags = 0;
57✔
56
        const char *json;
57✔
57
        int r;
57✔
58

59
        assert(token >= 0);
57✔
60
        assert(pin || pin_size == 0);
57✔
61
        assert(ret_password);
57✔
62
        assert(ret_password_len);
57✔
63

64
        /* This must not fail at this moment (internal error) */
65
        r = crypt_token_json_get(cd, token, &json);
57✔
66
        assert(token == r);
57✔
67
        assert(json);
57✔
68

69
        r = crypt_normalize_pin(pin, pin_size, &pin_string);
57✔
70
        if (r < 0)
57✔
UNCOV
71
                return crypt_log_debug_errno(cd, r, "Cannot normalize PIN: %m");
×
72

73
        if (usrptr)
57✔
74
                params = *(systemd_tpm2_plugin_params *)usrptr;
9✔
75

76
        r = sd_json_parse(json, 0, &v, NULL, NULL);
57✔
77
        if (r < 0)
57✔
UNCOV
78
                return crypt_log_debug_errno(cd, r, "Failed to parse token JSON data: %m");
×
79

80
        struct iovec *blobs = NULL, *policy_hash = NULL;
57✔
81
        size_t n_blobs = 0, n_policy_hash = 0;
57✔
82
        CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
57✔
83
        CLEANUP_ARRAY(policy_hash, n_policy_hash, iovec_array_free);
57✔
84

85
        r = tpm2_parse_luks2_json(
57✔
86
                        v,
87
                        /* ret_keyslot= */ NULL,
88
                        &hash_pcr_mask,
89
                        &pcr_bank,
90
                        &pubkey,
91
                        &pubkey_pcr_mask,
92
                        &primary_alg,
93
                        &blobs,
94
                        &n_blobs,
95
                        &policy_hash,
96
                        &n_policy_hash,
97
                        &salt,
98
                        &srk,
99
                        &pcrlock_nv,
100
                        &flags);
101
        if (r < 0)
57✔
UNCOV
102
                return log_debug_open_error(cd, r);
×
103

104
        if (params.search_pcr_mask != UINT32_MAX && hash_pcr_mask != params.search_pcr_mask)
57✔
UNCOV
105
                return crypt_log_debug_errno(cd, ENXIO, "PCR mask doesn't match expectation (%" PRIu32 " vs. %" PRIu32 ")", hash_pcr_mask, params.search_pcr_mask);
×
106

107
        r = acquire_luks2_key(
57✔
108
                        params.device,
109
                        hash_pcr_mask,
110
                        pcr_bank,
111
                        &pubkey,
112
                        pubkey_pcr_mask,
113
                        params.signature_path,
114
                        pin_string,
115
                        params.pcrlock_path,
116
                        primary_alg,
117
                        blobs,
118
                        n_blobs,
119
                        policy_hash,
120
                        n_policy_hash,
121
                        &salt,
122
                        &srk,
123
                        &pcrlock_nv,
124
                        flags,
125
                        &decrypted_key);
126
        if (r < 0)
57✔
127
                return log_debug_open_error(cd, r);
28✔
128

129
        /* Before using this key as passphrase we base64 encode it, for compat with homed */
130
        base64_encoded_size = base64mem(decrypted_key.iov_base, decrypted_key.iov_len, &base64_encoded);
29✔
131
        if (base64_encoded_size < 0)
29✔
UNCOV
132
                return log_debug_open_error(cd, base64_encoded_size);
×
133

134
        /* free'd automatically by libcryptsetup */
135
        *ret_password = TAKE_PTR(base64_encoded);
29✔
136
        *ret_password_len = base64_encoded_size;
29✔
137

138
        return 0;
29✔
139
}
140

141
/*
142
 * This function is called from within following libcryptsetup calls
143
 * provided conditions further below are met:
144
 *
145
 * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-tpm2'):
146
 *
147
 * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
148
 *   (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
149
 *    and token is assigned to at least single keyslot).
150
 *
151
 * - if plugin defines validate function (see cryptsetup_token_validate below) it must have
152
 *   passed the check (aka return 0)
153
 */
154
_public_ int cryptsetup_token_open(
47✔
155
                struct crypt_device *cd, /* is always LUKS2 context */
156
                int token /* is always >= 0 */,
157
                char **ret_password, /* freed by cryptsetup_token_buffer_free */
158
                size_t *ret_password_len,
159
                void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
160

161
        return cryptsetup_token_open_pin(cd, token, NULL, 0, ret_password, ret_password_len, usrptr);
47✔
162
}
163

164
/*
165
 * libcryptsetup callback for memory deallocation of 'password' parameter passed in
166
 * any crypt_token_open_* plugin function
167
 */
168
_public_ void cryptsetup_token_buffer_free(void *buffer, size_t buffer_len) {
29✔
169
        erase_and_free(buffer);
29✔
170
}
29✔
171

172
/*
173
 * prints systemd-tpm2 token content in crypt_dump().
174
 * 'type' and 'keyslots' fields are printed by libcryptsetup
175
 */
UNCOV
176
_public_ void cryptsetup_token_dump(
×
177
                struct crypt_device *cd /* is always LUKS2 context */,
178
                const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
179

180
        _cleanup_free_ char *hash_pcrs_str = NULL, *pubkey_pcrs_str = NULL, *pubkey_str = NULL;
×
181
        _cleanup_(iovec_done) struct iovec pubkey = {}, salt = {}, srk = {}, pcrlock_nv = {};
×
182
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
×
183
        uint32_t hash_pcr_mask, pubkey_pcr_mask;
×
184
        uint16_t pcr_bank, primary_alg;
×
185
        TPM2Flags flags = 0;
×
UNCOV
186
        int r;
×
187

UNCOV
188
        assert(json);
×
189

190
        r = sd_json_parse(json, 0, &v, NULL, NULL);
×
191
        if (r < 0)
×
UNCOV
192
                return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON object: %m");
×
193

194
        struct iovec *blobs = NULL, *policy_hash = NULL;
×
195
        size_t n_blobs = 0, n_policy_hash = 0;
×
196
        CLEANUP_ARRAY(blobs, n_blobs, iovec_array_free);
×
UNCOV
197
        CLEANUP_ARRAY(policy_hash, n_policy_hash, iovec_array_free);
×
198

UNCOV
199
        r = tpm2_parse_luks2_json(
×
200
                        v,
201
                        NULL,
202
                        &hash_pcr_mask,
203
                        &pcr_bank,
204
                        &pubkey,
205
                        &pubkey_pcr_mask,
206
                        &primary_alg,
207
                        &blobs,
208
                        &n_blobs,
209
                        &policy_hash,
210
                        &n_policy_hash,
211
                        &salt,
212
                        &srk,
213
                        &pcrlock_nv,
214
                        &flags);
215
        if (r < 0)
×
UNCOV
216
                return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON fields: %m");
×
217

218
        hash_pcrs_str = tpm2_pcr_mask_to_string(hash_pcr_mask);
×
219
        if (!hash_pcrs_str)
×
UNCOV
220
                return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m");
×
221

222
        pubkey_pcrs_str = tpm2_pcr_mask_to_string(pubkey_pcr_mask);
×
223
        if (!pubkey_pcrs_str)
×
UNCOV
224
                return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m");
×
225

226
        r = crypt_dump_buffer_to_hex_string(pubkey.iov_base, pubkey.iov_len, &pubkey_str);
×
227
        if (r < 0)
×
UNCOV
228
                return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
×
229

230
        crypt_log(cd, "\ttpm2-hash-pcrs:   %s\n", strna(hash_pcrs_str));
×
231
        crypt_log(cd, "\ttpm2-pcr-bank:    %s\n", strna(tpm2_hash_alg_to_string(pcr_bank)));
×
232
        crypt_log(cd, "\ttpm2-pubkey:" CRYPT_DUMP_LINE_SEP "%s\n", pubkey_str);
×
233
        crypt_log(cd, "\ttpm2-pubkey-pcrs: %s\n", strna(pubkey_pcrs_str));
×
234
        crypt_log(cd, "\ttpm2-primary-alg: %s\n", strna(tpm2_asym_alg_to_string(primary_alg)));
×
235
        crypt_log(cd, "\ttpm2-pin:         %s\n", true_false(flags & TPM2_FLAGS_USE_PIN));
×
236
        crypt_log(cd, "\ttpm2-pcrlock:     %s\n", true_false(flags & TPM2_FLAGS_USE_PCRLOCK));
×
237
        crypt_log(cd, "\ttpm2-salt:        %s\n", true_false(iovec_is_set(&salt)));
×
238
        crypt_log(cd, "\ttpm2-srk:         %s\n", true_false(iovec_is_set(&srk)));
×
UNCOV
239
        crypt_log(cd, "\ttpm2-pcrlock-nv:  %s\n", true_false(iovec_is_set(&pcrlock_nv)));
×
240

241
        FOREACH_ARRAY(p, policy_hash, n_policy_hash) {
×
UNCOV
242
                _cleanup_free_ char *policy_hash_str = NULL;
×
243

244
                r = crypt_dump_buffer_to_hex_string(p->iov_base, p->iov_len, &policy_hash_str);
×
245
                if (r < 0)
×
UNCOV
246
                        return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
×
247

UNCOV
248
                crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
×
249
        }
250

251
        FOREACH_ARRAY(b, blobs, n_blobs) {
×
UNCOV
252
                _cleanup_free_ char *blob_str = NULL;
×
253

254
                r = crypt_dump_buffer_to_hex_string(b->iov_base, b->iov_len, &blob_str);
×
255
                if (r < 0)
×
UNCOV
256
                        return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m");
×
257

UNCOV
258
                crypt_log(cd, "\ttpm2-blob:  %s\n", blob_str);
×
259
        }
260
}
261

262
/*
263
 * Note:
264
 *   If plugin is available in library path, it's called in before following libcryptsetup calls:
265
 *
266
 *   crypt_token_json_set, crypt_dump, any crypt_activate_by_token_* flavour
267
 */
268
_public_ int cryptsetup_token_validate(
82✔
269
                struct crypt_device *cd, /* is always LUKS2 context */
270
                const char *json /* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-tpm2' */) {
271

272
        int r;
82✔
273
        sd_json_variant *w, *e;
82✔
274
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
82✔
275

276
        assert(json);
82✔
277

278
        r = sd_json_parse(json, 0, &v, NULL, NULL);
82✔
279
        if (r < 0)
82✔
UNCOV
280
                return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m");
×
281

282
        w = sd_json_variant_by_key(v, "tpm2-pcrs");
82✔
283
        if (!w || !sd_json_variant_is_array(w)) {
82✔
UNCOV
284
                crypt_log_debug(cd, "TPM2 token data lacks 'tpm2-pcrs' field.");
×
285
                return 1;
286
        }
287

288
        JSON_VARIANT_ARRAY_FOREACH(e, w) {
139✔
289
                uint64_t u;
57✔
290

291
                if (!sd_json_variant_is_number(e)) {
57✔
292
                        crypt_log_debug(cd, "TPM2 PCR is not a number.");
×
UNCOV
293
                        return 1;
×
294
                }
295

296
                u = sd_json_variant_unsigned(e);
57✔
297
                if (!TPM2_PCR_INDEX_VALID(u)) {
57✔
UNCOV
298
                        crypt_log_debug(cd, "TPM2 PCR number out of range.");
×
299
                        return 1;
300
                }
301
        }
302

303
        /* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded
304
         * to SHA256. */
305
        w = sd_json_variant_by_key(v, "tpm2-pcr-bank");
82✔
306
        if (w) {
82✔
307
                /* The PCR bank field is optional */
308

309
                if (!sd_json_variant_is_string(w)) {
72✔
UNCOV
310
                        crypt_log_debug(cd, "TPM2 PCR bank is not a string.");
×
311
                        return 1;
312
                }
313

314
                if (tpm2_hash_alg_from_string(sd_json_variant_string(w)) < 0) {
72✔
UNCOV
315
                        crypt_log_debug(cd, "TPM2 PCR bank invalid or not supported: %s.", sd_json_variant_string(w));
×
316
                        return 1;
317
                }
318
        }
319

320
        /* The primary key algorithm field is optional, since it was also added in systemd 250 only. Before
321
         * the algorithm was hardcoded to ECC. */
322
        w = sd_json_variant_by_key(v, "tpm2-primary-alg");
82✔
323
        if (w) {
82✔
324
                /* The primary key algorithm is optional */
325

326
                if (!sd_json_variant_is_string(w)) {
×
UNCOV
327
                        crypt_log_debug(cd, "TPM2 primary key algorithm is not a string.");
×
328
                        return 1;
329
                }
330

331
                if (tpm2_asym_alg_from_string(sd_json_variant_string(w)) < 0) {
×
UNCOV
332
                        crypt_log_debug(cd, "TPM2 primary key algorithm invalid or not supported: %s", sd_json_variant_string(w));
×
333
                        return 1;
334
                }
335
        }
336

337
        w = sd_json_variant_by_key(v, "tpm2-blob");
82✔
338
        if (!w) {
82✔
UNCOV
339
                crypt_log_debug(cd, "TPM2 token data lacks 'tpm2-blob' field.");
×
340
                return 1;
341
        }
342

343
        if (sd_json_variant_is_array(w)) {
82✔
344
                sd_json_variant *i;
1✔
345
                JSON_VARIANT_ARRAY_FOREACH(i, w) {
3✔
346
                        r = sd_json_variant_unbase64(i, /* ret= */ NULL, /* ret_size= */ NULL);
2✔
347
                        if (r < 0)
2✔
UNCOV
348
                                return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-blob' field: %m");
×
349
                }
350
        } else {
351
                r = sd_json_variant_unbase64(w, /* ret= */ NULL, /* ret_size= */ NULL);
81✔
352
                if (r < 0)
81✔
UNCOV
353
                        return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-blob' field: %m");
×
354
        }
355

356
        w = sd_json_variant_by_key(v, "tpm2-policy-hash");
82✔
357
        if (!w) {
82✔
UNCOV
358
                crypt_log_debug(cd, "TPM2 token data lacks 'tpm2-policy-hash' field.");
×
359
                return 1;
360
        }
361

362
        if (sd_json_variant_is_array(w)) {
82✔
363
                sd_json_variant *i;
1✔
364
                JSON_VARIANT_ARRAY_FOREACH(i, w) {
3✔
365
                        r = sd_json_variant_unhex(i, /* ret= */ NULL, /* ret_size= */ NULL);
2✔
366
                        if (r < 0)
2✔
UNCOV
367
                                return crypt_log_debug_errno(cd, r, "Invalid hex data in 'tpm2-policy-hash' field: %m");
×
368
                }
369
        } else {
370
                r = sd_json_variant_unhex(w, /* ret= */ NULL, /* ret_size= */ NULL);
81✔
371
                if (r < 0)
81✔
UNCOV
372
                        return crypt_log_debug_errno(cd, r, "Invalid hex data in 'tpm2-policy-hash' field: %m");
×
373
        }
374

375
        w = sd_json_variant_by_key(v, "tpm2-pin");
82✔
376
        if (w) {
82✔
377
                if (!sd_json_variant_is_boolean(w)) {
29✔
UNCOV
378
                        crypt_log_debug(cd, "TPM2 PIN policy is not a boolean.");
×
379
                        return 1;
380
                }
381
        }
382

383
        return 0;
384
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc