140
verify_last_certificate (EmpathyTLSVerifier *self,
141
gnutls_x509_crt_t cert,
142
EmpTLSCertificateRejectReason *reason)
117
build_certificate_list_for_gnutls (GcrCertificateChain *chain,
118
gnutls_x509_crt_t **list,
120
gnutls_x509_crt_t **anchors,
146
gnutls_x509_crt_t *trusted_ca_list;
147
EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
149
if (priv->trusted_ca_list->len > 0)
151
trusted_ca_list = ptr_array_to_x509_crt_list (priv->trusted_ca_list);
152
res = gnutls_x509_crt_verify (cert, trusted_ca_list,
153
priv->trusted_ca_list->len, 0, &verify_output);
155
DEBUG ("Checking last certificate %p against trusted CAs, output %u",
156
cert, verify_output);
158
g_free (trusted_ca_list);
162
/* check it against itself to see if it's structurally valid */
163
res = gnutls_x509_crt_verify (cert, &cert, 1, 0, &verify_output);
165
DEBUG ("Checking last certificate %p against itself, output %u", cert,
168
/* if it's valid, return the SelfSigned error, so that we can add it
169
* later to our trusted CAs whitelist.
171
if (res == GNUTLS_E_SUCCESS)
173
*reason = EMP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED;
178
return verification_output_to_reason (res, verify_output, reason);
123
GcrCertificate *cert;
125
gnutls_x509_crt_t *retval;
126
gnutls_x509_crt_t gcert;
127
gnutls_datum_t datum;
133
g_assert (n_anchors);
135
*list = *anchors = NULL;
136
*n_list = *n_anchors = 0;
138
length = gcr_certificate_chain_get_length (chain);
139
retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * length);
141
/* Convert the main body of the chain to gnutls */
142
for (idx = 0; idx < length; ++idx)
144
cert = gcr_certificate_chain_get_certificate (chain, idx);
145
datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
148
gnutls_x509_crt_init (&gcert);
149
if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
150
g_return_if_reached ();
158
/* See if we have an anchor */
159
if (gcr_certificate_chain_get_status (chain) ==
160
GCR_CERTIFICATE_CHAIN_ANCHORED)
162
cert = gcr_certificate_chain_get_anchor (chain);
163
g_return_if_fail (cert);
165
datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
168
gnutls_x509_crt_init (&gcert);
169
if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
170
g_return_if_reached ();
172
retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * 1);
182
verify_certificate (EmpathyTLSVerifier *self,
183
gnutls_x509_crt_t cert,
184
gnutls_x509_crt_t issuer,
185
EmpTLSCertificateRejectReason *reason)
180
free_certificate_list_for_gnutls (gnutls_x509_crt_t *list,
190
res = gnutls_x509_crt_verify (cert, &issuer, 1, 0, &verify_output);
192
DEBUG ("Verifying %p against %p, output %u", cert, issuer, verify_output);
194
return verification_output_to_reason (res, verify_output, reason);
185
for (idx = 0; idx < n_list; idx++)
186
gnutls_x509_crt_deinit (list[idx]);
226
real_start_verification (EmpathyTLSVerifier *self)
228
gnutls_x509_crt_t first_cert, last_cert;
230
gboolean res = FALSE;
219
debug_certificate (GcrCertificate *cert)
221
gchar *subject = gcr_certificate_get_subject_dn (cert);
222
DEBUG ("Certificate: %s", subject);
227
debug_certificate_chain (GcrCertificateChain *chain)
229
GEnumClass *enum_class;
230
GEnumValue *enum_value;
232
GcrCertificate *cert;
234
enum_class = G_ENUM_CLASS
235
(g_type_class_peek (GCR_TYPE_CERTIFICATE_CHAIN_STATUS));
236
enum_value = g_enum_get_value (enum_class,
237
gcr_certificate_chain_get_status (chain));
238
length = gcr_certificate_chain_get_length (chain);
239
DEBUG ("Certificate chain: length %u status %s",
240
length, enum_value ? enum_value->value_nick : "XXX");
242
for (idx = 0; idx < length; ++idx)
244
cert = gcr_certificate_chain_get_certificate (chain, idx);
245
debug_certificate (cert);
250
perform_verification (EmpathyTLSVerifier *self,
251
GcrCertificateChain *chain)
253
gboolean ret = FALSE;
232
254
EmpTLSCertificateRejectReason reason =
233
255
EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
256
gnutls_x509_crt_t *list, *anchors;
257
guint n_list, n_anchors;
235
261
gboolean matched = FALSE;
236
262
EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
238
DEBUG ("Starting verification");
240
/* check if the certificate matches one of the reference identities. */
241
first_cert = g_ptr_array_index (priv->cert_chain, 0);
264
DEBUG ("Performing verification");
265
debug_certificate_chain (chain);
267
list = anchors = NULL;
268
n_list = n_anchors = 0;
271
* If the first certificate is an pinned certificate then we completely
272
* ignore the rest of the verification process.
274
if (gcr_certificate_chain_get_status (chain) == GCR_CERTIFICATE_CHAIN_PINNED)
276
DEBUG ("Found pinned certificate for %s", priv->hostname);
277
complete_verification (self);
281
build_certificate_list_for_gnutls (chain, &list, &n_list,
282
&anchors, &n_anchors);
283
if (list == NULL || n_list == 0) {
284
g_warn_if_reached ();
285
abort_verification (self, EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN);
290
res = gnutls_x509_crt_list_verify (list, n_list, anchors, n_anchors,
291
NULL, 0, 0, &verify_output);
292
ret = verification_output_to_reason (res, verify_output, &reason);
294
DEBUG ("Certificate verification gave result %d with reason %u", ret,
298
abort_verification (self, reason);
302
/* now check if the certificate matches one of the reference identities. */
242
303
if (priv->reference_identities != NULL)
244
for (i = 0; priv->reference_identities[i] != NULL; ++i)
305
for (i = 0, matched = FALSE; priv->reference_identities[i] != NULL; ++i)
246
if (gnutls_x509_crt_check_hostname (first_cert,
307
if (gnutls_x509_crt_check_hostname (list[0],
247
308
priv->reference_identities[i]) == 1)
267
327
certified_hostname, priv->hostname);
269
329
g_free (certified_hostname);
330
abort_verification (self,
331
EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
273
335
DEBUG ("Hostname matched");
275
num_certs = priv->cert_chain->len;
277
if (priv->trusted_ca_list->len > 0)
279
/* if the last certificate is self-signed, and we have a list of
280
* trusted CAs, ignore it, as we want to check the chain against our
281
* trusted CAs list first.
282
* if we have only one certificate in the chain, don't ignore it though,
283
* as it's the CA certificate itself.
285
last_cert = g_ptr_array_index (priv->cert_chain, num_certs - 1);
287
if (gnutls_x509_crt_check_issuer (last_cert, last_cert) > 0 &&
292
for (idx = 1; idx < num_certs; idx++)
294
res = verify_certificate (self,
295
g_ptr_array_index (priv->cert_chain, idx -1),
296
g_ptr_array_index (priv->cert_chain, idx),
299
DEBUG ("Certificate verification %d gave result %d with reason %u", idx,
304
abort_verification (self, reason);
309
res = verify_last_certificate (self,
310
g_ptr_array_index (priv->cert_chain, num_certs - 1),
313
DEBUG ("Last verification gave result %d with reason %u", res, reason);
318
abort_verification (self, reason);
322
336
complete_verification (self);
326
start_verification (gpointer user_data)
328
EmpathyTLSVerifier *self = user_data;
330
real_start_verification (self);
339
free_certificate_list_for_gnutls (list, n_list);
340
free_certificate_list_for_gnutls (anchors, n_anchors);
336
build_gnutls_cert_list (EmpathyTLSVerifier *self)
340
GPtrArray *certificate_data = NULL;
341
EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
343
g_object_get (priv->certificate,
344
"cert-data", &certificate_data,
346
num_certs = certificate_data->len;
348
priv->cert_chain = g_ptr_array_new_with_free_func (
349
(GDestroyNotify) gnutls_x509_crt_deinit);
351
for (idx = 0; idx < num_certs; idx++)
353
gnutls_x509_crt_t cert;
355
gnutls_datum_t datum = { NULL, 0 };
357
one_cert = g_ptr_array_index (certificate_data, idx);
358
datum.data = (guchar *) one_cert->data;
359
datum.size = one_cert->len;
361
gnutls_x509_crt_init (&cert);
362
gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_DER);
364
g_ptr_array_add (priv->cert_chain, cert);
369
get_number_and_type_of_certificates (gnutls_datum_t *datum,
370
gnutls_x509_crt_fmt_t *format)
372
gnutls_x509_crt_t fake;
376
res = gnutls_x509_crt_list_import (&fake, &retval, datum,
377
GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
379
if (res == GNUTLS_E_SHORT_MEMORY_BUFFER || res > 0)
381
DEBUG ("Found PEM, with %u certificates", retval);
382
*format = GNUTLS_X509_FMT_PEM;
387
res = gnutls_x509_crt_list_import (&fake, &retval, datum,
388
GNUTLS_X509_FMT_DER, 0);
392
*format = GNUTLS_X509_FMT_DER;
400
build_gnutls_ca_and_crl_lists (GIOSchedulerJob *job,
401
GCancellable *cancellable,
405
gchar *user_certs_dir;
344
perform_verification_cb (GObject *object,
407
348
GError *error = NULL;
408
EmpathyTLSVerifier *self = user_data;
409
EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
411
priv->trusted_ca_list = g_ptr_array_new_with_free_func
412
((GDestroyNotify) gnutls_x509_crt_deinit);
414
for (idx = 0; idx < (gint) G_N_ELEMENTS (system_ca_paths) - 1; idx++)
417
gchar *contents = NULL;
420
gnutls_x509_crt_t *cert_list;
421
gnutls_datum_t datum = { NULL, 0 };
422
gnutls_x509_crt_fmt_t format = 0;
424
path = system_ca_paths[idx];
425
g_file_get_contents (path, &contents, &length, &error);
429
DEBUG ("Unable to read system CAs from path %s: %s", path,
431
g_clear_error (&error);
435
datum.data = (guchar *) contents;
437
n_certs = get_number_and_type_of_certificates (&datum, &format);
441
DEBUG ("Unable to parse the system CAs from path %s: GnuTLS "
442
"returned error %d", path, n_certs);
448
cert_list = g_malloc0 (sizeof (gnutls_x509_crt_t) * n_certs);
449
res = gnutls_x509_crt_list_import (cert_list, (guint *) &n_certs, &datum,
454
DEBUG ("Unable to import system CAs from path %s; "
455
"GnuTLS returned error %d", path, res);
461
DEBUG ("Successfully imported %d system CA certificates from path %s",
464
/* append the newly created cert structutes into the global GPtrArray */
465
for (idx = 0; idx < n_certs; idx++)
466
g_ptr_array_add (priv->trusted_ca_list, cert_list[idx]);
473
user_certs_dir = g_build_filename (g_get_user_config_dir (),
474
"telepathy", "certs", NULL);
475
dir = g_dir_open (user_certs_dir, 0, &error);
479
DEBUG ("Can't open the user certs dir at %s: %s", user_certs_dir,
482
g_error_free (error);
486
const gchar *cert_name;
488
while ((cert_name = g_dir_read_name (dir)) != NULL)
490
gchar *contents = NULL, *cert_path = NULL;
493
gnutls_datum_t datum = { NULL, 0 };
494
gnutls_x509_crt_t cert;
496
cert_path = g_build_filename (user_certs_dir, cert_name, NULL);
498
g_file_get_contents (cert_path, &contents, &length, &error);
502
DEBUG ("Can't open the certificate file at path %s: %s",
503
cert_path, error->message);
505
g_clear_error (&error);
510
datum.data = (guchar *) contents;
513
gnutls_x509_crt_init (&cert);
514
res = gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_PEM);
516
if (res != GNUTLS_E_SUCCESS)
518
DEBUG ("Can't import the certificate at path %s: "
519
"GnuTLS returned %d", cert_path, res);
523
g_ptr_array_add (priv->trusted_ca_list, cert);
533
g_free (user_certs_dir);
535
/* TODO: do the CRL too */
537
g_io_scheduler_job_send_to_mainloop_async (job,
538
start_verification, self, NULL);
350
GcrCertificateChain *chain = GCR_CERTIFICATE_CHAIN (object);
351
EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (user_data);
353
/* Even if building the chain fails, try verifying what we have */
354
if (!gcr_certificate_chain_build_finish (chain, res, &error))
356
DEBUG ("Building of certificate chain failed: %s", error->message);
357
g_clear_error (&error);
360
perform_verification (self, chain);
362
/* Matches ref when staring chain build */
363
g_object_unref (self);
697
505
GAsyncReadyCallback callback,
698
506
gpointer user_data)
508
GcrCertificateChain *chain;
509
GcrCertificate *cert;
510
GPtrArray *cert_data = NULL;
700
513
EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
515
DEBUG ("Starting verification");
702
517
g_return_if_fail (priv->verify_result == NULL);
519
g_object_get (priv->certificate, "cert-data", &cert_data, NULL);
520
g_return_if_fail (cert_data);
704
522
priv->verify_result = g_simple_async_result_new (G_OBJECT (self),
705
523
callback, user_data, NULL);
707
g_io_scheduler_push_job (build_gnutls_ca_and_crl_lists,
708
self, NULL, G_PRIORITY_DEFAULT, NULL);
525
/* Create a certificate chain */
526
chain = gcr_certificate_chain_new ();
527
for (idx = 0; idx < cert_data->len; ++idx) {
528
data = g_ptr_array_index (cert_data, idx);
529
cert = gcr_simple_certificate_new (data->data, data->len);
530
gcr_certificate_chain_add (chain, cert);
531
g_object_unref (cert);
534
gcr_certificate_chain_build_async (chain, GCR_PURPOSE_CLIENT_AUTH, priv->hostname, 0,
535
NULL, perform_verification_cb, g_object_ref (self));
537
g_object_unref (chain);
538
g_boxed_free (TP_ARRAY_TYPE_UCHAR_ARRAY_LIST, cert_data);