send full certificate chain during handshake
[platform/upstream/glib-networking.git] / tls / gnutls / gtlscertificate-gnutls.c
old mode 100644 (file)
new mode 100755 (executable)
index c2786e7..e47dcdd
@@ -25,6 +25,7 @@
 
 #include "gtlscertificate-gnutls.h"
 #include <glib/gi18n-lib.h>
+#include "TIZEN.h"
 
 static void     g_tls_certificate_gnutls_initable_iface_init (GInitableIface  *iface);
 
@@ -267,6 +268,16 @@ g_tls_certificate_gnutls_set_property (GObject      *object,
     }
 }
 
+#if ENABLE(TIZEN_TV_ADJUST_TIME)
+extern double soupTimeOffset;
+
+static time_t
+correct_time_func(time_t *t)
+{
+  return time(NULL) + (time_t)(soupTimeOffset / 1000);
+}
+#endif
+
 static void
 g_tls_certificate_gnutls_init (GTlsCertificateGnutls *gnutls)
 {
@@ -275,6 +286,9 @@ g_tls_certificate_gnutls_init (GTlsCertificateGnutls *gnutls)
                                              GTlsCertificateGnutlsPrivate);
 
   gnutls_x509_crt_init (&gnutls->priv->cert);
+#if ENABLE(TIZEN_TV_ADJUST_TIME)
+  gnutls_global_set_time_function(correct_time_func);
+#endif
 }
 
 static gboolean
@@ -310,6 +324,10 @@ g_tls_certificate_gnutls_verify (GTlsCertificate     *cert,
   gnutls_x509_crt_t *chain;
   GTlsCertificateFlags gtls_flags;
   time_t t, now;
+#if ENABLE(TIZEN_TV_DLOG)
+  char timebuf[256];
+#endif
+
   
   cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert);
   for (num_certs = 0; cert_gnutls; cert_gnutls = cert_gnutls->priv->issuer)
@@ -346,13 +364,38 @@ g_tls_certificate_gnutls_verify (GTlsCertificate     *cert,
    * won't bother if it gets an UNKNOWN_CA.
    */
   now = time (NULL);
+#if ENABLE(TIZEN_TV_ADJUST_TIME)
+  now = time (NULL) + (time_t)(soupTimeOffset / 1000);
+#endif
   for (i = 0; i < num_certs; i++)
     {
       t = gnutls_x509_crt_get_activation_time (chain[i]);
+
+#if ENABLE(TIZEN_TV_DLOG)
+      ctime_r(&now, timebuf);
+      TIZEN_LOGI("[Certificate] TV borad time is: %s", timebuf);
+      if (t != (time_t) -1) {
+       ctime_r(&t, timebuf);
+        TIZEN_LOGI("[Certificate] CA activation time is: %s", timebuf);
+      }
+      else
+        TIZEN_LOGI("[Certificate] gnutls_x509_crt_get_activation_time ERROR");
+#endif
+
       if (t == (time_t) -1 || t > now)
        gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
 
       t = gnutls_x509_crt_get_expiration_time (chain[i]);
+
+#if ENABLE(TIZEN_TV_DLOG)
+      if (t != (time_t) -1) {
+       ctime_r(&t, timebuf);
+        TIZEN_LOGI("[Certificate] CA expiration time is: %s", timebuf);
+      }
+      else
+        TIZEN_LOGI("[Certificate] gnutls_x509_crt_get_expiration_time ERROR");
+#endif
+
       if (t == (time_t) -1 || t < now)
        gtls_flags |= G_TLS_CERTIFICATE_EXPIRED;
     }
@@ -370,24 +413,45 @@ g_tls_certificate_gnutls_real_copy (GTlsCertificateGnutls    *gnutls,
                                     const gchar              *interaction_id,
                                     gnutls_retr2_st          *st)
 {
+  GTlsCertificateGnutls *chain;
   gnutls_x509_crt_t cert;
   gnutls_datum_t data;
+  guint num_certs = 0;
   size_t size = 0;
 
-  gnutls_x509_crt_export (gnutls->priv->cert, GNUTLS_X509_FMT_DER,
-                          NULL, &size);
-  data.data = g_malloc (size);
-  data.size = size;
-  gnutls_x509_crt_export (gnutls->priv->cert, GNUTLS_X509_FMT_DER,
-                          data.data, &size);
+  /* We will do this loop twice. It's probably more efficient than
+   * re-allocating memory.
+   */
+  chain = gnutls;
+  while (chain != NULL)
+    {
+      num_certs++;
+      chain = chain->priv->issuer;
+    }
+
+  st->ncerts = 0;
+  st->cert.x509 = gnutls_malloc (sizeof (gnutls_x509_crt_t) * num_certs);
+
+/* Now do the actual copy of the whole chain. */
+  chain = gnutls;
+  while (chain != NULL)
+    {
+      gnutls_x509_crt_export (chain->priv->cert, GNUTLS_X509_FMT_DER,
+                              NULL, &size);
+      data.data = g_malloc (size);
+      data.size = size;
+      gnutls_x509_crt_export (chain->priv->cert, GNUTLS_X509_FMT_DER,
+                              data.data, &size);
 
-  gnutls_x509_crt_init (&cert);
-  gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_DER);
-  g_free (data.data);
+      gnutls_x509_crt_init (&cert);
+      gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_DER);
+      g_free (data.data);
 
-  st->ncerts = 1;
-  st->cert.x509 = gnutls_malloc (sizeof (gnutls_x509_crt_t));
-  st->cert.x509[0] = cert;
+      st->cert.x509[st->ncerts] = cert;
+      st->ncerts++;
+
+      chain = chain->priv->issuer;
+    }
 
   if (gnutls->priv->key != NULL)
     {
@@ -541,6 +605,9 @@ g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls,
   /* FIXME: check sRVName and uniformResourceIdentifier
    * subjectAltNames, if appropriate for @identity.
    */
+#if ENABLE(TIZEN_TV_DLOG)
+  TIZEN_LOGI("[Network] SSL HandShake - Bad Identity");
+#endif
 
   return G_TLS_CERTIFICATE_BAD_IDENTITY;
 }
