/*
* Licensed Materials - Property of IBM
*
* trousers - An open source TCG Software Stack
*
* (C) Copyright International Business Machines Corp. 2004-2006
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#if defined (HAVE_BYTEORDER_H)
#include <sys/byteorder.h>
#elif defined (HTOLE_DEFINED)
#ifndef __APPLE__
#include <endian.h>
#else
#include "portable_endian.h"
#endif
#define LE_16 htole16
#define LE_32 htole32
#define LE_64 htole64
#else
#define LE_16(x) (x)
#define LE_32(x) (x)
#define LE_64(x) (x)
#endif
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include "trousers/tss.h"
#include "trousers_types.h"
#include "tcsps.h"
#include "tcs_tsp.h"
#include "tcs_utils.h"
#include "capabilities.h"
#include "tcslog.h"
#include "tcsd_wrap.h"
#include "tcsd.h"
int system_ps_fd = -1;
MUTEX_DECLARE(disk_cache_lock);
static struct flock fl;
int
get_file()
{
int rc;
/* check the global file handle first. If it exists, lock it and return */
if (system_ps_fd != -1) {
int rc = 0;
fl.l_type = F_WRLCK;
if ((rc = fcntl(system_ps_fd, F_SETLKW, &fl))) {
LogError("failed to get system PS lock: %s", strerror(errno));
return -1;
}
return system_ps_fd;
}
/* open and lock the file */
system_ps_fd = open(tcsd_options.system_ps_file, O_CREAT|O_RDWR, 0600);
if (system_ps_fd < 0) {
LogError("system PS: open() of %s failed: %s",
tcsd_options.system_ps_file, strerror(errno));
return -1;
}
fl.l_type = F_WRLCK;
if ((rc = fcntl(system_ps_fd, F_SETLKW, &fl))) {
LogError("failed to get system PS lock of file %s: %s",
tcsd_options.system_ps_file, strerror(errno));
return -1;
}
return system_ps_fd;
}
int
put_file(int fd)
{
int rc = 0;
/* release the file lock */
fl.l_type = F_UNLCK;
if ((rc = fcntl(fd, F_SETLKW, &fl))) {
LogError("failed to unlock system PS file: %s",
strerror(errno));
return -1;
}
return rc;
}
void
close_file(int fd)
{
close(fd);
system_ps_fd = -1;
}
TSS_RESULT
psfile_get_parent_uuid_by_uuid(int fd, TSS_UUID *uuid, TSS_UUID *ret_uuid)
{
int rc;
UINT32 file_offset = 0;
struct key_disk_cache *tmp;
MUTEX_LOCK(disk_cache_lock);
tmp = key_disk_cache_head;
while (tmp) {
if (memcmp(uuid, &tmp->uuid, sizeof(TSS_UUID)) || !(tmp->flags & CACHE_FLAG_VALID)) {
tmp = tmp->next;
continue;
}
/* jump to the location of the parent uuid */
file_offset = TSSPS_PARENT_UUID_OFFSET(tmp);
rc = lseek(fd, file_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
MUTEX_UNLOCK(disk_cache_lock);
return -1;
}
if ((rc = read_data(fd, ret_uuid, sizeof(TSS_UUID)))) {
LogError("%s", __FUNCTION__);
MUTEX_UNLOCK(disk_cache_lock);
return rc;
}
MUTEX_UNLOCK(disk_cache_lock);
return TSS_SUCCESS;
}
MUTEX_UNLOCK(disk_cache_lock);
/* key not found */
return -2;
}
/*
* return a key blob from PS given a uuid
*/
TSS_RESULT
psfile_get_key_by_uuid(int fd, TSS_UUID *uuid, BYTE *ret_buffer, UINT16 *ret_buffer_size)
{
int rc;
UINT32 file_offset = 0;
struct key_disk_cache *tmp;
MUTEX_LOCK(disk_cache_lock);
tmp = key_disk_cache_head;
while (tmp) {
if (memcmp(uuid, &tmp->uuid, sizeof(TSS_UUID)) || !(tmp->flags & CACHE_FLAG_VALID)) {
tmp = tmp->next;
continue;
}
/* jump to the location of the key blob */
file_offset = TSSPS_BLOB_DATA_OFFSET(tmp);
rc = lseek(fd, file_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
MUTEX_UNLOCK(disk_cache_lock);
return TCSERR(TSS_E_INTERNAL_ERROR);
}
/* we found the key; file ptr is pointing at the blob */
if (*ret_buffer_size < tmp->blob_size) {
/* not enough room */
MUTEX_UNLOCK(disk_cache_lock);
return TCSERR(TSS_E_FAIL);
}
if ((rc = read_data(fd, ret_buffer, tmp->blob_size))) {
LogError("%s", __FUNCTION__);
MUTEX_UNLOCK(disk_cache_lock);
return rc;
}
*ret_buffer_size = tmp->blob_size;
LogDebugUnrollKey(ret_buffer);
MUTEX_UNLOCK(disk_cache_lock);
return TSS_SUCCESS;
}
MUTEX_UNLOCK(disk_cache_lock);
/* key not found */
return TCSERR(TSS_E_FAIL);
}
/*
* return a key blob from PS given its cache entry. The disk cache must be
* locked by the caller.
*/
TSS_RESULT
psfile_get_key_by_cache_entry(int fd, struct key_disk_cache *c, BYTE *ret_buffer,
UINT16 *ret_buffer_size)
{
int rc;
UINT32 file_offset = 0;
/* jump to the location of the key blob */
file_offset = TSSPS_BLOB_DATA_OFFSET(c);
rc = lseek(fd, file_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
return TCSERR(TSS_E_INTERNAL_ERROR);
}
/* we found the key; file ptr is pointing at the blob */
if (*ret_buffer_size < c->blob_size) {
/* not enough room */
LogError("%s: Buf size too small. Needed %d bytes, passed %d", __FUNCTION__,
c->blob_size, *ret_buffer_size);
return TCSERR(TSS_E_INTERNAL_ERROR);
}
if ((rc = read_data(fd, ret_buffer, c->blob_size))) {
LogError("%s: error reading %d bytes", __FUNCTION__, c->blob_size);
return TCSERR(TSS_E_INTERNAL_ERROR);
}
*ret_buffer_size = c->blob_size;
return TSS_SUCCESS;
}
/*
* return the vendor data from PS given its cache entry. The disk cache must be
* locked by the caller.
*/
TSS_RESULT
psfile_get_vendor_data(int fd, struct key_disk_cache *c, UINT32 *size, BYTE **data)
{
int rc;
UINT32 file_offset;
/* jump to the location of the data */
file_offset = TSSPS_VENDOR_DATA_OFFSET(c);
rc = lseek(fd, file_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
return TCSERR(TSS_E_INTERNAL_ERROR);
}
if ((*data = malloc(c->vendor_data_size)) == NULL) {
LogError("malloc of %u bytes failed", c->vendor_data_size);
return TCSERR(TSS_E_OUTOFMEMORY);
}
if ((rc = read_data(fd, *data, c->vendor_data_size))) {
LogError("%s: error reading %u bytes", __FUNCTION__, c->vendor_data_size);
free(*data);
*data = NULL;
return TCSERR(TSS_E_INTERNAL_ERROR);
}
*size = c->vendor_data_size;
return TSS_SUCCESS;
}
TSS_RESULT
psfile_get_ps_type_by_uuid(int fd, TSS_UUID *uuid, UINT32 *ret_ps_type)
{
struct key_disk_cache *tmp;
MUTEX_LOCK(disk_cache_lock);
tmp = key_disk_cache_head;
while (tmp) {
if (memcmp(uuid, &tmp->uuid, sizeof(TSS_UUID)) ||
!(tmp->flags & CACHE_FLAG_VALID)) {
tmp = tmp->next;
continue;
}
if (tmp->flags & CACHE_FLAG_PARENT_PS_SYSTEM) {
*ret_ps_type = TSS_PS_TYPE_SYSTEM;
goto done;
} else
break;
}
*ret_ps_type = TSS_PS_TYPE_USER;
done:
MUTEX_UNLOCK(disk_cache_lock);
return TSS_SUCCESS;
}
TSS_RESULT
psfile_is_pub_registered(int fd, TCPA_STORE_PUBKEY *pub, TSS_BOOL *is_reg)
{
int rc;
UINT32 file_offset = 0;
struct key_disk_cache *tmp;
char tmp_buffer[2048];
MUTEX_LOCK(disk_cache_lock);
tmp = key_disk_cache_head;
while (tmp) {
/* if the key is of the wrong size or is invalid, try the next one */
if (pub->keyLength != tmp->pub_data_size || !(tmp->flags & CACHE_FLAG_VALID)) {
tmp = tmp->next;
continue;
}
/* we have a valid key with the same key size as the one we're looking for.
* grab the pub key data off disk and compare it. */
/* jump to the location of the public key */
file_offset = TSSPS_PUB_DATA_OFFSET(tmp);
rc = lseek(fd, file_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
MUTEX_UNLOCK(disk_cache_lock);
return TCSERR(TSS_E_INTERNAL_ERROR);
}
DBG_ASSERT(tmp->pub_data_size < 2048);
/* read in the key */
if ((rc = read_data(fd, tmp_buffer, tmp->pub_data_size))) {
LogError("%s", __FUNCTION__);
MUTEX_UNLOCK(disk_cache_lock);
return rc;
}
/* do the compare */
if (memcmp(tmp_buffer, pub->key, tmp->pub_data_size)) {
tmp = tmp->next;
continue;
}
/* the key matches, copy the uuid out */
*is_reg = TRUE;
MUTEX_UNLOCK(disk_cache_lock);
return TSS_SUCCESS;
}
MUTEX_UNLOCK(disk_cache_lock);
/* key not found */
*is_reg = FALSE;
return TSS_SUCCESS;
}
TSS_RESULT
psfile_get_uuid_by_pub(int fd, TCPA_STORE_PUBKEY *pub, TSS_UUID **ret_uuid)
{
int rc;
UINT32 file_offset = 0;
struct key_disk_cache *tmp;
char tmp_buffer[2048];
MUTEX_LOCK(disk_cache_lock);
tmp = key_disk_cache_head;
while (tmp) {
/* if the key is of the wrong size or is invalid, try the next one */
if (pub->keyLength != tmp->pub_data_size || !(tmp->flags & CACHE_FLAG_VALID)) {
tmp = tmp->next;
continue;
}
/* we have a valid key with the same key size as the one we're looking for.
* grab the pub key data off disk and compare it. */
/* jump to the location of the public key */
file_offset = TSSPS_PUB_DATA_OFFSET(tmp);
rc = lseek(fd, file_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
MUTEX_UNLOCK(disk_cache_lock);
return TCSERR(TSS_E_INTERNAL_ERROR);
}
DBG_ASSERT(tmp->pub_data_size < 2048);
if (tmp->pub_data_size > sizeof(tmp_buffer)) {
LogError("Source buffer size too big! Size: %d",
tmp->pub_data_size);
MUTEX_UNLOCK(disk_cache_lock);
return TCSERR(TSS_E_INTERNAL_ERROR);
}
/* read in the key */
if ((rc = read_data(fd, tmp_buffer, tmp->pub_data_size))) {
LogError("%s", __FUNCTION__);
MUTEX_UNLOCK(disk_cache_lock);
return rc;
}
/* do the compare */
if (memcmp(tmp_buffer, pub->key, tmp->pub_data_size)) {
tmp = tmp->next;
continue;
}
*ret_uuid = (TSS_UUID *)malloc(sizeof(TSS_UUID));
if (*ret_uuid == NULL) {
LogError("malloc of %zd bytes failed.", sizeof(TSS_UUID));
MUTEX_UNLOCK(disk_cache_lock);
return TCSERR(TSS_E_OUTOFMEMORY);
}
/* the key matches, copy the uuid out */
memcpy(*ret_uuid, &tmp->uuid, sizeof(TSS_UUID));
MUTEX_UNLOCK(disk_cache_lock);
return TSS_SUCCESS;
}
MUTEX_UNLOCK(disk_cache_lock);
/* key not found */
return TCSERR(TSS_E_PS_KEY_NOTFOUND);
}
TSS_RESULT
psfile_get_key_by_pub(int fd, TCPA_STORE_PUBKEY *pub, UINT32 *size, BYTE **ret_key)
{
int rc;
UINT32 file_offset = 0;
struct key_disk_cache *tmp;
BYTE tmp_buffer[4096];
MUTEX_LOCK(disk_cache_lock);
tmp = key_disk_cache_head;
while (tmp) {
/* if the key is of the wrong size or is invalid, try the next one */
if (pub->keyLength != tmp->pub_data_size || !(tmp->flags & CACHE_FLAG_VALID)) {
tmp = tmp->next;
continue;
}
/* we have a valid key with the same key size as the one we're looking for.
* grab the pub key data off disk and compare it. */
/* jump to the location of the public key */
file_offset = TSSPS_PUB_DATA_OFFSET(tmp);
rc = lseek(fd, file_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
MUTEX_UNLOCK(disk_cache_lock);
return TCSERR(TSS_E_INTERNAL_ERROR);
}
DBG_ASSERT(tmp->pub_data_size < 2048);
if (tmp->pub_data_size > sizeof(tmp_buffer)) {
LogError("Source buffer size too big! Size: %d",
tmp->pub_data_size);
MUTEX_UNLOCK(disk_cache_lock);
return TCSERR(TSS_E_INTERNAL_ERROR);
}
/* read in the key */
if ((rc = read_data(fd, tmp_buffer, tmp->pub_data_size))) {
LogError("%s", __FUNCTION__);
MUTEX_UNLOCK(disk_cache_lock);
return rc;
}
/* do the compare */
if (memcmp(tmp_buffer, pub->key, tmp->pub_data_size)) {
tmp = tmp->next;
continue;
}
/* jump to the location of the key blob */
file_offset = TSSPS_BLOB_DATA_OFFSET(tmp);
rc = lseek(fd, file_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
MUTEX_UNLOCK(disk_cache_lock);
return TCSERR(TSS_E_INTERNAL_ERROR);
}
DBG_ASSERT(tmp->blob_size < 4096);
if (tmp->blob_size > sizeof(tmp_buffer)) {
LogError("Blob size greater than 4096! Size: %d",
tmp->blob_size);
MUTEX_UNLOCK(disk_cache_lock);
return TCSERR(TSS_E_INTERNAL_ERROR);
}
/* read in the key blob */
if ((rc = read_data(fd, tmp_buffer, tmp->blob_size))) {
LogError("%s", __FUNCTION__);
MUTEX_UNLOCK(disk_cache_lock);
return rc;
}
*ret_key = malloc(tmp->blob_size);
if (*ret_key == NULL) {
LogError("malloc of %d bytes failed.", tmp->blob_size);
MUTEX_UNLOCK(disk_cache_lock);
return TCSERR(TSS_E_OUTOFMEMORY);
}
memcpy(*ret_key, tmp_buffer, tmp->blob_size);
*size = tmp->blob_size;
MUTEX_UNLOCK(disk_cache_lock);
return rc;
}
MUTEX_UNLOCK(disk_cache_lock);
/* key not found */
return -2;
}
/*
* disk store format:
*
* TrouSerS 0.2.0 and before:
* Version 0: cached?
* [UINT32 num_keys_on_disk]
* [TSS_UUID uuid0 ] yes
* [TSS_UUID uuid_parent0 ] yes
* [UINT16 pub_data_size0 ] yes
* [UINT16 blob_size0 ] yes
* [UINT16 cache_flags0 ] yes
* [BYTE[] pub_data0 ]
* [BYTE[] blob0 ]
* [...]
*
* TrouSerS 0.2.1+
* Version 1: cached?
* [BYTE PS version = '\1']
* [UINT32 num_keys_on_disk ]
* [TSS_UUID uuid0 ] yes
* [TSS_UUID uuid_parent0 ] yes
* [UINT16 pub_data_size0 ] yes
* [UINT16 blob_size0 ] yes
* [UINT32 vendor_data_size0] yes
* [UINT16 cache_flags0 ] yes
* [BYTE[] pub_data0 ]
* [BYTE[] blob0 ]
* [BYTE[] vendor_data0 ]
* [...]
*
*/
TSS_RESULT
psfile_write_key(int fd,
TSS_UUID *uuid,
TSS_UUID *parent_uuid,
UINT32 *parent_ps,
BYTE *vendor_data,
UINT32 vendor_size,
BYTE *key_blob,
UINT16 key_blob_size)
{
TSS_KEY key;
UINT16 pub_key_size, cache_flags = CACHE_FLAG_VALID;
UINT64 offset;
int rc = 0;
/* leaving the cache flag for parent ps type as 0 implies TSS_PS_TYPE_USER */
if (*parent_ps == TSS_PS_TYPE_SYSTEM)
cache_flags |= CACHE_FLAG_PARENT_PS_SYSTEM;
/* Unload the blob to get the public key */
offset = 0;
if ((rc = UnloadBlob_TSS_KEY(&offset, key_blob, &key)))
return rc;
pub_key_size = key.pubKey.keyLength;
if ((rc = write_key_init(fd, pub_key_size, key_blob_size, vendor_size)) < 0)
goto done;
/* offset now holds the number of bytes from the beginning of the file
* the key will be stored at
*/
offset = rc;
#ifdef TSS_DEBUG
if (offset == 0)
LogDebug("ERROR: key being written with offset 0!!");
#endif
/* [TSS_UUID uuid0 ] yes */
if ((rc = write_data(fd, (void *)uuid, sizeof(TSS_UUID)))) {
LogError("%s", __FUNCTION__);
goto done;
}
/* [TSS_UUID uuid_parent0 ] yes */
if ((rc = write_data(fd, (void *)parent_uuid, sizeof(TSS_UUID)))) {
LogError("%s", __FUNCTION__);
goto done;
}
/* [UINT16 pub_data_size0 ] yes */
pub_key_size = LE_16(pub_key_size);
if ((rc = write_data(fd, &pub_key_size, sizeof(UINT16)))) {
LogError("%s", __FUNCTION__);
goto done;
}
/* Swap it back for later */
pub_key_size = LE_16(pub_key_size);
/* [UINT16 blob_size0 ] yes */
key_blob_size = LE_16(key_blob_size);
if ((rc = write_data(fd, &key_blob_size, sizeof(UINT16)))) {
LogError("%s", __FUNCTION__);
goto done;
}
/* Swap it back for later */
key_blob_size = LE_16(key_blob_size);
/* [UINT32 vendor_data_size0 ] yes */
vendor_size = LE_32(vendor_size);
if ((rc = write_data(fd, &vendor_size, sizeof(UINT32)))) {
LogError("%s", __FUNCTION__);
goto done;
}
/* Swap it back for later */
vendor_size = LE_32(vendor_size);
/* [UINT16 cache_flags0 ] yes */
cache_flags = LE_16(cache_flags);
if ((rc = write_data(fd, &cache_flags, sizeof(UINT16)))) {
LogError("%s", __FUNCTION__);
goto done;
}
/* Swap it back for later */
cache_flags = LE_16(cache_flags);
/* [BYTE[] pub_data0 ] no */
if ((rc = write_data(fd, (void *)key.pubKey.key, pub_key_size))) {
LogError("%s", __FUNCTION__);
goto done;
}
/* [BYTE[] blob0 ] no */
if ((rc = write_data(fd, (void *)key_blob, key_blob_size))) {
LogError("%s", __FUNCTION__);
goto done;
}
/* [BYTE[] vendor_data0 ] no */
if (vendor_size > 0) {
if ((rc = write_data(fd, (void *)vendor_data, vendor_size))) {
LogError("%s", __FUNCTION__);
goto done;
}
}
if ((rc = cache_key((UINT32)offset, cache_flags, uuid, parent_uuid, pub_key_size,
key_blob_size, vendor_size)))
goto done;
done:
destroy_key_refs(&key);
return rc;
}
TSS_RESULT
psfile_remove_key(int fd, struct key_disk_cache *c)
{
TSS_RESULT result;
UINT32 head_offset = 0, tail_offset, num_keys;
BYTE buf[4096];
struct stat stat_buf;
int rc, size = 0;
if ((rc = fstat(fd, &stat_buf)) != 0) {
LogError("fstat: %s", strerror(errno));
return TSS_E_INTERNAL_ERROR;
}
/* head_offset is the offset the beginning of the key */
head_offset = TSSPS_UUID_OFFSET(c);
/* tail_offset is the offset the beginning of the next key */
tail_offset = TSSPS_VENDOR_DATA_OFFSET(c) + c->vendor_data_size;
rc = lseek(fd, tail_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
return TCSERR(TSS_E_INTERNAL_ERROR);
}
/* read in from tail, write out to head to fill the gap */
while ((rc = read(fd, buf, sizeof(buf))) > 0) {
size = rc;
tail_offset += size;
/* set the file pointer to where we want to write */
rc = lseek(fd, head_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
return TCSERR(TSS_E_INTERNAL_ERROR);
}
/* write the data */
if ((result = write_data(fd, (void *)buf, size))) {
LogError("%s", __FUNCTION__);
return result;
}
head_offset += size;
/* set the file pointer to where we want to read in the next
* loop */
rc = lseek(fd, tail_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
return TCSERR(TSS_E_INTERNAL_ERROR);
}
}
if (rc < 0) {
LogError("read: %s", strerror(errno));
return TCSERR(TSS_E_INTERNAL_ERROR);
}
/* set the file pointer to where we want to write */
rc = lseek(fd, head_offset, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
return TCSERR(TSS_E_INTERNAL_ERROR);
}
/* head_offset now contains a pointer to where we want to truncate the
* file. Zero out the old tail end of the file and truncate it. */
memset(buf, 0, sizeof(buf));
/* Zero out the old tail end of the file */
if ((result = write_data(fd, (void *)buf, tail_offset - head_offset))) {
LogError("%s", __FUNCTION__);
return result;
}
if ((rc = ftruncate(fd, head_offset)) < 0) {
LogError("ftruncate: %s", strerror(errno));
return TCSERR(TSS_E_INTERNAL_ERROR);
}
/* we succeeded in removing a key from the disk. Decrement the number
* of keys in the file */
rc = lseek(fd, TSSPS_NUM_KEYS_OFFSET, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
return TCSERR(TSS_E_INTERNAL_ERROR);
}
rc = read(fd, &num_keys, sizeof(UINT32));
num_keys = LE_32(num_keys);
if (rc != sizeof(UINT32)) {
LogError("read of %zd bytes: %s", sizeof(UINT32), strerror(errno));
return TCSERR(TSS_E_INTERNAL_ERROR);
}
rc = lseek(fd, TSSPS_NUM_KEYS_OFFSET, SEEK_SET);
if (rc == ((off_t) - 1)) {
LogError("lseek: %s", strerror(errno));
return TCSERR(TSS_E_INTERNAL_ERROR);
}
/* decrement, then write back out to disk */
num_keys--;
num_keys = LE_32(num_keys);
if ((result = write_data(fd, (void *)&num_keys, sizeof(UINT32)))) {
LogError("%s", __FUNCTION__);
return result;
}
return TSS_SUCCESS;
}