aboutsummaryrefslogtreecommitdiffstats
path: root/TPM2-Plugin/lib/files.c
diff options
context:
space:
mode:
Diffstat (limited to 'TPM2-Plugin/lib/files.c')
-rw-r--r--TPM2-Plugin/lib/files.c636
1 files changed, 636 insertions, 0 deletions
diff --git a/TPM2-Plugin/lib/files.c b/TPM2-Plugin/lib/files.c
new file mode 100644
index 0000000..e2e41f4
--- /dev/null
+++ b/TPM2-Plugin/lib/files.c
@@ -0,0 +1,636 @@
+//**********************************************************************;
+// Copyright (c) 2017, Intel Corporation
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. 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.
+//
+// 3. Neither the name of Intel 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 <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <sapi/tpm20.h>
+#include <sapi/tss2_mu.h>
+
+#include "files.h"
+#include "log.h"
+#include "tpm2_util.h"
+
+bool files_get_file_size(FILE *fp, unsigned long *file_size, const char *path) {
+
+ long current = ftell(fp);
+ if (current < 0) {
+ if (path) {
+ LOG_ERR("Error getting current file offset for file \"%s\" error: %s", path, strerror(errno));
+ }
+ return false;
+ }
+
+ int rc = fseek(fp, 0, SEEK_END);
+ if (rc < 0) {
+ if (path) {
+ LOG_ERR("Error seeking to end of file \"%s\" error: %s", path, strerror(errno));
+ }
+ return false;
+ }
+
+ long size = ftell(fp);
+ if (size < 0) {
+ if (path) {
+ LOG_ERR("ftell on file \"%s\" failed: %s", path, strerror(errno));
+ }
+ return false;
+ }
+
+ rc = fseek(fp, current, SEEK_SET);
+ if (rc < 0) {
+ if (path) {
+ LOG_ERR("Could not restore initial stream position for file \"%s\" failed: %s", path, strerror(errno));
+ }
+ return false;
+ }
+
+ /* size cannot be negative at this point */
+ *file_size = (unsigned long)size;
+ return true;
+}
+
+static bool read_bytes_from_file(FILE *f, UINT8 *buf, UINT16 *size,
+ const char *path) {
+ unsigned long file_size;
+ bool result = files_get_file_size(f, &file_size, path);
+ if (!result) {
+ /* get_file_size() logs errors */
+ return false;
+ }
+
+ /* max is bounded on UINT16 */
+ if (file_size > *size) {
+ if (path) {
+ LOG_ERR(
+ "File \"%s\" size is larger than buffer, got %lu expected less than %u",
+ path, file_size, *size);
+ }
+ return false;
+ }
+
+ result = files_read_bytes(f, buf, file_size);
+ if (!result) {
+ if (path) {
+ LOG_ERR("Could not read data from file \"%s\"", path);
+ }
+ return false;
+ }
+
+ *size = file_size;
+
+ return true;
+}
+
+bool files_load_bytes_from_path(const char *path, UINT8 *buf, UINT16 *size) {
+ if (!buf || !size || !path) {
+ return false;
+ }
+
+ FILE *f = fopen(path, "rb");
+ if (!f) {
+ LOG_ERR("Could not open file \"%s\" error %s", path, strerror(errno));
+ return false;
+ }
+
+ bool result = read_bytes_from_file(f, buf, size, path);
+
+ fclose(f);
+ return result;
+}
+
+bool files_save_bytes_to_file(const char *path, UINT8 *buf, UINT16 size) {
+
+ if (!path || !buf) {
+ return false;
+ }
+
+ FILE *fp = fopen(path, "wb+");
+ if (!fp) {
+ LOG_ERR("Could not open file \"%s\", error: %s", path, strerror(errno));
+ return false;
+ }
+
+ bool result = files_write_bytes(fp, buf, size);
+ if (!result) {
+ LOG_ERR("Could not write data to file \"%s\"", path);
+ }
+ fclose(fp);
+ return result;
+}
+
+/*
+ * Current version to write TPMS_CONTEXT to disk.
+ */
+#define CONTEXT_VERSION 1
+
+bool files_save_tpm_context_to_file(TSS2_SYS_CONTEXT *sysContext, TPM2_HANDLE handle,
+ FILE *stream) {
+
+ TPMS_CONTEXT context;
+
+ TSS2_RC rval = Tss2_Sys_ContextSave(sysContext, handle, &context);
+ if (rval != TPM2_RC_SUCCESS) {
+ LOG_PERR(Tss2_Sys_ContextSave, rval);
+ return false;
+ }
+
+ /*
+ * Saving the TPMS_CONTEXT structure to disk, format:
+ * TPM2.0-TOOLS HEADER
+ * U32 hiearchy
+ * U32 savedHandle
+ * U64 sequence
+ * U16 contextBlobLength
+ * BYTE[] contextBlob
+ */
+ bool result = files_write_header(stream, CONTEXT_VERSION);
+ if (!result) {
+ LOG_ERR("Could not write context file header");
+ goto out;
+ }
+
+ // UINT32
+ result = files_write_32(stream, context.hierarchy);
+ if (!result) {
+ LOG_ERR("Could not write hierarchy");
+ goto out;
+ }
+
+ result = files_write_32(stream, context.savedHandle);
+ if (!result) {
+ LOG_ERR("Could not write savedHandle");
+ goto out;
+ }
+
+ // UINT64
+ result = files_write_64(stream, context.sequence);
+ if (!result) {
+ LOG_ERR("Could not write sequence");
+ goto out;
+ }
+
+ // U16 LENGTH
+ result = files_write_16(stream, context.contextBlob.size);
+ if (!result) {
+ LOG_ERR("Could not write contextBob size");
+ goto out;
+ }
+
+ // BYTE[] contextBlob
+ result = files_write_bytes(stream, context.contextBlob.buffer,
+ context.contextBlob.size);
+ if (!result) {
+ LOG_ERR("Could not write contextBlob buffer");
+ }
+ /* result is set by file_write_bytes() */
+
+out:
+ return result;
+}
+
+bool files_save_tpm_context_to_path(TSS2_SYS_CONTEXT *sysContext, TPM2_HANDLE handle,
+ const char *path) {
+
+ FILE *f = fopen(path, "w+b");
+ if (!f) {
+ LOG_ERR("Error opening file \"%s\" due to error: %s", path,
+ strerror(errno));
+ return false;
+ }
+
+ bool result = files_save_tpm_context_to_file(sysContext, handle, f);
+ fclose(f);
+ return result;
+}
+
+
+bool files_load_tpm_context_from_file(TSS2_SYS_CONTEXT *sapi_context,
+ TPM2_HANDLE *handle, FILE *fstream) {
+
+ TSS2_RC rval;
+
+ /*
+ * Reading the TPMS_CONTEXT structure to disk, format:
+ * TPM2.0-TOOLS HEADER
+ * U32 hiearchy
+ * U32 savedHandle
+ * U64 sequence
+ * U16 contextBlobLength
+ * BYTE[] contextBlob
+ */
+ UINT32 version;
+ TPMS_CONTEXT context;
+ bool result = files_read_header(fstream, &version);
+ if (!result) {
+ LOG_WARN(
+ "The loaded tpm context does not appear to be in the proper format,"
+ "assuming old format, this will be converted on the next save.");
+ rewind(fstream);
+ result = files_read_bytes(fstream, (UINT8 *) &context, sizeof(context));
+ if (!result) {
+ LOG_ERR("Could not load tpm context file");
+ goto out;
+ }
+ /* Success load the context into the TPM */
+ goto load_to_tpm;
+ }
+
+ if (version != CONTEXT_VERSION) {
+ LOG_ERR("Unsupported context file format version found, got: %"PRIu32,
+ version);
+ result = false;
+ goto out;
+ }
+
+ result = files_read_32(fstream, &context.hierarchy);
+ if (!result) {
+ LOG_ERR("Error reading hierarchy!");
+ goto out;
+ }
+
+ result = files_read_32(fstream, &context.savedHandle);
+ if (!result) {
+ LOG_ERR("Error reading savedHandle!");
+ goto out;
+ }
+
+ result = files_read_64(fstream, &context.sequence);
+ if (!result) {
+ LOG_ERR("Error reading sequence!");
+ goto out;
+ }
+
+ result = files_read_16(fstream, &context.contextBlob.size);
+ if (!result) {
+ LOG_ERR("Error reading contextBlob.size!");
+ goto out;
+ }
+
+ if (context.contextBlob.size > sizeof(context.contextBlob.buffer)) {
+ LOG_ERR(
+ "Size mismatch found on contextBlob, got %"PRIu16" expected less than or equal to %zu",
+ context.contextBlob.size,
+ sizeof(context.contextBlob.buffer));
+ result = false;
+ goto out;
+ }
+
+ result = files_read_bytes(fstream, context.contextBlob.buffer,
+ context.contextBlob.size);
+ if (!result) {
+ LOG_ERR("Error reading contextBlob.size!");
+ goto out;
+ }
+
+load_to_tpm:
+ rval = Tss2_Sys_ContextLoad(sapi_context, &context, handle);
+ if (rval != TPM2_RC_SUCCESS) {
+ LOG_PERR(Tss2_Sys_ContextLoad, rval);
+ result = false;
+ goto out;
+ }
+
+ result = true;
+
+out:
+ return result;
+}
+
+bool files_load_tpm_context_from_path(TSS2_SYS_CONTEXT *sapi_context,
+ TPM2_HANDLE *handle, const char *path) {
+
+ FILE *f = fopen(path, "rb");
+ if (!f) {
+ LOG_ERR("Error opening file \"%s\" due to error: %s", path,
+ strerror(errno));
+ return false;
+ }
+
+ bool result = files_load_tpm_context_from_file(sapi_context, handle, f);
+
+ fclose(f);
+ return result;
+}
+
+bool files_does_file_exist(const char *path) {
+
+ if (!path) {
+ LOG_ERR("Path cannot be NULL");
+ return false;
+ }
+
+ FILE *fp = fopen(path,"rb");
+ if (fp) {
+ fclose(fp);
+ LOG_ERR("Path: %s already exists. Please rename or delete the file!",
+ path);
+ return true;
+ }
+ return false;
+}
+
+bool files_get_file_size_path(const char *path, unsigned long *file_size) {
+
+ bool result = false;
+
+ if (!path) {
+ LOG_ERR("Must specify a path argument, cannot be NULL!");
+ return false;
+ }
+
+ if (!file_size) {
+ LOG_ERR("Must specify a file size argument, cannot be NULL!");
+ return false;
+ }
+
+ FILE *fp = fopen(path,"rb");
+ if(!fp) {
+ LOG_ERR("Could not open file: \"%s\" error: %s", path, strerror(errno));
+ return false;
+ }
+
+ result = files_get_file_size(fp, file_size, path);
+
+ fclose(fp);
+ return result;
+}
+
+/**
+ * This is the magic for the file header. The header is organized
+ * as a big endian U32 (BEU32) of MAGIC followed by a BEU32 of the
+ * version number. Tools can define their own, individual file
+ * formats as they make sense, but they should always have the header.
+ */
+static const UINT32 MAGIC = 0xBADCC0DE;
+
+/**
+ * Writes size bytes to a file, continuing on EINTR short writes.
+ * @param f
+ * The file to write to.
+ * @param data
+ * The data to write.
+ * @param size
+ * The size, in bytes, of that data.
+ * @return
+ * True on success, False otherwise.
+ */
+static bool writex(FILE *f, UINT8 *data, size_t size) {
+
+ size_t wrote = 0;
+ size_t index = 0;
+ do {
+ wrote = fwrite(&data[index], 1, size, f);
+ if (wrote != size) {
+ if (errno != EINTR) {
+ return false;
+ }
+ /* continue on EINTR */
+ }
+ size -= wrote;
+ index += wrote;
+ } while (size > 0);
+
+ return true;
+}
+
+/**
+ * Reads size bytes from a file, continuing on EINTR short reads.
+ * @param f
+ * The file to read from.
+ * @param data
+ * The data buffer to read into.
+ * @param size
+ * The size of the buffer, which is also the amount of bytes to read.
+ * @return
+ * True on success, False otherwise.
+ */
+static bool readx(FILE *f, UINT8 *data, size_t size) {
+
+ size_t bread = 0;
+ size_t index = 0;
+ do {
+ bread = fread(&data[index], 1, size, f);
+ if (bread != size) {
+ if (feof(f) || (errno != EINTR)) {
+ return false;
+ }
+ /* continue on EINTR */
+ }
+ size -= bread;
+ index += bread;
+ } while (size > 0);
+
+ return true;
+}
+
+#define BAIL_ON_NULL(param, x) \
+ do { \
+ if (!x) { \
+ LOG_ERR(param" must be specified"); \
+ return false; \
+ } \
+ } while(0)
+
+#define BE_CONVERT(value, size) \
+ do { \
+ if (!tpm2_util_is_big_endian()) { \
+ value = tpm2_util_endian_swap_##size(value); \
+ } \
+ } while (0)
+
+#define FILE_WRITE(size) \
+ bool files_write_##size(FILE *out, UINT##size data) { \
+ BAIL_ON_NULL("FILE", out); \
+ BE_CONVERT(data, size); \
+ return writex(out, (UINT8 *)&data, sizeof(data)); \
+ }
+
+#define FILE_READ(size) \
+ bool files_read_##size(FILE *out, UINT##size *data) { \
+ BAIL_ON_NULL("FILE", out); \
+ BAIL_ON_NULL("data", data); \
+ bool res = readx(out, (UINT8 *)data, sizeof(*data)); \
+ if (res) { \
+ BE_CONVERT(*data, size); \
+ } \
+ return res; \
+ }
+
+/*
+ * all the files_read|write_bytes_16|32|64 functions
+ */
+FILE_READ(16);
+FILE_WRITE(16)
+
+FILE_READ(32);
+FILE_WRITE(32)
+
+FILE_READ(64)
+FILE_WRITE(64)
+
+bool files_read_bytes(FILE *out, UINT8 bytes[], size_t len) {
+
+ BAIL_ON_NULL("FILE", out);
+ BAIL_ON_NULL("bytes", bytes);
+ return readx(out, bytes, len);
+}
+
+bool files_write_bytes(FILE *out, uint8_t bytes[], size_t len) {
+
+ BAIL_ON_NULL("FILE", out);
+ BAIL_ON_NULL("bytes", bytes);
+ return writex(out, bytes, len);
+}
+
+bool files_write_header(FILE *out, UINT32 version) {
+
+ BAIL_ON_NULL("FILE", out);
+
+ bool res = files_write_32(out, MAGIC);
+ if (!res) {
+ return false;
+ }
+ return files_write_32(out, version);
+}
+
+bool files_read_header(FILE *out, uint32_t *version) {
+
+ BAIL_ON_NULL("FILE", out);
+ BAIL_ON_NULL("version", version);
+
+ UINT32 magic;
+ bool res = files_read_32(out, &magic);
+ if (!res) {
+ return false;
+ }
+
+ if (magic != MAGIC) {
+ LOG_ERR("Found magic 0x%x did not match expected magic of 0x%x!",
+ magic, MAGIC);
+ return false;
+ }
+
+ return files_read_32(out, version);
+}
+
+bool files_load_bytes_from_file_or_stdin(const char *path, UINT16 *size, BYTE *buf) {
+
+ FILE *file = path ? fopen(path, "rb") : stdin;
+ path = file != stdin ? path : "<stdin>";
+ if (!file) {
+ LOG_ERR("Could not open file: \"%s\", error: %s", path,
+ strerror(errno));
+ return false;
+ }
+
+ /*
+ * Attempt to accurately read the file based on the file size.
+ * This may fail on stdin when it's a pipe.
+ */
+ if (file == stdin) {
+ path = NULL;
+ }
+
+ UINT16 original_size = *size;
+ bool res = files_load_bytes_from_path(path, buf,
+ size);
+ if (!res) {
+ res = true;
+ *size = fread(buf, 1,
+ *size, file);
+ if (!feof(file)) {
+ LOG_ERR("Data to be sealed larger than expected. Got %u expected %u",
+ original_size, res);
+ res = false;
+ }
+ else if (ferror(file)) {
+ LOG_ERR("Error reading sealed data from \"<stdin>\"");
+ res = false;
+ }
+ }
+
+ if (file != stdin) {
+ fclose(file);
+ }
+
+ return res;
+}
+
+#define SAVE_TYPE(type, name) \
+ bool files_save_##name(type *name, const char *path) { \
+ \
+ size_t offset = 0; \
+ UINT8 buffer[sizeof(*name)]; \
+ TSS2_RC rc = Tss2_MU_##type##_Marshal(name, buffer, sizeof(buffer), &offset); \
+ if (rc != TSS2_RC_SUCCESS) { \
+ LOG_ERR("Error serializing "str(name)" structure: 0x%x", rc); \
+ return false; \
+ } \
+ \
+ return files_save_bytes_to_file(path, buffer, offset); \
+ }
+
+#define LOAD_TYPE(type, name) \
+ bool files_load_##name(const char *path, type *name) { \
+ \
+ UINT8 buffer[sizeof(*name)]; \
+ UINT16 size = sizeof(buffer); \
+ bool res = files_load_bytes_from_path(path, buffer, &size); \
+ if (!res) { \
+ return false; \
+ } \
+ \
+ size_t offset = 0; \
+ TSS2_RC rc = Tss2_MU_##type##_Unmarshal(buffer, size, &offset, name); \
+ if (rc != TSS2_RC_SUCCESS) { \
+ LOG_ERR("Error serializing "str(name)" structure: 0x%x", rc); \
+ return false; \
+ } \
+ \
+ return rc == TPM2_RC_SUCCESS; \
+ }
+
+SAVE_TYPE(TPM2B_PUBLIC, public)
+LOAD_TYPE(TPM2B_PUBLIC, public)
+
+SAVE_TYPE(TPMT_SIGNATURE, signature)
+LOAD_TYPE(TPMT_SIGNATURE, signature)
+
+SAVE_TYPE(TPMT_TK_VERIFIED, ticket)
+LOAD_TYPE(TPMT_TK_VERIFIED, ticket)
+
+SAVE_TYPE(TPM2B_SENSITIVE, sensitive)
+LOAD_TYPE(TPM2B_SENSITIVE, sensitive)
+
+SAVE_TYPE(TPMT_TK_HASHCHECK, validation)
+LOAD_TYPE(TPMT_TK_HASHCHECK, validation)