]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #26180 from ddstreet/tpm2_cleanup
authorLuca Boccassi <bluca@debian.org>
Wed, 1 Feb 2023 23:40:52 +0000 (23:40 +0000)
committerGitHub <noreply@github.com>
Wed, 1 Feb 2023 23:40:52 +0000 (23:40 +0000)
Add struct tpm2_handle and use _cleanup_ instead of goto

src/boot/measure.c
src/boot/pcrphase.c
src/cryptsetup/cryptsetup.c
src/shared/tpm2-util.c
src/shared/tpm2-util.h

index b0bcfe0f983f8b023d71db6dbca15a519751bee3..f2fbd9d2c1cc689c12fe128d7bbf2848485680ba 100644 (file)
@@ -726,9 +726,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
         _cleanup_(EVP_PKEY_freep) EVP_PKEY *privkey = NULL, *pubkey = NULL;
-        _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
         _cleanup_fclose_ FILE *privkeyf = NULL;
-        ESYS_TR session_handle = ESYS_TR_NONE;
         TSS2_RC rc;
         size_t n;
         int r;
@@ -811,7 +809,8 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = tpm2_context_init(arg_tpm2_device, &c);
+        _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+        r = tpm2_context_new(arg_tpm2_device, &c);
         if (r < 0)
                 return r;
 
@@ -829,8 +828,13 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
                         };
                         PcrState *p = pcr_states + i;
 
+                        _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+                        r = tpm2_handle_new(c, &session);
+                        if (r < 0)
+                                return r;
+
                         rc = sym_Esys_StartAuthSession(
-                                        c.esys_context,
+                                        c->esys_context,
                                         ESYS_TR_NONE,
                                         ESYS_TR_NONE,
                                         ESYS_TR_NONE,
@@ -840,12 +844,10 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
                                         TPM2_SE_TRIAL,
                                         &symmetric,
                                         TPM2_ALG_SHA256,
-                                        &session_handle);
-                        if (rc != TSS2_RC_SUCCESS) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
-                                goto finish;
-                        }
+                                        &session->esys_handle);
+                        if (rc != TSS2_RC_SUCCESS)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                       "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
 
                         /* Generate a single hash value from the PCRs included in our policy. Given that that's
                          * exactly one, the calculation is trivial. */
@@ -856,94 +858,72 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
                         sha256_direct(p->value, p->value_size, intermediate_digest.buffer);
 
                         int tpmalg = tpm2_hash_alg_from_string(EVP_MD_name(p->md));
-                        if (tpmalg < 0) {
-                                log_error_errno(tpmalg, "Unsupported PCR bank");
-                                goto finish;
-                        }
+                        if (tpmalg < 0)
+                                return log_error_errno(tpmalg, "Unsupported PCR bank");
 
                         TPML_PCR_SELECTION pcr_selection;
                         tpm2_pcr_mask_to_selection(1 << TPM_PCR_INDEX_KERNEL_IMAGE, tpmalg, &pcr_selection);
 
                         rc = sym_Esys_PolicyPCR(
-                                        c.esys_context,
-                                        session_handle,
+                                        c->esys_context,
+                                        session->esys_handle,
                                         ESYS_TR_NONE,
                                         ESYS_TR_NONE,
                                         ESYS_TR_NONE,
                                         &intermediate_digest,
                                         &pcr_selection);
-                        if (rc != TSS2_RC_SUCCESS) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to push PCR policy into TPM: %s", sym_Tss2_RC_Decode(rc));
-                                goto finish;
-                        }
+                        if (rc != TSS2_RC_SUCCESS)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                       "Failed to push PCR policy into TPM: %s", sym_Tss2_RC_Decode(rc));
 
                         _cleanup_(Esys_Freep) TPM2B_DIGEST *pcr_policy_digest = NULL;
                         rc = sym_Esys_PolicyGetDigest(
-                                        c.esys_context,
-                                        session_handle,
+                                        c->esys_context,
+                                        session->esys_handle,
                                         ESYS_TR_NONE,
                                         ESYS_TR_NONE,
                                         ESYS_TR_NONE,
                                         &pcr_policy_digest);
-                        if (rc != TSS2_RC_SUCCESS) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
-                                goto finish;
-                        }
-
-                        session_handle = tpm2_flush_context_verbose(c.esys_context, session_handle);
+                        if (rc != TSS2_RC_SUCCESS)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                       "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
 
                         _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = NULL;
                         mdctx = EVP_MD_CTX_new();
-                        if (!mdctx) {
-                                r = log_oom();
-                                goto finish;
-                        }
+                        if (!mdctx)
+                                return log_oom();
 
-                        if (EVP_DigestSignInit(mdctx, NULL, p->md, NULL, privkey) != 1) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to initialize signature context.");
-                                goto finish;
-                        }
+                        if (EVP_DigestSignInit(mdctx, NULL, p->md, NULL, privkey) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                       "Failed to initialize signature context.");
 
-                        if (EVP_DigestSignUpdate(mdctx, pcr_policy_digest->buffer, pcr_policy_digest->size) != 1) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to sign data.");
-                                goto finish;
-                        }
+                        if (EVP_DigestSignUpdate(mdctx, pcr_policy_digest->buffer, pcr_policy_digest->size) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                       "Failed to sign data.");
 
                         size_t ss;
-                        if (EVP_DigestSignFinal(mdctx, NULL, &ss) != 1) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to finalize signature");
-                                goto finish;
-                        }
+                        if (EVP_DigestSignFinal(mdctx, NULL, &ss) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                       "Failed to finalize signature");
 
                         _cleanup_free_ void *sig = malloc(ss);
-                        if (!sig) {
-                                r = log_oom();
-                                goto finish;
-                        }
+                        if (!sig)
+                                return log_oom();
 
-                        if (EVP_DigestSignFinal(mdctx, sig, &ss) != 1) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to acquire signature data");
-                                goto finish;
-                        }
+                        if (EVP_DigestSignFinal(mdctx, sig, &ss) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                       "Failed to acquire signature data");
 
                         _cleanup_free_ void *pubkey_fp = NULL;
                         size_t pubkey_fp_size = 0;
                         r = pubkey_fingerprint(pubkey, EVP_sha256(), &pubkey_fp, &pubkey_fp_size);
                         if (r < 0)
-                                goto finish;
+                                return r;
 
                         _cleanup_(json_variant_unrefp) JsonVariant *a = NULL;
                         r = tpm2_make_pcr_json_array(UINT64_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE, &a);