@@ -570,3 +637,101 @@ g_tls_certificate_gnutls_get_bytes (GTlsCertificateGnutls *gnutls)
   g_object_get (gnutls, "certificate", &array, NULL);
   return g_byte_array_free_to_bytes (array);
 }
+
+static gnutls_x509_crt_t *
+convert_data_to_gnutls_certs (const gnutls_datum_t  *certs,
+                              guint                  num_certs,
+                              gnutls_x509_crt_fmt_t  format)
+{
+  gnutls_x509_crt_t *gnutls_certs;
+  guint i;
+
+  gnutls_certs = g_new (gnutls_x509_crt_t, num_certs);
+
+  for (i = 0; i < num_certs; i++)
+    {
+      if (gnutls_x509_crt_init (&gnutls_certs[i]) < 0)
+        {
+          i--;
+          goto error;
+        }
+    }
+
+  for (i = 0; i < num_certs; i++)
+    {
+      if (gnutls_x509_crt_import (gnutls_certs[i], &certs[i], format) < 0)
+        {
+          i = num_certs - 1;
+          goto error;
+        }
+    }
+
+  return gnutls_certs;
+
+error:
+  for (; i != G_MAXUINT; i--)
+    gnutls_x509_crt_deinit (gnutls_certs[i]);
+  g_free (gnutls_certs);
+  return NULL;
+}
+
+GTlsCertificateGnutls *
+g_tls_certificate_gnutls_build_chain (const gnutls_datum_t  *certs,
+                                      guint                  num_certs,
+                                      gnutls_x509_crt_fmt_t  format)
+{
+  GPtrArray *glib_certs;
+  gnutls_x509_crt_t *gnutls_certs;
+  GTlsCertificateGnutls *issuer;
+  GTlsCertificateGnutls *result;
+  guint i, j;
+
+  g_return_val_if_fail (certs, NULL);
+
+  gnutls_certs = convert_data_to_gnutls_certs (certs, num_certs, format);
+  if (!gnutls_certs)
+    return NULL;
+
+  glib_certs = g_ptr_array_new_full (num_certs, g_object_unref);
+  for (i = 0; i < num_certs; i++)
+    g_ptr_array_add (glib_certs, g_tls_certificate_gnutls_new (&certs[i], NULL));
+
+  /* Some servers send certs out of order, or will send duplicate
+   * certs, so we need to be careful when assigning the issuer of
+   * our new GTlsCertificateGnutls.
+   */
+  for (i = 0; i < num_certs; i++)
+    {
+      issuer = NULL;
+
+      if (i < num_certs - 1 &&
+          gnutls_x509_crt_check_issuer (gnutls_certs[i], gnutls_certs[i + 1]))
+        {
+          issuer = glib_certs->pdata[i + 1];
+        }
+      else
+        {
+          for (j = 0; j < num_certs; j++)
+            {
+              if (j != i &&
+                  gnutls_x509_crt_check_issuer (gnutls_certs[i], gnutls_certs[j]))
+                {
+                  issuer = glib_certs->pdata[j];
+                  break;
+                }
+            }
+        }
+
+      if (issuer)
+        g_tls_certificate_gnutls_set_issuer (glib_certs->pdata[i], issuer);
+    }
+
+  result = g_object_ref (glib_certs->pdata[0]);
+  g_ptr_array_unref (glib_certs);
+
+  for (i = 0; i < num_certs; i++)
+    gnutls_x509_crt_deinit (gnutls_certs[i]);
+  g_free (gnutls_certs);
+
+  return result;
+}