[glib-networking/wip/nss: 2/2] nss: add an NSS-based GTlsBackend [wip]



commit 36b73df492ae2c16e0762657f8e863eca4c73740
Author: Dan Winship <danw gnome org>
Date:   Thu Sep 29 10:54:00 2011 -0400

    nss: add an NSS-based GTlsBackend [wip]

 .gitignore                         |    7 +-
 Makefile.am                        |    7 +
 configure.ac                       |   27 ++-
 po/POTFILES.in                     |    3 +
 tls/nss/Makefile.am                |   34 ++
 tls/nss/gtlsbackend-nss.c          |  157 +++++++
 tls/nss/gtlsbackend-nss.h          |   53 +++
 tls/nss/gtlscertificate-nss.c      |  872 ++++++++++++++++++++++++++++++++++++
 tls/nss/gtlscertificate-nss.h      |   62 +++
 tls/nss/gtlsclientconnection-nss.c |  218 +++++++++
 tls/nss/gtlsclientconnection-nss.h |   47 ++
 tls/nss/gtlsconnection-nss.c       |  433 ++++++++++++++++++
 tls/nss/gtlsconnection-nss.h       |   53 +++
 tls/nss/gtlsdatabase-nss.c         |  282 ++++++++++++
 tls/nss/gtlsdatabase-nss.h         |   60 +++
 tls/nss/gtlsfiledatabase-nss.c     |  317 +++++++++++++
 tls/nss/gtlsfiledatabase-nss.h     |   51 +++
 tls/nss/gtlsprfiledesc-nss.c       |  554 +++++++++++++++++++++++
 tls/nss/gtlsprfiledesc-nss.h       |   22 +
 tls/nss/gtlsserverconnection-nss.c |  140 ++++++
 tls/nss/gtlsserverconnection-nss.h |   47 ++
 tls/nss/nss-module.c               |   47 ++
 tls/tests/Makefile.am              |   36 ++-
 tls/tests/certificate.c            |   34 +-
 tls/tests/connection.c             |   36 ++-
 tls/tests/file-database.c          |    5 +-
 26 files changed, 3574 insertions(+), 30 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 0e70b23..28d9cdf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,9 +36,10 @@ proxy/libproxy/org.gtk.GLib.PACRunner.service
 proxy/tests/gnome
 proxy/tests/libproxy
 
-/tls/tests/certificate
-/tls/tests/file-database
-/tls/tests/connection
+/tls/tests/certificate-*
+/tls/tests/connection-*
+/tls/tests/file-database-*
+
 /tls/tests/pkcs11
 /tls/tests/pkcs11-array
 /tls/tests/pkcs11-pin
diff --git a/Makefile.am b/Makefile.am
index ff0f7b6..a561fd4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,6 +23,13 @@ endif
 
 if HAVE_GNUTLS
 SUBDIRS += tls/gnutls
+endif
+
+if HAVE_NSS
+SUBDIRS += tls/nss
+endif
+
+if HAVE_TLS
 SUBDIRS += tls/tests
 endif
 
diff --git a/configure.ac b/configure.ac
index 31d5b0a..c81f2dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -156,6 +156,30 @@ AM_CONDITIONAL(HAVE_PKCS11, [test "x$with_pkcs11" = "xyes"])
 AC_SUBST(PKCS11_CFLAGS)
 AC_SUBST(PKCS11_LIBS)
 
+dnl **********************
+dnl *** Checks for NSS ***
+dnl **********************
+
+AC_ARG_WITH(nss,
+    [AC_HELP_STRING([--with-nss],
+                    [support for NSS @<:@default=check@:>@])],
+    [],
+    [with_nss=check])
+AS_IF([test "$with_nss" != "no"],
+      [PKG_CHECK_MODULES(NSS, [nss],
+         [with_nss=yes
+         tls_support="nss $tls_support"],
+         [AS_IF([test "x$with_nss" = "xyes"],
+               [AC_MSG_FAILURE("$NSS_PKG_ERRORS")])])])
+AM_CONDITIONAL(HAVE_NSS, [test "$with_nss" = "yes"])
+AC_SUBST(NSS_CFLAGS)
+AC_SUBST(NSS_LIBS)
+
+dnl ***********************************
+dnl *** Do we have any TLS backend? ***
+dnl ***********************************
+AM_CONDITIONAL(HAVE_TLS, [test "$with_gnutls" = "yes" -o "$with_nss" = "yes"])
+
 dnl ************************************
 dnl *** Enable lcov coverage reports ***
 dnl ************************************
@@ -243,6 +267,7 @@ AC_CONFIG_FILES([Makefile
                  tls/base/Makefile
                  tls/gnutls/Makefile
                  tls/pkcs11/Makefile
+                 tls/nss/Makefile
                  tls/tests/Makefile
                 ])
 AC_OUTPUT
@@ -250,7 +275,7 @@ AC_OUTPUT
 echo ""
 echo     "  Proxy support:     ${proxy_support:-no}"
 echo     "  TLS support:       ${tls_support:-no}"
-if test "$tls_support" != "no"; then
+if test "$with_gnutls" = "yes"; then
     echo "  PKCS#11 Support:   $pkcs11_support"
     echo "  TLS CA file:       ${with_ca_certificates:-(none)}"
     if test -n "$with_ca_certificates"; then
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5608e05..5674230 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -4,5 +4,8 @@ tls/gnutls/gtlscertificate-gnutls.c
 tls/gnutls/gtlsclientconnection-gnutls.c
 tls/gnutls/gtlsconnection-gnutls.c
 tls/gnutls/gtlsserverconnection-gnutls.c
+tls/nss/gtlscertificate-nss.c
+tls/nss/gtlsconnection-nss.c
+tls/nss/gtlsfiledatabase-nss.c
 tls/pkcs11/gpkcs11pin.c
 tls/pkcs11/gpkcs11slot.c
