blob: b28a225c82cb4dd049d516858f4c96af90fc6f6d [file] [log] [blame]
//**********************************************************************;
// 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 <string.h>
#include <tss2/tss2_sys.h>
#include <tss2/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)
SAVE_TYPE(TPM2B_PRIVATE, private)
LOAD_TYPE(TPM2B_PRIVATE, private)