Upgrade to 2.72.alpha
[platform/upstream/glib-networking.git] / tls / gnutls / gtlsdatabase-gnutls.c
index 7704d56..eef1b16 100755 (executable)
@@ -34,6 +34,8 @@
 #include <gnutls/x509.h>
 
 #include "gtlscertificate-gnutls.h"
+#include "gtlshttp.h"
+#include "gtlsgnutls-version.h"
 
 typedef struct
 {
@@ -43,7 +45,7 @@ typedef struct
    */
   GMutex mutex;
 
-  /* read-only after construct */
+  /* Read-only after construct, but still has to be protected by the mutex. */
   gnutls_x509_trust_list_t trust_list;
 
   /*
@@ -92,7 +94,7 @@ bytes_multi_table_insert (GHashTable *table,
   GPtrArray *multi;
 
   multi = g_hash_table_lookup (table, key);
-  if (multi == NULL)
+  if (!multi)
     {
       multi = g_ptr_array_new_with_free_func ((GDestroyNotify)g_bytes_unref);
       g_hash_table_insert (table, g_bytes_ref (key), multi);
@@ -107,7 +109,7 @@ bytes_multi_table_lookup_ref_one (GHashTable *table,
   GPtrArray *multi;
 
   multi = g_hash_table_lookup (table, key);
-  if (multi == NULL)
+  if (!multi)
     return NULL;
 
   g_assert (multi->len > 0);
@@ -123,7 +125,7 @@ bytes_multi_table_lookup_ref_all (GHashTable *table,
   guint i;
 
   multi = g_hash_table_lookup (table, key);
-  if (multi == NULL)
+  if (!multi)
     return NULL;
 
   for (i = 0; i < multi->len; i++)
@@ -149,7 +151,7 @@ create_handles_array_unlocked (GTlsDatabaseGnutls *self,
     {
       g_assert (G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->create_handle_for_certificate);
       handle = G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->create_handle_for_certificate (self, der);
-      if (handle != NULL)
+      if (handle)
         g_hash_table_insert (handles, handle, g_bytes_ref (der));
     }
 
@@ -170,7 +172,7 @@ initialize_tables (gnutls_x509_trust_list_t  trust_list,
   GBytes *issuer = NULL;
   gint gerr;
 
-  while ((gerr = gnutls_x509_trust_list_iter_get_ca (trust_list, &iter, &cert)) == 0)
+  while (gnutls_x509_trust_list_iter_get_ca (trust_list, &iter, &cert) == 0)
     {
       gerr = gnutls_x509_crt_get_raw_dn (cert, &dn);
       if (gerr < 0)
@@ -251,7 +253,7 @@ g_tls_database_gnutls_create_certificate_handle (GTlsDatabase    *database,
   gchar *handle = NULL;
 
   der = g_tls_certificate_gnutls_get_bytes (G_TLS_CERTIFICATE_GNUTLS (certificate));
-  g_return_val_if_fail (der != NULL, FALSE);
+  g_return_val_if_fail (der, FALSE);
 
   g_mutex_lock (&priv->mutex);
 
@@ -299,12 +301,12 @@ g_tls_database_gnutls_lookup_certificate_for_handle (GTlsDatabase             *d
     priv->handles = create_handles_array_unlocked (self, priv->complete);
 
   der = g_hash_table_lookup (priv->handles, handle);
-  if (der != NULL)
+  if (der)
     g_bytes_ref (der);
 
   g_mutex_unlock (&priv->mutex);
 
-  if (der == NULL)
+  if (!der)
     return NULL;
 
   datum.data = (unsigned char *)g_bytes_get_data (der, &length);
@@ -367,14 +369,14 @@ g_tls_database_gnutls_lookup_certificate_issuer (GTlsDatabase             *datab
     {
       issuer = NULL;
     }
-  else if (der != NULL)
+  else if (der)
     {
       datum.data = (unsigned char *)g_bytes_get_data (der, &length);
       datum.size = length;
       issuer = g_tls_certificate_gnutls_new (&datum, NULL);
     }
 
-  if (der != NULL)
+  if (der)
     g_bytes_unref (der);
   return issuer;
 }
@@ -412,7 +414,7 @@ g_tls_database_gnutls_lookup_certificates_issued_by (GTlsDatabase             *d
 
   g_bytes_unref (issuer);
 
-  for (l = ders; l != NULL; l = g_list_next (l))
+  for (l = ders; l; l = g_list_next (l))
     {
       if (g_cancellable_set_error_if_cancelled (cancellable, error))
         {
@@ -430,28 +432,44 @@ g_tls_database_gnutls_lookup_certificates_issued_by (GTlsDatabase             *d
   return issued;
 }
 
+typedef struct {
+  gnutls_x509_crt_t *chain;
+  guint              length;
+} CertificateChain;
+
+static CertificateChain *
+certificate_chain_new (void)
+{
+  return g_new0 (CertificateChain, 1);
+}
+
 static void
-convert_certificate_chain_to_gnutls (GTlsCertificateGnutls  *chain,
-                                     gnutls_x509_crt_t     **gnutls_chain,
-                                     guint                  *gnutls_chain_length)
+certificate_chain_free (CertificateChain *chain)
+{
+  g_free (chain->chain);
+  g_free (chain);
+}
+
+static CertificateChain *
+convert_certificate_chain_to_gnutls (GTlsCertificateGnutls *chain)
 {
   GTlsCertificate *cert;
-  guint i;
+  CertificateChain *gnutls_chain;
+  guint i = 0;
+
+  gnutls_chain = certificate_chain_new ();
 
-  g_assert (gnutls_chain);
-  g_assert (gnutls_chain_length);
+  for (cert = G_TLS_CERTIFICATE (chain); cert; cert = g_tls_certificate_get_issuer (cert))
+    gnutls_chain->length++;
 
-  for (*gnutls_chain_length = 0, cert = G_TLS_CERTIFICATE (chain);
-       cert; cert = g_tls_certificate_get_issuer (cert))
-    ++(*gnutls_chain_length);
+  gnutls_chain->chain = g_new (gnutls_x509_crt_t, gnutls_chain->length);
 
-  *gnutls_chain = g_new0 (gnutls_x509_crt_t, *gnutls_chain_length);
+  for (cert = G_TLS_CERTIFICATE (chain); cert; cert = g_tls_certificate_get_issuer (cert), i++)
+    gnutls_chain->chain[i] = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (cert));
 
-  for (i = 0, cert = G_TLS_CERTIFICATE (chain);
-       cert; cert = g_tls_certificate_get_issuer (cert), ++i)
-    (*gnutls_chain)[i] = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (cert));
+  g_assert (i == gnutls_chain->length);
 
-  g_assert (i == *gnutls_chain_length);
+  return gnutls_chain;
 }
 
 static GTlsCertificateFlags
@@ -468,10 +486,7 @@ g_tls_database_gnutls_verify_chain (GTlsDatabase             *database,
   GTlsDatabaseGnutlsPrivate *priv = g_tls_database_gnutls_get_instance_private (self);
   GTlsCertificateFlags result;
   guint gnutls_result;
-  gnutls_x509_crt_t *certs;
-  guint certs_length;
-  const char *hostname = NULL;
-  char *free_hostname = NULL;
+  CertificateChain *gnutls_chain;
   int gerr;
 
   g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (chain),
@@ -481,39 +496,27 @@ g_tls_database_gnutls_verify_chain (GTlsDatabase             *database,
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return G_TLS_CERTIFICATE_GENERIC_ERROR;
 
-  convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (chain),
-                                       &certs, &certs_length);
+  g_mutex_lock (&priv->mutex);
+  gnutls_chain = convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (chain));
   gerr = gnutls_x509_trust_list_verify_crt (priv->trust_list,
-                                            certs, certs_length,
+                                            gnutls_chain->chain, gnutls_chain->length,
                                             0, &gnutls_result, NULL);
+  g_mutex_unlock (&priv->mutex);
 
   if (gerr != 0 || g_cancellable_set_error_if_cancelled (cancellable, error))
     {
-      g_free (certs);
+      certificate_chain_free (gnutls_chain);
       return G_TLS_CERTIFICATE_GENERIC_ERROR;
     }
 
   result = g_tls_certificate_gnutls_convert_flags (gnutls_result);
 
-  if (G_IS_NETWORK_ADDRESS (identity))
-    hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity));
-  else if (G_IS_NETWORK_SERVICE (identity))
-    hostname = g_network_service_get_domain (G_NETWORK_SERVICE (identity));
-  else if (G_IS_INET_SOCKET_ADDRESS (identity))
-    {
-      GInetAddress *addr;
+  if (identity)
+    result |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (chain),
+                                                        identity,
+                                                        error);
 
-      addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (identity));
-      hostname = free_hostname = g_inet_address_to_string (addr);
-    }
-  if (hostname)
-    {
-      if (!gnutls_x509_crt_check_hostname (certs[0], hostname))
-        result |= G_TLS_CERTIFICATE_BAD_IDENTITY;
-      g_free (free_hostname);
-    }
-
-  g_free (certs);
+  certificate_chain_free (gnutls_chain);
   return result;
 }
 
@@ -561,6 +564,57 @@ g_tls_database_gnutls_populate_trust_list (GTlsDatabaseGnutls        *self,
   return gerr >= 0;
 }
 
+static gnutls_x509_trust_list_t
+create_trust_list (GTlsDatabaseGnutls  *self,
+                   GError             **error)
+{
+  GTlsDatabaseGnutlsClass *database_class = G_TLS_DATABASE_GNUTLS_GET_CLASS (self);
+  gnutls_x509_trust_list_t trust_list;
+  int ret;
+
+  ret = gnutls_x509_trust_list_init (&trust_list, 0);
+  if (ret != 0)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC, "Failed to initialize trust list: %s", gnutls_strerror (ret));
+      return NULL;
+    }
+
+  g_assert (database_class->populate_trust_list);
+  if (!database_class->populate_trust_list (self, trust_list, error))
+    {
+      gnutls_x509_trust_list_deinit (trust_list, TRUE);
+      return NULL;
+    }
+
+  return trust_list;
+}
+
+gnutls_certificate_credentials_t
+g_tls_database_gnutls_get_credentials (GTlsDatabaseGnutls  *self,
+                                       GError             **error)
+{
+  gnutls_certificate_credentials_t credentials;
+  gnutls_x509_trust_list_t trust_list = NULL;
+  int ret;
+
+  ret = gnutls_certificate_allocate_credentials (&credentials);
+  if (ret != 0)
+    {
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC, "Failed to allocate credentials: %s", gnutls_strerror (ret));
+      return NULL;
+    }
+
+  trust_list = create_trust_list (self, error);
+  if (!trust_list)
+    {
+      gnutls_certificate_free_credentials (credentials);
+      return NULL;
+    }
+
+  gnutls_certificate_set_trust_list (credentials, trust_list, 0);
+  return credentials;
+}
+
 static void
 g_tls_database_gnutls_class_init (GTlsDatabaseGnutlsClass *klass)
 {
@@ -595,14 +649,9 @@ g_tls_database_gnutls_initable_init (GInitable     *initable,
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return FALSE;
 
-  gnutls_x509_trust_list_init (&trust_list, 0);
-
-  g_assert (G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->populate_trust_list);
-  if (!G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->populate_trust_list (self, trust_list, error))
-    {
-      result = FALSE;
-      goto out;
-    }
+  trust_list = create_trust_list (self, error);
+  if (!trust_list)
+    return FALSE;
 
   subjects = bytes_multi_table_new ();
   issuers = bytes_multi_table_new ();
@@ -642,14 +691,13 @@ g_tls_database_gnutls_initable_init (GInitable     *initable,
       g_mutex_unlock (&priv->mutex);
     }
 
-out:
-  if (trust_list != NULL)
+  if (trust_list)
     gnutls_x509_trust_list_deinit (trust_list, 1);
-  if (subjects != NULL)
+  if (subjects)
     g_hash_table_unref (subjects);
-  if (issuers != NULL)
+  if (issuers)
     g_hash_table_unref (issuers);
-  if (complete != NULL)
+  if (complete)
     g_hash_table_unref (complete);
   return result;
 }