1 #include <gnutls/gnutls.h>
2 #include <telepathy-glib/svc-tls.h>
4 #include "empathy-tls-verifier.h"
5 #include "mock-pkcs11.h"
6 #include "test-helper.h"
8 #define MOCK_TLS_CERTIFICATE_PATH "/mock/certificate"
11 GType mock_tls_certificate_get_type (void);
13 #define MOCK_TLS_CERTIFICATE(obj) \
14 (G_TYPE_CHECK_INSTANCE_CAST((obj), mock_tls_certificate_get_type (), \
17 typedef struct _MockTLSCertificate {
20 GPtrArray *rejections;
25 typedef struct _MockTLSCertificateClass {
27 TpDBusPropertiesMixinClass dbus_props_class;
28 } MockTLSCertificateClass;
34 PROP_CERTIFICATE_TYPE,
35 PROP_CERTIFICATE_CHAIN_DATA
38 static void mock_tls_certificate_iface_init (gpointer, gpointer);
40 G_DEFINE_TYPE_WITH_CODE(MockTLSCertificate, mock_tls_certificate, G_TYPE_OBJECT,
41 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_AUTHENTICATION_TLS_CERTIFICATE,
42 mock_tls_certificate_iface_init)
43 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
44 tp_dbus_properties_mixin_iface_init)
48 mock_tls_certificate_init (MockTLSCertificate *self)
50 self->state = TP_TLS_CERTIFICATE_STATE_PENDING;
51 self->cert_type = g_strdup ("x509");
52 self->cert_data = g_ptr_array_new_with_free_func((GDestroyNotify) g_array_unref);
53 self->rejections = g_ptr_array_new ();
57 mock_tls_certificate_get_property (GObject *object,
62 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (object);
67 g_value_set_uint (value, self->state);
70 g_value_set_boxed (value, self->rejections);
72 case PROP_CERTIFICATE_TYPE:
73 g_value_set_string (value, self->cert_type);
75 case PROP_CERTIFICATE_CHAIN_DATA:
76 g_value_set_boxed (value, self->cert_data);
79 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
85 mock_tls_certificate_finalize (GObject *object)
87 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (object);
89 tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
91 g_free (self->cert_type);
92 self->cert_type = NULL;
93 g_ptr_array_unref (self->cert_data);
94 self->cert_data = NULL;
96 G_OBJECT_CLASS (mock_tls_certificate_parent_class)->finalize (object);
100 mock_tls_certificate_class_init (MockTLSCertificateClass *klass)
102 GObjectClass *oclass = G_OBJECT_CLASS (klass);
105 static TpDBusPropertiesMixinPropImpl object_props[] = {
106 { "State", "state", NULL },
107 { "Rejections", "rejections", NULL },
108 { "CertificateType", "certificate-type", NULL },
109 { "CertificateChainData", "certificate-chain-data", NULL },
113 static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
114 { TP_IFACE_AUTHENTICATION_TLS_CERTIFICATE,
115 tp_dbus_properties_mixin_getter_gobject_properties,
122 oclass->get_property = mock_tls_certificate_get_property;
123 oclass->finalize = mock_tls_certificate_finalize;
125 pspec = g_param_spec_uint ("state",
126 "State of this certificate",
127 "The state of this TLS certificate.",
128 0, NUM_TP_TLS_CERTIFICATE_STATES - 1,
129 TP_TLS_CERTIFICATE_STATE_PENDING,
130 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
131 g_object_class_install_property (oclass, PROP_STATE, pspec);
133 pspec = g_param_spec_boxed ("rejections",
134 "The reject reasons",
135 "The reasons why this TLS certificate has been rejected",
136 TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
137 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
138 g_object_class_install_property (oclass, PROP_REJECTIONS, pspec);
140 pspec = g_param_spec_string ("certificate-type",
141 "The certificate type",
142 "The type of this certificate.",
144 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
145 g_object_class_install_property (oclass, PROP_CERTIFICATE_TYPE, pspec);
147 pspec = g_param_spec_boxed ("certificate-chain-data",
148 "The certificate chain data",
149 "The raw PEM-encoded trust chain of this certificate.",
150 TP_ARRAY_TYPE_UCHAR_ARRAY_LIST,
151 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
152 g_object_class_install_property (oclass, PROP_CERTIFICATE_CHAIN_DATA, pspec);
154 klass->dbus_props_class.interfaces = prop_interfaces;
155 tp_dbus_properties_mixin_class_init (oclass,
156 G_STRUCT_OFFSET (MockTLSCertificateClass, dbus_props_class));
160 mock_tls_certificate_accept (TpSvcAuthenticationTLSCertificate *base,
161 DBusGMethodInvocation *context)
163 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (base);
164 self->state = TP_TLS_CERTIFICATE_STATE_ACCEPTED;
165 tp_svc_authentication_tls_certificate_emit_accepted (self);
166 tp_svc_authentication_tls_certificate_return_from_accept (context);
170 mock_tls_certificate_reject (TpSvcAuthenticationTLSCertificate *base,
171 const GPtrArray *in_Rejections,
172 DBusGMethodInvocation *context)
174 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (base);
175 self->state = TP_TLS_CERTIFICATE_STATE_REJECTED;
176 tp_svc_authentication_tls_certificate_emit_rejected (self, in_Rejections);
177 tp_svc_authentication_tls_certificate_return_from_reject (context);
181 mock_tls_certificate_iface_init (gpointer g_iface,
184 TpSvcAuthenticationTLSCertificateClass *klass =
185 (TpSvcAuthenticationTLSCertificateClass*)g_iface;
187 tp_svc_authentication_tls_certificate_implement_accept (klass,
188 mock_tls_certificate_accept);
189 tp_svc_authentication_tls_certificate_implement_reject (klass,
190 mock_tls_certificate_reject);
195 mock_tls_certificate_assert_rejected (MockTLSCertificate *self,
196 TpTLSCertificateRejectReason reason)
198 GValueArray *rejection;
199 TpTLSCertificateRejectReason rejection_reason;
200 gchar *rejection_error;
201 GHashTable *rejection_details;
204 g_assert (self->state == TP_TLS_CERTIFICATE_STATE_REJECTED);
205 g_assert (self->rejections);
206 g_assert (self->rejections->len > 0);
208 for (i = 0; i < self->rejections->len; ++i)
210 rejection = g_ptr_array_index (self->rejections, i);
211 tp_value_array_unpack (rejection, 3,
212 G_TYPE_UINT, &rejection_reason,
213 G_TYPE_STRING, &rejection_error,
214 TP_HASH_TYPE_STRING_VARIANT_MAP, &rejection_details,
216 g_free (rejection_error);
217 g_hash_table_unref (rejection_details);
219 if (rejection_reason == reason)
223 g_assert ("Certificate was not rejected for right reason" && 0);
227 static MockTLSCertificate*
228 mock_tls_certificate_new_and_register (TpDBusDaemon *dbus,
232 MockTLSCertificate *cert;
233 GError *error = NULL;
234 gchar *filename, *contents;
239 cert = g_object_new (mock_tls_certificate_get_type (), NULL);
242 while (path != NULL) {
243 filename = g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
244 "tests", "certificates", path, NULL);
245 g_file_get_contents (filename, &contents, &length, &error);
246 g_assert_no_error (error);
248 der = g_array_sized_new (TRUE, TRUE, sizeof (guchar), length);
249 g_array_append_vals (der, contents, length);
250 g_ptr_array_add (cert->cert_data, der);
255 path = va_arg (va, gchar*);
259 tp_dbus_daemon_register_object (dbus, MOCK_TLS_CERTIFICATE_PATH, cert);
263 /* ----------------------------------------------------------------------------
270 const gchar *dbus_name;
271 MockTLSCertificate *mock;
272 TpTLSCertificate *cert;
273 GAsyncResult *result;
277 setup (Test *test, gconstpointer data)
279 GError *error = NULL;
281 const gchar *trust_uris[2] = { MOCK_SLOT_ONE_URI, NULL };
283 test->loop = g_main_loop_new (NULL, FALSE);
285 test->dbus = tp_dbus_daemon_dup (&error);
286 g_assert_no_error (error);
288 test->dbus_name = tp_dbus_daemon_get_unique_name (test->dbus);
293 /* Add our mock module as the only PKCS#11 module */
294 module = gck_module_new (&mock_default_functions);
295 mock_C_Initialize (NULL);
297 gcr_pkcs11_set_modules (NULL);
298 gcr_pkcs11_add_module (module);
299 gcr_pkcs11_set_trust_lookup_uris (trust_uris);
303 teardown (Test *test, gconstpointer data)
305 mock_C_Finalize (NULL);
307 test->dbus_name = NULL;
311 tp_dbus_daemon_unregister_object (test->dbus, test->mock);
312 g_object_unref (test->mock);
317 g_object_unref (test->result);
321 g_object_unref (test->cert);
324 g_main_loop_unref (test->loop);
327 g_object_unref (test->dbus);
332 add_certificate_to_mock (Test *test,
333 const gchar *certificate,
336 GError *error = NULL;
337 GcrCertificate *cert;
342 path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
343 "tests", "certificates", certificate, NULL);
345 g_file_get_contents (path, &contents, &length, &error);
346 g_assert_no_error (error);
348 cert = gcr_simple_certificate_new ((const guchar *)contents, length);
349 mock_module_add_certificate (cert);
350 mock_module_add_assertion (cert,
351 peer ? CKT_X_PINNED_CERTIFICATE : CKT_X_ANCHORED_CERTIFICATE,
352 GCR_PURPOSE_SERVER_AUTH, peer);
353 g_object_unref (cert);
360 fetch_callback_result (GObject *object,
364 Test *test = user_data;
365 g_assert (!test->result);
366 test->result = g_object_ref (res);
367 g_main_loop_quit (test->loop);
371 ensure_certificate_proxy (Test *test)
373 GError *error = NULL;
374 GQuark features[] = { TP_TLS_CERTIFICATE_FEATURE_CORE, 0 };
379 /* Create and prepare a certificate */
380 /* We don't use tp_tls_certificate_new() as we don't pass a parent */
381 test->cert = g_object_new (TP_TYPE_TLS_CERTIFICATE,
382 "dbus-daemon", test->dbus,
383 "bus-name", test->dbus_name,
384 "object-path", MOCK_TLS_CERTIFICATE_PATH,
387 tp_proxy_prepare_async (test->cert, features, fetch_callback_result, test);
388 g_main_loop_run (test->loop);
389 tp_proxy_prepare_finish (test->cert, test->result, &error);
390 g_assert_no_error (error);
392 /* Clear for any future async stuff */
393 g_object_unref (test->result);
397 /* A simple test to make sure the test infrastructure is working */
399 test_certificate_mock_basics (Test *test,
400 gconstpointer data G_GNUC_UNUSED)
402 GError *error = NULL;
404 test->mock = mock_tls_certificate_new_and_register (test->dbus,
405 "server-cert.cer", NULL);
407 ensure_certificate_proxy (test);
409 tp_tls_certificate_accept_async (test->cert, fetch_callback_result, test);
410 g_main_loop_run (test->loop);
411 tp_tls_certificate_accept_finish (test->cert, test->result, &error);
412 g_assert_no_error (error);
414 g_assert (test->mock->state == TP_TLS_CERTIFICATE_STATE_ACCEPTED);
418 test_certificate_verify_success_with_pkcs11_lookup (Test *test,
419 gconstpointer data G_GNUC_UNUSED)
421 TpTLSCertificateRejectReason reason = 0;
422 GError *error = NULL;
423 EmpathyTLSVerifier *verifier;
424 const gchar *reference_identities[] = {
425 "test-server.empathy.gnome.org",
430 * In this test the mock TLS connection only has one certificate
431 * not a full certificat echain. The root anchor certificate is
432 * retrieved from PKCS#11 storage.
435 test->mock = mock_tls_certificate_new_and_register (test->dbus,
436 "server-cert.cer", NULL);
438 /* We add the collabora directory with the collabora root */
439 add_certificate_to_mock (test, "certificate-authority.cer", NULL);
441 ensure_certificate_proxy (test);
443 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
444 reference_identities);
445 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
446 g_main_loop_run (test->loop);
448 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
450 g_assert_no_error (error);
452 /* Yay the verification was a success! */
454 g_clear_error (&error);
455 g_object_unref (verifier);
459 test_certificate_verify_success_with_full_chain (Test *test,
460 gconstpointer data G_GNUC_UNUSED)
462 TpTLSCertificateRejectReason reason = 0;
463 GError *error = NULL;
464 EmpathyTLSVerifier *verifier;
465 const gchar *reference_identities[] = {
466 "test-server.empathy.gnome.org",
471 * In this test the mock TLS connection has a full certificate
472 * chain. We look for an anchor certificate in the chain.
475 test->mock = mock_tls_certificate_new_and_register (test->dbus,
476 "server-cert.cer", "certificate-authority.cer", NULL);
478 /* We add the collabora directory with the collabora root */
479 add_certificate_to_mock (test, "certificate-authority.cer", NULL);
481 ensure_certificate_proxy (test);
483 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
484 reference_identities);
485 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
486 g_main_loop_run (test->loop);
487 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
489 g_assert_no_error (error);
491 /* Yay the verification was a success! */
493 g_clear_error (&error);
494 g_object_unref (verifier);
498 test_certificate_verify_root_not_found (Test *test,
499 gconstpointer data G_GNUC_UNUSED)
501 TpTLSCertificateRejectReason reason = 0;
502 GError *error = NULL;
503 EmpathyTLSVerifier *verifier;
504 const gchar *reference_identities[] = {
505 "test-server.empathy.gnome.org",
509 test->mock = mock_tls_certificate_new_and_register (test->dbus,
510 "server-cert.cer", NULL);
512 /* Note that we're not adding any place to find root certs */
514 ensure_certificate_proxy (test);
516 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
517 reference_identities);
518 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
519 g_main_loop_run (test->loop);
521 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
524 /* And it should say we're self-signed (oddly enough) */
525 g_assert_error (error, G_IO_ERROR,
526 TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
528 g_clear_error (&error);
529 g_object_unref (verifier);
533 test_certificate_verify_root_not_anchored (Test *test,
534 gconstpointer data G_GNUC_UNUSED)
536 TpTLSCertificateRejectReason reason = 0;
537 GError *error = NULL;
538 EmpathyTLSVerifier *verifier;
539 const gchar *reference_identities[] = {
540 "test-server.empathy.gnome.org",
544 test->mock = mock_tls_certificate_new_and_register (test->dbus,
545 "server-cert.cer", "certificate-authority.cer", NULL);
547 /* Note that we're not adding any place to find root certs */
549 ensure_certificate_proxy (test);
551 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
552 reference_identities);
553 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
554 g_main_loop_run (test->loop);
556 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
559 /* And it should say we're self-signed (oddly enough) */
560 g_assert_error (error, G_IO_ERROR,
561 TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
563 g_clear_error (&error);
564 g_object_unref (verifier);
568 test_certificate_verify_identities_invalid (Test *test,
569 gconstpointer data G_GNUC_UNUSED)
571 TpTLSCertificateRejectReason reason = 0;
572 GError *error = NULL;
573 EmpathyTLSVerifier *verifier;
574 const gchar *reference_identities[] = {
579 test->mock = mock_tls_certificate_new_and_register (test->dbus,
580 "server-cert.cer", "certificate-authority.cer", NULL);
582 /* We add the collabora directory with the collabora root */
583 add_certificate_to_mock (test, "certificate-authority.cer", NULL);
585 ensure_certificate_proxy (test);
587 verifier = empathy_tls_verifier_new (test->cert, "invalid.host.name",
588 reference_identities);
589 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
590 g_main_loop_run (test->loop);
592 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
595 /* And it should say we're self-signed (oddly enough) */
596 g_assert_error (error, G_IO_ERROR,
597 TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
599 g_clear_error (&error);
600 g_object_unref (verifier);
604 test_certificate_verify_uses_reference_identities (Test *test,
605 gconstpointer data G_GNUC_UNUSED)
607 TpTLSCertificateRejectReason reason = 0;
608 GError *error = NULL;
609 EmpathyTLSVerifier *verifier;
610 const gchar *reference_identities[] = {
615 test->mock = mock_tls_certificate_new_and_register (test->dbus,
616 "server-cert.cer", "certificate-authority.cer", NULL);
618 /* We add the collabora directory with the collabora root */
619 add_certificate_to_mock (test, "certificate-authority.cer", NULL);
621 ensure_certificate_proxy (test);
623 /* Should be using the reference_identities and not host name for checks */
624 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
625 reference_identities);
626 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
627 g_main_loop_run (test->loop);
629 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
632 /* And it should say we're self-signed (oddly enough) */
633 g_assert_error (error, G_IO_ERROR,
634 TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
636 g_clear_error (&error);
637 g_object_unref (verifier);
641 test_certificate_verify_success_with_pinned (Test *test,
642 gconstpointer data G_GNUC_UNUSED)
644 TpTLSCertificateRejectReason reason = 0;
645 GError *error = NULL;
646 EmpathyTLSVerifier *verifier;
647 const gchar *reference_identities[] = {
648 "test-server.empathy.gnome.org",
653 * In this test the mock TLS connection has a full certificate
654 * chain. We look for an anchor certificate in the chain.
657 test->mock = mock_tls_certificate_new_and_register (test->dbus,
658 "server-cert.cer", NULL);
660 /* We add the collabora directory with the collabora root */
661 add_certificate_to_mock (test, "server-cert.cer", "test-server.empathy.gnome.org");
663 ensure_certificate_proxy (test);
665 verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
666 reference_identities);
667 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
668 g_main_loop_run (test->loop);
669 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
671 g_assert_no_error (error);
673 /* Yay the verification was a success! */
675 g_clear_error (&error);
676 g_object_unref (verifier);
680 test_certificate_verify_pinned_wrong_host (Test *test,
681 gconstpointer data G_GNUC_UNUSED)
683 TpTLSCertificateRejectReason reason = 0;
684 GError *error = NULL;
685 EmpathyTLSVerifier *verifier;
686 const gchar *reference_identities[] = {
687 "test-server.empathy.gnome.org",
691 test->mock = mock_tls_certificate_new_and_register (test->dbus,
692 "server-cert.cer", NULL);
694 /* Note that we're not adding any place to find root certs */
696 ensure_certificate_proxy (test);
698 verifier = empathy_tls_verifier_new (test->cert, "another.gnome.org",
699 reference_identities);
700 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
701 g_main_loop_run (test->loop);
703 empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
706 /* And it should say we're self-signed */
707 g_assert_error (error, G_IO_ERROR,
708 TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
710 g_clear_error (&error);
711 g_object_unref (verifier);
720 test_init (argc, argv);
721 gnutls_global_init ();
723 g_test_add ("/tls/certificate_basics", Test, NULL,
724 setup, test_certificate_mock_basics, teardown);
725 g_test_add ("/tls/certificate_verify_success_with_pkcs11_lookup", Test, NULL,
726 setup, test_certificate_verify_success_with_pkcs11_lookup, teardown);
727 g_test_add ("/tls/certificate_verify_success_with_full_chain", Test, NULL,
728 setup, test_certificate_verify_success_with_full_chain, teardown);
729 g_test_add ("/tls/certificate_verify_root_not_found", Test, NULL,
730 setup, test_certificate_verify_root_not_found, teardown);
731 g_test_add ("/tls/certificate_verify_root_not_anchored", Test, NULL,
732 setup, test_certificate_verify_root_not_anchored, teardown);
733 g_test_add ("/tls/certificate_verify_identities_invalid", Test, NULL,
734 setup, test_certificate_verify_identities_invalid, teardown);
735 g_test_add ("/tls/certificate_verify_uses_reference_identities", Test, NULL,
736 setup, test_certificate_verify_uses_reference_identities, teardown);
737 g_test_add ("/tls/certificate_verify_success_with_pinned", Test, NULL,
738 setup, test_certificate_verify_success_with_pinned, teardown);
739 g_test_add ("/tls/certificate_verify_pinned_wrong_host", Test, NULL,
740 setup, test_certificate_verify_pinned_wrong_host, teardown);
742 result = g_test_run ();