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