5 #include <gnutls/gnutls.h>
6 #include <telepathy-glib/telepathy-glib.h>
7 #include <telepathy-glib/svc-tls.h>
8 #include <telepathy-glib/svc-generic.h>
10 #include "empathy-tls-verifier.h"
11 #include "mock-pkcs11.h"
12 #include "test-helper.h"
14 #define MOCK_TLS_CERTIFICATE_PATH "/mock/certificate"
17 GType mock_tls_certificate_get_type (void);
19 #define MOCK_TLS_CERTIFICATE(obj) \
20 (G_TYPE_CHECK_INSTANCE_CAST((obj), mock_tls_certificate_get_type (), \
23 typedef struct _MockTLSCertificate {
26 GPtrArray *rejections;
31 typedef struct _MockTLSCertificateClass {
33 TpDBusPropertiesMixinClass dbus_props_class;
34 } MockTLSCertificateClass;
40 PROP_CERTIFICATE_TYPE,
41 PROP_CERTIFICATE_CHAIN_DATA
44 static void mock_tls_certificate_iface_init (gpointer, gpointer);
46 G_DEFINE_TYPE_WITH_CODE(MockTLSCertificate, mock_tls_certificate, G_TYPE_OBJECT,
47 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_AUTHENTICATION_TLS_CERTIFICATE,
48 mock_tls_certificate_iface_init)
49 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
50 tp_dbus_properties_mixin_iface_init)
54 mock_tls_certificate_init (MockTLSCertificate *self)
56 self->state = TP_TLS_CERTIFICATE_STATE_PENDING;
57 self->cert_type = g_strdup ("x509");
58 self->cert_data = g_ptr_array_new_with_free_func((GDestroyNotify) g_array_unref);
59 self->rejections = g_ptr_array_new ();
63 mock_tls_certificate_get_property (GObject *object,
68 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (object);
73 g_value_set_uint (value, self->state);
76 g_value_set_boxed (value, self->rejections);
78 case PROP_CERTIFICATE_TYPE:
79 g_value_set_string (value, self->cert_type);
81 case PROP_CERTIFICATE_CHAIN_DATA:
82 g_value_set_boxed (value, self->cert_data);
85 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
91 mock_tls_certificate_finalize (GObject *object)
93 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (object);
95 tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
97 g_free (self->cert_type);
98 self->cert_type = NULL;
99 g_ptr_array_unref (self->cert_data);
100 self->cert_data = NULL;
102 G_OBJECT_CLASS (mock_tls_certificate_parent_class)->finalize (object);
106 mock_tls_certificate_class_init (MockTLSCertificateClass *klass)
108 GObjectClass *oclass = G_OBJECT_CLASS (klass);
111 static TpDBusPropertiesMixinPropImpl object_props[] = {
112 { "State", "state", NULL },
113 { "Rejections", "rejections", NULL },
114 { "CertificateType", "certificate-type", NULL },
115 { "CertificateChainData", "certificate-chain-data", NULL },
119 static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
120 { TP_IFACE_AUTHENTICATION_TLS_CERTIFICATE,
121 tp_dbus_properties_mixin_getter_gobject_properties,
128 oclass->get_property = mock_tls_certificate_get_property;
129 oclass->finalize = mock_tls_certificate_finalize;
131 pspec = g_param_spec_uint ("state",
132 "State of this certificate",
133 "The state of this TLS certificate.",
134 0, NUM_TP_TLS_CERTIFICATE_STATES - 1,
135 TP_TLS_CERTIFICATE_STATE_PENDING,
136 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
137 g_object_class_install_property (oclass, PROP_STATE, pspec);
139 pspec = g_param_spec_boxed ("rejections",
140 "The reject reasons",
141 "The reasons why this TLS certificate has been rejected",
142 TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
143 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
144 g_object_class_install_property (oclass, PROP_REJECTIONS, pspec);
146 pspec = g_param_spec_string ("certificate-type",
147 "The certificate type",
148 "The type of this certificate.",
150 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
151 g_object_class_install_property (oclass, PROP_CERTIFICATE_TYPE, pspec);
153 pspec = g_param_spec_boxed ("certificate-chain-data",
154 "The certificate chain data",
155 "The raw PEM-encoded trust chain of this certificate.",
156 TP_ARRAY_TYPE_UCHAR_ARRAY_LIST,
157 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
158 g_object_class_install_property (oclass, PROP_CERTIFICATE_CHAIN_DATA, pspec);
160 klass->dbus_props_class.interfaces = prop_interfaces;
161 tp_dbus_properties_mixin_class_init (oclass,
162 G_STRUCT_OFFSET (MockTLSCertificateClass, dbus_props_class));
166 mock_tls_certificate_accept (TpSvcAuthenticationTLSCertificate *base,
167 DBusGMethodInvocation *context)
169 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (base);
170 self->state = TP_TLS_CERTIFICATE_STATE_ACCEPTED;
171 tp_svc_authentication_tls_certificate_emit_accepted (self);
172 tp_svc_authentication_tls_certificate_return_from_accept (context);
176 mock_tls_certificate_reject (TpSvcAuthenticationTLSCertificate *base,
177 const GPtrArray *in_Rejections,
178 DBusGMethodInvocation *context)
180 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (base);
181 self->state = TP_TLS_CERTIFICATE_STATE_REJECTED;
182 tp_svc_authentication_tls_certificate_emit_rejected (self, in_Rejections);
183 tp_svc_authentication_tls_certificate_return_from_reject (context);
187 mock_tls_certificate_iface_init (gpointer g_iface,
190 TpSvcAuthenticationTLSCertificateClass *klass =
191 (TpSvcAuthenticationTLSCertificateClass*)g_iface;
193 tp_svc_authentication_tls_certificate_implement_accept (klass,
194 mock_tls_certificate_accept);
195 tp_svc_authentication_tls_certificate_implement_reject (klass,
196 mock_tls_certificate_reject);
201 mock_tls_certificate_assert_rejected (MockTLSCertificate *self,
202 TpTLSCertificateRejectReason reason)
204 GValueArray *rejection;
205 TpTLSCertificateRejectReason rejection_reason;
206 gchar *rejection_error;
207 GHashTable *rejection_details;
210 g_assert (self->state == TP_TLS_CERTIFICATE_STATE_REJECTED);
211 g_assert (self->rejections);
212 g_assert (self->rejections->len > 0);
214 for (i = 0; i < self->rejections->len; ++i)
216 rejection = g_ptr_array_index (self->rejections, i);
217 tp_value_array_unpack (rejection, 3,
218 G_TYPE_UINT, &rejection_reason,
219 G_TYPE_STRING, &rejection_error,
220 TP_HASH_TYPE_STRING_VARIANT_MAP, &rejection_details,
222 g_free (rejection_error);
223 g_hash_table_unref (rejection_details);
225 if (rejection_reason == reason)
229 g_assert ("Certificate was not rejected for right reason" && 0);
233 static MockTLSCertificate*
234 mock_tls_certificate_new_and_register (TpDBusDaemon *dbus,
238 MockTLSCertificate *cert;
239 GError *error = NULL;
240 gchar *filename, *contents;
245 cert = g_object_new (mock_tls_certificate_get_type (), NULL);
248 while (path != NULL) {
249 filename = g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
250 "tests", "certificates", path, NULL);
251 g_file_get_contents (filename, &contents, &length, &error);
252 g_assert_no_error (error);
254 der = g_array_sized_new (TRUE, TRUE, sizeof (guchar), length);
255 g_array_append_vals (der, contents, length);
256 g_ptr_array_add (cert->cert_data, der);
261 path = va_arg (va, gchar*);
265 tp_dbus_daemon_register_object (dbus, MOCK_TLS_CERTIFICATE_PATH, cert);
269 /* ----------------------------------------------------------------------------
276 const gchar *dbus_name;
277 MockTLSCertificate *mock;
278 TpTLSCertificate *cert;
279 GAsyncResult *result;
283 setup (Test *test, gconstpointer data)
285 GError *error = NULL;
287 const gchar *trust_uris[2] = { MOCK_SLOT_ONE_URI, NULL };
289 test->loop = g_main_loop_new (NULL, FALSE);
291 test->dbus = tp_dbus_daemon_dup (&error);
292 g_assert_no_error (error);
294 test->dbus_name = tp_dbus_daemon_get_unique_name (test->dbus);
299 /* Add our mock module as the only PKCS#11 module */
300 module = gck_module_new (&mock_default_functions);
301 mock_C_Initialize (NULL);
303 gcr_pkcs11_set_modules (NULL);
304 gcr_pkcs11_add_module (module);
305 gcr_pkcs11_set_trust_lookup_uris (trust_uris);
309 teardown (Test *test, gconstpointer data)
311 mock_C_Finalize (NULL);
313 test->dbus_name = NULL;
317 tp_dbus_daemon_unregister_object (test->dbus, test->mock);
318 g_object_unref (test->mock);
323 g_object_unref (test->result);
327 g_object_unref (test->cert);
330 g_main_loop_unref (test->loop);
333 g_object_unref (test->dbus);
338 add_certificate_to_mock (Test *test,
339 const gchar *certificate,
342 GError *error = NULL;
343 GcrCertificate *cert;
348 path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
349 "tests", "certificates", certificate, NULL);
351 g_file_get_contents (path, &contents, &length, &error);
352 g_assert_no_error (error);
354 cert = gcr_simple_certificate_new ((const guchar *)contents, length);
355 mock_module_add_certificate (cert);
356 mock_module_add_assertion (cert,
357 peer ? CKT_X_PINNED_CERTIFICATE : CKT_X_ANCHORED_CERTIFICATE,
358 GCR_PURPOSE_SERVER_AUTH, peer);
359 g_object_unref (cert);
366 fetch_callback_result (GObject *object,
370 Test *test = user_data;
371 g_assert (!test->result);
372 test->result = g_object_ref (res);
373 g_main_loop_quit (test->loop);
377 ensure_certificate_proxy (Test *test)
379 GError *error = NULL;
380 GQuark features[] = { TP_TLS_CERTIFICATE_FEATURE_CORE, 0 };
385 /* Create and prepare a certificate */
386 /* We don't use tp_tls_certificate_new() as we don't pass a parent */
387 test->cert = g_object_new (TP_TYPE_TLS_CERTIFICATE,
388 "dbus-daemon", test->dbus,
389 "bus-name", test->dbus_name,
390 "object-path", MOCK_TLS_CERTIFICATE_PATH,
393 tp_proxy_prepare_async (test->cert, features, fetch_callback_result, test);
394 g_main_loop_run (test->loop);
395 tp_proxy_prepare_finish (test->cert, test->result, &error);
396 g_assert_no_error (error);
398 /* Clear for any future async stuff */
399 g_object_unref (test->result);
403 /* A simple test to make sure the test infrastructure is working */
405 test_certificate_mock_basics (Test *test,
406 gconstpointer data G_GNUC_UNUSED)
408 GError *error = NULL;
410 test->mock = mock_tls_certificate_new_and_register (test->dbus,
411 "server-cert.cer", NULL);
413 ensure_certificate_proxy (test);
415 tp_tls_certificate_accept_async (test->cert, fetch_callback_result, test);
416 g_main_loop_run (test->loop);
417 tp_tls_certificate_accept_finish (test->cert, test->result, &error);
418 g_assert_no_error (error);
420 g_assert (test->mock->state == TP_TLS_CERTIFICATE_STATE_ACCEPTED);
424 test_certificate_verify_success_with_pkcs11_lookup (Test *test,
425 gconstpointer data G_GNUC_UNUSED)
427 TpTLSCertificateRejectReason reason = 0;
428 GError *error = NULL;
429 EmpathyTLSVerifier *verifier;
430 const gchar *reference_identities[] = {
431 "test-server.empathy.gnome.org",
436 * In this test the mock TLS connection only has one certificate
437 * not a full certificat echain. The root anchor certificate is
438 * retrieved from PKCS#11 storage.
441 test->mock = mock_tls_certificate_new_and_register (test->dbus,
442 "server-cert.cer", NULL);
444 /* We add the collabora directory with the collabora root */
445 add_certificate_to_mock (test, "certificate-authority.cer", NULL);
447 ensure_certificate_proxy (test);
449 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
450 reference_identities);
451 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
452 g_main_loop_run (test->loop);
454 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
456 g_assert_no_error (error);
458 /* Yay the verification was a success! */
460 g_clear_error (&error);
461 g_object_unref (verifier);
465 test_certificate_verify_success_with_full_chain (Test *test,
466 gconstpointer data G_GNUC_UNUSED)
468 TpTLSCertificateRejectReason reason = 0;
469 GError *error = NULL;
470 EmpathyTLSVerifier *verifier;
471 const gchar *reference_identities[] = {
472 "test-server.empathy.gnome.org",
477 * In this test the mock TLS connection has a full certificate
478 * chain. We look for an anchor certificate in the chain.
481 test->mock = mock_tls_certificate_new_and_register (test->dbus,
482 "server-cert.cer", "certificate-authority.cer", NULL);
484 /* We add the collabora directory with the collabora root */
485 add_certificate_to_mock (test, "certificate-authority.cer", NULL);
487 ensure_certificate_proxy (test);
489 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
490 reference_identities);
491 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
492 g_main_loop_run (test->loop);
493 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
495 g_assert_no_error (error);
497 /* Yay the verification was a success! */
499 g_clear_error (&error);
500 g_object_unref (verifier);
504 test_certificate_verify_root_not_found (Test *test,
505 gconstpointer data G_GNUC_UNUSED)
507 TpTLSCertificateRejectReason reason = 0;
508 GError *error = NULL;
509 EmpathyTLSVerifier *verifier;
510 const gchar *reference_identities[] = {
511 "test-server.empathy.gnome.org",
515 test->mock = mock_tls_certificate_new_and_register (test->dbus,
516 "server-cert.cer", NULL);
518 /* Note that we're not adding any place to find root certs */
520 ensure_certificate_proxy (test);
522 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
523 reference_identities);
524 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
525 g_main_loop_run (test->loop);
527 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
530 /* And it should say we're self-signed (oddly enough) */
531 g_assert_error (error, G_IO_ERROR,
532 TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
534 g_clear_error (&error);
535 g_object_unref (verifier);
539 test_certificate_verify_root_not_anchored (Test *test,
540 gconstpointer data G_GNUC_UNUSED)
542 TpTLSCertificateRejectReason reason = 0;
543 GError *error = NULL;
544 EmpathyTLSVerifier *verifier;
545 const gchar *reference_identities[] = {
546 "test-server.empathy.gnome.org",
550 test->mock = mock_tls_certificate_new_and_register (test->dbus,
551 "server-cert.cer", "certificate-authority.cer", NULL);
553 /* Note that we're not adding any place to find root certs */
555 ensure_certificate_proxy (test);
557 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
558 reference_identities);
559 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
560 g_main_loop_run (test->loop);
562 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
565 /* And it should say we're self-signed (oddly enough) */
566 g_assert_error (error, G_IO_ERROR,
567 TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
569 g_clear_error (&error);
570 g_object_unref (verifier);
574 test_certificate_verify_identities_invalid (Test *test,
575 gconstpointer data G_GNUC_UNUSED)
577 TpTLSCertificateRejectReason reason = 0;
578 GError *error = NULL;
579 EmpathyTLSVerifier *verifier;
580 const gchar *reference_identities[] = {
585 test->mock = mock_tls_certificate_new_and_register (test->dbus,
586 "server-cert.cer", "certificate-authority.cer", NULL);
588 /* We add the collabora directory with the collabora root */
589 add_certificate_to_mock (test, "certificate-authority.cer", NULL);
591 ensure_certificate_proxy (test);
593 verifier = empathy_tls_verifier_new (test->cert, "invalid.host.name",
594 reference_identities);
595 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
596 g_main_loop_run (test->loop);
598 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
601 /* And it should say we're self-signed (oddly enough) */
602 g_assert_error (error, G_IO_ERROR,
603 TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
605 g_clear_error (&error);
606 g_object_unref (verifier);
610 test_certificate_verify_uses_reference_identities (Test *test,
611 gconstpointer data G_GNUC_UNUSED)
613 TpTLSCertificateRejectReason reason = 0;
614 GError *error = NULL;
615 EmpathyTLSVerifier *verifier;
616 const gchar *reference_identities[] = {
621 test->mock = mock_tls_certificate_new_and_register (test->dbus,
622 "server-cert.cer", "certificate-authority.cer", NULL);
624 /* We add the collabora directory with the collabora root */
625 add_certificate_to_mock (test, "certificate-authority.cer", NULL);
627 ensure_certificate_proxy (test);
629 /* Should be using the reference_identities and not host name for checks */
630 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
631 reference_identities);
632 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
633 g_main_loop_run (test->loop);
635 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
638 /* And it should say we're self-signed (oddly enough) */
639 g_assert_error (error, G_IO_ERROR,
640 TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
642 g_clear_error (&error);
643 g_object_unref (verifier);
647 test_certificate_verify_success_with_pinned (Test *test,
648 gconstpointer data G_GNUC_UNUSED)
650 TpTLSCertificateRejectReason reason = 0;
651 GError *error = NULL;
652 EmpathyTLSVerifier *verifier;
653 const gchar *reference_identities[] = {
654 "test-server.empathy.gnome.org",
659 * In this test the mock TLS connection has a full certificate
660 * chain. We look for an anchor certificate in the chain.
663 test->mock = mock_tls_certificate_new_and_register (test->dbus,
664 "server-cert.cer", NULL);
666 /* We add the collabora directory with the collabora root */
667 add_certificate_to_mock (test, "server-cert.cer", "test-server.empathy.gnome.org");
669 ensure_certificate_proxy (test);
671 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
672 reference_identities);
673 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
674 g_main_loop_run (test->loop);
675 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
677 g_assert_no_error (error);
679 /* Yay the verification was a success! */
681 g_clear_error (&error);
682 g_object_unref (verifier);
686 test_certificate_verify_pinned_wrong_host (Test *test,
687 gconstpointer data G_GNUC_UNUSED)
689 TpTLSCertificateRejectReason reason = 0;
690 GError *error = NULL;
691 EmpathyTLSVerifier *verifier;
692 const gchar *reference_identities[] = {
693 "test-server.empathy.gnome.org",
697 test->mock = mock_tls_certificate_new_and_register (test->dbus,
698 "server-cert.cer", NULL);
700 /* Note that we're not adding any place to find root certs */
702 ensure_certificate_proxy (test);
704 verifier = empathy_tls_verifier_new (test->cert, "another.gnome.org",
705 reference_identities);
706 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
707 g_main_loop_run (test->loop);
709 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
712 /* And it should say we're self-signed */
713 g_assert_error (error, G_IO_ERROR,
714 TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
716 g_clear_error (&error);
717 g_object_unref (verifier);
726 test_init (argc, argv);
727 gnutls_global_init ();
729 g_test_add ("/tls/certificate_basics", Test, NULL,
730 setup, test_certificate_mock_basics, teardown);
731 g_test_add ("/tls/certificate_verify_success_with_pkcs11_lookup", Test, NULL,
732 setup, test_certificate_verify_success_with_pkcs11_lookup, teardown);
733 g_test_add ("/tls/certificate_verify_success_with_full_chain", Test, NULL,
734 setup, test_certificate_verify_success_with_full_chain, teardown);
735 g_test_add ("/tls/certificate_verify_root_not_found", Test, NULL,
736 setup, test_certificate_verify_root_not_found, teardown);
737 g_test_add ("/tls/certificate_verify_root_not_anchored", Test, NULL,
738 setup, test_certificate_verify_root_not_anchored, teardown);
739 g_test_add ("/tls/certificate_verify_identities_invalid", Test, NULL,
740 setup, test_certificate_verify_identities_invalid, teardown);
741 g_test_add ("/tls/certificate_verify_uses_reference_identities", Test, NULL,
742 setup, test_certificate_verify_uses_reference_identities, teardown);
743 g_test_add ("/tls/certificate_verify_success_with_pinned", Test, NULL,
744 setup, test_certificate_verify_success_with_pinned, teardown);
745 g_test_add ("/tls/certificate_verify_pinned_wrong_host", Test, NULL,
746 setup, test_certificate_verify_pinned_wrong_host, teardown);
748 result = g_test_run ();