summaryrefslogtreecommitdiff
path: root/libempathy/empathy-tls-verifier.c
diff options
context:
space:
mode:
Diffstat (limited to 'libempathy/empathy-tls-verifier.c')
-rw-r--r--libempathy/empathy-tls-verifier.c598
1 files changed, 0 insertions, 598 deletions
diff --git a/libempathy/empathy-tls-verifier.c b/libempathy/empathy-tls-verifier.c
deleted file mode 100644
index fcbc559b..00000000
--- a/libempathy/empathy-tls-verifier.c
+++ /dev/null
@@ -1,598 +0,0 @@
-/*
- * empathy-tls-verifier.c - Source for EmpathyTLSVerifier
- * Copyright (C) 2010 Collabora Ltd.
- * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
- * @author Stef Walter <stefw@collabora.co.uk>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "config.h"
-#include "empathy-tls-verifier.h"
-
-#include <gcr/gcr.h>
-
-#include "empathy-utils.h"
-
-#define DEBUG_FLAG EMPATHY_DEBUG_TLS
-#include "empathy-debug.h"
-
-G_DEFINE_TYPE (EmpathyTLSVerifier, empathy_tls_verifier,
- G_TYPE_OBJECT)
-
-#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTLSVerifier);
-
-enum {
- PROP_TLS_CERTIFICATE = 1,
- PROP_HOSTNAME,
- PROP_REFERENCE_IDENTITIES,
-
- LAST_PROPERTY,
-};
-
-typedef struct {
- TpTLSCertificate *certificate;
- gchar *hostname;
- gchar **reference_identities;
-
- GSimpleAsyncResult *verify_result;
- GHashTable *details;
-
- gboolean dispose_run;
-} EmpathyTLSVerifierPriv;
-
-static gboolean
-verification_output_to_reason (gint res,
- guint verify_output,
- TpTLSCertificateRejectReason *reason)
-{
- gboolean retval = TRUE;
-
- g_assert (reason != NULL);
-
- if (res != GNUTLS_E_SUCCESS)
- {
- retval = FALSE;
-
- /* the certificate is not structurally valid */
- switch (res)
- {
- case GNUTLS_E_INSUFFICIENT_CREDENTIALS:
- *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED;
- break;
- case GNUTLS_E_CONSTRAINT_ERROR:
- *reason = TP_TLS_CERTIFICATE_REJECT_REASON_LIMIT_EXCEEDED;
- break;
- default:
- *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
- break;
- }
-
- goto out;
- }
-
- /* the certificate is structurally valid, check for other errors. */
- if (verify_output & GNUTLS_CERT_INVALID)
- {
- retval = FALSE;
-
- if (verify_output & GNUTLS_CERT_SIGNER_NOT_FOUND)
- *reason = TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED;
- else if (verify_output & GNUTLS_CERT_SIGNER_NOT_CA)
- *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED;
- else if (verify_output & GNUTLS_CERT_INSECURE_ALGORITHM)
- *reason = TP_TLS_CERTIFICATE_REJECT_REASON_INSECURE;
- else if (verify_output & GNUTLS_CERT_NOT_ACTIVATED)
- *reason = TP_TLS_CERTIFICATE_REJECT_REASON_NOT_ACTIVATED;
- else if (verify_output & GNUTLS_CERT_EXPIRED)
- *reason = TP_TLS_CERTIFICATE_REJECT_REASON_EXPIRED;
- else
- *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
-
- goto out;
- }
-
- out:
- return retval;
-}
-
-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 gcert;
- gnutls_datum_t datum;
- gsize n_data;
-
- g_assert (list);
- g_assert (n_list);
- g_assert (anchors);
- g_assert (n_anchors);
-
- *list = *anchors = NULL;
- *n_list = *n_anchors = 0;
-
- length = gcr_certificate_chain_get_length (chain);
- retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * length);
-
- /* Convert the main body of the chain to gnutls */
- for (idx = 0; idx < length; ++idx)
- {
- cert = gcr_certificate_chain_get_certificate (chain, idx);
- datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
- datum.size = n_data;
-
- gnutls_x509_crt_init (&gcert);
- if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
- g_return_if_reached ();
-
- retval[idx] = gcert;
- }
-
- *list = retval;
- *n_list = length;
-
- /* See if we have an anchor */
- if (gcr_certificate_chain_get_status (chain) ==
- GCR_CERTIFICATE_CHAIN_ANCHORED)
- {
- cert = gcr_certificate_chain_get_anchor (chain);
- g_return_if_fail (cert);
-
- datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
- datum.size = n_data;
-
- gnutls_x509_crt_init (&gcert);
- if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
- g_return_if_reached ();
-
- retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * 1);
- retval[0] = gcert;
- *anchors = retval;
- *n_anchors = 1;
- }
-}
-
-static void
-free_certificate_list_for_gnutls (gnutls_x509_crt_t *list,
- guint n_list)
-{
- guint idx;
-
- for (idx = 0; idx < n_list; idx++)
- gnutls_x509_crt_deinit (list[idx]);
- g_free (list);
-}
-
-static void
-complete_verification (EmpathyTLSVerifier *self)
-{
- EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
-
- DEBUG ("Verification successful, completing...");
-
- g_simple_async_result_complete_in_idle (priv->verify_result);
-
- tp_clear_object (&priv->verify_result);
-}
-
-static void
-abort_verification (EmpathyTLSVerifier *self,
- TpTLSCertificateRejectReason reason)
-{
- EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
-
- DEBUG ("Verification error %u, aborting...", reason);
-
- g_simple_async_result_set_error (priv->verify_result,
- G_IO_ERROR, reason, "TLS verification failed with reason %u",
- reason);
- g_simple_async_result_complete_in_idle (priv->verify_result);
-
- tp_clear_object (&priv->verify_result);
-}
-
-static void
-debug_certificate (GcrCertificate *cert)
-{
- gchar *subject = gcr_certificate_get_subject_dn (cert);
- DEBUG ("Certificate: %s", subject);
- g_free (subject);
-}
-
-static void
-debug_certificate_chain (GcrCertificateChain *chain)
-{
- GEnumClass *enum_class;
- GEnumValue *enum_value;
- gint idx, length;
- GcrCertificate *cert;
-
- enum_class = G_ENUM_CLASS
- (g_type_class_peek (GCR_TYPE_CERTIFICATE_CHAIN_STATUS));
- enum_value = g_enum_get_value (enum_class,
- gcr_certificate_chain_get_status (chain));
- length = gcr_certificate_chain_get_length (chain);
- DEBUG ("Certificate chain: length %u status %s",
- length, enum_value ? enum_value->value_nick : "XXX");
-
- for (idx = 0; idx < length; ++idx)
- {
- cert = gcr_certificate_chain_get_certificate (chain, idx);
- debug_certificate (cert);
- }
-}
-
-static void
-perform_verification (EmpathyTLSVerifier *self,
- GcrCertificateChain *chain)
-{
- gboolean ret = FALSE;
- TpTLSCertificateRejectReason reason =
- TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
- gnutls_x509_crt_t *list, *anchors;
- guint n_list, n_anchors;
- guint verify_output;
- gint res;
- gint i;
- gboolean matched = FALSE;
- EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
-
- DEBUG ("Performing verification");
- debug_certificate_chain (chain);
-
- list = anchors = NULL;
- n_list = n_anchors = 0;
-
- /*
- * If the first certificate is an pinned certificate then we completely
- * ignore the rest of the verification process.
- */
- if (gcr_certificate_chain_get_status (chain) == GCR_CERTIFICATE_CHAIN_PINNED)
- {
- DEBUG ("Found pinned certificate for %s", priv->hostname);
- complete_verification (self);
- goto out;
- }
-
- 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, TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN);
- goto out;
- }
-
- verify_output = 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);
-
- DEBUG ("Certificate verification gave result %d with reason %u", ret,
- reason);
-
- if (!ret) {
- abort_verification (self, reason);
- goto out;
- }
-
- /* now check if the certificate matches one of the reference identities. */
- if (priv->reference_identities != NULL)
- {
- for (i = 0, matched = FALSE; priv->reference_identities[i] != NULL; ++i)
- {
- if (gnutls_x509_crt_check_hostname (list[0],
- priv->reference_identities[i]) == 1)
- {
- matched = TRUE;
- break;
- }
- }
- }
-
- if (!matched)
- {
- gchar *certified_hostname;
-
- 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,
- "certificate-hostname", certified_hostname);
-
- DEBUG ("Hostname mismatch: got %s but expected %s",
- certified_hostname, priv->hostname);
-
- g_free (certified_hostname);
- abort_verification (self,
- TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
- goto out;
- }
-
- DEBUG ("Hostname matched");
- complete_verification (self);
-
- out:
- free_certificate_list_for_gnutls (list, n_list);
- free_certificate_list_for_gnutls (anchors, n_anchors);
-}
-
-static void
-perform_verification_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- GError *error = NULL;
-
- GcrCertificateChain *chain = GCR_CERTIFICATE_CHAIN (object);
- EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (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);
- }
-
- perform_verification (self, chain);
-
- /* Matches ref when staring chain build */
- g_object_unref (self);
-}
-
-static void
-empathy_tls_verifier_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- EmpathyTLSVerifierPriv *priv = GET_PRIV (object);
-
- switch (property_id)
- {
- case PROP_TLS_CERTIFICATE:
- g_value_set_object (value, priv->certificate);
- break;
- case PROP_HOSTNAME:
- g_value_set_string (value, priv->hostname);
- break;
- case PROP_REFERENCE_IDENTITIES:
- g_value_set_boxed (value, priv->reference_identities);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-empathy_tls_verifier_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- EmpathyTLSVerifierPriv *priv = GET_PRIV (object);
-
- switch (property_id)
- {
- case PROP_TLS_CERTIFICATE:
- priv->certificate = g_value_dup_object (value);
- break;
- case PROP_HOSTNAME:
- priv->hostname = g_value_dup_string (value);
- break;
- case PROP_REFERENCE_IDENTITIES:
- priv->reference_identities = g_value_dup_boxed (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-empathy_tls_verifier_dispose (GObject *object)
-{
- EmpathyTLSVerifierPriv *priv = GET_PRIV (object);
-
- if (priv->dispose_run)
- return;
-
- priv->dispose_run = TRUE;
-
- tp_clear_object (&priv->certificate);
-
- G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->dispose (object);
-}
-
-static void
-empathy_tls_verifier_finalize (GObject *object)
-{
- EmpathyTLSVerifierPriv *priv = GET_PRIV (object);
-
- DEBUG ("%p", object);
-
- tp_clear_boxed (G_TYPE_HASH_TABLE, &priv->details);
- g_free (priv->hostname);
- g_strfreev (priv->reference_identities);
-
- G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->finalize (object);
-}
-
-static void
-empathy_tls_verifier_init (EmpathyTLSVerifier *self)
-{
- EmpathyTLSVerifierPriv *priv;
-
- priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- EMPATHY_TYPE_TLS_VERIFIER, EmpathyTLSVerifierPriv);
- priv->details = tp_asv_new (NULL, NULL);
-}
-
-static void
-empathy_tls_verifier_class_init (EmpathyTLSVerifierClass *klass)
-{
- GParamSpec *pspec;
- GObjectClass *oclass = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (EmpathyTLSVerifierPriv));
-
- oclass->set_property = empathy_tls_verifier_set_property;
- oclass->get_property = empathy_tls_verifier_get_property;
- oclass->finalize = empathy_tls_verifier_finalize;
- oclass->dispose = empathy_tls_verifier_dispose;
-
- pspec = g_param_spec_object ("certificate", "The TpTLSCertificate",
- "The TpTLSCertificate to be verified.",
- TP_TYPE_TLS_CERTIFICATE,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (oclass, PROP_TLS_CERTIFICATE, pspec);
-
- pspec = g_param_spec_string ("hostname", "The hostname",
- "The hostname which is certified by the certificate.",
- NULL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (oclass, PROP_HOSTNAME, pspec);
-
- pspec = g_param_spec_boxed ("reference-identities",
- "The reference identities",
- "The certificate should certify one of these identities.",
- G_TYPE_STRV,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (oclass, PROP_REFERENCE_IDENTITIES, pspec);
-}
-
-EmpathyTLSVerifier *
-empathy_tls_verifier_new (TpTLSCertificate *certificate,
- const gchar *hostname,
- const gchar **reference_identities)
-{
- g_assert (TP_IS_TLS_CERTIFICATE (certificate));
- g_assert (hostname != NULL);
- g_assert (reference_identities != NULL);
-
- return g_object_new (EMPATHY_TYPE_TLS_VERIFIER,
- "certificate", certificate,
- "hostname", hostname,
- "reference-identities", reference_identities,
- NULL);
-}
-
-void
-empathy_tls_verifier_verify_async (EmpathyTLSVerifier *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GcrCertificateChain *chain;
- GcrCertificate *cert;
- GPtrArray *cert_data;
- GArray *data;
- guint idx;
- EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
-
- DEBUG ("Starting verification");
-
- g_return_if_fail (priv->verify_result == NULL);
-
- cert_data = tp_tls_certificate_get_cert_data (priv->certificate);
- g_return_if_fail (cert_data);
-
- priv->verify_result = g_simple_async_result_new (G_OBJECT (self),
- callback, user_data, NULL);
-
- /* Create a certificate chain */
- chain = gcr_certificate_chain_new ();
- for (idx = 0; idx < cert_data->len; ++idx) {
- data = g_ptr_array_index (cert_data, idx);
- cert = gcr_simple_certificate_new ((guchar *) data->data, data->len);
- gcr_certificate_chain_add (chain, cert);
- g_object_unref (cert);
- }
-
- gcr_certificate_chain_build_async (chain, GCR_PURPOSE_SERVER_AUTH, priv->hostname, 0,
- NULL, perform_verification_cb, g_object_ref (self));
-
- g_object_unref (chain);
-}
-
-gboolean
-empathy_tls_verifier_verify_finish (EmpathyTLSVerifier *self,
- GAsyncResult *res,
- TpTLSCertificateRejectReason *reason,
- GHashTable **details,
- GError **error)
-{
- EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
- error))
- {
- if (reason != NULL)
- *reason = (*error)->code;
-
- if (details != NULL)
- {
- *details = tp_asv_new (NULL, NULL);
- tp_g_hash_table_update (*details, priv->details,
- (GBoxedCopyFunc) g_strdup,
- (GBoxedCopyFunc) tp_g_value_slice_dup);
- }
-
- return FALSE;
- }
-
- if (reason != NULL)
- *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
-
- return TRUE;
-}
-
-void
-empathy_tls_verifier_store_exception (EmpathyTLSVerifier *self)
-{
- GArray *data;
- GcrCertificate *cert;
- GPtrArray *cert_data;
- GError *error = NULL;
- EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
-
- cert_data = tp_tls_certificate_get_cert_data (priv->certificate);
- g_return_if_fail (cert_data);
-
- if (!cert_data->len)
- {
- DEBUG ("No certificate to pin.");
- return;
- }
-
- /* The first certificate in the chain is for the host */
- data = g_ptr_array_index (cert_data, 0);
- cert = gcr_simple_certificate_new ((gpointer)data->data, data->len);
-
- DEBUG ("Storing pinned certificate:");
- debug_certificate (cert);
-
- if (!gcr_trust_add_pinned_certificate (cert, GCR_PURPOSE_SERVER_AUTH,
- priv->hostname, NULL, &error))
- DEBUG ("Can't store the pinned certificate: %s", error->message);
-
- g_object_unref (cert);
-}