summaryrefslogtreecommitdiffstats
path: root/src/tpm_library.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tpm_library.c677
1 files changed, 677 insertions, 0 deletions
diff --git a/src/tpm_library.c b/src/tpm_library.c
new file mode 100644
index 0000000..5dd8fa4
--- /dev/null
+++ b/src/tpm_library.c
@@ -0,0 +1,677 @@
+/********************************************************************************/
+/* */
+/* LibTPM interface functions */
+/* Written by Stefan Berger */
+/* IBM Thomas J. Watson Research Center */
+/* $Id: tpm_library.c 4615 2011-08-30 15:35:24Z stefanb $ */
+/* */
+/* (c) Copyright IBM Corporation 2010. */
+/* */
+/* 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>
+
+#include <assert.h>
+#include <string.h>
+#if defined __FreeBSD__
+# define _WITH_DPRINTF
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#ifdef USE_FREEBL_CRYPTO_LIBRARY
+# include <plbase64.h>
+#endif
+
+#ifdef USE_OPENSSL_CRYPTO_LIBRARY
+# include <openssl/bio.h>
+# include <openssl/evp.h>
+#endif
+
+#include "tpm_debug.h"
+#include "tpm_error.h"
+#include "tpm_library.h"
+#include "tpm_library_intern.h"
+#include "tpm_nvfilename.h"
+#include "tpm_tis.h"
+
+static const struct tags_and_indices {
+ const char *starttag;
+ const char *endtag;
+} tags_and_indices[] = {
+ [TPMLIB_BLOB_TYPE_INITSTATE] =
+ {
+ .starttag = TPMLIB_INITSTATE_START_TAG,
+ .endtag = TPMLIB_INITSTATE_END_TAG,
+ },
+};
+
+static const struct tpm_interface *const tpm_iface[] = {
+#if WITH_TPM1
+ &TPM12Interface,
+#else
+ &DisabledInterface,
+#endif
+#if WITH_TPM2
+ &TPM2Interface,
+#else
+ &DisabledInterface,
+#endif
+ NULL,
+};
+
+static int debug_fd = -1;
+static unsigned debug_level = 0;
+static char *debug_prefix = NULL;
+
+static struct sized_buffer cached_blobs[TPMLIB_STATE_SAVE_STATE + 1];
+
+static int tpmvers_choice = 0; /* default is TPM1.2 */
+static TPM_BOOL tpmvers_locked = FALSE;
+
+uint32_t TPMLIB_GetVersion(void)
+{
+ return TPM_LIBRARY_VERSION;
+}
+
+TPM_RESULT TPMLIB_ChooseTPMVersion(TPMLIB_TPMVersion ver)
+{
+ /* TPMLIB_Terminate will reset previous choice */
+ if (tpmvers_locked)
+ return TPM_FAIL;
+
+ switch (ver) {
+#if WITH_TPM1
+ case TPMLIB_TPM_VERSION_1_2:
+ if (tpmvers_choice != 0)
+ ClearAllCachedState();
+
+ tpmvers_choice = 0; // entry 0 in tpm_iface
+ return TPM_SUCCESS;
+#endif
+#if WITH_TPM2
+ case TPMLIB_TPM_VERSION_2:
+ if (tpmvers_choice != 1)
+ ClearAllCachedState();
+
+ tpmvers_choice = 1; // entry 1 in tpm_iface
+ return TPM_SUCCESS;
+#endif
+ default:
+ return TPM_FAIL;
+ }
+}
+
+TPM_RESULT TPMLIB_MainInit(void)
+{
+ if (!tpm_iface[tpmvers_choice]) {
+ return TPM_FAIL;
+ }
+
+ tpmvers_locked = TRUE;
+
+ return tpm_iface[tpmvers_choice]->MainInit();
+}
+
+void TPMLIB_Terminate(void)
+{
+ tpm_iface[tpmvers_choice]->Terminate();
+
+ tpmvers_locked = FALSE;
+}
+
+/*
+ * Send a command to the TPM. The command buffer must hold a well formatted
+ * TPM command and the command_size indicate the size of the command.
+ * The respbuffer parameter may be provided by the user and grow if
+ * the respbufsize size indicator is determined to be too small for the
+ * response. In that case a new buffer will be allocated and the size of that
+ * buffer returned in the respbufsize parameter. resp_size describes the
+ * size of the actual response within the respbuffer.
+ */
+TPM_RESULT TPMLIB_Process(unsigned char **respbuffer, uint32_t *resp_size,
+ uint32_t *respbufsize,
+ unsigned char *command, uint32_t command_size)
+{
+ return tpm_iface[tpmvers_choice]->Process(respbuffer,
+ resp_size, respbufsize,
+ command, command_size);
+}
+
+/*
+ * Get the volatile state from the TPM. This function will return the
+ * buffer and the length of the buffer to the caller in case everything
+ * went alright.
+ */
+TPM_RESULT TPMLIB_VolatileAll_Store(unsigned char **buffer,
+ uint32_t *buflen)
+{
+ return tpm_iface[tpmvers_choice]->VolatileAllStore(buffer, buflen);
+}
+
+/*
+ * Have the TPM cancel an ongoing command
+ */
+TPM_RESULT TPMLIB_CancelCommand(void)
+{
+ return tpm_iface[tpmvers_choice]->CancelCommand();
+}
+
+/*
+ * Get a property of the TPM. The functions currently only
+ * return compile-time #defines but this may change in future
+ * versions where we may return parameters with which the TPM
+ * was created (rather than compiled).
+ */
+TPM_RESULT TPMLIB_GetTPMProperty(enum TPMLIB_TPMProperty prop,
+ int *result)
+{
+ switch (prop) {
+ case TPMPROP_TPM_BUFFER_MAX:
+ *result = TPM_BUFFER_MAX;
+ break;
+
+ default:
+ return tpm_iface[tpmvers_choice]->GetTPMProperty(prop, result);
+ }
+
+ return TPM_SUCCESS;
+}
+
+char *TPMLIB_GetInfo(enum TPMLIB_InfoFlags flags)
+{
+ return tpm_iface[tpmvers_choice]->GetInfo(flags);
+}
+
+TPM_RESULT TPMLIB_SetState(enum TPMLIB_StateType st,
+ const unsigned char *buffer, uint32_t buflen)
+{
+ return tpm_iface[tpmvers_choice]->SetState(st, buffer, buflen);
+}
+
+TPM_RESULT TPMLIB_GetState(enum TPMLIB_StateType st,
+ unsigned char **buffer, uint32_t *buflen)
+{
+ return tpm_iface[tpmvers_choice]->GetState(st, buffer, buflen);
+}
+
+TPM_RESULT TPM_IO_Hash_Start(void)
+{
+ return tpm_iface[tpmvers_choice]->HashStart();
+}
+
+TPM_RESULT TPM_IO_Hash_Data(const unsigned char *data, uint32_t data_length)
+{
+ return tpm_iface[tpmvers_choice]->HashData(data, data_length);
+}
+
+TPM_RESULT TPM_IO_Hash_End(void)
+{
+ return tpm_iface[tpmvers_choice]->HashEnd();
+}
+
+TPM_RESULT TPM_IO_TpmEstablished_Get(TPM_BOOL *tpmEstablished)
+{
+ return tpm_iface[tpmvers_choice]->TpmEstablishedGet(tpmEstablished);
+}
+
+TPM_RESULT TPM_IO_TpmEstablished_Reset(void)
+{
+ return tpm_iface[tpmvers_choice]->TpmEstablishedReset();
+}
+
+uint32_t TPMLIB_SetBufferSize(uint32_t wanted_size,
+ uint32_t *min_size,
+ uint32_t *max_size)
+{
+ return tpm_iface[tpmvers_choice]->SetBufferSize(wanted_size,
+ min_size,
+ max_size);
+}
+
+TPM_RESULT TPMLIB_ValidateState(enum TPMLIB_StateType st,
+ unsigned int flags)
+{
+ return tpm_iface[tpmvers_choice]->ValidateState(st, flags);
+}
+
+static struct libtpms_callbacks libtpms_cbs;
+
+struct libtpms_callbacks *TPMLIB_GetCallbacks(void)
+{
+ return &libtpms_cbs;
+}
+
+TPM_RESULT TPMLIB_RegisterCallbacks(struct libtpms_callbacks *callbacks)
+{
+ int max_size = sizeof(struct libtpms_callbacks);
+
+ /* restrict the size of the structure to what we know currently
+ future versions may know more callbacks */
+ if (callbacks->sizeOfStruct < max_size)
+ max_size = callbacks->sizeOfStruct;
+
+ /* clear the internal callback structure and copy the user provided
+ callbacks into it */
+ memset(&libtpms_cbs, 0x0, sizeof(libtpms_cbs));
+ memcpy(&libtpms_cbs, callbacks, max_size);
+
+ return TPM_SUCCESS;
+}
+
+static int is_base64ltr(char c)
+{
+ return ((c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') ||
+ c == '+' ||
+ c == '/' ||
+ c == '=');
+}
+
+#ifdef USE_OPENSSL_CRYPTO_LIBRARY
+static unsigned char *TPMLIB_OpenSSL_Base64Decode(char *input,
+ unsigned int outputlen)
+{
+ BIO *b64, *bmem;
+ unsigned char *res = NULL;
+ int n;
+
+ b64 = BIO_new(BIO_f_base64());
+ if (!b64) {
+ return NULL;
+ }
+
+ bmem = BIO_new_mem_buf(input, strlen(input));
+ if (!bmem) {
+ BIO_free(b64);
+ goto cleanup;
+ }
+ bmem = BIO_push(b64, bmem);
+ BIO_set_flags(bmem, BIO_FLAGS_BASE64_NO_NL);
+
+ res = malloc(outputlen);
+ if (!res) {
+ TPMLIB_LogError("Could not allocate %u bytes.\n", outputlen);
+ goto cleanup;
+ }
+
+ n = BIO_read(bmem, res, outputlen);
+ if (n <= 0) {
+ free(res);
+ res = NULL;
+ goto cleanup;
+ }
+
+cleanup:
+ BIO_free_all(bmem);
+
+ return res;
+}
+#endif
+
+/*
+ * Base64 decode the string starting at 'start' and the last
+ * valid character may be a 'end'. The length of the decoded string
+ * is returned in *length.
+ */
+static unsigned char *TPMLIB_Base64Decode(const char *start, const char *end,
+ size_t *length)
+{
+ unsigned char *ret = NULL;
+ char *input = NULL, *d;
+ const char *s;
+ char c;
+ unsigned int numbase64chars = 0;
+
+ if (end < start)
+ return NULL;
+
+ while (end > start && !is_base64ltr(*end))
+ end--;
+
+ end++;
+
+ input = malloc(end - start + 1);
+ if (!input) {
+ TPMLIB_LogError("Could not allocate %u bytes.\n",
+ (unsigned int)(end - start + 1));
+ return NULL;
+ }
+
+ /* copy from source string skipping '\n' and '\r' and using
+ '=' to calculate the exact length */
+ d = input;
+ s = start;
+
+ while (s < end) {
+ c = *s;
+ if (is_base64ltr(c)) {
+ *d = c;
+ d++;
+ if (c != '=') {
+ numbase64chars++;
+ }
+ } else if (c == 0) {
+ break;
+ }
+ s++;
+ }
+ *d = 0;
+
+ *length = (numbase64chars / 4) * 3;
+ switch (numbase64chars % 4) {
+ case 2:
+ case 3:
+ *length += (numbase64chars % 4) - 1;
+ break;
+ case 0:
+ break;
+ case 1:
+ fprintf(stderr,"malformed base64\n");
+ goto err_exit;
+ break;
+ }
+
+#ifdef USE_FREEBL_CRYPTO_LIBRARY
+ ret = (unsigned char *)PL_Base64Decode(input, 0, NULL);
+#endif
+
+#ifdef USE_OPENSSL_CRYPTO_LIBRARY
+ ret = TPMLIB_OpenSSL_Base64Decode(input, *length);
+#endif
+
+err_exit:
+ free(input);
+
+ return ret;
+}
+
+static unsigned char *TPMLIB_GetPlaintext(const char *stream,
+ const char *starttag,
+ const char *endtag,
+ size_t *length)
+{
+ char *start, *end;
+ unsigned char *plaintext = NULL;
+
+ start = strstr(stream, starttag);
+ if (start) {
+ start += strlen(starttag);
+ while (isspace((int)*start))
+ start++;
+ end = strstr(start, endtag);
+ if (end) {
+ plaintext = TPMLIB_Base64Decode(start, --end, length);
+ }
+ }
+ return plaintext;
+}
+
+TPM_RESULT TPMLIB_DecodeBlob(const char *buffer, enum TPMLIB_BlobType type,
+ unsigned char **result, size_t *result_len)
+{
+ TPM_RESULT res = TPM_SUCCESS;
+
+ *result = TPMLIB_GetPlaintext(buffer,
+ tags_and_indices[type].starttag,
+ tags_and_indices[type].endtag,
+ result_len);
+
+ if (*result == NULL) {
+ res = TPM_FAIL;
+ }
+
+ return res;
+}
+
+void TPMLIB_SetDebugFD(int fd)
+{
+ debug_fd = fd;
+}
+
+void TPMLIB_SetDebugLevel(unsigned level)
+{
+ debug_level = level;
+}
+
+TPM_RESULT TPMLIB_SetDebugPrefix(const char *prefix)
+{
+ free(debug_prefix);
+
+ if (prefix) {
+ debug_prefix = strdup(prefix);
+ if (!debug_prefix)
+ return TPM_FAIL;
+ } else {
+ debug_prefix = NULL;
+ }
+
+ return TPM_SUCCESS;
+}
+
+int TPMLIB_LogPrintf(const char *format, ...)
+{
+ unsigned level = debug_level, i;
+ va_list args;
+ char buffer[256];
+ int n;
+
+ if (!debug_fd || !debug_level)
+ return -1;
+
+ va_start(args, format);
+ n = vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+
+ if (n < 0 || n >= (int)sizeof(buffer))
+ return -1;
+
+ level--;
+
+ i = 0;
+ while (1) {
+ if (buffer[i] == 0)
+ return -1;
+ if (buffer[i] != ' ')
+ break;
+ if (i == level)
+ return -1;
+ i++;
+ }
+
+ if (debug_prefix)
+ dprintf(debug_fd, "%s", debug_prefix);
+ dprintf(debug_fd, "%s", buffer);
+
+ return i;
+}
+
+/*
+ * TPMLIB_LogPrintfA: Printf to the logfd without indentation check
+ *
+ * @indent: how many spaces to indent; indent of ~0 forces logging
+ * with indent 0 even if not debug_level is set
+ * @format: format to use for formatting the following parameters
+ * @...: varargs
+ */
+void TPMLIB_LogPrintfA(unsigned int indent, const char *format, ...)
+{
+ va_list args;
+ char spaces[20];
+ int fd;
+
+ if (indent != (unsigned int)~0) {
+ if (!debug_fd || !debug_level)
+ return;
+ fd = debug_fd;
+ } else {
+ indent = 0;
+ fd = (debug_fd >= 0) ? debug_fd : STDERR_FILENO;
+ }
+
+ if (indent) {
+ if (indent > sizeof(spaces) - 1)
+ indent = sizeof(spaces) - 1;
+ memset(spaces, ' ', indent);
+ spaces[indent] = 0;
+ dprintf(fd, "%s", spaces);
+ }
+
+ va_start(args, format);
+ vdprintf(fd, format, args);
+ va_end(args);
+}
+
+/*
+ * TPMLIB_LogArray: Display an array of data
+ *
+ * @indent: how many spaces to indent; indent of ~0 forces logging
+ * with indent 0 even if not debug_level is set
+ * @data: the data to print
+ * @datalen: length of the data
+ */
+void TPMLIB_LogArray(unsigned int indent, const unsigned char *data,
+ size_t datalen)
+{
+ char line[80];
+ size_t i, o = 0;
+
+ for (i = 0; i < datalen; i++) {
+ snprintf(&line[o], sizeof(line) - o, "%02x ", data[i]);
+ o += 3;
+ if (o >= 16 * 3) {
+ TPMLIB_LogPrintfA(indent, "%s\n", line);
+ o = 0;
+ }
+ }
+ if (o > 0) {
+ TPMLIB_LogPrintfA(indent, "%s\n", line);
+ }
+}
+
+void ClearCachedState(enum TPMLIB_StateType st)
+{
+ free(cached_blobs[st].buffer);
+ cached_blobs[st].buffer = NULL;
+ cached_blobs[st].buflen = 0;
+}
+
+void ClearAllCachedState(void)
+{
+ ClearCachedState(TPMLIB_STATE_VOLATILE);
+ ClearCachedState(TPMLIB_STATE_PERMANENT);
+ ClearCachedState(TPMLIB_STATE_SAVE_STATE);
+}
+
+/*
+ * Set buffer for cached state; we allow setting an empty cached state
+ * by the caller passing a NULL pointer for the buffer.
+ */
+void SetCachedState(enum TPMLIB_StateType st,
+ unsigned char *buffer, uint32_t buflen)
+{
+ free(cached_blobs[st].buffer);
+ cached_blobs[st].buffer = buffer;
+ cached_blobs[st].buflen = buffer ? buflen : BUFLEN_EMPTY_BUFFER;
+}
+
+void GetCachedState(enum TPMLIB_StateType st,
+ unsigned char **buffer, uint32_t *buflen,
+ bool *is_empty_buffer)
+{
+ /* caller owns blob now */
+ *buffer = cached_blobs[st].buffer;
+ *buflen = cached_blobs[st].buflen;
+ *is_empty_buffer = (*buflen == BUFLEN_EMPTY_BUFFER);
+ cached_blobs[st].buffer = NULL;
+ cached_blobs[st].buflen = 0;
+}
+
+bool HasCachedState(enum TPMLIB_StateType st)
+{
+ return (cached_blobs[st].buffer != NULL || cached_blobs[st].buflen != 0);
+}
+
+TPM_RESULT CopyCachedState(enum TPMLIB_StateType st,
+ unsigned char **buffer, uint32_t *buflen,
+ bool *is_empty_buffer)
+{
+ TPM_RESULT ret = TPM_SUCCESS;
+
+ /* buflen may indicate an empty buffer */
+ *buflen = cached_blobs[st].buflen;
+ *is_empty_buffer = (*buflen == BUFLEN_EMPTY_BUFFER);
+
+ if (cached_blobs[st].buffer) {
+ *buffer = malloc(*buflen);
+ if (!*buffer) {
+ TPMLIB_LogError("Could not allocate %u bytes.\n", *buflen);
+ ret = TPM_SIZE;
+ } else {
+ memcpy(*buffer, cached_blobs[st].buffer, *buflen);
+ }
+ } else {
+ *buffer = NULL;
+ }
+
+ return ret;
+}
+
+const char *TPMLIB_StateTypeToName(enum TPMLIB_StateType st)
+{
+ switch (st) {
+ case TPMLIB_STATE_PERMANENT:
+ return TPM_PERMANENT_ALL_NAME;
+ case TPMLIB_STATE_VOLATILE:
+ return TPM_VOLATILESTATE_NAME;
+ case TPMLIB_STATE_SAVE_STATE:
+ return TPM_SAVESTATE_NAME;
+ }
+ return NULL;
+}
+
+enum TPMLIB_StateType TPMLIB_NameToStateType(const char *name)
+{
+ if (!name)
+ return 0;
+ if (!strcmp(name, TPM_PERMANENT_ALL_NAME))
+ return TPMLIB_STATE_PERMANENT;
+ if (!strcmp(name, TPM_VOLATILESTATE_NAME))
+ return TPMLIB_STATE_VOLATILE;
+ if (!strcmp(name, TPM_SAVESTATE_NAME))
+ return TPMLIB_STATE_SAVE_STATE;
+ return 0;
+}