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