-                        if (r < 0) {
-                                log_error_errno(r, "Failed to build JSON PCR mask array: %m");
-                                goto finish;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to build JSON PCR mask array: %m");
 
                         _cleanup_(json_variant_unrefp) JsonVariant *bv = NULL;
                         r = json_build(&bv, JSON_BUILD_OBJECT(
@@ -951,25 +931,19 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
                                                        JSON_BUILD_PAIR("pkfp", JSON_BUILD_HEX(pubkey_fp, pubkey_fp_size)),                         /* SHA256 fingerprint of public key (DER) used for the signature */
                                                        JSON_BUILD_PAIR("pol", JSON_BUILD_HEX(pcr_policy_digest->buffer, pcr_policy_digest->size)), /* TPM2 policy hash that is signed */
                                                        JSON_BUILD_PAIR("sig", JSON_BUILD_BASE64(sig, ss))));                                       /* signature data */
-                        if (r < 0) {
-                                log_error_errno(r, "Failed to build JSON object: %m");
-                                goto finish;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to build JSON object: %m");
 
                         _cleanup_(json_variant_unrefp) JsonVariant *av = NULL;
                         av = json_variant_ref(json_variant_by_key(v, p->bank));
 
                         r = json_variant_append_array_nodup(&av, bv);
-                        if (r < 0) {
-                                log_error_errno(r, "Failed to append JSON object: %m");
-                                goto finish;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to append JSON object: %m");
 
                         r = json_variant_set_field(&v, p->bank, av);
-                        if (r < 0) {
-                                log_error_errno(r, "Failed to add JSON field: %m");
-                                goto finish;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add JSON field: %m");
                 }
 
                 /* Return to the original kernel measurement for the next phase calculation */
@@ -980,11 +954,8 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
                 pager_open(arg_pager_flags);
 
         json_variant_dump(v, arg_json_format_flags, stdout, NULL);
-        r = 0;
 
-finish:
-        session_handle = tpm2_flush_context_verbose(c.esys_context, session_handle);
-        return r;
+        return 0;
 }
 
 static int compare_reported_pcr_nr(uint32_t pcr, const char *varname, const char *description) {
index 2add7f1f894bf7797f7541833f2f1f663f11b98c..05e9645de94220a6772a94f1defeb00fba883949 100644 (file)
@@ -155,7 +155,7 @@ static int parse_argv(int argc, char *argv[]) {
         return 1;
 }
 
-static int determine_banks(struct tpm2_context *c, unsigned target_pcr_nr) {
+static int determine_banks(Tpm2Context *c, unsigned target_pcr_nr) {
         _cleanup_strv_free_ char **l = NULL;
         int r;
 
@@ -164,7 +164,7 @@ static int determine_banks(struct tpm2_context *c, unsigned target_pcr_nr) {
         if (!strv_isempty(arg_banks)) /* Explicitly configured? Then use that */
                 return 0;
 
-        r = tpm2_get_good_pcr_banks_strv(c->esys_context, UINT32_C(1) << target_pcr_nr, &l);
+        r = tpm2_get_good_pcr_banks_strv(c, UINT32_C(1) << target_pcr_nr, &l);
         if (r < 0)
                 return r;
 
@@ -240,7 +240,6 @@ static int get_file_system_word(
 }
 
 static int run(int argc, char *argv[]) {
-        _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
         _cleanup_free_ char *joined = NULL, *word = NULL;
         unsigned target_pcr_nr;
         size_t length;
@@ -346,11 +345,12 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Failed to load TPM2 libraries: %m");
 
-        r = tpm2_context_init(arg_tpm2_device, &c);
+        _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+        r = tpm2_context_new(arg_tpm2_device, &c);
         if (r < 0)
                 return r;
 
-        r = determine_banks(&c, target_pcr_nr);
+        r = determine_banks(c, target_pcr_nr);
         if (r < 0)
                 return r;
         if (strv_isempty(arg_banks)) /* Still none? */
@@ -362,7 +362,7 @@ static int run(int argc, char *argv[]) {
 
         log_debug("Measuring '%s' into PCR index %u, banks %s.", word, target_pcr_nr, joined);
 
-        r = tpm2_extend_bytes(c.esys_context, arg_banks, target_pcr_nr, word, length, NULL, 0);
+        r = tpm2_extend_bytes(c, arg_banks, target_pcr_nr, word, length, NULL, 0);
         if (r < 0)
                 return r;
 
index 819d5bbb978217c0e6d4cd6e89f0c83f3068f26f..e7766055010ebd9e91944c2d77a6c88ccbc5d9dc 100644 (file)
@@ -841,14 +841,14 @@ static int measure_volume_key(
         if (r < 0)
                 return log_error_errno(r, "Failed to load TPM2 libraries: %m");
 
-        _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
-        r = tpm2_context_init(arg_tpm2_device, &c);
+        _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+        r = tpm2_context_new(arg_tpm2_device, &c);
         if (r < 0)
                 return r;
 
         _cleanup_strv_free_ char **l = NULL;
         if (strv_isempty(arg_tpm2_measure_banks)) {
-                r = tpm2_get_good_pcr_banks_strv(c.esys_context, UINT32_C(1) << arg_tpm2_measure_pcr, &l);
+                r = tpm2_get_good_pcr_banks_strv(c, UINT32_C(1) << arg_tpm2_measure_pcr, &l);
                 if (r < 0)
                         return r;
         }
@@ -871,7 +871,7 @@ static int measure_volume_key(
         if (!s)
                 return log_oom();
 
-        r = tpm2_extend_bytes(c.esys_context, l ?: arg_tpm2_measure_banks, arg_tpm2_measure_pcr, s, SIZE_MAX, volume_key, volume_key_size);
+        r = tpm2_extend_bytes(c, l ?: arg_tpm2_measure_banks, arg_tpm2_measure_pcr, s, SIZE_MAX, volume_key, volume_key_size);
         if (r < 0)
                 return r;
 
index 363e8df5cc87d22f7463e79b68e4cf4df2dae841..6552e66bf4ec0599e4712baea6b426dc0f23a240 100644 (file)
@@ -104,46 +104,33 @@ int dlopen_tpm2(void) {
                         DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Unmarshal));
 }
 
-void tpm2_context_destroy(struct tpm2_context *c) {
-        assert(c);
+static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
+        if (!c)
+                return NULL;
 
         if (c->esys_context)
                 sym_Esys_Finalize(&c->esys_context);
 
         c->tcti_context = mfree(c->tcti_context);
         c->tcti_dl = safe_dlclose(c->tcti_dl);
-}
 
-static inline void Esys_Finalize_wrapper(ESYS_CONTEXT **c) {
-        /* A wrapper around Esys_Finalize() for use with _cleanup_(). Only reasons we need this wrapper is
-         * because the function itself warn logs if we'd pass a pointer to NULL, and we don't want that. */
-        if (*c)
-                sym_Esys_Finalize(c);
+        return mfree(c);
 }
 
-ESYS_TR tpm2_flush_context_verbose(ESYS_CONTEXT *c, ESYS_TR handle) {
-        TSS2_RC rc;
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Tpm2Context, tpm2_context, tpm2_context_free);
 
-        if (!c || handle == ESYS_TR_NONE)
-                return ESYS_TR_NONE;
+int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
+        _cleanup_tpm2_context_ Tpm2Context *context = NULL;
+        TSS2_RC rc;
+        int r;
 
-        rc = sym_Esys_FlushContext(c, handle);
-        if (rc != TSS2_RC_SUCCESS) /* We ignore failures here (besides debug logging), since this is called
-                                    * in error paths, where we cannot do anything about failures anymore. And
-                                    * when it is called in successful codepaths by this time we already did
-                                    * what we wanted to do, and got the results we wanted so there's no
-                                    * reason to make this fail more loudly than necessary. */
-                log_debug("Failed to get flush context of TPM, ignoring: %s", sym_Tss2_RC_Decode(rc));
+        assert(ret_context);
 
-        return ESYS_TR_NONE;
-}
+        context = new0(Tpm2Context, 1);
+        if (!context)
+                return log_oom();
 
-int tpm2_context_init(const char *device, struct tpm2_context *ret) {
-        _cleanup_(Esys_Finalize_wrapper) ESYS_CONTEXT *c = NULL;
-        _cleanup_free_ TSS2_TCTI_CONTEXT *tcti = NULL;
-        _cleanup_(dlclosep) void *dl = NULL;
-        TSS2_RC rc;
-        int r;
+        context->n_ref = 1;
 
         r = dlopen_tpm2();
         if (r < 0)
@@ -192,11 +179,11 @@ int tpm2_context_init(const char *device, struct tpm2_context *ret) {
                 if (!filename_is_valid(fn))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 driver name '%s' not valid, refusing.", driver);
 
-                dl = dlopen(fn, RTLD_NOW);
-                if (!dl)
+                context->tcti_dl = dlopen(fn, RTLD_NOW);
+                if (!context->tcti_dl)
                         return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to load %s: %s", fn, dlerror());
 
-                func = dlsym(dl, TSS2_TCTI_INFO_SYMBOL);
+                func = dlsym(context->tcti_dl, TSS2_TCTI_INFO_SYMBOL);
                 if (!func)
                         return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                                "Failed to find TCTI info symbol " TSS2_TCTI_INFO_SYMBOL ": %s",
@@ -206,7 +193,6 @@ int tpm2_context_init(const char *device, struct tpm2_context *ret) {
                 if (!info)
                         return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to get TCTI info data.");
 
-
                 log_debug("Loaded TCTI module '%s' (%s) [Version %" PRIu32 "]", info->name, info->description, info->version);
 
                 rc = info->init(NULL, &sz, NULL);
@@ -214,22 +200,22 @@ int tpm2_context_init(const char *device, struct tpm2_context *ret) {
                         return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                                "Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
 
-                tcti = malloc0(sz);
-                if (!tcti)
+                context->tcti_context = malloc0(sz);
+                if (!context->tcti_context)
                         return log_oom();
 
-                rc = info->init(tcti, &sz, param);
+                rc = info->init(context->tcti_context, &sz, param);
                 if (rc != TPM2_RC_SUCCESS)
                         return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                                "Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
         }
 
-        rc = sym_Esys_Initialize(&c, tcti, NULL);
+        rc = sym_Esys_Initialize(&context->esys_context, context->tcti_context, NULL);
         if (rc != TSS2_RC_SUCCESS)
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                        "Failed to initialize TPM context: %s", sym_Tss2_RC_Decode(rc));
 
-        rc = sym_Esys_Startup(c, TPM2_SU_CLEAR);
+        rc = sym_Esys_Startup(context->esys_context, TPM2_SU_CLEAR);
         if (rc == TPM2_RC_INITIALIZE)
                 log_debug("TPM already started up.");
         else if (rc == TSS2_RC_SUCCESS)
@@ -238,18 +224,55 @@ int tpm2_context_init(const char *device, struct tpm2_context *ret) {
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                        "Failed to start up TPM: %s", sym_Tss2_RC_Decode(rc));
 
-        *ret = (struct tpm2_context) {
-                .esys_context = TAKE_PTR(c),
-                .tcti_context = TAKE_PTR(tcti),
-                .tcti_dl = TAKE_PTR(dl),
-        };
+        *ret_context = TAKE_PTR(context);
+
+        return 0;
+}
+
+static void tpm2_handle_flush(ESYS_CONTEXT *esys_context, ESYS_TR esys_handle) {
+        if (!esys_context || esys_handle == ESYS_TR_NONE)
+                return;
+
+        TSS2_RC rc = sym_Esys_FlushContext(esys_context, esys_handle);
+        if (rc != TSS2_RC_SUCCESS) /* We ignore failures here (besides debug logging), since this is called
+                                    * in error paths, where we cannot do anything about failures anymore. And
+                                    * when it is called in successful codepaths by this time we already did
+                                    * what we wanted to do, and got the results we wanted so there's no
+                                    * reason to make this fail more loudly than necessary. */
+                log_debug("Failed to flush TPM handle, ignoring: %s", sym_Tss2_RC_Decode(rc));
+}
+
+Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle) {
+        if (!handle)
+                return NULL;
+
+        _cleanup_tpm2_context_ Tpm2Context *context = (Tpm2Context*)handle->tpm2_context;
+        if (context)
+                tpm2_handle_flush(context->esys_context, handle->esys_handle);
+
+        return mfree(handle);
+}
+
+int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle) {
+        _cleanup_tpm2_handle_ Tpm2Handle *handle = NULL;
+
+        assert(ret_handle);
+
+        handle = new0(Tpm2Handle, 1);
+        if (!handle)
+                return log_oom();
+
+        handle->tpm2_context = tpm2_context_ref(context);
+        handle->esys_handle = ESYS_TR_NONE;
+
+        *ret_handle = TAKE_PTR(handle);
 
         return 0;
 }
 
 #define TPM2_CREDIT_RANDOM_FLAG_PATH "/run/systemd/tpm-rng-credited"
 
-static int tpm2_credit_random(ESYS_CONTEXT *c) {
+static int tpm2_credit_random(Tpm2Context *c) {
         size_t rps, done = 0;
         TSS2_RC rc;
         usec_t t;
@@ -276,7 +299,7 @@ static int tpm2_credit_random(ESYS_CONTEXT *c) {
                 _cleanup_(Esys_Freep) TPM2B_DIGEST *buffer = NULL;
 
                 rc = sym_Esys_GetRandom(
-                                c,
+                                c->esys_context,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
@@ -308,8 +331,8 @@ static int tpm2_credit_random(ESYS_CONTEXT *c) {
 }
 
 static int tpm2_make_primary(
-                ESYS_CONTEXT *c,
-                ESYS_TR *ret_primary,
+                Tpm2Context *c,
+                Tpm2Handle **ret_primary,
                 TPMI_ALG_PUBLIC alg,
                 TPMI_ALG_PUBLIC *ret_alg) {
 
@@ -351,9 +374,9 @@ static int tpm2_make_primary(
         };
 
         static const TPML_PCR_SELECTION creation_pcr = {};
-        ESYS_TR primary = ESYS_TR_NONE;
         TSS2_RC rc;
         usec_t ts;
+        int r;
 
         log_debug("Creating primary key on TPM.");
 
@@ -363,9 +386,14 @@ static int tpm2_make_primary(
 
         ts = now(CLOCK_MONOTONIC);
 
+        _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
+        r = tpm2_handle_new(c, &primary);
+        if (r < 0)
+                return r;
+
         if (IN_SET(alg, 0, TPM2_ALG_ECC)) {
                 rc = sym_Esys_CreatePrimary(
-                                c,
+                                c->esys_context,
                                 ESYS_TR_RH_OWNER,
                                 ESYS_TR_PASSWORD,
                                 ESYS_TR_NONE,
@@ -374,7 +402,7 @@ static int tpm2_make_primary(
                                 &primary_template_ecc,
                                 NULL,
                                 &creation_pcr,
-                                &primary,
+                                &primary->esys_handle,
                                 NULL,
                                 NULL,
                                 NULL,
@@ -394,7 +422,7 @@ static int tpm2_make_primary(
 
         if (IN_SET(alg, 0, TPM2_ALG_RSA)) {
                 rc = sym_Esys_CreatePrimary(
-                                c,
+                                c->esys_context,
                                 ESYS_TR_RH_OWNER,
                                 ESYS_TR_PASSWORD,
                                 ESYS_TR_NONE,
@@ -403,7 +431,7 @@ static int tpm2_make_primary(
                                 &primary_template_rsa,
                                 NULL,
                                 &creation_pcr,
-                                &primary,
+                                &primary->esys_handle,
                                 NULL,
                                 NULL,
                                 NULL,
@@ -422,7 +450,8 @@ static int tpm2_make_primary(
 
         log_debug("Generating primary key on TPM2 took %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - ts, USEC_PER_MSEC));
 
-        *ret_primary = primary;
+        if (ret_primary)
+                *ret_primary = TAKE_PTR(primary);
         if (ret_alg)
                 *ret_alg = alg;
 
@@ -470,7 +499,7 @@ static unsigned find_nth_bit(uint32_t mask, unsigned n) {
 }
 
 static int tpm2_pcr_mask_good(
-                ESYS_CONTEXT *c,
+                Tpm2Context *c,
                 TPMI_ALG_HASH bank,
                 uint32_t mask) {
 
@@ -488,7 +517,7 @@ static int tpm2_pcr_mask_good(
         tpm2_pcr_mask_to_selection(mask, bank, &selection);
 
         rc = sym_Esys_PCR_Read(
-                        c,
+                        c->esys_context,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
@@ -552,7 +581,7 @@ static int tpm2_bank_has24(const TPMS_PCR_SELECTION *selection) {
 }
 
 static int tpm2_get_best_pcr_bank(
-                ESYS_CONTEXT *c,
+                Tpm2Context *c,
                 uint32_t pcr_mask,
                 TPMI_ALG_HASH *ret) {
 
@@ -565,7 +594,7 @@ static int tpm2_get_best_pcr_bank(
         assert(c);
 
         rc = sym_Esys_GetCapability(
-                        c,
+                        c->esys_context,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
@@ -649,7 +678,7 @@ static int tpm2_get_best_pcr_bank(
 }
 
 int tpm2_get_good_pcr_banks(
-                ESYS_CONTEXT *c,
+                Tpm2Context *c,
                 uint32_t pcr_mask,
                 TPMI_ALG_HASH **ret) {
 
@@ -664,7 +693,7 @@ int tpm2_get_good_pcr_banks(
         assert(ret);
 
         rc = sym_Esys_GetCapability(
-                        c,
+                        c->esys_context,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
@@ -728,7 +757,7 @@ int tpm2_get_good_pcr_banks(
 }
 
 int tpm2_get_good_pcr_banks_strv(
-                ESYS_CONTEXT *c,
+                Tpm2Context *c,
                 uint32_t pcr_mask,
                 char ***ret) {
 
@@ -786,11 +815,11 @@ static void hash_pin(const char *pin, size_t len, TPM2B_AUTH *auth) {
 }
 
 static int tpm2_make_encryption_session(
-                ESYS_CONTEXT *c,
-                ESYS_TR primary,
-                ESYS_TR bind_key,
+                Tpm2Context *c,
+                const Tpm2Handle *primary,
+                const Tpm2Handle *bind_key,
                 const char *pin,
-                ESYS_TR *ret_session) {
+                Tpm2Handle **ret_session) {
 
         static const TPMT_SYM_DEF symmetric = {
                 .algorithm = TPM2_ALG_AES,
@@ -799,10 +828,11 @@ static int tpm2_make_encryption_session(
         };
         const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT |
                         TPMA_SESSION_CONTINUESESSION;
-        ESYS_TR session = ESYS_TR_NONE;
         TSS2_RC rc;
+        int r;
 
         assert(c);
+        assert(ret_session);
 
         /*
          * if a pin is set for the seal object, use it to bind the session
@@ -818,7 +848,7 @@ static int tpm2_make_encryption_session(
 
                 hash_pin(pin, strlen(pin), &auth);
 
-                rc = sym_Esys_TR_SetAuth(c, bind_key, &auth);
+                rc = sym_Esys_TR_SetAuth(c->esys_context, bind_key->esys_handle, &auth);
                 if (rc != TSS2_RC_SUCCESS)
                         return log_error_errno(
                                                SYNTHETIC_ERRNO(ENOTRECOVERABLE),
@@ -831,10 +861,15 @@ static int tpm2_make_encryption_session(
         /* Start a salted, unbound HMAC session with a well-known key (e.g. primary key) as tpmKey, which
          * means that the random salt will be encrypted with the well-known key. That way, only the TPM can
          * recover the salt, which is then used for key derivation. */
+        _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+        r = tpm2_handle_new(c, &session);
+        if (r < 0)
+                return r;
+
         rc = sym_Esys_StartAuthSession(
-                        c,
-                        primary,
-                        bind_key,
+                        c->esys_context,
+                        primary->esys_handle,
+                        bind_key->esys_handle,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
@@ -842,7 +877,7 @@ static int tpm2_make_encryption_session(
                         TPM2_SE_HMAC,
                         &symmetric,
                         TPM2_ALG_SHA256,
-                        &session);
+                        &session->esys_handle);
         if (rc != TSS2_RC_SUCCESS)
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                        "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
@@ -850,19 +885,15 @@ static int tpm2_make_encryption_session(
         /* Enable parameter encryption/decryption with AES in CFB mode. Together with HMAC digests (which are
          * always used for sessions), this provides confidentiality, integrity and replay protection for
          * operations that use this session. */
-        rc = sym_Esys_TRSess_SetAttributes(c, session, sessionAttributes, 0xff);
+        rc = sym_Esys_TRSess_SetAttributes(c->esys_context, session->esys_handle, sessionAttributes, 0xff);
         if (rc != TSS2_RC_SUCCESS)
                 return log_error_errno(
                                 SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                 "Failed to configure TPM session: %s",
                                 sym_Tss2_RC_Decode(rc));
 
-        if (ret_session) {
-                *ret_session = session;
-                session = ESYS_TR_NONE;
-        }
+        *ret_session = TAKE_PTR(session);
 
-        session = tpm2_flush_context_verbose(c, session);
         return 0;
 }
 
@@ -1049,9 +1080,9 @@ static int find_signature(
 #endif
 
 static int tpm2_make_policy_session(
-                ESYS_CONTEXT *c,
-                ESYS_TR primary,
-                ESYS_TR parent_session,
+                Tpm2Context *c,
+                const Tpm2Handle *primary,
+                const Tpm2Handle *parent_session,
                 TPM2_SE session_type,
                 uint32_t hash_pcr_mask,
                 uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
@@ -1060,7 +1091,7 @@ static int tpm2_make_policy_session(
                 uint32_t pubkey_pcr_mask,
                 JsonVariant *signature_json,
                 bool use_pin,
-                ESYS_TR *ret_session,
+                Tpm2Handle **ret_session,
                 TPM2B_DIGEST **ret_policy_digest,
                 TPMI_ALG_HASH *ret_pcr_bank) {
 
@@ -1070,7 +1101,6 @@ static int tpm2_make_policy_session(
                 .mode.aes = TPM2_ALG_CFB,
         };
         _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
-        ESYS_TR session = ESYS_TR_NONE, pubkey_handle = ESYS_TR_NONE;
         TSS2_RC rc;
         int r;
 
@@ -1125,18 +1155,23 @@ static int tpm2_make_policy_session(
         }
 #endif
 
+        _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+        r = tpm2_handle_new(c, &session);
+        if (r < 0)
+                return r;
+
         rc = sym_Esys_StartAuthSession(
-                        c,
-                        primary,
+                        c->esys_context,
+                        primary->esys_handle,
                         ESYS_TR_NONE,
-                        parent_session,
+                        parent_session->esys_handle,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
                         NULL,
                         session_type,
                         &symmetric,
                         TPM2_ALG_SHA256,
-                        &session);
+                        &session->esys_handle);
         if (rc != TSS2_RC_SUCCESS)
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                        "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
@@ -1149,10 +1184,15 @@ static int tpm2_make_policy_session(
                 TPM2B_PUBLIC pubkey_tpm2;
                 r = openssl_pubkey_to_tpm2_pubkey(pk, &pubkey_tpm2);
                 if (r < 0)
-                        goto finish;
+                        return r;
+
+                _cleanup_tpm2_handle_ Tpm2Handle *pubkey_handle = NULL;
+                r = tpm2_handle_new(c, &pubkey_handle);
+                if (r < 0)
+                        return r;
 
                 rc = sym_Esys_LoadExternal(
-                                c,
+                                c->esys_context,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
@@ -1165,56 +1205,48 @@ static int tpm2_make_policy_session(
 #else
                                 TPM2_RH_OWNER,
 #endif
-                                &pubkey_handle);
-                if (rc != TSS2_RC_SUCCESS) {
-                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                            "Failed to load public key into TPM: %s", sym_Tss2_RC_Decode(rc));
-                        goto finish;
-                }
+                                &pubkey_handle->esys_handle);
+                if (rc != TSS2_RC_SUCCESS)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                "Failed to load public key into TPM: %s", sym_Tss2_RC_Decode(rc));
 
                 /* Acquire the "name" of what we just loaded */
                 _cleanup_(Esys_Freep) TPM2B_NAME *pubkey_name = NULL;
                 rc = sym_Esys_TR_GetName(
-                                c,
-                                pubkey_handle,
+                                c->esys_context,
+                                pubkey_handle->esys_handle,
                                 &pubkey_name);
-                if (rc != TSS2_RC_SUCCESS) {
-                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                            "Failed to get name of public key from TPM: %s", sym_Tss2_RC_Decode(rc));
-                        goto finish;
-                }
+                if (rc != TSS2_RC_SUCCESS)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                               "Failed to get name of public key from TPM: %s", sym_Tss2_RC_Decode(rc));
 
                 /* Put together the PCR policy we want to use */
                 TPML_PCR_SELECTION pcr_selection;
                 tpm2_pcr_mask_to_selection(pubkey_pcr_mask, pcr_bank, &pcr_selection);
                 rc = sym_Esys_PolicyPCR(
-                                c,
-                                session,
+                                c->esys_context,
+                                session->esys_handle,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 NULL,
                                 &pcr_selection);
-                if (rc != TSS2_RC_SUCCESS) {
-                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                            "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
-                        goto finish;
-                }
+                if (rc != TSS2_RC_SUCCESS)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                               "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
 
                 /* Get the policy hash of the PCR policy */
                 _cleanup_(Esys_Freep) TPM2B_DIGEST *approved_policy = NULL;
                 rc = sym_Esys_PolicyGetDigest(
-                                c,
-                                session,
+                                c->esys_context,
+                                session->esys_handle,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 &approved_policy);
-                if (rc != TSS2_RC_SUCCESS) {
-                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                            "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
-                        goto finish;
-                }
+                if (rc != TSS2_RC_SUCCESS)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                               "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
 
                 /* When we are unlocking and have a signature, let's pass it to the TPM */
                 _cleanup_(Esys_Freep) TPMT_TK_VERIFIED *check_ticket_buffer = NULL;
@@ -1233,7 +1265,7 @@ static int tpm2_make_policy_session(
                                         &signature_raw,
                                         &signature_size);
                         if (r < 0)
-                                goto finish;
+                                return r;
 
                         /* TPM2_VerifySignature() will only verify the RSA part of the RSA+SHA256 signature,
                          * hence we need to do the SHA256 part ourselves, first */
@@ -1250,26 +1282,22 @@ static int tpm2_make_policy_session(
                                         .sig.size = signature_size,
                                 },
                         };
-                        if (signature_size > sizeof(policy_signature.signature.rsassa.sig.buffer)) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Signature larger than buffer.");
-                                goto finish;
-                        }
+                        if (signature_size > sizeof(policy_signature.signature.rsassa.sig.buffer))
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Signature larger than buffer.");
                         memcpy(policy_signature.signature.rsassa.sig.buffer, signature_raw, signature_size);
 
                         rc = sym_Esys_VerifySignature(
-                                        c,
-                                        pubkey_handle,
+                                        c->esys_context,
+                                        pubkey_handle->esys_handle,
                                         ESYS_TR_NONE,
                                         ESYS_TR_NONE,
                                         ESYS_TR_NONE,
                                         &signature_hash,
                                         &policy_signature,
                                         &check_ticket_buffer);
-                        if (rc != TSS2_RC_SUCCESS) {
-                                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                                    "Failed to validate signature in TPM: %s", sym_Tss2_RC_Decode(rc));
-                                goto finish;
-                        }
+                        if (rc != TSS2_RC_SUCCESS)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                                        "Failed to validate signature in TPM: %s", sym_Tss2_RC_Decode(rc));
 
                         check_ticket = check_ticket_buffer;
                 } else {
@@ -1283,8 +1311,8 @@ static int tpm2_make_policy_session(
                 }
 
                 rc = sym_Esys_PolicyAuthorize(
-                                c,
-                                session,
+                                c->esys_context,
+                                session->esys_handle,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
@@ -1292,11 +1320,9 @@ static int tpm2_make_policy_session(
                                 /* policyRef= */ &(const TPM2B_NONCE) {},
                                 pubkey_name,
                                 check_ticket);
-                if (rc != TSS2_RC_SUCCESS) {
-                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                            "Failed to push Authorize policy into TPM: %s", sym_Tss2_RC_Decode(rc));
-                        goto finish;
-                }
+                if (rc != TSS2_RC_SUCCESS)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                               "Failed to push Authorize policy into TPM: %s", sym_Tss2_RC_Decode(rc));
 #else
                 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled.");
 #endif
@@ -1308,71 +1334,61 @@ static int tpm2_make_policy_session(
                 TPML_PCR_SELECTION pcr_selection;
                 tpm2_pcr_mask_to_selection(hash_pcr_mask, pcr_bank, &pcr_selection);
                 rc = sym_Esys_PolicyPCR(
-                                c,
-                                session,
+                                c->esys_context,
+                                session->esys_handle,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 NULL,
                                 &pcr_selection);
-                if (rc != TSS2_RC_SUCCESS) {
-                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                            "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
-                        goto finish;
-                }
+                if (rc != TSS2_RC_SUCCESS)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                               "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
         }
 
         if (use_pin) {
                 log_debug("Configuring PIN policy.");
 
                 rc = sym_Esys_PolicyAuthValue(
-                                c,
-                                session,
+                                c->esys_context,
+                                session->esys_handle,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE);
-                if (rc != TSS2_RC_SUCCESS) {
-                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                            "Failed to add authValue policy to TPM: %s",
-                                            sym_Tss2_RC_Decode(rc));
-                        goto finish;
-                }
+                if (rc != TSS2_RC_SUCCESS)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                               "Failed to add authValue policy to TPM: %s",
+                                               sym_Tss2_RC_Decode(rc));
         }
 
         if (DEBUG_LOGGING || ret_policy_digest) {
                 log_debug("Acquiring policy digest.");
 
                 rc = sym_Esys_PolicyGetDigest(
-                                c,
-                                session,
+                                c->esys_context,
+                                session->esys_handle,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 ESYS_TR_NONE,
                                 &policy_digest);
 
-                if (rc != TSS2_RC_SUCCESS) {
-                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                            "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
-                        goto finish;
-                }
+                if (rc != TSS2_RC_SUCCESS)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                               "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
 
                 if (DEBUG_LOGGING) {
                         _cleanup_free_ char *h = NULL;
 
                         h = hexmem(policy_digest->buffer, policy_digest->size);
-                        if (!h) {
-                                r = log_oom();
-                                goto finish;
-                        }
+                        if (!h)
+                                return log_oom();
 
                         log_debug("Session policy digest: %s", h);
                 }
         }
 
-        if (ret_session) {
-                *ret_session = session;
-                session = ESYS_TR_NONE;
-        }
+        if (ret_session)
+                *ret_session = TAKE_PTR(session);
 
         if (ret_policy_digest)
                 *ret_policy_digest = TAKE_PTR(policy_digest);
@@ -1380,12 +1396,7 @@ static int tpm2_make_policy_session(
         if (ret_pcr_bank)
                 *ret_pcr_bank = pcr_bank;
 
-        r = 0;
-
-finish:
-        session = tpm2_flush_context_verbose(c, session);
-        pubkey_handle = tpm2_flush_context_verbose(c, pubkey_handle);
-        return r;
+        return 0;
 }
 
 int tpm2_seal(const char *device,
@@ -1403,14 +1414,12 @@ int tpm2_seal(const char *device,
               uint16_t *ret_pcr_bank,
               uint16_t *ret_primary_alg) {
 
-        _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
         _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
         _cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
         _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
         static const TPML_PCR_SELECTION creation_pcr = {};
         _cleanup_(erase_and_freep) void *secret = NULL;
         _cleanup_free_ void *blob = NULL, *hash = NULL;
-        ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE;
         TPM2B_SENSITIVE_CREATE hmac_sensitive;
         TPMI_ALG_PUBLIC primary_alg;
         TPM2B_PUBLIC hmac_template;
@@ -1453,21 +1462,24 @@ int tpm2_seal(const char *device,
 
         CLEANUP_ERASE(hmac_sensitive);
 
-        r = tpm2_context_init(device, &c);
+        _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+        r = tpm2_context_new(device, &c);
         if (r < 0)
                 return r;
 
-        r = tpm2_make_primary(c.esys_context, &primary, 0, &primary_alg);
+        _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
+        r = tpm2_make_primary(c, &primary, 0, &primary_alg);
         if (r < 0)
                 return r;
 
         /* we cannot use the bind key before its created */
-        r = tpm2_make_encryption_session(c.esys_context, primary, ESYS_TR_NONE, NULL, &session);
+        _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+        r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, NULL, &session);
         if (r < 0)
-                goto finish;
+                return r;
 
         r = tpm2_make_policy_session(
-                        c.esys_context,
+                        c,
                         primary,
                         session,
                         TPM2_SE_TRIAL,
@@ -1481,7 +1493,7 @@ int tpm2_seal(const char *device,
                         &policy_digest,
                         &pcr_bank);
         if (r < 0)
-                goto finish;
+                return r;
 
         /* We use a keyed hash object (i.e. HMAC) to store the secret key we want to use for unlocking the
          * LUKS2 volume with. We don't ever use for HMAC/keyed hash operations however, we just use it
@@ -1507,22 +1519,20 @@ int tpm2_seal(const char *device,
 
         assert(sizeof(hmac_sensitive.sensitive.data.buffer) >= hmac_sensitive.sensitive.data.size);
 
-        (void) tpm2_credit_random(c.esys_context);
+        (void) tpm2_credit_random(c);
 
         log_debug("Generating secret key data.");
 
         r = crypto_random_bytes(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
-        if (r < 0) {
-                log_error_errno(r, "Failed to generate secret key: %m");
-                goto finish;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate secret key: %m");
 
         log_debug("Creating HMAC key.");
 
         rc = sym_Esys_Create(
-                        c.esys_context,
-                        primary,
-                        session, /* use HMAC session to enable parameter encryption */
+                        c->esys_context,
+                        primary->esys_handle,
+                        session->esys_handle, /* use HMAC session to enable parameter encryption */
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
                         &hmac_sensitive,
@@ -1534,17 +1544,13 @@ int tpm2_seal(const char *device,
                         NULL,
                         NULL,
                         NULL);
-        if (rc != TSS2_RC_SUCCESS) {
-                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                    "Failed to generate HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
-                goto finish;
-        }
+        if (rc != TSS2_RC_SUCCESS)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                       "Failed to generate HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
 
         secret = memdup(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
-        if (!secret) {
-                r = log_oom();
-                goto finish;
-        }
+        if (!secret)
+                return log_oom();
 
         log_debug("Marshalling private and public part of HMAC key.");
 
@@ -1554,10 +1560,8 @@ int tpm2_seal(const char *device,
                 size_t offset = 0;
 
                 buf = malloc(k);
-                if (!buf) {
-                        r = log_oom();
-                        goto finish;
-                }
+                if (!buf)
+                        return log_oom();
 
                 rc = sym_Tss2_MU_TPM2B_PRIVATE_Marshal(private, buf, k, &offset);
                 if (rc == TSS2_RC_SUCCESS) {
@@ -1568,16 +1572,12 @@ int tpm2_seal(const char *device,
                                 break;
                         }
                 }
-                if (rc != TSS2_MU_RC_INSUFFICIENT_BUFFER) {
-                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                            "Failed to marshal private/public key: %s", sym_Tss2_RC_Decode(rc));
-                        goto finish;
-                }
+                if (rc != TSS2_MU_RC_INSUFFICIENT_BUFFER)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                               "Failed to marshal private/public key: %s", sym_Tss2_RC_Decode(rc));
 
-                if (k > SIZE_MAX / 2) {
-                        r = log_oom();
-                        goto finish;
-                }
+                if (k > SIZE_MAX / 2)
+                        return log_oom();
 
                 k *= 2;
         }
@@ -1598,12 +1598,7 @@ int tpm2_seal(const char *device,
         *ret_pcr_bank = pcr_bank;
         *ret_primary_alg = primary_alg;
 
-        r = 0;
-
-finish:
-        primary = tpm2_flush_context_verbose(c.esys_context, primary);
-        session = tpm2_flush_context_verbose(c.esys_context, session);
-        return r;
+        return 0;
 }
 
 #define RETRY_UNSEAL_MAX 30u
@@ -1624,9 +1619,6 @@ int tpm2_unseal(const char *device,
                 void **ret_secret,
                 size_t *ret_secret_size) {
 
-        _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
-        ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE, hmac_session = ESYS_TR_NONE,
-                hmac_key = ESYS_TR_NONE;
         _cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
         _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
         _cleanup_(erase_and_freep) char *secret = NULL;
@@ -1675,11 +1667,13 @@ int tpm2_unseal(const char *device,
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                        "Failed to unmarshal public key: %s", sym_Tss2_RC_Decode(rc));
 
-        r = tpm2_context_init(device, &c);
+        _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+        r = tpm2_context_new(device, &c);
         if (r < 0)
                 return r;
 
-        r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL);
+        _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
+        r = tpm2_make_primary(c, &primary, primary_alg, NULL);
         if (r < 0)
                 return r;
 
@@ -1691,37 +1685,43 @@ int tpm2_unseal(const char *device,
          * is provided. If an attacker gives back a bad key, we already lost since
          * primary key is not verified and they could attack there as well.
          */
+        _cleanup_tpm2_handle_ Tpm2Handle *hmac_key = NULL;
+        r = tpm2_handle_new(c, &hmac_key);
+        if (r < 0)
+                return r;
+
         rc = sym_Esys_Load(
-                        c.esys_context,
-                        primary,
+                        c->esys_context,
+                        primary->esys_handle,
                         ESYS_TR_PASSWORD,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
                         &private,
                         &public,
-                        &hmac_key);
+                        &hmac_key->esys_handle);
         if (rc != TSS2_RC_SUCCESS) {
                 /* If we're in dictionary attack lockout mode, we should see a lockout error here, which we
                  * need to translate for the caller. */
                 if (rc == TPM2_RC_LOCKOUT)
-                        r = log_error_errno(
+                        return log_error_errno(
                                         SYNTHETIC_ERRNO(ENOLCK),
                                         "TPM2 device is in dictionary attack lockout mode.");
                 else
-                        r = log_error_errno(
+                        return log_error_errno(
                                         SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                         "Failed to load HMAC key in TPM: %s",
                                         sym_Tss2_RC_Decode(rc));
-                goto finish;
         }
 
-        r = tpm2_make_encryption_session(c.esys_context, primary, hmac_key, pin, &hmac_session);
+        _cleanup_tpm2_handle_ Tpm2Handle *hmac_session = NULL;
+        r = tpm2_make_encryption_session(c, primary, hmac_key, pin, &hmac_session);
         if (r < 0)
-                goto finish;
+                return r;
 
         for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
+                _cleanup_tpm2_handle_ Tpm2Handle *policy_session = NULL;
                 r = tpm2_make_policy_session(
-                                c.esys_context,
+                                c,
                                 primary,
                                 hmac_session,
                                 TPM2_SE_POLICY,
@@ -1731,11 +1731,11 @@ int tpm2_unseal(const char *device,
                                 pubkey_pcr_mask,
                                 signature,
                                 !!pin,
-                                &session,
+                                &policy_session,
                                 &policy_digest,
                                 /* ret_pcr_bank= */ NULL);
                 if (r < 0)
-                        goto finish;
+                        return r;
 
                 /* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
                  * wait until the TPM2 tells us to go away. */
@@ -1748,32 +1748,24 @@ int tpm2_unseal(const char *device,
                 log_debug("Unsealing HMAC key.");
 
                 rc = sym_Esys_Unseal(
-                                c.esys_context,
-                                hmac_key,
-                                session,
-                                hmac_session, /* use HMAC session to enable parameter encryption */
+                                c->esys_context,
+                                hmac_key->esys_handle,
+                                policy_session->esys_handle,
+                                hmac_session->esys_handle, /* use HMAC session to enable parameter encryption */
                                 ESYS_TR_NONE,
                                 &unsealed);
-                if (rc == TPM2_RC_PCR_CHANGED && i > 0) {
-                        log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
-                        session = tpm2_flush_context_verbose(c.esys_context, session);
-                        continue;
-                }
-                if (rc != TSS2_RC_SUCCESS) {
-                        r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
-                                            "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
-                        goto finish;
-                }
-
-                break;
+                if (rc == TSS2_RC_SUCCESS)
+                        break;
+                if (rc != TPM2_RC_PCR_CHANGED || i == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                               "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
+                log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
         }
 
         secret = memdup(unsealed->buffer, unsealed->size);
         explicit_bzero_safe(unsealed->buffer, unsealed->size);
-        if (!secret) {
-                r = log_oom();
-                goto finish;
-        }
+        if (!secret)
+                return log_oom();
 
         if (DEBUG_LOGGING)
                 log_debug("Completed TPM2 key unsealing in %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - start, 1));
@@ -1781,13 +1773,7 @@ int tpm2_unseal(const char *device,
         *ret_secret = TAKE_PTR(secret);
         *ret_secret_size = unsealed->size;
 
-        r = 0;
-
-finish:
-        primary = tpm2_flush_context_verbose(c.esys_context, primary);
-        session = tpm2_flush_context_verbose(c.esys_context, session);
-        hmac_key = tpm2_flush_context_verbose(c.esys_context, hmac_key);
-        return r;
+        return 0;
 }
 
 #endif
@@ -1918,7 +1904,7 @@ int tpm2_find_device_auto(
 
 #if HAVE_TPM2
 int tpm2_extend_bytes(
-                ESYS_CONTEXT *c,
+                Tpm2Context *c,
                 char **banks,
                 unsigned pcr_index,
                 const void *data,
@@ -1979,7 +1965,7 @@ int tpm2_extend_bytes(
         }
 
         rc = sym_Esys_PCR_Extend(
-                        c,
+                        c->esys_context,
                         ESYS_TR_PCR0 + pcr_index,
                         ESYS_TR_PASSWORD,
                         ESYS_TR_NONE,
index 99b258c2900274fbc1d715baa76ddb10cf83d254..d26a945a90739bd0ae8702d0c110a2212a230b8d 100644 (file)
@@ -53,13 +53,32 @@ int dlopen_tpm2(void);
 int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg);
 int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, void **ret_secret, size_t *ret_secret_size);
 
-struct tpm2_context {
+typedef struct {
+        unsigned n_ref;
+
         void *tcti_dl;
         TSS2_TCTI_CONTEXT *tcti_context;
         ESYS_CONTEXT *esys_context;
-};
+} Tpm2Context;
+
+int tpm2_context_new(const char *device, Tpm2Context **ret_context);
+Tpm2Context *tpm2_context_ref(Tpm2Context *context);
+Tpm2Context *tpm2_context_unref(Tpm2Context *context);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Context*, tpm2_context_unref);
+#define _cleanup_tpm2_context_ _cleanup_(tpm2_context_unrefp)
+
+typedef struct {
+        Tpm2Context *tpm2_context;
+        ESYS_TR esys_handle;
+} Tpm2Handle;
+
+#define _tpm2_handle(c, h) { .tpm2_context = (c), .esys_handle = (h), }
+static const Tpm2Handle TPM2_HANDLE_NONE = _tpm2_handle(NULL, ESYS_TR_NONE);
 
-ESYS_TR tpm2_flush_context_verbose(ESYS_CONTEXT *c, ESYS_TR handle);
+int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle);
+Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
+#define _cleanup_tpm2_handle_ _cleanup_(tpm2_handle_freep)
 
 void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret);
 
@@ -68,17 +87,15 @@ static inline void Esys_Freep(void *p) {
                 sym_Esys_Free(*(void**) p);
 }
 
-int tpm2_get_good_pcr_banks(ESYS_CONTEXT *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
-int tpm2_get_good_pcr_banks_strv(ESYS_CONTEXT *c, uint32_t pcr_mask, char ***ret);
+int tpm2_get_good_pcr_banks(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
+int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret);
 
-int tpm2_extend_bytes(ESYS_CONTEXT *c, char **banks, unsigned pcr_index, const void *data, size_t data_size, const void *secret, size_t secret_size);
-
-#else
-struct tpm2_context;
-#endif
+int tpm2_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const void *data, size_t data_size, const void *secret, size_t secret_size);
 
-int tpm2_context_init(const char *device, struct tpm2_context *ret);
-void tpm2_context_destroy(struct tpm2_context *c);
+#else /* HAVE_TPM2 */
+typedef struct {} Tpm2Context;
+typedef struct {} Tpm2Handle;
+#endif /* HAVE_TPM2 */
 
 int tpm2_list_devices(void);
 int tpm2_find_device_auto(int log_level, char **ret);