libtpmtss: Protect TPM 2.0 context by mutex
authorkrinfels <kornel@duleba.com.pl>
Wed, 26 Jun 2019 13:32:29 +0000 (15:32 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Wed, 26 Jun 2019 14:30:01 +0000 (16:30 +0200)
Each private key object created to access a key residing in a TPM 2.0
creates a context structure used for communication with the TSS.
When multiple IKE SAs are established at the same time and using the
same private key, it is possible to make concurrent calls to the
TSS with the same context which results in multiple threads writing
to the same place in memory causing undefined behaviour.

Fix this by protecting calls to the TSS with a mutex unique for
each TPM 2.0 context object.

src/libtpmtss/tpm_tss_tss2_v1.c
src/libtpmtss/tpm_tss_tss2_v2.c

index 1c214af..0335fab 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2018 Tobias Brunner
- * Copyright (C) 2016-2018 Andreas Steffen
+ * Copyright (C) 2016-2019 Andreas Steffen
  * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -22,6 +22,7 @@
 #include <asn1/asn1.h>
 #include <asn1/oid.h>
 #include <bio/bio_reader.h>
+#include <threading/mutex.h>
 
 #include <tpm20.h>
 
@@ -75,6 +76,11 @@ struct private_tpm_tss_tss2_t {
         */
        bool fips_186_4;
 
+       /**
+        * Mutex controlling access to the TPM 2.0 context
+        */
+       mutex_t *mutex;
+
 };
 
 /**
@@ -168,8 +174,10 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this)
        int written;
 
        /* get fixed properties */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM_CAP_TPM_PROPERTIES,
                                                PT_FIXED, MAX_TPM_PROPERTIES, &more_data, &cap_data, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s GetCapability failed for TPM_CAP_TPM_PROPERTIES: 0x%06x",
@@ -222,8 +230,10 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this)
                 fips_140_2 ? "FIPS 140-2" : (this->fips_186_4 ? "FIPS 186-4" : ""));
 
        /* get supported algorithms */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM_CAP_ALGS,
                                                0, TPM_PT_ALGORITHM_SET, &more_data, &cap_data, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s GetCapability failed for TPM_CAP_ALGS: 0x%06x",
@@ -251,8 +261,10 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this)
        DBG2(DBG_PTS, "%s algorithms:%s", LABEL, buf);
 
        /* get supported ECC curves */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM_CAP_ECC_CURVES,
                                                0, TPM_PT_LOADED_CURVES, &more_data, &cap_data, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s GetCapability failed for TPM_ECC_CURVES: 0x%06x",