diff --git a/tls/nss/Makefile.am b/tls/nss/Makefile.am
new file mode 100644
index 0000000..d59e069
--- /dev/null
+++ b/tls/nss/Makefile.am
@@ -0,0 +1,34 @@
+include $(top_srcdir)/Makefile.decl
+
+giomodule_LTLIBRARIES = libgionss.la
+
+libgionss_la_SOURCES =                 \
+       nss-module.c                    \
+       gtlsbackend-nss.c               \
+       gtlsbackend-nss.h               \
+       gtlscertificate-nss.c           \
+       gtlscertificate-nss.h           \
+       gtlsclientconnection-nss.c      \
+       gtlsclientconnection-nss.h      \
+       gtlsconnection-nss.c            \
+       gtlsconnection-nss.h            \
+       gtlsdatabase-nss.c              \
+       gtlsdatabase-nss.h              \
+       gtlsfiledatabase-nss.c          \
+       gtlsfiledatabase-nss.h          \
+       gtlsprfiledesc-nss.c            \
+       gtlsprfiledesc-nss.h            \
+       gtlsserverconnection-nss.c      \
+       gtlsserverconnection-nss.h      \
+       $(NULL)
+
+INCLUDES +=                            \
+       -I$(top_srcdir)/tls/base        \
+       $(NSS_CFLAGS)
+
+libgionss_la_LDFLAGS = $(module_flags)
+libgionss_la_LIBADD =                  \
+       ../base/libtlsbase.la           \
+       $(GLIB_LIBS)                    \
+       $(NSS_LIBS)                     \
+       $(NULL)
diff --git a/tls/nss/gtlsbackend-nss.c b/tls/nss/gtlsbackend-nss.c
new file mode 100644
index 0000000..8012c0f
--- /dev/null
+++ b/tls/nss/gtlsbackend-nss.c
@@ -0,0 +1,157 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 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
+ * License as published by the Free Software Foundation; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include <errno.h>
+
+#include <nss.h>
+#include <keyhi.h>
+#include <pk11pub.h>
+#include <secmod.h>
+#include <ssl.h>
+
+#include "gtlsbackend-nss.h"
+#include "gtlscertificate-nss.h"
+#include "gtlsclientconnection-nss.h"
+#include "gtlsfiledatabase-nss.h"
+#include "gtlsserverconnection-nss.h"
+
+GTlsDatabaseNss *g_tls_backend_nss_default_database;
+CERTCertDBHandle *g_tls_backend_nss_certdbhandle;
+PK11SlotInfo *g_tls_backend_nss_pem_slot;
+
+struct _GTlsBackendNssPrivate
+{
+  NSSInitContext *context;
+};
+
+static void g_tls_backend_nss_interface_init (GTlsBackendInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GTlsBackendNss, g_tls_backend_nss, G_TYPE_OBJECT, 0,
+                               G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_TLS_BACKEND,
+                                                              g_tls_backend_nss_interface_init);)
+
+static void
+g_tls_backend_nss_init (GTlsBackendNss *backend)
+{
+  static volatile gsize inited;
+  int i;
+
+  backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend, G_TYPE_TLS_BACKEND_NSS, GTlsBackendNssPrivate);
+
+  backend->priv->context = NSS_InitContext ("sql:/etc/pki/nssdb", "", "",
+                                           SECMOD_DB, NULL, 0);
+
+  /* FIXME? */
+  NSS_SetDomesticPolicy ();
+
+  if (g_once_init_enter (&inited))
+    {
+      SECMODModule *pem_module;
+
+      g_tls_backend_nss_certdbhandle = CERT_GetDefaultCertDB ();
+      g_tls_backend_nss_default_database = g_object_new (G_TYPE_TLS_DATABASE_NSS, NULL);
+
+      pem_module = SECMOD_LoadUserModule ("library=libnsspem.so name=PEM",
+                                         NULL, PR_FALSE);
+      g_assert (pem_module != NULL);
+
+      /* Find an open slot in the PEM loader; slot 0 is reserved for
+       * CA certificates.
+       */
+      for (i = 1; i <= 8; i++)
+       {
+         char *slot_name = g_strdup_printf ("PEM Token #%d", i);
+         PK11SlotInfo *slot = PK11_FindSlotByName (slot_name);
+         SECKEYPublicKeyList *pubkeys;
+
+         if (!slot)
+           continue;
+
+         pubkeys = PK11_ListPublicKeysInSlot (slot, NULL);
+         if (!pubkeys)
+           {
+             g_tls_backend_nss_pem_slot = slot;
+             break;
+           }
+
+         SECKEY_DestroyPublicKeyList (pubkeys);
+         PK11_FreeSlot (slot);
+       }
+
+      g_assert (g_tls_backend_nss_pem_slot != NULL);
+
+      g_once_init_leave (&inited, TRUE);
+    }
+}
+
+static void
+g_tls_backend_nss_finalize (GObject *object)
+{
+  GTlsBackendNss *backend = G_TLS_BACKEND_NSS (object);
+
+  if (backend->priv->context)
+    NSS_ShutdownContext (backend->priv->context);
+
+  G_OBJECT_CLASS (g_tls_backend_nss_parent_class)->finalize (object);
+}
+
+static void
+g_tls_backend_nss_class_init (GTlsBackendNssClass *backend_class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (backend_class);
+
+  g_type_class_add_private (backend_class, sizeof (GTlsBackendNssPrivate));
+
+  gobject_class->finalize = g_tls_backend_nss_finalize;
+}
+
+static void
+g_tls_backend_nss_class_finalize (GTlsBackendNssClass *backend_class)
+{
+}
+
+static GTlsDatabase *
+g_tls_backend_nss_get_default_database (GTlsBackend *backend)
+{
+  return g_object_ref (g_tls_backend_nss_default_database);
+}
+
+static void
+g_tls_backend_nss_interface_init (GTlsBackendInterface *iface)
+{
+  iface->get_certificate_type       = g_tls_certificate_nss_get_type;
+  iface->get_client_connection_type = g_tls_client_connection_nss_get_type;
+  iface->get_server_connection_type = g_tls_server_connection_nss_get_type;
+  iface->get_file_database_type     = g_tls_file_database_nss_get_type;
+  iface->get_default_database       = g_tls_backend_nss_get_default_database;
+}
+
+void
+g_tls_backend_nss_register (GIOModule *module)
+{
+  g_tls_backend_nss_register_type (G_TYPE_MODULE (module));
+  g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME,
+                                 g_tls_backend_nss_get_type(),
+                                 "nss",
+                                 0);
+}
diff --git a/tls/nss/gtlsbackend-nss.h b/tls/nss/gtlsbackend-nss.h
new file mode 100644
index 0000000..22c7743
--- /dev/null
+++ b/tls/nss/gtlsbackend-nss.h
@@ -0,0 +1,53 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This program 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_BACKEND_NSS_H__
+#define __G_TLS_BACKEND_NSS_H__
+
+#include <gio/gio.h>
+
+#include "gtlsdatabase-nss.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_BACKEND_NSS            (g_tls_backend_nss_get_type ())
+#define G_TLS_BACKEND_NSS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_BACKEND_NSS, 
GTlsBackendNss))
+#define G_TLS_BACKEND_NSS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_BACKEND_NSS, 
GTlsBackendNssClass))
+#define G_IS_TLS_BACKEND_NSS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_BACKEND_NSS))
+#define G_IS_TLS_BACKEND_NSS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_BACKEND_NSS))
+#define G_TLS_BACKEND_NSS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_BACKEND_NSS, 
GTlsBackendNssClass))
+
+typedef struct _GTlsBackendNss        GTlsBackendNss;
+typedef struct _GTlsBackendNssClass   GTlsBackendNssClass;
+typedef struct _GTlsBackendNssPrivate GTlsBackendNssPrivate;
+
+struct _GTlsBackendNssClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GTlsBackendNss
+{
+  GObject parent_instance;
+  GTlsBackendNssPrivate *priv;
+};
+
+GType g_tls_backend_nss_get_type (void) G_GNUC_CONST;
+void  g_tls_backend_nss_register (GIOModule *module);
+
+extern GTlsDatabaseNss *g_tls_backend_nss_default_database;
+extern CERTCertDBHandle *g_tls_backend_nss_certdbhandle;
+extern PK11SlotInfo *g_tls_backend_nss_pem_slot;
+
+G_END_DECLS
+
+#endif /* __G_TLS_BACKEND_NSS_H___ */
diff --git a/tls/nss/gtlscertificate-nss.c b/tls/nss/gtlscertificate-nss.c
new file mode 100644
index 0000000..e7b8fb7
--- /dev/null
+++ b/tls/nss/gtlscertificate-nss.c
@@ -0,0 +1,872 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 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
+ * License as published by the Free Software Foundation; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include <errno.h>
+
+#include <cert.h>
+#include <keyhi.h>
+#include <pk11pub.h>
+#include <secerr.h>
+
+#include "gtlscertificate-nss.h"
+#include "gtlsbackend-nss.h"
+#include "gtlsdatabase-nss.h"
+#include "gtlsfiledatabase-nss.h"
+#include <glib/gi18n-lib.h>
+
+static void     g_tls_certificate_nss_initable_iface_init (GInitableIface  *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsCertificateNss, g_tls_certificate_nss, G_TYPE_TLS_CERTIFICATE,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                               g_tls_certificate_nss_initable_iface_init);)
+
+enum
+{
+  PROP_0,
+
+  PROP_CERTIFICATE,
+  PROP_CERTIFICATE_PEM,
+  PROP_CERTIFICATE_FILE,
+  PROP_PRIVATE_KEY,
+  PROP_PRIVATE_KEY_PEM,
+  PROP_PRIVATE_KEY_FILE,
+  PROP_ISSUER
+};
+
+struct _GTlsCertificateNssPrivate
+{
+  CERTCertificate *cert;
+  SECKEYPrivateKey *key;
+
+  GTlsCertificateNss *issuer;
+  gboolean expanded;
+
+  GError *construct_error;
+  char *cert_file, *key_file;
+  gboolean delete_key_file;
+};
+
+static gboolean load_from_files (GTlsCertificateNss  *nss,
+                                GError             **error);
+
+static gboolean
+idle_unref (gpointer obj)
+{
+  g_object_unref (obj);
+  return FALSE;
+}
+
+static GObject *
+g_tls_certificate_nss_constructor (GType                  type,
+                                  guint                  n_construct_properties,
+                                  GObjectConstructParam *construct_properties)
+{
+  GObject *obj;
+  GTlsCertificateNss *nss, *existing;
+
+  obj = G_OBJECT_CLASS (g_tls_certificate_nss_parent_class)->constructor (type, n_construct_properties, 
construct_properties);
+  nss = G_TLS_CERTIFICATE_NSS (obj);
+
+  if (nss->priv->construct_error)
+    return obj;
+
+  if (!nss->priv->cert && !nss->priv->cert_file)
+    return obj;
+
+  /* We can't do this in set_property() because cert_file and key_file
+   * could be set in either order.
+   */
+  if (nss->priv->cert_file || nss->priv->key_file)
+    load_from_files (nss, &nss->priv->construct_error);
+
+  if (nss->priv->construct_error)
+    return obj;
+
+  /* If this is a duplicate of an existing certificate then NSS will
+   * have returned a new reference to the same CERTCertificate it created
+   * before. In order to be able to map CERTCertificates to
+   * GTlsCertificates then, we need to uniquify GTlsCertificates too.
+   * So check if we already have one for this cert.
+   */
+  existing = g_tls_database_nss_get_gcert (g_tls_backend_nss_default_database,
+                                          nss->priv->cert, FALSE);
+  if (existing)
+    {
+      /* Return the existing one rather than the new one, but we can't
+       * just unref the new one immediately.
+       * (https://bugzilla.gnome.org/show_bug.cgi?id=661576).
+       */
+      g_idle_add (idle_unref, obj);
+      obj = G_OBJECT (existing);
+    }
+  else
+    {
+      g_tls_database_nss_gcert_created (g_tls_backend_nss_default_database,
+                                       nss->priv->cert, nss);
+    }
+
+  return obj;
+}
+
+static void
+g_tls_certificate_nss_finalize (GObject *object)
+{
+  GTlsCertificateNss *nss = G_TLS_CERTIFICATE_NSS (object);
+
+  if (nss->priv->cert)
+    {
+      g_tls_database_nss_gcert_destroyed (g_tls_backend_nss_default_database,
+                                         nss->priv->cert);
+      CERT_DestroyCertificate (nss->priv->cert);
+    }
+  if (nss->priv->key)
+    SECKEY_DestroyPrivateKey (nss->priv->key);
+
+  g_clear_object (&nss->priv->issuer);
+
+  g_clear_error (&nss->priv->construct_error);
+
+  g_free (nss->priv->cert_file);
+  if (nss->priv->delete_key_file)
+    unlink (nss->priv->key_file);
+  g_free (nss->priv->key_file);
+
+  G_OBJECT_CLASS (g_tls_certificate_nss_parent_class)->finalize (object);
+}
+
+static PK11GenericObject *
+create_pkcs11_object (const char      *filename,
+                     CK_OBJECT_CLASS  obj_class)
+{
+  CK_BBOOL cktrue = CK_TRUE;
+  CK_ATTRIBUTE attrs[3];
+
+  attrs[0].type = CKA_CLASS;
+  attrs[0].pValue = &obj_class;
+  attrs[0].ulValueLen = sizeof (obj_class);
+
+  attrs[1].type = CKA_TOKEN;
+  attrs[1].pValue = &cktrue;
+  attrs[1].ulValueLen = sizeof (CK_BBOOL);
+
+  attrs[2].type = CKA_LABEL;
+  attrs[2].pValue = (unsigned char *)filename;
+  attrs[2].ulValueLen = strlen (filename) + 1;
+
+  return PK11_CreateGenericObject (g_tls_backend_nss_pem_slot,
+                                  attrs, 3, PR_FALSE);
+}
+
+static gboolean
+load_from_files (GTlsCertificateNss  *nss,
+                GError             **error)
+{
+  PK11GenericObject *cert_obj, *key_obj = NULL;
+  SECItem cert_der, id;
+
+  if (nss->priv->cert_file)
+    {
+      cert_obj = create_pkcs11_object (nss->priv->cert_file, CKO_CERTIFICATE);
+      if (!cert_obj)
+       {
+       cert_fail:
+         g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                              _("Could not read certificate data"));
+         return FALSE;
+       }
+
+      if (PK11_ReadRawAttribute (PK11_TypeGeneric, cert_obj,
+                                CKA_VALUE, &cert_der) != SECSuccess)
+       {
+         PK11_DestroyGenericObject (cert_obj);
+         goto cert_fail;
+       }
+
+      nss->priv->cert = PK11_FindCertFromDERCertItem (g_tls_backend_nss_pem_slot,
+                                                     &cert_der, NULL);
+      SECITEM_FreeItem (&cert_der, PR_FALSE);
+      PK11_DestroyGenericObject (cert_obj);
+      if (!nss->priv->cert)
+       goto cert_fail;
+    }
+
+  /* Try to create a private key object from the cert file if a
+   * separate key file was not specified, but don't consider failure
+   * to be an error in that case.
+   */
+  key_obj = create_pkcs11_object (nss->priv->key_file ?
+                                 nss->priv->key_file :
+                                 nss->priv->cert_file,
+                                 CKO_PRIVATE_KEY);
+  if (!key_obj)
+    {
+      if (!nss->priv->key_file)
+       return TRUE;
+
+    key_fail:
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                          _("Could not read private key data"));
+      return FALSE;
+    }
+
+  /* In theory we should be able to use PK11_FindKeyByDERCert here,
+   * but it doesn't work right with the PEM module.
+   */
+  if (PK11_ReadRawAttribute (PK11_TypePrivKey, key_obj,
+                            CKA_ID, &id) != SECSuccess)
+    {
+      PK11_DestroyGenericObject (key_obj);
+      goto key_fail;
+    }
+  nss->priv->key = PK11_FindKeyByKeyID (g_tls_backend_nss_pem_slot,
+                                       &id, NULL);
+  SECITEM_FreeItem (&id, PR_FALSE);
+  PK11_DestroyGenericObject (key_obj);
+  if (!nss->priv->key)
+    goto key_fail;
+
+  if (nss->priv->delete_key_file)
+    {
+      unlink (nss->priv->key_file);
+      nss->priv->delete_key_file = FALSE;
+    }
+
+  return TRUE;
+}
+
+#define PEM_CERTIFICATE_HEADER "-----BEGIN CERTIFICATE-----"
+#define PEM_CERTIFICATE_FOOTER "-----END CERTIFICATE-----"
+#define PEM_PRIVKEY_HEADER     "-----BEGIN RSA PRIVATE KEY-----"
+#define PEM_PRIVKEY_FOOTER     "-----END RSA PRIVATE KEY-----"
+#define PEM_PKCS8_HEADER     "-----BEGIN PRIVATE KEY-----"
+#define PEM_PKCS8_FOOTER     "-----END PRIVATE KEY-----"
+
+static char *
+encode_pem (const char *header,
+           const char *footer,
+           guint8     *data,
+           gsize       length)
+{
+  GString *pem;
+  char *out;
+  int encoded_len, broken_len, full_len;
+  int state = 0, save = 0;
+
+  encoded_len = (length / 3 + 1) * 4;
+  broken_len = encoded_len + (encoded_len / 72) + 1;
+  full_len = strlen (header) + 1 + broken_len + strlen (footer) + 1;
+
+  pem = g_string_sized_new (full_len + 1);
+  g_string_append (pem, header);
+  g_string_append_c (pem, '\n');
+  out = pem->str + pem->len;
+  out += g_base64_encode_step (data, length, TRUE, out, &state, &save);
+  out += g_base64_encode_close (TRUE, out, &state, &save);
+  pem->len = out - pem->str;
+  g_string_append (pem, footer);
+  g_string_append_c (pem, '\n');
+
+  return g_string_free (pem, FALSE);
+}
+
+static void
+g_tls_certificate_nss_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  GTlsCertificateNss *nss = G_TLS_CERTIFICATE_NSS (object);
+  CERTCertificate *nss_cert = nss->priv->cert;
+
+  switch (prop_id)
+    {
+    case PROP_CERTIFICATE:
+      if (nss_cert)
+       {
+         GByteArray *certificate;
+
+         certificate = g_byte_array_sized_new (nss_cert->derCert.len);
+         certificate->len = nss_cert->derCert.len;
+         memcpy (certificate->data, nss_cert->derCert.data,
+                 nss_cert->derCert.len);
+         g_value_take_boxed (value, certificate);
+       }
+      else
+       g_value_set_boxed (value, NULL);
+      break;
+
+    case PROP_CERTIFICATE_PEM:
+      if (nss_cert)
+       {
+         g_value_take_string (value, encode_pem (PEM_CERTIFICATE_HEADER,
+                                                 PEM_CERTIFICATE_FOOTER,
+                                                 nss_cert->derCert.data,
+                                                 nss_cert->derCert.len));
+       }
+      else
+       g_value_set_string (value, NULL);
+      break;
+
+    case PROP_ISSUER:
+      g_value_set_object (value, nss->priv->issuer);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_certificate_nss_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  GTlsCertificateNss *nss = G_TLS_CERTIFICATE_NSS (object);
+  GByteArray *bytes;
+  const gchar *string;
+  gchar *free_string = NULL;
+  gint fd, left, nwrote;
+
+  if (nss->priv->construct_error)
+    return;
+
+  switch (prop_id)
+    {
+    case PROP_CERTIFICATE:
+      bytes = g_value_get_boxed (value);
+      if (!bytes)
+       break;
+      g_return_if_fail (nss->priv->cert == NULL);
+      g_return_if_fail (nss->priv->cert_file == NULL);
+
+      /* Make sure it's really DER */
+      if (!g_strstr_len ((gchar *)bytes->data, bytes->len, PEM_CERTIFICATE_HEADER))
+       nss->priv->cert = CERT_DecodeCertFromPackage ((gchar *)bytes->data, bytes->len);
+
+      if (!nss->priv->cert)
+       {
+         nss->priv->construct_error =
+           g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                        _("Could not parse DER certificate"));
+       }
+      break;
+
+    case PROP_CERTIFICATE_PEM:
+      string = g_value_get_string (value);
+      if (!string)
+       break;
+      g_return_if_fail (nss->priv->cert == NULL);
+      g_return_if_fail (nss->priv->cert_file == NULL);
+
+      /* Make sure it's really PEM */
+      if (strstr (string, PEM_CERTIFICATE_HEADER))
+       nss->priv->cert = CERT_DecodeCertFromPackage ((gchar *)string, strlen (string));
+
+      if (!nss->priv->cert)
+       {
+         nss->priv->construct_error =
+           g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                        _("Could not parse PEM certificate"));
+       }
+      break;
+
+    case PROP_CERTIFICATE_FILE:
+      string = g_value_get_string (value);
+      if (!string)
+       break;
+      g_return_if_fail (nss->priv->cert == NULL);
+      g_return_if_fail (nss->priv->cert_file == NULL);
+
+      nss->priv->cert_file = g_strdup (string);
+      break;
+
+    case PROP_PRIVATE_KEY:
+      bytes = g_value_get_boxed (value);
+      if (!bytes)
+       break;
+      g_return_if_fail (nss->priv->key_file == NULL);
+
+      /* Make sure it's really DER */
+      if (g_strstr_len ((gchar *)bytes->data, bytes->len, PEM_PRIVKEY_HEADER) ||
+         g_strstr_len ((gchar *)bytes->data, bytes->len, PEM_PKCS8_HEADER))
+       {
+         nss->priv->construct_error =
+           g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                        _("Could not parse DER private key"));
+         break;
+       }
+      string = free_string = encode_pem (PEM_PRIVKEY_HEADER,
+                                        PEM_PRIVKEY_FOOTER,
+                                        bytes->data, bytes->len);
+      goto write_private_key_pem;      
+
+    case PROP_PRIVATE_KEY_PEM:
+      string = g_value_get_string (value);
+      if (!string)
+       break;
+      g_return_if_fail (nss->priv->key_file == NULL);
+
+      /* Make sure it's really PEM */
+      if (!strstr (string, PEM_PRIVKEY_HEADER) &&
+         !strstr (string, PEM_PKCS8_HEADER))
+       {
+         nss->priv->construct_error =
+           g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                        _("Could not parse PEM certificate"));
+         break;
+       }
+
+    write_private_key_pem:
+      /* There is no public NSS API to create a SECKEYPrivateKey from
+       * raw DER data. So we have to have the PEM PKCS#11 module do it
+       * for us, which means we have to write the data out to a
+       * temporary file. Ugh.
+       */
+      nss->priv->key_file = g_build_filename (g_get_user_runtime_dir (),
+                                             "XXXXXX.key",
+                                             NULL);
+      fd = g_mkstemp (nss->priv->key_file);
+      if (fd == -1)
+       {
+         int errsv = errno;
+
+         g_set_error (&nss->priv->construct_error,
+                      G_IO_ERROR, g_io_error_from_errno (errsv),
+                      _("Unable to create temporary private key file: %s"),
+                      g_strerror (errsv));
+       }
+      else
+       {
+         left = strlen (string);
+         while (left)
+           {
+             nwrote = write (fd, string, left);
+             if (nwrote == -1)
+               {
+                 int errsv = errno;
+
+                 if (errno == EINTR)
+                   continue;
+                 g_set_error (&nss->priv->construct_error,
+                              G_IO_ERROR, g_io_error_from_errno (errsv),
+                              _("Unable to create temporary private key file: %s"),
+                              g_strerror (errsv));
+                 break;
+               }
+
+             string += nwrote;
+             left -= nwrote;
+           }
+
+         close (fd);
+         nss->priv->delete_key_file = TRUE;
+       }
+      break;
+
+    case PROP_PRIVATE_KEY_FILE:
+      string = g_value_get_string (value);
+      if (!string)
+       break;
+      g_return_if_fail (nss->priv->key_file == NULL);
+
+      nss->priv->key_file = g_strdup (string);
+      break;
+
+    case PROP_ISSUER:
+      nss->priv->issuer = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+
+  g_free (free_string);
+}
+
+static void
+g_tls_certificate_nss_init (GTlsCertificateNss *nss)
+{
+  nss->priv = G_TYPE_INSTANCE_GET_PRIVATE (nss,
+                                          G_TYPE_TLS_CERTIFICATE_NSS,
+                                          GTlsCertificateNssPrivate);
+}
+
+static gboolean
+g_tls_certificate_nss_initable_init (GInitable       *initable,
+                                    GCancellable    *cancellable,
+                                    GError         **error)
+{
+  GTlsCertificateNss *nss = G_TLS_CERTIFICATE_NSS (initable);
+
+  if (nss->priv->construct_error)
+    {
+      g_propagate_error (error, nss->priv->construct_error);
+      nss->priv->construct_error = NULL;
+      return FALSE;
+    }
+
+  if (!nss->priv->cert)
+    {
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                          _("No certificate data provided"));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+/* FIXME: mostly dup from gnutls */
+static GTlsCertificateFlags
+g_tls_certificate_nss_verify_identity (GTlsCertificateNss *nss,
+                                      GSocketConnectable *identity)
+{
+  const char *hostname;
+
+  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
+    hostname = NULL;
+
+  if (hostname)
+    {
+      if (CERT_VerifyCertName (nss->priv->cert, hostname) == SECSuccess)
+       return 0;
+    }
+
+  /* FIXME: check sRVName and uniformResourceIdentifier
+   * subjectAltNames, if appropriate for @identity.
+   */
+
+  return G_TLS_CERTIFICATE_BAD_IDENTITY;
+}
+
+static void
+g_tls_certificate_nss_expand_chain (GTlsCertificateNss *nss_cert)
+{
+  GTlsCertificateNss *c, *issuer = NULL;
+  CERTCertificateList *list;
+  CERTCertificate *cert;
+  SECCertUsage usage;
+  int i;
+
+  g_return_if_fail (nss_cert->priv->cert->nsCertType != 0);
+
+  if (nss_cert->priv->expanded)
+    return;
+
+  if (nss_cert->priv->cert->nsCertType & NS_CERT_TYPE_SSL_CLIENT)
+    usage = certUsageSSLClient;
+  else
+    usage = certUsageSSLServer;
+
+  list = CERT_CertChainFromCert (nss_cert->priv->cert, usage, PR_TRUE);
+  /* list->certs[0] is nss_cert itself, so start from index 1 */
+  for (i = 1, c = nss_cert; i < list->len; i++, c = c->priv->issuer)
+    {
+      cert = CERT_FindCertByDERCert (g_tls_backend_nss_certdbhandle,
+                                    &list->certs[i]);
+      issuer = g_tls_database_nss_get_gcert (g_tls_backend_nss_default_database, cert, TRUE);
+      CERT_DestroyCertificate (cert);
+
+      if (c->priv->issuer == issuer)
+       {
+         g_object_unref (issuer);
+         break;
+       }
+
+      if (c->priv->issuer)
+       g_object_unref (c->priv->issuer);
+      c->priv->issuer = issuer;
+      c->priv->expanded = TRUE;
+    }
+
+  if (i == list->len && issuer)
+    {
+      issuer->priv->expanded = TRUE;
+      g_clear_object (&issuer->priv->issuer);
+    }
+
+  CERT_DestroyCertificateList (list);
+}
+
+/* Our certificate verification routine... called by both
+ * g_tls_certificate_nss_verify() and g_tls_database_nss_verify_chain().
+ *
+ * For our verification purposes, we have to treat the certificates in
+ * @database or @trusted_ca as though they were trusted, but we can't
+ * actually mark them trusted because we don't know why our caller is
+ * currently considering them trusted, so we can't let that trust
+ * "leak" into the rest of the program.
+ *
+ * Fortunately, NSS will tell us in excruciating detail exactly what it
+ * doesn't like about a certificate, and so if the only problem with
+ * the cert is that it's signed by a @database or @trusted_ca cert that
+ * NSS doesn't like, then we can just ignore that error.
+ */
+GTlsCertificateFlags
+g_tls_certificate_nss_verify_full (GTlsCertificate          *chain,
+                                  GTlsDatabase             *database,
+                                  GTlsCertificate          *trusted_ca,
+                                  const gchar              *purpose,
+                                  GSocketConnectable       *identity,
+                                  GTlsInteraction          *interaction,
+                                  GTlsDatabaseVerifyFlags   flags,
+                                  GCancellable             *cancellable,
+                                  GError                  **error)
+{
+  GTlsCertificateNss *nss_cert = G_TLS_CERTIFICATE_NSS (chain);
+  GTlsCertificateFlags result;
+  SECCertificateUsage usage;
+  PLArenaPool *arena;
+  CERTVerifyLog *log;
+  CERTVerifyLogNode *node;
+  PRTime now = PR_Now ();
+  SECCertTimeValidity time_validity;
+  int trusted_ca_index = -1;
+
+  g_return_val_if_fail (database == NULL || trusted_ca == NULL, G_TLS_CERTIFICATE_GENERIC_ERROR);
+
+  if (database == (GTlsDatabase *)g_tls_backend_nss_default_database)
+    database = NULL;
+
+  if (!strcmp (purpose, G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER))
+    usage = certificateUsageSSLServer;
+  else if (!strcmp (purpose, G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT))
+    usage = certificateUsageSSLClient;
+  else
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                  _("Unsupported key purpose OID '%s'"), purpose);
+      return G_TLS_CERTIFICATE_GENERIC_ERROR;
+    }
+
+  result = 0;
+
+  /* Verify the certificate and log all errors. As a side effect, this
+   * will ensure that nss_cert->priv->cert->nsCertType is set.
+   */
+  arena = PORT_NewArena (512);
+  log = PORT_ArenaZNew (arena, CERTVerifyLog);
+  log->arena = arena;
+  CERT_VerifyCert (g_tls_backend_nss_certdbhandle, nss_cert->priv->cert,
+                  PR_TRUE, usage, now, interaction, log);
+
+  /* Now expand the gtls-level chain, and see if it contains a cert
+   * from @trusted_ca or @database, and if so, remember where in the
+   * chain it is.
+   */
+  g_tls_certificate_nss_expand_chain (nss_cert);
+  if (database || trusted_ca)
+    {
+      GTlsFileDatabaseNss *db_nss = database ? G_TLS_FILE_DATABASE_NSS (database) : NULL;
+      GTlsCertificateNss *c;
+      int n;
+
+      for (c = nss_cert, n = 0; c; c = c->priv->issuer, n++)
+       {
+         if (trusted_ca && c == (GTlsCertificateNss *)trusted_ca)
+           break;
+         if (db_nss && g_tls_file_database_nss_contains (db_nss, c))
+           break;
+       }
+
+      if (c)
+       trusted_ca_index = n;
+      else
+       result |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+    }
+
+  /* Now go through the verification log translating the errors */
+  for (node = log->head; node; node = node->next)
+    {
+      if (trusted_ca_index != -1 && node->depth > trusted_ca_index)
+       break;
+
+      switch (node->error)
+       {
+       case SEC_ERROR_INADEQUATE_KEY_USAGE:
+         /* Cert is not appropriately tagged for signing. For
+          * historical/compatibility reasons, we ignore this when
+          * using PEM certificates.
+          */
+         if (database || trusted_ca)
+           break;
+         /* else fall through */
+
+       case SEC_ERROR_UNKNOWN_ISSUER:
+         /* Cert was issued by an unknown CA */
+       case SEC_ERROR_UNTRUSTED_ISSUER:
+         /* Cert is a CA that is not marked trusted */
+       case SEC_ERROR_CA_CERT_INVALID:
+         /* Cert is not a CA */
+
+         /* These are all OK if they occur on the trusted CA, but not
+          * before it.
+          */
+         if (node->depth != trusted_ca_index)
+           result |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+         break;
+
+       case SEC_ERROR_CERT_NOT_IN_NAME_SPACE:
+         /* Cert is not authorized to sign the cert it signed */
+         result |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+         break;
+
+       case SEC_ERROR_EXPIRED_CERTIFICATE:
+       case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+         /* Cert is either expired or not yet valid;
+          * CERT_VerifyCert() doesn't distinguish.
+          */
+         time_validity =  CERT_CheckCertValidTimes (node->cert, now, PR_FALSE);
+         if (time_validity == secCertTimeNotValidYet)
+           result |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
+         else if (time_validity == secCertTimeExpired)
+           result |= G_TLS_CERTIFICATE_EXPIRED;
+         break;
+
+       case SEC_ERROR_REVOKED_CERTIFICATE:
+         result |= G_TLS_CERTIFICATE_REVOKED;
+         break;
+
+       default:
+         result |= G_TLS_CERTIFICATE_GENERIC_ERROR;
+         break;
+       }
+
+      CERT_DestroyCertificate (node->cert);
+    }
+
+  for (; node; node = node->next)
+    CERT_DestroyCertificate (node->cert);
+  PORT_FreeArena (log->arena, PR_FALSE);
+
+  if (identity)
+    result |= g_tls_certificate_nss_verify_identity (nss_cert, identity);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    result = G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+  return result;
+}
+
+static GTlsCertificateFlags
+g_tls_certificate_nss_verify (GTlsCertificate     *cert,
+                             GSocketConnectable  *identity,
+                             GTlsCertificate     *trusted_ca)
+{
+  GTlsCertificateNss *nss_cert = G_TLS_CERTIFICATE_NSS (cert);
+  GTlsCertificateFlags flags;
+
+  /* nss_cert->priv->cert->nsCertType may not have been set yet, but
+   * it will get set as a side effect of verifying the cert. If we
+   * don't know yet what kind of key it is, we'll try server first.
+   */
+
+  if ((nss_cert->priv->cert->nsCertType & NS_CERT_TYPE_SSL_SERVER) ||
+      (nss_cert->priv->cert->nsCertType == 0))
+    {
+      flags = g_tls_certificate_nss_verify_full (cert, NULL, trusted_ca,
+                                                G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+                                                identity, NULL, 0, NULL, NULL);
+    }
+
+  if (!(nss_cert->priv->cert->nsCertType & NS_CERT_TYPE_SSL_SERVER))
+    {
+      flags = g_tls_certificate_nss_verify_full (cert, NULL, trusted_ca,
+                                                G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT,
+                                                identity, NULL, 0, NULL, NULL);
+    }
+
+  return flags;
+}
+
+static void
+g_tls_certificate_nss_class_init (GTlsCertificateNssClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsCertificateClass *certificate_class = G_TLS_CERTIFICATE_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsCertificateNssPrivate));
+
+  gobject_class->constructor  = g_tls_certificate_nss_constructor;
+  gobject_class->get_property = g_tls_certificate_nss_get_property;
+  gobject_class->set_property = g_tls_certificate_nss_set_property;
+  gobject_class->finalize     = g_tls_certificate_nss_finalize;
+
+  certificate_class->verify = g_tls_certificate_nss_verify;
+
+  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");
+  g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem");
+  g_object_class_override_property (gobject_class, PROP_ISSUER, "issuer");
+
+  g_object_class_install_property (gobject_class, PROP_CERTIFICATE_FILE,
+                                  g_param_spec_string ("certificate-file",
+                                                       "Certificate file",
+                                                       "File containing the certificate",
+                                                       NULL,
+                                                       G_PARAM_WRITABLE |
+                                                       G_PARAM_CONSTRUCT_ONLY |
+                                                       G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, PROP_PRIVATE_KEY_FILE,
+                                  g_param_spec_string ("private-key-file",
+                                                       "Private key file",
+                                                       "File containing the private key",
+                                                       NULL,
+                                                       G_PARAM_WRITABLE |
+                                                       G_PARAM_CONSTRUCT_ONLY |
+                                                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+g_tls_certificate_nss_initable_iface_init (GInitableIface  *iface)
+{
+  iface->init = g_tls_certificate_nss_initable_init;
+}
+
+GTlsCertificateNss *
+g_tls_certificate_nss_new_for_cert (CERTCertificate *cert)
+{
+  GTlsCertificateNss *nss;
+
+  nss = g_object_new (G_TYPE_TLS_CERTIFICATE_NSS, NULL);
+  nss->priv->cert = CERT_DupCertificate (cert);
+
+  return nss;
+}
+
+CERTCertificate *
+g_tls_certificate_nss_get_cert (GTlsCertificateNss *nss)
+{
+  return nss->priv->cert;
+}
+
+SECKEYPrivateKey *
+g_tls_certificate_nss_get_key (GTlsCertificateNss *nss)
+{
+  return nss->priv->key;
+}
diff --git a/tls/nss/gtlscertificate-nss.h b/tls/nss/gtlscertificate-nss.h
new file mode 100644
index 0000000..6b74f40
--- /dev/null
+++ b/tls/nss/gtlscertificate-nss.h
@@ -0,0 +1,62 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc
+ *
+ * This program 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_CERTIFICATE_NSS_H__
+#define __G_TLS_CERTIFICATE_NSS_H__
+
+#include <gio/gio.h>
+#include <cert.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CERTIFICATE_NSS            (g_tls_certificate_nss_get_type ())
+#define G_TLS_CERTIFICATE_NSS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), 
G_TYPE_TLS_CERTIFICATE_NSS, GTlsCertificateNss))
+#define G_TLS_CERTIFICATE_NSS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CERTIFICATE_NSS, 
GTlsCertificateNssClass))
+#define G_IS_TLS_CERTIFICATE_NSS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), 
G_TYPE_TLS_CERTIFICATE_NSS))
+#define G_IS_TLS_CERTIFICATE_NSS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CERTIFICATE_NSS))
+#define G_TLS_CERTIFICATE_NSS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), 
G_TYPE_TLS_CERTIFICATE_NSS, GTlsCertificateNssClass))
+
+typedef struct _GTlsCertificateNssPrivate                   GTlsCertificateNssPrivate;
+typedef struct _GTlsCertificateNssClass                     GTlsCertificateNssClass;
+typedef struct _GTlsCertificateNss                          GTlsCertificateNss;
+
+struct _GTlsCertificateNssClass
+{
+  GTlsCertificateClass parent_class;
+};
+
+struct _GTlsCertificateNss
+{
+  GTlsCertificate parent_instance;
+  GTlsCertificateNssPrivate *priv;
+};
+
+GType g_tls_certificate_nss_get_type (void) G_GNUC_CONST;
+
+GTlsCertificateNss   *g_tls_certificate_nss_new_for_cert (CERTCertificate          *cert);
+
+CERTCertificate      *g_tls_certificate_nss_get_cert     (GTlsCertificateNss       *nss);
+SECKEYPrivateKey     *g_tls_certificate_nss_get_key      (GTlsCertificateNss       *nss);
+
+GTlsCertificateFlags  g_tls_certificate_nss_verify_full  (GTlsCertificate          *chain,
+                                                         GTlsDatabase             *database,
+                                                         GTlsCertificate          *trusted_ca,
+                                                         const gchar              *purpose,
+                                                         GSocketConnectable       *identity,
+                                                         GTlsInteraction          *interaction,
+                                                         GTlsDatabaseVerifyFlags   flags,
+                                                         GCancellable             *cancellable,
+                                                         GError                  **error);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CERTIFICATE_NSS_H___ */
diff --git a/tls/nss/gtlsclientconnection-nss.c b/tls/nss/gtlsclientconnection-nss.c
new file mode 100644
index 0000000..46da8d0
--- /dev/null
+++ b/tls/nss/gtlsclientconnection-nss.c
@@ -0,0 +1,218 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 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
+ * License as published by the Free Software Foundation; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <glib.h>
+
+#include "gtlsclientconnection-nss.h"
+#include "gtlscertificate-nss.h"
+
+#include <key.h>
+
+#include <glib/gi18n-lib.h>
+
+enum
+{
+  PROP_0,
+  PROP_VALIDATION_FLAGS,
+  PROP_SERVER_IDENTITY,
+  PROP_USE_SSL3,
+  PROP_ACCEPTED_CAS
+};
+
+SECStatus g_tls_client_connection_nss_certificate_requested (void              *arg,
+                                                            PRFileDesc        *fd,
+                                                            CERTDistNames     *caNames,
+                                                            CERTCertificate  **pRetCert,
+                                                            SECKEYPrivateKey **pRetKey);
+
+static void g_tls_client_connection_nss_client_connection_interface_init (GTlsClientConnectionInterface 
*iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsClientConnectionNss, g_tls_client_connection_nss, G_TYPE_TLS_CONNECTION_NSS,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_TLS_CLIENT_CONNECTION,
+                                               
g_tls_client_connection_nss_client_connection_interface_init));
+
+struct _GTlsClientConnectionNssPrivate
+{
+  GTlsCertificateFlags validation_flags;
+  GSocketConnectable *server_identity;
+  gboolean use_ssl3;
+  GList *accepted_cas;
+};
+
+static void
+g_tls_client_connection_nss_init (GTlsClientConnectionNss *nss)
+{
+  GTlsConnectionNss *conn_nss = G_TLS_CONNECTION_NSS (nss);
+
+  nss->priv = G_TYPE_INSTANCE_GET_PRIVATE (nss, G_TYPE_TLS_CLIENT_CONNECTION_NSS, 
GTlsClientConnectionNssPrivate);
+
+  SSL_ResetHandshake (conn_nss->prfd, PR_FALSE);
+  SSL_GetClientAuthDataHook (conn_nss->prfd, g_tls_client_connection_nss_certificate_requested, nss);
+}
+
+static void
+g_tls_client_connection_nss_finalize (GObject *object)
+{
+  GTlsClientConnectionNss *nss = G_TLS_CLIENT_CONNECTION_NSS (object);
+
+  if (nss->priv->server_identity)
+    g_object_unref (nss->priv->server_identity);
+  if (nss->priv->accepted_cas)
+    g_list_free_full (nss->priv->accepted_cas, (GDestroyNotify)g_byte_array_unref);
+
+  G_OBJECT_CLASS (g_tls_client_connection_nss_parent_class)->finalize (object);
+}
+
+static void
+g_tls_client_connection_nss_get_property (GObject    *object,
+                                         guint       prop_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+  GTlsClientConnectionNss *nss = G_TLS_CLIENT_CONNECTION_NSS (object);
+  GList *accepted_cas, *iter;
+
+  switch (prop_id)
+    {
+    case PROP_VALIDATION_FLAGS:
+      g_value_set_flags (value, nss->priv->validation_flags);
+      break;
+
+    case PROP_SERVER_IDENTITY:
+      g_value_set_object (value, nss->priv->server_identity);
+      break;
+
+    case PROP_USE_SSL3:
+      g_value_set_boolean (value, nss->priv->use_ssl3);
+      break;
+
+    case PROP_ACCEPTED_CAS:
+      accepted_cas = NULL;
+      for (iter = nss->priv->accepted_cas; iter; iter = iter->next)
+       accepted_cas = g_list_prepend (accepted_cas, g_byte_array_ref (iter->data));
+      g_value_set_pointer (value, accepted_cas);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_client_connection_nss_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+  GTlsClientConnectionNss *nss = G_TLS_CLIENT_CONNECTION_NSS (object);
+  GTlsConnectionNss *conn_nss = G_TLS_CONNECTION_NSS (object);
+  const char *identity_host;
+
+  switch (prop_id)
+    {
+    case PROP_VALIDATION_FLAGS:
+      nss->priv->validation_flags = g_value_get_flags (value);
+      break;
+
+    case PROP_SERVER_IDENTITY:
+      if (nss->priv->server_identity)
+       g_object_unref (nss->priv->server_identity);
+      nss->priv->server_identity = g_value_dup_object (value);
+
+      if (G_IS_NETWORK_ADDRESS (nss->priv->server_identity))
+       identity_host = g_network_address_get_hostname (G_NETWORK_ADDRESS (nss->priv->server_identity));
+      else if (G_IS_NETWORK_SERVICE (nss->priv->server_identity))
+       identity_host = g_network_service_get_domain (G_NETWORK_SERVICE (nss->priv->server_identity));
+      else
+       identity_host = NULL;
+
+      if (identity_host)
+       {
+         SSL_SetURL (conn_nss->prfd, identity_host);
+         SSL_SetSockPeerID (conn_nss->prfd, identity_host);
+       }
+      break;
+
+    case PROP_USE_SSL3:
+      nss->priv->use_ssl3 = g_value_get_boolean (value);
+      SSL_OptionSet (conn_nss->prfd, SSL_ENABLE_TLS, !nss->priv->use_ssl3);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_client_connection_nss_class_init (GTlsClientConnectionNssClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsClientConnectionNssPrivate));
+
+  gobject_class->get_property = g_tls_client_connection_nss_get_property;
+  gobject_class->set_property = g_tls_client_connection_nss_set_property;
+  gobject_class->finalize     = g_tls_client_connection_nss_finalize;
+
+  g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
+  g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
+  g_object_class_override_property (gobject_class, PROP_USE_SSL3, "use-ssl3");
+  g_object_class_override_property (gobject_class, PROP_ACCEPTED_CAS, "accepted-cas");
+}
+
+static void
+g_tls_client_connection_nss_client_connection_interface_init (GTlsClientConnectionInterface *iface)
+{
+}
+
+SECStatus
+g_tls_client_connection_nss_certificate_requested (void              *arg,
+                                                  PRFileDesc        *fd,
+                                                  CERTDistNames     *caNames,
+                                                  CERTCertificate  **pRetCert,
+                                                  SECKEYPrivateKey **pRetKey)
+{
+  GTlsClientConnectionNss *nss = arg;
+  GTlsConnectionBase *tls = arg;
+  GTlsCertificateNss *gcert;
+  GByteArray *name;
+  GList *cas = NULL;
+  int i;
+
+  tls->certificate_requested = TRUE;
+
+  for (i = 0; i < caNames->nnames; i++)
+    {
+      name = g_byte_array_new ();
+      g_byte_array_append (name, caNames->names[i].data, caNames->names[i].len);
+      cas = g_list_prepend (cas, name);
+    }
+  nss->priv->accepted_cas = cas;
+  g_object_notify (G_OBJECT (nss), "accepted-cas");
+
+  if (!tls->certificate)
+    return SECFailure;
+
+  gcert = G_TLS_CERTIFICATE_NSS (tls->certificate);
+  *pRetCert = CERT_DupCertificate (g_tls_certificate_nss_get_cert (gcert));
+  *pRetKey = SECKEY_CopyPrivateKey (g_tls_certificate_nss_get_key (gcert));
+  return SECSuccess;
+}
diff --git a/tls/nss/gtlsclientconnection-nss.h b/tls/nss/gtlsclientconnection-nss.h
new file mode 100644
index 0000000..0643b65
--- /dev/null
+++ b/tls/nss/gtlsclientconnection-nss.h
@@ -0,0 +1,47 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This program 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_CLIENT_CONNECTION_NSS_H__
+#define __G_TLS_CLIENT_CONNECTION_NSS_H__
+
+#include "gtlsconnection-nss.h"
+#include <ssl.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CLIENT_CONNECTION_NSS            (g_tls_client_connection_nss_get_type ())
+#define G_TLS_CLIENT_CONNECTION_NSS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), 
G_TYPE_TLS_CLIENT_CONNECTION_NSS, GTlsClientConnectionNss))
+#define G_TLS_CLIENT_CONNECTION_NSS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), 
G_TYPE_TLS_CLIENT_CONNECTION_NSS, GTlsClientConnectionNssClass))
+#define G_IS_TLS_CLIENT_CONNECTION_NSS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), 
G_TYPE_TLS_CLIENT_CONNECTION_NSS))
+#define G_IS_TLS_CLIENT_CONNECTION_NSS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), 
G_TYPE_TLS_CLIENT_CONNECTION_NSS))
+#define G_TLS_CLIENT_CONNECTION_NSS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), 
G_TYPE_TLS_CLIENT_CONNECTION_NSS, GTlsClientConnectionNssClass))
+
+typedef struct _GTlsClientConnectionNssPrivate GTlsClientConnectionNssPrivate;
+typedef struct _GTlsClientConnectionNssClass   GTlsClientConnectionNssClass;
+typedef struct _GTlsClientConnectionNss        GTlsClientConnectionNss;
+
+struct _GTlsClientConnectionNssClass
+{
+  GTlsConnectionNssClass parent_class;
+};
+
+struct _GTlsClientConnectionNss
+{
+  GTlsConnectionNss parent_instance;
+  GTlsClientConnectionNssPrivate *priv;
+};
+
+GType g_tls_client_connection_nss_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_TLS_CLIENT_CONNECTION_NSS_H___ */
diff --git a/tls/nss/gtlsconnection-nss.c b/tls/nss/gtlsconnection-nss.c
new file mode 100644
index 0000000..dc29c8b
--- /dev/null
+++ b/tls/nss/gtlsconnection-nss.c
@@ -0,0 +1,433 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 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
+ * License as published by the Free Software Foundation; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <glib.h>
+
+#include "gtlsconnection-nss.h"
+#include "gtlsbackend-nss.h"
+#include "gtlscertificate-nss.h"
+#include "gtlsclientconnection-nss.h"
+#include "gtlsdatabase-nss.h"
+#include "gtlsprfiledesc-nss.h"
+#include <glib/gi18n-lib.h>
+
+#include <secerr.h>
+#include <ssl.h>
+#include <sslerr.h>
+#include <nspr.h>
+
+static SECStatus g_tls_connection_nss_auth_certificate (void       *arg,
+                                                       PRFileDesc *fd, 
+                                                       PRBool      checkSig,
+                                                       PRBool      isServer);
+static SECStatus g_tls_connection_nss_bad_cert         (void       *arg,
+                                                       PRFileDesc *fd);
+static void      g_tls_connection_nss_handshaked       (PRFileDesc *fd,
+                                                       void       *arg);
+
+static void g_tls_connection_nss_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionNss, g_tls_connection_nss, G_TYPE_TLS_CONNECTION_BASE,
+                                 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                                        g_tls_connection_nss_initable_iface_init););
+
+struct _GTlsConnectionNssPrivate
+{
+  gboolean handshake_completed;
+};
+
+static void
+g_tls_connection_nss_init (GTlsConnectionNss *nss)
+{
+  PRFileDesc *prfd;
+
+  nss->priv = G_TYPE_INSTANCE_GET_PRIVATE (nss, G_TYPE_TLS_CONNECTION_NSS, GTlsConnectionNssPrivate);
+
+  prfd = g_tls_prfiledesc_new (nss);
+  nss->prfd = SSL_ImportFD (NULL, prfd);
+
+  SSL_OptionSet (nss->prfd, SSL_SECURITY, PR_TRUE);
+  SSL_OptionSet (nss->prfd, SSL_ENABLE_FDX, PR_TRUE);
+  SSL_OptionSet (nss->prfd, SSL_ENABLE_SSL2, PR_FALSE);
+  SSL_OptionSet (nss->prfd, SSL_V2_COMPATIBLE_HELLO, PR_FALSE);
+  SSL_OptionSet (nss->prfd, SSL_ENABLE_SSL3, PR_TRUE);
+  SSL_OptionSet (nss->prfd, SSL_ENABLE_TLS, PR_TRUE);
+}
+
+static gboolean
+g_tls_connection_nss_initable_init (GInitable     *initable,
+                                   GCancellable  *cancellable,
+                                   GError       **error)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (initable);
+  GTlsConnectionNss *nss = G_TLS_CONNECTION_NSS (initable);
+
+  g_return_val_if_fail (tls->base_istream != NULL &&
+                       tls->base_ostream != NULL, FALSE);
+
+  SSL_AuthCertificateHook (nss->prfd, g_tls_connection_nss_auth_certificate, nss);
+  SSL_BadCertHook (nss->prfd, g_tls_connection_nss_bad_cert, nss);
+  SSL_SetPKCS11PinArg (nss->prfd, nss);
+  SSL_HandshakeCallback (nss->prfd, g_tls_connection_nss_handshaked, nss);
+
+  return TRUE;
+}
+
+static void
+g_tls_connection_nss_finalize (GObject *object)
+{
+  GTlsConnectionNss *nss = G_TLS_CONNECTION_NSS (object);
+
+  if (nss->prfd)
+    PR_Close (nss->prfd);
+
+  G_OBJECT_CLASS (g_tls_connection_nss_parent_class)->finalize (object);
+}
+
+static SECStatus
+g_tls_connection_nss_auth_certificate (void       *arg,
+                                      PRFileDesc *fd, 
+                                      PRBool      checkSig,
+                                      PRBool      isServer)
+{
+  GTlsConnectionBase *tls = arg;
+  CERTCertificate *cert;
+  GTlsCertificateNss *gcert;
+  GSocketConnectable *identity;
+  gboolean accepted;
+
+  if (isServer)
+    identity = NULL;
+  else
+    identity = g_tls_client_connection_get_server_identity (G_TLS_CLIENT_CONNECTION (tls));
+
+  cert = SSL_RevealCert (fd);
+  gcert = g_tls_database_nss_get_gcert (g_tls_backend_nss_default_database,
+                                       cert, TRUE);
+  CERT_DestroyCertificate (cert);
+
+  tls->peer_certificate = G_TLS_CERTIFICATE (gcert);
+  tls->peer_certificate_errors =
+    g_tls_database_verify_chain (tls->database ? tls->database :
+                                G_TLS_DATABASE (g_tls_backend_nss_default_database),
+                                tls->peer_certificate,
+                                isServer ? G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT : 
G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+                                identity,
+                                tls->interaction,
+                                G_TLS_DATABASE_VERIFY_NONE,
+                                tls->read_cancellable,
+                                &tls->read_error);
+
+  if (tls->read_error)
+    {
+      PR_SetError (SSL_ERROR_BAD_CERTIFICATE, 0);
+      return SECFailure;
+    }
+  
+  if (isServer)
+    {
+      accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (tls),
+                                                          tls->peer_certificate,
+                                                          tls->peer_certificate_errors);
+    }
+  else
+    {
+      GTlsCertificateFlags validation_flags = g_tls_client_connection_get_validation_flags 
(G_TLS_CLIENT_CONNECTION (tls));
+
+      if (tls->peer_certificate_errors & validation_flags)
+       {
+         accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (tls),
+                                                              tls->peer_certificate,
+                                                              tls->peer_certificate_errors);
+       }
+      else
+       accepted = TRUE;
+    }
+
+  if (accepted)
+    return SECSuccess;
+  else
+    {
+      PR_SetError (SSL_ERROR_BAD_CERTIFICATE, 0);
+      return SECFailure;
+    }
+}
+
+static SECStatus
+g_tls_connection_nss_bad_cert (void       *arg,
+                              PRFileDesc *fd)
+{
+  g_print ("BAD CERT\n");
+
+  /* FIXME */
+
+  return SECFailure;
+}
+
+static GTlsConnectionBaseStatus
+end_nss_io (GTlsConnectionNss  *nss,
+           GIOCondition        direction,
+           gboolean            success,
+           GError            **error)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (nss);
+  GError *my_error = NULL;
+  GTlsConnectionBaseStatus status;
+
+  status = g_tls_connection_base_pop_io (tls, direction, success, &my_error);
+  if (status == G_TLS_CONNECTION_BASE_OK ||
+      status == G_TLS_CONNECTION_BASE_WOULD_BLOCK)
+    {
+      if (my_error)
+       g_propagate_error (error, my_error);
+      return status;
+    }
+
+  if (my_error)
+    g_propagate_error (error, my_error);
+  if (error && !*error)
+    {
+      int errnum = PR_GetError ();
+
+      /* FIXME: need real error descriptions */
+
+      if (errnum == SSL_ERROR_BAD_CERTIFICATE)
+       {
+         g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                              _("Unacceptable TLS certificate"));
+
+       }
+      else if (errnum == SSL_ERROR_BAD_CERT_ALERT)
+       {
+         if (tls->certificate_requested)
+           {
+             g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+                                  _("TLS connection peer did not send a certificate"));
+           }
+         else
+           {
+             g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_HANDSHAKE,
+                                  _("Peer rejected the provided TLS certificate"));
+           }
+       }
+      else if (errnum == SSL_ERROR_NO_CERTIFICATE)
+       {
+         g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+                              _("TLS connection peer did not send a certificate"));
+       }
+      else if (IS_SSL_ERROR (errnum))
+       {
+         g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                      "SSL error %d", errnum);
+       }
+      else if (IS_SEC_ERROR (errnum))
+       {
+         g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                      "SEC error %d", errnum);
+       }
+      else
+       {
+         g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                              PR_ErrorToString (PR_GetError (), 1));
+       }
+    }
+
+  return G_TLS_CONNECTION_BASE_ERROR;
+}
+
+#define BEGIN_NSS_IO(nss, direction, blocking, cancellable)            \
+  g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (nss),          \
+                                direction, blocking, cancellable);
+
+#define END_NSS_IO(nss, direction, status, success, error)             \
+  status = end_nss_io (nss, direction, success, error);
+
+static GTlsConnectionBaseStatus
+g_tls_connection_nss_read (GTlsConnectionBase    *tls,
+                          void                  *buffer,
+                          gsize                  count,
+                          gboolean               blocking,
+                          gssize                *nread,
+                          GCancellable          *cancellable,
+                          GError               **error)
+{
+  GTlsConnectionNss *nss = G_TLS_CONNECTION_NSS (tls);
+  GTlsConnectionBaseStatus status;
+  PRInt32 ret;
+
+  BEGIN_NSS_IO (nss, G_IO_IN, blocking, cancellable);
+  ret = PR_Recv (nss->prfd, buffer, count, 0, PR_INTERVAL_NO_TIMEOUT);
+  END_NSS_IO (nss, G_IO_IN, status, ret >= 0, error);
+
+  if (ret >= 0)
+    *nread = ret;
+  else
+    g_prefix_error (error, _("Error reading data from TLS socket: "));
+
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_nss_write (GTlsConnectionBase    *tls,
+                           const void            *buffer,
+                           gsize                  count,
+                           gboolean               blocking,
+                           gssize                *nwrote,
+                           GCancellable          *cancellable,
+                           GError               **error)
+{
+  GTlsConnectionNss *nss = G_TLS_CONNECTION_NSS (tls);
+  GTlsConnectionBaseStatus status;
+  PRInt32 ret;
+
+  BEGIN_NSS_IO (nss, G_IO_OUT, blocking, cancellable);
+  ret = PR_Send (nss->prfd, buffer, count, 0, PR_INTERVAL_NO_TIMEOUT);
+  END_NSS_IO (nss, G_IO_OUT, status, ret >= 0, error);
+
+  if (ret >= 0)
+    *nwrote = ret;
+  else
+    g_prefix_error (error, _("Error writing data to TLS socket: "));
+
+  return status;
+}
+
+static void
+g_tls_connection_nss_handshaked (PRFileDesc *fd,
+                                void       *arg)
+{
+  GTlsConnectionNss *nss = arg;
+
+  nss->priv->handshake_completed = TRUE;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_nss_request_rehandshake (GTlsConnectionBase  *tls,
+                                         GCancellable        *cancellable,
+                                         GError             **error)
+{
+  GTlsConnectionNss *nss = G_TLS_CONNECTION_NSS (tls);
+  GTlsConnectionBaseStatus status;
+  SECStatus sec;
+
+  BEGIN_NSS_IO (nss, G_IO_IN | G_IO_OUT, TRUE, cancellable);
+  sec = SSL_ReHandshake (nss->prfd, G_IS_TLS_CLIENT_CONNECTION (tls));
+  END_NSS_IO (nss, G_IO_IN | G_IO_OUT, status, sec == SECSuccess, error);
+
+  if (error && *error)
+    g_prefix_error (error, _("Error performing TLS handshake: "));
+
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_nss_handshake (GTlsConnectionBase  *tls,
+                               GCancellable        *cancellable,
+                               GError             **error)
+{
+  GTlsConnectionNss *nss = G_TLS_CONNECTION_NSS (tls);
+  GTlsConnectionBaseStatus status;
+  GError *my_error = NULL;
+  SECStatus sec;
+
+  nss->priv->handshake_completed = FALSE;
+
+  while (!nss->priv->handshake_completed && !my_error)
+    {
+      BEGIN_NSS_IO (nss, G_IO_IN | G_IO_OUT, TRUE, cancellable);
+      sec = SSL_ForceHandshake (nss->prfd);
+      END_NSS_IO (nss, G_IO_IN | G_IO_OUT, status, sec == SECSuccess, &my_error);
+
+      if (!nss->priv->handshake_completed && !my_error)
+       {
+         guint8 buf[1024];
+         gssize nread;
+
+         /* Got app data instead of rehandshake; buffer it and try again */
+         status = g_tls_connection_nss_read (tls, buf, sizeof (buf), TRUE,
+                                             &nread, cancellable, &my_error);
+         if (status != G_TLS_CONNECTION_BASE_OK)
+           break;
+         if (!tls->app_data_buf)
+           tls->app_data_buf = g_byte_array_new ();
+         g_byte_array_append (tls->app_data_buf, buf, nread);
+       }
+    }
+
+  if (my_error)
+    g_propagate_error (error, my_error);
+  if (error && *error)
+    g_prefix_error (error, _("Error performing TLS handshake: "));
+
+  return status;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_nss_complete_handshake (GTlsConnectionBase  *tls,
+                                        GError             **error)
+{
+  /* FIXME */
+  return G_TLS_CONNECTION_BASE_OK;
+}
+
+static GTlsConnectionBaseStatus
+g_tls_connection_nss_close (GTlsConnectionBase  *tls,
+                           GCancellable        *cancellable,
+                           GError             **error)
+{
+  GTlsConnectionNss *nss = G_TLS_CONNECTION_NSS (tls);
+  GTlsConnectionBaseStatus status;
+  PRInt32 ret;
+
+  BEGIN_NSS_IO (nss, G_IO_IN, TRUE, cancellable);
+  ret = PR_Close (nss->prfd);
+  END_NSS_IO (nss, G_IO_IN, status, ret == 0, error);
+
+  nss->prfd = NULL;
+  if (error && *error)
+    g_prefix_error (error, _("Error performing TLS close: "));
+
+  return status;
+}
+
+static void
+g_tls_connection_nss_class_init (GTlsConnectionNssClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsConnectionNssPrivate));
+
+  gobject_class->finalize     = g_tls_connection_nss_finalize;
+
+  base_class->request_rehandshake = g_tls_connection_nss_request_rehandshake;
+  base_class->handshake           = g_tls_connection_nss_handshake;
+  base_class->complete_handshake  = g_tls_connection_nss_complete_handshake;
+  base_class->read_fn             = g_tls_connection_nss_read;
+  base_class->write_fn            = g_tls_connection_nss_write;
+  base_class->close_fn            = g_tls_connection_nss_close;
+}
+
+static void
+g_tls_connection_nss_initable_iface_init (GInitableIface *iface)
+{
+  iface->init = g_tls_connection_nss_initable_init;
+}
diff --git a/tls/nss/gtlsconnection-nss.h b/tls/nss/gtlsconnection-nss.h
new file mode 100644
index 0000000..43a7263
--- /dev/null
+++ b/tls/nss/gtlsconnection-nss.h
@@ -0,0 +1,53 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This program 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_CONNECTION_NSS_H__
+#define __G_TLS_CONNECTION_NSS_H__
+
+#include <gio/gio.h>
+#include <nspr.h>
+
+#include "gtlsconnection-base.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CONNECTION_NSS            (g_tls_connection_nss_get_type ())
+#define G_TLS_CONNECTION_NSS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CONNECTION_NSS, 
GTlsConnectionNss))
+#define G_TLS_CONNECTION_NSS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CONNECTION_NSS, 
GTlsConnectionNssClass))
+#define G_IS_TLS_CONNECTION_NSS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CONNECTION_NSS))
+#define G_IS_TLS_CONNECTION_NSS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CONNECTION_NSS))
+#define G_TLS_CONNECTION_NSS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CONNECTION_NSS, 
GTlsConnectionNssClass))
+
+typedef struct _GTlsConnectionNssPrivate                   GTlsConnectionNssPrivate;
+typedef struct _GTlsConnectionNssClass                     GTlsConnectionNssClass;
+typedef struct _GTlsConnectionNss                          GTlsConnectionNss;
+
+struct _GTlsConnectionNssClass
+{
+  GTlsConnectionBaseClass parent_class;
+
+};
+
+struct _GTlsConnectionNss
+{
+  GTlsConnectionBase parent_instance;
+
+  PRFileDesc *prfd;
+
+  GTlsConnectionNssPrivate *priv;
+};
+
+GType g_tls_connection_nss_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_TLS_CONNECTION_NSS_H___ */
diff --git a/tls/nss/gtlsdatabase-nss.c b/tls/nss/gtlsdatabase-nss.c
new file mode 100644
index 0000000..82ad232
--- /dev/null
+++ b/tls/nss/gtlsdatabase-nss.c
@@ -0,0 +1,282 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 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
+ * License as published by the Free Software Foundation; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <pk11pub.h>
+#include <secerr.h>
+
+#include "gtlsdatabase-nss.h"
+#include "gtlsbackend-nss.h"
+#include "gtlscertificate-nss.h"
+
+#include <glib/gi18n-lib.h>
+
+G_DEFINE_TYPE (GTlsDatabaseNss, g_tls_database_nss, G_TYPE_TLS_DATABASE);
+
+struct _GTlsDatabaseNssPrivate
+{
+  GMutex mutex;
+  GHashTable *gcerts;
+};
+
+static void
+g_tls_database_nss_init (GTlsDatabaseNss *nss)
+{
+  nss->priv = G_TYPE_INSTANCE_GET_PRIVATE (nss,
+                                          G_TYPE_TLS_DATABASE_NSS,
+                                          GTlsDatabaseNssPrivate);
+
+  g_mutex_init (&nss->priv->mutex);
+
+  /* gcerts is a cache of CERTCertificate to GTlsCertificateNss
+   * mappings, including every live GTlsCertificateNss. Note that both
+   * types enforce uniqueness, so there should be a one-to-one
+   * mapping.
+   */
+  nss->priv->gcerts = g_hash_table_new (NULL, NULL);
+}
+
+static void
+g_tls_database_nss_finalize (GObject *object)
+{
+  GTlsDatabaseNss *nss = G_TLS_DATABASE_NSS (object);
+  GHashTableIter iter;
+  gpointer cert, gcert;
+
+  g_mutex_clear (&nss->priv->mutex);
+
+  g_hash_table_iter_init (&iter, nss->priv->gcerts);
+  while (g_hash_table_iter_next (&iter, &cert, &gcert))
+    CERT_DestroyCertificate (cert);
+  g_hash_table_destroy (nss->priv->gcerts);
+
+  G_OBJECT_CLASS (g_tls_database_nss_parent_class)->finalize (object);
+}
+
+GTlsCertificateNss *
+g_tls_database_nss_get_gcert (GTlsDatabaseNss *nss,
+                             CERTCertificate *cert,
+                             gboolean         create)
+{
+  GTlsCertificateNss *gcert;
+
+  g_mutex_lock (&nss->priv->mutex);
+
+  gcert = g_hash_table_lookup (nss->priv->gcerts, cert);
+  if (gcert)
+    g_object_ref (gcert);
+  else if (create)
+    {
+      gcert = g_tls_certificate_nss_new_for_cert (cert);
+      /* The GTlsCertificate constructor will call
+       * g_tls_database_nss_gcert_created() to add it to the hash.
+       */
+    }
+
+  g_mutex_unlock (&nss->priv->mutex);
+  return gcert;
+}
+
+void
+g_tls_database_nss_gcert_created (GTlsDatabaseNss    *nss,
+                                 CERTCertificate    *cert,
+                                 GTlsCertificateNss *gcert)
+{
+  g_mutex_lock (&nss->priv->mutex);
+  /* We keep a ref on the CERTCertificate, but not the GTlsCertificate */
+  g_hash_table_insert (nss->priv->gcerts, CERT_DupCertificate (cert), gcert);
+  g_mutex_unlock (&nss->priv->mutex);
+}
+
+void
+g_tls_database_nss_gcert_destroyed (GTlsDatabaseNss *nss,
+                                   CERTCertificate *cert)
+{
+  g_mutex_lock (&nss->priv->mutex);
+  g_hash_table_remove (nss->priv->gcerts, cert);
+  CERT_DestroyCertificate (cert);
+  g_mutex_unlock (&nss->priv->mutex);
+}
+
+static GTlsCertificateFlags
+g_tls_database_nss_verify_chain (GTlsDatabase             *database,
+                                GTlsCertificate          *chain,
+                                const gchar              *purpose,
+                                GSocketConnectable       *identity,
+                                GTlsInteraction          *interaction,
+                                GTlsDatabaseVerifyFlags   flags,
+                                GCancellable             *cancellable,
+                                GError                  **error)
+{
+  return g_tls_certificate_nss_verify_full (chain, database, NULL,
+                                           purpose, identity, interaction,
+                                           flags, cancellable, error);
+}
+
+static gchar *
+g_tls_database_nss_create_certificate_handle (GTlsDatabase    *database,
+                                             GTlsCertificate *certificate)
+{
+
+  CERTCertificate *cert = g_tls_certificate_nss_get_cert (G_TLS_CERTIFICATE_NSS (certificate));
+  gchar *issuer, *serial, *handle;
+
+  issuer = g_base64_encode ((guchar *)cert->derIssuer.data,
+                           cert->derIssuer.len);
+  serial = g_base64_encode ((guchar *)cert->serialNumber.data,
+                           cert->serialNumber.len);
+
+  handle = g_strdup_printf ("nss:%s#%s", issuer, serial);
+  g_free (issuer);
+  g_free (serial);
+
+  return handle;
+}
+
+static GTlsCertificate *
+g_tls_database_nss_lookup_certificate_for_handle (GTlsDatabase             *database,
+                                                 const gchar              *handle,
+                                                 GTlsInteraction          *interaction,
+                                                 GTlsDatabaseLookupFlags   flags,
+                                                 GCancellable             *cancellable,
+                                                 GError                  **error)
+{
+  GTlsDatabaseNss *nss = G_TLS_DATABASE_NSS (database);
+  const gchar *split, *issuer, *serial;
+  CERTIssuerAndSN issuerAndSN;
+  CERTCertificate *cert;
+  GTlsCertificateNss *ret;
+  gsize length;
+
+  if (!g_str_has_prefix (handle, "nss:"))
+    return NULL;
+
+  issuer = handle + 4;
+  split = strchr (issuer, '#');
+  if (!split)
+    return NULL;
+  serial = split + 1;
+
+  issuerAndSN.derIssuer.data = g_base64_decode (issuer, &length);
+  issuerAndSN.derIssuer.len = length;
+
+  issuerAndSN.serialNumber.data = g_base64_decode (serial, &length);
+  issuerAndSN.serialNumber.len = length;
+
+  cert = CERT_FindCertByIssuerAndSN (g_tls_backend_nss_certdbhandle, &issuerAndSN);
+  g_free (issuerAndSN.derIssuer.data);
+  g_free (issuerAndSN.serialNumber.data);
+  if (!cert)
+    return NULL;
+
+  ret = g_tls_database_nss_get_gcert (nss, cert, TRUE);
+  CERT_DestroyCertificate (cert);
+  return G_TLS_CERTIFICATE (ret);
+}
+
+static GTlsCertificate *
+g_tls_database_nss_lookup_certificate_issuer (GTlsDatabase             *database,
+                                             GTlsCertificate          *certificate,
+                                             GTlsInteraction          *interaction,
+                                             GTlsDatabaseLookupFlags   flags,
+                                             GCancellable             *cancellable,
+                                             GError                  **error)
+{
+  GTlsDatabaseNss *nss = G_TLS_DATABASE_NSS (database);
+  GTlsCertificateNss *cert_nss = G_TLS_CERTIFICATE_NSS (certificate);
+  CERTCertificate *cert, *issuer_cert;
+  GTlsCertificateNss *issuer;
+
+  cert = g_tls_certificate_nss_get_cert (cert_nss);
+  issuer_cert = CERT_FindCertIssuer(cert, PR_Now (),
+                                   /* FIXME? Though it seems to not actually
+                                    * matter if this is wrong.
+                                    */
+                                   certUsageSSLServer);
+  if (issuer_cert)
+    {
+      issuer = g_tls_database_nss_get_gcert (nss, issuer_cert, TRUE);
+      CERT_DestroyCertificate (issuer_cert);
+      return G_TLS_CERTIFICATE (issuer);
+    }
+  else
+    return NULL;
+}
+
+static GList *
+g_tls_database_nss_lookup_certificates_issued_by (GTlsDatabase             *database,
+                                                 GByteArray               *issuer_raw_dn,
+                                                 GTlsInteraction          *interaction,
+                                                 GTlsDatabaseLookupFlags   flags,
+                                                 GCancellable             *cancellable,
+                                                 GError                  **error)
+{
+  GTlsDatabaseNss *nss = G_TLS_DATABASE_NSS (database);
+  GList *certs;
+  CERTCertNicknames *nicknames;
+  CERTCertificate *cert;
+  SECItem issuerName;
+  int i;
+
+  nicknames = CERT_GetCertNicknames (g_tls_backend_nss_certdbhandle,
+                                    SEC_CERT_NICKNAMES_ALL, interaction);
+  if (!nicknames)
+    return NULL;
+
+  certs = NULL;
+  for (i = 0; i < nicknames->numnicknames; i++)
+    {
+      cert = PK11_FindCertFromNickname (nicknames->nicknames[i], interaction);
+      if (!cert)
+       continue;
+
+      if (CERT_IssuerNameFromDERCert (&cert->derCert, &issuerName) == SECSuccess)
+       {
+         if (issuer_raw_dn->len == issuerName.len &&
+             memcmp (issuer_raw_dn->data, issuerName.data, issuerName.len) == 0)
+           certs = g_list_prepend (certs, g_tls_database_nss_get_gcert (nss, cert, TRUE));
+
+         SECITEM_FreeItem (&issuerName, PR_FALSE);
+       }
+
+      CERT_DestroyCertificate (cert);
+    }
+
+  CERT_FreeNicknames (nicknames);
+  return certs;
+}
+
+static void
+g_tls_database_nss_class_init (GTlsDatabaseNssClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsDatabaseNssPrivate));
+
+  object_class->finalize = g_tls_database_nss_finalize;
+
+  database_class->verify_chain = g_tls_database_nss_verify_chain;
+  database_class->create_certificate_handle = g_tls_database_nss_create_certificate_handle;            
+  database_class->lookup_certificate_for_handle = g_tls_database_nss_lookup_certificate_for_handle;        
+  database_class->lookup_certificate_issuer = g_tls_database_nss_lookup_certificate_issuer;            
+  database_class->lookup_certificates_issued_by = g_tls_database_nss_lookup_certificates_issued_by;        
+}
diff --git a/tls/nss/gtlsdatabase-nss.h b/tls/nss/gtlsdatabase-nss.h
new file mode 100644
index 0000000..ac85e10
--- /dev/null
+++ b/tls/nss/gtlsdatabase-nss.h
@@ -0,0 +1,60 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc..
+ *
+ * This program 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_DATABASE_NSS_H__
+#define __G_TLS_DATABASE_NSS_H__
+
+#include <gio/gio.h>
+#include <cert.h>
+
+#include "gtlscertificate-nss.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_DATABASE_NSS            (g_tls_database_nss_get_type ())
+#define G_TLS_DATABASE_NSS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_DATABASE_NSS, 
GTlsDatabaseNss))
+#define G_TLS_DATABASE_NSS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_DATABASE_NSS, 
GTlsDatabaseNssClass))
+#define G_IS_TLS_DATABASE_NSS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_DATABASE_NSS))
+#define G_IS_TLS_DATABASE_NSS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_DATABASE_NSS))
+#define G_TLS_DATABASE_NSS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_DATABASE_NSS, 
GTlsDatabaseNssClass))
+
+typedef struct _GTlsDatabaseNssPrivate                   GTlsDatabaseNssPrivate;
+typedef struct _GTlsDatabaseNssClass                     GTlsDatabaseNssClass;
+typedef struct _GTlsDatabaseNss                          GTlsDatabaseNss;
+
+struct _GTlsDatabaseNssClass
+{
+  GTlsDatabaseClass parent_class;
+
+};
+
+struct _GTlsDatabaseNss
+{
+  GTlsDatabase parent_instance;
+  GTlsDatabaseNssPrivate *priv;
+};
+
+GType g_tls_database_nss_get_type (void) G_GNUC_CONST;
+
+GTlsCertificateNss *g_tls_database_nss_get_gcert       (GTlsDatabaseNss    *nss,
+                                                       CERTCertificate    *cert,
+                                                       gboolean            create);
+void                g_tls_database_nss_gcert_created   (GTlsDatabaseNss    *nss,
+                                                       CERTCertificate    *cert,
+                                                       GTlsCertificateNss *gcert);
+
+void                g_tls_database_nss_gcert_destroyed (GTlsDatabaseNss    *nss,
+                                                       CERTCertificate    *cert);
+
+G_END_DECLS
+
+#endif /* __G_TLS_DATABASE_NSS_H___ */
diff --git a/tls/nss/gtlsfiledatabase-nss.c b/tls/nss/gtlsfiledatabase-nss.c
new file mode 100644
index 0000000..4501167
--- /dev/null
+++ b/tls/nss/gtlsfiledatabase-nss.c
@@ -0,0 +1,317 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 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
+ * License as published by the Free Software Foundation; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gtlsfiledatabase-nss.h"
+#include "gtlsbackend-nss.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+
+/* NSS only has a single global database. The strategy here then is to
+ * remember which certificates we read out of this file, and then when
+ * asked to do some operation, we have the default database do it, and
+ * then filter the results to only the certs in this database.
+ */
+
+/* The handle format is the same as the GNUTLS backend, for no real
+ * reason other than "that's what the regression tests test for". We
+ * could just as easily chain up.
+ */
+
+static void g_tls_file_database_nss_file_database_interface_init (GTlsFileDatabaseInterface *iface);
+
+static void g_tls_file_database_nss_initable_interface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsFileDatabaseNss, g_tls_file_database_nss, G_TYPE_TLS_DATABASE_NSS,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_TLS_FILE_DATABASE,
+                                                g_tls_file_database_nss_file_database_interface_init);
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                                g_tls_file_database_nss_initable_interface_init);
+);
+
+enum
+{
+  PROP_0,
+  PROP_ANCHORS,
+};
+
+struct _GTlsFileDatabaseNssPrivate
+{
+  /* read-only after construct */
+  gchar *anchor_filename;
+  GHashTable *certs;
+  GTlsDatabase *default_db;
+
+  GHashTable *hashes, *certs_by_hash;
+};
+
+static void
+g_tls_file_database_nss_init (GTlsFileDatabaseNss *nss)
+{
+  nss->priv = G_TYPE_INSTANCE_GET_PRIVATE (nss,
+                                          G_TYPE_TLS_FILE_DATABASE_NSS,
+                                          GTlsFileDatabaseNssPrivate);
+  nss->priv->certs = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
+  nss->priv->default_db = G_TLS_DATABASE (g_tls_backend_nss_default_database);
+
+  nss->priv->hashes = g_hash_table_new (NULL, NULL);
+  nss->priv->certs_by_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                   g_free, NULL);
+}
+
+static void
+g_tls_file_database_nss_finalize (GObject *object)
+{
+  GTlsFileDatabaseNss *nss = G_TLS_FILE_DATABASE_NSS (object);
+
+  if (nss->priv->certs)
+    g_hash_table_destroy (nss->priv->certs);
+  if (nss->priv->hashes)
+    g_hash_table_destroy (nss->priv->hashes);
+  if (nss->priv->certs_by_hash)
+    g_hash_table_destroy (nss->priv->certs_by_hash);
+  g_free (nss->priv->anchor_filename);
+
+  G_OBJECT_CLASS (g_tls_file_database_nss_parent_class)->finalize (object);
+}
+
+static void
+g_tls_file_database_nss_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  GTlsFileDatabaseNss *nss = G_TLS_FILE_DATABASE_NSS (object);
+
+  switch (prop_id)
+    {
+    case PROP_ANCHORS:
+      g_value_set_string (value, nss->priv->anchor_filename);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_file_database_nss_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  GTlsFileDatabaseNss *nss = G_TLS_FILE_DATABASE_NSS (object);
+  const gchar *anchor_path;
+
+  switch (prop_id)
+    {
+    case PROP_ANCHORS:
+      anchor_path = g_value_get_string (value);
+      if (anchor_path && !g_path_is_absolute (anchor_path))
+        {
+          g_warning ("The anchor file name for used with a GTlsFileDatabase "
+                     "must be an absolute path, and not relative: %s", anchor_path);
+        }
+      else
+        {
+          nss->priv->anchor_filename = g_strdup (anchor_path);
+        }
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static gchar *
+g_tls_file_database_nss_create_certificate_handle (GTlsDatabase    *database,
+                                                  GTlsCertificate *certificate)
+{
+  GTlsFileDatabaseNss *nss = G_TLS_FILE_DATABASE_NSS (database);
+  const gchar *hash;
+
+  hash = g_strdup (g_hash_table_lookup (nss->priv->hashes, certificate));
+  if (!hash)
+    return NULL;
+
+  return g_strdup_printf ("file://%s#%s", nss->priv->anchor_filename, hash);
+}
+
+static GTlsCertificate *
+g_tls_file_database_nss_lookup_certificate_for_handle (GTlsDatabase            *database,
+                                                      const gchar             *handle,
+                                                      GTlsInteraction         *interaction,
+                                                      GTlsDatabaseLookupFlags  flags,
+                                                      GCancellable            *cancellable,
+                                                      GError                 **error)
+{
+  GTlsFileDatabaseNss *nss = G_TLS_FILE_DATABASE_NSS (database);
+  GTlsCertificate *cert;
+
+  if (!g_str_has_prefix (handle, "file://"))
+    return NULL;
+  handle += 7;
+  if (!g_str_has_prefix (handle, nss->priv->anchor_filename))
+    return NULL;
+  handle += strlen (nss->priv->anchor_filename);
+  if (*handle != '#')
+    return NULL;
+  handle++;
+
+  cert = g_hash_table_lookup (nss->priv->certs_by_hash, handle);
+  if (cert)
+    g_object_ref (cert);
+  return cert;
+}
+
+static GTlsCertificate *
+g_tls_file_database_nss_lookup_certificate_issuer (GTlsDatabase             *database,
+                                                  GTlsCertificate          *certificate,
+                                                  GTlsInteraction          *interaction,
+                                                  GTlsDatabaseLookupFlags   flags,
+                                                  GCancellable             *cancellable,
+                                                  GError                  **error)
+{
+  GTlsFileDatabaseNss *nss = G_TLS_FILE_DATABASE_NSS (database);
+  GTlsCertificate *issuer;
+
+  issuer = g_tls_database_lookup_certificate_issuer (nss->priv->default_db,
+                                                    certificate, interaction,
+                                                    flags, cancellable,
+                                                    error);
+  if (issuer && g_hash_table_lookup (nss->priv->certs, issuer))
+    return issuer;
+  else if (issuer)
+    g_object_unref (issuer);
+  return NULL;
+}
+
+static GList*
+g_tls_file_database_nss_lookup_certificates_issued_by (GTlsDatabase           *database,
+                                                      GByteArray             *issuer_raw_dn,
+                                                      GTlsInteraction        *interaction,
+                                                      GTlsDatabaseLookupFlags flags,
+                                                      GCancellable           *cancellable,
+                                                      GError                **error)
+{
+  GTlsFileDatabaseNss *nss = G_TLS_FILE_DATABASE_NSS (database);
+  GList *certs, *l, *next;
+  GTlsCertificate *cert;
+
+  certs = g_tls_database_lookup_certificates_issued_by (nss->priv->default_db,
+                                                       issuer_raw_dn,
+                                                       interaction, flags,
+                                                       cancellable, error);
+  if (!certs)
+    return NULL;
+
+  for (l = certs; l; l = next)
+    {
+      cert = l->data;
+      next = l->next;
+      if (!g_hash_table_lookup (nss->priv->certs, cert))
+       {
+         g_object_unref (cert);
+         certs = g_list_delete_link (certs, l);
+       }
+    }
+
+  return certs;
+}
+
+static void
+g_tls_file_database_nss_class_init (GTlsFileDatabaseNssClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsFileDatabaseNssPrivate));
+
+  gobject_class->get_property = g_tls_file_database_nss_get_property;
+  gobject_class->set_property = g_tls_file_database_nss_set_property;
+  gobject_class->finalize     = g_tls_file_database_nss_finalize;
+
+  database_class->create_certificate_handle = g_tls_file_database_nss_create_certificate_handle;
+  database_class->lookup_certificate_for_handle = g_tls_file_database_nss_lookup_certificate_for_handle;
+  database_class->lookup_certificate_issuer = g_tls_file_database_nss_lookup_certificate_issuer;
+  database_class->lookup_certificates_issued_by = g_tls_file_database_nss_lookup_certificates_issued_by;
+
+  g_object_class_override_property (gobject_class, PROP_ANCHORS, "anchors");
+}
+
+static void
+g_tls_file_database_nss_file_database_interface_init (GTlsFileDatabaseInterface *iface)
+{
+
+}
+
+static gboolean
+g_tls_file_database_nss_initable_init (GInitable     *initable,
+                                      GCancellable  *cancellable,
+                                      GError       **error)
+{
+  GTlsFileDatabaseNss *nss = G_TLS_FILE_DATABASE_NSS (initable);
+  GError *my_error = NULL;
+  GList *certs, *c;
+
+  if (!nss->priv->anchor_filename)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                  _("No certificate database filename specified"));
+      return FALSE;
+    }
+
+  certs = g_tls_certificate_list_new_from_file (nss->priv->anchor_filename,
+                                               &my_error);
+  if (my_error)
+    {
+      g_propagate_error (error, my_error);
+      return FALSE;
+    }
+
+  for (c = certs; c; c = c->next)
+    {
+      GTlsCertificateNss *nss_cert = c->data;
+      CERTCertificate *cert = g_tls_certificate_nss_get_cert (nss_cert);
+      gchar *hash = g_compute_checksum_for_data (G_CHECKSUM_SHA256,
+                                                cert->derCert.data,
+                                                cert->derCert.len);
+
+      g_hash_table_insert (nss->priv->certs, nss_cert, nss_cert);
+      g_hash_table_insert (nss->priv->certs_by_hash, hash, nss_cert);
+      g_hash_table_insert (nss->priv->hashes, nss_cert, hash);
+    }
+  g_list_free (certs);
+
+  return TRUE;
+}
+
+static void
+g_tls_file_database_nss_initable_interface_init (GInitableIface *iface)
+{
+  iface->init = g_tls_file_database_nss_initable_init;
+}
+
+gboolean
+g_tls_file_database_nss_contains (GTlsFileDatabaseNss *nss,
+                                 GTlsCertificateNss  *nss_cert)
+{
+  return g_hash_table_lookup (nss->priv->certs, nss_cert) == nss_cert;
+}
diff --git a/tls/nss/gtlsfiledatabase-nss.h b/tls/nss/gtlsfiledatabase-nss.h
new file mode 100644
index 0000000..8139864
--- /dev/null
+++ b/tls/nss/gtlsfiledatabase-nss.h
@@ -0,0 +1,51 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This program 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_FILE_DATABASE_NSS_H__
+#define __G_TLS_FILE_DATABASE_NSS_H__
+
+#include <gio/gio.h>
+
+#include "gtlsdatabase-nss.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_FILE_DATABASE_NSS            (g_tls_file_database_nss_get_type ())
+#define G_TLS_FILE_DATABASE_NSS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), 
G_TYPE_TLS_FILE_DATABASE_NSS, GTlsFileDatabaseNss))
+#define G_TLS_FILE_DATABASE_NSS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), 
G_TYPE_TLS_FILE_DATABASE_NSS, GTlsFileDatabaseNssClass))
+#define G_IS_TLS_FILE_DATABASE_NSS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), 
G_TYPE_TLS_FILE_DATABASE_NSS))
+#define G_IS_TLS_FILE_DATABASE_NSS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), 
G_TYPE_TLS_FILE_DATABASE_NSS))
+#define G_TLS_FILE_DATABASE_NSS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), 
G_TYPE_TLS_FILE_DATABASE_NSS, GTlsFileDatabaseNssClass))
+
+typedef struct _GTlsFileDatabaseNssPrivate                   GTlsFileDatabaseNssPrivate;
+typedef struct _GTlsFileDatabaseNssClass                     GTlsFileDatabaseNssClass;
+typedef struct _GTlsFileDatabaseNss                          GTlsFileDatabaseNss;
+
+struct _GTlsFileDatabaseNssClass
+{
+  GTlsDatabaseNssClass parent_class;
+};
+
+struct _GTlsFileDatabaseNss
+{
+  GTlsDatabaseNss parent_instance;
+  GTlsFileDatabaseNssPrivate *priv;
+};
+
+GType g_tls_file_database_nss_get_type (void) G_GNUC_CONST;
+
+gboolean g_tls_file_database_nss_contains (GTlsFileDatabaseNss *nss,
+                                          GTlsCertificateNss  *nss_cert);
+
+G_END_DECLS
+
+#endif /* __G_TLS_FILE_DATABASE_NSS_H___ */
diff --git a/tls/nss/gtlsprfiledesc-nss.c b/tls/nss/gtlsprfiledesc-nss.c
new file mode 100644
index 0000000..eb0ee0f
--- /dev/null
+++ b/tls/nss/gtlsprfiledesc-nss.c
@@ -0,0 +1,554 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 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
+ * License as published by the Free Software Foundation; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+
+#include "config.h"
+#include "glib.h"
+
+#include <ssl.h>
+
+#include "gtlsprfiledesc-nss.h"
+
+/* This is a minimal PRFileDesc implementation that reads from and
+ * writes to a GIOStream. It only implements the functionality needed
+ * for GTlsConnectionNss's use of the NSS SSL APIs.
+ */
+
+typedef struct {
+  GTlsConnectionBase *tls;
+
+  int record_length, header_length;
+  guint8 header[5];
+  gboolean shutdown;
+} GTlsPRFileDescPrivate;
+
+#define TLS_RECORD_IS_HANDSHAKE(header) ((header)[0] == 22)
+#define TLS_RECORD_LENGTH(header) (((header)[3] << 8) + (header)[4])
+
+static PRStatus
+g_tls_prfiledesc_get_peer_name (PRFileDesc *fd,
+                               PRNetAddr  *addr)
+{
+  GTlsPRFileDescPrivate *priv = (GTlsPRFileDescPrivate *)fd->secret;
+  GSocketConnection *conn;
+  GSocketAddress *remote_addr;
+  GInetSocketAddress *isaddr;
+  GInetAddress *iaddr;
+  guint port;
+  GSocketFamily family;
+
+  /* Called first to see if @fd is connected (which it always is)
+   * and then later to get the remote IP address to use in the
+   * session cache ID.
+   */
+
+  if (!G_IS_SOCKET_CONNECTION (priv->tls->base_io_stream))
+    goto fail;
+
+  conn = G_SOCKET_CONNECTION (priv->tls->base_io_stream);
+  remote_addr = g_socket_connection_get_remote_address (conn, NULL);
+  if (!G_IS_INET_SOCKET_ADDRESS (remote_addr))
+    goto fail;
+
+  isaddr = G_INET_SOCKET_ADDRESS (remote_addr);
+  iaddr = g_inet_socket_address_get_address (isaddr);
+  port = g_inet_socket_address_get_port (isaddr);
+  family = g_inet_address_get_family (iaddr);
+
+  if (family == G_SOCKET_FAMILY_IPV4)
+    {
+      addr->inet.family = family;
+      addr->inet.port = port;
+      memcpy (&addr->inet.ip, g_inet_address_to_bytes (iaddr), 4);
+    }
+  else if (family == G_SOCKET_FAMILY_IPV6)
+    {
+      addr->ipv6.family = family;
+      addr->ipv6.port = port;
+      memcpy (&addr->ipv6.ip, g_inet_address_to_bytes (iaddr), 16);
+    }
+  else
+    {
+    fail:
+      /* NSS will error out completely if we don't return an IPv4 or
+       * IPv6 address. But it doesn't need it for anything other than
+       * the session cache keys, so as long as we tell it to not
+       * use the cache, it doesn't matter what we return.
+       */
+      SSL_OptionSet (fd->higher, SSL_NO_CACHE, PR_TRUE);
+
+      addr->inet.family = AF_INET;
+      addr->inet.port = 0;
+      addr->inet.ip = INADDR_LOOPBACK;
+    }
+
+  return PR_SUCCESS;
+}
+
+static PRInt32
+g_tls_prfiledesc_recv (PRFileDesc     *fd,
+                      void           *buf,
+                      PRInt32         len,
+                      PRIntn          flags,
+                      PRIntervalTime  timeout)
+{
+  GTlsPRFileDescPrivate *priv = (GTlsPRFileDescPrivate *)fd->secret;
+  PRInt32 ret;
+
+  if (priv->shutdown)
+    {
+      PR_SetError (PR_IO_ERROR, 0);
+      return -1;
+    }
+
+  /* "This obsolete parameter must always be zero." */
+  g_return_val_if_fail (flags == 0, -1);
+
+  /* We never call PR_Recv with a timeout, though there may
+   * be one specified on the underlying socket.
+   */
+  g_return_val_if_fail (timeout == PR_INTERVAL_NO_TIMEOUT, -1);
+
+  ret = g_pollable_stream_read (G_INPUT_STREAM (priv->tls->base_istream),
+                               buf, len, priv->tls->read_blocking,
+                               priv->tls->read_cancellable, &priv->tls->read_error);
+  if (ret == -1)
+    {
+      if (g_error_matches (priv->tls->read_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+       PR_SetError (PR_WOULD_BLOCK_ERROR, 0);
+      else
+       PR_SetError (PR_IO_ERROR, 0);
+      return -1;
+    }
+
+  /* GTlsConnectionBase needs to know when handshakes are happening,
+   * but NSS prefers to just do them behind your back. We work around
+   * this by snooping the traffic to see if the other side is
+   * starting/requesting a handshake. This only requires looking at
+   * the TLS framing, so it works even if the data is encrypted.
+   *
+   * This code assumes that NSS never makes reads that cross the
+   * header/data boundary. Which is true.
+   */
+  if (priv->header_length < sizeof (priv->header))
+    {
+      guint8 *header = NULL;
+
+      /* Reading the start of a new record */
+      if (priv->header_length == 0 && ret == sizeof (priv->header))
+       {
+         header = buf;
+         priv->header_length = sizeof (priv->header);
+       }
+      else
+       {
+         int header_nread = MIN (ret, sizeof (priv->header) - priv->header_length);
+
+         /* We got a short read, either now or before, so need to assemble the header. */
+         memcpy (priv->header + priv->header_length, buf, header_nread);
+         priv->header_length += header_nread;
+         if (priv->header_length == sizeof (priv->header))
+           header = priv->header;
+       }
+
+      if (header)
+       {
+         priv->record_length = TLS_RECORD_LENGTH (header);
+         if (TLS_RECORD_IS_HANDSHAKE (header) && !priv->tls->handshaking)
+           {
+             fflush (stdout);
+             priv->tls->need_handshake = TRUE;
+           }
+       }
+    }
+  else
+    {
+      priv->record_length -= ret;
+      if (priv->record_length == 0)
+       priv->header_length = 0;
+    }
+
+  return ret;
+}
+
+static PRInt32
+g_tls_prfiledesc_send (PRFileDesc     *fd,
+                      const void     *buf,
+                      PRInt32         len,
+                      PRIntn          flags,
+                      PRIntervalTime  timeout)
+{
+  GTlsPRFileDescPrivate *priv = (GTlsPRFileDescPrivate *)fd->secret;
+  PRInt32 ret;
+
+  if (priv->shutdown)
+    {
+      PR_SetError (PR_IO_ERROR, 0);
+      return -1;
+    }
+
+  /* "This obsolete parameter must always be zero." */
+  g_return_val_if_fail (flags == 0, -1);
+
+  /* We never call PR_Send with a timeout, though there may
+   * be one specified on the underlying socket.
+   */
+  g_return_val_if_fail (timeout == PR_INTERVAL_NO_TIMEOUT, -1);
+
+  ret = g_pollable_stream_write (G_OUTPUT_STREAM (priv->tls->base_ostream),
+                                buf, len, priv->tls->write_blocking,
+                                priv->tls->write_cancellable, &priv->tls->write_error);
+  if (ret == -1)
+    {
+      if (g_error_matches (priv->tls->write_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+       PR_SetError (PR_WOULD_BLOCK_ERROR, 0);
+      else
+       PR_SetError (PR_IO_ERROR, 0);
+    }
+
+  return ret;
+}
+
+static PRStatus
+g_tls_prfiledesc_get_socket_option (PRFileDesc         *fd,
+                                   PRSocketOptionData *data)
+{
+  GTlsPRFileDescPrivate *priv = (GTlsPRFileDescPrivate *)fd->secret;
+
+  if (data->option == PR_SockOpt_Nonblocking)
+    {
+      /* NSS wants to know if the socket is blocking or not. But
+       * GTlsConnections are only blocking or non-blocking per
+       * operation, so the answer is "it depends; why do you want to
+       * know?"
+       */
+      if (priv->tls->handshaking || priv->tls->closing)
+       data->value.non_blocking = FALSE;
+      else if (!priv->tls->reading)
+       data->value.non_blocking = !priv->tls->write_blocking;
+      else if (!priv->tls->writing)
+       data->value.non_blocking = !priv->tls->read_blocking;
+      else if (priv->tls->read_blocking == priv->tls->write_blocking)
+       data->value.non_blocking = !priv->tls->read_blocking;
+      else
+       {
+         // FIXME
+       }
+
+      return PR_SUCCESS;
+    }
+
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (PR_FAILURE);
+}
+
+static PRStatus
+g_tls_prfiledesc_set_socket_option (PRFileDesc               *fd,
+                                   const PRSocketOptionData *data)
+{
+  if (data->option == PR_SockOpt_NoDelay)
+    return PR_SUCCESS;
+
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (PR_FAILURE);
+}
+
+static PRStatus
+g_tls_prfiledesc_shutdown (PRFileDesc *fd,
+                          PRIntn      how)
+{
+  GTlsPRFileDescPrivate *priv = (GTlsPRFileDescPrivate *)fd->secret;
+
+  /* Gets called after some handshake failures */
+
+  priv->shutdown = TRUE;
+  return PR_SUCCESS;
+}
+
+static PRStatus
+g_tls_prfiledesc_close (PRFileDesc *fd)
+{
+  GTlsPRFileDescPrivate *priv = (GTlsPRFileDescPrivate *)fd->secret;
+
+  /* This will be called by the SSL layer after doing the SSL close,
+   * but we don't want to close the underlying iostream;
+   * GTlsConnectionBase will take care of that.
+   */
+  g_slice_free (GTlsPRFileDescPrivate, priv);
+  return PR_SUCCESS;
+}
+
+static PRInt32
+g_tls_prfiledesc_read (PRFileDesc *fd,
+                      void       *buf,
+                      PRInt32     len)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRInt32
+g_tls_prfiledesc_write (PRFileDesc *fd,
+                       const void *buf,
+                       PRInt32     len)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRInt32
+g_tls_prfiledesc_available (PRFileDesc *fd)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRInt64
+g_tls_prfiledesc_available64 (PRFileDesc *fd)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRStatus
+g_tls_prfiledesc_fsync (PRFileDesc *fd)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (PR_FAILURE);
+}
+
+static PRInt32
+g_tls_prfiledesc_seek (PRFileDesc   *fd,
+                      PRInt32       offset,
+                      PRSeekWhence  how)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRInt64
+g_tls_prfiledesc_seek64 (PRFileDesc   *fd,
+                        PRInt64       offset,
+                        PRSeekWhence  how)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRStatus
+g_tls_prfiledesc_fileinfo (PRFileDesc *fd,
+                          PRFileInfo *info)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (PR_FAILURE);
+}
+
+static PRStatus
+g_tls_prfiledesc_fileinfo64 (PRFileDesc   *fd,
+                            PRFileInfo64 *info)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (PR_FAILURE);
+}
+
+static PRInt32
+g_tls_prfiledesc_writev (PRFileDesc     *fd,
+                        const PRIOVec  *iov,
+                        PRInt32         vectors, 
+                        PRIntervalTime  timeout)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRStatus
+g_tls_prfiledesc_connect (PRFileDesc      *fd,
+                         const PRNetAddr *addr,
+                         PRIntervalTime   timeout)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (PR_FAILURE);
+}
+
+static PRFileDesc*
+g_tls_prfiledesc_accept (PRFileDesc     *fd,
+                        PRNetAddr      *addr,
+                        PRIntervalTime  timeout)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (NULL);
+}
+
+static PRStatus
+g_tls_prfiledesc_bind (PRFileDesc      *fd,
+                      const PRNetAddr *addr)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (PR_FAILURE);
+}
+
+static PRStatus
+g_tls_prfiledesc_listen (PRFileDesc *fd,
+                        PRIntn      backlog)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (PR_FAILURE);
+}
+
+static PRInt32
+g_tls_prfiledesc_recvfrom (PRFileDesc     *fd,
+                          void           *buf,
+                          PRInt32         amount,
+                          PRIntn          flags,
+                          PRNetAddr      *addr,
+                          PRIntervalTime  timeout)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRInt32
+g_tls_prfiledesc_sendto (PRFileDesc      *fd,
+                        const void      *buf,
+                        PRInt32          amount,
+                        PRIntn           flags,
+                        const PRNetAddr *addr,
+                        PRIntervalTime   timeout)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRInt16
+g_tls_prfiledesc_poll (PRFileDesc *fd,
+                      PRInt16     how_flags,
+                      PRInt16    *p_out_flags)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRInt32
+g_tls_prfiledesc_accept_read (PRFileDesc      *sd,
+                             PRFileDesc     **nd,
+                             PRNetAddr      **raddr,
+                             void            *buf,
+                             PRInt32          amount,
+                             PRIntervalTime   t)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRInt32
+g_tls_prfiledesc_transmit_file (PRFileDesc          *sd,
+                               PRFileDesc          *fd,
+                               const void          *headers,
+                               PRInt32              hlen,
+                               PRTransmitFileFlags  flags,
+                               PRIntervalTime       timeout)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRStatus
+g_tls_prfiledesc_get_sock_name (PRFileDesc *fd,
+                               PRNetAddr  *name)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (PR_FAILURE);
+}
+
+static PRInt32
+g_tls_prfiledesc_send_file (PRFileDesc          *sd,
+                           PRSendFileData      *sfd,
+                           PRTransmitFileFlags  flags,
+                           PRIntervalTime       timeout)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (-1);
+}
+
+static PRStatus
+g_tls_prfiledesc_connect_continue (PRFileDesc *fd,
+                                  PRInt16     out_flags)
+{
+  PR_SetError (PR_NOT_IMPLEMENTED_ERROR, 0);
+  g_return_val_if_reached (PR_FAILURE);
+}
+
+
+static const PRIOMethods g_tls_prfiledesc_methods = {
+  PR_DESC_LAYERED,
+  g_tls_prfiledesc_close,
+  g_tls_prfiledesc_read,
+  g_tls_prfiledesc_write,
+  g_tls_prfiledesc_available,
+  g_tls_prfiledesc_available64,
+  g_tls_prfiledesc_fsync,
+  g_tls_prfiledesc_seek,
+  g_tls_prfiledesc_seek64,
+  g_tls_prfiledesc_fileinfo,
+  g_tls_prfiledesc_fileinfo64,
+  g_tls_prfiledesc_writev,
+  g_tls_prfiledesc_connect,
+  g_tls_prfiledesc_accept,
+  g_tls_prfiledesc_bind,
+  g_tls_prfiledesc_listen,
+  g_tls_prfiledesc_shutdown,
+  g_tls_prfiledesc_recv,
+  g_tls_prfiledesc_send,
+  g_tls_prfiledesc_recvfrom,
+  g_tls_prfiledesc_sendto,
+  g_tls_prfiledesc_poll,
+  g_tls_prfiledesc_accept_read,
+  g_tls_prfiledesc_transmit_file,
+  g_tls_prfiledesc_get_sock_name,
+  g_tls_prfiledesc_get_peer_name,
+  NULL, /* getsockopt (obsolete) */
+  NULL, /* setsockopt (obsolete) */
+  g_tls_prfiledesc_get_socket_option,
+  g_tls_prfiledesc_set_socket_option,
+  g_tls_prfiledesc_send_file,
+  g_tls_prfiledesc_connect_continue,
+  NULL, /* reserved for future use */
+  NULL, /* reserved for future use */
+  NULL, /* reserved for future use */
+  NULL  /* reserved for future use */
+};
+
+PRFileDesc *
+g_tls_prfiledesc_new (GTlsConnectionNss *nss)
+{
+  PRFileDesc *prfd = PR_NEWZAP (PRFileDesc);
+  GTlsPRFileDescPrivate *priv;
+
+  prfd->methods = &g_tls_prfiledesc_methods;
+  prfd->identity = PR_GetUniqueIdentity ("GTls");
+
+  priv = g_slice_new0 (GTlsPRFileDescPrivate);
+  prfd->secret = (gpointer)priv;
+  priv->tls = G_TLS_CONNECTION_BASE (nss);
+
+  return prfd;
+}
+
diff --git a/tls/nss/gtlsprfiledesc-nss.h b/tls/nss/gtlsprfiledesc-nss.h
new file mode 100644
index 0000000..03ae619
--- /dev/null
+++ b/tls/nss/gtlsprfiledesc-nss.h
@@ -0,0 +1,22 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This program 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_PRFILEDESC_NSS_H__
+#define __G_TLS_PRFILEDESC_NSS_H__
+
+#include <nspr.h>
+
+#include "gtlsconnection-nss.h"
+
+PRFileDesc *g_tls_prfiledesc_new (GTlsConnectionNss *nss);
+
+#endif /* __G_TLS_PRFILEDESC_NSS_H__ */
diff --git a/tls/nss/gtlsserverconnection-nss.c b/tls/nss/gtlsserverconnection-nss.c
new file mode 100644
index 0000000..c8d8e12
--- /dev/null
+++ b/tls/nss/gtlsserverconnection-nss.c
@@ -0,0 +1,140 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 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
+ * License as published by the Free Software Foundation; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <glib.h>
+
+#include "gtlsserverconnection-nss.h"
+#include "gtlscertificate-nss.h"
+
+#include <ssl.h>
+
+#include <glib/gi18n-lib.h>
+
+enum
+{
+  PROP_0,
+  PROP_AUTHENTICATION_MODE
+};
+
+static void g_tls_server_connection_nss_server_connection_interface_init (GTlsServerConnectionInterface 
*iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionNss, g_tls_server_connection_nss, G_TYPE_TLS_CONNECTION_NSS,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_TLS_SERVER_CONNECTION,
+                                               g_tls_server_connection_nss_server_connection_interface_init))
+
+struct _GTlsServerConnectionNssPrivate
+{
+  GTlsAuthenticationMode authentication_mode;
+};
+
+static void
+certificate_set (GObject    *object,
+                GParamSpec *pspec,
+                gpointer    user_data)
+{
+  GTlsConnectionNss *conn_nss = G_TLS_CONNECTION_NSS (object);
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+
+  SSL_ConfigSecureServer (conn_nss->prfd,
+                         g_tls_certificate_nss_get_cert (G_TLS_CERTIFICATE_NSS (tls->certificate)),
+                         g_tls_certificate_nss_get_key (G_TLS_CERTIFICATE_NSS (tls->certificate)),
+                         kt_rsa);
+}
+
+static void
+g_tls_server_connection_nss_init (GTlsServerConnectionNss *nss)
+{
+  GTlsConnectionNss *conn_nss = G_TLS_CONNECTION_NSS (nss);
+
+  nss->priv = G_TYPE_INSTANCE_GET_PRIVATE (nss, G_TYPE_TLS_SERVER_CONNECTION_NSS, 
GTlsServerConnectionNssPrivate);
+
+  SSL_ResetHandshake (conn_nss->prfd, PR_TRUE);
+
+  g_signal_connect (nss, "notify::certificate",
+                   G_CALLBACK (certificate_set), NULL);
+}
+
+static void
+g_tls_server_connection_nss_get_property (GObject    *object,
+                                         guint       prop_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+  GTlsServerConnectionNss *nss = G_TLS_SERVER_CONNECTION_NSS (object);
+
+  switch (prop_id)
+    {
+    case PROP_AUTHENTICATION_MODE:
+      g_value_set_enum (value, nss->priv->authentication_mode);
+      break;
+      
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_server_connection_nss_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+  GTlsServerConnectionNss *nss = G_TLS_SERVER_CONNECTION_NSS (object);
+  GTlsConnectionNss *conn_nss = G_TLS_CONNECTION_NSS (object);
+
+  switch (prop_id)
+    {
+    case PROP_AUTHENTICATION_MODE:
+      nss->priv->authentication_mode = g_value_get_enum (value);
+      SSL_OptionSet (conn_nss->prfd, SSL_REQUEST_CERTIFICATE,
+                    nss->priv->authentication_mode != G_TLS_AUTHENTICATION_NONE);
+      SSL_OptionSet (conn_nss->prfd, SSL_REQUIRE_CERTIFICATE,
+                    nss->priv->authentication_mode == G_TLS_AUTHENTICATION_REQUIRED);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_server_connection_nss_class_init (GTlsServerConnectionNssClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsServerConnectionNssPrivate));
+
+  gobject_class->get_property = g_tls_server_connection_nss_get_property;
+  gobject_class->set_property = g_tls_server_connection_nss_set_property;
+
+  g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
+
+  /* FIXME: global! (but if we don't call it, it will crash
+   * if the client aborts a handshake).
+   */
+  SSL_ConfigServerSessionIDCache (0, 0, 0, NULL);
+}
+
+static void
+g_tls_server_connection_nss_server_connection_interface_init (GTlsServerConnectionInterface *iface)
+{
+}
+
diff --git a/tls/nss/gtlsserverconnection-nss.h b/tls/nss/gtlsserverconnection-nss.h
new file mode 100644
index 0000000..6da6a86
--- /dev/null
+++ b/tls/nss/gtlsserverconnection-nss.h
@@ -0,0 +1,47 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This program 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 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_SERVER_CONNECTION_NSS_H__
+#define __G_TLS_SERVER_CONNECTION_NSS_H__
+
+#include <gio/gio.h> 
+#include "gtlsconnection-nss.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_SERVER_CONNECTION_NSS            (g_tls_server_connection_nss_get_type ())
+#define G_TLS_SERVER_CONNECTION_NSS(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), 
G_TYPE_TLS_SERVER_CONNECTION_NSS, GTlsServerConnectionNss))
+#define G_TLS_SERVER_CONNECTION_NSS_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), 
G_TYPE_TLS_SERVER_CONNECTION_NSS, GTlsServerConnectionNssClass))
+#define G_IS_TLS_SERVER_CONNECTION_NSS(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), 
G_TYPE_TLS_SERVER_CONNECTION_NSS))
+#define G_IS_TLS_SERVER_CONNECTION_NSS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), 
G_TYPE_TLS_SERVER_CONNECTION_NSS))
+#define G_TLS_SERVER_CONNECTION_NSS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), 
G_TYPE_TLS_SERVER_CONNECTION_NSS, GTlsServerConnectionNssClass))
+
+typedef struct _GTlsServerConnectionNssPrivate                   GTlsServerConnectionNssPrivate;
+typedef struct _GTlsServerConnectionNssClass                     GTlsServerConnectionNssClass;
+typedef struct _GTlsServerConnectionNss                          GTlsServerConnectionNss;
+
+struct _GTlsServerConnectionNssClass
+{
+  GTlsConnectionNssClass parent_class;
+};
+
+struct _GTlsServerConnectionNss
+{
+  GTlsConnectionNss parent_instance;
+  GTlsServerConnectionNssPrivate *priv;
+};
+
+GType g_tls_server_connection_nss_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_TLS_SERVER_CONNECTION_NSS_H___ */
diff --git a/tls/nss/nss-module.c b/tls/nss/nss-module.c
new file mode 100644
index 0000000..a1c36d2
--- /dev/null
+++ b/tls/nss/nss-module.c
@@ -0,0 +1,47 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 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
+ * License as published by the Free Software Foundation; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "gtlsbackend-nss.h"
+
+
+void
+g_io_module_load (GIOModule *module)
+{
+  g_tls_backend_nss_register (module);
+}
+
+void
+g_io_module_unload (GIOModule *module)
+{
+}
+
+gchar **
+g_io_module_query (void)
+{
+  gchar *eps[] = {
+    G_TLS_BACKEND_EXTENSION_POINT_NAME,
+    NULL
+  };
+  return g_strdupv (eps);
+}
diff --git a/tls/tests/Makefile.am b/tls/tests/Makefile.am
index 305394d..fd6cc08 100644
--- a/tls/tests/Makefile.am
+++ b/tls/tests/Makefile.am
@@ -11,11 +11,39 @@ noinst_PROGRAMS = $(TEST_PROGS)
 LDADD  = \
        $(GLIB_LIBS)
 
