2 * empathy-tls-certificate.c - Source for EmpathyTLSCertificate
3 * Copyright (C) 2010 Collabora Ltd.
4 * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
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.
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.
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
23 #include "empathy-tls-certificate.h"
27 #include <glib/gstdio.h>
29 #include <gnutls/gnutls.h>
30 #include <gnutls/x509.h>
32 #include <telepathy-glib/util.h>
34 #define DEBUG_FLAG EMPATHY_DEBUG_TLS
35 #include "empathy-debug.h"
36 #include "empathy-utils.h"
38 #include "extensions/extensions.h"
41 /* proxy properties */
49 GSimpleAsyncResult
*async_prepare_res
;
51 /* TLSCertificate properties */
54 EmpTLSCertificateState state
;
57 } EmpathyTLSCertificatePriv
;
59 G_DEFINE_TYPE (EmpathyTLSCertificate
, empathy_tls_certificate
,
62 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTLSCertificate);
65 array_of_ay_get_type (void)
69 if (G_UNLIKELY (t
== 0))
71 t
= dbus_g_type_get_collection ("GPtrArray",
72 dbus_g_type_get_collection ("GArray",
80 tls_certificate_got_all_cb (TpProxy
*proxy
,
81 GHashTable
*properties
,
87 EmpathyTLSCertificate
*self
= EMPATHY_TLS_CERTIFICATE (weak_object
);
88 EmpathyTLSCertificatePriv
*priv
= GET_PRIV (self
);
92 g_simple_async_result_set_from_error (priv
->async_prepare_res
, error
);
93 g_simple_async_result_complete (priv
->async_prepare_res
);
94 tp_clear_object (&priv
->async_prepare_res
);
99 priv
->cert_type
= g_strdup (tp_asv_get_string (properties
,
101 priv
->state
= tp_asv_get_uint32 (properties
, "State", NULL
);
103 cert_data
= tp_asv_get_boxed (properties
, "CertificateChainData",
104 array_of_ay_get_type ());
105 g_assert (cert_data
!= NULL
);
106 priv
->cert_data
= g_boxed_copy (array_of_ay_get_type (), cert_data
);
108 DEBUG ("Got a certificate chain long %u, of type %s",
109 priv
->cert_data
->len
, priv
->cert_type
);
111 priv
->is_prepared
= TRUE
;
113 g_simple_async_result_complete (priv
->async_prepare_res
);
114 tp_clear_object (&priv
->async_prepare_res
);
118 empathy_tls_certificate_prepare_async (EmpathyTLSCertificate
*self
,
119 GAsyncReadyCallback callback
,
122 EmpathyTLSCertificatePriv
*priv
= GET_PRIV (self
);
124 /* emit an error if we're already preparing the object */
125 if (priv
->async_prepare_res
!= NULL
)
127 g_simple_async_report_error_in_idle (G_OBJECT (self
),
129 G_IO_ERROR
, G_IO_ERROR_PENDING
,
131 "Prepare operation already in progress on the TLS certificate.");
136 /* if the object is already prepared, just complete in idle */
137 if (priv
->is_prepared
)
139 tp_simple_async_report_success_in_idle (G_OBJECT (self
),
140 callback
, user_data
, empathy_tls_certificate_prepare_async
);
145 priv
->async_prepare_res
= g_simple_async_result_new (G_OBJECT (self
),
146 callback
, user_data
, empathy_tls_certificate_prepare_async
);
148 /* call GetAll() on the certificate */
149 tp_cli_dbus_properties_call_get_all (self
,
150 -1, EMP_IFACE_AUTHENTICATION_TLS_CERTIFICATE
,
151 tls_certificate_got_all_cb
, NULL
, NULL
,
156 empathy_tls_certificate_prepare_finish (EmpathyTLSCertificate
*self
,
157 GAsyncResult
*result
,
160 gboolean retval
= TRUE
;
162 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result
),
170 empathy_tls_certificate_finalize (GObject
*object
)
172 EmpathyTLSCertificatePriv
*priv
= GET_PRIV (object
);
174 DEBUG ("%p", object
);
176 g_free (priv
->cert_type
);
177 tp_clear_boxed (array_of_ay_get_type (), &priv
->cert_data
);
179 G_OBJECT_CLASS (empathy_tls_certificate_parent_class
)->finalize (object
);
183 empathy_tls_certificate_get_property (GObject
*object
,
188 EmpathyTLSCertificatePriv
*priv
= GET_PRIV (object
);
193 g_value_set_string (value
, priv
->cert_type
);
196 g_value_set_boxed (value
, priv
->cert_data
);
199 g_value_set_uint (value
, priv
->state
);
202 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
208 empathy_tls_certificate_init (EmpathyTLSCertificate
*self
)
210 self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
211 EMPATHY_TYPE_TLS_CERTIFICATE
, EmpathyTLSCertificatePriv
);
215 empathy_tls_certificate_class_init (EmpathyTLSCertificateClass
*klass
)
218 GObjectClass
*oclass
= G_OBJECT_CLASS (klass
);
219 TpProxyClass
*pclass
= TP_PROXY_CLASS (klass
);
221 oclass
->get_property
= empathy_tls_certificate_get_property
;
222 oclass
->finalize
= empathy_tls_certificate_finalize
;
224 pclass
->interface
= EMP_IFACE_QUARK_AUTHENTICATION_TLS_CERTIFICATE
;
225 pclass
->must_have_unique_name
= TRUE
;
227 g_type_class_add_private (klass
, sizeof (EmpathyTLSCertificatePriv
));
229 pspec
= g_param_spec_string ("cert-type", "Certificate type",
230 "The type of this certificate.",
232 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
233 g_object_class_install_property (oclass
, PROP_CERT_TYPE
, pspec
);
235 pspec
= g_param_spec_boxed ("cert-data", "Certificate chain data",
236 "The raw DER-encoded certificate chain data.",
237 array_of_ay_get_type (),
238 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
239 g_object_class_install_property (oclass
, PROP_CERT_DATA
, pspec
);
241 pspec
= g_param_spec_uint ("state", "State",
242 "The state of this certificate.",
243 EMP_TLS_CERTIFICATE_STATE_PENDING
, NUM_EMP_TLS_CERTIFICATE_STATES
-1,
244 EMP_TLS_CERTIFICATE_STATE_PENDING
,
245 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
246 g_object_class_install_property (oclass
, PROP_STATE
, pspec
);
250 cert_proxy_accept_cb (TpProxy
*proxy
,
253 GObject
*weak_object
)
255 GSimpleAsyncResult
*accept_result
= user_data
;
257 DEBUG ("Callback for accept(), error %p", error
);
261 DEBUG ("Error was %s", error
->message
);
262 g_simple_async_result_set_from_error (accept_result
, error
);
265 g_simple_async_result_complete (accept_result
);
269 cert_proxy_reject_cb (TpProxy
*proxy
,
272 GObject
*weak_object
)
274 GSimpleAsyncResult
*reject_result
= user_data
;
276 DEBUG ("Callback for reject(), error %p", error
);
280 DEBUG ("Error was %s", error
->message
);
281 g_simple_async_result_set_from_error (reject_result
, error
);
284 g_simple_async_result_complete (reject_result
);
288 reject_reason_get_dbus_error (EmpTLSCertificateRejectReason reason
)
290 const gchar
*retval
= NULL
;
294 case EMP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED
:
295 retval
= tp_error_get_dbus_name (TP_ERROR_CERT_UNTRUSTED
);
297 case EMP_TLS_CERTIFICATE_REJECT_REASON_EXPIRED
:
298 retval
= tp_error_get_dbus_name (TP_ERROR_CERT_EXPIRED
);
300 case EMP_TLS_CERTIFICATE_REJECT_REASON_NOT_ACTIVATED
:
301 retval
= tp_error_get_dbus_name (TP_ERROR_CERT_NOT_ACTIVATED
);
303 case EMP_TLS_CERTIFICATE_REJECT_REASON_FINGERPRINT_MISMATCH
:
304 retval
= tp_error_get_dbus_name (TP_ERROR_CERT_FINGERPRINT_MISMATCH
);
306 case EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH
:
307 retval
= tp_error_get_dbus_name (TP_ERROR_CERT_HOSTNAME_MISMATCH
);
309 case EMP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED
:
310 retval
= tp_error_get_dbus_name (TP_ERROR_CERT_SELF_SIGNED
);
312 case EMP_TLS_CERTIFICATE_REJECT_REASON_REVOKED
:
313 retval
= tp_error_get_dbus_name (TP_ERROR_CERT_REVOKED
);
315 case EMP_TLS_CERTIFICATE_REJECT_REASON_INSECURE
:
316 retval
= tp_error_get_dbus_name (TP_ERROR_CERT_INSECURE
);
318 case EMP_TLS_CERTIFICATE_REJECT_REASON_LIMIT_EXCEEDED
:
319 retval
= tp_error_get_dbus_name (TP_ERROR_CERT_LIMIT_EXCEEDED
);
321 case EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN
:
323 retval
= tp_error_get_dbus_name (TP_ERROR_CERT_INVALID
);
330 EmpathyTLSCertificate
*
331 empathy_tls_certificate_new (TpDBusDaemon
*dbus
,
332 const gchar
*bus_name
,
333 const gchar
*object_path
,
336 EmpathyTLSCertificate
*retval
= NULL
;
338 if (!tp_dbus_check_valid_bus_name (bus_name
,
339 TP_DBUS_NAME_TYPE_UNIQUE
, error
))
342 if (!tp_dbus_check_valid_object_path (object_path
, error
))
345 retval
= g_object_new (EMPATHY_TYPE_TLS_CERTIFICATE
,
347 "bus-name", bus_name
,
348 "object-path", object_path
,
353 DEBUG ("Error while creating the TLS certificate: %s",
360 empathy_tls_certificate_accept_async (EmpathyTLSCertificate
*self
,
361 GAsyncReadyCallback callback
,
364 GSimpleAsyncResult
*accept_result
;
366 g_assert (EMPATHY_IS_TLS_CERTIFICATE (self
));
368 DEBUG ("Accepting TLS certificate");
370 accept_result
= g_simple_async_result_new (G_OBJECT (self
),
371 callback
, user_data
, empathy_tls_certificate_accept_async
);
373 emp_cli_authentication_tls_certificate_call_accept (TP_PROXY (self
),
374 -1, cert_proxy_accept_cb
,
375 accept_result
, g_object_unref
,
380 empathy_tls_certificate_accept_finish (EmpathyTLSCertificate
*self
,
381 GAsyncResult
*result
,
384 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result
),
392 empathy_tls_certificate_reject_async (EmpathyTLSCertificate
*self
,
393 EmpTLSCertificateRejectReason reason
,
395 GAsyncReadyCallback callback
,
398 const gchar
*dbus_error
;
399 GSimpleAsyncResult
*reject_result
;
401 g_assert (EMPATHY_IS_TLS_CERTIFICATE (self
));
403 DEBUG ("Rejecting TLS certificate with reason %u", reason
);
405 dbus_error
= reject_reason_get_dbus_error (reason
);
406 reject_result
= g_simple_async_result_new (G_OBJECT (self
),
407 callback
, user_data
, empathy_tls_certificate_reject_async
);
409 emp_cli_authentication_tls_certificate_call_reject (TP_PROXY (self
),
410 -1, reason
, dbus_error
, details
, cert_proxy_reject_cb
,
411 reject_result
, g_object_unref
, G_OBJECT (self
));
415 empathy_tls_certificate_reject_finish (EmpathyTLSCertificate
*self
,
416 GAsyncResult
*result
,
419 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result
),
427 get_exported_size (gnutls_x509_crt_t cert
)
432 /* fake an export so we get the size to allocate */
433 gnutls_x509_crt_export (cert
, GNUTLS_X509_FMT_PEM
,
436 DEBUG ("Should allocate %lu bytes", (gulong
) retval
);
442 empathy_tls_certificate_store_ca (EmpathyTLSCertificate
*self
)
445 gnutls_x509_crt_t cert
;
446 gnutls_datum_t datum
= { NULL
, 0 };
448 guchar
*exported_cert
= NULL
;
450 gchar
*user_certs_dir
= NULL
, *filename
= NULL
, *path
= NULL
;
451 GError
*error
= NULL
;
452 EmpathyTLSCertificatePriv
*priv
= GET_PRIV (self
);
454 last_cert
= g_ptr_array_index (priv
->cert_data
, priv
->cert_data
->len
- 1);
455 datum
.data
= (guchar
*) last_cert
->data
;
456 datum
.size
= last_cert
->len
;
458 gnutls_x509_crt_init (&cert
);
459 gnutls_x509_crt_import (cert
, &datum
, GNUTLS_X509_FMT_DER
);
461 /* make sure it's self-signed, otherwise it's not a CA */
462 if (gnutls_x509_crt_check_issuer (cert
, cert
) <= 0)
464 DEBUG ("Can't import the CA, as it's not self-signed");
465 gnutls_x509_crt_deinit (cert
);
470 if (gnutls_x509_crt_get_ca_status (cert
, NULL
) <= 0)
472 DEBUG ("Can't import the CA, it's not a valid CA certificate");
473 gnutls_x509_crt_deinit (cert
);
478 exported_len
= get_exported_size (cert
);
479 exported_cert
= g_malloc (sizeof (guchar
) * exported_len
);
481 res
= gnutls_x509_crt_export (cert
, GNUTLS_X509_FMT_PEM
,
482 exported_cert
, &exported_len
);
486 DEBUG ("Failed to export the CA certificate; GnuTLS returned %d", res
);
487 gnutls_x509_crt_deinit (cert
);
492 gnutls_x509_crt_deinit (cert
);
495 user_certs_dir
= g_build_filename (g_get_user_config_dir (),
496 "telepathy", "certs", NULL
);
498 res
= g_mkdir_with_parents (user_certs_dir
, S_IRWXU
| S_IRWXG
);
502 DEBUG ("Failed to create the user certificate directory: %s",
512 filename
= g_strdup_printf ("cert-%p", cert
);
513 path
= g_build_filename (user_certs_dir
, filename
, NULL
);
517 while (g_file_test (path
, G_FILE_TEST_EXISTS
));
519 DEBUG ("Will save to %s", path
);
521 g_file_set_contents (path
, (const gchar
*) exported_cert
, exported_len
,
526 DEBUG ("Can't save the CA certificate to %s: %s",
527 path
, error
->message
);
529 g_error_free (error
);
534 g_free (exported_cert
);
535 g_free (user_certs_dir
);