From: Nikos Mavrogiannopoulos Date: Wed, 4 Jul 2012 20:53:00 +0000 (+0200) Subject: Added initial support for TPM keys. X-Git-Tag: gnutls_3_1_0pre0~90 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d1b7996804f90b0d5efb8a6c468f78fc4b07386f;p=thirdparty%2Fgnutls.git Added initial support for TPM keys. --- diff --git a/NEWS b/NEWS index ada7b42896..20e8886854 100644 --- a/NEWS +++ b/NEWS @@ -44,6 +44,7 @@ by Alexandre Bique. ** API and ABI modifications: GNUTLS_CERT_SIGNATURE_FAILURE: Added GNUTLS_CAMELLIA_192_CBC: Added +gnutls_privkey_import_tpm_raw: Added gnutls_privkey_import_pkcs11_url: Added gnutls_privkey_import_openpgp_raw: Added gnutls_privkey_import_x509_raw: Added diff --git a/configure.ac b/configure.ac index eba91ad044..e774be4910 100644 --- a/configure.ac +++ b/configure.ac @@ -153,6 +153,33 @@ fi AM_CONDITIONAL(ENABLE_PKCS11, test "$with_p11_kit" != "no") +AC_ARG_WITH(tpm, + AS_HELP_STRING([--without-tpm], + [Build without TPM (trousers) support])) +if test "$with_tpm" != "no"; then + LIBS="$oldlibs -ltspi" + AC_MSG_CHECKING([for tss library]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([ + #include + #include ],[ + int err = Tspi_Context_Create((void *)0); + Trspi_Error_String(err);])], + [AC_MSG_RESULT(yes) + AC_SUBST([TSS_LIBS], [-ltspi]) + AC_SUBST([TSS_CFLAGS], []) + AC_DEFINE([HAVE_TROUSERS], 1, [Enable TPM]) + with_tpm=yes], + [AC_MSG_RESULT(no) + AC_MSG_WARN([[ +*** +*** trousers was not found. TPM support will be disabled. +*** ]]) + with_tpm=no]) + LIBS="$oldlibs" +fi + +AM_CONDITIONAL(ENABLE_TROUSERS, test "$with_tpm" != "no") + enable_local_libopts=yes dnl PKG_CHECK_MODULES([autoopts], autoopts >= 36.2.11,, [enable_local_libopts=yes]) @@ -208,6 +235,7 @@ AC_SUBST(GNUTLS_REQUIRES_PRIVATE) AC_SUBST(GNUTLS_ZLIB_LIBS_PRIVATE) + gl_INIT dnl GCC warnings to enable @@ -581,4 +609,5 @@ AC_MSG_NOTICE([Hardware acceleration/support: /dev/crypto: $enable_cryptodev Hardware accel: $hw_accel PKCS#11 support: $with_p11_kit + TPM support: $with_tpm ]) diff --git a/doc/Makefile.am b/doc/Makefile.am index 685d387229..ce3e25be98 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -514,8 +514,10 @@ FUNCS += functions/gnutls_privkey_import_x509 FUNCS += functions/gnutls_privkey_import_openpgp FUNCS += functions/gnutls_privkey_import_openpgp_raw FUNCS += functions/gnutls_privkey_import_x509_raw +FUNCS += functions/gnutls_privkey_import_tpm_raw FUNCS += functions/gnutls_privkey_import_pkcs11_url FUNCS += functions/gnutls_privkey_import_ext +FUNCS += functions/gnutls_privkey_import_ext2 FUNCS += functions/gnutls_privkey_sign_data FUNCS += functions/gnutls_privkey_sign_hash FUNCS += functions/gnutls_privkey_decrypt_data diff --git a/lib/Makefile.am b/lib/Makefile.am index f3289b9b9c..69d98b15f7 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -76,6 +76,10 @@ COBJECTS = gnutls_record.c gnutls_compress.c debug.c gnutls_cipher.c \ gnutls_pubkey.c locks.c hash.c gnutls_dtls.c system_override.c \ crypto-backend.c verify-tofu.c +if ENABLE_TROUSERS +COBJECTS += tpm.c +endif + if ENABLE_PKCS11 COBJECTS += pkcs11.c pkcs11_privkey.c pkcs11_write.c pkcs11_secret.c endif @@ -120,7 +124,7 @@ libgnutls_la_LIBADD = ../gl/libgnu.la x509/libgnutls_x509.la \ auth/libgnutls_auth.la algorithms/libgnutls_alg.la \ extras/libgnutls_extras.la \ $(LTLIBZ) $(LTLIBINTL) $(LIBSOCKET) $(LTLIBDL) \ - $(LTLIBPTHREAD) $(P11_KIT_LIBS) $(LIB_SELECT) + $(LTLIBPTHREAD) $(P11_KIT_LIBS) $(LIB_SELECT) $(TSS_LIBS) if ENABLE_OPENPGP libgnutls_la_LIBADD += openpgp/libgnutls_openpgp.la diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index 71b6e6acf1..83e485d885 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -286,16 +286,16 @@ static const gnutls_error_entry error_algorithms[] = { ERROR_ENTRY (N_("Channel binding data not available"), GNUTLS_E_CHANNEL_BINDING_NOT_AVAILABLE, 1), + ERROR_ENTRY (N_("TPM error."), + GNUTLS_E_TPM_ERROR, 1), ERROR_ENTRY (N_("PKCS #11 error."), GNUTLS_E_PKCS11_ERROR, 1), ERROR_ENTRY (N_("PKCS #11 initialization error."), GNUTLS_E_PKCS11_LOAD_ERROR, 1), ERROR_ENTRY (N_("Error in parsing."), GNUTLS_E_PARSING_ERROR, 1), - ERROR_ENTRY (N_("PKCS #11 error in PIN."), - GNUTLS_E_PKCS11_PIN_ERROR, 1), - ERROR_ENTRY (N_("PKCS #11 PIN should be saved."), - GNUTLS_E_PKCS11_ERROR, 1), + ERROR_ENTRY (N_("Error in provided PIN."), + GNUTLS_E_PIN_ERROR, 1), ERROR_ENTRY (N_("PKCS #11 error in slot"), GNUTLS_E_PKCS11_SLOT_ERROR, 1), ERROR_ENTRY (N_("Thread locking error"), diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h index af798f9730..bb7586462a 100644 --- a/lib/includes/gnutls/abstract.h +++ b/lib/includes/gnutls/abstract.h @@ -50,7 +50,7 @@ typedef int (*gnutls_privkey_decrypt_func) (gnutls_privkey_t key, const gnutls_datum_t * ciphertext, gnutls_datum_t * plaintext); -typedef int (*gnutls_privkey_deinit_func) (gnutls_privkey_t key, +typedef void (*gnutls_privkey_deinit_func) (gnutls_privkey_t key, void *userdata); int gnutls_pubkey_init (gnutls_pubkey_t * key); @@ -189,6 +189,11 @@ int gnutls_privkey_import_x509_raw (gnutls_privkey_t pkey, gnutls_x509_crt_fmt_t format, const char* password); +int gnutls_privkey_import_tpm_raw (gnutls_privkey_t pkey, + const gnutls_datum_t * fdata, + gnutls_x509_crt_fmt_t format, + const char* password); + int gnutls_privkey_import_pkcs11_url (gnutls_privkey_t key, const char *url); int diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index d27070f4ab..92bd6d3866 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1914,6 +1914,9 @@ int gnutls_load_file(const char* filename, gnutls_datum_t * data); #define GNUTLS_E_X509_UNSUPPORTED_EXTENSION -327 #define GNUTLS_E_SESSION_EOF -328 +#define GNUTLS_E_TPM_ERROR -329 +#define GNUTLS_E_PIN_ERROR GNUTLS_E_PKCS11_PIN_ERROR + #define GNUTLS_E_UNIMPLEMENTED_FEATURE -1250 diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 746281395f..cf7a7dc608 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -806,6 +806,7 @@ GNUTLS_3_1_0 { gnutls_x509_privkey_import_openssl; gnutls_x509_privkey_import2; gnutls_privkey_import_ext2; + gnutls_privkey_import_tpm_raw; } GNUTLS_3_0_0; GNUTLS_PRIVATE { diff --git a/lib/pkcs11.c b/lib/pkcs11.c index 2a62f99208..ec8a482cf6 100644 --- a/lib/pkcs11.c +++ b/lib/pkcs11.c @@ -74,11 +74,11 @@ static struct gnutls_pkcs11_provider_s providers[MAX_PROVIDERS]; static unsigned int active_providers = 0; static unsigned int initialized_registered = 0; -static gnutls_pkcs11_pin_callback_t pin_func; -static void *pin_data; +gnutls_pkcs11_pin_callback_t _gnutls_pin_func; +void *_gnutls_pin_data; -gnutls_pkcs11_token_callback_t token_func; -void *token_data; +gnutls_pkcs11_token_callback_t _gnutls_token_func; +void *_gnutls_token_data; int pkcs11_rv_to_err (ck_rv_t rv) @@ -651,8 +651,8 @@ void gnutls_pkcs11_set_pin_function (gnutls_pkcs11_pin_callback_t fn, void *userdata) { - pin_func = fn; - pin_data = userdata; + _gnutls_pin_func = fn; + _gnutls_pin_data = userdata; } /** @@ -669,8 +669,8 @@ void gnutls_pkcs11_set_token_function (gnutls_pkcs11_token_callback_t fn, void *userdata) { - token_func = fn; - token_data = userdata; + _gnutls_token_func = fn; + _gnutls_token_data = userdata; } int @@ -1953,7 +1953,7 @@ retrieve_pin_for_callback (struct ck_token_info *token_info, int attempts, if (attempts > 0) flags |= GNUTLS_PKCS11_PIN_WRONG; - ret = pin_func (pin_data, attempts, (char*)token_str, label, + ret = _gnutls_pin_func (_gnutls_pin_data, attempts, (char*)token_str, label, flags, pin_value, GNUTLS_PKCS11_MAX_PIN_LEN); free (token_str); free (label); @@ -1987,7 +1987,7 @@ retrieve_pin (struct p11_kit_uri *info, struct ck_token_info *token_info, } /* The global gnutls pin callback */ - if (pin_func && ret < 0) + if (_gnutls_pin_func && ret < 0) ret = retrieve_pin_for_callback (token_info, attempts, user_type, pin); /* Otherwise, PIN entry is necessary for login, so fail if there's @@ -2102,7 +2102,7 @@ pkcs11_call_token_func (struct p11_kit_uri *info, const unsigned retry) tinfo = p11_kit_uri_get_token_info (info); label = p11_kit_space_strdup (tinfo->label, sizeof (tinfo->label)); - ret = (token_func) (token_data, label, retry); + ret = (_gnutls_token_func) (_gnutls_token_data, label, retry); free (label); return ret; diff --git a/lib/pkcs11_int.h b/lib/pkcs11_int.h index 538da55fe3..4da4107670 100644 --- a/lib/pkcs11_int.h +++ b/lib/pkcs11_int.h @@ -34,6 +34,9 @@ #include typedef unsigned char ck_bool_t; +extern gnutls_pkcs11_pin_callback_t _gnutls_pin_func; +extern void *_gnutls_pin_data; + struct pkcs11_session_info { struct ck_function_list * module; struct ck_token_info tinfo; @@ -83,8 +86,8 @@ int pkcs11_login (struct pkcs11_session_info * sinfo, int pkcs11_call_token_func (struct p11_kit_uri *info, const unsigned retry); -extern gnutls_pkcs11_token_callback_t token_func; -extern void *token_data; +extern gnutls_pkcs11_token_callback_t _gnutls_token_func; +extern void *_gnutls_token_data; void pkcs11_rescan_slots (void); int pkcs11_info_to_url (struct p11_kit_uri *info, diff --git a/lib/pkcs11_privkey.c b/lib/pkcs11_privkey.c index f377a9d289..487230cdcf 100644 --- a/lib/pkcs11_privkey.c +++ b/lib/pkcs11_privkey.c @@ -135,7 +135,7 @@ gnutls_pkcs11_privkey_get_info (gnutls_pkcs11_privkey_t pkey, ret = pkcs11_find_object (sinfo, &obj, key->info, \ SESSION_LOGIN); \ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { \ - if (token_func) \ + if (_gnutls_token_func) \ { \ rret = pkcs11_call_token_func (key->info, retries++); \ if (rret == 0) continue; \ diff --git a/lib/tpm.c b/lib/tpm.c new file mode 100644 index 0000000000..650a846908 --- /dev/null +++ b/lib/tpm.c @@ -0,0 +1,376 @@ +/* + * OpenConnect (SSL + DTLS) VPN client + * + * Copyright © 2012 Free Software Foundation. + * Copyright © 2008-2012 Intel Corporation. + * + * Author: David Woodhouse + * Author: Nikos Mavrogiannopoulos + * + * GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * This library 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + */ + +/* + * TPM code based on client-tpm.c from + * Carolin Latze and Tobias Soder + */ + +#include +#include + +#include +#include +#include + +#include +#include + +/* Signing function for TPM privkeys, set with gnutls_privkey_import_ext() */ + +struct tpm_ctx_st +{ + TSS_HCONTEXT tpm_context; + TSS_HKEY tpm_key; + TSS_HPOLICY tpm_key_policy; + TSS_HKEY srk; + TSS_HPOLICY srk_policy; +}; + +static void +tpm_deinit_fn (gnutls_privkey_t key, void *_s) +{ + struct tpm_ctx_st *s = _s; + + Tspi_Context_CloseObject (s->tpm_context, s->tpm_key_policy); + Tspi_Context_CloseObject (s->tpm_context, s->tpm_key); + Tspi_Context_CloseObject (s->tpm_context, s->srk_policy); + Tspi_Context_CloseObject (s->tpm_context, s->srk); + Tspi_Context_Close (s->tpm_context); + gnutls_free (s); +} + +static int +tpm_sign_fn (gnutls_privkey_t key, void *_s, + const gnutls_datum_t * data, gnutls_datum_t * sig) +{ + struct tpm_ctx_st *s = _s; + TSS_HHASH hash; + int err; + + _gnutls_debug_log ("TPM sign function called for %u bytes.\n", + data->size); + + err = + Tspi_Context_CreateObject (s->tpm_context, + TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, + &hash); + if (err) + { + gnutls_assert (); + _gnutls_debug_log ("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) + { + gnutls_assert (); + _gnutls_debug_log ("Failed to set value in TPM hash object: %s\n", + Trspi_Error_String (err)); + Tspi_Context_CloseObject (s->tpm_context, hash); + return GNUTLS_E_PK_SIGN_FAILED; + } + err = Tspi_Hash_Sign (hash, s->tpm_key, &sig->size, &sig->data); + Tspi_Context_CloseObject (s->tpm_context, hash); + if (err) + { + if (s->tpm_key_policy || err != TPM_E_AUTHFAIL) + _gnutls_debug_log ("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; +} + + +/** + * gnutls_privkey_import_tpm_raw: + * @pkey: The private key + * @fdata: The TPM key to be imported + * @format: The format of the private key + * @password: A password (optional) + * + * This function will import the given private key to the abstract + * #gnutls_privkey_t structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.0 + * + **/ +int +gnutls_privkey_import_tpm_raw (gnutls_privkey_t pkey, + const gnutls_datum_t * fdata, + gnutls_x509_crt_fmt_t format, + const char *password) +{ + static const TSS_UUID SRK_UUID = TSS_UUID_SRK; + char pin_value[GNUTLS_PKCS11_MAX_PIN_LEN]; + gnutls_datum_t asn1; + unsigned int tss_len; + unsigned int attempts = 0; + int ofs, err, ret; + struct tpm_ctx_st *s; + gnutls_datum_t tmp_sig; + static const char nullpass[20]; + + err = gnutls_pem_base64_decode_alloc ("TSS KEY BLOB", fdata, &asn1); + if (err) + { + gnutls_assert (); + _gnutls_debug_log ("Error decoding TSS key blob: %s\n", + gnutls_strerror (err)); + return GNUTLS_E_INVALID_REQUEST; + } + + /* FIXME: do proper decoding */ + + /* Ick. We have to parse the ASN1 OCTET_STRING for ourselves. */ + if (asn1.size < 2 || asn1.data[0] != 0x04 /* OCTET_STRING */ ) + { + gnutls_assert (); + _gnutls_debug_log ("Error in TSS key blob\n"); + ret = GNUTLS_E_PARSING_ERROR; + goto out_blob; + } + + s = gnutls_malloc (sizeof (*s)); + if (s == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_MEMORY_ERROR; + goto out_ctx; + } + + tss_len = asn1.data[1]; + ofs = 2; + if (tss_len & 0x80) + { + unsigned int lenlen = tss_len & 0x7f; + + if (asn1.size < 2 + lenlen || lenlen > 3) + { + gnutls_assert (); + _gnutls_debug_log ("Error in TSS key blob\n"); + ret = GNUTLS_E_PARSING_ERROR; + goto out_blob; + } + + tss_len = 0; + while (lenlen) + { + tss_len <<= 8; + tss_len |= asn1.data[ofs++]; + lenlen--; + } + } + if (tss_len + ofs != asn1.size) + { + gnutls_assert (); + _gnutls_debug_log ("Error in TSS key blob\n"); + ret = GNUTLS_E_PARSING_ERROR; + goto out_blob; + } + + err = Tspi_Context_Create (&s->tpm_context); + if (err) + { + gnutls_assert (); + _gnutls_debug_log ("Failed to create TPM context: %s\n", + Trspi_Error_String (err)); + ret = GNUTLS_E_TPM_ERROR; + goto out_blob; + } + err = Tspi_Context_Connect (s->tpm_context, NULL); + if (err) + { + gnutls_assert (); + _gnutls_debug_log ("Failed to connect TPM context: %s\n", + Trspi_Error_String (err)); + ret = GNUTLS_E_TPM_ERROR; + goto out_context; + } + err = + Tspi_Context_LoadKeyByUUID (s->tpm_context, TSS_PS_TYPE_SYSTEM, + SRK_UUID, &s->srk); + if (err) + { + gnutls_assert (); + _gnutls_debug_log + ("Failed to load TPM SRK key: %s\n", Trspi_Error_String (err)); + ret = GNUTLS_E_TPM_ERROR; + goto out_context; + } + err = Tspi_GetPolicyObject (s->srk, TSS_POLICY_USAGE, &s->srk_policy); + if (err) + { + gnutls_assert (); + _gnutls_debug_log ("Failed to load TPM SRK policy object: %s\n", + Trspi_Error_String (err)); + ret = GNUTLS_E_TPM_ERROR; + goto out_srk; + } + + /* We don't seem to get the error here... */ + if (password) + err = Tspi_Policy_SetSecret (s->srk_policy, + TSS_SECRET_MODE_PLAIN, + strlen (password), (BYTE *) password); + else /* Well-known NULL key */ + err = Tspi_Policy_SetSecret (s->srk_policy, + TSS_SECRET_MODE_SHA1, + sizeof (nullpass), (BYTE *) nullpass); + if (err) + { + gnutls_assert (); + _gnutls_debug_log ("Failed to set TPM PIN: %s\n", + Trspi_Error_String (err)); + ret = GNUTLS_E_TPM_ERROR; + goto out_srkpol; + } + + /* ... we get it here instead. */ + err = Tspi_Context_LoadKeyByBlob (s->tpm_context, s->srk, + tss_len, asn1.data + ofs, &s->tpm_key); + if (err != 0) + { + if (password) + { + gnutls_assert (); + _gnutls_debug_log + ("Failed to load TPM key blob: %s\n", + Trspi_Error_String (err)); + } + + if (err != TPM_E_AUTHFAIL) + { + gnutls_assert (); + ret = GNUTLS_E_TPM_ERROR; + goto out_srkpol; + } + else + { + ret = gnutls_assert_val (GNUTLS_E_PIN_ERROR); + goto out_srkpol; + } + } + + ret = + gnutls_privkey_import_ext2 (pkey, GNUTLS_PK_RSA, s, + tpm_sign_fn, NULL, tpm_deinit_fn, 0); + if (ret < 0) + { + gnutls_assert (); + goto out_srkpol; + } + +retry_sign: + ret = + gnutls_privkey_sign_data (pkey, GNUTLS_DIG_SHA1, 0, fdata, &tmp_sig); + if (ret == GNUTLS_E_INSUFFICIENT_CREDENTIALS) + { + if (!s->tpm_key_policy) + { + err = Tspi_Context_CreateObject (s->tpm_context, + TSS_OBJECT_TYPE_POLICY, + TSS_POLICY_USAGE, + &s->tpm_key_policy); + if (err) + { + gnutls_assert (); + _gnutls_debug_log + ("Failed to create key policy object: %s\n", + Trspi_Error_String (err)); + ret = GNUTLS_E_TPM_ERROR; + goto out_key; + } + err = Tspi_Policy_AssignToObject (s->tpm_key_policy, s->tpm_key); + if (err) + { + gnutls_assert (); + _gnutls_debug_log ("Failed to assign policy to key: %s\n", + Trspi_Error_String (err)); + ret = GNUTLS_E_TPM_ERROR; + goto out_key_policy; + } + } + + ret = + _gnutls_pin_func (_gnutls_pin_data, attempts++, "tpm:", + "TPM key", 0, pin_value, + GNUTLS_PKCS11_MAX_PIN_LEN); + if (ret < 0) + { + ret = gnutls_assert_val (GNUTLS_E_PIN_ERROR); + goto out_key_policy; + } + + err = Tspi_Policy_SetSecret (s->tpm_key_policy, + TSS_SECRET_MODE_PLAIN, + strlen (pin_value), (void *) pin_value); + + if (err) + { + gnutls_assert (); + _gnutls_debug_log ("Failed to set key PIN: %s\n", + Trspi_Error_String (err)); + goto out_key_policy; + } + goto retry_sign; + } + else if (ret < 0) + { + gnutls_assert (); + goto out_blob; + } + + gnutls_free (asn1.data); + return 0; +out_key_policy: + Tspi_Context_CloseObject (s->tpm_context, s->tpm_key_policy); + s->tpm_key_policy = 0; +out_key: + Tspi_Context_CloseObject (s->tpm_context, s->tpm_key); + s->tpm_key = 0; +out_srkpol: + Tspi_Context_CloseObject (s->tpm_context, s->srk_policy); + s->srk_policy = 0; +out_srk: + Tspi_Context_CloseObject (s->tpm_context, s->srk); + s->srk = 0; +out_context: + Tspi_Context_Close (s->tpm_context); + s->tpm_context = 0; +out_ctx: + gnutls_free (s); +out_blob: + gnutls_free (asn1.data); + return ret; +} +