swtpm: Abstract NVRAM interface for pluggable state store

Related GH issue: https://github.com/stefanberger/swtpm/issues/461

This patch adds an abstraction layer to NVRAM state store implementation
for pluggable storage backends. No functional changes are intended in
this change. The default state store backend ("dir backend") keeps
current behavior.

To make swtpm ready for pluggable store, this patch moves file related
operations to the seperate file (swtpm_nvstore_dir.c) and defined the
interface for plugins (nvram_backend_ops in swtpm_nvstore.h). The
interface can be used by each plugin which will be added later.

With the interface, each plugin can access its "backend_uri" which
points to the location of the backend storage, for example S3 bucket
URL or iSCSI URL, and decide how it stores TPM state data.

Signed-off-by: Eiichi Tsukata <eiichi.tsukata@nutanix.com>
This commit is contained in:
Eiichi Tsukata 2021-06-25 11:29:44 +09:00 committed by Stefan Berger
parent 3eac247752
commit 2d3deaef29
13 changed files with 685 additions and 423 deletions

View File

@ -24,7 +24,8 @@ noinst_HEADERS = \
swtpm_aes.h \
swtpm_debug.h \
swtpm_io.h \
swtpm_nvfile.h \
swtpm_nvstore.h \
swtpm_nvstore_dir.h \
threadpool.h \
tlv.h \
tpmlib.h \
@ -49,7 +50,8 @@ libswtpm_libtpms_la_SOURCES = \
swtpm_aes.c \
swtpm_debug.c \
swtpm_io.c \
swtpm_nvfile.c \
swtpm_nvstore.c \
swtpm_nvstore_dir.c \
tlv.c \
tpmlib.c \
tpmstate.c \

View File

