Imported Upstream version 2.72.alpha
[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   PROP_PKCS11_URI,
44   PROP_PRIVATE_KEY_PKCS11_URI,
45   PROP_NOT_VALID_BEFORE,
46   PROP_NOT_VALID_AFTER,
47   PROP_SUBJECT_NAME,
48   PROP_ISSUER_NAME,
49   PROP_DNS_NAMES,
50   PROP_IP_ADDRESSES,
51 };
52
53 struct _GTlsCertificateGnutls
54 {
55   GTlsCertificate parent_instance;
56
57   gnutls_x509_crt_t cert;
58   gnutls_privkey_t key;
59
60   gchar *pkcs11_uri;
61   gchar *private_key_pkcs11_uri;
62
63   GTlsCertificateGnutls *issuer;
64
65   GError *construct_error;
66
67   guint have_cert : 1;
68   guint have_key  : 1;
69 };
70
71 static void     g_tls_certificate_gnutls_initable_iface_init (GInitableIface  *iface);
72
73 G_DEFINE_TYPE_WITH_CODE (GTlsCertificateGnutls, g_tls_certificate_gnutls, G_TYPE_TLS_CERTIFICATE,
74                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
75                                                 g_tls_certificate_gnutls_initable_iface_init);)
76
77 static void
78 g_tls_certificate_gnutls_finalize (GObject *object)
79 {
80   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
81
82   g_clear_pointer (&gnutls->cert, gnutls_x509_crt_deinit);
83   g_clear_pointer (&gnutls->key, gnutls_privkey_deinit);
84
85   g_clear_pointer (&gnutls->pkcs11_uri, g_free);
86   g_clear_pointer (&gnutls->private_key_pkcs11_uri, g_free);
87
88   g_clear_object (&gnutls->issuer);
89
90   g_clear_error (&gnutls->construct_error);
91
92   G_OBJECT_CLASS (g_tls_certificate_gnutls_parent_class)->finalize (object);
93 }
94
95 static GPtrArray *
96 get_subject_alt_names (GTlsCertificateGnutls          *cert,
97                        gnutls_x509_subject_alt_name_t  type)
98 {
99   GPtrArray *data = NULL;
100   guint8 *san = NULL;
101   size_t san_size;
102   guint san_type;
103   guint critical;
104   guint i;
105   guint status;
106
107   if (type == GNUTLS_SAN_IPADDRESS)
108     data = g_ptr_array_new_with_free_func (g_object_unref);
109   else
110     data = g_ptr_array_new_with_free_func ((GDestroyNotify)g_bytes_unref);
111
112   for (i = 0; ; i++)
113   {
114     san_size = 0;
115     san = NULL;
116     status = gnutls_x509_crt_get_subject_alt_name2 (cert->cert, i, san, &san_size, &san_type, &critical);
117     if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
118       return data;
119     else if (san_type != (guint)type)
120       continue;
121
122     if (san_size == 0)
123       continue;
124
125     san = g_malloc (san_size);
126     status = gnutls_x509_crt_get_subject_alt_name2 (cert->cert, i, san, &san_size, &san_type, &critical);
127     if (status == (guint)type)
128       {
129         if (status == (guint)GNUTLS_SAN_IPADDRESS)
130           {
131             if (san_size == 4)
132               g_ptr_array_add (data, g_inet_address_new_from_bytes (san, G_SOCKET_FAMILY_IPV4));
133             else if (san_size == 16)
134               g_ptr_array_add (data, g_inet_address_new_from_bytes (san, G_SOCKET_FAMILY_IPV6));
135           }
136         else
137           {
138             g_assert (status == (guint)GNUTLS_SAN_DNSNAME);
139             g_ptr_array_add (data, g_bytes_new (san, san_size));
140           }
141       }
142
143     g_free (san);
144   }
145
146   return data;
147 }
148
149 static void
150 export_privkey (GTlsCertificateGnutls  *gnutls,
151                 gnutls_x509_crt_fmt_t   format,
152                 void                  **output_data,
153                 size_t                 *output_size)
154 {
155   gnutls_x509_privkey_t x509_privkey = NULL;
156   int status;
157
158   if (!gnutls->key)
159     goto err;
160
161   status = gnutls_privkey_export_x509 (gnutls->key, &x509_privkey);
162   if (status != 0)
163     goto err;
164
165   *output_size = 0;
166   status = gnutls_x509_privkey_export_pkcs8 (x509_privkey,
167                                              format,
168                                              NULL, GNUTLS_PKCS_PLAIN,
169                                              NULL, output_size);
170   if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
171     goto err;
172
173   *output_data = g_malloc (*output_size);
174   status = gnutls_x509_privkey_export_pkcs8 (x509_privkey,
175                                              format,
176                                              NULL, GNUTLS_PKCS_PLAIN,
177                                              *output_data, output_size);
178   if (status == 0)
179     {
180       gnutls_x509_privkey_deinit (x509_privkey);
181       return;
182     }
183
184   g_free (*output_data);
185
186 err:
187   *output_data = NULL;
188   *output_size = 0;
189
190   if (x509_privkey)
191     gnutls_x509_privkey_deinit (x509_privkey);
192 }
193
194 static void
195 g_tls_certificate_gnutls_get_property (GObject    *object,
196                                        guint       prop_id,
197                                        GValue     *value,
198                                        GParamSpec *pspec)
199 {
200   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
201   GByteArray *byte_array;
202   char *pem;
203   guint8 *der;
204   int status;
205   size_t size;
206   gnutls_x509_dn_t dn;
207   gnutls_datum_t data;
208   time_t time;
209
210   switch (prop_id)
211     {
212     case PROP_CERTIFICATE:
213       size = 0;
214       status = gnutls_x509_crt_export (gnutls->cert,
215                                        GNUTLS_X509_FMT_DER,
216                                        NULL, &size);
217       if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
218         byte_array = NULL;
219       else
220         {
221           byte_array = g_byte_array_sized_new (size);
222           byte_array->len = size;
223           status = gnutls_x509_crt_export (gnutls->cert,
224                                            GNUTLS_X509_FMT_DER,
225                                            byte_array->data, &size);
226           if (status != 0)
227             {
228               g_byte_array_free (byte_array, TRUE);
229               byte_array = NULL;
230             }
231         }
232       g_value_take_boxed (value, byte_array);
233       break;
234
235     case PROP_CERTIFICATE_PEM:
236       size = 0;
237       status = gnutls_x509_crt_export (gnutls->cert,
238                                        GNUTLS_X509_FMT_PEM,
239                                        NULL, &size);
240       if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
241         pem = NULL;
242       else
243         {
244           pem = g_malloc (size);
245           status = gnutls_x509_crt_export (gnutls->cert,
246                                            GNUTLS_X509_FMT_PEM,
247                                            pem, &size);
248           if (status != 0)
249             g_clear_pointer (&pem, g_free);
250         }
251       g_value_take_string (value, pem);
252       break;
253
254     case PROP_PRIVATE_KEY:
255       export_privkey (gnutls, GNUTLS_X509_FMT_DER, (void **)&der, &size);
256       if (size > 0 && size <= G_MAXUINT)
257         {
258           byte_array = g_byte_array_new_take (der, size);
259           g_value_take_boxed (value, byte_array);
260         }
261       break;
262
263     case PROP_PRIVATE_KEY_PEM:
264       export_privkey (gnutls, GNUTLS_X509_FMT_PEM, (void **)&pem, &size);
265       if (size > 0)
266         g_value_take_string (value, pem);
267       break;
268
269     case PROP_ISSUER:
270       g_value_set_object (value, gnutls->issuer);
271       break;
272
273     case PROP_PKCS11_URI:
274       g_value_set_string (value, gnutls->pkcs11_uri);
275       break;
276
277     case PROP_PRIVATE_KEY_PKCS11_URI:
278       g_value_set_string (value, gnutls->private_key_pkcs11_uri);
279       break;
280
281     case PROP_NOT_VALID_BEFORE:
282       time = gnutls_x509_crt_get_activation_time (gnutls->cert);
283       if (time != (time_t)-1)
284         g_value_take_boxed (value, g_date_time_new_from_unix_utc (time));
285       break;
286
287     case PROP_NOT_VALID_AFTER:
288       time = gnutls_x509_crt_get_expiration_time (gnutls->cert);
289       if (time != (time_t)-1)
290         g_value_take_boxed (value, g_date_time_new_from_unix_utc (time));
291       break;
292
293     case PROP_SUBJECT_NAME:
294       status = gnutls_x509_crt_get_subject (gnutls->cert, &dn);
295       if (status != GNUTLS_E_SUCCESS)
296         return;
297
298       status = gnutls_x509_dn_get_str (dn, &data);
299       if (status != GNUTLS_E_SUCCESS)
300         return;
301
302       g_value_take_string (value, g_strndup ((gchar *)data.data, data.size));
303       gnutls_free (data.data);
304       break;
305
306     case PROP_ISSUER_NAME:
307       status = gnutls_x509_crt_get_issuer (gnutls->cert, &dn);
308       if (status != GNUTLS_E_SUCCESS)
309         return;
310
311       status = gnutls_x509_dn_get_str (dn, &data);
312       if (status != GNUTLS_E_SUCCESS)
313         return;
314
315       g_value_take_string (value, g_strndup ((gchar *)data.data, data.size));
316       gnutls_free (data.data);
317       break;
318
319     case PROP_DNS_NAMES:
320       g_value_take_boxed (value, get_subject_alt_names (gnutls, GNUTLS_SAN_DNSNAME));
321       break;
322
323     case PROP_IP_ADDRESSES:
324       g_value_take_boxed (value, get_subject_alt_names (gnutls, GNUTLS_SAN_IPADDRESS));
325       break;
326
327     default:
328       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
329     }
330 }
331
332 static void
333 g_tls_certificate_gnutls_set_property (GObject      *object,
334                                        guint         prop_id,
335                                        const GValue *value,
336                                        GParamSpec   *pspec)
337 {
338   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
339   GByteArray *bytes;
340   const char *string;
341   gnutls_datum_t data;
342   int status;
343
344   switch (prop_id)
345     {
346     case PROP_CERTIFICATE:
347       bytes = g_value_get_boxed (value);
348       if (!bytes)
349         break;
350       g_return_if_fail (gnutls->have_cert == FALSE);
351       data.data = bytes->data;
352       data.size = bytes->len;
353       status = gnutls_x509_crt_import (gnutls->cert, &data,
354                                        GNUTLS_X509_FMT_DER);
355       if (status == 0)
356         gnutls->have_cert = TRUE;
357       else if (!gnutls->construct_error)
358         {
359           gnutls->construct_error =
360             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
361                          _("Could not parse DER certificate: %s"),
362                          gnutls_strerror (status));
363         }
364
365       break;
366
367     case PROP_CERTIFICATE_PEM:
368       string = g_value_get_string (value);
369       if (!string)
370         break;
371       g_return_if_fail (gnutls->have_cert == FALSE);
372       data.data = (void *)string;
373       data.size = strlen (string);
374       status = gnutls_x509_crt_import (gnutls->cert, &data,
375                                        GNUTLS_X509_FMT_PEM);
376       if (status == 0)
377         gnutls->have_cert = TRUE;
378       else if (!gnutls->construct_error)
379         {
380           gnutls->construct_error =
381             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
382                          _("Could not parse PEM certificate: %s"),
383                          gnutls_strerror (status));
384         }
385       break;
386
387     case PROP_PRIVATE_KEY:
388       bytes = g_value_get_boxed (value);
389       if (!bytes)
390         break;
391       g_return_if_fail (gnutls->have_key == FALSE);
392       data.data = bytes->data;
393       data.size = bytes->len;
394       if (!gnutls->key)
395         gnutls_privkey_init (&gnutls->key);
396       status = gnutls_privkey_import_x509_raw (gnutls->key, &data,
397                                                GNUTLS_X509_FMT_DER,
398                                                NULL, GNUTLS_PKCS_PLAIN);
399       if (status == 0)
400         gnutls->have_key = TRUE;
401       else if (!gnutls->construct_error)
402         {
403           gnutls->construct_error =
404             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
405                          _("Could not parse DER private key: %s"),
406                          gnutls_strerror (status));
407         }
408       break;
409
410     case PROP_PRIVATE_KEY_PEM:
411       string = g_value_get_string (value);
412       if (!string)
413         break;
414       g_return_if_fail (gnutls->have_key == FALSE);
415       data.data = (void *)string;
416       data.size = strlen (string);
417       if (!gnutls->key)
418         gnutls_privkey_init (&gnutls->key);
419       status = gnutls_privkey_import_x509_raw (gnutls->key, &data,
420                                                GNUTLS_X509_FMT_PEM,
421                                                NULL, GNUTLS_PKCS_PLAIN);
422       if (status == 0)
423         gnutls->have_key = TRUE;
424       else if (!gnutls->construct_error)
425         {
426           gnutls->construct_error =
427             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
428                          _("Could not parse PEM private key: %s"),
429                          gnutls_strerror (status));
430         }
431       break;
432
433     case PROP_ISSUER:
434       gnutls->issuer = g_value_dup_object (value);
435       break;
436
437     case PROP_PKCS11_URI:
438       string = g_value_get_string (value);
439       if (!string)
440         break;
441       g_return_if_fail (gnutls->have_cert == FALSE);
442       g_return_if_fail (!gnutls->pkcs11_uri);
443
444       gnutls->pkcs11_uri = g_strdup (string);
445
446       status = gnutls_x509_crt_import_url (gnutls->cert, string, GNUTLS_PKCS11_OBJ_FLAG_CRT);
447       if (status == GNUTLS_E_SUCCESS)
448         {
449           gnutls->have_cert = TRUE;
450         }
451       else if (!gnutls->construct_error)
452         {
453           gnutls->construct_error =
454             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
455                           _("Could not import PKCS #11 certificate URI: %s"),
456                           gnutls_strerror (status));
457         }
458       break;
459
460     case PROP_PRIVATE_KEY_PKCS11_URI:
461       string = g_value_get_string (value);
462       if (!string)
463         break;
464       g_return_if_fail (gnutls->have_key == FALSE);
465       g_return_if_fail (!gnutls->private_key_pkcs11_uri);
466
467       gnutls->private_key_pkcs11_uri = g_strdup (string);
468       break;
469
470     default:
471       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
472     }
473 }
474
475 static void
476 g_tls_certificate_gnutls_init (GTlsCertificateGnutls *gnutls)
477 {
478   gnutls_x509_crt_init (&gnutls->cert);
479 }
480
481 static gboolean
482 g_tls_certificate_gnutls_initable_init (GInitable       *initable,
483                                         GCancellable    *cancellable,
484                                         GError         **error)
485 {
486   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (initable);
487
488   if (gnutls->construct_error)
489     {
490       g_propagate_error (error, gnutls->construct_error);
491       gnutls->construct_error = NULL;
492       return FALSE;
493     }
494   else if (!gnutls->have_cert)
495     {
496       g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
497                            _("No certificate data provided"));
498       return FALSE;
499     }
500   else
501     return TRUE;
502 }
503
504 static GTlsCertificateFlags
505 g_tls_certificate_gnutls_verify (GTlsCertificate     *cert,
506                                  GSocketConnectable  *identity,
507                                  GTlsCertificate     *trusted_ca)
508 {
509   GTlsCertificateGnutls *cert_gnutls;
510   guint num_certs, i;
511   gnutls_x509_crt_t *chain;
512   GTlsCertificateFlags gtls_flags;
513   GError *error = NULL;
514
515   cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert);
516   num_certs = 0;
517   do
518     {
519       cert_gnutls = cert_gnutls->issuer;
520       num_certs++;
521     }
522   while (cert_gnutls);
523
524   chain = g_new (gnutls_x509_crt_t, num_certs);
525   cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert);
526   for (i = 0; i < num_certs; i++)
527     {
528       chain[i] = cert_gnutls->cert;
529       cert_gnutls = cert_gnutls->issuer;
530     }
531   g_assert (!cert_gnutls);
532
533   if (trusted_ca)
534     {
535       gnutls_x509_crt_t ca;
536       guint gnutls_flags;
537       int status;
538
539       ca = G_TLS_CERTIFICATE_GNUTLS (trusted_ca)->cert;
540       status = gnutls_x509_crt_list_verify (chain, num_certs,
541                                             &ca, 1,
542                                             NULL, 0, 0,
543                                             &gnutls_flags);
544       if (status != 0)
545         {
546           g_free (chain);
547           return G_TLS_CERTIFICATE_GENERIC_ERROR;
548         }
549
550       gtls_flags = g_tls_certificate_gnutls_convert_flags (gnutls_flags);
551     }
552   else
553     gtls_flags = 0;
554
555   g_free (chain);
556
557   if (identity)
558     {
559       gtls_flags |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (cert), identity, &error);
560       if (error)
561         {
562           g_warning ("Error verifying TLS certificate: %s", error->message);
563           g_error_free (error);
564         }
565     }
566
567   return gtls_flags;
568 }
569
570 static void
571 g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
572 {
573   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
574   GTlsCertificateClass *certificate_class = G_TLS_CERTIFICATE_CLASS (klass);
575
576   gobject_class->get_property = g_tls_certificate_gnutls_get_property;
577   gobject_class->set_property = g_tls_certificate_gnutls_set_property;
578   gobject_class->finalize     = g_tls_certificate_gnutls_finalize;
579
580   certificate_class->verify = g_tls_certificate_gnutls_verify;
581
582   g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
583   g_object_class_override_property (gobject_class, PROP_CERTIFICATE_PEM, "certificate-pem");
584   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key");
585   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem");
586   g_object_class_override_property (gobject_class, PROP_ISSUER, "issuer");
587   g_object_class_override_property (gobject_class, PROP_PKCS11_URI, "pkcs11-uri");
588   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PKCS11_URI, "private-key-pkcs11-uri");
589   g_object_class_override_property (gobject_class, PROP_NOT_VALID_BEFORE, "not-valid-before");
590   g_object_class_override_property (gobject_class, PROP_NOT_VALID_AFTER, "not-valid-after");
591   g_object_class_override_property (gobject_class, PROP_SUBJECT_NAME, "subject-name");
592   g_object_class_override_property (gobject_class, PROP_ISSUER_NAME, "issuer-name");
593   g_object_class_override_property (gobject_class, PROP_DNS_NAMES, "dns-names");
594   g_object_class_override_property (gobject_class, PROP_IP_ADDRESSES, "ip-addresses");
595 }
596
597 static void
598 g_tls_certificate_gnutls_initable_iface_init (GInitableIface  *iface)
599 {
600   iface->init = g_tls_certificate_gnutls_initable_init;
601 }
602
603 GTlsCertificate *
604 g_tls_certificate_gnutls_new (const gnutls_datum_t *datum,
605                               GTlsCertificate      *issuer)
606 {
607   GTlsCertificateGnutls *gnutls;
608
609   gnutls = g_object_new (G_TYPE_TLS_CERTIFICATE_GNUTLS,
610                          "issuer", issuer,
611                          NULL);
612   g_tls_certificate_gnutls_set_data (gnutls, datum);
613
614   return G_TLS_CERTIFICATE (gnutls);
615 }
616
617 void
618 g_tls_certificate_gnutls_set_data (GTlsCertificateGnutls *gnutls,
619                                    const gnutls_datum_t  *datum)
620 {
621   g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
622   g_return_if_fail (!gnutls->have_cert);
623
624   if (gnutls_x509_crt_import (gnutls->cert, datum,
625                               GNUTLS_X509_FMT_DER) == 0)
626     gnutls->have_cert = TRUE;
627 }
628
629 const gnutls_x509_crt_t
630 g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls)
631 {
632   return gnutls->cert;
633 }
634
635 gboolean
636 g_tls_certificate_gnutls_is_pkcs11_backed (GTlsCertificateGnutls *gnutls)
637 {
638   return gnutls->pkcs11_uri != NULL;
639 }
640
641 gboolean
642 g_tls_certificate_gnutls_has_key (GTlsCertificateGnutls *gnutls)
643 {
644   return gnutls->have_key;
645 }
646
647 void
648 g_tls_certificate_gnutls_copy  (GTlsCertificateGnutls  *gnutls,
649                                 const gchar            *interaction_id,
650                                 gnutls_pcert_st       **pcert,
651                                 unsigned int           *pcert_length,
652                                 gnutls_privkey_t       *pkey)
653 {
654   GTlsCertificateGnutls *chain;
655   guint num_certs = 0;
656   int status;
657
658   g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
659   g_return_if_fail (pcert);
660   g_return_if_fail (pcert_length);
661   g_return_if_fail (pkey);
662
663   /* We will do this loop twice. It's probably more efficient than
664    * re-allocating memory.
665    */
666   chain = gnutls;
667   while (chain)
668     {
669       num_certs++;
670       chain = chain->issuer;
671     }
672
673   *pcert_length = 0;
674   *pcert = g_malloc (sizeof (gnutls_pcert_st) * num_certs);
675
676   /* Now do the actual copy of the whole chain. */
677   chain = gnutls;
678   while (chain)
679     {
680       gnutls_x509_crt_t cert;
681       gnutls_datum_t data;
682
683       gnutls_x509_crt_export2 (chain->cert, GNUTLS_X509_FMT_DER, &data);
684
685       gnutls_x509_crt_init (&cert);
686       status = gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_DER);
687       g_warn_if_fail (status == 0);
688       gnutls_free (data.data);
689
690       gnutls_pcert_import_x509 (*pcert + *pcert_length, cert, 0);
691       gnutls_x509_crt_deinit (cert);
692       (*pcert_length)++;
693
694       chain = chain->issuer;
695     }
696
697   if (gnutls->key)
698     {
699       gnutls_x509_privkey_t x509_privkey;
700
701       gnutls_privkey_export_x509 (gnutls->key, &x509_privkey);
702       gnutls_privkey_import_x509 (*pkey, x509_privkey, GNUTLS_PRIVKEY_IMPORT_COPY);
703       gnutls_x509_privkey_deinit (x509_privkey);
704     }
705   else if (gnutls->private_key_pkcs11_uri || gnutls->pkcs11_uri)
706     {
707       int status;
708
709       status = gnutls_privkey_import_pkcs11_url (*pkey,
710                                                  gnutls->private_key_pkcs11_uri ? gnutls->private_key_pkcs11_uri : gnutls->pkcs11_uri);
711       if (status != GNUTLS_E_SUCCESS)
712         {
713           gnutls_privkey_deinit (*pkey);
714           *pkey = NULL;
715           g_info ("Failed to copy PKCS #11 private key: %s", gnutls_strerror (status));
716         }
717     }
718   else
719     {
720       gnutls_privkey_deinit (*pkey);
721       *pkey = NULL;
722     }
723 }
724
725 void
726 g_tls_certificate_gnutls_copy_free (gnutls_pcert_st  *pcert,
727                                     unsigned int      pcert_length,
728                                     gnutls_privkey_t  pkey)
729 {
730   if (pcert)
731     {
732       for (unsigned int i = 0; i < pcert_length; i++)
733         gnutls_pcert_deinit (&pcert[i]);
734       g_free (pcert);
735     }
736
737   if (pkey)
738     gnutls_privkey_deinit (pkey);
739 }
740
741 static const struct {
742   int gnutls_flag;
743   GTlsCertificateFlags gtls_flag;
744 } flags_map[] = {
745   { GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_SIGNER_NOT_CA, G_TLS_CERTIFICATE_UNKNOWN_CA },
746   { GNUTLS_CERT_NOT_ACTIVATED, G_TLS_CERTIFICATE_NOT_ACTIVATED },
747   { GNUTLS_CERT_EXPIRED, G_TLS_CERTIFICATE_EXPIRED },
748   { GNUTLS_CERT_REVOKED, G_TLS_CERTIFICATE_REVOKED },
749   { GNUTLS_CERT_INSECURE_ALGORITHM, G_TLS_CERTIFICATE_INSECURE },
750   { GNUTLS_CERT_UNEXPECTED_OWNER, G_TLS_CERTIFICATE_BAD_IDENTITY }
751 };
752 static const int flags_map_size = G_N_ELEMENTS (flags_map);
753
754 GTlsCertificateFlags
755 g_tls_certificate_gnutls_convert_flags (guint gnutls_flags)
756 {
757   int i;
758   GTlsCertificateFlags gtls_flags;
759
760   /* Convert GNUTLS status to GTlsCertificateFlags. GNUTLS sets
761    * GNUTLS_CERT_INVALID if it sets any other flag, so we want to
762    * strip that out unless it's the only flag set. Then we convert
763    * specific flags we recognize, and if there are any flags left over
764    * at the end, we add G_TLS_CERTIFICATE_GENERIC_ERROR.
765    */
766   gtls_flags = 0;
767
768   if (gnutls_flags != GNUTLS_CERT_INVALID)
769     gnutls_flags = gnutls_flags & ~GNUTLS_CERT_INVALID;
770   for (i = 0; i < flags_map_size && gnutls_flags != 0; i++)
771     {
772       if (gnutls_flags & flags_map[i].gnutls_flag)
773         {
774           gnutls_flags &= ~flags_map[i].gnutls_flag;
775           gtls_flags |= flags_map[i].gtls_flag;
776         }
777     }
778   if (gnutls_flags)
779     gtls_flags |= G_TLS_CERTIFICATE_GENERIC_ERROR;
780
781   return gtls_flags;
782 }
783
784 GTlsCertificateFlags
785 g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls  *gnutls,
786                                           GSocketConnectable     *identity,
787                                           GError                **error)
788 {
789   GTlsCertificateFlags result = 0;
790   const char *hostname;
791   char *free_hostname = NULL;
792
793   if (G_IS_NETWORK_ADDRESS (identity))
794     hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity));
795   else if (G_IS_NETWORK_SERVICE (identity))
796     hostname = g_network_service_get_domain (G_NETWORK_SERVICE (identity));
797   else if (G_IS_INET_SOCKET_ADDRESS (identity))
798     {
799       GInetAddress *addr;
800
801       addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (identity));
802       hostname = free_hostname = g_inet_address_to_string (addr);
803     }
804   else
805     {
806       g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
807                    _("Cannot verify peer identity of unexpected type %s"), G_OBJECT_TYPE_NAME (identity));
808       return G_TLS_CERTIFICATE_BAD_IDENTITY;
809     }
810
811   g_assert (hostname);
812   if (!gnutls_x509_crt_check_hostname (gnutls->cert, hostname))
813     result |= G_TLS_CERTIFICATE_BAD_IDENTITY;
814
815   g_free (free_hostname);
816
817   return result;
818 }
819
820 void
821 g_tls_certificate_gnutls_set_issuer (GTlsCertificateGnutls *gnutls,
822                                      GTlsCertificateGnutls *issuer)
823 {
824   g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
825   g_return_if_fail (!issuer || G_IS_TLS_CERTIFICATE_GNUTLS (issuer));
826
827   if (issuer)
828     g_object_ref (issuer);
829   if (gnutls->issuer)
830     g_object_unref (gnutls->issuer);
831   gnutls->issuer = issuer;
832   g_object_notify (G_OBJECT (gnutls), "issuer");
833 }
834
835 GBytes *
836 g_tls_certificate_gnutls_get_bytes (GTlsCertificateGnutls *gnutls)
837 {
838   GByteArray *array;
839
840   g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls), NULL);
841
842   g_object_get (gnutls, "certificate", &array, NULL);
843   return g_byte_array_free_to_bytes (array);
844 }
845
846 static gnutls_x509_crt_t *
847 convert_data_to_gnutls_certs (const gnutls_datum_t  *certs,
848                               guint                  num_certs,
849                               gnutls_x509_crt_fmt_t  format)
850 {
851   gnutls_x509_crt_t *gnutls_certs;
852   guint i;
853
854   gnutls_certs = g_new (gnutls_x509_crt_t, num_certs);
855
856   for (i = 0; i < num_certs; i++)
857     {
858       if (gnutls_x509_crt_init (&gnutls_certs[i]) < 0)
859         {
860           i--;
861           goto error;
862         }
863     }
864
865   for (i = 0; i < num_certs; i++)
866     {
867       if (gnutls_x509_crt_import (gnutls_certs[i], &certs[i], format) < 0)
868         {
869           i = num_certs - 1;
870           goto error;
871         }
872     }
873
874   return gnutls_certs;
875
876 error:
877   for (; i != G_MAXUINT; i--)
878     gnutls_x509_crt_deinit (gnutls_certs[i]);
879   g_free (gnutls_certs);
880   return NULL;
881 }
882
883 GTlsCertificateGnutls *
884 g_tls_certificate_gnutls_build_chain (const gnutls_datum_t  *certs,
885                                       guint                  num_certs,
886                                       gnutls_x509_crt_fmt_t  format)
887 {
888   GPtrArray *glib_certs;
889   gnutls_x509_crt_t *gnutls_certs;
890   GTlsCertificateGnutls *issuer;
891   GTlsCertificateGnutls *result;
892   guint i, j;
893
894   g_return_val_if_fail (certs, NULL);
895
896   gnutls_certs = convert_data_to_gnutls_certs (certs, num_certs, format);
897   if (!gnutls_certs)
898     return NULL;
899
900   glib_certs = g_ptr_array_new_full (num_certs, g_object_unref);
901   for (i = 0; i < num_certs; i++)
902     g_ptr_array_add (glib_certs, g_tls_certificate_gnutls_new (&certs[i], NULL));
903
904   /* Some servers send certs out of order, or will send duplicate
905    * certs, so we need to be careful when assigning the issuer of
906    * our new GTlsCertificateGnutls.
907    */
908   for (i = 0; i < num_certs; i++)
909     {
910       issuer = NULL;
911
912       /* Check if the cert issued itself */
913       if (gnutls_x509_crt_check_issuer (gnutls_certs[i], gnutls_certs[i]))
914         continue;
915
916       if (i < num_certs - 1 &&
917           gnutls_x509_crt_check_issuer (gnutls_certs[i], gnutls_certs[i + 1]))
918         {
919           issuer = glib_certs->pdata[i + 1];
920         }
921       else
922         {
923           for (j = 0; j < num_certs; j++)
924             {
925               if (j != i &&
926                   gnutls_x509_crt_check_issuer (gnutls_certs[i], gnutls_certs[j]))
927                 {
928                   issuer = glib_certs->pdata[j];
929                   break;
930                 }
931             }
932         }
933
934       if (issuer)
935         g_tls_certificate_gnutls_set_issuer (glib_certs->pdata[i], issuer);
936     }
937
938   result = g_object_ref (glib_certs->pdata[0]);
939   g_ptr_array_unref (glib_certs);
940
941   for (i = 0; i < num_certs; i++)
942     gnutls_x509_crt_deinit (gnutls_certs[i]);
943   g_free (gnutls_certs);
944
945   return result;
946 }