-TEST_PROGS +=         \
-       certificate   \
-       file-database \
-       connection    \
+if HAVE_GNUTLS
+TEST_PROGS +=                  \
+       certificate-gnutls      \
+       connection-gnutls       \
+       file-database-gnutls    \
        $(NULL)
+endif
+
+if HAVE_NSS
+TEST_PROGS +=                  \
+       certificate-nss         \
+       connection-nss          \
+       file-database-nss       \
+       $(NULL)
+endif
+
+certificate_gnutls_SOURCES = certificate.c
+certificate_gnutls_CPPFLAGS = -DBACKEND=\""gnutls"\"
+
+connection_gnutls_SOURCES = connection.c
+connection_gnutls_CPPFLAGS = -DBACKEND=\""gnutls"\"
+
+file_database_gnutls_SOURCES = file-database.c
+file_database_gnutls_CPPFLAGS = -DBACKEND=\""gnutls"\"
+
+certificate_nss_SOURCES = certificate.c
+certificate_nss_CPPFLAGS = -DBACKEND=\""nss"\"
+
+connection_nss_SOURCES = connection.c
+connection_nss_CPPFLAGS = -DBACKEND=\""nss"\"
+
+file_database_nss_SOURCES = file-database.c
+file_database_nss_CPPFLAGS = -DBACKEND=\""nss"\"
 
 if HAVE_PKCS11
 
