]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-tls-verifier.c
Center the 'smiley images' inside the menu items
[empathy.git] / libempathy / empathy-tls-verifier.c
index 2f33afad277656817212ef4943fa0d98d4dc7598..fcbc559b3f973d93fc76eba8dfa3078e7183b73b 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#include <config.h>
-
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-
-#include <telepathy-glib/util.h>
-
+#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"
-#include "empathy-utils.h"
 
 G_DEFINE_TYPE (EmpathyTLSVerifier, empathy_tls_verifier,
     G_TYPE_OBJECT)
@@ -42,13 +37,15 @@ G_DEFINE_TYPE (EmpathyTLSVerifier, empathy_tls_verifier,
 enum {
   PROP_TLS_CERTIFICATE = 1,
   PROP_HOSTNAME,
+  PROP_REFERENCE_IDENTITIES,
 
   LAST_PROPERTY,
 };
 
 typedef struct {
-  EmpathyTLSCertificate *certificate;
+  TpTLSCertificate *certificate;
   gchar *hostname;
+  gchar **reference_identities;
 
   GSimpleAsyncResult *verify_result;
   GHashTable *details;
@@ -59,7 +56,7 @@ typedef struct {
 static gboolean
 verification_output_to_reason (gint res,
     guint verify_output,
-    EmpTLSCertificateRejectReason *reason)
+    TpTLSCertificateRejectReason *reason)
 {
   gboolean retval = TRUE;
 
@@ -73,13 +70,13 @@ verification_output_to_reason (gint res,
       switch (res)
         {
         case GNUTLS_E_INSUFFICIENT_CREDENTIALS:
-          *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED;
+          *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED;
           break;
         case GNUTLS_E_CONSTRAINT_ERROR:
-          *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_LIMIT_EXCEEDED;
+          *reason = TP_TLS_CERTIFICATE_REJECT_REASON_LIMIT_EXCEEDED;
           break;
         default:
-          *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
+          *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
           break;
         }
 
@@ -92,17 +89,17 @@ verification_output_to_reason (gint res,
       retval = FALSE;
 
       if (verify_output & GNUTLS_CERT_SIGNER_NOT_FOUND)
-        *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED;
+        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED;
       else if (verify_output & GNUTLS_CERT_SIGNER_NOT_CA)
-        *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED;
+        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNTRUSTED;
       else if (verify_output & GNUTLS_CERT_INSECURE_ALGORITHM)
-        *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_INSECURE;
+        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_INSECURE;
       else if (verify_output & GNUTLS_CERT_NOT_ACTIVATED)
-        *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_NOT_ACTIVATED;
+        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_NOT_ACTIVATED;
       else if (verify_output & GNUTLS_CERT_EXPIRED)
-        *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_EXPIRED;
+        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_EXPIRED;
       else
-        *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
+        *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
 
       goto out;
     }
@@ -113,8 +110,10 @@ verification_output_to_reason (gint res,
 
 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)
+        gnutls_x509_crt_t **list,
+        guint *n_list,
+        gnutls_x509_crt_t **anchors,
+        guint *n_anchors)
 {
   GcrCertificate *cert;
   guint idx, length;
@@ -173,7 +172,8 @@ build_certificate_list_for_gnutls (GcrCertificateChain *chain,
 }
 
 static void
-free_certificate_list_for_gnutls (gnutls_x509_crt_t *list, guint n_list)
+free_certificate_list_for_gnutls (gnutls_x509_crt_t *list,
+        guint n_list)
 {
   guint idx;
 
@@ -196,7 +196,7 @@ complete_verification (EmpathyTLSVerifier *self)
 
 static void
 abort_verification (EmpathyTLSVerifier *self,
-    EmpTLSCertificateRejectReason reason)
+    TpTLSCertificateRejectReason reason)
 {
   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
 
@@ -210,6 +210,14 @@ abort_verification (EmpathyTLSVerifier *self,
   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)
 {
@@ -217,7 +225,6 @@ debug_certificate_chain (GcrCertificateChain *chain)
     GEnumValue *enum_value;
     gint idx, length;
     GcrCertificate *cert;
-    gchar *subject;
 
     enum_class = G_ENUM_CLASS
             (g_type_class_peek (GCR_TYPE_CERTIFICATE_CHAIN_STATUS));
@@ -230,27 +237,31 @@ debug_certificate_chain (GcrCertificateChain *chain)
     for (idx = 0; idx < length; ++idx)
       {
         cert = gcr_certificate_chain_get_certificate (chain, idx);
-        subject = gcr_certificate_get_subject_dn (cert);
-        DEBUG ("  Certificate: %s", subject);
-        g_free (subject);
+        debug_certificate (cert);
       }
 }
 
 static void
-perform_verification (EmpathyTLSVerifier *self, GcrCertificateChain *chain)
+perform_verification (EmpathyTLSVerifier *self,
+        GcrCertificateChain *chain)
 {
   gboolean ret = FALSE;
-  EmpTLSCertificateRejectReason reason =
-    EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
+  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.
@@ -266,7 +277,7 @@ perform_verification (EmpathyTLSVerifier *self, GcrCertificateChain *chain)
           &anchors, &n_anchors);
   if (list == NULL || n_list == 0) {
       g_warn_if_reached ();
-      abort_verification (self, EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN);
+      abort_verification (self, TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN);
       goto out;
   }
 
@@ -283,8 +294,21 @@ perform_verification (EmpathyTLSVerifier *self, GcrCertificateChain *chain)
       goto out;
   }
 
-  /* now check if the certificate matches the hostname. */
-  if (gnutls_x509_crt_check_hostname (list[0], priv->hostname) == 0)
+  /* 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;
 
@@ -299,7 +323,7 @@ perform_verification (EmpathyTLSVerifier *self, GcrCertificateChain *chain)
 
       g_free (certified_hostname);
       abort_verification (self,
-              EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
+              TP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
       goto out;
     }
 
@@ -312,7 +336,9 @@ perform_verification (EmpathyTLSVerifier *self, GcrCertificateChain *chain)
 }
 
 static void
-perform_verification_cb (GObject *object, GAsyncResult *res, gpointer user_data)
+perform_verification_cb (GObject *object,
+        GAsyncResult *res,
+        gpointer user_data)
 {
   GError *error = NULL;
 
@@ -348,6 +374,9 @@ empathy_tls_verifier_get_property (GObject *object,
     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;
@@ -370,6 +399,9 @@ empathy_tls_verifier_set_property (GObject *object,
     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;
@@ -400,6 +432,7 @@ empathy_tls_verifier_finalize (GObject *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);
 }
@@ -427,29 +460,39 @@ empathy_tls_verifier_class_init (EmpathyTLSVerifierClass *klass)
   oclass->finalize = empathy_tls_verifier_finalize;
   oclass->dispose = empathy_tls_verifier_dispose;
 
-  pspec = g_param_spec_object ("certificate", "The EmpathyTLSCertificate",
-      "The EmpathyTLSCertificate to be verified.",
-      EMPATHY_TYPE_TLS_CERTIFICATE,
+  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 should be certified by the certificate.",
+      "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 (EmpathyTLSCertificate *certificate,
-    const gchar *hostname)
+empathy_tls_verifier_new (TpTLSCertificate *certificate,
+    const gchar *hostname,
+    const gchar **reference_identities)
 {
-  g_assert (EMPATHY_IS_TLS_CERTIFICATE (certificate));
+  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);
 }
 
@@ -460,8 +503,8 @@ empathy_tls_verifier_verify_async (EmpathyTLSVerifier *self,
 {
   GcrCertificateChain *chain;
   GcrCertificate *cert;
-  GPtrArray *certs = NULL;
-  GArray *cert_data;
+  GPtrArray *cert_data;
+  GArray *data;
   guint idx;
   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
 
@@ -469,32 +512,31 @@ empathy_tls_verifier_verify_async (EmpathyTLSVerifier *self,
 
   g_return_if_fail (priv->verify_result == NULL);
 
-  g_object_get (priv->certificate, "cert-data", &certs, NULL);
-  g_return_if_fail (certs);
+  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 < certs->len; ++idx) {
-    cert_data = g_ptr_array_index (certs, idx);
-    cert = gcr_simple_certificate_new_static (cert_data->data, cert_data->len);
+  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_CLIENT_AUTH, priv->hostname, 0,
+  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);
-  g_ptr_array_unref (certs);
 }
 
 gboolean
 empathy_tls_verifier_verify_finish (EmpathyTLSVerifier *self,
     GAsyncResult *res,
-    EmpTLSCertificateRejectReason *reason,
+    TpTLSCertificateRejectReason *reason,
     GHashTable **details,
     GError **error)
 {
@@ -518,7 +560,7 @@ empathy_tls_verifier_verify_finish (EmpathyTLSVerifier *self,
     }
 
   if (reason != NULL)
-    *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
+    *reason = TP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
 
   return TRUE;
 }
@@ -526,20 +568,31 @@ empathy_tls_verifier_verify_finish (EmpathyTLSVerifier *self,
 void
 empathy_tls_verifier_store_exception (EmpathyTLSVerifier *self)
 {
-  GArray *last_cert;
+  GArray *data;
   GcrCertificate *cert;
-  GPtrArray *certs;
+  GPtrArray *cert_data;
   GError *error = NULL;
   EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
 
-  g_object_get (priv->certificate, "cert-data", &certs, NULL);
-  last_cert = g_ptr_array_index (certs, certs->len - 1);
-  cert = gcr_simple_certificate_new_static ((gpointer)last_cert->data,
-          last_cert->len);
+  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_CLIENT_AUTH,
+  if (!gcr_trust_add_pinned_certificate (cert, GCR_PURPOSE_SERVER_AUTH,
           priv->hostname, NULL, &error))
-      DEBUG ("Can't store the certificate exeption: %s", error->message);
+      DEBUG ("Can't store the pinned certificate: %s", error->message);
 
   g_object_unref (cert);
 }