mirror of
https://github.com/stefanberger/libtpms
synced 2026-01-01 04:45:32 +00:00
Add a new API call TPMLIB_SetProfile that enables a user to set a profile. The user gets control over the name of the profile to apply and may supply the algoritms to enable. The user does not get control over the individual commands to enable. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
537 lines
15 KiB
C
537 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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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,
|
|
};
|