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 68b6e2d..e47dcdd
@@ -1,6 +1,6 @@
 /* GIO - GLib Input, Output and Streaming Library
  *
- * Copyright © 2009 Red Hat, Inc
+ * Copyright 2009 Red Hat, Inc
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -13,9 +13,8 @@
  * 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., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
@@ -26,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);
 
@@ -63,7 +63,8 @@ g_tls_certificate_gnutls_finalize (GObject *object)
   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
 
   gnutls_x509_crt_deinit (gnutls->priv->cert);
-  gnutls_x509_privkey_deinit (gnutls->priv->key);
+  if (gnutls->priv->key)
+    gnutls_x509_privkey_deinit (gnutls->priv->key);
 
   if (gnutls->priv->issuer)
     g_object_unref (gnutls->priv->issuer);
@@ -203,8 +204,19 @@ g_tls_certificate_gnutls_set_property (GObject      *object,
       g_return_if_fail (gnutls->priv->have_key == FALSE);
       data.data = bytes->data;
       data.size = bytes->len;
+      if (!gnutls->priv->key)
+        gnutls_x509_privkey_init (&gnutls->priv->key);
       status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
                                           GNUTLS_X509_FMT_DER);
+      if (status != 0)
+       {
+         int pkcs8_status =
+           gnutls_x509_privkey_import_pkcs8 (gnutls->priv->key, &data,
+                                             GNUTLS_X509_FMT_DER, NULL,
+                                             GNUTLS_PKCS_PLAIN);
+         if (pkcs8_status == 0)
+           status = 0;
+       }
       if (status == 0)
        gnutls->priv->have_key = TRUE;
       else if (!gnutls->priv->construct_error)
@@ -223,8 +235,19 @@ g_tls_certificate_gnutls_set_property (GObject      *object,
       g_return_if_fail (gnutls->priv->have_key == FALSE);
       data.data = (void *)string;
       data.size = strlen (string);
+      if (!gnutls->priv->key)
+        gnutls_x509_privkey_init (&gnutls->priv->key);
       status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
                                           GNUTLS_X509_FMT_PEM);
+      if (status != 0)
+       {
+         int pkcs8_status =
+           gnutls_x509_privkey_import_pkcs8 (gnutls->priv->key, &data,
+                                             GNUTLS_X509_FMT_PEM, NULL,
+                                             GNUTLS_PKCS_PLAIN);
+         if (pkcs8_status == 0)
+           status = 0;
+       }
       if (status == 0)
        gnutls->priv->have_key = TRUE;
       else if (!gnutls->priv->construct_error)
@@ -245,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)
 {
@@ -253,7 +286,9 @@ g_tls_certificate_gnutls_init (GTlsCertificateGnutls *gnutls)
                                              GTlsCertificateGnutlsPrivate);
 
   gnutls_x509_crt_init (&gnutls->priv->cert);
-  gnutls_x509_privkey_init (&gnutls->priv->key);
+#if ENABLE(TIZEN_TV_ADJUST_TIME)
+  gnutls_global_set_time_function(correct_time_func);
+#endif
 }
 
 static gboolean
@@ -289,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)
@@ -318,18 +357,45 @@ g_tls_certificate_gnutls_verify (GTlsCertificate     *cert,
 
       gtls_flags = g_tls_certificate_gnutls_convert_flags (gnutls_flags);
     }
+  else
+    gtls_flags = 0;
 
   /* We have to check these ourselves since gnutls_x509_crt_list_verify
    * 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;
     }
@@ -343,6 +409,61 @@ g_tls_certificate_gnutls_verify (GTlsCertificate     *cert,
 }
 
 static void
+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;
+
+  /* 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);
+
+      st->cert.x509[st->ncerts] = cert;
+      st->ncerts++;
+
+      chain = chain->priv->issuer;
+    }
+
+  if (gnutls->priv->key != NULL)
+    {
+      gnutls_x509_privkey_init (&st->key.x509);
+      gnutls_x509_privkey_cpy (st->key.x509, gnutls->priv->key);
+      st->key_type = GNUTLS_PRIVKEY_X509;
+    }
+
+  st->deinit_all = TRUE;
+}
+
+static void
 g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
@@ -356,6 +477,8 @@ g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
 
   certificate_class->verify = g_tls_certificate_gnutls_verify;
 
+  klass->copy = g_tls_certificate_gnutls_real_copy;
+
   g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
   g_object_class_override_property (gobject_class, PROP_CERTIFICATE_PEM, "certificate-pem");
   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key");
@@ -370,63 +493,52 @@ g_tls_certificate_gnutls_initable_iface_init (GInitableIface  *iface)
 }
 
 GTlsCertificate *
-g_tls_certificate_gnutls_new (const gnutls_datum *datum,
-                             GTlsCertificate    *issuer)
+g_tls_certificate_gnutls_new (const gnutls_datum_t *datum,
+                             GTlsCertificate      *issuer)
 {
   GTlsCertificateGnutls *gnutls;
 
   gnutls = g_object_new (G_TYPE_TLS_CERTIFICATE_GNUTLS,
                         "issuer", issuer,
                         NULL);
-  if (gnutls_x509_crt_import (gnutls->priv->cert, datum,
-                             GNUTLS_X509_FMT_DER) == 0)
-    gnutls->priv->have_cert = TRUE;
+  g_tls_certificate_gnutls_set_data (gnutls, datum);
 
   return G_TLS_CERTIFICATE (gnutls);
 }
 
-const gnutls_x509_crt_t
-g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls)
+void
+g_tls_certificate_gnutls_set_data (GTlsCertificateGnutls *gnutls,
+                                   const gnutls_datum_t  *datum)
 {
-  return gnutls->priv->cert;
+  g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
+  g_return_if_fail (!gnutls->priv->have_cert);
+
+  if (gnutls_x509_crt_import (gnutls->priv->cert, datum,
+                              GNUTLS_X509_FMT_DER) == 0)
+    gnutls->priv->have_cert = TRUE;
 }
 
-const gnutls_x509_privkey_t
-g_tls_certificate_gnutls_get_key (GTlsCertificateGnutls *gnutls)
+const gnutls_x509_crt_t
+g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls)
 {
-  return gnutls->priv->key;
+  return gnutls->priv->cert;
 }
 
-gnutls_x509_crt_t
-g_tls_certificate_gnutls_copy_cert (GTlsCertificateGnutls *gnutls)
+gboolean
+g_tls_certificate_gnutls_has_key (GTlsCertificateGnutls *gnutls)
 {
-  gnutls_x509_crt_t cert;
-  gnutls_datum data;
-  size_t size;
-
-  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);
-
-  gnutls_x509_crt_init (&cert);
-  gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_DER);
-  g_free (data.data);
-
-  return cert;
+  return gnutls->priv->have_key;
 }
 
-gnutls_x509_privkey_t
-g_tls_certificate_gnutls_copy_key  (GTlsCertificateGnutls *gnutls)
+void
+g_tls_certificate_gnutls_copy  (GTlsCertificateGnutls *gnutls,
+                                const gchar           *interaction_id,
+                                gnutls_retr2_st       *st)
 {
-  gnutls_x509_privkey_t key;
-
-  gnutls_x509_privkey_init (&key);
-  gnutls_x509_privkey_cpy (key, gnutls->priv->key);
-  return key;
+  g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
+  g_return_if_fail (st != NULL);
+  g_return_if_fail (G_TLS_CERTIFICATE_GNUTLS_GET_CLASS (gnutls)->copy);
+  G_TLS_CERTIFICATE_GNUTLS_GET_CLASS (gnutls)->copy (gnutls, interaction_id, st);
 }
 
 static const struct {
@@ -493,6 +605,133 @@ 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;
 }
+
+void
+g_tls_certificate_gnutls_set_issuer (GTlsCertificateGnutls *gnutls,
+                                     GTlsCertificateGnutls *issuer)
+{
+  g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
+  g_return_if_fail (!issuer || G_IS_TLS_CERTIFICATE_GNUTLS (issuer));
+
+  if (issuer)
+    g_object_ref (issuer);
+  if (gnutls->priv->issuer)
+    g_object_unref (gnutls->priv->issuer);
+  gnutls->priv->issuer = issuer;
+  g_object_notify (G_OBJECT (gnutls), "issuer");
+}
+
+GBytes *
+g_tls_certificate_gnutls_get_bytes (GTlsCertificateGnutls *gnutls)
+{
+  GByteArray *array;
+
+  g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls), NULL);
+
+  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;
+}