send full certificate chain during handshake
[platform/upstream/glib-networking.git] / tls / gnutls / gtlscertificate-gnutls.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright 2009 Red Hat, Inc
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, see
17  * <http://www.gnu.org/licenses/>.
18  */
19
20 #include "config.h"
21
22 #include <gnutls/gnutls.h>
23 #include <gnutls/x509.h>
24 #include <string.h>
25
26 #include "gtlscertificate-gnutls.h"
27 #include <glib/gi18n-lib.h>
28 #include "TIZEN.h"
29
30 static void     g_tls_certificate_gnutls_initable_iface_init (GInitableIface  *iface);
31
32 G_DEFINE_TYPE_WITH_CODE (GTlsCertificateGnutls, g_tls_certificate_gnutls, G_TYPE_TLS_CERTIFICATE,
33                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
34                                                 g_tls_certificate_gnutls_initable_iface_init);)
35
36 enum
37 {
38   PROP_0,
39
40   PROP_CERTIFICATE,
41   PROP_CERTIFICATE_PEM,
42   PROP_PRIVATE_KEY,
43   PROP_PRIVATE_KEY_PEM,
44   PROP_ISSUER
45 };
46
47 struct _GTlsCertificateGnutlsPrivate
48 {
49   gnutls_x509_crt_t cert;
50   gnutls_x509_privkey_t key;
51
52   GTlsCertificateGnutls *issuer;
53
54   GError *construct_error;
55
56   guint have_cert : 1;
57   guint have_key  : 1;
58 };
59
60 static void
61 g_tls_certificate_gnutls_finalize (GObject *object)
62 {
63   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
64
65   gnutls_x509_crt_deinit (gnutls->priv->cert);
66   if (gnutls->priv->key)
67     gnutls_x509_privkey_deinit (gnutls->priv->key);
68
69   if (gnutls->priv->issuer)
70     g_object_unref (gnutls->priv->issuer);
71
72   g_clear_error (&gnutls->priv->construct_error);
73
74   G_OBJECT_CLASS (g_tls_certificate_gnutls_parent_class)->finalize (object);
75 }
76
77 static void
78 g_tls_certificate_gnutls_get_property (GObject    *object,
79                                        guint       prop_id,
80                                        GValue     *value,
81                                        GParamSpec *pspec)
82 {
83   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
84   GByteArray *certificate;
85   char *certificate_pem;
86   int status;
87   size_t size;
88
89   switch (prop_id)
90     {
91     case PROP_CERTIFICATE:
92       size = 0;
93       status = gnutls_x509_crt_export (gnutls->priv->cert,
94                                        GNUTLS_X509_FMT_DER,
95                                        NULL, &size);
96       if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
97         certificate = NULL;
98       else
99         {
100           certificate = g_byte_array_sized_new (size);
101           certificate->len = size;
102           status = gnutls_x509_crt_export (gnutls->priv->cert,
103                                            GNUTLS_X509_FMT_DER,
104                                            certificate->data, &size);
105           if (status != 0)
106             {
107               g_byte_array_free (certificate, TRUE);
108               certificate = NULL;
109             }
110         }
111       g_value_take_boxed (value, certificate);
112       break;
113
114     case PROP_CERTIFICATE_PEM:
115       size = 0;
116       status = gnutls_x509_crt_export (gnutls->priv->cert,
117                                        GNUTLS_X509_FMT_PEM,
118                                        NULL, &size);
119       if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
120         certificate_pem = NULL;
121       else
122         {
123           certificate_pem = g_malloc (size);
124           status = gnutls_x509_crt_export (gnutls->priv->cert,
125                                            GNUTLS_X509_FMT_PEM,
126                                            certificate_pem, &size);
127           if (status != 0)
128             {
129               g_free (certificate_pem);
130               certificate_pem = NULL;
131             }
132         }
133       g_value_take_string (value, certificate_pem);
134       break;
135
136     case PROP_ISSUER:
137       g_value_set_object (value, gnutls->priv->issuer);
138       break;
139
140     default:
141       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
142     }
143 }
144
145 static void
146 g_tls_certificate_gnutls_set_property (GObject      *object,
147                                        guint         prop_id,
148                                        const GValue *value,
149                                        GParamSpec   *pspec)
150 {
151   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
152   GByteArray *bytes;
153   const char *string;
154   gnutls_datum_t data;
155   int status;
156
157   switch (prop_id)
158     {
159     case PROP_CERTIFICATE:
160       bytes = g_value_get_boxed (value);
161       if (!bytes)
162         break;
163       g_return_if_fail (gnutls->priv->have_cert == FALSE);
164       data.data = bytes->data;
165       data.size = bytes->len;
166       status = gnutls_x509_crt_import (gnutls->priv->cert, &data,
167                                        GNUTLS_X509_FMT_DER);
168       if (status == 0)
169         gnutls->priv->have_cert = TRUE;
170       else if (!gnutls->priv->construct_error)
171         {
172           gnutls->priv->construct_error =
173             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
174                          _("Could not parse DER certificate: %s"),
175                          gnutls_strerror (status));
176         }
177
178       break;
179
180     case PROP_CERTIFICATE_PEM:
181       string = g_value_get_string (value);
182       if (!string)
183         break;
184       g_return_if_fail (gnutls->priv->have_cert == FALSE);
185       data.data = (void *)string;
186       data.size = strlen (string);
187       status = gnutls_x509_crt_import (gnutls->priv->cert, &data,
188                                        GNUTLS_X509_FMT_PEM);
189       if (status == 0)
190         gnutls->priv->have_cert = TRUE;
191       else if (!gnutls->priv->construct_error)
192         {
193           gnutls->priv->construct_error =
194             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
195                          _("Could not parse PEM certificate: %s"),
196                          gnutls_strerror (status));
197         }
198       break;
199
200     case PROP_PRIVATE_KEY:
201       bytes = g_value_get_boxed (value);
202       if (!bytes)
203         break;
204       g_return_if_fail (gnutls->priv->have_key == FALSE);
205       data.data = bytes->data;
206       data.size = bytes->len;
207       if (!gnutls->priv->key)
208         gnutls_x509_privkey_init (&gnutls->priv->key);
209       status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
210                                            GNUTLS_X509_FMT_DER);
211       if (status != 0)
212         {
213           int pkcs8_status =
214             gnutls_x509_privkey_import_pkcs8 (gnutls->priv->key, &data,
215                                               GNUTLS_X509_FMT_DER, NULL,
216                                               GNUTLS_PKCS_PLAIN);
217           if (pkcs8_status == 0)
218             status = 0;
219         }
220       if (status == 0)
221         gnutls->priv->have_key = TRUE;
222       else if (!gnutls->priv->construct_error)
223         {
224           gnutls->priv->construct_error =
225             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
226                          _("Could not parse DER private key: %s"),
227                          gnutls_strerror (status));
228         }
229       break;
230
231     case PROP_PRIVATE_KEY_PEM:
232       string = g_value_get_string (value);
233       if (!string)
234         break;
235       g_return_if_fail (gnutls->priv->have_key == FALSE);
236       data.data = (void *)string;
237       data.size = strlen (string);
238       if (!gnutls->priv->key)
239         gnutls_x509_privkey_init (&gnutls->priv->key);
240       status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
241                                            GNUTLS_X509_FMT_PEM);
242       if (status != 0)
243         {
244           int pkcs8_status =
245             gnutls_x509_privkey_import_pkcs8 (gnutls->priv->key, &data,
246                                               GNUTLS_X509_FMT_PEM, NULL,
247                                               GNUTLS_PKCS_PLAIN);
248           if (pkcs8_status == 0)
249             status = 0;
250         }
251       if (status == 0)
252         gnutls->priv->have_key = TRUE;
253       else if (!gnutls->priv->construct_error)
254         {
255           gnutls->priv->construct_error =
256             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
257                          _("Could not parse PEM private key: %s"),
258                          gnutls_strerror (status));
259         }
260       break;
261
262     case PROP_ISSUER:
263       gnutls->priv->issuer = g_value_dup_object (value);
264       break;
265
266     default:
267       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
268     }
269 }
270
271 #if ENABLE(TIZEN_TV_ADJUST_TIME)
272 extern double soupTimeOffset;
273
274 static time_t
275 correct_time_func(time_t *t)
276 {
277   return time(NULL) + (time_t)(soupTimeOffset / 1000);
278 }
279 #endif
280
281 static void
282 g_tls_certificate_gnutls_init (GTlsCertificateGnutls *gnutls)
283 {
284   gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls,
285                                               G_TYPE_TLS_CERTIFICATE_GNUTLS,
286                                               GTlsCertificateGnutlsPrivate);
287
288   gnutls_x509_crt_init (&gnutls->priv->cert);
289 #if ENABLE(TIZEN_TV_ADJUST_TIME)
290   gnutls_global_set_time_function(correct_time_func);
291 #endif
292 }
293
294 static gboolean
295 g_tls_certificate_gnutls_initable_init (GInitable       *initable,
296                                         GCancellable    *cancellable,
297                                         GError         **error)
298 {
299   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (initable);
300
301   if (gnutls->priv->construct_error)
302     {
303       g_propagate_error (error, gnutls->priv->construct_error);
304       gnutls->priv->construct_error = NULL;
305       return FALSE;
306     }
307   else if (!gnutls->priv->have_cert)
308     {
309       g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
310                            _("No certificate data provided"));
311       return FALSE;
312     }
313   else
314     return TRUE;
315 }
316
317 static GTlsCertificateFlags
318 g_tls_certificate_gnutls_verify (GTlsCertificate     *cert,
319                                  GSocketConnectable  *identity,
320                                  GTlsCertificate     *trusted_ca)
321 {
322   GTlsCertificateGnutls *cert_gnutls;
323   guint num_certs, i;
324   gnutls_x509_crt_t *chain;
325   GTlsCertificateFlags gtls_flags;
326   time_t t, now;
327 #if ENABLE(TIZEN_TV_DLOG)
328   char timebuf[256];
329 #endif
330
331   
332   cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert);
333   for (num_certs = 0; cert_gnutls; cert_gnutls = cert_gnutls->priv->issuer)
334     num_certs++;
335   chain = g_new (gnutls_x509_crt_t, num_certs);
336   cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert);
337   for (i = 0; cert_gnutls; cert_gnutls = cert_gnutls->priv->issuer, i++)
338     chain[i] = cert_gnutls->priv->cert;
339
340   if (trusted_ca)
341     {
342       gnutls_x509_crt_t ca;
343       guint gnutls_flags;
344       int status;
345
346       ca = G_TLS_CERTIFICATE_GNUTLS (trusted_ca)->priv->cert;
347       status = gnutls_x509_crt_list_verify (chain, num_certs,
348                                             &ca, 1,
349                                             NULL, 0,
350                                             GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
351                                             &gnutls_flags);
352       if (status != 0)
353         {
354           g_free (chain);
355           return G_TLS_CERTIFICATE_GENERIC_ERROR;
356         }
357
358       gtls_flags = g_tls_certificate_gnutls_convert_flags (gnutls_flags);
359     }
360   else
361     gtls_flags = 0;
362
363   /* We have to check these ourselves since gnutls_x509_crt_list_verify
364    * won't bother if it gets an UNKNOWN_CA.
365    */
366   now = time (NULL);
367 #if ENABLE(TIZEN_TV_ADJUST_TIME)
368   now = time (NULL) + (time_t)(soupTimeOffset / 1000);
369 #endif
370   for (i = 0; i < num_certs; i++)
371     {
372       t = gnutls_x509_crt_get_activation_time (chain[i]);
373
374 #if ENABLE(TIZEN_TV_DLOG)
375       ctime_r(&now, timebuf);
376       TIZEN_LOGI("[Certificate] TV borad time is: %s", timebuf);
377       if (t != (time_t) -1) {
378         ctime_r(&t, timebuf);
379         TIZEN_LOGI("[Certificate] CA activation time is: %s", timebuf);
380       }
381       else
382         TIZEN_LOGI("[Certificate] gnutls_x509_crt_get_activation_time ERROR");
383 #endif
384
385       if (t == (time_t) -1 || t > now)
386         gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
387
388       t = gnutls_x509_crt_get_expiration_time (chain[i]);
389
390 #if ENABLE(TIZEN_TV_DLOG)
391       if (t != (time_t) -1) {
392         ctime_r(&t, timebuf);
393         TIZEN_LOGI("[Certificate] CA expiration time is: %s", timebuf);
394       }
395       else
396         TIZEN_LOGI("[Certificate] gnutls_x509_crt_get_expiration_time ERROR");
397 #endif
398
399       if (t == (time_t) -1 || t < now)
400         gtls_flags |= G_TLS_CERTIFICATE_EXPIRED;
401     }
402
403   g_free (chain);
404
405   if (identity)
406     gtls_flags |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (cert), identity);
407
408   return gtls_flags;
409 }
410
411 static void
412 g_tls_certificate_gnutls_real_copy (GTlsCertificateGnutls    *gnutls,
413                                     const gchar              *interaction_id,
414                                     gnutls_retr2_st          *st)
415 {
416   GTlsCertificateGnutls *chain;
417   gnutls_x509_crt_t cert;
418   gnutls_datum_t data;
419   guint num_certs = 0;
420   size_t size = 0;
421
422   /* We will do this loop twice. It's probably more efficient than
423    * re-allocating memory.
424    */
425   chain = gnutls;
426   while (chain != NULL)
427     {
428       num_certs++;
429       chain = chain->priv->issuer;
430     }
431
432   st->ncerts = 0;
433   st->cert.x509 = gnutls_malloc (sizeof (gnutls_x509_crt_t) * num_certs);
434
435 /* Now do the actual copy of the whole chain. */
436   chain = gnutls;
437   while (chain != NULL)
438     {
439       gnutls_x509_crt_export (chain->priv->cert, GNUTLS_X509_FMT_DER,
440                               NULL, &size);
441       data.data = g_malloc (size);
442       data.size = size;
443       gnutls_x509_crt_export (chain->priv->cert, GNUTLS_X509_FMT_DER,
444                               data.data, &size);
445
446       gnutls_x509_crt_init (&cert);
447       gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_DER);
448       g_free (data.data);
449
450       st->cert.x509[st->ncerts] = cert;
451       st->ncerts++;
452
453       chain = chain->priv->issuer;
454     }
455
456   if (gnutls->priv->key != NULL)
457     {
458       gnutls_x509_privkey_init (&st->key.x509);
459       gnutls_x509_privkey_cpy (st->key.x509, gnutls->priv->key);
460       st->key_type = GNUTLS_PRIVKEY_X509;
461     }
462
463   st->deinit_all = TRUE;
464 }
465
466 static void
467 g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
468 {
469   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
470   GTlsCertificateClass *certificate_class = G_TLS_CERTIFICATE_CLASS (klass);
471
472   g_type_class_add_private (klass, sizeof (GTlsCertificateGnutlsPrivate));
473
474   gobject_class->get_property = g_tls_certificate_gnutls_get_property;
475   gobject_class->set_property = g_tls_certificate_gnutls_set_property;
476   gobject_class->finalize     = g_tls_certificate_gnutls_finalize;
477
478   certificate_class->verify = g_tls_certificate_gnutls_verify;
479
480   klass->copy = g_tls_certificate_gnutls_real_copy;
481
482   g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
483   g_object_class_override_property (gobject_class, PROP_CERTIFICATE_PEM, "certificate-pem");
484   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key");
485   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem");
486   g_object_class_override_property (gobject_class, PROP_ISSUER, "issuer");
487 }
488
489 static void
490 g_tls_certificate_gnutls_initable_iface_init (GInitableIface  *iface)
491 {
492   iface->init = g_tls_certificate_gnutls_initable_init;
493 }
494
495 GTlsCertificate *
496 g_tls_certificate_gnutls_new (const gnutls_datum_t *datum,
497                               GTlsCertificate      *issuer)
498 {
499   GTlsCertificateGnutls *gnutls;
500
501   gnutls = g_object_new (G_TYPE_TLS_CERTIFICATE_GNUTLS,
502                          "issuer", issuer,
503                          NULL);
504   g_tls_certificate_gnutls_set_data (gnutls, datum);
505
506   return G_TLS_CERTIFICATE (gnutls);
507 }
508
509 void
510 g_tls_certificate_gnutls_set_data (GTlsCertificateGnutls *gnutls,
511                                    const gnutls_datum_t  *datum)
512 {
513   g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
514   g_return_if_fail (!gnutls->priv->have_cert);
515
516   if (gnutls_x509_crt_import (gnutls->priv->cert, datum,
517                               GNUTLS_X509_FMT_DER) == 0)
518     gnutls->priv->have_cert = TRUE;
519 }
520
521 const gnutls_x509_crt_t
522 g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls)
523 {
524   return gnutls->priv->cert;
525 }
526
527 gboolean
528 g_tls_certificate_gnutls_has_key (GTlsCertificateGnutls *gnutls)
529 {
530   return gnutls->priv->have_key;
531 }
532
533 void
534 g_tls_certificate_gnutls_copy  (GTlsCertificateGnutls *gnutls,
535                                 const gchar           *interaction_id,
536                                 gnutls_retr2_st       *st)
537 {
538   g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
539   g_return_if_fail (st != NULL);
540   g_return_if_fail (G_TLS_CERTIFICATE_GNUTLS_GET_CLASS (gnutls)->copy);
541   G_TLS_CERTIFICATE_GNUTLS_GET_CLASS (gnutls)->copy (gnutls, interaction_id, st);
542 }
543
544 static const struct {
545   int gnutls_flag;
546   GTlsCertificateFlags gtls_flag;
547 } flags_map[] = {
548   { GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_SIGNER_NOT_CA, G_TLS_CERTIFICATE_UNKNOWN_CA },
549   { GNUTLS_CERT_NOT_ACTIVATED, G_TLS_CERTIFICATE_NOT_ACTIVATED },
550   { GNUTLS_CERT_EXPIRED, G_TLS_CERTIFICATE_EXPIRED },
551   { GNUTLS_CERT_REVOKED, G_TLS_CERTIFICATE_REVOKED },
552   { GNUTLS_CERT_INSECURE_ALGORITHM, G_TLS_CERTIFICATE_INSECURE }
553 };
554 static const int flags_map_size = G_N_ELEMENTS (flags_map);
555
556 GTlsCertificateFlags
557 g_tls_certificate_gnutls_convert_flags (guint gnutls_flags)
558 {
559   int i;
560   GTlsCertificateFlags gtls_flags;
561
562   /* Convert GNUTLS status to GTlsCertificateFlags. GNUTLS sets
563    * GNUTLS_CERT_INVALID if it sets any other flag, so we want to
564    * strip that out unless it's the only flag set. Then we convert
565    * specific flags we recognize, and if there are any flags left over
566    * at the end, we add G_TLS_CERTIFICATE_GENERIC_ERROR.
567    */
568   gtls_flags = 0;
569
570   if (gnutls_flags != GNUTLS_CERT_INVALID)
571     gnutls_flags = gnutls_flags & ~GNUTLS_CERT_INVALID;
572   for (i = 0; i < flags_map_size && gnutls_flags != 0; i++)
573     {
574       if (gnutls_flags & flags_map[i].gnutls_flag)
575         {
576           gnutls_flags &= ~flags_map[i].gnutls_flag;
577           gtls_flags |= flags_map[i].gtls_flag;
578         }
579     }
580   if (gnutls_flags)
581     gtls_flags |= G_TLS_CERTIFICATE_GENERIC_ERROR;
582
583   return gtls_flags;
584 }
585
586 GTlsCertificateFlags
587 g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls,
588                                           GSocketConnectable    *identity)
589 {
590   const char *hostname;
591
592   if (G_IS_NETWORK_ADDRESS (identity))
593     hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity));
594   else if (G_IS_NETWORK_SERVICE (identity))
595     hostname = g_network_service_get_domain (G_NETWORK_SERVICE (identity));
596   else
597     hostname = NULL;
598
599   if (hostname)
600     {
601       if (gnutls_x509_crt_check_hostname (gnutls->priv->cert, hostname))
602         return 0;
603     }
604
605   /* FIXME: check sRVName and uniformResourceIdentifier
606    * subjectAltNames, if appropriate for @identity.
607    */
608 #if ENABLE(TIZEN_TV_DLOG)
609   TIZEN_LOGI("[Network] SSL HandShake - Bad Identity");
610 #endif
611
612   return G_TLS_CERTIFICATE_BAD_IDENTITY;
613 }
614
615 void
616 g_tls_certificate_gnutls_set_issuer (GTlsCertificateGnutls *gnutls,
617                                      GTlsCertificateGnutls *issuer)
618 {
619   g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
620   g_return_if_fail (!issuer || G_IS_TLS_CERTIFICATE_GNUTLS (issuer));
621
622   if (issuer)
623     g_object_ref (issuer);
624   if (gnutls->priv->issuer)
625     g_object_unref (gnutls->priv->issuer);
626   gnutls->priv->issuer = issuer;
627   g_object_notify (G_OBJECT (gnutls), "issuer");
628 }
629
630 GBytes *
631 g_tls_certificate_gnutls_get_bytes (GTlsCertificateGnutls *gnutls)
632 {
633   GByteArray *array;
634
635   g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls), NULL);
636
637   g_object_get (gnutls, "certificate", &array, NULL);
638   return g_byte_array_free_to_bytes (array);
639 }
640
641 static gnutls_x509_crt_t *
642 convert_data_to_gnutls_certs (const gnutls_datum_t  *certs,
643                               guint                  num_certs,
644                               gnutls_x509_crt_fmt_t  format)
645 {
646   gnutls_x509_crt_t *gnutls_certs;
647   guint i;
648
649   gnutls_certs = g_new (gnutls_x509_crt_t, num_certs);
650
651   for (i = 0; i < num_certs; i++)
652     {
653       if (gnutls_x509_crt_init (&gnutls_certs[i]) < 0)
654         {
655           i--;
656           goto error;
657         }
658     }
659
660   for (i = 0; i < num_certs; i++)
661     {
662       if (gnutls_x509_crt_import (gnutls_certs[i], &certs[i], format) < 0)
663         {
664           i = num_certs - 1;
665           goto error;
666         }
667     }
668
669   return gnutls_certs;
670
671 error:
672   for (; i != G_MAXUINT; i--)
673     gnutls_x509_crt_deinit (gnutls_certs[i]);
674   g_free (gnutls_certs);
675   return NULL;
676 }
677
678 GTlsCertificateGnutls *
679 g_tls_certificate_gnutls_build_chain (const gnutls_datum_t  *certs,
680                                       guint                  num_certs,
681                                       gnutls_x509_crt_fmt_t  format)
682 {
683   GPtrArray *glib_certs;
684   gnutls_x509_crt_t *gnutls_certs;
685   GTlsCertificateGnutls *issuer;
686   GTlsCertificateGnutls *result;
687   guint i, j;
688
689   g_return_val_if_fail (certs, NULL);
690
691   gnutls_certs = convert_data_to_gnutls_certs (certs, num_certs, format);
692   if (!gnutls_certs)
693     return NULL;
694
695   glib_certs = g_ptr_array_new_full (num_certs, g_object_unref);
696   for (i = 0; i < num_certs; i++)
697     g_ptr_array_add (glib_certs, g_tls_certificate_gnutls_new (&certs[i], NULL));
698
699   /* Some servers send certs out of order, or will send duplicate
700    * certs, so we need to be careful when assigning the issuer of
701    * our new GTlsCertificateGnutls.
702    */
703   for (i = 0; i < num_certs; i++)
704     {
705       issuer = NULL;
706
707       if (i < num_certs - 1 &&
708           gnutls_x509_crt_check_issuer (gnutls_certs[i], gnutls_certs[i + 1]))
709         {
710           issuer = glib_certs->pdata[i + 1];
711         }
712       else
713         {
714           for (j = 0; j < num_certs; j++)
715             {
716               if (j != i &&
717                   gnutls_x509_crt_check_issuer (gnutls_certs[i], gnutls_certs[j]))
718                 {
719                   issuer = glib_certs->pdata[j];
720                   break;
721                 }
722             }
723         }
724
725       if (issuer)
726         g_tls_certificate_gnutls_set_issuer (glib_certs->pdata[i], issuer);
727     }
728
729   result = g_object_ref (glib_certs->pdata[0]);
730   g_ptr_array_unref (glib_certs);
731
732   for (i = 0; i < num_certs; i++)
733     gnutls_x509_crt_deinit (gnutls_certs[i]);
734   g_free (gnutls_certs);
735
736   return result;
737 }