5 #include <libempathy/empathy-tls-certificate.h>
6 #include <libempathy/empathy-tls-verifier.h>
7 #include "test-helper.h"
11 #include <gnutls/gnutls.h>
13 #include <telepathy-glib/dbus-properties-mixin.h>
14 #include <telepathy-glib/enums.h>
15 #include <telepathy-glib/interfaces.h>
16 #include <telepathy-glib/svc-tls.h>
17 #include <telepathy-glib/svc-generic.h>
18 #include <telepathy-glib/telepathy-glib.h>
20 #define MOCK_TLS_CERTIFICATE_PATH "/mock/certificate"
23 GType mock_tls_certificate_get_type (void);
25 #define MOCK_TLS_CERTIFICATE(obj) \
26 (G_TYPE_CHECK_INSTANCE_CAST((obj), mock_tls_certificate_get_type (), \
29 typedef struct _MockTLSCertificate {
32 GPtrArray *rejections;
37 typedef struct _MockTLSCertificateClass {
39 TpDBusPropertiesMixinClass dbus_props_class;
40 } MockTLSCertificateClass;
46 PROP_CERTIFICATE_TYPE,
47 PROP_CERTIFICATE_CHAIN_DATA
50 static void mock_tls_certificate_iface_init (gpointer, gpointer);
52 G_DEFINE_TYPE_WITH_CODE(MockTLSCertificate, mock_tls_certificate, G_TYPE_OBJECT,
53 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_AUTHENTICATION_TLS_CERTIFICATE,
54 mock_tls_certificate_iface_init)
55 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
56 tp_dbus_properties_mixin_iface_init)
60 mock_tls_certificate_init (MockTLSCertificate *self)
62 self->state = TP_TLS_CERTIFICATE_STATE_PENDING;
63 self->cert_type = g_strdup ("x509");
64 self->cert_data = g_ptr_array_new_with_free_func((GDestroyNotify) g_array_unref);
65 self->rejections = g_ptr_array_new ();
69 mock_tls_certificate_get_property (GObject *object,
74 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (object);
79 g_value_set_uint (value, self->state);
82 g_value_set_boxed (value, self->rejections);
84 case PROP_CERTIFICATE_TYPE:
85 g_value_set_string (value, self->cert_type);
87 case PROP_CERTIFICATE_CHAIN_DATA:
88 g_value_set_boxed (value, self->cert_data);
91 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
97 mock_tls_certificate_finalize (GObject *object)
99 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (object);
101 tp_clear_boxed (TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
103 g_free (self->cert_type);
104 self->cert_type = NULL;
105 g_ptr_array_unref (self->cert_data);
106 self->cert_data = NULL;
108 G_OBJECT_CLASS (mock_tls_certificate_parent_class)->finalize (object);
112 mock_tls_certificate_class_init (MockTLSCertificateClass *klass)
114 GObjectClass *oclass = G_OBJECT_CLASS (klass);
117 static TpDBusPropertiesMixinPropImpl object_props[] = {
118 { "State", "state", NULL },
119 { "Rejections", "rejections", NULL },
120 { "CertificateType", "certificate-type", NULL },
121 { "CertificateChainData", "certificate-chain-data", NULL },
125 static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
126 { TP_IFACE_AUTHENTICATION_TLS_CERTIFICATE,
127 tp_dbus_properties_mixin_getter_gobject_properties,
134 oclass->get_property = mock_tls_certificate_get_property;
135 oclass->finalize = mock_tls_certificate_finalize;
137 pspec = g_param_spec_uint ("state",
138 "State of this certificate",
139 "The state of this TLS certificate.",
140 0, NUM_TP_TLS_CERTIFICATE_STATES - 1,
141 TP_TLS_CERTIFICATE_STATE_PENDING,
142 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
143 g_object_class_install_property (oclass, PROP_STATE, pspec);
145 pspec = g_param_spec_boxed ("rejections",
146 "The reject reasons",
147 "The reasons why this TLS certificate has been rejected",
148 TP_ARRAY_TYPE_TLS_CERTIFICATE_REJECTION_LIST,
149 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
150 g_object_class_install_property (oclass, PROP_REJECTIONS, pspec);
152 pspec = g_param_spec_string ("certificate-type",
153 "The certificate type",
154 "The type of this certificate.",
156 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
157 g_object_class_install_property (oclass, PROP_CERTIFICATE_TYPE, pspec);
159 pspec = g_param_spec_boxed ("certificate-chain-data",
160 "The certificate chain data",
161 "The raw PEM-encoded trust chain of this certificate.",
162 TP_ARRAY_TYPE_UCHAR_ARRAY_LIST,
163 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
164 g_object_class_install_property (oclass, PROP_CERTIFICATE_CHAIN_DATA, pspec);
166 klass->dbus_props_class.interfaces = prop_interfaces;
167 tp_dbus_properties_mixin_class_init (oclass,
168 G_STRUCT_OFFSET (MockTLSCertificateClass, dbus_props_class));
172 mock_tls_certificate_accept (TpSvcAuthenticationTLSCertificate *base,
173 DBusGMethodInvocation *context)
175 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (base);
176 self->state = TP_TLS_CERTIFICATE_STATE_ACCEPTED;
177 tp_svc_authentication_tls_certificate_emit_accepted (self);
178 tp_svc_authentication_tls_certificate_return_from_accept (context);
182 mock_tls_certificate_reject (TpSvcAuthenticationTLSCertificate *base,
183 const GPtrArray *in_Rejections,
184 DBusGMethodInvocation *context)
186 MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (base);
187 self->state = TP_TLS_CERTIFICATE_STATE_REJECTED;
188 tp_svc_authentication_tls_certificate_emit_rejected (self, in_Rejections);
189 tp_svc_authentication_tls_certificate_return_from_reject (context);
193 mock_tls_certificate_iface_init (gpointer g_iface,
196 TpSvcAuthenticationTLSCertificateClass *klass =
197 (TpSvcAuthenticationTLSCertificateClass*)g_iface;
199 tp_svc_authentication_tls_certificate_implement_accept (klass,
200 mock_tls_certificate_accept);
201 tp_svc_authentication_tls_certificate_implement_reject (klass,
202 mock_tls_certificate_reject);
207 mock_tls_certificate_assert_rejected (MockTLSCertificate *self,
208 EmpTLSCertificateRejectReason reason)
210 GValueArray *rejection;
211 EmpTLSCertificateRejectReason rejection_reason;
212 gchar *rejection_error;
213 GHashTable *rejection_details;
216 g_assert (self->state == TP_TLS_CERTIFICATE_STATE_REJECTED);
217 g_assert (self->rejections);
218 g_assert (self->rejections->len > 0);
220 for (i = 0; i < self->rejections->len; ++i)
222 rejection = g_ptr_array_index (self->rejections, i);
223 tp_value_array_unpack (rejection, 3,
224 G_TYPE_UINT, &rejection_reason,
225 G_TYPE_STRING, &rejection_error,
226 TP_HASH_TYPE_STRING_VARIANT_MAP, &rejection_details,
228 g_free (rejection_error);
229 g_hash_table_unref (rejection_details);
231 if (rejection_reason == reason)
235 g_assert ("Certificate was not rejected for right reason" && 0);
239 static MockTLSCertificate*
240 mock_tls_certificate_new_and_register (TpDBusDaemon *dbus,
244 MockTLSCertificate *cert;
245 GError *error = NULL;
246 gchar *filename, *contents;
251 cert = g_object_new (mock_tls_certificate_get_type (), NULL);
254 while (path != NULL) {
255 filename = g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
256 "tests", "certificates", path, NULL);
257 g_file_get_contents (filename, &contents, &length, &error);
258 g_assert_no_error (error);
260 der = g_array_sized_new (TRUE, TRUE, sizeof (guchar), length);
261 g_array_append_vals (der, contents, length);
262 g_ptr_array_add (cert->cert_data, der);
267 path = va_arg (va, gchar*);
271 tp_dbus_daemon_register_object (dbus, MOCK_TLS_CERTIFICATE_PATH, cert);
275 /* ----------------------------------------------------------------------------
282 const gchar *dbus_name;
283 MockTLSCertificate *mock;
284 EmpathyTLSCertificate *cert;
285 GAsyncResult *result;
289 setup (Test *test, gconstpointer data)
291 GError *error = NULL;
292 test->loop = g_main_loop_new (NULL, FALSE);
294 test->dbus = tp_dbus_daemon_dup (&error);
295 g_assert_no_error (error);
297 test->dbus_name = tp_dbus_daemon_get_unique_name (test->dbus);
302 /* No PKCS#11 modules by default, tests add them */
303 gcr_pkcs11_set_modules (NULL);
307 teardown (Test *test, gconstpointer data)
309 test->dbus_name = NULL;
313 tp_dbus_daemon_unregister_object (test->dbus, test->mock);
314 g_object_unref (test->mock);
319 g_object_unref (test->result);
323 g_object_unref (test->cert);
326 g_main_loop_unref (test->loop);
329 g_object_unref (test->dbus);
334 add_pkcs11_module_for_testing (Test *test,
335 const gchar *filename,
338 GError *error = NULL;
339 gchar *args, *path, *directory;
340 gchar *standalone, *error_output;
343 directory = g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
344 "tests", "certificates", subdir, NULL);
347 * Lookup the directory for standalone pkcs11 modules installed by
348 * gnome-keyring. We use these for testing our implementation.
350 g_spawn_command_line_sync ("pkg-config --variable=pkcs11standalonedir gcr-3",
351 &standalone, &error_output, &exit_status, &error);
352 g_assert_no_error (error);
353 if (exit_status != 0)
355 g_warning ("couldn't determine standalone pkcs11 module directory: %d: %s",
356 exit_status, error_output);
357 g_assert_not_reached ();
360 g_strstrip (standalone);
361 args = g_strdup_printf ("directory=\"%s\"", directory);
362 path = g_build_filename (standalone, filename, NULL);
363 gcr_pkcs11_add_module_from_file (path, args, &error);
364 g_assert_no_error (error);
368 g_free (error_output);
374 fetch_callback_result (GObject *object,
378 Test *test = user_data;
379 g_assert (!test->result);
380 test->result = g_object_ref (res);
381 g_main_loop_quit (test->loop);
385 ensure_certificate_proxy (Test *test)
387 GError *error = NULL;
392 /* Create and prepare a certificate */
393 test->cert = empathy_tls_certificate_new (test->dbus, test->dbus_name,
394 MOCK_TLS_CERTIFICATE_PATH, &error);
395 g_assert_no_error (error);
396 empathy_tls_certificate_prepare_async (test->cert, fetch_callback_result, test);
397 g_main_loop_run (test->loop);
398 empathy_tls_certificate_prepare_finish (test->cert, test->result, &error);
399 g_assert_no_error (error);
401 /* Clear for any future async stuff */
402 g_object_unref (test->result);
406 /* A simple test to make sure the test infrastructure is working */
408 test_certificate_mock_basics (Test *test,
409 gconstpointer data G_GNUC_UNUSED)
411 GError *error = NULL;
413 test->mock = mock_tls_certificate_new_and_register (test->dbus,
414 "dhansak-collabora.cer", NULL);
416 ensure_certificate_proxy (test);
418 empathy_tls_certificate_accept_async (test->cert, fetch_callback_result, test);
419 g_main_loop_run (test->loop);
420 empathy_tls_certificate_accept_finish (test->cert, test->result, &error);
421 g_assert_no_error (error);
423 g_assert (test->mock->state == TP_TLS_CERTIFICATE_STATE_ACCEPTED);
427 test_certificate_verify_success_with_pkcs11_lookup (Test *test,
428 gconstpointer data G_GNUC_UNUSED)
430 EmpTLSCertificateRejectReason reason = 0;
431 GError *error = NULL;
432 EmpathyTLSVerifier *verifier;
433 const gchar *reference_identities[] = {
434 "www.collabora.co.uk",
439 * In this test the mock TLS connection only has one certificate
440 * not a full certificat echain. The root anchor certificate is
441 * retrieved from PKCS#11 storage.
444 test->mock = mock_tls_certificate_new_and_register (test->dbus,
445 "dhansak-collabora.cer", NULL);
447 /* We add the collabora directory with the collabora root */
448 add_pkcs11_module_for_testing (test, "gkm-roots-store-standalone.so",
451 ensure_certificate_proxy (test);
453 verifier = empathy_tls_verifier_new (test->cert, "www.collabora.co.uk",
454 reference_identities);
455 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
456 g_main_loop_run (test->loop);
457 if (!empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
459 g_assert_not_reached ();
461 /* Yay the verification was a success! */
463 g_clear_error (&error);
464 g_object_unref (verifier);
468 test_certificate_verify_success_with_full_chain (Test *test,
469 gconstpointer data G_GNUC_UNUSED)
471 EmpTLSCertificateRejectReason reason = 0;
472 GError *error = NULL;
473 EmpathyTLSVerifier *verifier;
474 const gchar *reference_identities[] = {
475 "www.collabora.co.uk",
480 * In this test the mock TLS connection has a full certificate
481 * chain. We look for an anchor certificate in the chain.
484 test->mock = mock_tls_certificate_new_and_register (test->dbus,
485 "dhansak-collabora.cer", "collabora-ca/collabora-ca.cer", NULL);
487 /* We add the collabora directory with the collabora root */
488 add_pkcs11_module_for_testing (test, "gkm-roots-store-standalone.so",
491 ensure_certificate_proxy (test);
493 verifier = empathy_tls_verifier_new (test->cert, "www.collabora.co.uk",
494 reference_identities);
495 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
496 g_main_loop_run (test->loop);
497 if (!empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
499 g_assert_not_reached ();
501 /* Yay the verification was a success! */
503 g_clear_error (&error);
504 g_object_unref (verifier);
508 test_certificate_verify_root_not_found (Test *test,
509 gconstpointer data G_GNUC_UNUSED)
511 EmpTLSCertificateRejectReason reason = 0;
512 GError *error = NULL;
513 EmpathyTLSVerifier *verifier;
514 const gchar *reference_identities[] = {
515 "www.collabora.co.uk",
519 test->mock = mock_tls_certificate_new_and_register (test->dbus,
520 "dhansak-collabora.cer", NULL);
522 /* Note that we're not adding any place to find root certs */
524 ensure_certificate_proxy (test);
526 verifier = empathy_tls_verifier_new (test->cert, "www.collabora.co.uk",
527 reference_identities);
528 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
529 g_main_loop_run (test->loop);
531 if (empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
533 g_assert_not_reached ();
535 /* And it should say we're self-signed (oddly enough) */
536 g_assert_cmpuint (reason, ==, EMP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
538 g_clear_error (&error);
539 g_object_unref (verifier);
543 test_certificate_verify_root_not_anchored (Test *test,
544 gconstpointer data G_GNUC_UNUSED)
546 EmpTLSCertificateRejectReason reason = 0;
547 GError *error = NULL;
548 EmpathyTLSVerifier *verifier;
549 const gchar *reference_identities[] = {
550 "www.collabora.co.uk",
554 test->mock = mock_tls_certificate_new_and_register (test->dbus,
555 "dhansak-collabora.cer", "collabora-ca/collabora-ca.cer", NULL);
557 /* Note that we're not adding any place to find root certs */
559 ensure_certificate_proxy (test);
561 verifier = empathy_tls_verifier_new (test->cert, "www.collabora.co.uk",
562 reference_identities);
563 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
564 g_main_loop_run (test->loop);
566 if (empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
568 g_assert_not_reached ();
570 /* And it should say we're self-signed (oddly enough) */
571 g_assert_cmpuint (reason, ==, EMP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
573 g_clear_error (&error);
574 g_object_unref (verifier);
578 test_certificate_verify_identities_invalid (Test *test,
579 gconstpointer data G_GNUC_UNUSED)
581 EmpTLSCertificateRejectReason reason = 0;
582 GError *error = NULL;
583 EmpathyTLSVerifier *verifier;
584 const gchar *reference_identities[] = {
589 test->mock = mock_tls_certificate_new_and_register (test->dbus,
590 "dhansak-collabora.cer", "collabora-ca/collabora-ca.cer", NULL);
592 /* We add the collabora directory with the collabora root */
593 add_pkcs11_module_for_testing (test, "gkm-roots-store-standalone.so",
596 ensure_certificate_proxy (test);
598 verifier = empathy_tls_verifier_new (test->cert, "invalid.host.name",
599 reference_identities);
600 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
601 g_main_loop_run (test->loop);
603 if (empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
605 g_assert_not_reached ();
607 /* And it should say we're self-signed (oddly enough) */
608 g_assert_cmpuint (reason, ==, EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
610 g_clear_error (&error);
611 g_object_unref (verifier);
615 test_certificate_verify_uses_reference_identities (Test *test,
616 gconstpointer data G_GNUC_UNUSED)
618 EmpTLSCertificateRejectReason reason = 0;
619 GError *error = NULL;
620 EmpathyTLSVerifier *verifier;
621 const gchar *reference_identities[] = {
626 test->mock = mock_tls_certificate_new_and_register (test->dbus,
627 "dhansak-collabora.cer", "collabora-ca/collabora-ca.cer", NULL);
629 /* We add the collabora directory with the collabora root */
630 add_pkcs11_module_for_testing (test, "gkm-roots-store-standalone.so",
633 ensure_certificate_proxy (test);
635 /* Should be using the reference_identities and not host name for checks */
636 verifier = empathy_tls_verifier_new (test->cert, "www.collabora.co.uk",
637 reference_identities);
638 empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
639 g_main_loop_run (test->loop);
641 if (empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
643 g_assert_not_reached ();
645 /* And it should say we're self-signed (oddly enough) */
646 g_assert_cmpuint (reason, ==, EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
648 g_clear_error (&error);
649 g_object_unref (verifier);
658 test_init (argc, argv);
659 gnutls_global_init ();
661 g_test_add ("/tls/certificate_basics", Test, NULL,
662 setup, test_certificate_mock_basics, teardown);
663 g_test_add ("/tls/certificate_verify_success_with_pkcs11_lookup", Test, NULL,
664 setup, test_certificate_verify_success_with_pkcs11_lookup, teardown);
665 g_test_add ("/tls/certificate_verify_success_with_full_chain", Test, NULL,
666 setup, test_certificate_verify_success_with_full_chain, teardown);
667 g_test_add ("/tls/certificate_verify_root_not_found", Test, NULL,
668 setup, test_certificate_verify_root_not_found, teardown);
669 g_test_add ("/tls/certificate_verify_root_not_anchored", Test, NULL,
670 setup, test_certificate_verify_root_not_anchored, teardown);
671 g_test_add ("/tls/certificate_verify_identities_invalid", Test, NULL,
672 setup, test_certificate_verify_identities_invalid, teardown);
673 g_test_add ("/tls/certificate_verify_uses_reference_identities", Test, NULL,
674 setup, test_certificate_verify_uses_reference_identities, teardown);
676 result = g_test_run ();