@ -37,6 +37,7 @@
#include "config.h"
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <errno.h>
@ -62,7 +63,7 @@
#include "key.h"
#include "locality.h"
#include "logging.h"
#include "swtpm_nvfile.h"
#include "swtpm_nvstore.h"
#include "pidfile.h"
#include "tpmstate.h"
#include "ctrlchannel.h"
@ -656,6 +657,7 @@ int
handle_tpmstate_options(char *options)
{
char *tpmstatedir = NULL;
char *tpmbackend_uri = NULL;
int ret = 0;
mode_t mode;
@ -665,11 +667,18 @@ handle_tpmstate_options(char *options)
if (parse_tpmstate_options(options, &tpmstatedir, &mode) < 0)
return -1;
if (tpmstate_set_dir(tpmstatedir) < 0 ||
if (asprintf(&tpmbackend_uri, "dir://%s", tpmstatedir) < 0) {
logprintf(STDERR_FILENO,
"Could not asprintf TPM backend uri\n");
return -1;
}
if (tpmstate_set_backend_uri(tpmbackend_uri) < 0 ||
tpmstate_set_mode(mode) < 0)
ret = -1;
free(tpmstatedir);
free(tpmbackend_uri);
return ret;
}

View File

@ -56,7 +56,7 @@
#include "logging.h"
#include "tpm_ioctl.h"
#include "tpmlib.h"
#include "swtpm_nvfile.h"
#include "swtpm_nvstore.h"
#include "locality.h"
#include "mainloop.h"
#include "utils.h"

View File

@ -70,7 +70,7 @@
#include "locality.h"
#include "logging.h"
#include "tpm_ioctl.h"
#include "swtpm_nvfile.h"
#include "swtpm_nvstore.h"
#include "tpmlib.h"
#include "main.h"
#include "utils.h"
@ -432,7 +432,8 @@ static int tpm_start(uint32_t flags, TPMLIB_TPMVersion l_tpmversion,
TPM_RESULT *res)
{
DIR *dir;
const char *tpmdir = tpmstate_get_dir();
const char *uri = tpmstate_get_backend_uri();
const char *tpmdir = uri + strlen("dir://");
*res = TPM_FAIL;
@ -1461,7 +1462,7 @@ int swtpm_cuse_main(int argc, char **argv, const char *prgname, const char *ifac
char *cinfo_argv[1] = { 0 };
unsigned int num;
struct passwd *passwd;
const char *tpmdir;
const char *uri = NULL;
int n, tpmfd;
char path[PATH_MAX];
int ret = 0;
@ -1598,7 +1599,7 @@ int swtpm_cuse_main(int argc, char **argv, const char *prgname, const char *ifac
goto exit;
}
SWTPM_NVRAM_Set_TPMVersion(tpmversion);
tpmstate_set_version(tpmversion);
if (!cinfo.dev_info_argv) {
logprintf(STDERR_FILENO, "Error: device name missing\n");
@ -1635,8 +1636,8 @@ int swtpm_cuse_main(int argc, char **argv, const char *prgname, const char *ifac
}
}
tpmdir = tpmstate_get_dir();
if (tpmdir == NULL) {
uri = tpmstate_get_backend_uri();
if (uri == NULL) {
logprintf(STDERR_FILENO,
"Error: No TPM state directory is defined; "
"TPM_PATH is not set\n");

View File

@ -57,7 +57,7 @@
#include "main.h"
#include "swtpm_debug.h"
#include "swtpm_io.h"
#include "swtpm_nvfile.h"
#include "swtpm_nvstore.h"
#include "server.h"
#include "common.h"
#include "logging.h"
@ -431,7 +431,7 @@ int swtpm_main(int argc, char **argv, const char *prgname, const char *iface)
goto exit_failure;
}
SWTPM_NVRAM_Set_TPMVersion(mlp.tpmversion);
tpmstate_set_version(mlp.tpmversion);
if (handle_log_options(logdata) < 0 ||
handle_key_options(keydata) < 0 ||

View File

@ -57,7 +57,7 @@
#include "main.h"
#include "swtpm_debug.h"
#include "swtpm_io.h"
#include "swtpm_nvfile.h"
#include "swtpm_nvstore.h"
#include "common.h"
#include "locality.h"
#include "logging.h"
@ -474,7 +474,7 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i
exit(EXIT_FAILURE);
}
SWTPM_NVRAM_Set_TPMVersion(mlp.tpmversion);
tpmstate_set_version(mlp.tpmversion);
if (mlp.fd < 0) {
logprintf(STDERR_FILENO,

View File

@ -82,7 +82,8 @@
#include "swtpm.h"
#include "swtpm_aes.h"
#include "swtpm_debug.h"
#include "swtpm_nvfile.h"
#include "swtpm_nvstore.h"
#include "swtpm_nvstore_dir.h"
#include "key.h"
#include "logging.h"
#include "tpmstate.h"
@ -128,15 +129,10 @@ static encryptionkey migrationkey = {
static uint32_t g_ivec_length;
static unsigned char *g_ivec;
static struct nvram_backend_ops *g_nvram_backend_ops;
/* local prototypes */
static TPM_RESULT SWTPM_NVRAM_GetFilenameForName(char *filename,
size_t bufsize,
uint32_t tpm_number,
const char *name,
bool is_tempfile);
static TPM_RESULT SWTPM_NVRAM_EncryptData(const encryptionkey *key,
tlv_data *td,
size_t *td_len,
@ -167,74 +163,6 @@ static TPM_RESULT SWTPM_NVRAM_CheckHeader(unsigned char *data, uint32_t length,
uint8_t *hdrversion,
bool quiet);
/* A file name in NVRAM is composed of 3 parts:
1 - 'state_directory' is the rooted path to the TPM state home directory
2 = 'tpm_number' is the TPM instance, 00 for a single TPM
2 - the file name
For the IBM cryptographic coprocessor version, the root path is hard coded.
For the Linux and Windows versions, the path comes from an environment variable. This variable is
used once in SWTPM_NVRAM_Init().
One root path is used for all virtual TPM's, so it can be a static variable.
*/
static int lockfile_fd = -1;
char state_directory[FILENAME_MAX];
static TPMLIB_TPMVersion tpmversion = TPMLIB_TPM_VERSION_1_2;
/*
* SWTPM_NVRAM_Set_TPMVersion() - set the version of the TPM being used
*/
void SWTPM_NVRAM_Set_TPMVersion(TPMLIB_TPMVersion version)
{
tpmversion = version;
}
static TPM_RESULT SWTPM_NVRAM_Lock_Lockfile(const char *directory,
int *fd)
{
TPM_RESULT rc = 0;
char *lockfile = NULL;
struct flock flock = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = 0,
};
if (asprintf(&lockfile, "%s/.lock", directory) < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_Lock_Lockfile: Could not asprintf lock filename\n");
return TPM_FAIL;
}
*fd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_NOFOLLOW, 0660);
if (*fd < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_Lock_Lockfile: Could not open lockfile: %s\n",
strerror(errno));
rc = TPM_FAIL;
goto exit;
}
if (fcntl(*fd, F_SETLK, &flock) < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_Lock_Lockfile: Could not lock access to lockfile: %s\n",
strerror(errno));
rc = TPM_FAIL;
close(*fd);
*fd = -1;
}
exit:
free(lockfile);
return rc;
}
/* SWTPM_NVRAM_Init() is called once at startup. It does any NVRAM required initialization.
This function sets some static variables that are used by all TPM's.
@ -242,40 +170,21 @@ exit:
TPM_RESULT SWTPM_NVRAM_Init(void)
{
const char *backend_uri;
TPM_RESULT rc = 0;
const char *tpm_state_path;
size_t length;
TPM_DEBUG(" SWTPM_NVRAM_Init:\n");
/* TPM_NV_DISK TPM emulation stores in local directory determined by environment variable. */
if (rc == 0) {
tpm_state_path = tpmstate_get_dir();
if (tpm_state_path == NULL) {
backend_uri = tpmstate_get_backend_uri();
if (strncmp(backend_uri, "dir://", 6) == 0) {
g_nvram_backend_ops = &nvram_dir_ops;
} else {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_Init: Error (fatal), TPM_PATH environment "
"variable not set\n");
"SWTPM_NVRAM_Init: Unsupported backend.\n");
rc = TPM_FAIL;
}
}
/* check that the directory name plus a file name will not overflow FILENAME_MAX */
if (rc == 0) {
length = strlen(tpm_state_path);
if ((length + TPM_FILENAME_MAX) > FILENAME_MAX) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_Init: Error (fatal), TPM state path name "
"%s too large\n", tpm_state_path);
rc = TPM_FAIL;
}
}
if (rc == 0) {
strcpy(state_directory, tpm_state_path);
TPM_DEBUG("SWTPM_NVRAM_Init: Rooted state path %s\n", state_directory);
}
if (rc == 0 && lockfile_fd < 0)
rc = SWTPM_NVRAM_Lock_Lockfile(state_directory, &lockfile_fd);
if (rc == 0)
rc = g_nvram_backend_ops->prepare(backend_uri);
return rc;
}
@ -297,101 +206,22 @@ SWTPM_NVRAM_LoadData(unsigned char **data, /* freed by caller */
const char *name)
{
TPM_RESULT rc = 0;
size_t src;
int irc;
int fd = -1;
char filename[FILENAME_MAX]; /* rooted file name from name */
unsigned char *decrypt_data = NULL;
uint32_t decrypt_length;
uint32_t dataoffset = 0;
uint8_t hdrversion = 0;
uint16_t hdrflags;
struct stat statbuf;
const char *backend_uri = NULL;
TPM_DEBUG(" SWTPM_NVRAM_LoadData: From file %s\n", name);
*data = NULL;
*length = 0;
/* open the file */
if (rc == 0) {
/* map name to the rooted filename */
rc = SWTPM_NVRAM_GetFilenameForName(filename, sizeof(filename),
tpm_number, name, false);
}
if (rc == 0) {
TPM_DEBUG(" SWTPM_NVRAM_LoadData: Opening file %s\n", filename);
fd = open(filename, O_RDONLY); /* closed @1 */
if (fd < 0) { /* if failure, determine cause */
if (errno == ENOENT) {
TPM_DEBUG("SWTPM_NVRAM_LoadData: No such file %s\n",
filename);
rc = TPM_RETRY; /* first time start up */
}
else {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Error (fatal) opening "
"%s for read, %s\n", filename, strerror(errno));
rc = TPM_FAIL;
}
}
}
if (rc == 0) {
if (fchmod(fd, tpmstate_get_mode()) < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Could not fchmod %s : %s\n",
filename, strerror(errno));
rc = TPM_FAIL;
}
}
/* determine the file length */
if (rc == 0) {
irc = fstat(fd, &statbuf);
if (irc == -1L) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Error (fatal) fstat'ing %s, %s\n",
filename, strerror(errno));
rc = TPM_FAIL;
}
}
if (rc == 0) {
*length = statbuf.st_size; /* save the length */
}
/* allocate a buffer for the actual data */
if ((rc == 0) && *length != 0) {
TPM_DEBUG(" SWTPM_NVRAM_LoadData: Reading %u bytes of data\n", *length);
*data = malloc(*length);
if (!*data) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Error (fatal) allocating %u "
"bytes\n", *length);
rc = TPM_FAIL;
}
}
/* read the contents of the file into the data buffer */
if ((rc == 0) && *length != 0) {
src = read(fd, *data, *length);
if (src != *length) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Error (fatal), data read of %u "
"only read %lu\n", *length, (unsigned long)src);
rc = TPM_FAIL;
}
}
/* close the file */
if (fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_LoadData: Closing file %s\n", filename);
irc = close(fd); /* @1 */
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Error (fatal) closing file %s\n",
filename);
rc = TPM_FAIL;
}
else {
TPM_DEBUG(" SWTPM_NVRAM_LoadData: Closed file %s\n", filename);
}
backend_uri = tpmstate_get_backend_uri();
rc = g_nvram_backend_ops->load(data, length, tpm_number, name,
backend_uri);
}
if (rc == 0) {
@ -455,44 +285,14 @@ SWTPM_NVRAM_StoreData_Intern(const unsigned char *data,
TPM_BOOL encrypt /* encrypt if key is set */)
{
TPM_RESULT rc = 0;
uint32_t lrc;
int irc;
int fd = -1;
int dir_fd = -1;
char tmpfile[FILENAME_MAX]; /* rooted temporary file */
char filename[FILENAME_MAX]; /* rooted file name from name */
unsigned char *filedata = NULL;
uint32_t filedata_length = 0;
tlv_data td[3];
size_t td_len = 0;
uint16_t flags = 0;
const char *backend_uri = NULL;
TPM_DEBUG(" SWTPM_NVRAM_StoreData: To name %s\n", name);
if (rc == 0) {
/* map name to the rooted filename */
rc = SWTPM_NVRAM_GetFilenameForName(filename, sizeof(filename),
tpm_number, name, false);
}
if (rc == 0) {
/* map name to the rooted temporary file */
rc = SWTPM_NVRAM_GetFilenameForName(tmpfile, sizeof(tmpfile),
tpm_number, name, true);
}
if (rc == 0) {
/* open the file */
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Opening file %s\n", tmpfile);
fd = open(tmpfile, O_WRONLY|O_CREAT|O_TRUNC|O_NOFOLLOW,
tpmstate_get_mode()); /* closed @1 */
if (fd < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) opening %s for "
"write failed, %s\n", tmpfile, strerror(errno));
rc = TPM_FAIL;
}
}
if (rc == 0) {
if (encrypt && SWTPM_NVRAM_Has_FileKey()) {
@ -523,96 +323,10 @@ SWTPM_NVRAM_StoreData_Intern(const unsigned char *data,
if (rc == 0)
rc = SWTPM_NVRAM_PrependHeader(&filedata, &filedata_length, flags);
/* write the data to the file */
if (rc == 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Writing %u bytes of data\n", length);
lrc = write_full(fd, filedata, filedata_length);
if (lrc != filedata_length) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal), data write "
"of %u only wrote %u\n", filedata_length, lrc);
rc = TPM_FAIL;
}
}
if (rc == 0 && fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Syncing file %s\n", tmpfile);
irc = fsync(fd);
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) syncing file, %s\n",
strerror(errno));
rc = TPM_FAIL;
} else {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Synced file %s\n", tmpfile);
}
}
if (fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Closing file %s\n", tmpfile);
irc = close(fd); /* @1 */
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) closing file\n");
rc = TPM_FAIL;
}
else {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Closed file %s\n", tmpfile);
}
}
if (rc == 0 && fd >= 0) {
irc = rename(tmpfile, filename);
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) renaming file: %s\n",
strerror(errno));
rc = TPM_FAIL;
} else {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Renamed file to %s\n", filename);
}
}
/*
* Quote from linux man 2 fsync:
* Calling fsync() does not necessarily ensure that the entry in the
* directory containing the file has also reached disk. For that an
* explicit fsync() on a file descriptor for the directory is also needed.
*/
if (rc == 0 && fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Opening dir %s\n", state_directory);
dir_fd = open(state_directory, O_RDONLY);
if (dir_fd < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) opening %s for "
"fsync failed, %s\n", state_directory, strerror(errno));
rc = TPM_FAIL;
}
}
if (rc == 0 && dir_fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Syncing dir %s\n", state_directory);
irc = fsync(dir_fd);
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) syncing dir, %s\n",
strerror(errno));
rc = TPM_FAIL;
} else {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Synced dir %s\n", state_directory);
}
}
if (dir_fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Closing dir %s\n", state_directory);
irc = close(dir_fd);
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) closing dir\n");
rc = TPM_FAIL;
} else {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Closed dir %s\n", state_directory);
}
}
if (rc != 0 && fd >= 0) {
unlink(tmpfile);
backend_uri = tpmstate_get_backend_uri();
rc = g_nvram_backend_ops->store(filedata, filedata_length, tpm_number, name,
backend_uri);
}
tlv_data_free(td, td_len);
@ -631,52 +345,6 @@ TPM_RESULT SWTPM_NVRAM_StoreData(const unsigned char *data,
return SWTPM_NVRAM_StoreData_Intern(data, length, tpm_number, name, TRUE);
}
/* SWTPM_NVRAM_GetFilenameForName() constructs a rooted file name from the name.
The filename is of the form:
state_directory/tpm_number.name
A temporary filename used to write to may be created. It shold be rename()'d to
the non-temporary filename.
*/
static TPM_RESULT SWTPM_NVRAM_GetFilenameForName(char *filename, /* output: rooted filename */
size_t bufsize,
uint32_t tpm_number,
const char *name, /* input: abstract name */
bool is_tempfile) /* input: is temporary file? */
{
TPM_RESULT res = TPM_SUCCESS;
int n;
const char *suffix = "";
TPM_DEBUG(" SWTPM_NVRAM_GetFilenameForName: For name %s\n", name);
switch (tpmversion) {
case TPMLIB_TPM_VERSION_1_2:
break;
case TPMLIB_TPM_VERSION_2:
suffix = "2";
break;
}
if (is_tempfile) {
n = snprintf(filename, bufsize, "%s/TMP%s-%02lx.%s",
state_directory, suffix, (unsigned long)tpm_number, name);
} else {
n = snprintf(filename, bufsize, "%s/tpm%s-%02lx.%s",
state_directory, suffix, (unsigned long)tpm_number, name);
}
if ((size_t)n > bufsize) {
res = TPM_FAIL;
}
TPM_DEBUG(" SWTPM_NVRAM_GetFilenameForName: File name %s\n", filename);
return res;
}
/* SWTPM_NVRAM_DeleteName() deletes the 'name' from NVRAM
Returns:
@ -691,26 +359,11 @@ TPM_RESULT SWTPM_NVRAM_DeleteName(uint32_t tpm_number,
const char *name,
TPM_BOOL mustExist)
{
TPM_RESULT rc = 0;
int irc;
char filename[FILENAME_MAX]; /* rooted file name from name */
const char *backend_uri = NULL;
backend_uri = tpmstate_get_backend_uri();
TPM_DEBUG(" SWTPM_NVRAM_DeleteName: Name %s\n", name);
/* map name to the rooted filename */
rc = SWTPM_NVRAM_GetFilenameForName(filename, sizeof(filename),
tpm_number, name, false);
if (rc == 0) {
irc = remove(filename);
if ((irc != 0) && /* if the remove failed */
(mustExist || /* if any error is a failure, or */
(errno != ENOENT))) { /* if error other than no such file */
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_DeleteName: Error, (fatal) file "
"remove failed, errno %d\n", errno);
rc = TPM_FAIL;
}
}
return rc;
return g_nvram_backend_ops->delete(tpm_number, name, mustExist,
backend_uri);
}

