-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
+#include "config.h"
-#include <libempathy/empathy-tls-certificate.h>
-#include "test-helper.h"
-
-#include <telepathy-glib/dbus-properties-mixin.h>
-#include <telepathy-glib/enums.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/svc-tls.h>
-#include <telepathy-glib/svc-generic.h>
+#include <gnutls/gnutls.h>
#include <telepathy-glib/telepathy-glib.h>
+#include <telepathy-glib/telepathy-glib-dbus.h>
+
+#include "empathy-tls-verifier.h"
+#include "mock-pkcs11.h"
+#include "test-helper.h"
#define MOCK_TLS_CERTIFICATE_PATH "/mock/certificate"
{
self->state = TP_TLS_CERTIFICATE_STATE_PENDING;
self->cert_type = g_strdup ("x509");
- self->cert_data = g_ptr_array_new_with_free_func((GDestroyNotify) g_array_unref);
+ self->cert_data = g_ptr_array_new_with_free_func ((GDestroyNotify)
+ g_array_unref);
self->rejections = g_ptr_array_new ();
}
static void
-mock_tls_certificate_get_property (GObject *object, guint property_id,
- GValue *value, GParamSpec *pspec)
+mock_tls_certificate_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
{
MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (object);
&self->rejections);
g_free (self->cert_type);
self->cert_type = NULL;
- g_ptr_array_free (self->cert_data, TRUE);
+ g_ptr_array_unref (self->cert_data);
self->cert_data = NULL;
G_OBJECT_CLASS (mock_tls_certificate_parent_class)->finalize (object);
pspec = g_param_spec_uint ("state",
"State of this certificate",
"The state of this TLS certificate.",
- 0, NUM_TP_TLS_CERTIFICATE_STATES - 1,
+ 0, TP_NUM_TLS_CERTIFICATE_STATES - 1,
TP_TLS_CERTIFICATE_STATE_PENDING,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (oclass, PROP_STATE, pspec);
static void
mock_tls_certificate_reject (TpSvcAuthenticationTLSCertificate *base,
- const GPtrArray *in_Rejections, DBusGMethodInvocation *context)
+ const GPtrArray *in_Rejections,
+ DBusGMethodInvocation *context)
{
MockTLSCertificate *self = MOCK_TLS_CERTIFICATE (base);
self->state = TP_TLS_CERTIFICATE_STATE_REJECTED;
}
static void
-mock_tls_certificate_iface_init (gpointer g_iface, gpointer iface_data)
+mock_tls_certificate_iface_init (gpointer g_iface,
+ gpointer iface_data)
{
TpSvcAuthenticationTLSCertificateClass *klass =
- (TpSvcAuthenticationTLSCertificateClass*)g_iface;
+ (TpSvcAuthenticationTLSCertificateClass *) g_iface;
tp_svc_authentication_tls_certificate_implement_accept (klass,
mock_tls_certificate_accept);
mock_tls_certificate_reject);
}
-static MockTLSCertificate*
-mock_tls_certificate_new_and_register (TpDBusDaemon *dbus, const gchar *path, ...)
+#if 0
+static void
+mock_tls_certificate_assert_rejected (MockTLSCertificate *self,
+ TpTLSCertificateRejectReason reason)
+{
+ GValueArray *rejection;
+ TpTLSCertificateRejectReason rejection_reason;
+ gchar *rejection_error;
+ GHashTable *rejection_details;
+ guint i;
+
+ g_assert (self->state == TP_TLS_CERTIFICATE_STATE_REJECTED);
+ g_assert (self->rejections);
+ g_assert (self->rejections->len > 0);
+
+ for (i = 0; i < self->rejections->len; ++i)
+ {
+ rejection = g_ptr_array_index (self->rejections, i);
+ tp_value_array_unpack (rejection, 3,
+ G_TYPE_UINT, &rejection_reason,
+ G_TYPE_STRING, &rejection_error,
+ TP_HASH_TYPE_STRING_VARIANT_MAP, &rejection_details,
+ NULL);
+ g_free (rejection_error);
+ g_hash_table_unref (rejection_details);
+
+ if (rejection_reason == reason)
+ return;
+ }
+
+ g_assert ("Certificate was not rejected for right reason" && 0);
+}
+#endif
+
+static MockTLSCertificate *
+mock_tls_certificate_new_and_register (TpDBusDaemon *dbus,
+ const gchar *path,
+ ...)
{
MockTLSCertificate *cert;
GError *error = NULL;
TpDBusDaemon *dbus;
const gchar *dbus_name;
MockTLSCertificate *mock;
+ TpTLSCertificate *cert;
+ GAsyncResult *result;
} Test;
static void
setup (Test *test, gconstpointer data)
{
GError *error = NULL;
+ GckModule *module;
+ const gchar *trust_uris[2] = { MOCK_SLOT_ONE_URI, NULL };
+
test->loop = g_main_loop_new (NULL, FALSE);
test->dbus = tp_dbus_daemon_dup (&error);
g_assert_no_error (error);
test->dbus_name = tp_dbus_daemon_get_unique_name (test->dbus);
+
+ test->result = NULL;
+ test->cert = NULL;
+
+ /* Add our mock module as the only PKCS#11 module */
+ module = gck_module_new (&mock_default_functions);
+ mock_C_Initialize (NULL);
+
+ gcr_pkcs11_set_modules (NULL);
+ gcr_pkcs11_add_module (module);
+ gcr_pkcs11_set_trust_lookup_uris (trust_uris);
}
static void
teardown (Test *test, gconstpointer data)
{
+ mock_C_Finalize (NULL);
+
test->dbus_name = NULL;
if (test->mock)
test->mock = NULL;
}
+ if (test->result)
+ g_object_unref (test->result);
+ test->result = NULL;
+
+ if (test->cert)
+ g_object_unref (test->cert);
+ test->cert = NULL;
+
g_main_loop_unref (test->loop);
test->loop = NULL;
+
+ g_object_unref (test->dbus);
+ test->dbus = NULL;
}
static void
-accepted_callback (GObject *object, GAsyncResult *res, gpointer user_data)
+add_certificate_to_mock (Test *test,
+ const gchar *certificate,
+ const gchar *peer)
{
GError *error = NULL;
- Test *test = user_data;
+ GcrCertificate *cert;
+ gchar *contents;
+ gsize length;
+ gchar *path;
- g_assert (EMPATHY_IS_TLS_CERTIFICATE (object));
- empathy_tls_certificate_accept_finish (EMPATHY_TLS_CERTIFICATE (object),
- res, &error);
+ path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"),
+ "tests", "certificates", certificate, NULL);
+
+ g_file_get_contents (path, &contents, &length, &error);
g_assert_no_error (error);
+ cert = gcr_simple_certificate_new ((const guchar *)contents, length);
+ mock_module_add_certificate (cert);
+ mock_module_add_assertion (cert,
+ peer ? CKT_X_PINNED_CERTIFICATE : CKT_X_ANCHORED_CERTIFICATE,
+ GCR_PURPOSE_SERVER_AUTH, peer);
+ g_object_unref (cert);
+
+ g_free (contents);
+ g_free (path);
+}
+
+static void
+fetch_callback_result (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ Test *test = user_data;
+ g_assert (!test->result);
+ test->result = g_object_ref (res);
g_main_loop_quit (test->loop);
}
+static void
+ensure_certificate_proxy (Test *test)
+{
+ GError *error = NULL;
+ GQuark features[] = { TP_TLS_CERTIFICATE_FEATURE_CORE, 0 };
+
+ if (test->cert)
+ return;
+
+ /* Create and prepare a certificate */
+ /* We don't use tp_tls_certificate_new() as we don't pass a parent */
+ test->cert = g_object_new (TP_TYPE_TLS_CERTIFICATE,
+ "dbus-daemon", test->dbus,
+ "bus-name", test->dbus_name,
+ "object-path", MOCK_TLS_CERTIFICATE_PATH,
+ NULL);
+
+ tp_proxy_prepare_async (test->cert, features, fetch_callback_result, test);
+ g_main_loop_run (test->loop);
+ tp_proxy_prepare_finish (test->cert, test->result, &error);
+ g_assert_no_error (error);
+
+ /* Clear for any future async stuff */
+ g_object_unref (test->result);
+ test->result = NULL;
+}
+
/* A simple test to make sure the test infrastructure is working */
static void
-test_certificate_mock_accept (Test *test, gconstpointer data G_GNUC_UNUSED)
+test_certificate_mock_basics (Test *test,
+ gconstpointer data G_GNUC_UNUSED)
{
GError *error = NULL;
- EmpathyTLSCertificate *cert;
test->mock = mock_tls_certificate_new_and_register (test->dbus,
- "dhansak-collabora.cer");
+ "server-cert.cer", NULL);
- cert = empathy_tls_certificate_new (test->dbus, test->dbus_name,
- MOCK_TLS_CERTIFICATE_PATH, &error);
+ ensure_certificate_proxy (test);
+
+ tp_tls_certificate_accept_async (test->cert, fetch_callback_result, test);
+ g_main_loop_run (test->loop);
+ tp_tls_certificate_accept_finish (test->cert, test->result, &error);
g_assert_no_error (error);
- empathy_tls_certificate_accept_async (cert, accepted_callback, test);
- g_object_unref (cert);
+ g_assert (test->mock->state == TP_TLS_CERTIFICATE_STATE_ACCEPTED);
+}
+
+static void
+test_certificate_verify_success_with_pkcs11_lookup (Test *test,
+ gconstpointer data G_GNUC_UNUSED)
+{
+ TpTLSCertificateRejectReason reason = 0;
+ GError *error = NULL;
+ EmpathyTLSVerifier *verifier;
+ const gchar *reference_identities[] = {
+ "test-server.empathy.gnome.org",
+ NULL
+ };
+
+ /*
+ * In this test the mock TLS connection only has one certificate
+ * not a full certificat echain. The root anchor certificate is
+ * retrieved from PKCS#11 storage.
+ */
+
+ test->mock = mock_tls_certificate_new_and_register (test->dbus,
+ "server-cert.cer", NULL);
+
+ /* We add the collabora directory with the collabora root */
+ add_certificate_to_mock (test, "certificate-authority.cer", NULL);
+ ensure_certificate_proxy (test);
+
+ verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
+ reference_identities);
+ empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
g_main_loop_run (test->loop);
- g_assert (test->mock->state == TP_TLS_CERTIFICATE_STATE_ACCEPTED);
+ empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+ NULL, &error);
+ g_assert_no_error (error);
+
+ /* Yay the verification was a success! */
+
+ g_clear_error (&error);
+ g_object_unref (verifier);
}
static void
-prepared_callback (GObject *object, GAsyncResult *res, gpointer user_data)
+test_certificate_verify_success_with_full_chain (Test *test,
+ gconstpointer data G_GNUC_UNUSED)
{
+ TpTLSCertificateRejectReason reason = 0;
GError *error = NULL;
- Test *test = user_data;
+ EmpathyTLSVerifier *verifier;
+ const gchar *reference_identities[] = {
+ "test-server.empathy.gnome.org",
+ NULL
+ };
+
+ /*
+ * In this test the mock TLS connection has a full certificate
+ * chain. We look for an anchor certificate in the chain.
+ */
+
+ test->mock = mock_tls_certificate_new_and_register (test->dbus,
+ "server-cert.cer", "certificate-authority.cer", NULL);
- g_assert (EMPATHY_IS_TLS_CERTIFICATE (object));
- empathy_tls_certificate_prepare_finish (EMPATHY_TLS_CERTIFICATE (object),
- res, &error);
+ /* We add the collabora directory with the collabora root */
+ add_certificate_to_mock (test, "certificate-authority.cer", NULL);
+
+ ensure_certificate_proxy (test);
+
+ verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
+ reference_identities);
+ empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
+ g_main_loop_run (test->loop);
+ empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+ NULL, &error);
g_assert_no_error (error);
- /* Stop the tests */
- g_main_loop_quit (test->loop);
+ /* Yay the verification was a success! */
+
+ g_clear_error (&error);
+ g_object_unref (verifier);
+}
+
+static void
+test_certificate_verify_root_not_found (Test *test,
+ gconstpointer data G_GNUC_UNUSED)
+{
+ TpTLSCertificateRejectReason reason = 0;
+ GError *error = NULL;
+ EmpathyTLSVerifier *verifier;
+ const gchar *reference_identities[] = {
+ "test-server.empathy.gnome.org",
+ NULL
+ };
+
+ test->mock = mock_tls_certificate_new_and_register (test->dbus,
+ "server-cert.cer", NULL);
+
+ /* Note that we're not adding any place to find root certs */
+
+ ensure_certificate_proxy (test);
+
+ verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
+ reference_identities);
+ empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
+ g_main_loop_run (test->loop);
+
+ empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+ NULL, &error);
+
+ /* And it should say we're self-signed (oddly enough) */
+ g_assert_error (error, G_IO_ERROR,
+ TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
+
+ g_clear_error (&error);
+ g_object_unref (verifier);
+}
+
+static void
+test_certificate_verify_root_not_anchored (Test *test,
+ gconstpointer data G_GNUC_UNUSED)
+{
+ TpTLSCertificateRejectReason reason = 0;
+ GError *error = NULL;
+ EmpathyTLSVerifier *verifier;
+ const gchar *reference_identities[] = {
+ "test-server.empathy.gnome.org",
+ NULL
+ };
+
+ test->mock = mock_tls_certificate_new_and_register (test->dbus,
+ "server-cert.cer", "certificate-authority.cer", NULL);
+
+ /* Note that we're not adding any place to find root certs */
+
+ ensure_certificate_proxy (test);
+
+ verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
+ reference_identities);
+ empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
+ g_main_loop_run (test->loop);
+
+ empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+ NULL, &error);
+
+ /* And it should say we're self-signed (oddly enough) */
+ g_assert_error (error, G_IO_ERROR,
+ TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
+
+ g_clear_error (&error);
+ g_object_unref (verifier);
+}
+
+static void
+test_certificate_verify_identities_invalid (Test *test,
+ gconstpointer data G_GNUC_UNUSED)
+{
+ TpTLSCertificateRejectReason reason = 0;
+ GError *error = NULL;
+ EmpathyTLSVerifier *verifier;
+ const gchar *reference_identities[] = {
+ "invalid.host.name",
+ NULL
+ };
+
+ test->mock = mock_tls_certificate_new_and_register (test->dbus,
+ "server-cert.cer", "certificate-authority.cer", NULL);
+
+ /* We add the collabora directory with the collabora root */
+ add_certificate_to_mock (test, "certificate-authority.cer", NULL);
+
+ ensure_certificate_proxy (test);
+
+ verifier = empathy_tls_verifier_new (test->cert, "invalid.host.name",
+ reference_identities);
+ empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
+ g_main_loop_run (test->loop);
+
+ empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+ NULL, &error);
+
+ /* And it should say we're self-signed (oddly enough) */
+ g_assert_error (error, G_IO_ERROR,
+ TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
+
+ g_clear_error (&error);
+ g_object_unref (verifier);
}
static void
-test_certificate_prepare (Test *test, gconstpointer data G_GNUC_UNUSED)
+test_certificate_verify_uses_reference_identities (Test *test,
+ gconstpointer data G_GNUC_UNUSED)
{
+ TpTLSCertificateRejectReason reason = 0;
GError *error = NULL;
- EmpathyTLSCertificate *cert;
+ EmpathyTLSVerifier *verifier;
+ const gchar *reference_identities[] = {
+ "invalid.host.name",
+ NULL
+ };
+
+ test->mock = mock_tls_certificate_new_and_register (test->dbus,
+ "server-cert.cer", "certificate-authority.cer", NULL);
+
+ /* We add the collabora directory with the collabora root */
+ add_certificate_to_mock (test, "certificate-authority.cer", NULL);
+
+ ensure_certificate_proxy (test);
+
+ /* Should be using the reference_identities and not host name for checks */
+ verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
+ reference_identities);
+ empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
+ g_main_loop_run (test->loop);
+
+ empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+ NULL, &error);
+
+ /* And it should say we're self-signed (oddly enough) */
+ g_assert_error (error, G_IO_ERROR,
+ TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
+
+ g_clear_error (&error);
+ g_object_unref (verifier);
+}
+
+static void
+test_certificate_verify_success_with_pinned (Test *test,
+ gconstpointer data G_GNUC_UNUSED)
+{
+ TpTLSCertificateRejectReason reason = 0;
+ GError *error = NULL;
+ EmpathyTLSVerifier *verifier;
+ const gchar *reference_identities[] = {
+ "test-server.empathy.gnome.org",
+ NULL
+ };
+
+ /*
+ * In this test the mock TLS connection has a full certificate
+ * chain. We look for an anchor certificate in the chain.
+ */
test->mock = mock_tls_certificate_new_and_register (test->dbus,
- "dhansak-collabora.cer");
+ "server-cert.cer", NULL);
+
+ /* We add the collabora directory with the collabora root */
+ add_certificate_to_mock (test, "server-cert.cer", "test-server.empathy.gnome.org");
+
+ ensure_certificate_proxy (test);
- cert = empathy_tls_certificate_new (test->dbus, test->dbus_name,
- MOCK_TLS_CERTIFICATE_PATH, &error);
+ verifier = empathy_tls_verifier_new (test->cert, "test-server.empathy.gnome.org",
+ reference_identities);
+ empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
+ g_main_loop_run (test->loop);
+ empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+ NULL, &error);
g_assert_no_error (error);
- empathy_tls_certificate_prepare_async (cert, prepared_callback, test);
+ /* Yay the verification was a success! */
+
+ g_clear_error (&error);
+ g_object_unref (verifier);
+}
+static void
+test_certificate_verify_pinned_wrong_host (Test *test,
+ gconstpointer data G_GNUC_UNUSED)
+{
+ TpTLSCertificateRejectReason reason = 0;
+ GError *error = NULL;
+ EmpathyTLSVerifier *verifier;
+ const gchar *reference_identities[] = {
+ "test-server.empathy.gnome.org",
+ NULL
+ };
+
+ test->mock = mock_tls_certificate_new_and_register (test->dbus,
+ "server-cert.cer", NULL);
+
+ /* Note that we're not adding any place to find root certs */
+
+ ensure_certificate_proxy (test);
+
+ verifier = empathy_tls_verifier_new (test->cert, "another.gnome.org",
+ reference_identities);
+ empathy_tls_verifier_verify_async (verifier, fetch_callback_result, test);
g_main_loop_run (test->loop);
- g_object_unref (cert);
+ empathy_tls_verifier_verify_finish (verifier, test->result, &reason,
+ NULL, &error);
+
+ /* And it should say we're self-signed */
+ g_assert_error (error, G_IO_ERROR,
+ TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED);
+
+ g_clear_error (&error);
+ g_object_unref (verifier);
}
int
int result;
test_init (argc, argv);
-
- g_test_add ("/tls/certificate_accept", Test, NULL,
- setup, test_certificate_mock_accept, teardown);
- g_test_add ("/tls/certificate_prepare", Test, NULL,
- setup, test_certificate_prepare, teardown);
+ gnutls_global_init ();
+
+ g_test_add ("/tls/certificate_basics", Test, NULL,
+ setup, test_certificate_mock_basics, teardown);
+ g_test_add ("/tls/certificate_verify_success_with_pkcs11_lookup", Test, NULL,
+ setup, test_certificate_verify_success_with_pkcs11_lookup, teardown);
+ g_test_add ("/tls/certificate_verify_success_with_full_chain", Test, NULL,
+ setup, test_certificate_verify_success_with_full_chain, teardown);
+ g_test_add ("/tls/certificate_verify_root_not_found", Test, NULL,
+ setup, test_certificate_verify_root_not_found, teardown);
+ g_test_add ("/tls/certificate_verify_root_not_anchored", Test, NULL,
+ setup, test_certificate_verify_root_not_anchored, teardown);
+ g_test_add ("/tls/certificate_verify_identities_invalid", Test, NULL,
+ setup, test_certificate_verify_identities_invalid, teardown);
+ g_test_add ("/tls/certificate_verify_uses_reference_identities", Test, NULL,
+ setup, test_certificate_verify_uses_reference_identities, teardown);
+ g_test_add ("/tls/certificate_verify_success_with_pinned", Test, NULL,
+ setup, test_certificate_verify_success_with_pinned, teardown);
+ g_test_add ("/tls/certificate_verify_pinned_wrong_host", Test, NULL,
+ setup, test_certificate_verify_pinned_wrong_host, teardown);
result = g_test_run ();
test_deinit ();