diff --git a/tls/tests/certificate.c b/tls/tests/certificate.c
index 408f3e2..7cee30c 100644
--- a/tls/tests/certificate.c
+++ b/tls/tests/certificate.c
@@ -88,15 +88,28 @@ test_create_pem (TestCertificate *test,
                  gconstpointer data)
 {
   GTlsCertificate *cert;
-  gchar *pem = NULL;
+  gchar *pem = NULL, *test_pem_clean, *cert_pem_clean;
   GError *error = NULL;
+  GRegex *re;
 
   cert = g_tls_certificate_new_from_pem (test->cert_pem, test->cert_pem_length, &error);
   g_assert_no_error (error);
   g_assert (G_IS_TLS_CERTIFICATE (cert));
 
   g_object_get (cert, "certificate-pem", &pem, NULL);
-  g_assert_cmpstr (pem, ==, test->cert_pem);
+  g_assert (pem != NULL);
+
+  /* It's OK if they differ in the placement of newlines */
+  re = g_regex_new ("\n", 0, 0, &error);
+  g_assert_no_error (error);
+  test_pem_clean = g_regex_replace_literal (re, test->cert_pem, -1, 0, "", 0, &error);
+  g_assert_no_error (error);
+  cert_pem_clean = g_regex_replace_literal (re, pem, -1, 0, "", 0, &error);
+  g_assert_no_error (error);
+
+  g_assert_cmpstr (cert_pem_clean, ==, test_pem_clean);
+  g_free (test_pem_clean);
+  g_free (cert_pem_clean);
   g_free (pem);
 
   g_object_add_weak_pointer (G_OBJECT (cert), (gpointer *)&cert);
@@ -234,6 +247,12 @@ static void
 teardown_verify (TestVerify      *test,
                  gconstpointer    data)
 {
+  g_assert (G_IS_TLS_DATABASE (test->database));
+  g_object_add_weak_pointer (G_OBJECT (test->database),
+                            (gpointer *)&test->database);
+  g_object_unref (test->database);
+  g_assert (test->database == NULL);
+
   g_assert (G_IS_TLS_CERTIFICATE (test->cert));
   g_object_add_weak_pointer (G_OBJECT (test->cert),
                             (gpointer *)&test->cert);
@@ -246,12 +265,6 @@ teardown_verify (TestVerify      *test,
   g_object_unref (test->anchor);
   g_assert (test->anchor == NULL);
 
-  g_assert (G_IS_TLS_DATABASE (test->database));
-  g_object_add_weak_pointer (G_OBJECT (test->database),
-                            (gpointer *)&test->database);
-  g_object_unref (test->database);
-  g_assert (test->database == NULL);
-
   g_object_add_weak_pointer (G_OBJECT (test->identity),
                             (gpointer *)&test->identity);
   g_object_unref (test->identity);
@@ -415,8 +428,9 @@ main (int   argc,
   g_test_init (&argc, &argv, NULL);
 
   g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
-  g_setenv ("GIO_EXTRA_MODULES", TOP_BUILDDIR "/tls/gnutls/.libs", TRUE);
-  g_setenv ("GIO_USE_TLS", "gnutls", TRUE);
+  g_setenv ("GIO_EXTRA_MODULES", TOP_BUILDDIR "/tls/" BACKEND "/.libs", TRUE);
+  g_setenv ("GIO_USE_TLS", BACKEND, TRUE);
+  g_assert (g_ascii_strcasecmp (G_OBJECT_TYPE_NAME (g_tls_backend_get_default ()), "GTlsBackend" BACKEND) == 
0);
 
   g_test_add ("/tls/certificate/create-pem", TestCertificate, NULL,
               setup_certificate, test_create_pem, teardown_certificate);
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 6236f83..b501953 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -165,7 +165,16 @@ on_server_close_finish (GObject        *object,
 
   g_io_stream_close_finish (G_IO_STREAM (object), res, &error);
   if (test->expect_server_error)
-    g_assert (error != NULL);
+    {
+      if (error)
+       {
+#if GLIB_CHECK_VERSION (2, 35, 3)
+         g_assert_error (error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE);
+#else
+         g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+#endif
+       }
+    }
   else
     g_assert_no_error (error);
   test->server_closed = TRUE;
@@ -464,9 +473,9 @@ on_notify_accepted_cas (GObject *obj,
                         GParamSpec *spec,
                         gpointer user_data)
 {
-  gboolean *changed = user_data;
-  g_assert (*changed == FALSE);
-  *changed = TRUE;
+  gint *changed = user_data;
+
+  (*changed)++;
 }
 
 static void
@@ -477,7 +486,8 @@ test_client_auth_connection (TestConnection *test,
   GError *error = NULL;
   GTlsCertificate *cert;
   GTlsCertificate *peer;
-  gboolean cas_changed;
+  gboolean rehandshaking = test->rehandshake;
+  gint cas_changed;
 
   test->database = g_tls_file_database_new (TEST_FILE ("ca-roots.pem"), &error);
   g_assert_no_error (error);
@@ -500,7 +510,7 @@ test_client_auth_connection (TestConnection *test,
   g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
                                                 G_TLS_CERTIFICATE_VALIDATE_ALL);
 
-  cas_changed = FALSE;
+  cas_changed = 0;
   g_signal_connect (test->client_connection, "notify::accepted-cas",
                     G_CALLBACK (on_notify_accepted_cas), &cas_changed);
 
@@ -513,7 +523,10 @@ test_client_auth_connection (TestConnection *test,
   peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (test->server_connection));
   g_assert (peer != NULL);
   g_assert (g_tls_certificate_is_same (peer, cert));
-  g_assert (cas_changed == TRUE);
+  if (rehandshaking)
+    g_assert_cmpint (cas_changed, <=, 2); /* FIXME: gnutls/nss inconsistency */
+  else
+    g_assert_cmpint (cas_changed, ==, 1);
 
   g_object_unref (cert);
 }
@@ -640,7 +653,9 @@ test_failed_connection (TestConnection *test,
   g_main_loop_run (test->loop);
 
   g_assert_error (test->read_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
-  g_assert_no_error (test->server_error);
+  /* FIXME: gnutls/nss inconsistency */
+  if (test->server_error)
+    g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_HANDSHAKE);
 }
 
 static void
@@ -1070,8 +1085,9 @@ main (int   argc,
   g_test_bug_base ("http://bugzilla.gnome.org/";);
 
   g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
-  g_setenv ("GIO_EXTRA_MODULES", TOP_BUILDDIR "/tls/gnutls/.libs", TRUE);
-  g_setenv ("GIO_USE_TLS", "gnutls", TRUE);
+  g_setenv ("GIO_EXTRA_MODULES", TOP_BUILDDIR "/tls/" BACKEND "/.libs", TRUE);
+  g_setenv ("GIO_USE_TLS", BACKEND, TRUE);
+  g_assert (g_ascii_strcasecmp (G_OBJECT_TYPE_NAME (g_tls_backend_get_default ()), "GTlsBackend" BACKEND) == 
0);
 
   g_test_add ("/tls/connection/basic", TestConnection, NULL,
               setup_connection, test_basic_connection, teardown_connection);
diff --git a/tls/tests/file-database.c b/tls/tests/file-database.c
index 5b6756f..5710f91 100644
--- a/tls/tests/file-database.c
+++ b/tls/tests/file-database.c
@@ -510,8 +510,9 @@ main (int   argc,
   g_test_init (&argc, &argv, NULL);
 
   g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
-  g_setenv ("GIO_EXTRA_MODULES", TOP_BUILDDIR "/tls/gnutls/.libs", TRUE);
-  g_setenv ("GIO_USE_TLS", "gnutls", TRUE);
+  g_setenv ("GIO_EXTRA_MODULES", TOP_BUILDDIR "/tls/" BACKEND "/.libs", TRUE);
+  g_setenv ("GIO_USE_TLS", BACKEND, TRUE);
+  g_assert (g_ascii_strcasecmp (G_OBJECT_TYPE_NAME (g_tls_backend_get_default ()), "GTlsBackend" BACKEND) == 
0);
 
   g_test_add_func ("/tls/backend/default-database-is-singleton",
                    test_default_database_is_singleton);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]