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