2 * OpenConnect (SSL + DTLS) VPN client
4 * Copyright © 2008-2012 Intel Corporation.
6 * Author: David Woodhouse <dwmw2@infradead.org>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * version 2.1, as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301 USA
26 * TPM code based on client-tpm.c from
27 * Carolin Latze <latze@angry-red-pla.net> and Tobias Soder
33 #include <gnutls/gnutls.h>
34 #include "openconnect-internal.h"
40 /* Signing function for TPM privkeys, set with gnutls_privkey_import_ext() */
41 static int tpm_sign_fn(gnutls_privkey_t key, void *_vpninfo,
42 const gnutls_datum_t *data, gnutls_datum_t *sig);
45 #ifndef HAVE_GNUTLS_CERTIFICATE_SET_KEY
46 /* We *want* to use gnutls_privkey_import_ext() to create a privkey with our
47 own signing function tpm_sign_fn(). But GnuTLS 2.12 doesn't support that,
48 so instead we have to register this sign_callback function with the
50 int gtls2_tpm_sign_cb(gnutls_session_t sess, void *_vpninfo,
51 gnutls_certificate_type_t cert_type,
52 const gnutls_datum_t *cert, const gnutls_datum_t *data,
55 struct openconnect_info *vpninfo = _vpninfo;
57 if (cert_type != GNUTLS_CRT_X509)
58 return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
60 return tpm_sign_fn(NULL, vpninfo, data, sig);
63 /* In GnuTLS 2.12 since we don't have a normal privkey and hence can't just
64 use gnutls_privkey_sign_data() with it, we have to jump through hoops to
65 prepare the hash in exactly the right way and call our internal TPM
67 int gtls2_tpm_sign_dummy_data(struct openconnect_info *vpninfo,
68 const gnutls_datum_t *data,
71 static const unsigned char ber_encode[15] = {
72 0x30, 0x21, /* SEQUENCE, length 31 */
73 0x30, 0x09, /* SEQUENCE, length 9 */
74 0x06, 0x05, /* OBJECT_ID, length 5 */
75 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* SHA1 OID: 1.3.14.3.2.26 */
76 0x05, 0x00, /* NULL (parameters) */
77 0x04, 0x14, /* OCTET_STRING, length 20 */
78 /* followed by the 20-byte sha1 */
81 unsigned char digest[sizeof(ber_encode) + SHA1_SIZE];
82 size_t shalen = SHA1_SIZE;
85 err = gnutls_fingerprint(GNUTLS_DIG_SHA1, data,
86 &digest[sizeof(ber_encode)], &shalen);
88 vpn_progress(vpninfo, PRG_ERR,
89 _("Failed to SHA1 input data for signing: %s\n"),
90 gnutls_strerror(err));
94 memcpy(digest, ber_encode, sizeof(ber_encode));
97 hash.size = sizeof(digest);
99 return tpm_sign_fn(NULL, vpninfo, &hash, sig);
101 #endif /* !HAVE_GNUTLS_CERTIFICATE_SET_KEY */
103 static int tpm_sign_fn(gnutls_privkey_t key, void *_vpninfo,
104 const gnutls_datum_t *data, gnutls_datum_t *sig)
106 struct openconnect_info *vpninfo = _vpninfo;
110 vpn_progress(vpninfo, PRG_TRACE,
111 _("TPM sign function called for %d bytes.\n"),
114 err = Tspi_Context_CreateObject(vpninfo->tpm_context, TSS_OBJECT_TYPE_HASH,
115 TSS_HASH_OTHER, &hash);
117 vpn_progress(vpninfo, PRG_ERR,
118 _("Failed to create TPM hash object: %s\n"),
119 Trspi_Error_String(err));
120 return GNUTLS_E_PK_SIGN_FAILED;
122 err = Tspi_Hash_SetHashValue(hash, data->size, data->data);
124 vpn_progress(vpninfo, PRG_ERR,
125 _("Failed to set value in TPM hash object: %s\n"),
126 Trspi_Error_String(err));
127 Tspi_Context_CloseObject(vpninfo->tpm_context, hash);
128 return GNUTLS_E_PK_SIGN_FAILED;
130 err = Tspi_Hash_Sign(hash, vpninfo->tpm_key, &sig->size, &sig->data);
131 Tspi_Context_CloseObject(vpninfo->tpm_context, hash);
133 if (vpninfo->tpm_key_policy || err != TPM_E_AUTHFAIL)
134 vpn_progress(vpninfo, PRG_ERR,
135 _("TPM hash signature failed: %s\n"),
136 Trspi_Error_String(err));
137 if (err == TPM_E_AUTHFAIL)
138 return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
140 return GNUTLS_E_PK_SIGN_FAILED;
145 int load_tpm_key(struct openconnect_info *vpninfo, gnutls_datum_t *fdata,
146 gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig)
148 static const TSS_UUID SRK_UUID = TSS_UUID_SRK;
150 unsigned int tss_len;
154 err = gnutls_pem_base64_decode_alloc("TSS KEY BLOB", fdata, &asn1);
156 vpn_progress(vpninfo, PRG_ERR,
157 _("Error decoding TSS key blob: %s\n"),
158 gnutls_strerror(err));
161 /* Ick. We have to parse the ASN1 OCTET_STRING for ourselves. */
162 if (asn1.size < 2 || asn1.data[0] != 0x04 /* OCTET_STRING */) {
163 vpn_progress(vpninfo, PRG_ERR,
164 _("Error in TSS key blob\n"));
168 tss_len = asn1.data[1];
170 if (tss_len & 0x80) {
171 int lenlen = tss_len & 0x7f;
173 if (asn1.size < 2 + lenlen || lenlen > 3) {
174 vpn_progress(vpninfo, PRG_ERR,
175 _("Error in TSS key blob\n"));
182 tss_len |= asn1.data[ofs++];
186 if (tss_len + ofs != asn1.size) {
187 vpn_progress(vpninfo, PRG_ERR,
188 _("Error in TSS key blob\n"));
192 err = Tspi_Context_Create(&vpninfo->tpm_context);
194 vpn_progress(vpninfo, PRG_ERR,
195 _("Failed to create TPM context: %s\n"),
196 Trspi_Error_String(err));
199 err = Tspi_Context_Connect(vpninfo->tpm_context, NULL);
201 vpn_progress(vpninfo, PRG_ERR,
202 _("Failed to connect TPM context: %s\n"),
203 Trspi_Error_String(err));
206 err = Tspi_Context_LoadKeyByUUID(vpninfo->tpm_context, TSS_PS_TYPE_SYSTEM,
207 SRK_UUID, &vpninfo->srk);
209 vpn_progress(vpninfo, PRG_ERR,
210 _("Failed to load TPM SRK key: %s\n"),
211 Trspi_Error_String(err));
214 err = Tspi_GetPolicyObject(vpninfo->srk, TSS_POLICY_USAGE, &vpninfo->srk_policy);
216 vpn_progress(vpninfo, PRG_ERR,
217 _("Failed to load TPM SRK policy object: %s\n"),
218 Trspi_Error_String(err));
222 pass = vpninfo->cert_password;
223 vpninfo->cert_password = NULL;
225 static const char nullpass[20];
227 /* We don't seem to get the error here... */
229 err = Tspi_Policy_SetSecret(vpninfo->srk_policy,
230 TSS_SECRET_MODE_PLAIN,
231 strlen(pass), (BYTE *)pass);
232 else /* Well-known NULL key */
233 err = Tspi_Policy_SetSecret(vpninfo->srk_policy,
234 TSS_SECRET_MODE_SHA1,
235 sizeof(nullpass), (BYTE *)nullpass);
237 vpn_progress(vpninfo, PRG_ERR,
238 _("Failed to set TPM PIN: %s\n"),
239 Trspi_Error_String(err));
245 /* ... we get it here instead. */
246 err = Tspi_Context_LoadKeyByBlob(vpninfo->tpm_context, vpninfo->srk,
247 tss_len, asn1.data + ofs,
253 vpn_progress(vpninfo, PRG_ERR,
254 _("Failed to load TPM key blob: %s\n"),
255 Trspi_Error_String(err));
257 if (err != TPM_E_AUTHFAIL)
260 err = request_passphrase(vpninfo, "openconnect_tpm_srk",
261 &pass, _("Enter TPM SRK PIN:"));
266 #ifdef HAVE_GNUTLS_CERTIFICATE_SET_KEY
267 gnutls_privkey_init(pkey);
268 /* This would be nicer if there was a destructor callback. I could
269 allocate a data structure with the TPM handles and the vpninfo
270 pointer, and destroy that properly when the key is destroyed. */
271 gnutls_privkey_import_ext(*pkey, GNUTLS_PK_RSA, vpninfo, tpm_sign_fn, NULL, 0);
273 *pkey = OPENCONNECT_TPM_PKEY;
277 err = sign_dummy_data(vpninfo, *pkey, fdata, pkey_sig);
278 if (err == GNUTLS_E_INSUFFICIENT_CREDENTIALS) {
279 if (!vpninfo->tpm_key_policy) {
280 err = Tspi_Context_CreateObject(vpninfo->tpm_context,
281 TSS_OBJECT_TYPE_POLICY,
283 &vpninfo->tpm_key_policy);
285 vpn_progress(vpninfo, PRG_ERR,
286 _("Failed to create key policy object: %s\n"),
287 Trspi_Error_String(err));
290 err = Tspi_Policy_AssignToObject(vpninfo->tpm_key_policy,
293 vpn_progress(vpninfo, PRG_ERR,
294 _("Failed to assign policy to key: %s\n"),
295 Trspi_Error_String(err));
299 err = request_passphrase(vpninfo, "openconnect_tpm_key",
300 &pass, _("Enter TPM key PIN:"));
304 err = Tspi_Policy_SetSecret(vpninfo->tpm_key_policy,
305 TSS_SECRET_MODE_PLAIN,
306 strlen(pass), (void *)pass);
310 vpn_progress(vpninfo, PRG_ERR,
311 _("Failed to set key PIN: %s\n"),
312 Trspi_Error_String(err));
321 Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->tpm_key_policy);
322 vpninfo->tpm_key_policy = 0;
324 Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->tpm_key);
325 vpninfo->tpm_key = 0;
327 Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->srk_policy);
328 vpninfo->srk_policy = 0;
330 Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->srk);
333 Tspi_Context_Close(vpninfo->tpm_context);
334 vpninfo->tpm_context = 0;
340 #endif /* HAVE_TROUSERS */