From 584cbb01a1dcfc880205b8d6262df135b8629042 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 27 Sep 2018 14:59:19 +0200 Subject: [PATCH] Add shell of TPM2 support This doesn't do anything useful at all yet. It would be nice if the two available OpenSSL engines were actually compatible. Signed-off-by: David Woodhouse --- Makefile.am | 2 +- configure.ac | 8 ++++++ gnutls.c | 34 ++++++++++++++++++------ gnutls.h | 4 +++ gnutls_tpm2.c | 60 ++++++++++++++++++++++++++++++++++++++++++ openconnect-internal.h | 4 +++ 6 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 gnutls_tpm2.c diff --git a/Makefile.am b/Makefile.am index fc36c590..545c8451 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,7 +31,7 @@ library_srcs = ssl.c http.c http-auth.c auth-common.c library.c compat.c lzs.c m lib_srcs_cisco = auth.c cstp.c lib_srcs_juniper = oncp.c lzo.c auth-juniper.c lib_srcs_globalprotect = gpst.c auth-globalprotect.c -lib_srcs_gnutls = gnutls.c gnutls_tpm.c +lib_srcs_gnutls = gnutls.c gnutls_tpm.c gnutls_tpm2.c lib_srcs_openssl = openssl.c openssl-pkcs11.c lib_srcs_win32 = tun-win32.c sspi.c lib_srcs_posix = tun.c diff --git a/configure.ac b/configure.ac index 7c136a62..55376d46 100644 --- a/configure.ac +++ b/configure.ac @@ -483,6 +483,14 @@ case "$ssl_library" in LIBS="$oldlibs" CFLAGS="$oldcflags" + tss2lib= + AC_MSG_CHECKING([for tss2 library]) + AC_CHECK_LIB([tss], [TSS_Create], [tss2lib=tss2], + AC_CHECK_LIB([TSS_Create], [ibmtss], [tss2lib=ibmtss], [])) + if test "%tss2lib" != ""; then + AC_CHECK_HEADER($tss2lib/tss.h, AC_DEFINE_UNQUOTED(HAVE_TSS2, $tss2lib, [TSS2 library]), []) + fi + AC_DEFINE(OPENCONNECT_GNUTLS, 1, [Using GnuTLS]) AC_SUBST(SSL_PC, [gnutls]) AC_SUBST(SSL_LIBS, ['$(GNUTLS_LIBS)']) diff --git a/gnutls.c b/gnutls.c index dc285570..c2ecc347 100644 --- a/gnutls.c +++ b/gnutls.c @@ -561,7 +561,7 @@ static int get_cert_name(gnutls_x509_crt_t cert, char *name, size_t namelen) return 0; } -#if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined (HAVE_GNUTLS_SYSTEM_KEYS) +#if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined(HAVE_TSS2) || defined (HAVE_GNUTLS_SYSTEM_KEYS) /* We have to convert the array of X509 certificates to gnutls_pcert_st for ourselves. There's no function that takes a gnutls_privkey_t as the key and gnutls_x509_crt_t certificates. */ @@ -611,7 +611,7 @@ static int verify_signed_data(gnutls_pubkey_t pubkey, gnutls_privkey_t privkey, return gnutls_pubkey_verify_data2(pubkey, algo, 0, data, sig); } -#endif /* (P11KIT || TROUSERS || SYSTEM_KEYS) */ +#endif /* (P11KIT || TROUSERS || TSS2 || SYSTEM_KEYS) */ static int openssl_hash_password(struct openconnect_info *vpninfo, char *pass, gnutls_datum_t *key, gnutls_datum_t *salt) @@ -901,7 +901,7 @@ static int load_certificate(struct openconnect_info *vpninfo) { gnutls_datum_t fdata; gnutls_x509_privkey_t key = NULL; -#if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined(HAVE_GNUTLS_SYSTEM_KEYS) +#if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined(HAVE_TSS2) || defined(HAVE_GNUTLS_SYSTEM_KEYS) gnutls_privkey_t pkey = NULL; gnutls_datum_t pkey_sig = {NULL, 0}; void *dummy_hash_data = &load_certificate; @@ -1319,6 +1319,21 @@ static int load_certificate(struct openconnect_info *vpninfo) #endif } + /* Is it a PEM file with a TPM key blob? */ + if (strstr((char *)fdata.data, "-----BEGIN TSS2 KEY BLOB-----")) { +#ifndef HAVE_TSS2 + vpn_progress(vpninfo, PRG_ERR, + _("This version of OpenConnect was built without TPM2 support\n")); + return -EINVAL; +#else + ret = load_tpm2_key(vpninfo, &fdata, &pkey, &pkey_sig); + if (ret) + goto out; + + goto match_cert; +#endif + } + /* OK, try other PEM files... */ gnutls_x509_privkey_init(&key); if ((pem_header = strstr((char *)fdata.data, "-----BEGIN RSA PRIVATE KEY-----")) || @@ -1462,7 +1477,7 @@ static int load_certificate(struct openconnect_info *vpninfo) enabled we'll fall straight through the bit at match_cert: below, and go directly to the bit where it prints the 'no match found' error and exits. */ -#if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined(HAVE_GNUTLS_SYSTEM_KEYS) +#if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined(HAVE_TSS2) || defined(HAVE_GNUTLS_SYSTEM_KEYS) match_cert: /* If we have a privkey from PKCS#11 or TPM, we can't do the simple comparison of key ID that we do for software keys to find which certificate is a @@ -1516,7 +1531,7 @@ static int load_certificate(struct openconnect_info *vpninfo) } gnutls_free(pkey_sig.data); } -#endif /* P11KIT || TROUSERS || SYSTEM_KEYS */ +#endif /* P11KIT || TROUSERS || TSS2 || SYSTEM_KEYS */ /* We shouldn't reach this. It means that we didn't find *any* matching cert */ vpn_progress(vpninfo, PRG_ERR, @@ -1693,7 +1708,7 @@ static int load_certificate(struct openconnect_info *vpninfo) key and certs. GnuTLS makes us do this differently for X509 privkeys vs. TPM/PKCS#11 "generic" privkeys, and the latter is particularly 'fun' for GnuTLS 2.12... */ -#if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined(HAVE_GNUTLS_SYSTEM_KEYS) +#if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined(HAVE_TSS2) || defined(HAVE_GNUTLS_SYSTEM_KEYS) if (pkey) { err = assign_privkey(vpninfo, pkey, supporting_certs, @@ -1704,7 +1719,7 @@ static int load_certificate(struct openconnect_info *vpninfo) of extra_certs[] may have been zeroed. */ } } else -#endif /* P11KIT || TROUSERS */ +#endif /* P11KIT || TROUSERS || TSS2 || SYSTEM_KEYS */ err = gnutls_certificate_set_x509_key(vpninfo->https_cred, supporting_certs, nr_supporting_certs, key); @@ -1741,7 +1756,7 @@ static int load_certificate(struct openconnect_info *vpninfo) } gnutls_free(extra_certs); -#if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined(HAVE_GNUTLS_SYSTEM_KEYS) +#if defined(HAVE_P11KIT) || defined(HAVE_TROUSERS) || defined(HAVE_TSS2) || defined(HAVE_GNUTLS_SYSTEM_KEYS) if (pkey) gnutls_privkey_deinit(pkey); /* If we support arbitrary privkeys, we might have abused fdata.data @@ -2315,6 +2330,9 @@ void openconnect_close_https(struct openconnect_info *vpninfo, int final) vpninfo->https_cred = NULL; #ifdef HAVE_TROUSERS release_tpm1_ctx(vpninfo); +#endif +#ifdef HAVE_TSS2 + release_tpm2_ctx(vpninfo); #endif } } diff --git a/gnutls.h b/gnutls.h index 93ad42dc..5d6e2277 100644 --- a/gnutls.h +++ b/gnutls.h @@ -28,6 +28,10 @@ int load_tpm1_key(struct openconnect_info *vpninfo, gnutls_datum_t *fdata, gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig); void release_tpm1_ctx(struct openconnect_info *info); +int load_tpm2_key(struct openconnect_info *vpninfo, gnutls_datum_t *fdata, + gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig); +void release_tpm2_ctx(struct openconnect_info *info); + char *get_gnutls_cipher(gnutls_session_t session); #endif /* __OPENCONNECT_GNUTLS_H__ */ diff --git a/gnutls_tpm2.c b/gnutls_tpm2.c new file mode 100644 index 00000000..f7ef8266 --- /dev/null +++ b/gnutls_tpm2.c @@ -0,0 +1,60 @@ +/* + * OpenConnect (SSL + DTLS) VPN client + * + * Copyright © 2018 David Woodhouse. + * + * Author: David Woodhouse + * + * 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. + */ + +#include + +#include +#include + +#include +#include "openconnect-internal.h" + +#include "gnutls.h" + +#ifdef HAVE_TSS2 +#define TSSINCLUDE(x) < HAVE_TSS2/x > +#include TSSINCLUDE(tss.h) + +struct oc_tpm2_ctx { +}; + +int load_tpm2_key(struct openconnect_info *vpninfo, gnutls_datum_t *fdata, + gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig) +{ + gnutls_datum_t asn1; + int err; + + err = gnutls_pem_base64_decode_alloc("TSS2 KEY BLOB", fdata, &asn1); + if (err) { + vpn_progress(vpninfo, PRG_ERR, + _("Error decoding TSS2 key blob: %s\n"), + gnutls_strerror(err)); + return -EINVAL; + } + free(asn1.data); + vpn_progress(vpninfo, PRG_ERR, + _("TPM2 not really implemented yet\n")); + return -EINVAL; +} + +void release_tpm2_ctx(struct openconnect_info *vpninfo) +{ + if (vpninfo->tpm2) + free(vpninfo->tpm2); + vpninfo->tpm2 = NULL; +} +#endif /* HAVE_TSS2 */ diff --git a/openconnect-internal.h b/openconnect-internal.h index 63547a4a..2fac50bc 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -346,6 +346,7 @@ struct esp { struct oc_pcsc_ctx; struct oc_tpm1_ctx; +struct oc_tpm2_ctx; struct openconnect_info { const struct vpn_proto *proto; @@ -503,6 +504,9 @@ struct openconnect_info { #ifdef HAVE_TROUSERS struct oc_tpm1_ctx *tpm1; #endif +#ifdef HAVE_TSS2 + struct oc_tpm2_ctx *tpm2; +#endif #endif /* OPENCONNECT_GNUTLS */ struct pin_cache *pin_cache; struct keepalive_info ssl_times;