View File

@ -36,27 +36,17 @@
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/********************************************************************************/
#ifndef _SWTPM_NVFILE_H
#define _SWTPM_NVFILE_H
#ifndef _SWTPM_NVSTORE_H
#define _SWTPM_NVSTORE_H
#include <stdio.h>
#include <libtpms/tpm_types.h>
#include <libtpms/tpm_library.h>
#include "key.h"
/* characters in the TPM base file name, 14 for file name, slash, NUL terminator, etc.
This macro is used once during initialization to ensure that the TPM_PATH environment variable
length will not cause the rooted file name to overflow file name buffers.
*/
#define TPM_FILENAME_MAX 20
#include <libtpms/tpm_library.h>
TPM_RESULT SWTPM_NVRAM_Init(void);
void SWTPM_NVRAM_Set_TPMVersion(TPMLIB_TPMVersion version);
/*
Basic abstraction for read and write
*/
@ -107,5 +97,22 @@ static inline TPM_BOOL SWTPM_NVRAM_Has_MigrationKey(void)
return SWTPM_NVRAM_MigrationKey_Size() > 0;
}
#endif /* _SWTPM_NVFILE_H */
struct nvram_backend_ops {
TPM_RESULT (*prepare)(const char *uri);
TPM_RESULT (*load)(unsigned char **data,
uint32_t *length,
uint32_t tpm_number,
const char *name,
const char *uri);
TPM_RESULT (*store)(unsigned char *edata,
uint32_t data_length,
uint32_t tpm_number,
const char *name,
const char *uri);
TPM_RESULT (*delete)(uint32_t tpm_number,
const char *name,
TPM_BOOL mustExist,
const char *uri);
};
#endif /* _SWTPM_NVSTORE_H */

