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_free (self->cert_data, TRUE);
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_destroy (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 ();