Make GnuTLS parse_pkcs12() return extra certificates from the PKCS#12 too
[platform/upstream/openconnect.git] / gnutls_pkcs12.c
1 /*
2  * Ick. This is (or at least started off as) a straight copy of
3  * parse_pkcs12() from GnuTLS lib/gnutls_x509.c, as of commit ID
4  * 77670476814c078bbad56ce8772b192a3b5736b6 on the gnutls_2_12_x
5  * branch.
6  *
7  * We need to *see* the cert so that we can check its expiry, and
8  * we'll also want to get all the other certs in the PKCS#12 file
9  * rather than only the leaf node. Hopefully these changes can be
10  * merged back into GnuTLS as soon as possible, it can be made a
11  * public function, and this copy can die.
12  */
13 #define opaque unsigned char
14 #define gnutls_assert() do {} while(0)
15
16 /*
17  * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
18  * Free Software Foundation, Inc.
19  *
20  * Author: Nikos Mavrogiannopoulos
21  *
22  * This file WAS part of GnuTLS.
23  *
24  * The GnuTLS is free software; you can redistribute it and/or
25  * modify it under the terms of the GNU Lesser General Public License
26  * as published by the Free Software Foundation; either version 2.1 of
27  * the License, or (at your option) any later version.
28  *
29  * This library is distributed in the hope that it will be useful, but
30  * WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
32  * Lesser General Public License for more details.
33  *
34  * You should have received a copy of the GNU Lesser General Public
35  * License along with this library; if not, write to the Free Software
36  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
37  * USA
38  *
39  */
40
41
42 static int
43 parse_pkcs12 (gnutls_certificate_credentials_t res,
44               gnutls_pkcs12_t p12,
45               const char *password,
46               gnutls_x509_privkey_t * key,
47               gnutls_x509_crt_t * cert,
48               gnutls_x509_crt_t ** extra_certs_ret,
49               unsigned int * extra_certs_ret_len,
50               gnutls_x509_crl_t * crl)
51 {
52   gnutls_pkcs12_bag_t bag = NULL;
53   gnutls_x509_crt_t *extra_certs = NULL;
54   int extra_certs_len = 0;
55   int idx = 0;
56   int ret;
57   size_t cert_id_size = 0;
58   size_t key_id_size = 0;
59   opaque cert_id[20];
60   opaque key_id[20];
61   int privkey_ok = 0;
62
63   *cert = NULL;
64   *key = NULL;
65   *crl = NULL;
66
67   /* find the first private key */
68   for (;;)
69     {
70       int elements_in_bag;
71       int i;
72
73       ret = gnutls_pkcs12_bag_init (&bag);
74       if (ret < 0)
75         {
76           bag = NULL;
77           gnutls_assert ();
78           goto done;
79         }
80
81       ret = gnutls_pkcs12_get_bag (p12, idx, bag);
82       if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
83         break;
84       if (ret < 0)
85         {
86           gnutls_assert ();
87           goto done;
88         }
89
90       ret = gnutls_pkcs12_bag_get_type (bag, 0);
91       if (ret < 0)
92         {
93           gnutls_assert ();
94           goto done;
95         }
96
97       if (ret == GNUTLS_BAG_ENCRYPTED)
98         {
99           ret = gnutls_pkcs12_bag_decrypt (bag, password);
100           if (ret < 0)
101             {
102               gnutls_assert ();
103               goto done;
104             }
105         }
106
107       elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
108       if (elements_in_bag < 0)
109         {
110           gnutls_assert ();
111           goto done;
112         }
113
114       for (i = 0; i < elements_in_bag; i++)
115         {
116           int type;
117           gnutls_datum_t data;
118
119           type = gnutls_pkcs12_bag_get_type (bag, i);
120           if (type < 0)
121             {
122               gnutls_assert ();
123               goto done;
124             }
125
126           ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
127           if (ret < 0)
128             {
129               gnutls_assert ();
130               goto done;
131             }
132
133           switch (type)
134             {
135             case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
136             case GNUTLS_BAG_PKCS8_KEY:
137               if (*key != NULL) /* too simple to continue */
138                 {
139                   gnutls_assert ();
140                   break;
141                 }
142
143               ret = gnutls_x509_privkey_init (key);
144               if (ret < 0)
145                 {
146                   gnutls_assert ();
147                   goto done;
148                 }
149
150               ret = gnutls_x509_privkey_import_pkcs8
151                 (*key, &data, GNUTLS_X509_FMT_DER, password,
152                  type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0);
153               if (ret < 0)
154                 {
155                   gnutls_assert ();
156                   gnutls_x509_privkey_deinit (*key);
157                   goto done;
158                 }
159
160               key_id_size = sizeof (key_id);
161               ret =
162                 gnutls_x509_privkey_get_key_id (*key, 0, key_id,
163                                                 &key_id_size);
164               if (ret < 0)
165                 {
166                   gnutls_assert ();
167                   gnutls_x509_privkey_deinit (*key);
168                   goto done;
169                 }
170
171               privkey_ok = 1;   /* break */
172               break;
173             default:
174               break;
175             }
176         }
177
178       idx++;
179       gnutls_pkcs12_bag_deinit (bag);
180
181       if (privkey_ok != 0)      /* private key was found */
182         break;
183     }
184
185   if (privkey_ok == 0)          /* no private key */
186     {
187       gnutls_assert ();
188       return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
189     }
190
191   /* now find the corresponding certificate 
192    */
193   idx = 0;
194   bag = NULL;
195   for (;;)
196     {
197       int elements_in_bag;
198       int i;
199
200       ret = gnutls_pkcs12_bag_init (&bag);
201       if (ret < 0)
202         {
203           bag = NULL;
204           gnutls_assert ();
205           goto done;
206         }
207
208       ret = gnutls_pkcs12_get_bag (p12, idx, bag);
209       if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
210         break;
211       if (ret < 0)
212         {
213           gnutls_assert ();
214           goto done;
215         }
216
217       ret = gnutls_pkcs12_bag_get_type (bag, 0);
218       if (ret < 0)
219         {
220           gnutls_assert ();
221           goto done;
222         }
223
224       if (ret == GNUTLS_BAG_ENCRYPTED)
225         {
226           ret = gnutls_pkcs12_bag_decrypt (bag, password);
227           if (ret < 0)
228             {
229               gnutls_assert ();
230               goto done;
231             }
232         }
233
234       elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
235       if (elements_in_bag < 0)
236         {
237           gnutls_assert ();
238           goto done;
239         }
240
241       for (i = 0; i < elements_in_bag; i++)
242         {
243           int type;
244           gnutls_datum_t data;
245           gnutls_x509_crt_t this_cert;
246
247           type = gnutls_pkcs12_bag_get_type (bag, i);
248           if (type < 0)
249             {
250               gnutls_assert ();
251               goto done;
252             }
253
254           ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
255           if (ret < 0)
256             {
257               gnutls_assert ();
258               goto done;
259             }
260
261           switch (type)
262             {
263             case GNUTLS_BAG_CERTIFICATE:
264               ret = gnutls_x509_crt_init (&this_cert);
265               if (ret < 0)
266                 {
267                   gnutls_assert ();
268                   goto done;
269                 }
270
271               ret =
272                 gnutls_x509_crt_import (this_cert, &data, GNUTLS_X509_FMT_DER);
273               if (ret < 0)
274                 {
275                   gnutls_assert ();
276                   gnutls_x509_crt_deinit (this_cert);
277                   goto done;
278                 }
279
280               /* check if the key id match */
281               cert_id_size = sizeof (cert_id);
282               ret =
283                 gnutls_x509_crt_get_key_id (this_cert, 0, cert_id, &cert_id_size);
284               if (ret < 0)
285                 {
286                   gnutls_assert ();
287                   gnutls_x509_crt_deinit (this_cert);
288                   goto done;
289                 }
290
291               if (memcmp (cert_id, key_id, cert_id_size) != 0)
292                 {               /* they don't match - skip the certificate */
293                   if (extra_certs_ret)
294                     {
295                       extra_certs = gnutls_realloc (extra_certs,
296                                                     sizeof(extra_certs[0]) *
297                                                     ++extra_certs_len);
298                       if (!extra_certs)
299                         {
300                           gnutls_assert ();
301                           ret = GNUTLS_E_MEMORY_ERROR;
302                           goto done;
303                         }
304                       extra_certs[extra_certs_len - 1] = this_cert;
305                       this_cert = NULL;
306                     }
307                   else
308                     {
309                        gnutls_x509_crt_deinit (this_cert);
310                     }
311                   break;
312                 }
313               else
314                 {
315                    if (*cert != NULL)        /* no need to set it again */
316                      {
317                         gnutls_assert ();
318                         break;
319                      }
320                    *cert = this_cert;
321                    this_cert = NULL;
322                 }
323               break;
324
325             case GNUTLS_BAG_CRL:
326               if (*crl != NULL)
327                 {
328                   gnutls_assert ();
329                   break;
330                 }
331
332               ret = gnutls_x509_crl_init (crl);
333               if (ret < 0)
334                 {
335                   gnutls_assert ();
336                   goto done;
337                 }
338
339               ret = gnutls_x509_crl_import (*crl, &data, GNUTLS_X509_FMT_DER);
340               if (ret < 0)
341                 {
342                   gnutls_assert ();
343                   gnutls_x509_crl_deinit (*crl);
344                   goto done;
345                 }
346               break;
347
348             case GNUTLS_BAG_ENCRYPTED:
349               /* XXX Bother to recurse one level down?  Unlikely to
350                  use the same password anyway. */
351             case GNUTLS_BAG_EMPTY:
352             default:
353               break;
354             }
355         }
356
357       idx++;
358       gnutls_pkcs12_bag_deinit (bag);
359     }
360
361   ret = 0;
362
363 done:
364   if (bag)
365     gnutls_pkcs12_bag_deinit (bag);
366
367   if (ret)
368     {
369       if (*key)
370         gnutls_x509_privkey_deinit(*key);
371       if (*cert)
372         gnutls_x509_crt_deinit(*cert);
373       if (extra_certs_len)
374         {
375           int i;
376           for (i = 0; i < extra_certs_len; i++)
377             gnutls_x509_crt_deinit(extra_certs[i]);
378           gnutls_free(extra_certs);
379         }
380     }
381   else if (extra_certs_ret)
382     {
383       *extra_certs_ret = extra_certs;
384       *extra_certs_ret_len = extra_certs_len;
385     }
386
387   return ret;
388 }