View File

@ -0,0 +1,484 @@
/********************************************************************************/
/* */
/* NVRAM File Abstraction Layer */
/* Written by Ken Goldman */
/* Adapted to SWTPM by Stefan Berger */
/* IBM Thomas J. Watson Research Center */
/* */
/* (c) Copyright IBM Corporation 2006, 2010, 2014, 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libtpms/tpm_error.h>
#include "swtpm.h"
#include "swtpm_debug.h"
#include "swtpm_nvstore.h"
#include "swtpm_nvstore_dir.h"
#include "key.h"
#include "logging.h"
#include "tpmstate.h"
#include "utils.h"
struct nvram_backend_ops nvram_dir_ops = {
.prepare = SWTPM_NVRAM_Prepare_Dir,
.load = SWTPM_NVRAM_LoadData_Dir,
.store = SWTPM_NVRAM_StoreData_Dir,
.delete = SWTPM_NVRAM_DeleteName_Dir,
};
/* SWTPM_NVRAM_GetFilenameForName() constructs a rooted file name from the name.
The filename is of the form:
tpm_state_path/tpm_number.name
A temporary filename used to write to may be created. It should be rename()'d to
the non-temporary filename.
*/
static TPM_RESULT
SWTPM_NVRAM_GetFilenameForName(char *filename, /* output: rooted filename */
size_t bufsize,
uint32_t tpm_number,
const char *name, /* input: abstract name */
bool is_tempfile, /* input: is temporary file? */
const char *tpm_state_path)
{
TPM_RESULT res = TPM_SUCCESS;
int n;
const char *suffix = "";
TPM_DEBUG(" SWTPM_NVRAM_GetFilenameForName: For name %s\n", name);
switch (tpmstate_get_version()) {
case TPMLIB_TPM_VERSION_1_2:
break;
case TPMLIB_TPM_VERSION_2:
suffix = "2";
break;
}
if (is_tempfile) {
n = snprintf(filename, bufsize, "%s/TMP%s-%02lx.%s",
tpm_state_path, suffix, (unsigned long)tpm_number, name);
} else {
n = snprintf(filename, bufsize, "%s/tpm%s-%02lx.%s",
tpm_state_path, suffix, (unsigned long)tpm_number, name);
}
if ((size_t)n > bufsize) {
res = TPM_FAIL;
}
TPM_DEBUG(" SWTPM_NVRAM_GetFilenameForName: File name %s\n", filename);
return res;
}
static const char *
SWTPM_NVRAM_Uri_to_Dir(const char *uri)
{
return uri + strlen("dir://");
}
static TPM_RESULT
SWTPM_NVRAM_Validate_Dir(const char *tpm_state_path)
{
TPM_RESULT rc = 0;
size_t length;
/* TPM_NV_DISK TPM emulation stores in local directory determined by environment variable. */
if (rc == 0) {
if (tpm_state_path == NULL) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_Validate_Dir: Error (fatal), TPM_PATH environment "
"variable not set\n");
rc = TPM_FAIL;
}
}
/* check that the directory name plus a file name will not overflow FILENAME_MAX */
if (rc == 0) {
length = strlen(tpm_state_path);
if ((length + TPM_FILENAME_MAX) > FILENAME_MAX) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_Validate_Dir: Error (fatal), TPM state path name "
"%s too large\n", tpm_state_path);
rc = TPM_FAIL;
}
}
if (rc == 0)
TPM_DEBUG("SWTPM_NVRAM_Validate_Dir: Rooted state path %s\n", tpm_state_path);
return rc;
}
static TPM_RESULT
SWTPM_NVRAM_Lock_Dir(const char *tpm_state_path)
{
TPM_RESULT rc = 0;
int fd;
char *lockfile = NULL;
struct flock flock = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = 0,
};
if (asprintf(&lockfile, "%s/.lock", tpm_state_path) < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_Lock_Dir: Could not asprintf lock filename\n");
return TPM_FAIL;
}
fd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_NOFOLLOW, 0660);
if (fd < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_Lock_Dir: Could not open lockfile: %s\n",
strerror(errno));
rc = TPM_FAIL;
goto exit;
}
if (fcntl(fd, F_SETLK, &flock) < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_Lock_Dir: Could not lock access to lockfile: %s\n",
strerror(errno));
rc = TPM_FAIL;
close(fd);
}
exit:
free(lockfile);
return rc;
}
TPM_RESULT
SWTPM_NVRAM_Prepare_Dir(const char *uri)
{
TPM_RESULT rc = 0;
const char *tpm_state_path = NULL;
tpm_state_path = SWTPM_NVRAM_Uri_to_Dir(uri);
if (rc == 0)
rc = SWTPM_NVRAM_Validate_Dir(tpm_state_path);
if (rc == 0)
rc = SWTPM_NVRAM_Lock_Dir(tpm_state_path);
return rc;
}
TPM_RESULT
SWTPM_NVRAM_LoadData_Dir(unsigned char **data,
uint32_t *length,
uint32_t tpm_number,
const char *name,
const char *uri)
{
TPM_RESULT rc = 0;
int irc;
size_t src;
int fd = -1;
char filename[FILENAME_MAX]; /* rooted file name from name */
struct stat statbuf;
const char *tpm_state_path = NULL;
tpm_state_path = SWTPM_NVRAM_Uri_to_Dir(uri);
/* open the file */
if (rc == 0) {
/* map name to the rooted filename */
rc = SWTPM_NVRAM_GetFilenameForName(filename, sizeof(filename),
tpm_number, name, false,
tpm_state_path);
}
if (rc == 0) {
TPM_DEBUG(" SWTPM_NVRAM_LoadData: Opening file %s\n", filename);
fd = open(filename, O_RDONLY); /* closed @1 */
if (fd < 0) { /* if failure, determine cause */
if (errno == ENOENT) {
TPM_DEBUG("SWTPM_NVRAM_LoadData: No such file %s\n",
filename);
rc = TPM_RETRY; /* first time start up */
}
else {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Error (fatal) opening "
"%s for read, %s\n", filename, strerror(errno));
rc = TPM_FAIL;
}
}
}
if (rc == 0) {
if (fchmod(fd, tpmstate_get_mode()) < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Could not fchmod %s : %s\n",
filename, strerror(errno));
rc = TPM_FAIL;
}
}
/* determine the file length */
if (rc == 0) {
irc = fstat(fd, &statbuf);
if (irc == -1L) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Error (fatal) fstat'ing %s, %s\n",
filename, strerror(errno));
rc = TPM_FAIL;
}
}
if (rc == 0) {
*length = statbuf.st_size; /* save the length */
}
/* allocate a buffer for the actual data */
if ((rc == 0) && *length != 0) {
TPM_DEBUG(" SWTPM_NVRAM_LoadData: Reading %u bytes of data\n", *length);
*data = malloc(*length);
if (!*data) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Error (fatal) allocating %u "
"bytes\n", *length);
rc = TPM_FAIL;
}
}
/* read the contents of the file into the data buffer */
if ((rc == 0) && *length != 0) {
src = read(fd, *data, *length);
if (src != *length) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Error (fatal), data read of %u "
"only read %lu\n", *length, (unsigned long)src);
rc = TPM_FAIL;
}
}
/* close the file */
if (fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_LoadData: Closing file %s\n", filename);
irc = close(fd); /* @1 */
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_LoadData: Error (fatal) closing file %s\n",
filename);
rc = TPM_FAIL;
}
else {
TPM_DEBUG(" SWTPM_NVRAM_LoadData: Closed file %s\n", filename);
}
}
return rc;
}
TPM_RESULT
SWTPM_NVRAM_StoreData_Dir(unsigned char *filedata,
uint32_t filedata_length,
uint32_t tpm_number,
const char *name,
const char *uri)
{
TPM_RESULT rc = 0;
int fd = -1;
int dir_fd = -1;
uint32_t lrc;
int irc;
char tmpfile[FILENAME_MAX]; /* rooted temporary file */
char filename[FILENAME_MAX]; /* rooted file name from name */
const char *tpm_state_path = NULL;
tpm_state_path = SWTPM_NVRAM_Uri_to_Dir(uri);
if (rc == 0) {
/* map name to the rooted filename */
rc = SWTPM_NVRAM_GetFilenameForName(filename, sizeof(filename),
tpm_number, name, false,
tpm_state_path);
}
if (rc == 0) {
/* map name to the rooted temporary file */
rc = SWTPM_NVRAM_GetFilenameForName(tmpfile, sizeof(tmpfile),
tpm_number, name, true,
tpm_state_path);
}
if (rc == 0) {
/* open the file */
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Opening file %s\n", tmpfile);
fd = open(tmpfile, O_WRONLY|O_CREAT|O_TRUNC|O_NOFOLLOW,
tpmstate_get_mode()); /* closed @1 */
if (fd < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) opening %s for "
"write failed, %s\n", tmpfile, strerror(errno));
rc = TPM_FAIL;
}
}
/* write the data to the file */
if (rc == 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Writing %u bytes of data\n",
filedata_length);
lrc = write_full(fd, filedata, filedata_length);
if (lrc != filedata_length) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal), data write "
"of %u only wrote %u\n", filedata_length, lrc);
rc = TPM_FAIL;
}
}
if (rc == 0 && fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Syncing file %s\n", tmpfile);
irc = fsync(fd);
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) syncing file, %s\n",
strerror(errno));
rc = TPM_FAIL;
} else {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Synced file %s\n", tmpfile);
}
}
if (fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Closing file %s\n", tmpfile);
irc = close(fd); /* @1 */
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) closing file\n");
rc = TPM_FAIL;
}
else {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Closed file %s\n", tmpfile);
}
}
if (rc == 0 && fd >= 0) {
irc = rename(tmpfile, filename);
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) renaming file: %s\n",
strerror(errno));
rc = TPM_FAIL;
} else {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Renamed file to %s\n", filename);
}
}
/*
* Quote from linux man 2 fsync:
* Calling fsync() does not necessarily ensure that the entry in the
* directory containing the file has also reached disk. For that an
* explicit fsync() on a file descriptor for the directory is also needed.
*/
if (rc == 0 && fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Opening dir %s\n", tpm_state_path);
dir_fd = open(tpm_state_path, O_RDONLY);
if (dir_fd < 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) opening %s for "
"fsync failed, %s\n", tpm_state_path, strerror(errno));
rc = TPM_FAIL;
}
}
if (rc == 0 && dir_fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Syncing dir %s\n", tpm_state_path);
irc = fsync(dir_fd);
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) syncing dir, %s\n",
strerror(errno));
rc = TPM_FAIL;
} else {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Synced dir %s\n", tpm_state_path);
}
}
if (dir_fd >= 0) {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Closing dir %s\n", tpm_state_path);
irc = close(dir_fd);
if (irc != 0) {
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_StoreData: Error (fatal) closing dir\n");
rc = TPM_FAIL;
} else {
TPM_DEBUG(" SWTPM_NVRAM_StoreData: Closed dir %s\n", tpm_state_path);
}
}
if (rc != 0 && fd >= 0) {
unlink(tmpfile);
}
return rc;
}
TPM_RESULT SWTPM_NVRAM_DeleteName_Dir(uint32_t tpm_number,
const char *name,
TPM_BOOL mustExist,
const char *uri)
{
TPM_RESULT rc = 0;
int irc;
char filename[FILENAME_MAX]; /* rooted file name from name */
const char *tpm_state_path = NULL;
tpm_state_path = SWTPM_NVRAM_Uri_to_Dir(uri);
TPM_DEBUG(" SWTPM_NVRAM_DeleteName: Name %s\n", name);
/* map name to the rooted filename */
rc = SWTPM_NVRAM_GetFilenameForName(filename, sizeof(filename),
tpm_number, name, false,
tpm_state_path);
if (rc == 0) {
irc = remove(filename);
if ((irc != 0) && /* if the remove failed */
(mustExist || /* if any error is a failure, or */
(errno != ENOENT))) { /* if error other than no such file */
logprintf(STDERR_FILENO,
"SWTPM_NVRAM_DeleteName: Error, (fatal) file "
"remove failed, errno %d\n", errno);
rc = TPM_FAIL;
}
}
return rc;
}

