mirror of
https://github.com/stefanberger/libtpms
synced 2025-08-25 10:34:37 +00:00
543 lines
15 KiB
C
543 lines
15 KiB
C
/********************************************************************************/
|
|
/* */
|
|
/* 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 LIBTPMS_ATTR_UNUSED)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static TPM_RESULT TPM12_SetProfile(const char *profile)
|
|
{
|
|
return TPM_FAIL;
|
|
}
|
|
|
|
static TPM_BOOL TPM12_WasManufactured(void)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
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,
|
|
.SetProfile = TPM12_SetProfile,
|
|
.WasManufactured = TPM12_WasManufactured,
|
|
};
|