diff --git a/gnutls_tpm2_esys.c b/gnutls_tpm2_esys.c index 815cc8ea..c68baa28 100644 --- a/gnutls_tpm2_esys.c +++ b/gnutls_tpm2_esys.c @@ -43,10 +43,12 @@ #include struct oc_tpm2_ctx { - TPM2B_DIGEST userauth; TPM2B_PUBLIC pub; TPM2B_PRIVATE priv; + TPM2B_DIGEST userauth; TPM2B_DIGEST ownerauth; + unsigned int need_userauth:1; + unsigned int need_ownerauth:1; }; static TPM2B_PUBLIC primaryTemplate = { @@ -111,8 +113,8 @@ static TPML_PCR_SELECTION allCreationPCR = { * @retval TSS2_RC_SUCCESS on success * @retval TSS2_RCs according to the error */ -static TSS2_RC init_tpm2_primary(struct openconnect_info *vpninfo, - ESYS_CONTEXT **ctx, ESYS_TR *primaryHandle) +static int init_tpm2_primary(struct openconnect_info *vpninfo, + ESYS_CONTEXT **ctx, ESYS_TR *primaryHandle) { TSS2_RC r; *primaryHandle = ESYS_TR_NONE; @@ -140,7 +142,26 @@ static TSS2_RC init_tpm2_primary(struct openconnect_info *vpninfo, } vpn_progress(vpninfo, PRG_DEBUG, _("Creating primary key under owner.\n")); + 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); + vpninfo->tpm2->need_ownerauth = 0; + } r = Esys_TR_SetAuth(*ctx, ESYS_TR_RH_OWNER, &vpninfo->tpm2->ownerauth); if (r) { vpn_progress(vpninfo, PRG_ERR, @@ -157,8 +178,8 @@ static TSS2_RC init_tpm2_primary(struct openconnect_info *vpninfo, if (r == 0x000009a2) { vpn_progress(vpninfo, PRG_DEBUG, _("TPM2 Esys_CreatePrimary owner auth failed\n")); - /* XXX: Prompt and retry */ - goto error; + vpninfo->tpm2->need_ownerauth = 1; + goto reauth; } else if (r) { vpn_progress(vpninfo, PRG_ERR, _("TPM2 Esys_CreatePrimary failed: 0x%x\n"), @@ -166,14 +187,14 @@ static TSS2_RC init_tpm2_primary(struct openconnect_info *vpninfo, goto error; } - return TSS2_RC_SUCCESS; + return 0; error: if (*primaryHandle != ESYS_TR_NONE) Esys_FlushContext(*ctx, *primaryHandle); *primaryHandle = ESYS_TR_NONE; Esys_Finalize(ctx); - return r; + return -EIO; } /** Initialize the ESYS TPM connection and load the key @@ -187,15 +208,14 @@ static TSS2_RC init_tpm2_primary(struct openconnect_info *vpninfo, * @retval TSS2_RC_SUCCESS on success * @retval TSS2_RCs according to the error */ -static TSS2_RC init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *keyHandle, - struct openconnect_info *vpninfo) +static int init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *keyHandle, + struct openconnect_info *vpninfo) { TSS2_RC r; ESYS_TR primaryHandle = ESYS_TR_NONE; *keyHandle = ESYS_TR_NONE; - r = init_tpm2_primary(vpninfo, ctx, &primaryHandle); - if (r) + if (init_tpm2_primary(vpninfo, ctx, &primaryHandle)) goto error; vpn_progress(vpninfo, PRG_DEBUG, _("Loading TPM2 key blob.\n")); @@ -220,15 +240,7 @@ static TSS2_RC init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *keyHandle, } primaryHandle = ESYS_TR_NONE; - r = Esys_TR_SetAuth(*ctx, *keyHandle, &vpninfo->tpm2->userauth); - if (r) { - vpn_progress(vpninfo, PRG_ERR, - _("TPM2 Esys_TR_SetAuth failed: 0x%x\n"), - r); - goto error; - } - - return TSS2_RC_SUCCESS; + return 0; error: if (primaryHandle != ESYS_TR_NONE) Esys_FlushContext(*ctx, primaryHandle); @@ -237,7 +249,46 @@ static TSS2_RC init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *keyHandle, *keyHandle = ESYS_TR_NONE; Esys_Finalize(ctx); - return r; + return -EIO; +} + +static int auth_tpm2_key(struct openconnect_info *vpninfo, ESYS_CONTEXT *ctx, ESYS_TR key_handle) +{ + TSS2_RC r; + + if (vpninfo->tpm2->need_userauth || vpninfo->cert_password) { + char *pass = NULL; + + if (vpninfo->cert_password) { + pass = vpninfo->cert_password; + vpninfo->cert_password = NULL; + } else { + int err = request_passphrase(vpninfo, "openconnect_tpm2_key", + &pass, _("Enter TPM2 key password:")); + 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); + + vpninfo->tpm2->need_userauth = 0; + } + + r = Esys_TR_SetAuth(ctx, key_handle, &vpninfo->tpm2->userauth); + if (r) { + vpn_progress(vpninfo, PRG_ERR, + _("TPM2 Esys_TR_SetAuth failed: 0x%x\n"), + r); + return -EIO; + } + return 0; } #define PKCS1_PAD_OVERHEAD 11 @@ -277,18 +328,26 @@ static int tpm2_rsa_sign_fn(gnutls_privkey_t key, void *_vpninfo, if (init_tpm2_key(&ectx, &key_handle, vpninfo)) goto out; + reauth: + if (auth_tpm2_key(vpninfo, ectx, key_handle)) + goto out; r = Esys_RSA_Decrypt(ectx, key_handle, ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, &digest, &inScheme, &label, &tsig); + if (r == 0x9a2) { + vpn_progress(vpninfo, PRG_DEBUG, + _("TPM2 Esys_RSA_Decrypt auth failed\n")); + vpninfo->tpm2->need_userauth = 1; + goto reauth; + } if (r) { vpn_progress(vpninfo, PRG_ERR, - _("TPM2 failed to generate RSA signature: %d\n"), + _("TPM2 failed to generate RSA signature: 0x%x\n"), r); goto out; } - sig->data = malloc(tsig->size); if (!sig->data) goto out; @@ -337,7 +396,7 @@ int install_tpm2_key(struct openconnect_info *vpninfo, gnutls_privkey_t *pkey, g &vpninfo->tpm2->priv); if (r) { vpn_progress(vpninfo, PRG_ERR, - _("Failed to import RPM2 private key data: %d\n"), + _("Failed to import TPM2 private key data: 0x%x\n"), r); err_out: release_tpm2_ctx(vpninfo); @@ -348,7 +407,7 @@ int install_tpm2_key(struct openconnect_info *vpninfo, gnutls_privkey_t *pkey, g &vpninfo->tpm2->pub); if (r) { vpn_progress(vpninfo, PRG_ERR, - _("Failed to import RPM2 private key data: %d\n"), + _("Failed to import TPM2 private key data: 0x%x\n"), r); goto err_out; }