View File

@ -0,0 +1,78 @@
/********************************************************************************/
/* */
/* NVRAM Utilities */
/* Written by Ken Goldman */
/* IBM Thomas J. Watson Research Center */
/* */
/* (c) Copyright IBM Corporation 2006, 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. */
/********************************************************************************/
#ifndef _SWTPM_NVSTORE_DIR_H
#define _SWTPM_NVSTORE_DIR_H
#include <libtpms/tpm_types.h>
#include <libtpms/tpm_library.h>
/* characters in the TPM base file name, 14 for file name, slash, NUL terminator, etc.
This macro is used once during initialization to ensure that the TPM_PATH environment variable
length will not cause the rooted file name to overflow file name buffers.
*/
#define TPM_FILENAME_MAX 20
TPM_RESULT
SWTPM_NVRAM_Prepare_Dir(const char *uri);
TPM_RESULT
SWTPM_NVRAM_LoadData_Dir(unsigned char **data,
uint32_t *length,
uint32_t tpm_number,
const char *name,
const char *uri);
TPM_RESULT
SWTPM_NVRAM_StoreData_Dir(unsigned char *filedata,
uint32_t filedata_length,
uint32_t tpm_number,
const char *name,
const char *uri);
TPM_RESULT
SWTPM_NVRAM_DeleteName_Dir(uint32_t tpm_number,
const char *name,
TPM_BOOL mustExist,
const char *uri);
extern struct nvram_backend_ops nvram_dir_ops;
#endif /* _SWTPM_NVSTORE_DIR_H */

