]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tls-verifier.c
Use tp_clear_pointer() where possible
[empathy.git] / libempathy / empathy-tls-verifier.c
1 /*
2  * empathy-tls-verifier.c - Source for EmpathyTLSVerifier
3  * Copyright (C) 2010 Collabora Ltd.
4  * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * Some snippets are taken from GnuTLS 2.8.6, which is distributed under the
21  * same GNU Lesser General Public License 2.1 (or later) version. See
22  * get_certified_hostname ().
23  */
24
25 #include <config.h>
26
27 #include <gnutls/gnutls.h>
28 #include <gnutls/x509.h>
29
30 #include <telepathy-glib/util.h>
31
32 #include "empathy-tls-verifier.h"
33
34 #define DEBUG_FLAG EMPATHY_DEBUG_TLS
35 #include "empathy-debug.h"
36 #include "empathy-utils.h"
37
38 G_DEFINE_TYPE (EmpathyTLSVerifier, empathy_tls_verifier,
39     G_TYPE_OBJECT)
40
41 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTLSVerifier);
42
43 enum {
44   PROP_TLS_CERTIFICATE = 1,
45   PROP_HOSTNAME,
46
47   LAST_PROPERTY,
48 };
49
50 static const gchar* system_ca_paths[] = {
51   "/etc/ssl/certs/ca-certificates.crt",
52   NULL,
53 };
54
55 typedef struct {
56   GPtrArray *cert_chain;
57
58   GPtrArray *trusted_ca_list;
59   GPtrArray *trusted_crl_list;
60
61   EmpathyTLSCertificate *certificate;
62   gchar *hostname;
63
64   GSimpleAsyncResult *verify_result;
65   GHashTable *details;
66
67   gboolean dispose_run;
68 } EmpathyTLSVerifierPriv;
69
70 static gnutls_x509_crt_t *
71 ptr_array_to_x509_crt_list (GPtrArray *chain)
72 {
73   gnutls_x509_crt_t *retval;
74   gint idx;
75
76   retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * chain->len);
77
78   for (idx = 0; idx < (gint) chain->len; idx++)
79     retval[idx] = g_ptr_array_index (chain, idx);
80
81   return retval;
82 }
83
84 static gboolean
85 verification_output_to_reason (gint res,
86     guint verify_output,
87     EmpTLSCertificateRejectReason *reason)
88 {
89   gboolean retval = TRUE;
90
91   g_assert (reason != NULL);
92
93   if (res != GNUTLS_E_SUCCESS)
94     {
95       retval = FALSE;
96
97       /* the certificate is not structurally valid */
98       switch (res)
99         {
100         case GNUTLS_E_INSUFFICIENT_CREDENTIALS:
101           *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED;
102           break;
103         case GNUTLS_E_CONSTRAINT_ERROR:
104           *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_LIMIT_EXCEEDED;
105           break;
106         default:
107           *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
108           break;
109         }
110
111       goto out;
112     }
113
114   /* the certificate is structurally valid, check for other errors. */
115   if (verify_output & GNUTLS_CERT_INVALID)
116     {
117       retval = FALSE;
118
119       if (verify_output & GNUTLS_CERT_SIGNER_NOT_FOUND)
120         *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED;
121       else if (verify_output & GNUTLS_CERT_SIGNER_NOT_CA)
122         *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED;
123       else if (verify_output & GNUTLS_CERT_INSECURE_ALGORITHM)
124         *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_INSECURE;
125       else if (verify_output & GNUTLS_CERT_NOT_ACTIVATED)
126         *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_NOT_ACTIVATED;
127       else if (verify_output & GNUTLS_CERT_EXPIRED)
128         *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_EXPIRED;
129       else
130         *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
131
132       goto out;
133     }
134
135  out:
136   return retval;
137 }
138
139 static gboolean
140 verify_last_certificate (EmpathyTLSVerifier *self,
141     gnutls_x509_crt_t cert,
142     EmpTLSCertificateRejectReason *reason)
143 {
144   guint verify_output;
145   gint res;
146   gnutls_x509_crt_t *trusted_ca_list;
147   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
148
149   if (priv->trusted_ca_list->len > 0)
150     {
151       trusted_ca_list = ptr_array_to_x509_crt_list (priv->trusted_ca_list);
152       res = gnutls_x509_crt_verify (cert, trusted_ca_list,
153           priv->trusted_ca_list->len, 0, &verify_output);
154
155       DEBUG ("Checking last certificate %p against trusted CAs, output %u",
156           cert, verify_output);
157
158       g_free (trusted_ca_list);
159     }
160   else
161     {
162       /* check it against itself to see if it's structurally valid */
163       res = gnutls_x509_crt_verify (cert, &cert, 1, 0, &verify_output);
164
165       DEBUG ("Checking last certificate %p against itself, output %u", cert,
166           verify_output);
167
168       /* if it's valid, return the SelfSigned error, so that we can add it
169        * later to our trusted CAs whitelist.
170        */
171       if (res == GNUTLS_E_SUCCESS)
172         {
173           *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED;
174           return FALSE;
175         }
176     }
177
178   return verification_output_to_reason (res, verify_output, reason);
179 }
180
181 static gboolean
182 verify_certificate (EmpathyTLSVerifier *self,
183     gnutls_x509_crt_t cert,
184     gnutls_x509_crt_t issuer,
185     EmpTLSCertificateRejectReason *reason)
186 {
187   guint verify_output;
188   gint res;
189
190   res = gnutls_x509_crt_verify (cert, &issuer, 1, 0, &verify_output);
191
192   DEBUG ("Verifying %p against %p, output %u", cert, issuer, verify_output);
193
194   return verification_output_to_reason (res, verify_output, reason);
195 }
196
197 static void
198 complete_verification (EmpathyTLSVerifier *self)
199 {
200   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
201
202   DEBUG ("Verification successful, completing...");
203
204   g_simple_async_result_complete_in_idle (priv->verify_result);
205
206   tp_clear_object (&priv->verify_result);  
207 }
208
209 static void
210 abort_verification (EmpathyTLSVerifier *self,
211     EmpTLSCertificateRejectReason reason)
212 {
213   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
214
215   DEBUG ("Verification error %u, aborting...", reason);
216
217   g_simple_async_result_set_error (priv->verify_result,
218       G_IO_ERROR, reason, "TLS verification failed with reason %u",
219       reason);
220   g_simple_async_result_complete_in_idle (priv->verify_result);
221
222   tp_clear_object (&priv->verify_result);
223 }
224
225 static gchar *
226 get_certified_hostname (gnutls_x509_crt_t cert)
227 {
228   gchar dns_name[256];
229   gsize dns_name_size;
230   gint idx;
231   gint res = 0;
232
233   /* this snippet is taken from GnuTLS.
234    * see gnutls/lib/x509/rfc2818_hostname.c
235    */
236   for (idx = 0; res >= 0; idx++)
237     {
238       dns_name_size = sizeof (dns_name);
239       res = gnutls_x509_crt_get_subject_alt_name (cert, idx,
240           dns_name, &dns_name_size, NULL);
241
242       if (res == GNUTLS_SAN_DNSNAME || res == GNUTLS_SAN_IPADDRESS)
243         return g_strndup (dns_name, dns_name_size);
244     }
245
246   dns_name_size = sizeof (dns_name);
247   res = gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME,
248       0, 0, dns_name, &dns_name_size);
249
250   if (res >= 0)
251     return g_strndup (dns_name, dns_name_size);
252
253   return NULL;
254 }
255
256 static void
257 real_start_verification (EmpathyTLSVerifier *self)
258 {
259   gnutls_x509_crt_t first_cert, last_cert;
260   gint idx;
261   gboolean res = FALSE;
262   gint num_certs;
263   EmpTLSCertificateRejectReason reason =
264     EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
265   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
266
267   DEBUG ("Starting verification");
268
269   /* check if the certificate matches the hostname first. */
270   first_cert = g_ptr_array_index (priv->cert_chain, 0);
271   if (gnutls_x509_crt_check_hostname (first_cert, priv->hostname) == 0)
272     {
273       gchar *certified_hostname;
274
275       reason = EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH;
276       certified_hostname = get_certified_hostname (first_cert);
277       tp_asv_set_string (priv->details,
278           "expected-hostname", priv->hostname);
279       tp_asv_set_string (priv->details,
280           "certificate-hostname", certified_hostname);
281
282       DEBUG ("Hostname mismatch: got %s but expected %s",
283           certified_hostname, priv->hostname);
284
285       g_free (certified_hostname);
286       goto out;
287     }
288
289   DEBUG ("Hostname matched");
290
291   num_certs = priv->cert_chain->len;
292
293   if (priv->trusted_ca_list->len > 0)
294     {
295       /* if the last certificate is self-signed, and we have a list of
296        * trusted CAs, ignore it, as we want to check the chain against our
297        * trusted CAs list first.
298        */
299       last_cert = g_ptr_array_index (priv->cert_chain, num_certs - 1);
300
301       if (gnutls_x509_crt_check_issuer (last_cert, last_cert) > 0)
302         num_certs--;
303     }
304
305   for (idx = 1; idx < num_certs; idx++)
306     {
307       res = verify_certificate (self,
308           g_ptr_array_index (priv->cert_chain, idx -1),
309           g_ptr_array_index (priv->cert_chain, idx),
310           &reason);
311
312       DEBUG ("Certificate verification %d gave result %d with reason %u", idx,
313           res, reason);
314
315       if (!res)
316         {
317           abort_verification (self, reason);
318           return;
319         }
320     }
321
322   res = verify_last_certificate (self,
323       g_ptr_array_index (priv->cert_chain, num_certs - 1),
324       &reason);
325
326   DEBUG ("Last verification gave result %d with reason %u", res, reason);
327
328  out:
329   if (!res)
330     {
331       abort_verification (self, reason);
332       return;
333     }
334
335   complete_verification (self);
336 }
337
338 static gboolean
339 start_verification (gpointer user_data)
340 {
341   EmpathyTLSVerifier *self = user_data;
342
343   real_start_verification (self);
344
345   return FALSE;
346 }
347
348 static void
349 build_gnutls_cert_list (EmpathyTLSVerifier *self)
350 {
351   guint num_certs;
352   guint idx;
353   GPtrArray *certificate_data = NULL;
354   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
355
356   g_object_get (priv->certificate,
357       "cert-data", &certificate_data,
358       NULL);
359   num_certs = certificate_data->len;
360
361   priv->cert_chain = g_ptr_array_new_with_free_func (
362       (GDestroyNotify) gnutls_x509_crt_deinit);
363
364   for (idx = 0; idx < num_certs; idx++)
365     {
366       gnutls_x509_crt_t cert;
367       GArray *one_cert;
368       gnutls_datum_t datum = { NULL, 0 };
369
370       one_cert = g_ptr_array_index (certificate_data, idx);
371       datum.data = (guchar *) one_cert->data;
372       datum.size = one_cert->len;
373
374       gnutls_x509_crt_init (&cert);
375       gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_DER);
376
377       g_ptr_array_add (priv->cert_chain, cert);
378     }
379 }
380
381 static gint
382 get_number_and_type_of_certificates (gnutls_datum_t *datum,
383     gnutls_x509_crt_fmt_t *format)
384 {
385   gnutls_x509_crt_t fake;
386   guint retval = 1;
387   gint res;
388
389   res = gnutls_x509_crt_list_import (&fake, &retval, datum,
390       GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
391
392   if (res == GNUTLS_E_SHORT_MEMORY_BUFFER || res > 0)
393     {
394       DEBUG ("Found PEM, with %u certificates", retval);
395       *format = GNUTLS_X509_FMT_PEM;
396       return retval;
397     }
398
399   /* try DER */
400   res = gnutls_x509_crt_list_import (&fake, &retval, datum,
401       GNUTLS_X509_FMT_DER, 0);
402
403   if (res > 0)
404     {
405       *format = GNUTLS_X509_FMT_DER;
406       return retval;
407     }
408
409   return res;
410 }
411
412 static gboolean
413 build_gnutls_ca_and_crl_lists (GIOSchedulerJob *job,
414     GCancellable *cancellable,
415     gpointer user_data)
416 {
417   gint idx;
418   gchar *user_certs_dir;
419   GDir *dir;
420   GError *error = NULL;
421   EmpathyTLSVerifier *self = user_data;
422   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
423
424   priv->trusted_ca_list = g_ptr_array_new_with_free_func
425     ((GDestroyNotify) gnutls_x509_crt_deinit);
426
427   for (idx = 0; idx < (gint) G_N_ELEMENTS (system_ca_paths) - 1; idx++)
428     {
429       const gchar *path;
430       gchar *contents = NULL;
431       gsize length = 0;
432       gint res, n_certs;
433       gnutls_x509_crt_t *cert_list;
434       gnutls_datum_t datum = { NULL, 0 };
435       gnutls_x509_crt_fmt_t format = 0;
436
437       path = system_ca_paths[idx];
438       g_file_get_contents (path, &contents, &length, &error);
439
440       if (error != NULL)
441         {
442           DEBUG ("Unable to read system CAs from path %s: %s", path,
443               error->message);
444           g_clear_error (&error);
445           continue;
446         }
447
448       datum.data = (guchar *) contents;
449       datum.size = length;
450       n_certs = get_number_and_type_of_certificates (&datum, &format);
451
452       if (n_certs < 0)
453         {
454           DEBUG ("Unable to parse the system CAs from path %s: GnuTLS "
455               "returned error %d", path, n_certs);
456
457           g_free (contents);
458           continue;
459         }
460
461       cert_list = g_malloc0 (sizeof (gnutls_x509_crt_t) * n_certs);
462       res = gnutls_x509_crt_list_import (cert_list, (guint *) &n_certs, &datum,
463           format, 0);
464
465       if (res < 0)
466         {
467           DEBUG ("Unable to import system CAs from path %s; "
468               "GnuTLS returned error %d", path, res);
469
470           g_free (contents);
471           continue;
472         }
473
474       DEBUG ("Successfully imported %d system CA certificates from path %s",
475           n_certs, path);
476
477       /* append the newly created cert structutes into the global GPtrArray */
478       for (idx = 0; idx < n_certs; idx++)
479         g_ptr_array_add (priv->trusted_ca_list, cert_list[idx]);
480
481       g_free (contents);
482       g_free (cert_list);
483     }
484
485   /* user certs */
486   user_certs_dir = g_build_filename (g_get_user_config_dir (),
487       "telepathy", "certs", NULL);
488   dir = g_dir_open (user_certs_dir, 0, &error);
489
490   if (error != NULL)
491     {
492       DEBUG ("Can't open the user certs dir at %s: %s", user_certs_dir,
493           error->message);
494
495       g_error_free (error);
496     }
497   else
498     {
499       const gchar *cert_name;
500
501       while ((cert_name = g_dir_read_name (dir)) != NULL)
502         {
503           gchar *contents = NULL, *cert_path = NULL;
504           gsize length = 0;
505           gint res;
506           gnutls_datum_t datum = { NULL, 0 };
507           gnutls_x509_crt_t cert;
508
509           cert_path = g_build_filename (user_certs_dir, cert_name, NULL);
510
511           g_file_get_contents (cert_path, &contents, &length, &error);
512
513           if (error != NULL)
514             {
515               DEBUG ("Can't open the certificate file at path %s: %s",
516                   cert_path, error->message);
517
518               g_clear_error (&error);
519               g_free (cert_path);
520               continue;
521             }
522
523           datum.data = (guchar *) contents;
524           datum.size = length;
525
526           gnutls_x509_crt_init (&cert);
527           res = gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_PEM);
528
529           if (res != GNUTLS_E_SUCCESS)
530             {
531               DEBUG ("Can't import the certificate at path %s: "
532                   "GnuTLS returned %d", cert_path, res);
533             }
534           else
535             {
536               g_ptr_array_add (priv->trusted_ca_list, cert);
537             }
538
539           g_free (contents);
540           g_free (cert_path);
541         }
542
543       g_dir_close (dir);
544     }
545
546   g_free (user_certs_dir);
547
548   /* TODO: do the CRL too */
549
550   g_io_scheduler_job_send_to_mainloop_async (job,
551       start_verification, self, NULL);
552
553   return FALSE;
554 }
555
556 static void
557 empathy_tls_verifier_get_property (GObject *object,
558     guint property_id,
559     GValue *value,
560     GParamSpec *pspec)
561 {
562   EmpathyTLSVerifierPriv *priv = GET_PRIV (object);
563
564   switch (property_id)
565     {
566     case PROP_TLS_CERTIFICATE:
567       g_value_set_object (value, priv->certificate);
568       break;
569     case PROP_HOSTNAME:
570       g_value_set_string (value, priv->hostname);
571       break;
572     default:
573       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
574       break;
575     }
576 }
577
578 static void
579 empathy_tls_verifier_set_property (GObject *object,
580     guint property_id,
581     const GValue *value,
582     GParamSpec *pspec)
583 {
584   EmpathyTLSVerifierPriv *priv = GET_PRIV (object);
585
586   switch (property_id)
587     {
588     case PROP_TLS_CERTIFICATE:
589       priv->certificate = g_value_dup_object (value);
590       break;
591     case PROP_HOSTNAME:
592       priv->hostname = g_value_dup_string (value);
593       break;
594     default:
595       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
596       break;
597     }
598 }
599
600 static void
601 empathy_tls_verifier_dispose (GObject *object)
602 {
603   EmpathyTLSVerifierPriv *priv = GET_PRIV (object);
604
605   if (priv->dispose_run)
606     return;
607
608   priv->dispose_run = TRUE;
609
610   tp_clear_object (&priv->certificate);
611
612   G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->dispose (object);
613 }
614
615 static void
616 empathy_tls_verifier_finalize (GObject *object)
617 {
618   EmpathyTLSVerifierPriv *priv = GET_PRIV (object);
619
620   DEBUG ("%p", object);
621
622   tp_clear_pointer (&priv->trusted_ca_list, g_ptr_array_unref);
623   tp_clear_pointer (&priv->cert_chain, g_ptr_array_unref);
624   tp_clear_boxed (G_TYPE_HASH_TABLE, &priv->details);
625   g_free (priv->hostname);
626
627   G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->finalize (object);
628 }
629
630 static void
631 empathy_tls_verifier_constructed (GObject *object)
632 {
633   EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (object);
634
635   build_gnutls_cert_list (self);
636   
637   if (G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->constructed != NULL)
638     G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->constructed (object);
639 }
640
641 static void
642 empathy_tls_verifier_init (EmpathyTLSVerifier *self)
643 {
644   EmpathyTLSVerifierPriv *priv;
645
646   priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
647       EMPATHY_TYPE_TLS_VERIFIER, EmpathyTLSVerifierPriv);
648   priv->details = tp_asv_new (NULL, NULL);
649 }
650
651 static void
652 empathy_tls_verifier_class_init (EmpathyTLSVerifierClass *klass)
653 {
654   GParamSpec *pspec;
655   GObjectClass *oclass = G_OBJECT_CLASS (klass);
656
657   g_type_class_add_private (klass, sizeof (EmpathyTLSVerifierPriv));
658
659   oclass->set_property = empathy_tls_verifier_set_property;
660   oclass->get_property = empathy_tls_verifier_get_property;
661   oclass->finalize = empathy_tls_verifier_finalize;
662   oclass->dispose = empathy_tls_verifier_dispose;
663   oclass->constructed = empathy_tls_verifier_constructed;
664
665   pspec = g_param_spec_object ("certificate", "The EmpathyTLSCertificate",
666       "The EmpathyTLSCertificate to be verified.",
667       EMPATHY_TYPE_TLS_CERTIFICATE,
668       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
669   g_object_class_install_property (oclass, PROP_TLS_CERTIFICATE, pspec);
670
671   pspec = g_param_spec_string ("hostname", "The hostname",
672       "The hostname which should be certified by the certificate.",
673       NULL,
674       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
675   g_object_class_install_property (oclass, PROP_HOSTNAME, pspec);
676 }
677
678 EmpathyTLSVerifier *
679 empathy_tls_verifier_new (EmpathyTLSCertificate *certificate,
680     const gchar *hostname)
681 {
682   g_assert (EMPATHY_IS_TLS_CERTIFICATE (certificate));
683   g_assert (hostname != NULL);
684
685   return g_object_new (EMPATHY_TYPE_TLS_VERIFIER,
686       "certificate", certificate,
687       "hostname", hostname,
688       NULL);
689 }
690
691 void
692 empathy_tls_verifier_verify_async (EmpathyTLSVerifier *self,
693     GAsyncReadyCallback callback,
694     gpointer user_data)
695 {
696   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
697
698   priv->verify_result = g_simple_async_result_new (G_OBJECT (self),
699       callback, user_data, NULL);
700
701   g_io_scheduler_push_job (build_gnutls_ca_and_crl_lists,
702       self, NULL, G_PRIORITY_DEFAULT, NULL);
703 }
704
705 gboolean
706 empathy_tls_verifier_verify_finish (EmpathyTLSVerifier *self,
707     GAsyncResult *res,
708     EmpTLSCertificateRejectReason *reason,
709     GHashTable **details,
710     GError **error)
711 {
712   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
713
714   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
715           error))
716     {
717       if (reason != NULL)
718         *reason = (*error)->code;
719
720       if (details != NULL)
721         {
722           *details = tp_asv_new (NULL, NULL);
723           tp_g_hash_table_update (*details, priv->details,
724               (GBoxedCopyFunc) g_strdup,
725               (GBoxedCopyFunc) tp_g_value_slice_dup);
726         }
727
728       return FALSE;
729     }
730
731   if (reason != NULL)
732     *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
733
734   return TRUE;
735 }