mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
When sending commands to a vTPM, commands with the VTPM_TAG_REQ2 tag are passed directly to the TPM Manager since they are used in the management interface to the TPM Manager. The VTPM_TAG_REQ tag is translated to TPM_TAG_RQU_COMMAND to allow access to the physical TPM for certain ordinals (PCRRead, Extend, and GetRandom). Signed-off-by: Daniel De Graaf <dgdegra@tycho.nsa.gov> Acked-by: Ian Campbell <ian.campbell@citrix.com>
505 lines
14 KiB
C
505 lines
14 KiB
C
/*
|
|
* Copyright (c) 2010-2012 United States Government, as represented by
|
|
* the Secretary of Defense. All rights reserved.
|
|
*
|
|
* THIS SOFTWARE AND ITS DOCUMENTATION ARE PROVIDED AS IS AND WITHOUT
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES WHATSOEVER. ALL WARRANTIES
|
|
* INCLUDING, BUT NOT LIMITED TO, PERFORMANCE, MERCHANTABILITY, FITNESS
|
|
* FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT ARE HEREBY
|
|
* DISCLAIMED. USERS ASSUME THE ENTIRE RISK AND LIABILITY OF USING THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <stdbool.h>
|
|
#include <errno.h>
|
|
#include <sys/time.h>
|
|
#include <xen/xen.h>
|
|
#include <tpmback.h>
|
|
#include <tpmfront.h>
|
|
|
|
#include <polarssl/entropy.h>
|
|
#include <polarssl/ctr_drbg.h>
|
|
|
|
#include "tpm/tpm_emulator_extern.h"
|
|
#include "tpm/tpm_marshalling.h"
|
|
#include "vtpm.h"
|
|
#include "vtpm_cmd.h"
|
|
#include "vtpm_pcrs.h"
|
|
#include "vtpmblk.h"
|
|
#include "vtpm_manager.h"
|
|
|
|
#define TPM_LOG_INFO LOG_INFO
|
|
#define TPM_LOG_ERROR LOG_ERR
|
|
#define TPM_LOG_DEBUG LOG_DEBUG
|
|
|
|
/* Global commandline options - default values */
|
|
struct Opt_args opt_args = {
|
|
.startup = ST_CLEAR,
|
|
.loglevel = TPM_LOG_INFO,
|
|
.hwinitpcrs = VTPM_PCRNONE,
|
|
.tpmconf = 0,
|
|
.enable_maint_cmds = false,
|
|
};
|
|
|
|
static uint32_t badords[32];
|
|
static unsigned int n_badords = 0;
|
|
|
|
entropy_context entropy;
|
|
ctr_drbg_context ctr_drbg;
|
|
|
|
struct tpmfront_dev* tpmfront_dev;
|
|
|
|
void vtpm_get_extern_random_bytes(void *buf, size_t nbytes)
|
|
{
|
|
ctr_drbg_random(&ctr_drbg, buf, nbytes);
|
|
}
|
|
|
|
int vtpm_read_from_file(uint8_t **data, size_t *data_length) {
|
|
return read_vtpmblk(tpmfront_dev, data, data_length);
|
|
}
|
|
|
|
int vtpm_write_to_file(uint8_t *data, size_t data_length) {
|
|
return write_vtpmblk(tpmfront_dev, data, data_length);
|
|
}
|
|
|
|
int vtpm_extern_init_fake(void) {
|
|
return 0;
|
|
}
|
|
|
|
void vtpm_extern_release_fake(void) {
|
|
}
|
|
|
|
|
|
void vtpm_log(int priority, const char *fmt, ...)
|
|
{
|
|
if(opt_args.loglevel >= priority) {
|
|
va_list v;
|
|
va_start(v, fmt);
|
|
vprintf(fmt, v);
|
|
va_end(v);
|
|
}
|
|
}
|
|
|
|
static uint64_t vtpm_get_ticks(void)
|
|
{
|
|
static uint64_t old_t = 0;
|
|
uint64_t new_t, res_t;
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
new_t = (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec;
|
|
res_t = (old_t > 0) ? new_t - old_t : 0;
|
|
old_t = new_t;
|
|
return res_t;
|
|
}
|
|
|
|
|
|
static int tpm_entropy_source(void* dummy, unsigned char* data, size_t len, size_t* olen) {
|
|
UINT32 sz = len;
|
|
TPM_RESULT rc = VTPM_GetRandom(tpmfront_dev, data, &sz);
|
|
*olen = sz;
|
|
return rc == TPM_SUCCESS ? 0 : POLARSSL_ERR_ENTROPY_SOURCE_FAILED;
|
|
}
|
|
|
|
int init_random(void) {
|
|
/* Initialize the rng */
|
|
entropy_init(&entropy);
|
|
entropy_add_source(&entropy, tpm_entropy_source, NULL, 0);
|
|
entropy_gather(&entropy);
|
|
ctr_drbg_init(&ctr_drbg, entropy_func, &entropy, NULL, 0);
|
|
ctr_drbg_set_prediction_resistance( &ctr_drbg, CTR_DRBG_PR_OFF );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int check_passthru(tpmcmd_t* tpmcmd) {
|
|
TPM_TAG tag;
|
|
UINT32 len = 10;
|
|
BYTE* ptr;
|
|
size_t size;
|
|
|
|
if(tpmcmd->req_len < 10) {
|
|
return false;
|
|
}
|
|
|
|
ptr = tpmcmd->req;
|
|
tpm_unmarshal_UINT16(&ptr, &len, &tag);
|
|
|
|
if (tag == VTPM_TAG_REQ2) {
|
|
info("VTPM passthru: %d bytes", (int)tpmcmd->req_len);
|
|
tpmfront_cmd(tpmfront_dev, tpmcmd->req, tpmcmd->req_len, &tpmcmd->resp, &size);
|
|
tpmcmd->resp_len = size;
|
|
info("VTPM passthru return: %d bytes", (int)size);
|
|
return true;
|
|
}
|
|
|
|
if (tag == VTPM_TAG_REQ) {
|
|
info("VTPM pTPM-cmd: %d bytes", (int)tpmcmd->req_len);
|
|
ptr = tpmcmd->req;
|
|
tpm_marshal_UINT16(&ptr, &len, TPM_TAG_RQU_COMMAND);
|
|
tpmfront_cmd(tpmfront_dev, tpmcmd->req, tpmcmd->req_len, &tpmcmd->resp, &size);
|
|
tpmcmd->resp_len = size;
|
|
info("VTPM pTPM-cmd return: %d bytes", (int)size);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int check_ordinal(tpmcmd_t* tpmcmd) {
|
|
TPM_COMMAND_CODE ord;
|
|
UINT32 len = 4;
|
|
BYTE* ptr;
|
|
unsigned int i;
|
|
|
|
if(tpmcmd->req_len < 10) {
|
|
return true;
|
|
}
|
|
|
|
ptr = tpmcmd->req + 6;
|
|
tpm_unmarshal_UINT32(&ptr, &len, &ord);
|
|
|
|
for(i = 0; i < n_badords; ++i) {
|
|
if(ord == badords[i]) {
|
|
error("Disabled command ordinal (%" PRIu32") requested!\n");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
struct locality_item {
|
|
char* lbl;
|
|
uint8_t mask[32];
|
|
};
|
|
#define MAX_CLIENT_LOCALITIES 16
|
|
static struct locality_item client_locality[MAX_CLIENT_LOCALITIES];
|
|
static int nr_client_localities = 0;
|
|
|
|
static void *generate_locality_mask(domid_t domid, unsigned int handle)
|
|
{
|
|
char label[512];
|
|
int i;
|
|
if (tpmback_get_peercontext(domid, handle, label, sizeof(label)))
|
|
BUG();
|
|
for(i=0; i < nr_client_localities; i++) {
|
|
if (!strcmp(client_locality[i].lbl, label))
|
|
goto found;
|
|
if (client_locality[i].lbl[0] == '*') {
|
|
char * f = strstr(label, 1 + client_locality[i].lbl);
|
|
if (!strcmp(f, 1 + client_locality[i].lbl))
|
|
goto found;
|
|
}
|
|
}
|
|
return NULL;
|
|
found:
|
|
tpmback_set_opaque(domid, handle, client_locality[i].mask);
|
|
return client_locality[i].mask;
|
|
}
|
|
|
|
static void main_loop(void) {
|
|
tpmcmd_t* tpmcmd = NULL;
|
|
int res = -1;
|
|
|
|
info("VTPM Initializing\n");
|
|
|
|
/* Set required tpm config args */
|
|
opt_args.tpmconf |= TPM_CONF_STRONG_PERSISTENCE;
|
|
opt_args.tpmconf &= ~TPM_CONF_USE_INTERNAL_PRNG;
|
|
opt_args.tpmconf |= TPM_CONF_GENERATE_EK;
|
|
opt_args.tpmconf |= TPM_CONF_GENERATE_SEED_DAA;
|
|
|
|
/* Initialize the emulator */
|
|
tpm_emulator_init(opt_args.startup, opt_args.tpmconf);
|
|
|
|
/* Initialize any requested PCRs with hardware TPM values */
|
|
if(vtpm_initialize_hw_pcrs(tpmfront_dev, opt_args.hwinitpcrs) != TPM_SUCCESS) {
|
|
error("Failed to initialize PCRs with hardware TPM values");
|
|
goto abort_postpcrs;
|
|
}
|
|
|
|
tpmcmd = tpmback_req_any();
|
|
while(tpmcmd) {
|
|
/* Handle the request */
|
|
if(tpmcmd->req_len) {
|
|
uint8_t* locality_mask = tpmcmd->opaque;
|
|
uint8_t locality_bit = (1 << (tpmcmd->locality & 7));
|
|
int locality_byte = tpmcmd->locality >> 3;
|
|
tpmcmd->resp = NULL;
|
|
tpmcmd->resp_len = 0;
|
|
|
|
if (nr_client_localities && !locality_mask)
|
|
locality_mask = generate_locality_mask(tpmcmd->domid, tpmcmd->handle);
|
|
if (nr_client_localities && !locality_mask) {
|
|
error("Unknown client label in tpm_handle_command");
|
|
create_error_response(tpmcmd, TPM_FAIL);
|
|
}
|
|
else if (nr_client_localities && !(locality_mask[locality_byte] & locality_bit)) {
|
|
error("Invalid locality (%d) for client in tpm_handle_command", tpmcmd->locality);
|
|
create_error_response(tpmcmd, TPM_FAIL);
|
|
}
|
|
/* Check for TPM Manager passthrough command */
|
|
else if(check_passthru(tpmcmd)) {
|
|
}
|
|
/* Check for disabled ordinals */
|
|
else if(!check_ordinal(tpmcmd)) {
|
|
create_error_response(tpmcmd, TPM_BAD_ORDINAL);
|
|
}
|
|
/* If not disabled, do the command */
|
|
else {
|
|
if((res = tpm_handle_command(tpmcmd->req, tpmcmd->req_len, &tpmcmd->resp, &tpmcmd->resp_len, tpmcmd->locality)) != 0) {
|
|
error("tpm_handle_command() failed");
|
|
create_error_response(tpmcmd, TPM_FAIL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Send the response */
|
|
tpmback_resp(tpmcmd);
|
|
|
|
/* Wait for the next request */
|
|
tpmcmd = tpmback_req_any();
|
|
|
|
}
|
|
|
|
abort_postpcrs:
|
|
info("VTPM Shutting down");
|
|
|
|
tpm_emulator_shutdown();
|
|
}
|
|
|
|
int parse_cmd_line(int argc, char** argv)
|
|
{
|
|
char sval[25];
|
|
char* logstr = NULL;
|
|
/* Parse the command strings */
|
|
for(unsigned int i = 1; i < argc; ++i) {
|
|
if (sscanf(argv[i], "loglevel=%25s", sval) == 1){
|
|
if (!strcmp(sval, "debug")) {
|
|
opt_args.loglevel = TPM_LOG_DEBUG;
|
|
logstr = "debug";
|
|
}
|
|
else if (!strcmp(sval, "info")) {
|
|
logstr = "info";
|
|
opt_args.loglevel = TPM_LOG_INFO;
|
|
}
|
|
else if (!strcmp(sval, "error")) {
|
|
logstr = "error";
|
|
opt_args.loglevel = TPM_LOG_ERROR;
|
|
}
|
|
}
|
|
else if (!strcmp(argv[i], "clear")) {
|
|
opt_args.startup = ST_CLEAR;
|
|
}
|
|
else if (!strcmp(argv[i], "save")) {
|
|
opt_args.startup = ST_SAVE;
|
|
}
|
|
else if (!strcmp(argv[i], "deactivated")) {
|
|
opt_args.startup = ST_DEACTIVATED;
|
|
}
|
|
else if (!strncmp(argv[i], "maintcmds=", 10)) {
|
|
if(!strcmp(argv[i] + 10, "1")) {
|
|
opt_args.enable_maint_cmds = true;
|
|
} else if(!strcmp(argv[i] + 10, "0")) {
|
|
opt_args.enable_maint_cmds = false;
|
|
}
|
|
}
|
|
else if(!strncmp(argv[i], "hwinitpcr=", 10)) {
|
|
char *pch = argv[i] + 10;
|
|
unsigned int v1, v2;
|
|
pch = strtok(pch, ",");
|
|
while(pch != NULL) {
|
|
if(!strcmp(pch, "all")) {
|
|
//Set all
|
|
opt_args.hwinitpcrs = VTPM_PCRALL;
|
|
} else if(!strcmp(pch, "none")) {
|
|
//Set none
|
|
opt_args.hwinitpcrs = VTPM_PCRNONE;
|
|
} else if(sscanf(pch, "%u", &v1) == 1) {
|
|
//Set one
|
|
if(v1 >= TPM_NUM_PCR) {
|
|
error("hwinitpcr error: Invalid PCR index %u", v1);
|
|
return -1;
|
|
}
|
|
opt_args.hwinitpcrs |= (1 << v1);
|
|
} else if(sscanf(pch, "%u-%u", &v1, &v2) == 2) {
|
|
//Set range
|
|
if(v1 >= TPM_NUM_PCR) {
|
|
error("hwinitpcr error: Invalid PCR index %u", v1);
|
|
return -1;
|
|
}
|
|
if(v2 >= TPM_NUM_PCR) {
|
|
error("hwinitpcr error: Invalid PCR index %u", v1);
|
|
return -1;
|
|
}
|
|
if(v2 < v1) {
|
|
unsigned tp = v1;
|
|
v1 = v2;
|
|
v2 = tp;
|
|
}
|
|
for(unsigned int i = v1; i <= v2; ++i) {
|
|
opt_args.hwinitpcrs |= (1 << i);
|
|
}
|
|
} else {
|
|
error("hwintipcr error: Invalid PCR specification : %s", pch);
|
|
return -1;
|
|
}
|
|
pch = strtok(NULL, ",");
|
|
}
|
|
}
|
|
else if(!strncmp(argv[i], "locality=", 9)) {
|
|
char *lbl = argv[i] + 9;
|
|
char *pch = strchr(lbl, '=');
|
|
uint8_t* locality_mask = client_locality[nr_client_localities].mask;
|
|
if (pch == NULL) {
|
|
error("Invalid locality specification: %s", lbl);
|
|
return -1;
|
|
}
|
|
if (nr_client_localities == MAX_CLIENT_LOCALITIES) {
|
|
error("Too many locality specifications");
|
|
return -1;
|
|
}
|
|
client_locality[nr_client_localities].lbl = lbl;
|
|
memset(locality_mask, 0, 32);
|
|
nr_client_localities++;
|
|
*pch = 0;
|
|
pch = strtok(pch + 1, ",");
|
|
while (pch != NULL) {
|
|
unsigned int loc;
|
|
if (sscanf(pch, "%u", &loc) == 1 && loc < 256) {
|
|
uint8_t locality_bit = (1 << (loc & 7));
|
|
int locality_byte = loc >> 3;
|
|
locality_mask[locality_byte] |= locality_bit;
|
|
} else {
|
|
error("Invalid locality item: %s", pch);
|
|
return -1;
|
|
}
|
|
pch = strtok(NULL, ",");
|
|
}
|
|
}
|
|
else {
|
|
error("Invalid command line option `%s'", argv[i]);
|
|
}
|
|
|
|
}
|
|
|
|
/* Check Errors and print results */
|
|
switch(opt_args.startup) {
|
|
case ST_CLEAR:
|
|
info("Startup mode is `clear'");
|
|
break;
|
|
case ST_SAVE:
|
|
info("Startup mode is `save'");
|
|
break;
|
|
case ST_DEACTIVATED:
|
|
info("Startup mode is `deactivated'");
|
|
break;
|
|
default:
|
|
error("Invalid startup mode %d", opt_args.startup);
|
|
return -1;
|
|
}
|
|
|
|
if(opt_args.hwinitpcrs & (VTPM_PCRALL))
|
|
{
|
|
char pcrstr[1024];
|
|
char* ptr = pcrstr;
|
|
|
|
pcrstr[0] = '\0';
|
|
info("The following PCRs will be initialized with values from the hardware TPM:");
|
|
for(unsigned int i = 0; i < TPM_NUM_PCR; ++i) {
|
|
if(opt_args.hwinitpcrs & (1 << i)) {
|
|
ptr += sprintf(ptr, "%u, ", i);
|
|
}
|
|
}
|
|
/* get rid of the last comma if any numbers were printed */
|
|
*(ptr -2) = '\0';
|
|
|
|
info("\t%s", pcrstr);
|
|
} else {
|
|
info("All PCRs initialized to default values");
|
|
}
|
|
|
|
if(!opt_args.enable_maint_cmds) {
|
|
info("TPM Maintenance Commands disabled");
|
|
badords[n_badords++] = TPM_ORD_CreateMaintenanceArchive;
|
|
badords[n_badords++] = TPM_ORD_LoadMaintenanceArchive;
|
|
badords[n_badords++] = TPM_ORD_KillMaintenanceFeature;
|
|
badords[n_badords++] = TPM_ORD_LoadManuMaintPub;
|
|
badords[n_badords++] = TPM_ORD_ReadManuMaintPub;
|
|
} else {
|
|
info("TPM Maintenance Commands enabled");
|
|
}
|
|
|
|
info("Log level set to %s", logstr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cleanup_opt_args(void) {
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
//FIXME: initializing blkfront without this sleep causes the domain to crash on boot
|
|
sleep(2);
|
|
|
|
/* Setup extern function pointers */
|
|
tpm_extern_init = vtpm_extern_init_fake;
|
|
tpm_extern_release = vtpm_extern_release_fake;
|
|
tpm_malloc = malloc;
|
|
tpm_free = free;
|
|
tpm_log = vtpm_log;
|
|
tpm_get_ticks = vtpm_get_ticks;
|
|
tpm_get_extern_random_bytes = vtpm_get_extern_random_bytes;
|
|
tpm_write_to_storage = vtpm_write_to_file;
|
|
tpm_read_from_storage = vtpm_read_from_file;
|
|
|
|
info("starting TPM Emulator (1.2.%d.%d-%d)", VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD);
|
|
if(parse_cmd_line(argc, argv)) {
|
|
error("Error parsing commandline\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Initialize devices */
|
|
init_tpmback(NULL, NULL);
|
|
if((tpmfront_dev = init_tpmfront(NULL)) == NULL) {
|
|
error("Unable to initialize tpmfront device");
|
|
goto abort_posttpmfront;
|
|
}
|
|
|
|
/* Seed the RNG with entropy from hardware TPM */
|
|
if(init_random()) {
|
|
error("Unable to initialize RNG");
|
|
goto abort_postrng;
|
|
}
|
|
|
|
/* Initialize blkfront device */
|
|
if(init_vtpmblk(tpmfront_dev)) {
|
|
error("Unable to initialize Blkfront persistent storage");
|
|
goto abort_postvtpmblk;
|
|
}
|
|
|
|
/* Run main loop */
|
|
main_loop();
|
|
|
|
/* Shutdown blkfront */
|
|
shutdown_vtpmblk();
|
|
abort_postvtpmblk:
|
|
abort_postrng:
|
|
|
|
/* Close devices */
|
|
shutdown_tpmfront(tpmfront_dev);
|
|
abort_posttpmfront:
|
|
shutdown_tpmback();
|
|
|
|
cleanup_opt_args();
|
|
|
|
return 0;
|
|
}
|