[glib-networking/wip/nss: 2/2] nss: add an NSS-based GTlsBackend [wip]
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/wip/nss: 2/2] nss: add an NSS-based GTlsBackend [wip]
- Date: Mon, 24 Jun 2013 15:02:11 +0000 (UTC)
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]