diff options
Diffstat (limited to 'src/tpm_tpm12_interface.c')
-rw-r--r-- | src/tpm_tpm12_interface.c | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/src/tpm_tpm12_interface.c b/src/tpm_tpm12_interface.c new file mode 100644 index 0000000..6e3cde6 --- /dev/null +++ b/src/tpm_tpm12_interface.c @@ -0,0 +1,530 @@ +/********************************************************************************/ +/* */ +/* LibTPM TPM 1.2 call interface functions */ +/* Written by Stefan Berger */ +/* IBM Thomas J. Watson Research Center */ +/* */ +/* (c) Copyright IBM Corporation 2015. */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions are */ +/* met: */ +/* */ +/* Redistributions of source code must retain the above copyright notice, */ +/* this list of conditions and the following disclaimer. */ +/* */ +/* Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in the */ +/* documentation and/or other materials provided with the distribution. */ +/* */ +/* Neither the names of the IBM Corporation nor the names of its */ +/* contributors may be used to endorse or promote products derived from */ +/* this software without specific prior written permission. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */ +/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */ +/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */ +/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */ +/* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */ +/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY */ +/* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */ +/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */ +/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/********************************************************************************/ + +#include <config.h> + +#define _GNU_SOURCE +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#include "tpm_debug.h" +#include "tpm_error.h" +#include "tpm12/tpm_init.h" +#include "tpm_library_intern.h" +#include "tpm12/tpm_process.h" +#include "tpm12/tpm_startup.h" +#include "tpm12/tpm_global.h" +#include "tpm12/tpm_permanent.h" +#include "tpm_nvfile.h" + +static TPM_RESULT TPM12_MainInit(void) +{ + return TPM_MainInit(); +} + +static void TPM12_Terminate(void) +{ + TPM_Global_Delete(tpm_instances[0]); + free(tpm_instances[0]); + tpm_instances[0] = NULL; +} + +static TPM_RESULT TPM12_Process(unsigned char **respbuffer, uint32_t *resp_size, + uint32_t *respbufsize, + unsigned char *command, uint32_t command_size) +{ + *resp_size = 0; + return TPM_ProcessA(respbuffer, resp_size, respbufsize, + command, command_size); +} + +static TPM_RESULT TPM12_VolatileAllStore(unsigned char **buffer, + uint32_t *buflen) +{ + TPM_RESULT rc; + TPM_STORE_BUFFER tsb; + TPM_Sbuffer_Init(&tsb); + uint32_t total; + +#ifdef TPM_DEBUG + assert(tpm_instances[0] != NULL); +#endif + + rc = TPM_VolatileAll_Store(&tsb, tpm_instances[0]); + + if (rc == TPM_SUCCESS) { + /* caller now owns the buffer and needs to free it */ + TPM_Sbuffer_GetAll(&tsb, buffer, buflen, &total); + } else { + TPM_Sbuffer_Delete(&tsb); + *buflen = 0; + *buffer = NULL; + } + + return rc; +} + +static TPM_RESULT TPM12_CancelCommand(void) +{ + return TPM_FAIL; /* not supported */ +} + + +static TPM_RESULT TPM12_GetTPMProperty(enum TPMLIB_TPMProperty prop, + int *result) +{ + switch (prop) { + case TPMPROP_TPM_RSA_KEY_LENGTH_MAX: + *result = TPM_RSA_KEY_LENGTH_MAX; + break; + + case TPMPROP_TPM_KEY_HANDLES: + *result = TPM_KEY_HANDLES; + break; + + case TPMPROP_TPM_OWNER_EVICT_KEY_HANDLES: + *result = TPM_OWNER_EVICT_KEY_HANDLES; + break; + + case TPMPROP_TPM_MIN_AUTH_SESSIONS: + *result = TPM_MIN_AUTH_SESSIONS; + break; + + case TPMPROP_TPM_MIN_TRANS_SESSIONS: + *result = TPM_MIN_TRANS_SESSIONS; + break; + + case TPMPROP_TPM_MIN_DAA_SESSIONS: + *result = TPM_MIN_DAA_SESSIONS; + break; + + case TPMPROP_TPM_MIN_SESSION_LIST: + *result = TPM_MIN_SESSION_LIST; + break; + + case TPMPROP_TPM_MIN_COUNTERS: + *result = TPM_MIN_COUNTERS; + break; + + case TPMPROP_TPM_NUM_FAMILY_TABLE_ENTRY_MIN: + *result = TPM_NUM_FAMILY_TABLE_ENTRY_MIN; + break; + + case TPMPROP_TPM_NUM_DELEGATE_TABLE_ENTRY_MIN: + *result = TPM_NUM_DELEGATE_TABLE_ENTRY_MIN; + break; + + case TPMPROP_TPM_SPACE_SAFETY_MARGIN: + *result = TPM_SPACE_SAFETY_MARGIN; + break; + + case TPMPROP_TPM_MAX_NV_SPACE: + /* fill up 20 kb.; this provides some safety margin (currently + >4Kb) for possible future expansion of this blob */ + *result = ROUNDUP(TPM_MAX_NV_SPACE, 20 * 1024); + break; + + case TPMPROP_TPM_MAX_SAVESTATE_SPACE: + *result = TPM_MAX_SAVESTATE_SPACE; + break; + + case TPMPROP_TPM_MAX_VOLATILESTATE_SPACE: + *result = TPM_MAX_VOLATILESTATE_SPACE; + break; + + default: + return TPM_FAIL; + } + + return TPM_SUCCESS; +} + +/* + * TPM12_GetInfo: + * + * @flags: logical or of flags that query for information + * + * Return a JSON document with contents queried for by the user's passed flags + */ +static char *TPM12_GetInfo(enum TPMLIB_InfoFlags flags) +{ + const char *tpmspec = + "\"TPMSpecification\":{" + "\"family\":\"1.2\"," + "\"level\":2," + "\"revision\":116" + "}"; + const char *tpmattrs = + "\"TPMAttributes\":{" + "\"manufacturer\":\"id:00001014\"," + "\"version\":\"id:00740001\"," /* 146.1 */ + "\"model\":\"swtpm\"" + "}"; + char *fmt = NULL, *buffer; + bool printed = false; + + if (!(buffer = strdup("{%s%s%s}"))) + return NULL; + + if ((flags & TPMLIB_INFO_TPMSPECIFICATION)) { + fmt = buffer; + buffer = NULL; + if (asprintf(&buffer, fmt, "", tpmspec, "%s%s%s") < 0) + goto error; + free(fmt); + printed = true; + } + if ((flags & TPMLIB_INFO_TPMATTRIBUTES)) { + fmt = buffer; + buffer = NULL; + if (asprintf(&buffer, fmt, printed ? "," : "", + tpmattrs, "%s%s%s") < 0) + goto error; + free(fmt); + printed = true; + } + + /* nothing else to add */ + fmt = buffer; + buffer = NULL; + if (asprintf(&buffer, fmt, "", "", "") < 0) + goto error; + free(fmt); + + return buffer; + +error: + free(fmt); + free(buffer); + + return NULL; +} + +static uint32_t tpm12_buffersize = TPM_BUFFER_MAX; + +static uint32_t TPM12_SetBufferSize(uint32_t wanted_size, + uint32_t *min_size, + uint32_t *max_size) +{ + if (min_size) + *min_size = TPM_BUFFER_MIN; + if (max_size) + *max_size = TPM_BUFFER_MAX; + + if (wanted_size == 0) + return tpm12_buffersize; + + if (wanted_size > TPM_BUFFER_MAX) + wanted_size = TPM_BUFFER_MAX; + else if (wanted_size < TPM_BUFFER_MIN) + wanted_size = TPM_BUFFER_MIN; + + tpm12_buffersize = wanted_size; + + return tpm12_buffersize; +} + +uint32_t TPM12_GetBufferSize(void) +{ + return TPM12_SetBufferSize(0, NULL, NULL); +} + +static TPM_RESULT TPM12_ValidateState(enum TPMLIB_StateType st, + unsigned int flags) +{ + TPM_RESULT ret = TPM_SUCCESS; + tpm_state_t tpm_state; + enum TPMLIB_StateType sts[] = { + TPMLIB_STATE_PERMANENT, + TPMLIB_STATE_VOLATILE, + TPMLIB_STATE_SAVE_STATE, + 0, + }; + enum TPMLIB_StateType c_st; + unsigned i; + +#ifdef TPM_LIBTPMS_CALLBACKS + struct libtpms_callbacks *cbs = TPMLIB_GetCallbacks(); + + if (cbs->tpm_nvram_init) { + ret = cbs->tpm_nvram_init(); + if (ret != TPM_SUCCESS) + return ret; + } +#endif + + ret = TPM_Global_Init(&tpm_state); + tpm_state.tpm_number = 0; + + if (ret == TPM_SUCCESS) { + /* permanent state needs to be there and loaded first */ + ret = TPM_PermanentAll_NVLoad(&tpm_state); + } + + for (i = 0; sts[i] && ret == TPM_SUCCESS; i++) { + c_st = st & sts[i]; + + /* 'cached' state is known to 'work', so skip it */ + if (!c_st || HasCachedState(c_st)) + continue; + + switch (c_st) { + case TPMLIB_STATE_PERMANENT: + break; + case TPMLIB_STATE_VOLATILE: + ret = TPM_VolatileAll_NVLoad(&tpm_state); + break; + case TPMLIB_STATE_SAVE_STATE: + ret = TPM_SaveState_NVLoad(&tpm_state); + break; + } + } + + TPM_Global_Delete(&tpm_state); + + return ret; +} + +static TPM_RESULT _TPM_PermanentAll_Store(TPM_STORE_BUFFER *sbuffer, + tpm_state_t *tpm_state) +{ + const unsigned char *buffer = NULL; + uint32_t buflen; + + return TPM_PermanentAll_Store(sbuffer, &buffer, &buflen, tpm_state); +} + +/* + * TPM_PermanentAll_NVLoad_Preserve + * + * @tpm_state: The tpm_state to load the permanent state into + * + * Call TPM_PermanentAll_NVLoad and preserve any cached data that a call + * to TPM_PermanentAll_NVLoad (TPM_NVRAM_LoadData) may otherwise consume + * and remove if it was available. + */ +static TPM_RESULT TPM_PermanentAll_NVLoad_Preserve(tpm_state_t *tpm_state) +{ + TPM_RESULT ret; + unsigned char *buffer = NULL; + uint32_t buffer_len; + bool is_empty_buffer; + + ret = CopyCachedState(TPMLIB_STATE_PERMANENT, + &buffer, &buffer_len, &is_empty_buffer); + if (ret == TPM_SUCCESS) { + ret = TPM_PermanentAll_NVLoad(tpm_state); + + /* restore a previous empty buffer or any valid buffer */ + if (is_empty_buffer || buffer != NULL) + SetCachedState(TPMLIB_STATE_PERMANENT, buffer, buffer_len); + } + + return ret; +} + +/* + * Get the state blob of the given type. If we TPM is not running, we + * get the cached state blobs, if available, otherwise we try to read + * it from files. In case the TPM is running, we get it from the running + * TPM. + */ +static TPM_RESULT TPM12_GetState(enum TPMLIB_StateType st, + unsigned char **buffer, uint32_t *buflen) +{ + TPM_RESULT ret = TPM_FAIL; + TPM_STORE_BUFFER tsb; + uint32_t total; + + /* TPM not running ? */ + if (tpm_instances[0] == NULL) { + struct libtpms_callbacks *cbs = TPMLIB_GetCallbacks(); + bool is_empty_buffer; + + /* try cached blob before file */ + ret = CopyCachedState(st, buffer, buflen, &is_empty_buffer); + if (ret != TPM_SUCCESS || *buffer != NULL || is_empty_buffer) + return ret; + + if (cbs->tpm_nvram_init) { + ret = cbs->tpm_nvram_init(); + if (ret != TPM_SUCCESS) + return ret; + + ret = TPM_NVRAM_LoadData(buffer, buflen, 0, + TPMLIB_StateTypeToName(st)); + } else { + ret = TPM_FAIL; + } + return ret; + } + + TPM_Sbuffer_Init(&tsb); + + switch (st) { + case TPMLIB_STATE_PERMANENT: + ret = _TPM_PermanentAll_Store(&tsb, tpm_instances[0]); + break; + case TPMLIB_STATE_VOLATILE: + ret = TPM_VolatileAll_Store(&tsb, tpm_instances[0]); + break; + case TPMLIB_STATE_SAVE_STATE: + ret = TPM_SaveState_Store(&tsb, tpm_instances[0]); + break; + } + + if (ret == TPM_SUCCESS) { + /* caller now owns the buffer and needs to free it */ + TPM_Sbuffer_GetAll(&tsb, buffer, buflen, &total); + } else { + TPM_Sbuffer_Delete(&tsb); + *buflen = 0; + *buffer = NULL; + } + + return ret; +} + +/* + * Set the state the TPM 1.2 will use upon next TPM_MainInit(). The TPM 1.2 + * must not have been started, yet, or it must have been terminated for this + * function to set the state. + * + * @st: The TPMLIB_StateType describing the type of blob in the buffer + * @buffer: pointer to the buffer containing the state blob; NULL pointer clears + * previous state + * @buflen: length of the buffer + */ +static TPM_RESULT TPM12_SetState(enum TPMLIB_StateType st, + const unsigned char *buffer, uint32_t buflen) +{ + TPM_RESULT ret = TPM_SUCCESS; + unsigned char *stream = NULL, *orig_stream = NULL; + uint32_t stream_size = buflen; + tpm_state_t *tpm_state = NULL; + + if (buffer == NULL) { + SetCachedState(st, NULL, 0); + return TPM_SUCCESS; + } + + if (tpm_instances[0]) + return TPM_INVALID_POSTINIT; + + if (ret == TPM_SUCCESS) { + stream = malloc(buflen); + if (!stream) { + TPMLIB_LogError("Could not allocate %u bytes.\n", buflen); + ret = TPM_SIZE; + } + } + + if (ret == TPM_SUCCESS) { + orig_stream = stream; + memcpy(stream, buffer, buflen); + + tpm_state = malloc(sizeof(tpm_state_t)); + if (!tpm_state) { + TPMLIB_LogError("Could not allocated %zu bytes.\n", + sizeof(tpm_state_t)); + ret = TPM_SIZE; + } + } + + if (ret == TPM_SUCCESS) { + ret = TPM_Global_Init(tpm_state); + } + + /* test whether we can accept the blob */ + if (ret == TPM_SUCCESS) { + tpm_state->tpm_number = 0; + + switch (st) { + case TPMLIB_STATE_PERMANENT: + ret = TPM_PermanentAll_Load(tpm_state, &stream, &stream_size); + break; + case TPMLIB_STATE_VOLATILE: + /* permanent state needs to be there and loaded first */ + ret = TPM_PermanentAll_NVLoad_Preserve(tpm_state); + if (ret == TPM_SUCCESS) + ret = TPM_VolatileAll_Load(tpm_state, &stream, &stream_size); + break; + case TPMLIB_STATE_SAVE_STATE: + ret = TPM_PermanentAll_NVLoad_Preserve(tpm_state); + if (ret == TPM_SUCCESS) + ret = TPM_SaveState_Load(tpm_state, &stream, &stream_size); + break; + } + if (ret) + ClearAllCachedState(); + } + + /* cache the blob for the TPM_MainInit() to pick it up */ + if (ret == TPM_SUCCESS) { + SetCachedState(st, orig_stream, buflen); + } else { + free(orig_stream); + } + + TPM_Global_Delete(tpm_state); + free(tpm_state); + + return ret; +} + +const struct tpm_interface TPM12Interface = { + .MainInit = TPM12_MainInit, + .Terminate = TPM12_Terminate, + .Process = TPM12_Process, + .VolatileAllStore = TPM12_VolatileAllStore, + .CancelCommand = TPM12_CancelCommand, + .GetTPMProperty = TPM12_GetTPMProperty, + .GetInfo = TPM12_GetInfo, + .TpmEstablishedGet = TPM12_IO_TpmEstablished_Get, + .TpmEstablishedReset = TPM12_IO_TpmEstablished_Reset, + .HashStart = TPM12_IO_Hash_Start, + .HashData = TPM12_IO_Hash_Data, + .HashEnd = TPM12_IO_Hash_End, + .SetBufferSize = TPM12_SetBufferSize, + .ValidateState = TPM12_ValidateState, + .SetState = TPM12_SetState, + .GetState = TPM12_GetState, +}; |