resetting manifest requested domain to floor
[platform/upstream/openconnect.git] / gnutls_tpm.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2012 Intel Corporation.
5  *
6  * Author: David Woodhouse <dwmw2@infradead.org>
7  *
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.
11  *
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.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to:
19  *
20  *   Free Software Foundation, Inc.
21  *   51 Franklin Street, Fifth Floor,
22  *   Boston, MA 02110-1301 USA
23  */
24
25 /*
26  * TPM code based on client-tpm.c from
27  * Carolin Latze <latze@angry-red-pla.net> and Tobias Soder
28  */
29
30 #include <errno.h>
31 #include <string.h>
32
33 #include <gnutls/gnutls.h>
34 #include "openconnect-internal.h"
35
36 #include "gnutls.h"
37
38 #ifdef HAVE_TROUSERS
39
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);
43
44
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
49    *session* */
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,
53                       gnutls_datum_t *sig)
54 {
55         struct openconnect_info *vpninfo = _vpninfo;
56
57         if (cert_type != GNUTLS_CRT_X509)
58                 return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
59
60         return tpm_sign_fn(NULL, vpninfo, data, sig);
61 }
62
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
66    signing function. */
67 int gtls2_tpm_sign_dummy_data(struct openconnect_info *vpninfo,
68                               const gnutls_datum_t *data,
69                               gnutls_datum_t *sig)
70 {
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 */
79         };
80         gnutls_datum_t hash;
81         unsigned char digest[sizeof(ber_encode) + SHA1_SIZE];
82         size_t shalen = SHA1_SIZE;
83         int err;
84
85         err = gnutls_fingerprint(GNUTLS_DIG_SHA1, data,
86                                  &digest[sizeof(ber_encode)], &shalen);
87         if (err) {
88                 vpn_progress(vpninfo, PRG_ERR,
89                              _("Failed to SHA1 input data for signing: %s\n"),
90                              gnutls_strerror(err));
91                 return err;
92         }
93
94         memcpy(digest, ber_encode, sizeof(ber_encode));
95
96         hash.data = digest;
97         hash.size = sizeof(digest);
98
99         return tpm_sign_fn(NULL, vpninfo, &hash, sig);
100 }
101 #endif /* !HAVE_GNUTLS_CERTIFICATE_SET_KEY */
102
103 static int tpm_sign_fn(gnutls_privkey_t key, void *_vpninfo,
104                        const gnutls_datum_t *data, gnutls_datum_t *sig)
105 {
106         struct openconnect_info *vpninfo = _vpninfo;
107         TSS_HHASH hash;
108         int err;
109
110         vpn_progress(vpninfo, PRG_TRACE,
111                      _("TPM sign function called for %d bytes.\n"),
112                      data->size);
113
114         err = Tspi_Context_CreateObject(vpninfo->tpm_context, TSS_OBJECT_TYPE_HASH,
115                                         TSS_HASH_OTHER, &hash);
116         if (err) {
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;
121         }
122         err = Tspi_Hash_SetHashValue(hash, data->size, data->data);
123         if (err) {
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;
129         }
130         err = Tspi_Hash_Sign(hash, vpninfo->tpm_key, &sig->size, &sig->data);
131         Tspi_Context_CloseObject(vpninfo->tpm_context, hash);
132         if (err) {
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;
139                 else
140                         return GNUTLS_E_PK_SIGN_FAILED;
141         }
142         return 0;
143 }
144
145 int load_tpm_key(struct openconnect_info *vpninfo, gnutls_datum_t *fdata,
146                  gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig)
147 {
148         static const TSS_UUID SRK_UUID = TSS_UUID_SRK;
149         gnutls_datum_t asn1;
150         unsigned int tss_len;
151         char *pass;
152         int ofs, err;
153
154         err = gnutls_pem_base64_decode_alloc("TSS KEY BLOB", fdata, &asn1);
155         if (err) {
156                 vpn_progress(vpninfo, PRG_ERR,
157                              _("Error decoding TSS key blob: %s\n"),
158                              gnutls_strerror(err));
159                 return -EINVAL;
160         }
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"));
165                 goto out_blob;
166         }
167
168         tss_len = asn1.data[1];
169         ofs = 2;
170         if (tss_len & 0x80) {
171                 int lenlen = tss_len & 0x7f;
172
173                 if (asn1.size < 2 + lenlen || lenlen > 3) {
174                         vpn_progress(vpninfo, PRG_ERR,
175                                      _("Error in TSS key blob\n"));
176                         goto out_blob;
177                 }
178
179                 tss_len = 0;
180                 while (lenlen) {
181                         tss_len <<= 8;
182                         tss_len |= asn1.data[ofs++];
183                         lenlen--;
184                 }
185         }
186         if (tss_len + ofs != asn1.size) {
187                 vpn_progress(vpninfo, PRG_ERR,
188                              _("Error in TSS key blob\n"));
189                 goto out_blob;
190         }
191
192         err = Tspi_Context_Create(&vpninfo->tpm_context);
193         if (err) {
194                 vpn_progress(vpninfo, PRG_ERR,
195                              _("Failed to create TPM context: %s\n"),
196                              Trspi_Error_String(err));
197                 goto out_blob;
198         }
199         err = Tspi_Context_Connect(vpninfo->tpm_context, NULL);
200         if (err) {
201                 vpn_progress(vpninfo, PRG_ERR,
202                              _("Failed to connect TPM context: %s\n"),
203                              Trspi_Error_String(err));
204                 goto out_context;
205         }
206         err = Tspi_Context_LoadKeyByUUID(vpninfo->tpm_context, TSS_PS_TYPE_SYSTEM,
207                                          SRK_UUID, &vpninfo->srk);
208         if (err) {
209                 vpn_progress(vpninfo, PRG_ERR,
210                              _("Failed to load TPM SRK key: %s\n"),
211                              Trspi_Error_String(err));
212                 goto out_context;
213         }
214         err = Tspi_GetPolicyObject(vpninfo->srk, TSS_POLICY_USAGE, &vpninfo->srk_policy);
215         if (err) {
216                 vpn_progress(vpninfo, PRG_ERR,
217                              _("Failed to load TPM SRK policy object: %s\n"),
218                              Trspi_Error_String(err));
219                 goto out_srk;
220         }
221
222         pass = vpninfo->cert_password;
223         vpninfo->cert_password = NULL;
224         while (1) {
225                 static const char nullpass[20];
226
227                 /* We don't seem to get the error here... */
228                 if (pass)
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);
236                 if (err) {
237                         vpn_progress(vpninfo, PRG_ERR,
238                                      _("Failed to set TPM PIN: %s\n"),
239                                      Trspi_Error_String(err));
240                         goto out_srkpol;
241                 }
242
243                 free(pass);
244
245                 /* ... we get it here instead. */
246                 err = Tspi_Context_LoadKeyByBlob(vpninfo->tpm_context, vpninfo->srk,
247                                                  tss_len, asn1.data + ofs,
248                                                  &vpninfo->tpm_key);
249                 if (!err)
250                         break;
251
252                 if (pass)
253                         vpn_progress(vpninfo, PRG_ERR,
254                                      _("Failed to load TPM key blob: %s\n"),
255                                      Trspi_Error_String(err));
256
257                 if (err != TPM_E_AUTHFAIL)
258                         goto out_srkpol;
259
260                 err = request_passphrase(vpninfo, "openconnect_tpm_srk",
261                                          &pass, _("Enter TPM SRK PIN:"));
262                 if (err)
263                         goto out_srkpol;
264         }
265
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);
272 #else
273         *pkey = OPENCONNECT_TPM_PKEY;
274 #endif
275
276  retry_sign:
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,
282                                                         TSS_POLICY_USAGE,
283                                                         &vpninfo->tpm_key_policy);
284                         if (err) {
285                                 vpn_progress(vpninfo, PRG_ERR,
286                                              _("Failed to create key policy object: %s\n"),
287                                              Trspi_Error_String(err));
288                                 goto out_key;
289                         }
290                         err = Tspi_Policy_AssignToObject(vpninfo->tpm_key_policy,
291                                                          vpninfo->tpm_key);
292                         if (err) {
293                                 vpn_progress(vpninfo, PRG_ERR,
294                                              _("Failed to assign policy to key: %s\n"),
295                                              Trspi_Error_String(err));
296                                 goto out_key_policy;
297                         }
298                 }
299                 err = request_passphrase(vpninfo, "openconnect_tpm_key",
300                                          &pass, _("Enter TPM key PIN:"));
301                 if (err)
302                         goto out_key_policy;
303
304                 err = Tspi_Policy_SetSecret(vpninfo->tpm_key_policy,
305                                             TSS_SECRET_MODE_PLAIN,
306                                             strlen(pass), (void *)pass);
307                 free (pass);
308
309                 if (err) {
310                         vpn_progress(vpninfo, PRG_ERR,
311                                      _("Failed to set key PIN: %s\n"),
312                                      Trspi_Error_String(err));
313                         goto out_key_policy;
314                 }
315                 goto retry_sign;
316         }
317
318         free (asn1.data);
319         return 0;
320  out_key_policy:
321         Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->tpm_key_policy);
322         vpninfo->tpm_key_policy = 0;
323  out_key:
324         Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->tpm_key);
325         vpninfo->tpm_key = 0;
326  out_srkpol:
327         Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->srk_policy);
328         vpninfo->srk_policy = 0;
329  out_srk:
330         Tspi_Context_CloseObject(vpninfo->tpm_context, vpninfo->srk);
331         vpninfo->srk = 0;
332  out_context:
333         Tspi_Context_Close(vpninfo->tpm_context);
334         vpninfo->tpm_context = 0;
335  out_blob:
336         free (asn1.data);
337         return -EIO;
338 }
339
340 #endif /* HAVE_TROUSERS */