Commit 0d749155 authored by Robert P Ricci's avatar Robert P Ricci

First cut at 'quoteprep' tmcd command

This command is for TPM secure booting, and does three things:
1) Figures out what sets of PCRs a node is supposed to include in
   its quote to get to the given state
2) Gives the node its (encrypted) identity key for use in generating
   the quote
3) Generates a noce for replay prevention, stores it in the database,
   and passes it back to the client

This version compiles, but it totally untested, so I'm sure it doesn't
work yet. I haven't added the database modifications to the schema file
yet, since it's not certain that I've got 'em right.
parent f48c4e02
......@@ -41,6 +41,9 @@
#include "event.h"
#endif
/* XXX: Not sure this is okay! */
#include "tpm.h"
/*
* XXX This needs to be localized!
*/
......@@ -288,6 +291,7 @@ COMMAND_PROTOTYPE(dotpmpubkey);
COMMAND_PROTOTYPE(dotpmdummy);
COMMAND_PROTOTYPE(dodhcpdconf);
COMMAND_PROTOTYPE(dosecurestate);
COMMAND_PROTOTYPE(doquoteprep);
/*
* The fullconfig slot determines what routines get called when pushing
......@@ -390,6 +394,8 @@ struct command {
{ "tpmdummy", FULLCONFIG_ALL, F_REQTPM, dotpmdummy },
{ "dhcpdconf", FULLCONFIG_ALL, 0, dodhcpdconf },
{ "securestate", FULLCONFIG_NONE, F_REQTPM, dosecurestate},
{ "securestate", FULLCONFIG_NONE, F_REMREQSSL, dosecurestate},
{ "quoteprep", FULLCONFIG_NONE, F_REMREQSSL, doquoteprep},
};
static int numcommands = sizeof(command_array)/sizeof(struct command);
......@@ -4355,6 +4361,155 @@ COMMAND_PROTOTYPE(dosecurestate)
}
/*
* Prepare for a TPM quote: give the client the encrypted identity key,
* a nonce to use in the quote, and the set of PCRs that need to be included in
* the quote. This saves some state (the nonce) that will be checked again in
* dosecurestate().
*/
COMMAND_PROTOTYPE(doquoteprep)
{
char newstate[128]; /* More then we will ever need */
TPM_NONCE nonce;
char nonce_hex[2*TPM_NONCE_BYTES + 1];
int i;
// XXX: is MYBUFSIZE big enough?
char buf[MYBUFSIZE];
char *bufp = buf;
char *bufe = &buf[MYBUFSIZE];
MYSQL_RES *res;
MYSQL_ROW row;
int nrows;
unsigned long *nlen;
/*
* Dig out state that the node is reporting - we need this so that we
* can tell it what PCRs to include
*/
if (sscanf(rdata, "%128s", newstate) != 1 ||
strlen(newstate) == sizeof(newstate)) {
error("DOQUOTEPREP: %s: Bad arguments\n", reqp->nodeid);
return 1;
}
/*
* Get the set of PCRs that have to be quoted to move into this state.
*/
res = mydb_query("select q.pcr from nodes as n "
"left join tpm_quote_values as q "
"on n.op_mode = q.op_mode "
"where n.node_id='%s' and q.state ='%s'",
1, reqp->nodeid,newstate);
if (!res){
error("quoteprep: %s: DB error getting pcr list\n",
reqp->nodeid);
return 1;
}
nrows = mysql_num_rows(res);
if (!nrows){
error("%s: no TPM quote values in database for state %s\n",
reqp->nodeid,newstate);
mysql_free_result(res);
return 1;
}
bufp += OUTPUT(bufp, bufe - bufp, "PCR=");
for (i = 0; i < nrows; i++) {
row = mysql_fetch_row(res);
// XXX: Is this already passed to us as a string?
bufp += OUTPUT(bufp, bufe - bufp,"%s",row[0]);
if (i < (nrows - 1)) {
bufp += OUTPUT(bufp, bufe - bufp, ",");
}
}
bufp += OUTPUT(bufp, bufe - bufp, " ");
mysql_free_result(res);
/*
* Grab the (encrypted) identity key for the node - noone else will be
* able to decrypt it, so we don't have to be too paranoid about who
* we give it to.
*/
res = mydb_query("select tpmidentity "
"from node_hostkeys "
"where node_id='%s' ",
1, reqp->nodeid);
if (!res){
error("quoteprep: %s: DB error getting tpmidentity\n",
reqp->nodeid);
return 1;
}
nrows = mysql_num_rows(res);
if (!nrows){
error("%s: no tpmidentity in database for this node.\n",
reqp->nodeid);
mysql_free_result(res);
return 1;
}
row = mysql_fetch_row(res);
nlen = mysql_fetch_lengths(res);
if (!nlen || !nlen[0]){
error("%s: invalid identity length.\n",
reqp->nodeid);
mysql_free_result(res);
return 1;
}
bufp += OUTPUT(bufp, bufe - bufp, "IDENTITY=");
for (i = 0;i < nlen[0];++i)
bufp += OUTPUT(bufp, bufe - bufp,
"%.02x", (0xff & ((char)*(row[0]+i))));
bufp += OUTPUT(bufp, bufe - bufp, " ");
mysql_free_result(res);
/*
* Generate a cryptographic nonce - we have to keep track of this to
* prevent replay attacks.
*/
if (!tmcd_tpm_generate_nonce(nonce)) {
error("DOQUOTEPREP: %s: Failed to generate nonce\n", reqp->nodeid);
return 1;
}
// Make a hex representation of the nonce
for (i = 0; i < TPM_NONCE_BYTES; i++) {
sprintf(nonce_hex + (i*2),"%.02x",nonce[i]);
}
nonce_hex[TPM_NONCE_BYTES] = '\0';
// XXX
info("NONCE: %s", nonce_hex);
// Store the nonce in the database. It expires in one minute, and we
// overwrite any existing nonces for this node/state combo
mydb_update("replace into nonces "
" (node_id, purpose, nonce, expires) "
" values ('%s', 'state-%s','%s', UNIX_TIMESTAMP()+60)",
reqp->nodeid,newstate,nonce_hex);
bufp += OUTPUT(bufp, bufe - bufp, "NONCE=%s",nonce_hex);
bufp += OUTPUT(bufp, bufe - bufp, "\n");
/*
* Return to the client
*/
client_writeback(sock, buf, bufp - buf, tcp);
return 0;
}
/*
* Return creator of experiment. Total hack. Must kill this.
*/
......
......@@ -11,6 +11,12 @@
#define error printf
#endif /* STANDALONE */
#include <sys/time.h>
#include <unistd.h>
#include <strings.h>
static unsigned int nonce_counter = 0;
/*
* XXX: Teach me how to do this properly . . . This is so boss can build ssl.o
* properly without installing trousers headers
......@@ -124,3 +130,54 @@ tmcd_tpm_free(void)
return 1;
#endif /* TPM */
}
int
tmcd_tpm_generate_nonce(unsigned char *nonce)
{
struct timeval time;
pid_t pid;
int byte_count = 0;
bzero(nonce,TPM_NONCE_BYTES);
/* Nonce must be 160 bits long, and we must be quite sure that we will
* never use the same one twice. We put three things into the nonce to
* make it unique:
* 1) Timestamp to the best accuracy we can get
* 2) The PID of the current process, to avoid someone asking two
* different tmcds for nonces at thte same time
* 3) A local counter, in case someone can ask for nonces faster than our
* clock resolution
*/
// timestamp
if (gettimeofday(&time,NULL)) {
return -1;
}
if (sizeof(time) + byte_count > TPM_NONCE_BYTES) {
return -1;
}
bcopy(&time, nonce, sizeof(time));
byte_count += sizeof(time);
// pid
pid = getpid();
if (sizeof(pid) + byte_count > TPM_NONCE_BYTES) {
return -1;
}
bcopy(&pid, nonce + byte_count, sizeof(pid));
byte_count += sizeof(pid);
// counter
if (sizeof(nonce_counter) + byte_count > TPM_NONCE_BYTES) {
return -1;
}
bcopy(&nonce_counter, nonce + byte_count, sizeof(nonce_counter));
byte_count += sizeof(nonce_counter);
// TODO: Maybe hash to avoid giving away info on state on boss?
return 0;
}
......@@ -7,7 +7,8 @@
#ifndef _ETPM_
#define _ETPM_
#include<openssl/engine.h>
#include <sys/types.h>
#include <openssl/engine.h>
extern EVP_PKEY *tpmk;
......@@ -15,4 +16,11 @@ int tmcd_tpm_loadengine();
int tmcd_tpm_getkey(char *);
int tmcd_tpm_free(void);
/*
* Nonce-related functions
*/
#define TPM_NONCE_BYTES 0x14 // 160 bits
typedef unsigned char TPM_NONCE[TPM_NONCE_BYTES];
int tmcd_tpm_generate_nonce(unsigned char*);
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment