aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libempathy/empathy-tls-verifier.c291
1 files changed, 103 insertions, 188 deletions
diff --git a/libempathy/empathy-tls-verifier.c b/libempathy/empathy-tls-verifier.c
index f52a6a8cb..ef2d5e199 100644
--- a/libempathy/empathy-tls-verifier.c
+++ b/libempathy/empathy-tls-verifier.c
@@ -111,165 +111,75 @@ verification_output_to_reason (gint res,
return retval;
}
-static gboolean
-check_is_certificate_exception (EmpathyTLSVerifier *self, GcrCertificate *cert)
-{
- GError *error = NULL;
- gboolean ret;
- EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
-
- ret = gcr_trust_is_certificate_exception (cert, GCR_PURPOSE_CLIENT_AUTH,
- priv->hostname, NULL, &error);
-
- if (!ret && error) {
- DEBUG ("Can't lookup certificate exception for %s: %s", priv->hostname,
- error->message);
- g_clear_error (&error);
- }
-
- return ret;
-}
-
-static gboolean
-check_is_certificate_anchor (EmpathyTLSVerifier *self, GcrCertificate *cert)
-{
- GError *error = NULL;
- gboolean ret;
-
- ret = gcr_trust_is_certificate_anchor (cert, GCR_PURPOSE_CLIENT_AUTH,
- NULL, &error);
-
- if (!ret && error) {
- DEBUG ("Can't lookup certificate anchor: %s", error->message);
- g_clear_error (&error);
- }
-
- return ret;
-}
-
-static gnutls_x509_crt_t*
-convert_chain_to_gnutls (GPtrArray *chain, guint *chain_length)
+static void
+build_certificate_list_for_gnutls (GcrCertificateChain *chain,
+ gnutls_x509_crt_t **list, guint *n_list,
+ gnutls_x509_crt_t **anchors, guint *n_anchors)
{
+ GcrCertificate *cert;
+ guint idx, length;
gnutls_x509_crt_t *retval;
- gnutls_x509_crt_t cert;
+ gnutls_x509_crt_t gcert;
gnutls_datum_t datum;
gsize n_data;
- gint idx;
- retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * chain->len);
+ g_assert (list);
+ g_assert (n_list);
+ g_assert (anchors);
+ g_assert (n_anchors);
- for (idx = 0; idx < (gint) chain->len; idx++)
- {
- datum.data = (gpointer)gcr_certificate_get_der_data
- (g_ptr_array_index (chain, idx), &n_data);
- datum.size = n_data;
-
- gnutls_x509_crt_init (&cert);
- if (gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_DER) < 0)
- g_return_val_if_reached (NULL);
-
- retval[idx] = cert;
- cert = NULL;
- }
-
- *chain_length = chain->len;
- return retval;
-}
-
-static gnutls_x509_crt_t*
-build_certificate_chain_for_gnutls (EmpathyTLSVerifier *self,
- GPtrArray *certs, guint *chain_length, gboolean *chain_anchor)
-{
- GPtrArray *chain;
- gnutls_x509_crt_t *result;
- GArray *cert_data;
- GError *error = NULL;
- GcrCertificate *cert;
- GcrCertificate *anchor;
- guint idx;
+ *list = *anchors = NULL;
+ *n_list = *n_anchors = 0;
- g_assert (chain_length);
- g_assert (chain_anchor);
+ length = gcr_certificate_chain_get_length (chain);
+ retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * length);
- chain = g_ptr_array_new_with_free_func (g_object_unref);
-
- /*
- * The first certificate always goes in the chain unconditionally.
- */
- cert_data = g_ptr_array_index (certs, 0);
- cert = gcr_simple_certificate_new_static (cert_data->data, cert_data->len);
- g_ptr_array_add (chain, cert);
-
- /*
- * Find out which of our certificates is the anchor. Note that we
- * don't allow the leaf certificate on the tree to be an anchor.
- * Also build up the certificate chain. But only up to our anchor.
- */
- anchor = NULL;
- for (idx = 1; idx < certs->len && anchor == NULL; idx++)
+ /* Convert the main body of the chain to gnutls */
+ for (idx = 0; idx < length; ++idx)
{
- /* Stop the chain if previous was self-signed */
- if (gcr_certificate_is_issuer (cert, cert))
- break;
-
- cert_data = g_ptr_array_index (certs, idx);
- cert = gcr_simple_certificate_new_static (cert_data->data,
- cert_data->len);
+ cert = gcr_certificate_chain_get_certificate (chain, idx);
+ datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
+ datum.size = n_data;
- /* Add this to the chain */
- g_ptr_array_add (chain, cert);
+ gnutls_x509_crt_init (&gcert);
+ if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
+ g_return_if_reached ();
- /* Stop the chain at the first anchor */
- if (check_is_certificate_anchor (self, cert))
- anchor = cert;
+ retval[idx] = gcert;
}
- /*
- * If at this point we haven't found a anchor, then we need
- * to keep looking up certificates until we do.
- */
- while (!anchor) {
- /* Stop the chain if previous was self-signed */
- if (gcr_certificate_is_issuer (cert, cert))
- break;
-
- cert = gcr_pkcs11_certificate_lookup_issuer (cert, NULL, &error);
- if (cert == NULL)
- {
- if (error != NULL)
- {
- DEBUG ("Lookup of certificate in PKCS#11 store failed: %s",
- error->message);
- g_clear_error (&error);
- }
- break;
- }
+ *list = retval;
+ *n_list = length;
- /* Add this to the chain */
- g_ptr_array_add (chain, cert);
+ /* See if we have an anchor */
+ if (gcr_certificate_chain_get_chain_type (chain) ==
+ GCR_CERTIFICATE_CHAIN_ANCHORED)
+ {
+ cert = gcr_certificate_chain_get_anchor (chain);
+ g_return_if_fail (cert);
- /* Stop the chain if this is an anchor */
- if (check_is_certificate_anchor (self, cert))
- anchor = cert;
- }
+ datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
+ datum.size = n_data;
- /* Now convert to a form that gnutls understands... */
- result = convert_chain_to_gnutls (chain, chain_length);
- g_assert (!anchor || g_ptr_array_index (chain, chain->len - 1) == anchor);
- *chain_anchor = (anchor != NULL);
- g_ptr_array_free (chain, TRUE);
+ gnutls_x509_crt_init (&gcert);
+ if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
+ g_return_if_reached ();
- return result;
+ retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * 1);
+ retval[0] = gcert;
+ *anchors = retval;
+ *n_anchors = 1;
+ }
}
static void
-free_certificate_chain_for_gnutls (gnutls_x509_crt_t *chain, guint n_chain)
+free_certificate_list_for_gnutls (gnutls_x509_crt_t *list, guint n_list)
{
guint idx;
- for (idx = 0; idx < n_chain; idx++)
- gnutls_x509_crt_deinit (chain[idx]);
- g_free (chain);
+ for (idx = 0; idx < n_list; idx++)
+ gnutls_x509_crt_deinit (list[idx]);
+ g_free (list);
}
static void
@@ -301,54 +211,40 @@ abort_verification (EmpathyTLSVerifier *self,
}
static void
-perform_verification (EmpathyTLSVerifier *self)
+perform_verification (EmpathyTLSVerifier *self, GcrCertificateChain *chain)
{
gboolean ret = FALSE;
EmpTLSCertificateRejectReason reason =
EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
- GcrCertificate *cert;
- gboolean have_anchor = FALSE;
- GPtrArray *certs = NULL;
- GArray *cert_data;
- gint res;
- gnutls_x509_crt_t *chain;
- guint n_chain;
+ gnutls_x509_crt_t *list, *anchors;
+ guint n_list, n_anchors;
guint verify_output;
+ gint res;
EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
- DEBUG ("Starting verification");
-
- g_object_get (priv->certificate, "cert-data", &certs, NULL);
+ DEBUG ("Performing verification");
/*
- * If the first certificate is an exception then we completely
+ * If the first certificate is an pinned certificate then we completely
* ignore the rest of the verification process.
*/
- cert_data = g_ptr_array_index (certs, 0);
- cert = gcr_simple_certificate_new_static (cert_data->data, cert_data->len);
- ret = check_is_certificate_exception (self, cert);
- g_object_unref (cert);
-
- if (ret) {
- DEBUG ("Found certificate exception for %s", priv->hostname);
+ if (gcr_certificate_chain_get_chain_type (chain) == GCR_CERTIFICATE_CHAIN_PINNED)
+ {
+ DEBUG ("Found pinned certificate for %s", priv->hostname);
complete_verification (self);
goto out;
}
- n_chain = 0;
- have_anchor = FALSE;
- chain = build_certificate_chain_for_gnutls (self, certs, &n_chain,
- &have_anchor);
- if (chain == NULL || n_chain == 0) {
+ build_certificate_list_for_gnutls (chain, &list, &n_list,
+ &anchors, &n_anchors);
+ if (list == NULL || n_list == 0) {
g_warn_if_reached ();
abort_verification (self, EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN);
goto out;
}
verify_output = 0;
- res = gnutls_x509_crt_list_verify (chain, n_chain,
- have_anchor ? chain + (n_chain - 1) : NULL,
- have_anchor ? 1 : 0,
+ res = gnutls_x509_crt_list_verify (list, n_list, anchors, n_anchors,
NULL, 0, 0, &verify_output);
ret = verification_output_to_reason (res, verify_output, &reason);
@@ -360,12 +256,12 @@ perform_verification (EmpathyTLSVerifier *self)
goto out;
}
- /* now check if the certificate matches the hostname first. */
- if (gnutls_x509_crt_check_hostname (chain[0], priv->hostname) == 0)
+ /* now check if the certificate matches the hostname. */
+ if (gnutls_x509_crt_check_hostname (list[0], priv->hostname) == 0)
{
gchar *certified_hostname;
- certified_hostname = empathy_get_x509_certificate_hostname (chain[0]);
+ certified_hostname = empathy_get_x509_certificate_hostname (list[0]);
tp_asv_set_string (priv->details,
"expected-hostname", priv->hostname);
tp_asv_set_string (priv->details,
@@ -383,33 +279,30 @@ perform_verification (EmpathyTLSVerifier *self)
DEBUG ("Hostname matched");
complete_verification (self);
- /* TODO: And here is where we check negative trust (ie: revocation) */
-
out:
- free_certificate_chain_for_gnutls (chain, n_chain);
+ free_certificate_list_for_gnutls (list, n_list);
+ free_certificate_list_for_gnutls (anchors, n_anchors);
}
-static gboolean
-perform_verification_cb (gpointer user_data)
+static void
+perform_verification_cb (GObject *object, GAsyncResult *res, gpointer user_data)
{
- EmpathyTLSVerifier *self = user_data;
-
- perform_verification (self);
+ GError *error = NULL;
- return FALSE;
-}
+ GcrCertificateChain *chain = GCR_CERTIFICATE_CHAIN (object);
+ EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (user_data);
-static gboolean
-start_verification (GIOSchedulerJob *job,
- GCancellable *cancellable,
- gpointer user_data)
-{
- EmpathyTLSVerifier *self = user_data;
+ /* Even if building the chain fails, try verifying what we have */
+ if (!gcr_certificate_chain_build_finish (chain, res, &error))
+ {
+ DEBUG ("Building of certificate chain failed: %s", error->message);
+ g_clear_error (&error);
+ }
- g_io_scheduler_job_send_to_mainloop_async (job,
- perform_verification_cb, self, NULL);
+ perform_verification (self, chain);
- return FALSE;
+ /* Matches ref when staring chain build */
+ g_object_unref (self);
}
static void
@@ -538,15 +431,37 @@ empathy_tls_verifier_verify_async (EmpathyTLSVerifier *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ GcrCertificateChain *chain;
+ GcrCertificate *cert;
+ GPtrArray *certs = NULL;
+ GArray *cert_data;
+ guint idx;
EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
+ DEBUG ("Starting verification");
+
g_return_if_fail (priv->verify_result == NULL);
+ g_object_get (priv->certificate, "cert-data", &certs, NULL);
+ g_return_if_fail (certs);
+
priv->verify_result = g_simple_async_result_new (G_OBJECT (self),
callback, user_data, NULL);
- g_io_scheduler_push_job (start_verification,
- self, NULL, G_PRIORITY_DEFAULT, NULL);
+ /* Create a certificate chain */
+ chain = gcr_certificate_chain_new ();
+ for (idx = 0; idx < certs->len; ++idx) {
+ cert_data = g_ptr_array_index (certs, idx);
+ cert = gcr_simple_certificate_new_static (cert_data->data, cert_data->len);
+ gcr_certificate_chain_add (chain, cert);
+ g_object_unref (cert);
+ }
+
+ gcr_certificate_chain_build_async (chain, GCR_PURPOSE_CLIENT_AUTH, priv->hostname, 0,
+ NULL, perform_verification_cb, g_object_ref (self));
+
+ g_object_unref (chain);
+ g_ptr_array_unref (certs);
}
gboolean
@@ -595,7 +510,7 @@ empathy_tls_verifier_store_exception (EmpathyTLSVerifier *self)
cert = gcr_simple_certificate_new_static ((gpointer)last_cert->data,
last_cert->len);
- if (!gcr_trust_add_certificate_exception (cert, GCR_PURPOSE_CLIENT_AUTH,
+ if (!gcr_trust_add_pinned_certificate (cert, GCR_PURPOSE_CLIENT_AUTH,
priv->hostname, NULL, &error))
DEBUG ("Can't store the certificate exeption: %s", error->message);