Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add support for persistent parent keys and other hierarchies
We should now be able to cope with anything James's tpm2 engine creates,
except for the policies.

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
  • Loading branch information
dwmw2 committed Oct 11, 2018
1 parent 72d9cb0 commit c72c751
Showing 1 changed file with 119 additions and 101 deletions.
220 changes: 119 additions & 101 deletions gnutls_tpm2_esys.c
Expand Up @@ -66,6 +66,7 @@ struct oc_tpm2_ctx {
TPM2B_DIGEST ownerauth;
unsigned int need_userauth:1;
unsigned int need_ownerauth:1;
unsigned int parent;
};

static TPM2B_PUBLIC primaryTemplate = {
Expand Down Expand Up @@ -121,78 +122,61 @@ static TPML_PCR_SELECTION allCreationPCR = {
};


/** Initialize the ESYS TPM connection and primary key
*
* Establish a connection with the TPM using ESYS libraries and create a primary
* key under the owner hierarchy.
* @param ctx The resulting ESYS context.
* @param primaryHandle The resulting handle for the primary key.
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_RCs according to the error
*/
static int init_tpm2_primary(struct openconnect_info *vpninfo,
ESYS_CONTEXT **ctx, ESYS_TR *primaryHandle)
{
TSS2_RC r;
*primaryHandle = ESYS_TR_NONE;

vpn_progress(vpninfo, PRG_DEBUG,
_("Establishing connection with TPM.\n"));
/* Where do these error values come from? */
#define KEY_AUTH_FAILED 0x9a2
#define PARENT_AUTH_FAILED 0x98e

r = Esys_Initialize(ctx, NULL, NULL);
if (r) {
static void install_tpm_passphrase(struct openconnect_info *vpninfo, TPM2B_DIGEST *auth, char *pass)
{
if (strlen(pass) > sizeof(auth->buffer) - 1) {
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 Esys_Initialize failed: 0x%x\n"),
r);
goto error;
_("TPM2 password too long; truncating\n"));
pass[sizeof(auth->buffer) - 1] = 0;
}
auth->size = strlen(pass);
strcpy((char *)auth->buffer, pass);
memset(pass, 0, strlen(pass));
free(pass);
}

r = Esys_Startup(*ctx, TPM2_SU_CLEAR);
if (r == TPM2_RC_INITIALIZE) {
vpn_progress(vpninfo, PRG_DEBUG,
_("TPM2 was already started up thus false positive failing in tpm2tss log.\n"));
} else if (r) {
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 Esys_Startup failed: 0x%x\n"),
r);
goto error;
static int init_tpm2_primary(struct openconnect_info *vpninfo,
ESYS_CONTEXT *ctx, ESYS_TR *primaryHandle)
{
TSS2_RC r;
const char *hierarchy_name;
ESYS_TR hierarchy;

switch(vpninfo->tpm2->parent) {
case TPM2_RH_OWNER: hierarchy = ESYS_TR_RH_OWNER; hierarchy_name = _("owner"); break;
case TPM2_RH_NULL: hierarchy = ESYS_TR_RH_NULL; hierarchy_name = _("null"); break;
case TPM2_RH_ENDORSEMENT:hierarchy = ESYS_TR_RH_ENDORSEMENT; hierarchy_name = _("endorsement"); break;
case TPM2_RH_PLATFORM: hierarchy = ESYS_TR_RH_PLATFORM; hierarchy_name = _("platform"); break;
default: return -EINVAL;
}

vpn_progress(vpninfo, PRG_DEBUG, _("Creating primary key under owner.\n"));
vpn_progress(vpninfo, PRG_DEBUG, _("Creating primary key under %s hierarchy.\n"), hierarchy_name);
reauth:
if (vpninfo->tpm2->need_ownerauth) {
char *pass = NULL;
int err = request_passphrase(vpninfo, "openconnect_tpm2_owner",
&pass, _("Enter TPM2 owner password:"));
if (err)
goto error;

if (strlen(pass) > sizeof(vpninfo->tpm2->ownerauth.buffer) - 1) {
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 owner password too long; truncating\n"));
pass[sizeof(vpninfo->tpm2->ownerauth.buffer) - 1] = 0;
}
vpninfo->tpm2->ownerauth.size = strlen(pass);
strcpy((char *)vpninfo->tpm2->ownerauth.buffer, pass);
memset(pass, 0, strlen(pass));
free(pass);

if (request_passphrase(vpninfo, "openconnect_tpm2_hierarchy", &pass,
_("Enter TPM2 %s hierarchy password:"), hierarchy_name))
return -EPERM;
install_tpm_passphrase(vpninfo, &vpninfo->tpm2->ownerauth, pass);
vpninfo->tpm2->need_ownerauth = 0;
}
r = Esys_TR_SetAuth(*ctx, ESYS_TR_RH_OWNER, &vpninfo->tpm2->ownerauth);
r = Esys_TR_SetAuth(ctx, hierarchy, &vpninfo->tpm2->ownerauth);
if (r) {
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 Esys_TR_SetAuth failed: 0x%x\n"),
r);
goto error;
return -EPERM;
}

r = Esys_CreatePrimary(*ctx, ESYS_TR_RH_OWNER,
r = Esys_CreatePrimary(ctx, hierarchy,
ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
&primarySensitive, &primaryTemplate,
&allOutsideInfo, &allCreationPCR,
primaryHandle, NULL, NULL, NULL, NULL);
if (r == 0x000009a2) {
if (r == KEY_AUTH_FAILED) {
vpn_progress(vpninfo, PRG_DEBUG,
_("TPM2 Esys_CreatePrimary owner auth failed\n"));
vpninfo->tpm2->need_ownerauth = 1;
Expand All @@ -201,66 +185,106 @@ static int init_tpm2_primary(struct openconnect_info *vpninfo,
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 Esys_CreatePrimary failed: 0x%x\n"),
r);
goto error;
return -EIO;
}

return 0;
error:
if (*primaryHandle != ESYS_TR_NONE)
Esys_FlushContext(*ctx, *primaryHandle);
*primaryHandle = ESYS_TR_NONE;

Esys_Finalize(ctx);
return -EIO;
}

/** Initialize the ESYS TPM connection and load the key
*
* Establish a connection with the TPM using ESYS libraries, create a primary
* key under the owner hierarchy and then load the TPM key and set its auth
* value.
* @param ctx The resulting ESYS context.
* @param keyHandle The resulting handle for the key key.
* @param tpm2Data The key data, owner auth and key auth to be used
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_RCs according to the error
*/
#define parent_is_generated(vpninfo) (vpninfo->tpm2->parent >> TPM2_HR_SHIFT == TPM2_HT_PERMANENT)

static int init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *keyHandle,
struct openconnect_info *vpninfo)
{
ESYS_TR parentHandle = ESYS_TR_NONE;
TSS2_RC r;
ESYS_TR primaryHandle = ESYS_TR_NONE;

*keyHandle = ESYS_TR_NONE;

if (init_tpm2_primary(vpninfo, ctx, &primaryHandle))
vpn_progress(vpninfo, PRG_DEBUG,
_("Establishing connection with TPM.\n"));

r = Esys_Initialize(ctx, NULL, NULL);
if (r) {
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 Esys_Initialize failed: 0x%x\n"),
r);
goto error;
}

vpn_progress(vpninfo, PRG_DEBUG, _("Loading TPM2 key blob.\n"));
r = Esys_Startup(*ctx, TPM2_SU_CLEAR);
if (r == TPM2_RC_INITIALIZE) {
vpn_progress(vpninfo, PRG_DEBUG,
_("TPM2 was already started up thus false positive failing in tpm2tss log.\n"));
} else if (r) {
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 Esys_Startup failed: 0x%x\n"),
r);
goto error;
}

