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