123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- /*
- * OpenConnect (SSL + DTLS) VPN client
- *
- * Copyright © 2008-2015 Intel Corporation.
- *
- * Author: David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- */
- /*
- * TPM code based on client-tpm.c from
- * Carolin Latze <latze@angry-red-pla.net> and Tobias Soder
- */
- #include <config.h>
- #include "openconnect-internal.h"
- #include "gnutls.h"
- #include <gnutls/gnutls.h>
- #include <errno.h>
- #include <string.h>
- #ifdef HAVE_TROUSERS
- #include <trousers/tss.h>
- #include <trousers/trousers.h>
- struct oc_tpm1_ctx {
- TSS_HCONTEXT tpm_context;
- TSS_HKEY srk;
- TSS_HPOLICY srk_policy;
- TSS_HKEY tpm_key;
- TSS_HPOLICY tpm_key_policy;
- };
- /* Signing function for TPM privkeys, set with gnutls_privkey_import_ext() */
- static int tpm_sign_fn(gnutls_privkey_t key, void *_certinfo,
- const gnutls_datum_t *data, gnutls_datum_t *sig)
- {
- struct cert_info *certinfo = _certinfo;
- struct openconnect_info *vpninfo = certinfo->vpninfo;
- TSS_HHASH hash;
- int err;
- vpn_progress(vpninfo, PRG_DEBUG,
- _("TPM sign function called for %d bytes.\n"),
- data->size);
- err = Tspi_Context_CreateObject(certinfo->tpm1->tpm_context, TSS_OBJECT_TYPE_HASH,
- TSS_HASH_OTHER, &hash);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to create TPM hash object: %s\n"),
- Trspi_Error_String(err));
- return GNUTLS_E_PK_SIGN_FAILED;
- }
- err = Tspi_Hash_SetHashValue(hash, data->size, data->data);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to set value in TPM hash object: %s\n"),
- Trspi_Error_String(err));
- Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, hash);
- return GNUTLS_E_PK_SIGN_FAILED;
- }
- err = Tspi_Hash_Sign(hash, certinfo->tpm1->tpm_key, &sig->size, &sig->data);
- Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, hash);
- if (err) {
- if (certinfo->tpm1->tpm_key_policy || err != TPM_E_AUTHFAIL)
- vpn_progress(vpninfo, PRG_ERR,
- _("TPM hash signature failed: %s\n"),
- Trspi_Error_String(err));
- if (err == TPM_E_AUTHFAIL)
- return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
- else
- return GNUTLS_E_PK_SIGN_FAILED;
- }
- return 0;
- }
- int load_tpm1_key(struct openconnect_info *vpninfo, struct cert_info *certinfo,
- gnutls_datum_t *fdata, gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig)
- {
- static const TSS_UUID SRK_UUID = TSS_UUID_SRK;
- gnutls_datum_t asn1;
- unsigned int tss_len;
- char *pass;
- int ofs, err;
- err = gnutls_pem_base64_decode_alloc("TSS KEY BLOB", fdata, &asn1);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Error decoding TSS key blob: %s\n"),
- gnutls_strerror(err));
- return -EINVAL;
- }
- certinfo->tpm1 = calloc(1, sizeof(*certinfo->tpm1));
- /* Ick. We have to parse the ASN1 OCTET_STRING for ourselves. */
- if (asn1.size < 2 || asn1.data[0] != 0x04 /* OCTET_STRING */) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Error in TSS key blob\n"));
- goto out_blob;
- }
- tss_len = asn1.data[1];
- ofs = 2;
- if (tss_len & 0x80) {
- int lenlen = tss_len & 0x7f;
- if (asn1.size < 2 + lenlen || lenlen > 3) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Error in TSS key blob\n"));
- goto out_blob;
- }
- tss_len = 0;
- while (lenlen) {
- tss_len <<= 8;
- tss_len |= asn1.data[ofs++];
- lenlen--;
- }
- }
- if (tss_len + ofs != asn1.size) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Error in TSS key blob\n"));
- goto out_blob;
- }
- err = Tspi_Context_Create(&certinfo->tpm1->tpm_context);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to create TPM context: %s\n"),
- Trspi_Error_String(err));
- goto out_blob;
- }
- err = Tspi_Context_Connect(certinfo->tpm1->tpm_context, NULL);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to connect TPM context: %s\n"),
- Trspi_Error_String(err));
- goto out_context;
- }
- err = Tspi_Context_LoadKeyByUUID(certinfo->tpm1->tpm_context, TSS_PS_TYPE_SYSTEM,
- SRK_UUID, &certinfo->tpm1->srk);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to load TPM SRK key: %s\n"),
- Trspi_Error_String(err));
- goto out_context;
- }
- err = Tspi_GetPolicyObject(certinfo->tpm1->srk, TSS_POLICY_USAGE, &certinfo->tpm1->srk_policy);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to load TPM SRK policy object: %s\n"),
- Trspi_Error_String(err));
- goto out_srk;
- }
- pass = certinfo->password;
- certinfo->password = NULL;
- while (1) {
- static const char nullpass[20];
- /* We don't seem to get the error here... */
- if (pass)
- err = Tspi_Policy_SetSecret(certinfo->tpm1->srk_policy,
- TSS_SECRET_MODE_PLAIN,
- strlen(pass), (BYTE *)pass);
- else /* Well-known NULL key */
- err = Tspi_Policy_SetSecret(certinfo->tpm1->srk_policy,
- TSS_SECRET_MODE_SHA1,
- sizeof(nullpass), (BYTE *)nullpass);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to set TPM PIN: %s\n"),
- Trspi_Error_String(err));
- goto out_srkpol;
- }
- free_pass(&pass);
- /* ... we get it here instead. */
- err = Tspi_Context_LoadKeyByBlob(certinfo->tpm1->tpm_context, certinfo->tpm1->srk,
- tss_len, asn1.data + ofs,
- &certinfo->tpm1->tpm_key);
- if (!err)
- break;
- if (pass)
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to load TPM key blob: %s\n"),
- Trspi_Error_String(err));
- if (err != TPM_E_AUTHFAIL)
- goto out_srkpol;
- err = request_passphrase(vpninfo, "openconnect_tpm_srk",
- &pass, _("Enter TPM SRK PIN:"));
- if (err)
- goto out_srkpol;
- }
- gnutls_privkey_init(pkey);
- /* This would be nicer if there was a destructor callback. I could
- allocate a data structure with the TPM handles and the vpninfo
- pointer, and destroy that properly when the key is destroyed. */
- gnutls_privkey_import_ext(*pkey, GNUTLS_PK_RSA, certinfo, tpm_sign_fn, NULL, 0);
- retry_sign:
- err = gnutls_privkey_sign_data(*pkey, GNUTLS_DIG_SHA1, 0, fdata, pkey_sig);
- if (err == GNUTLS_E_INSUFFICIENT_CREDENTIALS) {
- if (!certinfo->tpm1->tpm_key_policy) {
- err = Tspi_Context_CreateObject(certinfo->tpm1->tpm_context,
- TSS_OBJECT_TYPE_POLICY,
- TSS_POLICY_USAGE,
- &certinfo->tpm1->tpm_key_policy);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to create key policy object: %s\n"),
- Trspi_Error_String(err));
- goto out_key;
- }
- err = Tspi_Policy_AssignToObject(certinfo->tpm1->tpm_key_policy,
- certinfo->tpm1->tpm_key);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to assign policy to key: %s\n"),
- Trspi_Error_String(err));
- goto out_key_policy;
- }
- }
- err = request_passphrase(vpninfo,
- certinfo_string(certinfo, "openconnect_tpm_key",
- "openconnect_secondary_tpm_key"),
- &pass,
- certinfo_string(certinfo, _("Enter TPM key PIN:"),
- _("Enter secondary key TPM PIN:")));
- if (err)
- goto out_key_policy;
- err = Tspi_Policy_SetSecret(certinfo->tpm1->tpm_key_policy,
- TSS_SECRET_MODE_PLAIN,
- strlen(pass), (void *)pass);
- free_pass(&pass);
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to set key PIN: %s\n"),
- Trspi_Error_String(err));
- goto out_key_policy;
- }
- goto retry_sign;
- }
- free(asn1.data);
- return 0;
- out_key_policy:
- Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->tpm_key_policy);
- certinfo->tpm1->tpm_key_policy = 0;
- out_key:
- Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->tpm_key);
- certinfo->tpm1->tpm_key = 0;
- out_srkpol:
- Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->srk_policy);
- certinfo->tpm1->srk_policy = 0;
- out_srk:
- Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->srk);
- certinfo->tpm1->srk = 0;
- out_context:
- Tspi_Context_Close(certinfo->tpm1->tpm_context);
- certinfo->tpm1->tpm_context = 0;
- out_blob:
- free(asn1.data);
- free(certinfo->tpm1);
- certinfo->tpm1 = NULL;
- return -EIO;
- }
- void release_tpm1_ctx(struct openconnect_info *vpninfo, struct cert_info *certinfo)
- {
- if (!certinfo->tpm1)
- return;
- if (certinfo->tpm1->tpm_key_policy) {
- Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->tpm_key_policy);
- certinfo->tpm1->tpm_key = 0;
- }
- if (certinfo->tpm1->tpm_key) {
- Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->tpm_key);
- certinfo->tpm1->tpm_key = 0;
- }
- if (certinfo->tpm1->srk_policy) {
- Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->srk_policy);
- certinfo->tpm1->srk_policy = 0;
- }
- if (certinfo->tpm1->srk) {
- Tspi_Context_CloseObject(certinfo->tpm1->tpm_context, certinfo->tpm1->srk);
- certinfo->tpm1->srk = 0;
- }
- if (certinfo->tpm1->tpm_context) {
- Tspi_Context_Close(certinfo->tpm1->tpm_context);
- certinfo->tpm1->tpm_context = 0;
- }
- free(certinfo->tpm1);
- certinfo->tpm1 = NULL;
- };
- #endif /* HAVE_TROUSERS */
|