r = Esys_Load(*ctx, primaryHandle,
if (parent_is_generated(vpninfo)) {
if (init_tpm2_primary(vpninfo, *ctx, &parentHandle))
goto error;
} else {
r = Esys_TR_FromTPMPublic(*ctx, vpninfo->tpm2->parent,
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &parentHandle);
if (r) {
vpn_progress(vpninfo, PRG_ERR,
_("Esys_TR_FromTPMPublic failed for handle 0x%x: 0x%x\n"),
vpninfo->tpm2->parent, r);
goto error;
}
reauth:
if (vpninfo->tpm2->need_ownerauth) {
char *pass = NULL;
if (request_passphrase(vpninfo, "openconnect_tpm2_parent", &pass,
_("Enter TPM2 parent key password:")))
return -EPERM;
install_tpm_passphrase(vpninfo, &vpninfo->tpm2->ownerauth, pass);
vpninfo->tpm2->need_ownerauth = 0;
}
r = Esys_TR_SetAuth(*ctx, parentHandle, &vpninfo->tpm2->ownerauth);
if (r) {
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 Esys_TR_SetAuth failed: 0x%x\n"),
r);
goto error;
}
}

vpn_progress(vpninfo, PRG_DEBUG, _("Loading TPM2 key blob, parent %x.\n"), parentHandle);

