]> git.0d.be Git - empathy.git/blobdiff - tests/empathy-tls-test.c
Updated Galician translations for docs
[empathy.git] / tests / empathy-tls-test.c
index 8da2cd0025d6d3b3ae57203b489b620f045b4bc3..91b05761f9b9ebb44157bc40ffb4606c4f3650e7 100644 (file)
@@ -1,16 +1,12 @@
-#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"
 
@@ -56,13 +52,16 @@ mock_tls_certificate_init (MockTLSCertificate *self)
 {
   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);
 
@@ -95,7 +94,7 @@ mock_tls_certificate_finalize (GObject *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);
@@ -130,7 +129,7 @@ mock_tls_certificate_class_init (MockTLSCertificateClass *klass)
   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);
@@ -173,7 +172,8 @@ mock_tls_certificate_accept (TpSvcAuthenticationTLSCertificate *base,
 
 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;
@@ -182,10 +182,11 @@ mock_tls_certificate_reject (TpSvcAuthenticationTLSCertificate *base,
 }
 
 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);
@@ -193,8 +194,44 @@ mock_tls_certificate_iface_init (gpointer g_iface, gpointer iface_data)
       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;
@@ -236,23 +273,41 @@ typedef struct {
   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)
@@ -262,79 +317,402 @@ teardown (Test *test, gconstpointer data)
       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
@@ -344,11 +722,26 @@ main (int argc,
   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 ();