View File

@ -51,7 +51,7 @@
#include "tpmlib.h"
#include "logging.h"
#include "tpm_ioctl.h"
#include "swtpm_nvfile.h"
#include "swtpm_nvstore.h"
#include "locality.h"
#ifdef WITH_VTPM_PROXY
#include "vtpm_proxy.h"

View File

@ -37,6 +37,7 @@
#include "config.h"
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
@ -45,14 +46,22 @@
#include "tpmstate.h"
#include "logging.h"
#include "swtpm_nvstore.h"
static char *g_tpmstatedir;
static char *g_backend_uri;
static mode_t g_tpmstate_mode = 0640;
static TPMLIB_TPMVersion g_tpmstate_version = TPMLIB_TPM_VERSION_1_2;
int tpmstate_set_dir(char *tpmstatedir)
void tpmstate_global_free(void)
{
g_tpmstatedir = strdup(tpmstatedir);
if (!g_tpmstatedir) {
free(g_backend_uri);
g_backend_uri = NULL;
}
int tpmstate_set_backend_uri(char *backend_uri)
{
g_backend_uri = strdup(backend_uri);
if (!g_backend_uri) {
logprintf(STDERR_FILENO, "Out of memory.\n");
return -1;
}
@ -60,17 +69,21 @@ int tpmstate_set_dir(char *tpmstatedir)
return 0;
}
void tpmstate_global_free(void)
const char *tpmstate_get_backend_uri(void)
{
free(g_tpmstatedir);
g_tpmstatedir = NULL;
}
if (g_backend_uri)
return g_backend_uri;
const char *tpmstate_get_dir(void)
{
if (g_tpmstatedir)
return g_tpmstatedir;
return getenv("TPM_PATH");
if (getenv("TPM_PATH")) {
if (asprintf(&g_backend_uri, "dir://%s", getenv("TPM_PATH")) < 0) {
logprintf(STDERR_FILENO,
"Could not asprintf TPM backend uri\n");
return NULL;
}
return g_backend_uri;
}
return NULL;
}
int tpmstate_set_mode(mode_t mode)
@ -84,3 +97,13 @@ mode_t tpmstate_get_mode(void)
{
return g_tpmstate_mode;
}
void tpmstate_set_version(TPMLIB_TPMVersion version)
{
g_tpmstate_version = version;
}
TPMLIB_TPMVersion tpmstate_get_version(void)
{
return g_tpmstate_version;
}

View File

@ -39,11 +39,16 @@
#define _SWTPM_TPMSTATE_H_
#include <sys/types.h>
#include <libtpms/tpm_library.h>
int tpmstate_set_backend_uri(char *backend_uri);
const char *tpmstate_get_backend_uri(void);
int tpmstate_set_dir(char *tpmstatdir);
const char *tpmstate_get_dir(void);
int tpmstate_set_mode(mode_t mode);
mode_t tpmstate_get_mode(void);
void tpmstate_global_free(void);
void tpmstate_set_version(TPMLIB_TPMVersion version);
TPMLIB_TPMVersion tpmstate_get_version(void);
#endif /* _SWTPM_TPMSTATE_H_ */