Import pkcs12_parse() function from GnuTLS to fix PKCS#12 handling
[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, gnutls_x509_crl_t * crl)
48 {
49   gnutls_pkcs12_bag_t bag = NULL;
50   int idx = 0;
51   int ret;
52   size_t cert_id_size = 0;
53   size_t key_id_size = 0;
54   opaque cert_id[20];
55   opaque key_id[20];
56   int privkey_ok = 0;
57
58   *cert = NULL;
59   *key = NULL;
60   *crl = NULL;
61
62   /* find the first private key */
63   for (;;)
64     {
65       int elements_in_bag;
66       int i;
67
68       ret = gnutls_pkcs12_bag_init (&bag);
69       if (ret < 0)
70         {
71           bag = NULL;
72           gnutls_assert ();
73           goto done;
74         }
75
76       ret = gnutls_pkcs12_get_bag (p12, idx, bag);
77       if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
78         break;
79       if (ret < 0)
80         {
81           gnutls_assert ();
82           goto done;
83         }
84
85       ret = gnutls_pkcs12_bag_get_type (bag, 0);
86       if (ret < 0)
87         {
88           gnutls_assert ();
89           goto done;
90         }
91
92       if (ret == GNUTLS_BAG_ENCRYPTED)
93         {
94           ret = gnutls_pkcs12_bag_decrypt (bag, password);
95           if (ret < 0)
96             {
97               gnutls_assert ();
98               goto done;
99             }
100         }
101
102       elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
103       if (elements_in_bag < 0)
104         {
105           gnutls_assert ();
106           goto done;
107         }
108
109       for (i = 0; i < elements_in_bag; i++)
110         {
111           int type;
112           gnutls_datum_t data;
113
114           type = gnutls_pkcs12_bag_get_type (bag, i);
115           if (type < 0)
116             {
117               gnutls_assert ();
118               goto done;
119             }
120
121           ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
122           if (ret < 0)
123             {
124               gnutls_assert ();
125               goto done;
126             }
127
128           switch (type)
129             {
130             case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
131             case GNUTLS_BAG_PKCS8_KEY:
132               if (*key != NULL) /* too simple to continue */
133                 {
134                   gnutls_assert ();
135                   break;
136                 }
137
138               ret = gnutls_x509_privkey_init (key);
139               if (ret < 0)
140                 {
141                   gnutls_assert ();
142                   goto done;
143                 }
144
145               ret = gnutls_x509_privkey_import_pkcs8
146                 (*key, &data, GNUTLS_X509_FMT_DER, password,
147                  type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0);
148               if (ret < 0)
149                 {
150                   gnutls_assert ();
151                   gnutls_x509_privkey_deinit (*key);
152                   goto done;
153                 }
154
155               key_id_size = sizeof (key_id);
156               ret =
157                 gnutls_x509_privkey_get_key_id (*key, 0, key_id,
158                                                 &key_id_size);
159               if (ret < 0)
160                 {
161                   gnutls_assert ();
162                   gnutls_x509_privkey_deinit (*key);
163                   goto done;
164                 }
165
166               privkey_ok = 1;   /* break */
167               break;
168             default:
169               break;
170             }
171         }
172
173       idx++;
174       gnutls_pkcs12_bag_deinit (bag);
175
176       if (privkey_ok != 0)      /* private key was found */
177         break;
178     }
179
180   if (privkey_ok == 0)          /* no private key */
181     {
182       gnutls_assert ();
183       return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
184     }
185
186   /* now find the corresponding certificate 
187    */
188   idx = 0;
189   bag = NULL;
190   for (;;)
191     {
192       int elements_in_bag;
193       int i;
194
195       ret = gnutls_pkcs12_bag_init (&bag);
196       if (ret < 0)
197         {
198           bag = NULL;
199           gnutls_assert ();
200           goto done;
201         }
202
203       ret = gnutls_pkcs12_get_bag (p12, idx, bag);
204       if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
205         break;
206       if (ret < 0)
207         {
208           gnutls_assert ();
209           goto done;
210         }
211
212       ret = gnutls_pkcs12_bag_get_type (bag, 0);
213       if (ret < 0)
214         {
215           gnutls_assert ();
216           goto done;
217         }
218
219       if (ret == GNUTLS_BAG_ENCRYPTED)
220         {
221           ret = gnutls_pkcs12_bag_decrypt (bag, password);
222           if (ret < 0)
223             {
224               gnutls_assert ();
225               goto done;
226             }
227         }
228
229       elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
230       if (elements_in_bag < 0)
231         {
232           gnutls_assert ();
233           goto done;
234         }
235
236       for (i = 0; i < elements_in_bag; i++)
237         {
238           int type;
239           gnutls_datum_t data;
240
241           type = gnutls_pkcs12_bag_get_type (bag, i);
242           if (type < 0)
243             {
244               gnutls_assert ();
245               goto done;
246             }
247
248           ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
249           if (ret < 0)
250             {
251               gnutls_assert ();
252               goto done;
253             }
254
255           switch (type)
256             {
257             case GNUTLS_BAG_CERTIFICATE:
258               if (*cert != NULL)        /* no need to set it again */
259                 {
260                   gnutls_assert ();
261                   break;
262                 }
263
264               ret = gnutls_x509_crt_init (cert);
265               if (ret < 0)
266                 {
267                   gnutls_assert ();
268                   goto done;
269                 }
270
271               ret =
272                 gnutls_x509_crt_import (*cert, &data, GNUTLS_X509_FMT_DER);
273               if (ret < 0)
274                 {
275                   gnutls_assert ();
276                   gnutls_x509_crt_deinit (*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 (*cert, 0, cert_id, &cert_id_size);
284               if (ret < 0)
285                 {
286                   gnutls_assert ();
287                   gnutls_x509_crt_deinit (*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                   gnutls_x509_crt_deinit (*cert);
294                   *cert = NULL;
295                 }
296               break;
297
298             case GNUTLS_BAG_CRL:
299               if (*crl != NULL)
300                 {
301                   gnutls_assert ();
302                   break;
303                 }
304
305               ret = gnutls_x509_crl_init (crl);
306               if (ret < 0)
307                 {
308                   gnutls_assert ();
309                   goto done;
310                 }
311
312               ret = gnutls_x509_crl_import (*crl, &data, GNUTLS_X509_FMT_DER);
313               if (ret < 0)
314                 {
315                   gnutls_assert ();
316                   gnutls_x509_crl_deinit (*crl);
317                   goto done;
318                 }
319               break;
320
321             case GNUTLS_BAG_ENCRYPTED:
322               /* XXX Bother to recurse one level down?  Unlikely to
323                  use the same password anyway. */
324             case GNUTLS_BAG_EMPTY:
325             default:
326               break;
327             }
328         }
329
330       idx++;
331       gnutls_pkcs12_bag_deinit (bag);
332     }
333
334   ret = 0;
335
336 done:
337   if (bag)
338     gnutls_pkcs12_bag_deinit (bag);
339
340   return ret;
341 }