2 * empathy-tls-verifier.c - Source for EmpathyTLSVerifier
3 * Copyright (C) 2010 Collabora Ltd.
4 * Copyright (C) 2017 Red Hat, Inc.
5 * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
6 * @author Debarshi Ray <debarshir@gnome.org>
7 * @author Stef Walter <stefw@collabora.co.uk>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "empathy-tls-verifier.h"
29 #include "empathy-utils.h"
31 #define DEBUG_FLAG EMPATHY_DEBUG_TLS
32 #include "empathy-debug.h"
34 G_DEFINE_TYPE (EmpathyTLSVerifier
, empathy_tls_verifier
,
37 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTLSVerifier);
40 PROP_TLS_CERTIFICATE
= 1,
42 PROP_REFERENCE_IDENTITIES
,
48 GTlsCertificate
*g_certificate
;
49 GTlsDatabase
*database
;
50 TpTLSCertificate
*certificate
;
52 gchar
**reference_identities
;
54 GSimpleAsyncResult
*verify_result
;
58 } EmpathyTLSVerifierPriv
;
60 static GTlsCertificate
*
61 tls_certificate_new_from_der (GPtrArray
*data
, GError
**error
)
63 GTlsBackend
*tls_backend
;
64 GTlsCertificate
*cert
= NULL
;
65 GTlsCertificate
*issuer
= NULL
;
66 GTlsCertificate
*retval
= NULL
;
67 GType tls_certificate_type
;
70 g_return_val_if_fail (error
== NULL
|| *error
== NULL
, NULL
);
72 tls_backend
= g_tls_backend_get_default ();
73 tls_certificate_type
= g_tls_backend_get_certificate_type (tls_backend
);
75 for (i
= (gint
) data
->len
- 1; i
>= 0; --i
)
79 cert_data
= g_ptr_array_index (data
, i
);
80 cert
= g_initable_new (tls_certificate_type
,
83 "certificate", (GByteArray
*) cert_data
,
90 g_clear_object (&issuer
);
91 issuer
= g_object_ref (cert
);
92 g_clear_object (&cert
);
96 g_assert_true (G_IS_TLS_CERTIFICATE (issuer
));
98 retval
= g_object_ref (issuer
);
101 g_clear_object (&cert
);
102 g_clear_object (&issuer
);
106 static TpTLSCertificateRejectReason
107 verification_output_to_reason (GTlsCertificateFlags flags
)
109 TpTLSCertificateRejectReason retval
;
111 g_assert (flags
!= 0);
115 case G_TLS_CERTIFICATE_UNKNOWN_CA
:
116 retval
= TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED
;
118 case G_TLS_CERTIFICATE_BAD_IDENTITY
:
119 retval
= TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH
;
121 case G_TLS_CERTIFICATE_NOT_ACTIVATED
:
122 retval
= TP_TLS_CERTIFICATE_REJECT_REASON_NOT_ACTIVATED
;
124 case G_TLS_CERTIFICATE_EXPIRED
:
125 retval
= TP_TLS_CERTIFICATE_REJECT_REASON_EXPIRED
;
127 case G_TLS_CERTIFICATE_REVOKED
:
128 retval
= TP_TLS_CERTIFICATE_REJECT_REASON_REVOKED
;
130 case G_TLS_CERTIFICATE_INSECURE
:
131 retval
= TP_TLS_CERTIFICATE_REJECT_REASON_INSECURE
;
133 case G_TLS_CERTIFICATE_GENERIC_ERROR
:
135 retval
= TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN
;
143 complete_verification (EmpathyTLSVerifier
*self
)
145 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (self
);
147 DEBUG ("Verification successful, completing...");
149 g_simple_async_result_complete_in_idle (priv
->verify_result
);
151 g_clear_object (&priv
->g_certificate
);
152 tp_clear_object (&priv
->verify_result
);
156 abort_verification (EmpathyTLSVerifier
*self
,
157 TpTLSCertificateRejectReason reason
)
159 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (self
);
161 DEBUG ("Verification error %u, aborting...", reason
);
163 g_simple_async_result_set_error (priv
->verify_result
,
164 G_IO_ERROR
, reason
, "TLS verification failed with reason %u",
166 g_simple_async_result_complete_in_idle (priv
->verify_result
);
168 g_clear_object (&priv
->g_certificate
);
169 tp_clear_object (&priv
->verify_result
);
173 debug_certificate (GcrCertificate
*cert
)
175 gchar
*subject
= gcr_certificate_get_subject_dn (cert
);
176 DEBUG ("Certificate: %s", subject
);
181 verify_chain_cb (GObject
*object
,
185 GError
*error
= NULL
;
187 GTlsCertificateFlags flags
;
188 GTlsDatabase
*tls_database
= G_TLS_DATABASE (object
);
190 EmpathyTLSVerifier
*self
= EMPATHY_TLS_VERIFIER (user_data
);
191 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (self
);
193 /* FIXME: g_tls_database_verify_chain doesn't set the GError if the
194 * certificate chain couldn't be verified. See:
195 * https://bugzilla.gnome.org/show_bug.cgi?id=780310
197 flags
= g_tls_database_verify_chain_finish (tls_database
, res
, &error
);
200 TpTLSCertificateRejectReason reason
;
202 /* We don't pass the identity to g_tls_database_verify. */
203 g_assert_false (flags
& G_TLS_CERTIFICATE_BAD_IDENTITY
);
205 reason
= verification_output_to_reason (flags
);
206 DEBUG ("Certificate verification gave flags %d with reason %u",
210 abort_verification (self
, reason
);
211 g_clear_error (&error
);
215 for (i
= 0; priv
->reference_identities
[i
] != NULL
; i
++)
217 GSocketConnectable
*identity
= NULL
;
219 identity
= g_network_address_new (priv
->reference_identities
[i
], 0);
220 flags
= g_tls_certificate_verify (priv
->g_certificate
, identity
, NULL
);
222 g_object_unref (identity
);
229 TpTLSCertificateRejectReason reason
;
231 g_assert_cmpint (flags
, ==, G_TLS_CERTIFICATE_BAD_IDENTITY
);
233 reason
= verification_output_to_reason (flags
);
234 DEBUG ("Certificate verification gave flags %d with reason %u",
238 /* FIXME: We don't set "certificate-hostname" because
239 * GTlsCertificate doesn't expose the hostname used in the
240 * certificate. We will temporarily lose some verbosity in
241 * EmpathyTLSDialog, but that's balanced by no longer
242 * relying on a specific encryption library.
244 tp_asv_set_string (priv
->details
, "expected-hostname", priv
->hostname
);
246 DEBUG ("Hostname mismatch: expected %s", priv
->hostname
);
248 abort_verification (self
, reason
);
252 DEBUG ("Verified certificate chain");
253 complete_verification (self
);
256 /* Matches ref when starting verify chain */
257 g_object_unref (self
);
261 is_certificate_pinned_cb (GObject
*object
,
265 GError
*error
= NULL
;
266 GPtrArray
*cert_data
;
267 EmpathyTLSVerifier
*self
= EMPATHY_TLS_VERIFIER (user_data
);
268 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (self
);
270 if (gcr_trust_is_certificate_pinned_finish (res
, &error
))
272 DEBUG ("Found pinned certificate for %s", priv
->hostname
);
273 complete_verification (self
);
277 /* error is set only when there is an actual failure. It won't be
278 * set, if it successfully determined that the ceritificate was not
282 DEBUG ("Failed to determine if certificate is pinned: %s",
284 g_clear_error (&error
);
287 cert_data
= tp_tls_certificate_get_cert_data (priv
->certificate
);
288 priv
->g_certificate
= tls_certificate_new_from_der (cert_data
, &error
);
291 DEBUG ("Verification of certificate chain failed: %s", error
->message
);
293 abort_verification (self
, TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN
);
294 g_clear_error (&error
);
298 DEBUG ("Performing verification");
300 g_tls_database_verify_chain_async (priv
->database
,
302 G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER
,
305 G_TLS_DATABASE_VERIFY_NONE
,
308 g_object_ref (self
));
311 /* Matches ref when starting is certificate pinned */
312 g_object_unref (self
);
316 empathy_tls_verifier_get_property (GObject
*object
,
321 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (object
);
325 case PROP_TLS_CERTIFICATE
:
326 g_value_set_object (value
, priv
->certificate
);
329 g_value_set_string (value
, priv
->hostname
);
331 case PROP_REFERENCE_IDENTITIES
:
332 g_value_set_boxed (value
, priv
->reference_identities
);
335 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
341 empathy_tls_verifier_set_property (GObject
*object
,
346 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (object
);
350 case PROP_TLS_CERTIFICATE
:
351 priv
->certificate
= g_value_dup_object (value
);
354 priv
->hostname
= g_value_dup_string (value
);
356 case PROP_REFERENCE_IDENTITIES
:
357 priv
->reference_identities
= g_value_dup_boxed (value
);
360 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
366 empathy_tls_verifier_dispose (GObject
*object
)
368 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (object
);
370 if (priv
->dispose_run
)
373 priv
->dispose_run
= TRUE
;
375 g_clear_object (&priv
->g_certificate
);
376 g_clear_object (&priv
->database
);
377 tp_clear_object (&priv
->certificate
);
379 G_OBJECT_CLASS (empathy_tls_verifier_parent_class
)->dispose (object
);
383 empathy_tls_verifier_finalize (GObject
*object
)
385 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (object
);
387 DEBUG ("%p", object
);
389 tp_clear_boxed (G_TYPE_HASH_TABLE
, &priv
->details
);
390 g_free (priv
->hostname
);
391 g_strfreev (priv
->reference_identities
);
393 G_OBJECT_CLASS (empathy_tls_verifier_parent_class
)->finalize (object
);
397 empathy_tls_verifier_init (EmpathyTLSVerifier
*self
)
399 EmpathyTLSVerifierPriv
*priv
;
400 GTlsBackend
*tls_backend
;
402 priv
= self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
403 EMPATHY_TYPE_TLS_VERIFIER
, EmpathyTLSVerifierPriv
);
404 priv
->details
= tp_asv_new (NULL
, NULL
);
406 tls_backend
= g_tls_backend_get_default ();
407 priv
->database
= g_tls_backend_get_default_database (tls_backend
);
411 empathy_tls_verifier_class_init (EmpathyTLSVerifierClass
*klass
)
414 GObjectClass
*oclass
= G_OBJECT_CLASS (klass
);
416 g_type_class_add_private (klass
, sizeof (EmpathyTLSVerifierPriv
));
418 oclass
->set_property
= empathy_tls_verifier_set_property
;
419 oclass
->get_property
= empathy_tls_verifier_get_property
;
420 oclass
->finalize
= empathy_tls_verifier_finalize
;
421 oclass
->dispose
= empathy_tls_verifier_dispose
;
423 pspec
= g_param_spec_object ("certificate", "The TpTLSCertificate",
424 "The TpTLSCertificate to be verified.",
425 TP_TYPE_TLS_CERTIFICATE
,
426 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
427 g_object_class_install_property (oclass
, PROP_TLS_CERTIFICATE
, pspec
);
429 pspec
= g_param_spec_string ("hostname", "The hostname",
430 "The hostname which is certified by the certificate.",
432 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
433 g_object_class_install_property (oclass
, PROP_HOSTNAME
, pspec
);
435 pspec
= g_param_spec_boxed ("reference-identities",
436 "The reference identities",
437 "The certificate should certify one of these identities.",
439 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
440 g_object_class_install_property (oclass
, PROP_REFERENCE_IDENTITIES
, pspec
);
444 empathy_tls_verifier_new (TpTLSCertificate
*certificate
,
445 const gchar
*hostname
,
446 const gchar
**reference_identities
)
448 g_assert (TP_IS_TLS_CERTIFICATE (certificate
));
449 g_assert (hostname
!= NULL
);
450 g_assert (reference_identities
!= NULL
);
452 return g_object_new (EMPATHY_TYPE_TLS_VERIFIER
,
453 "certificate", certificate
,
454 "hostname", hostname
,
455 "reference-identities", reference_identities
,
460 empathy_tls_verifier_verify_async (EmpathyTLSVerifier
*self
,
461 GAsyncReadyCallback callback
,
464 GcrCertificate
*cert
;
465 GPtrArray
*cert_data
;
467 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (self
);
469 DEBUG ("Starting verification");
471 g_return_if_fail (priv
->verify_result
== NULL
);
472 g_return_if_fail (priv
->g_certificate
== NULL
);
474 cert_data
= tp_tls_certificate_get_cert_data (priv
->certificate
);
475 g_return_if_fail (cert_data
);
477 priv
->verify_result
= g_simple_async_result_new (G_OBJECT (self
),
478 callback
, user_data
, NULL
);
480 /* The first certificate in the chain is for the host */
481 data
= g_ptr_array_index (cert_data
, 0);
482 cert
= gcr_simple_certificate_new ((gpointer
) data
->data
,
485 DEBUG ("Checking if certificate is pinned:");
486 debug_certificate (cert
);
488 gcr_trust_is_certificate_pinned_async (cert
,
489 GCR_PURPOSE_SERVER_AUTH
,
492 is_certificate_pinned_cb
,
493 g_object_ref (self
));
495 g_object_unref (cert
);
499 empathy_tls_verifier_verify_finish (EmpathyTLSVerifier
*self
,
501 TpTLSCertificateRejectReason
*reason
,
502 GHashTable
**details
,
505 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (self
);
507 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res
),
511 *reason
= (*error
)->code
;
515 *details
= tp_asv_new (NULL
, NULL
);
516 tp_g_hash_table_update (*details
, priv
->details
,
517 (GBoxedCopyFunc
) g_strdup
,
518 (GBoxedCopyFunc
) tp_g_value_slice_dup
);
525 *reason
= TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN
;
530 void empathy_tls_verifier_set_database (EmpathyTLSVerifier
*self
,
531 GTlsDatabase
*database
)
533 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (self
);
535 g_return_if_fail (EMPATHY_IS_TLS_VERIFIER (self
));
536 g_return_if_fail (G_IS_TLS_DATABASE (database
));
538 if (database
== priv
->database
)
541 g_clear_object (&priv
->database
);
542 priv
->database
= g_object_ref (database
);
546 empathy_tls_verifier_store_exception (EmpathyTLSVerifier
*self
)
549 GcrCertificate
*cert
;
550 GPtrArray
*cert_data
;
551 GError
*error
= NULL
;
552 EmpathyTLSVerifierPriv
*priv
= GET_PRIV (self
);
554 cert_data
= tp_tls_certificate_get_cert_data (priv
->certificate
);
555 g_return_if_fail (cert_data
);
559 DEBUG ("No certificate to pin.");
563 /* The first certificate in the chain is for the host */
564 data
= g_ptr_array_index (cert_data
, 0);
565 cert
= gcr_simple_certificate_new ((gpointer
)data
->data
, data
->len
);
567 DEBUG ("Storing pinned certificate:");
568 debug_certificate (cert
);
570 if (!gcr_trust_add_pinned_certificate (cert
, GCR_PURPOSE_SERVER_AUTH
,
571 priv
->hostname
, NULL
, &error
))
572 DEBUG ("Can't store the pinned certificate: %s", error
->message
);
574 g_object_unref (cert
);