r = Esys_Load(*ctx, parentHandle,
ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
&vpninfo->tpm2->priv, &vpninfo->tpm2->pub,
keyHandle);
if (r == PARENT_AUTH_FAILED) {
vpn_progress(vpninfo, PRG_DEBUG,
_("TPM2 Esys_Load auth failed\n"));
vpninfo->tpm2->need_ownerauth = 1;
goto reauth;
}
if (r) {
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 Esys_Load failed: 0x%x\n"),
r);
goto error;
}

r = Esys_FlushContext(*ctx, primaryHandle);
if (r) {
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 Esys_FlushContext failed: 0x%x\n"),
r);
goto error;
if (parent_is_generated(vpninfo)) {
r = Esys_FlushContext(*ctx, parentHandle);
if (r) {
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 Esys_FlushContext for generated primary failed: 0x%x\n"),
r);
}
/* But it's non-fatal. */
}
primaryHandle = ESYS_TR_NONE;

return 0;
error:
if (primaryHandle != ESYS_TR_NONE)
Esys_FlushContext(*ctx, primaryHandle);
if (parent_is_generated(vpninfo) && parentHandle != ESYS_TR_NONE)
Esys_FlushContext(*ctx, parentHandle);
if (*keyHandle != ESYS_TR_NONE)
Esys_FlushContext(*ctx, *keyHandle);
*keyHandle = ESYS_TR_NONE;
Expand All @@ -285,16 +309,7 @@ static int auth_tpm2_key(struct openconnect_info *vpninfo, ESYS_CONTEXT *ctx, ES
if (err)
return err;
}
if (strlen(pass) > sizeof(vpninfo->tpm2->userauth.buffer) - 1) {
vpn_progress(vpninfo, PRG_ERR,
_("TPM2 key password too long; truncating\n"));
pass[sizeof(vpninfo->tpm2->userauth.buffer) - 1] = 0;
}
vpninfo->tpm2->userauth.size = strlen(pass);
strcpy((char *)vpninfo->tpm2->userauth.buffer, pass);
memset(pass, 0, strlen(pass));
free(pass);

install_tpm_passphrase(vpninfo, &vpninfo->tpm2->userauth, pass);
vpninfo->tpm2->need_userauth = 0;
}

Expand Down Expand Up @@ -352,7 +367,7 @@ int tpm2_rsa_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo,
r = Esys_RSA_Decrypt(ectx, key_handle,
ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
&digest, &inScheme, &label, &tsig);
if (r == 0x9a2) {
if (r == KEY_AUTH_FAILED) {
vpn_progress(vpninfo, PRG_DEBUG,
_("TPM2 Esys_RSA_Decrypt auth failed\n"));
vpninfo->tpm2->need_userauth = 1;
Expand Down Expand Up @@ -432,7 +447,7 @@ int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo,
ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
&digest, &inScheme, &validation,
&tsig);
if (r == 0x9a2) {
if (r == KEY_AUTH_FAILED) {
vpn_progress(vpninfo, PRG_DEBUG,
_("TPM2 Esys_Sign auth failed\n"));
vpninfo->tpm2->need_userauth = 1;
Expand Down Expand Up @@ -468,17 +483,20 @@ int install_tpm2_key(struct openconnect_info *vpninfo, gnutls_privkey_t *pkey, g
{
TSS2_RC r;

if (parent != 0x40000001) {
if (parent >> TPM2_HR_SHIFT != TPM2_HT_PERSISTENT &&
parent != TPM2_RH_OWNER && parent != TPM2_RH_NULL &&
parent != TPM2_RH_ENDORSEMENT && parent != TPM2_RH_PLATFORM) {
vpn_progress(vpninfo, PRG_ERR,
_("Cannot use TPM2 key with non-default parent 0x%x\n"),
parent);
_("Invalid TPM2 parent handle 0x%08x\n"), parent);
return -EINVAL;
};
}

vpninfo->tpm2 = calloc(1, sizeof(*vpninfo->tpm2));
if (!vpninfo->tpm2)
return -ENOMEM;

vpninfo->tpm2->parent = parent;

r = Tss2_MU_TPM2B_PRIVATE_Unmarshal(privdata->data, privdata->size, NULL,
&vpninfo->tpm2->priv);
if (r) {
Expand Down

0 comments on commit c72c751

Please sign in to comment.