@@ -440,8 +452,10 @@ bool read_public(private_tpm_tss_tss2_t *this, TPMI_DH_OBJECT handle,
        sessions_data.rspAuthsCount = 1;
 
        /* read public key for a given object handle from TPM 2.0 NVRAM */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_ReadPublic(this->sys_context, handle, 0, public, &name,
                                                           &qualified_name, &sessions_data);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s could not read public key from handle 0x%08x: 0x%06x",
@@ -704,8 +718,10 @@ METHOD(tpm_tss_t, read_pcr, bool,
        memset(&pcr_values, 0, sizeof(TPML_DIGEST));
 
        /* read the PCR value */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_PCR_Read(this->sys_context, 0, &pcr_selection,
                                &pcr_update_counter, &pcr_selection, &pcr_values, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s PCR bank could not be read: 0x%60x",
@@ -800,8 +816,10 @@ METHOD(tpm_tss_t, extend_pcr, bool,
        }
 
        /* extend PCR */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_PCR_Extend(this->sys_context, pcr_num, &sessions_data_cmd,
                                                           &digest_values, &sessions_data_rsp);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s PCR %02u could not be extended: 0x%06x",
@@ -866,9 +884,11 @@ METHOD(tpm_tss_t, quote, bool,
                return FALSE;
        }
 
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_Quote(this->sys_context, aik_handle, &sessions_data_cmd,
                                                  &qualifying_data, &scheme, &pcr_selection,  &quoted,
                                                  &sig, &sessions_data_rsp);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM_RC_SUCCESS)
        {
                DBG1(DBG_PTS,"%s Tss2_Sys_Quote failed: 0x%06x", LABEL, rval);
@@ -1047,8 +1067,10 @@ METHOD(tpm_tss_t, sign, bool,
                memcpy(buffer.t.buffer, data.ptr, data.len);
                buffer.t.size = data.len;
 
+               this->mutex->lock(this->mutex);
                rval = Tss2_Sys_Hash(this->sys_context, 0, &buffer, alg_id, hierarchy,
                                                         &hash, &validation, 0);
+               this->mutex->unlock(this->mutex);
                if (rval != TPM_RC_SUCCESS)
                {
                        DBG1(DBG_PTS,"%s Tss2_Sys_Hash failed: 0x%06x", LABEL, rval);
@@ -1061,12 +1083,14 @@ METHOD(tpm_tss_t, sign, bool,
            TPM2B_AUTH null_auth;
 
                null_auth.t.size = 0;
+               this->mutex->lock(this->mutex);
                rval = Tss2_Sys_HashSequenceStart(this->sys_context, 0, &null_auth,
                                                                                  alg_id, &sequence_handle, 0);
                if (rval != TPM_RC_SUCCESS)
                {
                        DBG1(DBG_PTS,"%s Tss2_Sys_HashSequenceStart failed: 0x%06x",
                                 LABEL, rval);
+                       this->mutex->unlock(this->mutex);
                        return FALSE;
                }
 
@@ -1083,6 +1107,7 @@ METHOD(tpm_tss_t, sign, bool,
                        {
                                DBG1(DBG_PTS,"%s Tss2_Sys_SequenceUpdate failed: 0x%06x",
                                         LABEL, rval);
+                               this->mutex->unlock(this->mutex);
                                return FALSE;
                        }
                }
@@ -1091,6 +1116,7 @@ METHOD(tpm_tss_t, sign, bool,
                rval = Tss2_Sys_SequenceComplete(this->sys_context, sequence_handle,
                                                                                 &sessions_data_cmd, &buffer, hierarchy,
                                                                                 &hash, &validation, 0);
+               this->mutex->unlock(this->mutex);
                if (rval != TPM_RC_SUCCESS)
                {
                        DBG1(DBG_PTS,"%s Tss2_Sys_SequenceComplete failed: 0x%06x",
@@ -1099,8 +1125,10 @@ METHOD(tpm_tss_t, sign, bool,
                }
        }
 
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_Sign(this->sys_context, handle, &sessions_data_cmd, &hash,
                                                 &sig_scheme, &validation, &sig, &sessions_data_rsp);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM_RC_SUCCESS)
        {
                DBG1(DBG_PTS,"%s Tss2_Sys_Sign failed: 0x%06x", LABEL, rval);
@@ -1170,7 +1198,9 @@ METHOD(tpm_tss_t, get_random, bool,
        {
                len = min(bytes, random_len);
 
+               this->mutex->lock(this->mutex);
                rval = Tss2_Sys_GetRandom(this->sys_context, NULL, len, &random, NULL);
+               this->mutex->unlock(this->mutex);
                if (rval != TSS2_RC_SUCCESS)
                {
                        DBG1(DBG_PTS,"%s Tss2_Sys_GetRandom failed: 0x%06x", LABEL, rval);
@@ -1204,8 +1234,10 @@ METHOD(tpm_tss_t, get_data, bool,
        TPMS_AUTH_RESPONSE *session_data_rsp_array[1];
 
        /* query maximum TPM data transmission size */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM_CAP_TPM_PROPERTIES,
                                TPM_PT_NV_BUFFER_MAX, 1, &more_data, &cap_data, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM_RC_SUCCESS)
        {
                DBG1(DBG_PTS,"%s Tss2_Sys_GetCapability failed for "
@@ -1216,8 +1248,10 @@ METHOD(tpm_tss_t, get_data, bool,
                                                MAX_NV_BUFFER_SIZE);
 
        /* get size of NV object */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_NV_ReadPublic(this->sys_context, handle, 0, &nv_public,
                                                                                                                                &nv_name, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM_RC_SUCCESS)
        {
                DBG1(DBG_PTS,"%s Tss2_Sys_NV_ReadPublic failed: 0x%06x", LABEL, rval);
@@ -1252,10 +1286,11 @@ METHOD(tpm_tss_t, get_data, bool,
        /* read NV data a maximum data size block at a time */
        while (nv_size > 0)
        {
+               this->mutex->lock(this->mutex);
                rval = Tss2_Sys_NV_Read(this->sys_context, hierarchy, handle,
                                        &sessions_data_cmd, min(nv_size, max_data_size),
                                        nv_offset, &nv_data, &sessions_data_rsp);
-
+               this->mutex->unlock(this->mutex);
                if (rval != TPM_RC_SUCCESS)
                {
                        DBG1(DBG_PTS,"%s Tss2_Sys_NV_Read failed: 0x%06x", LABEL, rval);
@@ -1274,6 +1309,7 @@ METHOD(tpm_tss_t, destroy, void,
        private_tpm_tss_tss2_t *this)
 {
        finalize_context(this);
+       this->mutex->destroy(this->mutex);
        free(this);
 }
 
@@ -1300,6 +1336,7 @@ tpm_tss_t *tpm_tss_tss2_create()
                        .get_data = _get_data,
                        .destroy = _destroy,
                },
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
        );
 
        available = initialize_tcti_tabrmd_context(this);
index cac0dd6..e527443 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2018 Tobias Brunner
- * Copyright (C) 2018 Andreas Steffen
+ * Copyright (C) 2018-2019 Andreas Steffen
  * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -22,6 +22,7 @@
 #include <asn1/asn1.h>
 #include <asn1/oid.h>
 #include <bio/bio_reader.h>
+#include <threading/mutex.h>
 
 #include <tss2/tss2_sys.h>
 
@@ -71,6 +72,11 @@ struct private_tpm_tss_tss2_t {
         */
        bool fips_186_4;
 
+       /**
+        * Mutex controlling access to the TPM 2.0 context
+        */
+       mutex_t *mutex;
+
 };
 
 /**
@@ -167,9 +173,11 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this)
        int written;
 
        /* get fixed properties */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM2_CAP_TPM_PROPERTIES,
                                                                  TPM2_PT_FIXED, TPM2_MAX_TPM_PROPERTIES,
                                                                  &more_data, &cap_data, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM2_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s GetCapability failed for TPM2_CAP_TPM_PROPERTIES: 0x%06x",
@@ -222,8 +230,10 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this)
                 fips_140_2 ? "FIPS 140-2" : (this->fips_186_4 ? "FIPS 186-4" : ""));
 
        /* get supported algorithms */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM2_CAP_ALGS,
                                                0, TPM2_PT_ALGORITHM_SET, &more_data, &cap_data, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM2_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s GetCapability failed for TPM2_CAP_ALGS: 0x%06x",
@@ -251,8 +261,10 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this)
        DBG2(DBG_PTS, "%s algorithms:%s", LABEL, buf);
 
        /* get supported ECC curves */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM2_CAP_ECC_CURVES,
                                                0, TPM2_PT_LOADED_CURVES, &more_data, &cap_data, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM2_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s GetCapability failed for TPM2_ECC_CURVES: 0x%06x",
@@ -394,8 +406,10 @@ bool read_public(private_tpm_tss_tss2_t *this, TPMI_DH_OBJECT handle,
 
 
        /* read public key for a given object handle from TPM 2.0 NVRAM */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_ReadPublic(this->sys_context, handle, 0, public, &name,
                                                           &qualified_name, &auth_rsp);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM2_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s could not read public key from handle 0x%08x: 0x%06x",
@@ -658,8 +672,10 @@ METHOD(tpm_tss_t, read_pcr, bool,
        memset(&pcr_values, 0, sizeof(TPML_DIGEST));
 
        /* read the PCR value */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_PCR_Read(this->sys_context, 0, &pcr_selection,
                                &pcr_update_counter, &pcr_selection, &pcr_values, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM2_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s PCR bank could not be read: 0x%60x",
@@ -737,8 +753,10 @@ METHOD(tpm_tss_t, extend_pcr, bool,
        }
 
        /* extend PCR */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_PCR_Extend(this->sys_context, pcr_num, &auth_cmd,
                                                           &digest_values, &auth_rsp);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM2_RC_SUCCESS)
        {
                DBG1(DBG_PTS, "%s PCR %02u could not be extended: 0x%06x",
@@ -786,9 +804,11 @@ METHOD(tpm_tss_t, quote, bool,
                return FALSE;
        }
 
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_Quote(this->sys_context, aik_handle, &auth_cmd,
                                                  &qualifying_data, &scheme, &pcr_selection,  &quoted,
                                                  &sig, &auth_rsp);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM2_RC_SUCCESS)
        {
                DBG1(DBG_PTS,"%s Tss2_Sys_Quote failed: 0x%06x", LABEL, rval);
@@ -951,8 +971,10 @@ METHOD(tpm_tss_t, sign, bool,
                memcpy(buffer.buffer, data.ptr, data.len);
                buffer.size = data.len;
 
+               this->mutex->lock(this->mutex);
                rval = Tss2_Sys_Hash(this->sys_context, 0, &buffer, alg_id, hierarchy,
                                                         &hash, &validation, 0);
+               this->mutex->unlock(this->mutex);
                if (rval != TPM2_RC_SUCCESS)
                {
                        DBG1(DBG_PTS,"%s Tss2_Sys_Hash failed: 0x%06x", LABEL, rval);
@@ -965,12 +987,14 @@ METHOD(tpm_tss_t, sign, bool,
            TPM2B_AUTH null_auth;
 
                null_auth.size = 0;
+               this->mutex->lock(this->mutex);
                rval = Tss2_Sys_HashSequenceStart(this->sys_context, 0, &null_auth,
                                                                                  alg_id, &sequence_handle, 0);
                if (rval != TPM2_RC_SUCCESS)
                {
                        DBG1(DBG_PTS,"%s Tss2_Sys_HashSequenceStart failed: 0x%06x",
                                 LABEL, rval);
+                       this->mutex->unlock(this->mutex);
                        return FALSE;
                }
 
@@ -987,6 +1011,7 @@ METHOD(tpm_tss_t, sign, bool,
                        {
                                DBG1(DBG_PTS,"%s Tss2_Sys_SequenceUpdate failed: 0x%06x",
                                         LABEL, rval);
+                               this->mutex->unlock(this->mutex);
                                return FALSE;
                        }
                }
@@ -995,6 +1020,7 @@ METHOD(tpm_tss_t, sign, bool,
                rval = Tss2_Sys_SequenceComplete(this->sys_context, sequence_handle,
                                                                                 &auth_cmd, &buffer, hierarchy,
                                                                                 &hash, &validation, 0);
+               this->mutex->unlock(this->mutex);
                if (rval != TPM2_RC_SUCCESS)
                {
                        DBG1(DBG_PTS,"%s Tss2_Sys_SequenceComplete failed: 0x%06x",
@@ -1003,8 +1029,10 @@ METHOD(tpm_tss_t, sign, bool,
                }
        }
 
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_Sign(this->sys_context, handle, &auth_cmd, &hash,
                                                 &sig_scheme, &validation, &sig, &auth_rsp);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM2_RC_SUCCESS)
        {
                DBG1(DBG_PTS,"%s Tss2_Sys_Sign failed: 0x%06x", LABEL, rval);
@@ -1074,7 +1102,9 @@ METHOD(tpm_tss_t, get_random, bool,
        {
                len = min(bytes, random_len);
 
+               this->mutex->lock(this->mutex);
                rval = Tss2_Sys_GetRandom(this->sys_context, NULL, len, &random, NULL);
+               this->mutex->unlock(this->mutex);
                if (rval != TSS2_RC_SUCCESS)
                {
                        DBG1(DBG_PTS,"%s Tss2_Sys_GetRandom failed: 0x%06x", LABEL, rval);
@@ -1105,8 +1135,10 @@ METHOD(tpm_tss_t, get_data, bool,
        TSS2L_SYS_AUTH_RESPONSE auth_rsp;
 
        /* query maximum TPM data transmission size */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_GetCapability(this->sys_context, 0, TPM2_CAP_TPM_PROPERTIES,
                                TPM2_PT_NV_BUFFER_MAX, 1, &more_data, &cap_data, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM2_RC_SUCCESS)
        {
                DBG1(DBG_PTS,"%s Tss2_Sys_GetCapability failed for "
@@ -1117,8 +1149,10 @@ METHOD(tpm_tss_t, get_data, bool,
                                                TPM2_MAX_NV_BUFFER_SIZE);
 
        /* get size of NV object */
+       this->mutex->lock(this->mutex);
        rval = Tss2_Sys_NV_ReadPublic(this->sys_context, handle, 0, &nv_public,
                                                                                                                                &nv_name, 0);
+       this->mutex->unlock(this->mutex);
        if (rval != TPM2_RC_SUCCESS)
        {
                DBG1(DBG_PTS,"%s Tss2_Sys_NV_ReadPublic failed: 0x%06x", LABEL, rval);
@@ -1140,9 +1174,10 @@ METHOD(tpm_tss_t, get_data, bool,
        /* read NV data a maximum data size block at a time */
        while (nv_size > 0)
        {
+               this->mutex->lock(this->mutex);
                rval = Tss2_Sys_NV_Read(this->sys_context, hierarchy, handle, &auth_cmd,
                                        min(nv_size, max_data_size), nv_offset, &nv_data, &auth_rsp);
-
+               this->mutex->unlock(this->mutex);
                if (rval != TPM2_RC_SUCCESS)
                {
                        DBG1(DBG_PTS,"%s Tss2_Sys_NV_Read failed: 0x%06x", LABEL, rval);
@@ -1161,6 +1196,7 @@ METHOD(tpm_tss_t, destroy, void,
        private_tpm_tss_tss2_t *this)
 {
        finalize_context(this);
+       this->mutex->destroy(this->mutex);
        free(this);
 }
 
@@ -1187,6 +1223,7 @@ tpm_tss_t *tpm_tss_tss2_create()
                        .get_data = _get_data,
                        .destroy = _destroy,
                },
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
        );
 
        available = initialize_tcti_context(this);