proxy/libproxy/glib-pacrunner
proxy/libproxy/org.gtk.GLib.PACRunner.service
+
+/tls/tests/tls
SUBDIRS += tls/gnutls
endif
+SUBDIRS += tls/tests
+
install-exec-hook:
if test -n "$(GIO_QUERYMODULES)" -a -z "$(DESTDIR)"; then \
$(GIO_QUERYMODULES) $(GIO_MODULE_DIR) ; \
po/Makefile.in po/Makefile
proxy/libproxy/Makefile
proxy/gnome/Makefile
- tls/gnutls/Makefile
- ])
+ tls/gnutls/Makefile
+ tls/tests/Makefile
+ ])
AC_OUTPUT
echo ""
gtlsclientconnection-gnutls.h \
gtlsconnection-gnutls.c \
gtlsconnection-gnutls.h \
+ gtlsdatabase-gnutls.c \
+ gtlsdatabase-gnutls.h \
+ gtlsfiledatabase-gnutls.c \
+ gtlsfiledatabase-gnutls.h \
gtlsinputstream-gnutls.c \
gtlsinputstream-gnutls.h \
gtlsoutputstream-gnutls.c \
#include "gtlsbackend-gnutls.h"
#include "gtlscertificate-gnutls.h"
#include "gtlsclientconnection-gnutls.h"
+#include "gtlsfiledatabase-gnutls.h"
#include "gtlsserverconnection-gnutls.h"
+struct _GTlsBackendGnutlsPrivate
+{
+ GMutex *mutex;
+ GTlsDatabase *default_database;
+};
+
static void g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface);
G_DEFINE_DYNAMIC_TYPE_EXTENDED (GTlsBackendGnutls, g_tls_backend_gnutls, G_TYPE_OBJECT, 0,
* g_io_modules_scan_all_in_directory()).
*/
g_once (&gnutls_inited, gtls_gnutls_init, NULL);
+
+ backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend, G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutlsPrivate);
+ backend->priv->mutex = g_mutex_new ();
}
static void
-g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
+g_tls_backend_gnutls_finalize (GObject *object)
{
+ GTlsBackendGnutls *backend = G_TLS_BACKEND_GNUTLS (object);
+
+ if (backend->priv->default_database)
+ g_object_unref (backend->priv->default_database);
+ g_mutex_free (backend->priv->mutex);
+
+ G_OBJECT_CLASS (g_tls_backend_gnutls_parent_class)->finalize (object);
}
static void
-g_tls_backend_gnutls_class_finalize (GTlsBackendGnutlsClass *backend_class)
+g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (backend_class);
+ gobject_class->finalize = g_tls_backend_gnutls_finalize;
+ g_type_class_add_private (backend_class, sizeof (GTlsBackendGnutlsPrivate));
}
static void
-g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
+g_tls_backend_gnutls_class_finalize (GTlsBackendGnutlsClass *backend_class)
{
- iface->get_certificate_type = g_tls_certificate_gnutls_get_type;
- iface->get_client_connection_type = g_tls_client_connection_gnutls_get_type;
- iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
}
-#ifdef GTLS_SYSTEM_CA_FILE
-/* Parsing the system CA list takes a noticeable amount of time.
- * So we only do it once, and only when we actually need to see it.
- */
-static const GList *
-get_ca_lists (gnutls_x509_crt_t **cas,
- int *num_cas)
+static GTlsDatabase*
+g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
{
- static gnutls_x509_crt_t *ca_list_gnutls;
- static int ca_list_length;
- static GList *ca_list;
+ GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
+ const gchar *anchor_file = NULL;
+ GTlsDatabase *result;
+ GError *error = NULL;
- if (g_once_init_enter ((volatile gsize *)&ca_list_gnutls))
- {
- GError *error = NULL;
- gnutls_x509_crt_t *x509_crts;
- GList *c;
- int i;
+ g_mutex_lock (self->priv->mutex);
- ca_list = g_tls_certificate_list_new_from_file (GTLS_SYSTEM_CA_FILE, &error);
+ if (self->priv->default_database)
+ {
+ result = g_object_ref (self->priv->default_database);
+ }
+ else
+ {
+#ifdef GTLS_SYSTEM_CA_FILE
+ anchor_file = GTLS_SYSTEM_CA_FILE;
+#endif
+ result = g_tls_file_database_new (anchor_file, &error);
if (error)
- {
- g_warning ("Failed to read system CA file %s: %s.",
- GTLS_SYSTEM_CA_FILE, error->message);
- g_error_free (error);
- /* Note that this is not a security problem, since if
- * G_TLS_VALIDATE_CA is set, then this just means validation
- * will always fail, and if it isn't set, then it doesn't
- * matter that we couldn't read the CAs.
- */
- }
-
- ca_list_length = g_list_length (ca_list);
- x509_crts = g_new (gnutls_x509_crt_t, ca_list_length);
- for (c = ca_list, i = 0; c; c = c->next, i++)
- x509_crts[i] = g_tls_certificate_gnutls_get_cert (c->data);
-
- g_once_init_leave ((volatile gsize *)&ca_list_gnutls, GPOINTER_TO_SIZE (x509_crts));
+ {
+ g_warning ("couldn't load TLS file database: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ self->priv->default_database = g_object_ref (result);
+ }
}
- if (cas)
- *cas = ca_list_gnutls;
- if (num_cas)
- *num_cas = ca_list_length;
-
- return ca_list;
-}
-#endif
+ g_mutex_unlock (self->priv->mutex);
-const GList *
-g_tls_backend_gnutls_get_system_ca_list_gtls (void)
-{
-#ifdef GTLS_SYSTEM_CA_FILE
- return get_ca_lists (NULL, NULL);
-#else
- return NULL;
-#endif
+ return result;
}
-void
-g_tls_backend_gnutls_get_system_ca_list_gnutls (gnutls_x509_crt_t **cas,
- int *num_cas)
+static void
+g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
{
-#ifdef GTLS_SYSTEM_CA_FILE
- get_ca_lists (cas, num_cas);
-#else
- *cas = NULL;
- *num_cas = 0;
-#endif
+ iface->get_certificate_type = g_tls_certificate_gnutls_get_type;
+ iface->get_client_connection_type = g_tls_client_connection_gnutls_get_type;
+ iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
+ iface->get_file_database_type = g_tls_file_database_gnutls_get_type;
+ iface->get_default_database = g_tls_backend_gnutls_get_default_database;
}
/* Session cache support; all the details are sort of arbitrary. Note
#define G_IS_TLS_BACKEND_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_BACKEND_GNUTLS))
#define G_TLS_BACKEND_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutlsClass))
-typedef struct _GTlsBackendGnutlsClass GTlsBackendGnutlsClass;
-typedef struct _GTlsBackendGnutls GTlsBackendGnutls;
+typedef struct _GTlsBackendGnutls GTlsBackendGnutls;
+typedef struct _GTlsBackendGnutlsClass GTlsBackendGnutlsClass;
+typedef struct _GTlsBackendGnutlsPrivate GTlsBackendGnutlsPrivate;
struct _GTlsBackendGnutlsClass
{
struct _GTlsBackendGnutls
{
GObject parent_instance;
+ GTlsBackendGnutlsPrivate *priv;
};
GType g_tls_backend_gnutls_get_type (void) G_GNUC_CONST;
void g_tls_backend_gnutls_register (GIOModule *module);
-const GList *g_tls_backend_gnutls_get_system_ca_list_gtls (void) G_GNUC_CONST;
-void g_tls_backend_gnutls_get_system_ca_list_gnutls (gnutls_x509_crt_t **cas,
- int *num_cas);
-
void g_tls_backend_gnutls_cache_session_data (const gchar *session_id,
guchar *session_data,
gsize session_data_length);
return G_TLS_CERTIFICATE_BAD_IDENTITY;
}
+
+void
+g_tls_certificate_gnutls_set_issuer (GTlsCertificateGnutls *gnutls,
+ GTlsCertificateGnutls *issuer)
+{
+ g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
+ g_return_if_fail (!issuer || G_IS_TLS_CERTIFICATE_GNUTLS (issuer));
+
+ if (issuer)
+ g_object_ref (issuer);
+ if (gnutls->priv->issuer)
+ g_object_unref (gnutls->priv->issuer);
+ gnutls->priv->issuer = issuer;
+ g_object_notify (G_OBJECT (gnutls), "issuer");
+}
GTlsCertificateFlags g_tls_certificate_gnutls_convert_flags (guint gnutls_flags);
+void g_tls_certificate_gnutls_set_issuer (GTlsCertificateGnutls *gnutls,
+ GTlsCertificateGnutls *issuer);
+
+GTlsCertificateGnutls* g_tls_certificate_gnutls_steal_issuer (GTlsCertificateGnutls *gnutls);
G_END_DECLS
GTlsCertificateFlags *errors)
{
GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn_gnutls);
+ GTlsDatabase *database;
gboolean accepted;
+ GError *error = NULL;
- if (gnutls->priv->server_identity)
+ database = g_tls_connection_get_database (G_TLS_CONNECTION (conn_gnutls));
+ if (database == NULL)
+ {
+ *errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+ *errors |= g_tls_certificate_verify (peer_certificate, gnutls->priv->server_identity, NULL);
+ }
+ else
{
- *errors |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (peer_certificate),
- gnutls->priv->server_identity);
+ *errors |= g_tls_database_verify_chain (database, peer_certificate,
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+ gnutls->priv->server_identity,
+ g_tls_connection_get_interaction (G_TLS_CONNECTION (gnutls)),
+ G_TLS_DATABASE_VERIFY_NONE,
+ NULL, &error);
+ if (error)
+ {
+ g_warning ("failure verifying certificate chain: %s",
+ error->message);
+ g_clear_error (&error);
+ }
}
if (*errors & gnutls->priv->validation_flags)
PROP_REQUIRE_CLOSE_NOTIFY,
PROP_REHANDSHAKE_MODE,
PROP_USE_SYSTEM_CERTDB,
+ PROP_DATABASE,
PROP_CERTIFICATE,
+ PROP_INTERACTION,
PROP_PEER_CERTIFICATE,
PROP_PEER_CERTIFICATE_ERRORS
};
GPollableInputStream *base_istream;
GPollableOutputStream *base_ostream;
- GList *ca_list;
gnutls_certificate_credentials creds;
gnutls_session session;
GTlsCertificateFlags peer_certificate_errors;
gboolean require_close_notify;
GTlsRehandshakeMode rehandshake_mode;
- gboolean use_system_certdb;
+ gboolean is_system_certdb;
+ GTlsDatabase *database;
+ gboolean database_is_unset;
gboolean need_handshake, handshaking, ever_handshaked;
gboolean closing;
GInputStream *tls_istream;
GOutputStream *tls_ostream;
+ GTlsInteraction *interaction;
+
GError *error;
GCancellable *cancellable;
gboolean blocking, eof;
g_object_class_override_property (gobject_class, PROP_REQUIRE_CLOSE_NOTIFY, "require-close-notify");
g_object_class_override_property (gobject_class, PROP_REHANDSHAKE_MODE, "rehandshake-mode");
g_object_class_override_property (gobject_class, PROP_USE_SYSTEM_CERTDB, "use-system-certdb");
+ g_object_class_override_property (gobject_class, PROP_DATABASE, "database");
g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
+ g_object_class_override_property (gobject_class, PROP_INTERACTION, "interaction");
g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE, "peer-certificate");
g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE_ERRORS, "peer-certificate-errors");
}
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
gnutls->priv->need_handshake = TRUE;
+
+ gnutls->priv->database_is_unset = TRUE;
+ gnutls->priv->is_system_certdb = TRUE;
}
static gnutls_priority_t priorities[2][2];
if (connection->priv->creds)
gnutls_certificate_free_credentials (connection->priv->creds);
+ if (connection->priv->database)
+ g_object_unref (connection->priv->database);
if (connection->priv->certificate)
g_object_unref (connection->priv->certificate);
if (connection->priv->peer_certificate)
g_object_unref (connection->priv->peer_certificate);
+ g_clear_object (&connection->priv->interaction);
+
if (connection->priv->error)
g_error_free (connection->priv->error);
GParamSpec *pspec)
{
GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
+ GTlsBackend *backend;
switch (prop_id)
{
break;
case PROP_USE_SYSTEM_CERTDB:
- g_value_set_boolean (value, gnutls->priv->use_system_certdb);
+ g_value_set_boolean (value, gnutls->priv->is_system_certdb);
+ break;
+
+ case PROP_DATABASE:
+ if (gnutls->priv->database_is_unset)
+ {
+ backend = g_tls_backend_get_default ();
+ gnutls->priv->database = g_tls_backend_get_default_database (backend);
+ gnutls->priv->database_is_unset = FALSE;
+ }
+ g_value_set_object (value, gnutls->priv->database);
break;
case PROP_CERTIFICATE:
g_value_set_object (value, gnutls->priv->certificate);
break;
+ case PROP_INTERACTION:
+ g_value_set_object (value, gnutls->priv->interaction);
+ break;
+
case PROP_PEER_CERTIFICATE:
g_value_set_object (value, gnutls->priv->peer_certificate);
break;
GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
GInputStream *istream;
GOutputStream *ostream;
+ gboolean system_certdb;
+ GTlsBackend *backend;
switch (prop_id)
{
break;
case PROP_USE_SYSTEM_CERTDB:
- gnutls->priv->use_system_certdb = g_value_get_boolean (value);
-
- gnutls_certificate_free_cas (gnutls->priv->creds);
- if (gnutls->priv->use_system_certdb)
- {
- gnutls_x509_crt_t *cas;
- int num_cas;
+ system_certdb = g_value_get_boolean (value);
+ if (system_certdb != gnutls->priv->is_system_certdb)
+ {
+ g_clear_object (&gnutls->priv->database);
+ if (system_certdb)
+ {
+ backend = g_tls_backend_get_default ();
+ gnutls->priv->database = g_tls_backend_get_default_database (backend);
+ }
+ gnutls->priv->is_system_certdb = system_certdb;
+ }
+ break;
- g_tls_backend_gnutls_get_system_ca_list_gnutls (&cas, &num_cas);
- gnutls_certificate_set_x509_trust (gnutls->priv->creds, cas, num_cas);
- }
+ case PROP_DATABASE:
+ g_clear_object (&gnutls->priv->database);
+ gnutls->priv->database = g_value_dup_object (value);
+ gnutls->priv->is_system_certdb = FALSE;
+ gnutls->priv->database_is_unset = FALSE;
break;
case PROP_CERTIFICATE:
gnutls->priv->certificate = g_value_dup_object (value);
break;
+ case PROP_INTERACTION:
+ g_clear_object (&gnutls->priv->interaction);
+ gnutls->priv->interaction = g_value_dup_object (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
if (peer_certificate)
{
- int status;
-
- status = gnutls_certificate_verify_peers (gnutls->priv->session);
- peer_certificate_errors = g_tls_certificate_gnutls_convert_flags (status);
- if (peer_certificate_errors)
- {
- /* gnutls_certificate_verify_peers() bails out on the first
- * error, which may be G_TLS_CERTIFICATE_UNKNOWN_CA, but the
- * caller may be planning to check that part themselves. So
- * call g_tls_certificate_verify() to get any other errors.
- */
- peer_certificate_errors |= g_tls_certificate_verify (peer_certificate, NULL, NULL);
- }
-
if (!G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->verify_peer (gnutls, peer_certificate, &peer_certificate_errors))
{
g_object_unref (peer_certificate);
--- /dev/null
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 Collabora, Ltd
+ *
+ * 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.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "gtlsdatabase-gnutls.h"
+
+#include "gtlscertificate-gnutls.h"
+
+#include <glib/gi18n-lib.h>
+
+G_DEFINE_ABSTRACT_TYPE (GTlsDatabaseGnutls, g_tls_database_gnutls, G_TYPE_TLS_DATABASE);
+
+enum {
+ STATUS_FAILURE,
+ STATUS_INCOMPLETE,
+ STATUS_SELFSIGNED,
+ STATUS_PINNED,
+ STATUS_ANCHORED,
+};
+
+static void
+g_tls_database_gnutls_init (GTlsDatabaseGnutls *self)
+{
+
+}
+
+static gboolean
+is_self_signed (GTlsCertificateGnutls *certificate)
+{
+ const gnutls_x509_crt_t cert = g_tls_certificate_gnutls_get_cert (certificate);
+ return (gnutls_x509_crt_check_issuer (cert, cert) > 0);
+}
+
+static gint
+build_certificate_chain (GTlsDatabaseGnutls *self,
+ GTlsCertificateGnutls *chain,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GTlsInteraction *interaction,
+ GTlsDatabaseVerifyFlags flags,
+ GCancellable *cancellable,
+ GTlsCertificateGnutls **anchor,
+ GError **error)
+{
+
+ GTlsCertificateGnutls *certificate;
+ GTlsCertificate *issuer;
+
+ g_assert (anchor);
+ g_assert (chain);
+ g_assert (purpose);
+ g_assert (error);
+ g_assert (!*error);
+
+ /*
+ * Remember that the first certificate never changes in the chain.
+ * When we find a self-signed, pinned or anchored certificate, all
+ * issuers are truncated from the chain.
+ */
+
+ *anchor = NULL;
+ certificate = chain;
+
+ /* First check for pinned certificate */
+ if (g_tls_database_gnutls_lookup_assertion (self, certificate,
+ G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE,
+ purpose, identity, cancellable, error))
+ {
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ return STATUS_PINNED;
+ }
+ else if(*error)
+ {
+ return STATUS_FAILURE;
+ }
+
+ for (;;)
+ {
+ /* Was the last certificate self-signed? */
+ if (is_self_signed (certificate))
+ {
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ return STATUS_SELFSIGNED;
+ }
+
+ /* Bring over the next certificate in the chain */
+ issuer = g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (certificate));
+ if (issuer)
+ {
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+ }
+
+ /* Search for the next certificate in chain */
+ else
+ {
+ issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
+ G_TLS_CERTIFICATE (certificate),
+ interaction,
+ G_TLS_DATABASE_LOOKUP_NONE,
+ cancellable, error);
+ if (*error)
+ return STATUS_FAILURE;
+ else if (!issuer)
+ return STATUS_INCOMPLETE;
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+ g_tls_certificate_gnutls_set_issuer (certificate, G_TLS_CERTIFICATE_GNUTLS (issuer));
+ }
+
+ g_assert (issuer);
+ certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+
+ /* Now look up whether this certificate is an anchor */
+ if (g_tls_database_gnutls_lookup_assertion (self, certificate,
+ G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE,
+ purpose, identity, cancellable, error))
+ {
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ *anchor = certificate;
+ return STATUS_ANCHORED;
+ }
+ else if (*error)
+ {
+ return STATUS_FAILURE;
+ }
+ }
+
+ g_assert_not_reached ();
+}
+
+static GTlsCertificateFlags
+double_check_before_after_dates (GTlsCertificateGnutls *chain)
+{
+ GTlsCertificateFlags gtls_flags = 0;
+ gnutls_x509_crt_t cert;
+ time_t t, now;
+
+ now = time (NULL);
+ while (chain)
+ {
+ cert = g_tls_certificate_gnutls_get_cert (chain);
+ t = gnutls_x509_crt_get_activation_time (cert);
+ if (t == (time_t) -1 || t > now)
+ gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
+
+ t = gnutls_x509_crt_get_expiration_time (cert);
+ if (t == (time_t) -1 || t < now)
+ gtls_flags |= G_TLS_CERTIFICATE_EXPIRED;
+
+ chain = G_TLS_CERTIFICATE_GNUTLS (g_tls_certificate_get_issuer
+ (G_TLS_CERTIFICATE (chain)));
+ }
+
+ return gtls_flags;
+}
+
+static void
+convert_certificate_chain_to_gnutls (GTlsCertificateGnutls *chain,
+ gnutls_x509_crt_t **gnutls_chain,
+ guint *gnutls_chain_length)
+{
+ GTlsCertificate *cert;
+ guint i;
+
+ g_assert (gnutls_chain);
+ g_assert (gnutls_chain_length);
+
+ for (*gnutls_chain_length = 0, cert = G_TLS_CERTIFICATE (chain);
+ cert; cert = g_tls_certificate_get_issuer (cert))
+ ++(*gnutls_chain_length);
+
+ *gnutls_chain = g_new0 (gnutls_x509_crt_t, *gnutls_chain_length);
+
+ for (i = 0, cert = G_TLS_CERTIFICATE (chain);
+ cert; cert = g_tls_certificate_get_issuer (cert), ++i)
+ (*gnutls_chain)[i] = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (cert));
+
+ g_assert (i == *gnutls_chain_length);
+}
+
+static GTlsCertificateFlags
+g_tls_database_gnutls_verify_chain (GTlsDatabase *database,
+ GTlsCertificate *chain,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GTlsInteraction *interaction,
+ GTlsDatabaseVerifyFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsDatabaseGnutls *self;
+ GTlsCertificateFlags result;
+ GError *err = NULL;
+ GTlsCertificateGnutls *anchor;
+ guint gnutls_result;
+ gnutls_x509_crt_t *certs, *anchors;
+ guint certs_length, anchors_length;
+ gint status, gerr;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (chain),
+ G_TLS_CERTIFICATE_GENERIC_ERROR);
+
+ self = G_TLS_DATABASE_GNUTLS (database);
+ anchor = NULL;
+
+ status = build_certificate_chain (self, G_TLS_CERTIFICATE_GNUTLS (chain), purpose,
+ identity, interaction, flags, cancellable, &anchor, &err);
+ if (status == STATUS_FAILURE)
+ {
+ g_propagate_error (error, err);
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ /*
+ * A pinned certificate is verified on its own, without any further
+ * verification.
+ */
+ if (status == STATUS_PINNED)
+ return 0;
+
+ convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (chain),
+ &certs, &certs_length);
+
+ if (anchor)
+ {
+ g_assert (g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (anchor)) == NULL);
+ convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (anchor),
+ &anchors, &anchors_length);
+ }
+ else
+ {
+ anchors = NULL;
+ anchors_length = 0;
+ }
+
+ gerr = gnutls_x509_crt_list_verify (certs, certs_length,
+ anchors, anchors_length,
+ NULL, 0, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
+ &gnutls_result);
+
+ g_free (certs);
+ g_free (anchors);
+
+ if (gerr != 0)
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+ result = g_tls_certificate_gnutls_convert_flags (gnutls_result);
+
+ /*
+ * We have to check these ourselves since gnutls_x509_crt_list_verify
+ * won't bother if it gets an UNKNOWN_CA.
+ */
+ result |= double_check_before_after_dates (G_TLS_CERTIFICATE_GNUTLS (chain));
+
+ if (identity)
+ result |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (chain),
+ identity);
+
+ return result;
+}
+
+static void
+g_tls_database_gnutls_class_init (GTlsDatabaseGnutlsClass *klass)
+{
+ GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
+ database_class->verify_chain = g_tls_database_gnutls_verify_chain;
+}
+
+gboolean
+g_tls_database_gnutls_lookup_assertion (GTlsDatabaseGnutls *self,
+ GTlsCertificateGnutls *certificate,
+ GTlsDatabaseGnutlsAssertion assertion,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_TLS_DATABASE_GNUTLS (self), FALSE);
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (certificate), FALSE);
+ g_return_val_if_fail (purpose, FALSE);
+ g_return_val_if_fail (!identity || G_IS_SOCKET_CONNECTABLE (identity), FALSE);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
+ g_return_val_if_fail (G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->lookup_assertion, FALSE);
+ return G_TLS_DATABASE_GNUTLS_GET_CLASS (self)->lookup_assertion (self,
+ certificate,
+ assertion,
+ purpose,
+ identity,
+ cancellable,
+ error);
+}
--- /dev/null
+/* GIO - GLib Certificate, Output and Gnutlsing Library
+ *
+ * Copyright © 2010 Collabora, Ltd.
+ *
+ * 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.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#ifndef __G_TLS_DATABASE_GNUTLS_H__
+#define __G_TLS_DATABASE_GNUTLS_H__
+
+#include <gio/gio.h>
+
+#include "gtlscertificate-gnutls.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE = 1,
+ G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE = 2,
+} GTlsDatabaseGnutlsAssertion;
+
+#define G_TYPE_TLS_DATABASE_GNUTLS (g_tls_database_gnutls_get_type ())
+#define G_TLS_DATABASE_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_DATABASE_GNUTLS, GTlsDatabaseGnutls))
+#define G_TLS_DATABASE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_DATABASE_GNUTLS, GTlsDatabaseGnutlsClass))
+#define G_IS_TLS_DATABASE_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_DATABASE_GNUTLS))
+#define G_IS_TLS_DATABASE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_DATABASE_GNUTLS))
+#define G_TLS_DATABASE_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_DATABASE_GNUTLS, GTlsDatabaseGnutlsClass))
+
+typedef struct _GTlsDatabaseGnutlsPrivate GTlsDatabaseGnutlsPrivate;
+typedef struct _GTlsDatabaseGnutlsClass GTlsDatabaseGnutlsClass;
+typedef struct _GTlsDatabaseGnutls GTlsDatabaseGnutls;
+
+struct _GTlsDatabaseGnutlsClass
+{
+ GTlsDatabaseClass parent_class;
+
+ gboolean (*lookup_assertion) (GTlsDatabaseGnutls *self,
+ GTlsCertificateGnutls *certificate,
+ GTlsDatabaseGnutlsAssertion assertion,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GCancellable *cancellable,
+ GError **error);
+};
+
+struct _GTlsDatabaseGnutls
+{
+ GTlsDatabase parent_instance;
+ GTlsDatabaseGnutlsPrivate *priv;
+};
+
+GType g_tls_database_gnutls_get_type (void) G_GNUC_CONST;
+
+gboolean g_tls_database_gnutls_lookup_assertion (GTlsDatabaseGnutls *self,
+ GTlsCertificateGnutls *certificate,
+ GTlsDatabaseGnutlsAssertion assertion,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __G_TLS_DATABASE_GNUTLS_H___ */
--- /dev/null
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 Collabora, Ltd
+ *
+ * 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.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+
+#include "gtlsfiledatabase-gnutls.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <gnutls/x509.h>
+
+static void g_tls_file_database_gnutls_file_database_interface_init (GTlsFileDatabaseInterface *iface);
+
+static void g_tls_file_database_gnutls_initable_interface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsFileDatabaseGnutls, g_tls_file_database_gnutls, G_TYPE_TLS_DATABASE_GNUTLS,
+ G_IMPLEMENT_INTERFACE (G_TYPE_TLS_FILE_DATABASE,
+ g_tls_file_database_gnutls_file_database_interface_init);
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_tls_file_database_gnutls_initable_interface_init);
+);
+
+enum
+{
+ PROP_0,
+ PROP_ANCHORS,
+};
+
+struct _GTlsFileDatabaseGnutlsPrivate
+{
+ /* read-only after construct */
+ gchar *anchor_filename;
+
+ /* protected by mutex */
+ GMutex *mutex;
+
+ /*
+ * These are hash tables of GByteArray -> GPtrArray<GByteArray>. The values of
+ * the ptr array are full DER encoded certificate values. The keys are byte
+ * arrays containing either subject DNs, issuer DNs, or full DER encoded certs
+ */
+ GHashTable *subjects;
+ GHashTable *issuers;
+
+ /*
+ * This is a table of GByteArray -> GByteArray. The values and keys are
+ * DER encoded certificate values.
+ */
+ GHashTable *complete;
+
+ /*
+ * This is a table of gchar * -> GPtrArray<GByteArray>. The values of
+ * the ptr array are full DER encoded certificate values. The keys are the
+ * string handles. This array is populated on demand.
+ */
+ GHashTable *handles;
+};
+
+static guint
+byte_array_hash (gconstpointer v)
+{
+ const GByteArray *array = v;
+ const signed char *p;
+ guint32 h = 0;
+ gsize i;
+
+ g_assert (array);
+ g_assert (array->data);
+ p = (signed char*)array->data;
+
+ /* 31 bit hash function */
+ for (i = 0; i < array->len; ++i, ++p)
+ h = (h << 5) - h + *p;
+
+ return h;
+}
+
+static gboolean
+byte_array_equal (gconstpointer v1, gconstpointer v2)
+{
+ const GByteArray *array1 = v1;
+ const GByteArray *array2 = v2;
+
+ if (array1 == array2)
+ return TRUE;
+ if (!array1 || !array2)
+ return FALSE;
+
+ if (array1->len != array2->len)
+ return FALSE;
+
+ if (array1->data == array2->data)
+ return TRUE;
+ if (!array1->data || !array2->data)
+ return FALSE;
+
+ return (memcmp (array1->data, array2->data, array1->len) == 0) ? TRUE : FALSE;
+}
+
+static GHashTable *
+multi_byte_array_hash_new (void)
+{
+ return g_hash_table_new_full (byte_array_hash, byte_array_equal,
+ (GDestroyNotify)g_byte_array_unref,
+ (GDestroyNotify)g_ptr_array_unref);
+}
+
+static void
+multi_byte_array_hash_insert (GHashTable *table, GByteArray *key, GByteArray *value)
+{
+ GPtrArray *multi;
+
+ multi = g_hash_table_lookup (table, key);
+ if (multi == NULL)
+ {
+ multi = g_ptr_array_new_with_free_func ((GDestroyNotify)g_byte_array_unref);
+ g_hash_table_insert (table, g_byte_array_ref (key), multi);
+ }
+ g_ptr_array_add (multi, g_byte_array_ref (value));
+}
+
+static GByteArray *
+multi_byte_array_hash_lookup_one (GHashTable *table, GByteArray *key)
+{
+ GPtrArray *multi;
+
+ multi = g_hash_table_lookup (table, key);
+ if (multi == NULL)
+ return NULL;
+
+ g_assert (multi->len > 0);
+ return multi->pdata[0];
+}
+
+static GPtrArray *
+multi_byte_array_hash_lookup_all (GHashTable *table, GByteArray *key)
+{
+ return g_hash_table_lookup (table, key);
+}
+
+static gchar *
+create_handle_for_certificate (const gchar *filename,
+ GByteArray *der)
+{
+ gchar *bookmark;
+ gchar *uri_part;
+ gchar *uri;
+
+ /*
+ * Here we create a URI that looks like:
+ * file:///etc/ssl/certs/ca-certificates.crt#11b2641821252596420e468c275771f5e51022c121a17bd7a89a2f37b6336c8f
+ */
+
+ uri_part = g_filename_to_uri (filename, NULL, NULL);
+ if (!uri_part)
+ return NULL;
+
+ bookmark = g_compute_checksum_for_data (G_CHECKSUM_SHA256,
+ der->data, der->len);
+ uri = g_strconcat (uri_part, "#", bookmark, NULL);
+
+ g_free (bookmark);
+ g_free (uri_part);
+
+ return uri;
+}
+
+static GHashTable *
+create_handles_array_unlocked (const gchar *filename,
+ GHashTable *complete)
+{
+ GHashTable *handles;
+ GHashTableIter iter;
+ GByteArray *der;
+ gchar *handle;
+
+ handles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify)g_byte_array_unref);
+
+ g_hash_table_iter_init (&iter, complete);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&der))
+ {
+ handle = create_handle_for_certificate (filename, der);
+ if (handle != NULL)
+ g_hash_table_insert (handles, handle, g_byte_array_ref (der));
+ }
+
+ return handles;
+}
+
+static gboolean
+load_anchor_file (const gchar *filename,
+ GHashTable *subjects,
+ GHashTable *issuers,
+ GHashTable *complete,
+ GError **error)
+{
+ GList *list, *l;
+ gnutls_x509_crt_t cert;
+ gnutls_datum_t dn;
+ GByteArray *der;
+ GByteArray *subject;
+ GByteArray *issuer;
+ gint gerr;
+
+ g_assert (error);
+
+ list = g_tls_certificate_list_new_from_file (filename, error);
+ if (*error)
+ return FALSE;
+
+
+ for (l = list; l; l = g_list_next (l))
+ {
+ cert = g_tls_certificate_gnutls_get_cert (l->data);
+ gerr = gnutls_x509_crt_get_raw_dn (cert, &dn);
+ if (gerr < 0)
+ {
+ g_warning ("failed to get subject of anchor certificate: %s",
+ gnutls_strerror (gerr));
+ continue;
+ }
+
+ subject = g_byte_array_new ();
+ g_byte_array_append (subject, dn.data, dn.size);
+ gnutls_free (dn.data);
+
+ gerr = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn);
+ if (gerr < 0)
+ {
+ g_warning ("failed to get subject of anchor certificate: %s",
+ gnutls_strerror (gerr));
+ continue;
+ }
+
+ issuer = g_byte_array_new ();
+ g_byte_array_append (issuer, dn.data, dn.size);
+ gnutls_free (dn.data);
+
+ /* Dig out the full value of this certificate's DER encoding */
+ der = NULL;
+ g_object_get (l->data, "certificate", &der, NULL);
+ g_return_val_if_fail (der, FALSE);
+
+ /* Three different ways of looking up same certificate */
+ multi_byte_array_hash_insert (subjects, subject, der);
+ multi_byte_array_hash_insert (issuers, issuer, der);
+
+ g_hash_table_insert (complete, g_byte_array_ref (der),
+ g_byte_array_ref (der));
+
+ g_byte_array_unref (der);
+ g_byte_array_unref (subject);
+ g_byte_array_unref (issuer);
+ }
+
+ return TRUE;
+}
+
+
+
+static void
+g_tls_file_database_gnutls_finalize (GObject *object)
+{
+ GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
+
+ if (self->priv->subjects)
+ g_hash_table_destroy (self->priv->subjects);
+ self->priv->subjects = NULL;
+
+ if (self->priv->issuers)
+ g_hash_table_destroy (self->priv->issuers);
+ self->priv->issuers = NULL;
+
+ if (self->priv->complete)
+ g_hash_table_destroy (self->priv->complete);
+ self->priv->complete = NULL;
+
+ if (self->priv->handles)
+ g_hash_table_destroy (self->priv->handles);
+ self->priv->handles = NULL;
+
+ g_free (self->priv->anchor_filename);
+ self->priv->anchor_filename = NULL;
+
+ g_mutex_free (self->priv->mutex);
+
+ G_OBJECT_CLASS (g_tls_file_database_gnutls_parent_class)->finalize (object);
+}
+
+static void
+g_tls_file_database_gnutls_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
+
+ switch (prop_id)
+ {
+ case PROP_ANCHORS:
+ g_value_set_string (value, self->priv->anchor_filename);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_file_database_gnutls_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (object);
+ gchar *anchor_path;
+
+ switch (prop_id)
+ {
+ case PROP_ANCHORS:
+ anchor_path = g_value_dup_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
+ {
+ self->priv->anchor_filename = anchor_path;
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_file_database_gnutls_init (GTlsFileDatabaseGnutls *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ G_TYPE_TLS_FILE_DATABASE_GNUTLS,
+ GTlsFileDatabaseGnutlsPrivate);
+ self->priv->mutex = g_mutex_new ();
+}
+
+static gchar*
+g_tls_file_database_gnutls_create_certificate_handle (GTlsDatabase *database,
+ GTlsCertificate *certificate)
+{
+ GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+ GByteArray *der;
+ gboolean contains;
+ gchar *handle = NULL;
+
+ g_object_get (certificate, "certificate", &der, NULL);
+ g_return_val_if_fail (der, FALSE);
+
+ g_mutex_lock (self->priv->mutex);
+
+ /* At the same time look up whether this certificate is in list */
+ contains = g_hash_table_lookup (self->priv->complete, der) ? TRUE : FALSE;
+
+ g_mutex_unlock (self->priv->mutex);
+
+ /* Certificate is in the database */
+ if (contains)
+ handle = create_handle_for_certificate (self->priv->anchor_filename, der);
+
+ g_byte_array_unref (der);
+ return handle;
+}
+
+static GTlsCertificate*
+g_tls_file_database_gnutls_lookup_certificate_for_handle (GTlsDatabase *database,
+ const gchar *handle,
+ GTlsInteraction *interaction,
+ GTlsDatabaseLookupFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+ GByteArray *der;
+ gnutls_datum_t datum;
+
+ if (!handle)
+ return NULL;
+
+ g_mutex_lock (self->priv->mutex);
+
+ /* Create the handles table if not already done */
+ if (!self->priv->handles)
+ self->priv->handles = create_handles_array_unlocked (self->priv->anchor_filename,
+ self->priv->complete);
+
+ der = g_hash_table_lookup (self->priv->handles, handle);
+
+ g_mutex_unlock (self->priv->mutex);
+
+ if (der == NULL)
+ return NULL;
+
+ datum.data = der->data;
+ datum.size = der->len;
+
+ return g_tls_certificate_gnutls_new (&datum, NULL);
+}
+
+static gboolean
+g_tls_file_database_gnutls_lookup_assertion (GTlsDatabaseGnutls *database,
+ GTlsCertificateGnutls *certificate,
+ GTlsDatabaseGnutlsAssertion assertion,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+ GByteArray *der = NULL;
+ gboolean contains;
+
+ /* We only have anchored certificate assertions here */
+ if (assertion != G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE)
+ return FALSE;
+
+ /*
+ * TODO: We should be parsing any Extended Key Usage attributes and
+ * comparing them to the purpose.
+ */
+
+ g_object_get (certificate, "certificate", &der, NULL);
+ g_return_val_if_fail (der, FALSE);
+
+ g_mutex_lock (self->priv->mutex);
+ contains = g_hash_table_lookup (self->priv->complete, der) ? TRUE : FALSE;
+ g_mutex_unlock (self->priv->mutex);
+
+ g_byte_array_unref (der);
+
+ /* All certificates in our file are anchored certificates */
+ return contains;
+}
+
+static GTlsCertificate*
+g_tls_file_database_gnutls_lookup_certificate_issuer (GTlsDatabase *database,
+ GTlsCertificate *certificate,
+ GTlsInteraction *interaction,
+ GTlsDatabaseLookupFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+ gnutls_datum_t dn = { NULL, 0 };
+ GByteArray *subject, *der;
+ gnutls_datum_t datum;
+ GTlsCertificate *issuer = NULL;
+ gnutls_x509_crt_t cert;
+ int gerr;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (certificate), NULL);
+
+ if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
+ return NULL;
+
+ /* Dig out the issuer of this certificate */
+ cert = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (certificate));
+ gerr = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn);
+ if (gerr < 0)
+ {
+ g_warning ("failed to get issuer of certificate: %s", gnutls_strerror (gerr));
+ return NULL;
+ }
+
+ subject = g_byte_array_new ();
+ g_byte_array_append (subject, dn.data, dn.size);
+ gnutls_free (dn.data);
+
+ /* Find the full DER value of the certificate */
+ g_mutex_lock (self->priv->mutex);
+ der = multi_byte_array_hash_lookup_one (self->priv->subjects, subject);
+ g_mutex_unlock (self->priv->mutex);
+
+ g_byte_array_unref (subject);
+
+ if (der != NULL)
+ {
+ datum.data = der->data;
+ datum.size = der->len;
+ issuer = g_tls_certificate_gnutls_new (&datum, NULL);
+ }
+
+ return issuer;
+}
+
+static GList*
+g_tls_file_database_gnutls_lookup_certificates_issued_by (GTlsDatabase *database,
+ GByteArray *issuer_raw_dn,
+ GTlsInteraction *interaction,
+ GTlsDatabaseLookupFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (database);
+ GByteArray *der;
+ gnutls_datum_t datum;
+ GList *issued = NULL;
+ GPtrArray *ders;
+ guint i;
+
+ /* We don't have any private keys here */
+ if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
+ return NULL;
+
+ /* Find the full DER value of the certificate */
+ g_mutex_lock (self->priv->mutex);
+ ders = multi_byte_array_hash_lookup_all (self->priv->issuers, issuer_raw_dn);
+ g_mutex_unlock (self->priv->mutex);
+
+ for (i = 0; ders && i < ders->len; i++)
+ {
+ der = ders->pdata[i];
+ datum.data = der->data;
+ datum.size = der->len;
+ issued = g_list_prepend (issued, g_tls_certificate_gnutls_new (&datum, NULL));
+ }
+
+ return issued;
+}
+
+static void
+g_tls_file_database_gnutls_class_init (GTlsFileDatabaseGnutlsClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
+ GTlsDatabaseGnutlsClass *gnutls_class = G_TLS_DATABASE_GNUTLS_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GTlsFileDatabaseGnutlsPrivate));
+
+ gobject_class->get_property = g_tls_file_database_gnutls_get_property;
+ gobject_class->set_property = g_tls_file_database_gnutls_set_property;
+ gobject_class->finalize = g_tls_file_database_gnutls_finalize;
+
+ database_class->create_certificate_handle = g_tls_file_database_gnutls_create_certificate_handle;
+ database_class->lookup_certificate_for_handle = g_tls_file_database_gnutls_lookup_certificate_for_handle;
+ database_class->lookup_certificate_issuer = g_tls_file_database_gnutls_lookup_certificate_issuer;
+ database_class->lookup_certificates_issued_by = g_tls_file_database_gnutls_lookup_certificates_issued_by;
+ gnutls_class->lookup_assertion = g_tls_file_database_gnutls_lookup_assertion;
+
+ g_object_class_override_property (gobject_class, PROP_ANCHORS, "anchors");
+}
+
+static void
+g_tls_file_database_gnutls_file_database_interface_init (GTlsFileDatabaseInterface *iface)
+{
+
+}
+
+static gboolean
+g_tls_file_database_gnutls_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsFileDatabaseGnutls *self = G_TLS_FILE_DATABASE_GNUTLS (initable);
+ GHashTable *subjects, *issuers, *complete;
+ gboolean result;
+
+ subjects = multi_byte_array_hash_new ();
+ issuers = multi_byte_array_hash_new ();
+
+ complete = g_hash_table_new_full (byte_array_hash, byte_array_equal,
+ (GDestroyNotify)g_byte_array_unref,
+ (GDestroyNotify)g_byte_array_unref);
+
+ result = load_anchor_file (self->priv->anchor_filename, subjects, issuers,
+ complete, error);
+
+ if (result)
+ {
+ g_mutex_lock (self->priv->mutex);
+ if (!self->priv->subjects)
+ {
+ self->priv->subjects = subjects;
+ subjects = NULL;
+ }
+ if (!self->priv->issuers)
+ {
+ self->priv->issuers = issuers;
+ issuers = NULL;
+ }
+ if (!self->priv->complete)
+ {
+ self->priv->complete = complete;
+ complete = NULL;
+ }
+ g_mutex_unlock (self->priv->mutex);
+ }
+
+ if (subjects != NULL)
+ g_hash_table_unref (subjects);
+ if (issuers != NULL)
+ g_hash_table_unref (issuers);
+ if (complete != NULL)
+ g_hash_table_unref (complete);
+ return result;
+}
+
+static void
+g_tls_file_database_gnutls_initable_interface_init (GInitableIface *iface)
+{
+ iface->init = g_tls_file_database_gnutls_initable_init;
+}
--- /dev/null
+/* GIO - GLib Certificate, Output and Gnutlsing Library
+ *
+ * Copyright © 2010 Collabora, Ltd.
+ *
+ * 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.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#ifndef __G_TLS_FILE_DATABASE_GNUTLS_H__
+#define __G_TLS_FILE_DATABASE_GNUTLS_H__
+
+#include <gio/gio.h>
+
+#include "gtlsdatabase-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_FILE_DATABASE_GNUTLS (g_tls_file_database_gnutls_get_type ())
+#define G_TLS_FILE_DATABASE_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_FILE_DATABASE_GNUTLS, GTlsFileDatabaseGnutls))
+#define G_TLS_FILE_DATABASE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_FILE_DATABASE_GNUTLS, GTlsFileDatabaseGnutlsClass))
+#define G_IS_TLS_FILE_DATABASE_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_FILE_DATABASE_GNUTLS))
+#define G_IS_TLS_FILE_DATABASE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_FILE_DATABASE_GNUTLS))
+#define G_TLS_FILE_DATABASE_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_FILE_DATABASE_GNUTLS, GTlsFileDatabaseGnutlsClass))
+
+typedef struct _GTlsFileDatabaseGnutlsPrivate GTlsFileDatabaseGnutlsPrivate;
+typedef struct _GTlsFileDatabaseGnutlsClass GTlsFileDatabaseGnutlsClass;
+typedef struct _GTlsFileDatabaseGnutls GTlsFileDatabaseGnutls;
+
+struct _GTlsFileDatabaseGnutlsClass
+{
+ GTlsDatabaseGnutlsClass parent_class;
+};
+
+struct _GTlsFileDatabaseGnutls
+{
+ GTlsDatabaseGnutls parent_instance;
+ GTlsFileDatabaseGnutlsPrivate *priv;
+};
+
+GType g_tls_file_database_gnutls_get_type (void) G_GNUC_CONST;
+
+GTlsDatabase* g_tls_file_database_gnutls_new (const gchar *anchor_file);
+
+G_END_DECLS
+
+#endif /* __G_TLS_FILE_DATABASE_GNUTLS_H___ */
GTlsCertificate *peer_certificate,
GTlsCertificateFlags *errors)
{
+ GTlsDatabase *database;
+ GError *error = NULL;
+
+ database = g_tls_connection_get_database (G_TLS_CONNECTION (gnutls));
+ if (database == NULL)
+ {
+ *errors |= G_TLS_CERTIFICATE_UNKNOWN_CA;
+ *errors |= g_tls_certificate_verify (peer_certificate, NULL, NULL);
+ }
+ else
+ {
+ *errors |= g_tls_database_verify_chain (database, peer_certificate,
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_CLIENT, NULL,
+ g_tls_connection_get_interaction (G_TLS_CONNECTION (gnutls)),
+ G_TLS_DATABASE_VERIFY_NONE,
+ NULL, &error);
+ if (error)
+ {
+ g_warning ("failure verifying certificate chain: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
return g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls),
- peer_certificate, *errors);
+ peer_certificate, *errors);
}
static void
--- /dev/null
+NULL =
+
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES = \
+ -g \
+ $(GLIB_CFLAGS) \
+ -I$(top_srcdir)/tls \
+ -DSRCDIR=\""$(srcdir)"\"
+
+noinst_PROGRAMS = $(TEST_PROGS)
+
+LDADD = \
+ $(GLIB_LIBS)
+
+TEST_PROGS = \
+ tls \
+ $(NULL)
+
+EXTRA_DIST = \
+ files \
+ $(NULL)
+
+DISTCLEANFILES = \
+ $(NULL)
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDxjCCAy+gAwIBAgIJAOpd4Em2fjp3MA0GCSqGSIb3DQEBBQUAMIGGMRMwEQYK
+CZImiZPyLGQBGRYDQ09NMRcwFQYKCZImiZPyLGQBGRYHRVhBTVBMRTEeMBwGA1UE
+CxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRcwFQYDVQQDEw5jYS5leGFtcGxlLmNv
+bTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhcNMDcxMjIwMTc1NjA2
+WhcNMzUwNTA4MTc1NjA2WjCBhjETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmS
+JomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhvcml0
+eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkqhkiG9w0BCQEWDmNhQGV4
+YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD5OjHuXXN2LG3s
+FHISaZZ6L1RSYgRdTenu1nvqkMn/xvzOz385oede1z/7f6BoXyM0kNWCf4SOXtXr
+EIGmQoeURhFfLCnoK8NHfNcel3IPyMPhdJUMJlc3gfpWm+QxjkyqVyMhyYxC9Pmg
+QC7zx4ZKcQrL3zVGYtg8wxmaKY2HwQIDAQABo4IBODCCATQwHQYDVR0OBBYEFNSE
+nYhMCPaaFynFeQ2R5y25+AcFMIG7BgNVHSMEgbMwgbCAFNSEnYhMCPaaFynFeQ2R
+5y25+AcFoYGMpIGJMIGGMRMwEQYKCZImiZPyLGQBGRYDQ09NMRcwFQYKCZImiZPy
+LGQBGRYHRVhBTVBMRTEeMBwGA1UECxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRcw
+FQYDVQQDEw5jYS5leGFtcGxlLmNvbTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBs
+ZS5jb22CCQDqXeBJtn46dzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAZBgNVHREEEjAQgQ5jYUBleGFtcGxlLmNvbTAZBgNVHRIEEjAQgQ5jYUBleGFt
+cGxlLmNvbTANBgkqhkiG9w0BAQUFAAOBgQA6xjU2aPgMOh2yyz2KCb6d5gNNvfr4
+pLGpZWilbRkA36OOG43zxeRZoumh1ybyOvhm73cMvNihDUyOf7vQe75Qtp5koGPS
+V3mSruhsRGvOZxcV+SJnBj1exKyH3mdaZA74Xg4y5qkUkywPqnP5Y+E6UMJM7Nmw
+kHk2bKJC5vjxoA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
+IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
+IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
+Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
+BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
+MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
+ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
+8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
+zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
+fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
+w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
+G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
+epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
+laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
+QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
+fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
+YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
+ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
+gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
+MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
+IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
+dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
+czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
+dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
+aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
+AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
+b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
+ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
+nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
+18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
+gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
+Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
+sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
+SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
+CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
+GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
+zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
+omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGCDCCA/CgAwIBAgIBATANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
+IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
+IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
+Y2FjZXJ0Lm9yZzAeFw0wNTEwMTQwNzM2NTVaFw0zMzAzMjgwNzM2NTVaMFQxFDAS
+BgNVBAoTC0NBY2VydCBJbmMuMR4wHAYDVQQLExVodHRwOi8vd3d3LkNBY2VydC5v
+cmcxHDAaBgNVBAMTE0NBY2VydCBDbGFzcyAzIFJvb3QwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCrSTURSHzSJn5TlM9Dqd0o10Iqi/OHeBlYfA+e2ol9
+4fvrcpANdKGWZKufoCSZc9riVXbHF3v1BKxGuMO+f2SNEGwk82GcwPKQ+lHm9WkB
+Y8MPVuJKQs/iRIwlKKjFeQl9RrmK8+nzNCkIReQcn8uUBByBqBSzmGXEQ+xOgo0J
+0b2qW42S0OzekMV/CsLj6+YxWl50PpczWejDAz1gM7/30W9HxM3uYoNSbi4ImqTZ
+FRiRpoWSR7CuSOtttyHshRpocjWr//AQXcD0lKdq1TuSfkyQBX6TwSyLpI5idBVx
+bgtxA+qvFTia1NIFcm+M+SvrWnIl+TlG43IbPgTDZCciECqKT1inA62+tC4T7V2q
+SNfVfdQqe1z6RgRQ5MwOQluM7dvyz/yWk+DbETZUYjQ4jwxgmzuXVjit89Jbi6Bb
+6k6WuHzX1aCGcEDTkSm3ojyt9Yy7zxqSiuQ0e8DYbF/pCsLDpyCaWt8sXVJcukfV
+m+8kKHA4IC/VfynAskEDaJLM4JzMl0tF7zoQCqtwOpiVcK01seqFK6QcgCExqa5g
+eoAmSAC4AcCTY1UikTxW56/bOiXzjzFU6iaLgVn5odFTEcV7nQP2dBHgbbEsPyyG
+kZlxmqZ3izRg0RS0LKydr4wQ05/EavhvE/xzWfdmQnQeiuP43NJvmJzLR5iVQAX7
+6QIDAQABo4G/MIG8MA8GA1UdEwEB/wQFMAMBAf8wXQYIKwYBBQUHAQEEUTBPMCMG
+CCsGAQUFBzABhhdodHRwOi8vb2NzcC5DQWNlcnQub3JnLzAoBggrBgEFBQcwAoYc
+aHR0cDovL3d3dy5DQWNlcnQub3JnL2NhLmNydDBKBgNVHSAEQzBBMD8GCCsGAQQB
+gZBKMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuQ0FjZXJ0Lm9yZy9pbmRleC5w
+aHA/aWQ9MTAwDQYJKoZIhvcNAQEEBQADggIBAH8IiKHaGlBJ2on7oQhy84r3HsQ6
+tHlbIDCxRd7CXdNlafHCXVRUPIVfuXtCkcKZ/RtRm6tGpaEQU55tiKxzbiwzpvD0
+nuB1wT6IRanhZkP+VlrRekF490DaSjrxC1uluxYG5sLnk7mFTZdPsR44Q4Dvmw2M
+77inYACHV30eRBzLI++bPJmdr7UpHEV5FpZNJ23xHGzDwlVks7wU4vOkHx4y/CcV
+Bc/dLq4+gmF78CEQGPZE6lM5+dzQmiDgxrvgu1pPxJnIB721vaLbLmINQjRBvP+L
+ivVRIqqIMADisNS8vmW61QNXeZvo3MhN+FDtkaVSKKKs+zZYPumUK5FQhxvWXtaM
+zPcPEAxSTtAWYeXlCmy/F8dyRlecmPVsYGN6b165Ti/Iubm7aoW8mA3t+T6XhDSU
+rgCvoeXnkm5OvfPi2RSLXNLrAWygF6UtEOucekq9ve7O/e0iQKtwOIj1CodqwqsF
+YMlIBdpTwd5Ed2qz8zw87YC8pjhKKSRf/lk7myV6VmMAZLldpGJ9VzZPrYPvH5JT
+oI53V93lYRE9IwCQTDz6o2CTBKOvNfYOao9PSmCnhQVsRqGP9Md246FZV/dxssRu
+FFxtbUFm3xuTsdQAw+7Lzzw9IYCpX2Nl/N3gX6T0K/CFcUHUZyX7GrGXrtaZghNB
+0m6lG5kngOcLqagA
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBl
+cnNvbmFsIEJhc2ljIENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNp
+Y0B0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVow
+gcsxCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNV
+BAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAm
+BgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNV
+BAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBDQTEoMCYGCSqGSIb3DQEJARYZ
+cGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53dXLdjUmbllegeNTK
+P1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdKwPQIcOk8RHtQ
+fmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7G1sY0b8j
+kyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOB
+gQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7
+c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95
+B21P9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBl
+cnNvbmFsIEZyZWVtYWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1m
+cmVlbWFpbEB0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIz
+NTk1OVowgdExCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUx
+EjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRp
+bmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+JDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBDQTErMCkGCSqG
+SIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhhd3RlLmNvbTCBnzANBgkq
+hkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfYDFG26nKRsIRefS0N
+j3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5ErHzmj+hND3Ef
+QDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVquzgkCGqY
+x7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP
+MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgC
+neSa/RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr
+5PjRzneigQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBl
+cnNvbmFsIFByZW1pdW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXBy
+ZW1pdW1AdGhhd3RlLmNvbTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5
+NTlaMIHPMQswCQYDVQQGEwJaQTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIw
+EAYDVQQHEwlDYXBlIFRvd24xGjAYBgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5n
+MSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMSMw
+IQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJlbWl1bSBDQTEqMCgGCSqGSIb3
+DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0VsBd/eJxZRNkERbGw7
+7f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWIEt12TfIa/G8j
+Hnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYDZicRFTuq
+W/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIH
+b4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVx
+eTBhKXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1
+KzGJ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3Rl
+IFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1
+OVowgc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQ
+BgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcg
+Y2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+ITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3
+DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhI
+NTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPL
+lyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/qgeN
+9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
+AQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
+hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZ
+a4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcU
+Qg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3Rl
+IFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0
+ZS5jb20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkG
+A1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2Fw
+ZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQ
+VGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRz
+QHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I
+/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC
+6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCXL+eQbcAoQpnX
+TEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzARMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWD
+TSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
+QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdni
+TCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIDxjCCAy+gAwIBAgIJAOpd4Em2fjp3MA0GCSqGSIb3DQEBBQUAMIGGMRMwEQYK
+CZImiZPyLGQBGRYDQ09NMRcwFQYKCZImiZPyLGQBGRYHRVhBTVBMRTEeMBwGA1UE
+CxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRcwFQYDVQQDEw5jYS5leGFtcGxlLmNv
+bTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhcNMDcxMjIwMTc1NjA2
+WhcNMzUwNTA4MTc1NjA2WjCBhjETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmS
+JomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhvcml0
+eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkqhkiG9w0BCQEWDmNhQGV4
+YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD5OjHuXXN2LG3s
+FHISaZZ6L1RSYgRdTenu1nvqkMn/xvzOz385oede1z/7f6BoXyM0kNWCf4SOXtXr
+EIGmQoeURhFfLCnoK8NHfNcel3IPyMPhdJUMJlc3gfpWm+QxjkyqVyMhyYxC9Pmg
+QC7zx4ZKcQrL3zVGYtg8wxmaKY2HwQIDAQABo4IBODCCATQwHQYDVR0OBBYEFNSE
+nYhMCPaaFynFeQ2R5y25+AcFMIG7BgNVHSMEgbMwgbCAFNSEnYhMCPaaFynFeQ2R
+5y25+AcFoYGMpIGJMIGGMRMwEQYKCZImiZPyLGQBGRYDQ09NMRcwFQYKCZImiZPy
+LGQBGRYHRVhBTVBMRTEeMBwGA1UECxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRcw
+FQYDVQQDEw5jYS5leGFtcGxlLmNvbTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBs
+ZS5jb22CCQDqXeBJtn46dzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
+BjAZBgNVHREEEjAQgQ5jYUBleGFtcGxlLmNvbTAZBgNVHRIEEjAQgQ5jYUBleGFt
+cGxlLmNvbTANBgkqhkiG9w0BAQUFAAOBgQA6xjU2aPgMOh2yyz2KCb6d5gNNvfr4
+pLGpZWilbRkA36OOG43zxeRZoumh1ybyOvhm73cMvNihDUyOf7vQe75Qtp5koGPS
+V3mSruhsRGvOZxcV+SJnBj1exKyH3mdaZA74Xg4y5qkUkywPqnP5Y+E6UMJM7Nmw
+kHk2bKJC5vjxoA==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAkUCAQkwDQYJKoZIhvcNAQEFBQAwgYYxEzARBgoJkiaJk/IsZAEZFgND
+T00xFzAVBgoJkiaJk/IsZAEZFgdFWEFNUExFMR4wHAYDVQQLExVDZXJ0aWZpY2F0
+ZSBBdXRob3JpdHkxFzAVBgNVBAMTDmNhLmV4YW1wbGUuY29tMR0wGwYJKoZIhvcN
+AQkBFg5jYUBleGFtcGxlLmNvbTAeFw0xMTAxMTgwNjA0MTFaFw0yMTAxMTUwNjA0
+MTFaMGIxEzARBgoJkiaJk/IsZAEZFgNDT00xFzAVBgoJkiaJk/IsZAEZFgdFWEFN
+UExFMQ8wDQYDVQQDEwZDbGllbnQxITAfBgkqhkiG9w0BCQEWEmNsaWVudEBleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEgNDM/dg3t
+9DHNAPz/b87LSxEaKhoZ8AcNBym3LEOdCnXGEnKf0b9lkT5caXu5GAM84ahTCJ7n
+79RVNrqGKM7jbBdSX+ZUfkqJQPhOXD2+0niYQicH92nz78kxmjlbizvd3fM1BlO+
+C/++NWf2EAhORVAjvJrHNokJp3PTNRJ1WWteHeU9PwfGmWHKVc1IgvRFMH08604I
+ZzX5CcxIg/b56g27A7CBPh/KO/qKTDLFFNGc1T2asvY/P3+PeN6y+leFHStCTu7R
+Bi/l4hczZdnwq3BGT6mnjEN7wau2s7pA067SXimNOkYi5fgwspMHi8fJWmYyBypU
+mQBRzwfm77ECAwEAATANBgkqhkiG9w0BAQUFAAOBgQA3LuElj2QB9wQvmIxk2Jmb
+IPP2/WS8dwPoCv/N3+6nTx8yRsrILf4QsnEbbsxoYO5jW4r9Kt8m8B/M7YgnBDE9
+zlm7JbXKZf2isSm5TyT627Ymzxrzs5d+7o2eS7SN1DB6PyvRh2ye7EMbyEYD8ULi
+itDUkYkssNCVivYwVvJoMg==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAwSA0Mz92De30Mc0A/P9vzstLERoqGhnwBw0HKbcsQ50KdcYS
+cp/Rv2WRPlxpe7kYAzzhqFMInufv1FU2uoYozuNsF1Jf5lR+SolA+E5cPb7SeJhC
+Jwf3afPvyTGaOVuLO93d8zUGU74L/741Z/YQCE5FUCO8msc2iQmnc9M1EnVZa14d
+5T0/B8aZYcpVzUiC9EUwfTzrTghnNfkJzEiD9vnqDbsDsIE+H8o7+opMMsUU0ZzV
+PZqy9j8/f4943rL6V4UdK0JO7tEGL+XiFzNl2fCrcEZPqaeMQ3vBq7azukDTrtJe
+KY06RiLl+DCykweLx8laZjIHKlSZAFHPB+bvsQIDAQABAoIBAQCQUI1RYnHIdPFO
+qZ+8bvDQ+g8tR30ApjM8QZsBrDRyjg579bhhWVY2jSJdFFdqseTkvoDt9KZzgGQy
+Kj9MYOZru3xRbSfmiWsaLbiUFJJPPaIvpa+BVS2oSjX8BYn2pJbF9MRfclc5CsIS
+qMNl3XUbj8mx2hKdIpJ5EvLD1adKE4Se6peqSZAmEHONNCsrMrQ0GSQqV3viInJr
+tc3kp3HcPffSROWqmc6jAJ77Cs3ApgJavL5RGjx30Kd+dKVq4PXZ+IhWM8dOSput
+wcyxEosiP/W2g0rDgNW2mGOVOwa/D5SnOolicHifdV7idjwLAjkyYgvmBMNSsECj
+yKBkE0gxAoGBAN8iHMumyvriHuj9bSLZ1bcyYFz7jIwUxpHTT7VqN/j/Y1BoBIBy
+ZZLDGMa+ID/brpRHzJQAKSNtbFQ0S1HTSKcFud5OWE8Rp3pQJU+sdeO3pCMWAD1z
+Q4ggF07JjTSSnK+4fcXgEN9P2OdfXy7Rj3HFpSahql55Kp5udoUdzUVFAoGBAN2S
+krlcEuqsEYjqsCJw5pctIwPMvCM51JgirrdETwSGquMklSrobH0PHMlR67gsA/9I
+UGShT0LL4UWYpBn/4xLrLbua5aHIBfQQZp9K6jDZddWS+EFL5JkO/Up4/qM6fUbH
+CuweVv1gd6i2Ti35K60mgx6MqVunaB1k8Q9P3Pl9AoGALSVtxha9Qv21W1bLWh3R
+C/v5W1baHQ2nD6I9omsXYB3sLjydjI+Y1ZT70lptk/4S2JWeYuOVb0GYhYD/LFMf
+hAu4i642V+kuhaTpp7ExOR3S6/ZrngNQSp6TmLFXDKgNY9BkQkEPqN8y971oOMTV
+zSM8QxC6s9q4MM4Q1OYuvjECgYEAsO2V1AW95T45Ukd1FktpFlaomyQlJ0vKgyFO
+unEFV+vhETfpFTY7SzGCHxAXVh1vo62u5Gwayo/a9qQIhepa/IRnJGNv8luyxU1D
+ZPeBQjija0PMkPd1NvNNNuafDuBpoNbX1ev0MqeRZVsN2pAZXE5gbUiNA+8NqEsu
+Yre3EFECgYEA13rXE76zZgsefx+2spjqJDUWEmTDd1460xTtxCCgL9dy4rW5bgwo
+MvINphSUXOwSkn8Oja/IvpN28zSj9W/ci5wU52P5w4blkBmuj8UoCjP2FN1b1OBa
+86mkwVsCYUyyI2apuwrHP77yeb8jXZb+reqSns3hU+HyO/nUTVmnews=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAkUCAQowDQYJKoZIhvcNAQEFBQAwgYYxEzARBgoJkiaJk/IsZAEZFgND
+T00xFzAVBgoJkiaJk/IsZAEZFgdFWEFNUExFMR4wHAYDVQQLExVDZXJ0aWZpY2F0
+ZSBBdXRob3JpdHkxFzAVBgNVBAMTDmNhLmV4YW1wbGUuY29tMR0wGwYJKoZIhvcN
+AQkBFg5jYUBleGFtcGxlLmNvbTAeFw0yMDAxMTgxNzI3MDNaFw0yMTAxMTcxNzI3
+MDNaMGIxEzARBgoJkiaJk/IsZAEZFgNDT00xFzAVBgoJkiaJk/IsZAEZFgdFWEFN
+UExFMQ8wDQYDVQQDEwZDbGllbnQxITAfBgkqhkiG9w0BCQEWEmNsaWVudEBleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEgNDM/dg3t
+9DHNAPz/b87LSxEaKhoZ8AcNBym3LEOdCnXGEnKf0b9lkT5caXu5GAM84ahTCJ7n
+79RVNrqGKM7jbBdSX+ZUfkqJQPhOXD2+0niYQicH92nz78kxmjlbizvd3fM1BlO+
+C/++NWf2EAhORVAjvJrHNokJp3PTNRJ1WWteHeU9PwfGmWHKVc1IgvRFMH08604I
+ZzX5CcxIg/b56g27A7CBPh/KO/qKTDLFFNGc1T2asvY/P3+PeN6y+leFHStCTu7R
+Bi/l4hczZdnwq3BGT6mnjEN7wau2s7pA067SXimNOkYi5fgwspMHi8fJWmYyBypU
+mQBRzwfm77ECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBvt8v930fQtxR7f7Vcb1Hg
+irq1CtffsBqtKYupYg6IgloiRA6U5wdU0e6faA3Ppsmd4SmNKb9ZavIgnDBfx8MP
+1/IpsNOkg0366bP/zzkAhcXspo7PU8yZIqep//wT4TOFz04N8Lshqm8HUejShFdA
+fB8C0LX5Y/2219ZVMaaEbw==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAkUCAQswDQYJKoZIhvcNAQEFBQAwgYYxEzARBgoJkiaJk/IsZAEZFgND
+T00xFzAVBgoJkiaJk/IsZAEZFgdFWEFNUExFMR4wHAYDVQQLExVDZXJ0aWZpY2F0
+ZSBBdXRob3JpdHkxFzAVBgNVBAMTDmNhLmV4YW1wbGUuY29tMR0wGwYJKoZIhvcN
+AQkBFg5jYUBleGFtcGxlLmNvbTAeFw0wMDAxMTgxNzI3NDdaFw0wMTAxMTcxNzI3
+NDdaMGIxEzARBgoJkiaJk/IsZAEZFgNDT00xFzAVBgoJkiaJk/IsZAEZFgdFWEFN
+UExFMQ8wDQYDVQQDEwZDbGllbnQxITAfBgkqhkiG9w0BCQEWEmNsaWVudEBleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEgNDM/dg3t
+9DHNAPz/b87LSxEaKhoZ8AcNBym3LEOdCnXGEnKf0b9lkT5caXu5GAM84ahTCJ7n
+79RVNrqGKM7jbBdSX+ZUfkqJQPhOXD2+0niYQicH92nz78kxmjlbizvd3fM1BlO+
+C/++NWf2EAhORVAjvJrHNokJp3PTNRJ1WWteHeU9PwfGmWHKVc1IgvRFMH08604I
+ZzX5CcxIg/b56g27A7CBPh/KO/qKTDLFFNGc1T2asvY/P3+PeN6y+leFHStCTu7R
+Bi/l4hczZdnwq3BGT6mnjEN7wau2s7pA067SXimNOkYi5fgwspMHi8fJWmYyBypU
+mQBRzwfm77ECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBC3BOULAOkRFLKLajHIIB2
+VB0tHOFWuflP/LXso3ogGA8ItqbjacqjRHdTGK79etbxSTdi7k8owMVMPavJnBYk
+TraOkf/xxHo2zWy3XES1lniTUfGgKpjYNlALB6K6DJseZorSOmGA4KllL46MYwNu
+jsLO+5HkS/uNxlKo2l+xGw==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAkUCAQkwDQYJKoZIhvcNAQEFBQAwgYYxEzARBgoJkiaJk/IsZAEZFgND
+T00xFzAVBgoJkiaJk/IsZAEZFgdFWEFNUExFMR4wHAYDVQQLExVDZXJ0aWZpY2F0
+ZSBBdXRob3JpdHkxFzAVBgNVBAMTDmNhLmV4YW1wbGUuY29tMR0wGwYJKoZIhvcN
+AQkBFg5jYUBleGFtcGxlLmNvbTAeFw0xMTAxMTgwNjA0MTFaFw0yMTAxMTUwNjA0
+MTFaMGIxEzARBgoJkiaJk/IsZAEZFgNDT00xFzAVBgoJkiaJk/IsZAEZFgdFWEFN
+UExFMQ8wDQYDVQQDEwZDbGllbnQxITAfBgkqhkiG9w0BCQEWEmNsaWVudEBleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEgNDM/dg3t
+9DHNAPz/b87LSxEaKhoZ8AcNBym3LEOdCnXGEnKf0b9lkT5caXu5GAM84ahTCJ7n
+79RVNrqGKM7jbBdSX+ZUfkqJQPhOXD2+0niYQicH92nz78kxmjlbizvd3fM1BlO+
+C/++NWf2EAhORVAjvJrHNokJp3PTNRJ1WWteHeU9PwfGmWHKVc1IgvRFMH08604I
+ZzX5CcxIg/b56g27A7CBPh/KO/qKTDLFFNGc1T2asvY/P3+PeN6y+leFHStCTu7R
+Bi/l4hczZdnwq3BGT6mnjEN7wau2s7pA067SXimNOkYi5fgwspMHi8fJWmYyBypU
+mQBRzwfm77ECAwEAATANBgkqhkiG9w0BAQUFAAOBgQA3LuElj2QB9wQvmIxk2Jmb
+IPP2/WS8dwPoCv/N3+6nTx8yRsrILf4QsnEbbsxoYO5jW4r9Kt8m8B/M7YgnBDE9
+zlm7JbXKZf2isSm5TyT627Ymzxrzs5d+7o2eS7SN1DB6PyvRh2ye7EMbyEYD8ULi
+itDUkYkssNCVivYwVvJoMg==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICJjCCAY+gAwIBAgIBBzANBgkqhkiG9w0BAQUFADCBhjETMBEGCgmSJomT8ixk
+ARkWA0NPTTEXMBUGCgmSJomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRp
+ZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkq
+hkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tMB4XDTExMDExNzE5NDcxN1oXDTIxMDEx
+NDE5NDcxN1owSzETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixkARkW
+B0VYQU1QTEUxGzAZBgNVBAMTEnNlcnZlci5leGFtcGxlLmNvbTBcMA0GCSqGSIb3
+DQEBAQUAA0sAMEgCQQDYScTxk55XBmbDM9zzwO+grVySE4rudWuzH2PpObIonqbf
+hRoAalKVluG9jvbHI81eXxCdSObv1KBP1sbN5RzpAgMBAAGjIjAgMAkGA1UdEwQC
+MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAYx6fMqT1
+Gvo0jq88E8mc+bmp4LfXD4wJ7KxYeadQxt75HFRpj4FhFO3DOpVRFgzHlOEo3Fwk
+PZOKjvkT0cbcoEq5whLH25dHoQxGoVQgFyAP5s+7Vp5AlHh8Y/vAoXeEVyy/RCIH
+QkhUlAflfDMcrrYjsmwoOPSjhx6Mm/AopX4=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIBOgIBAAJBANhJxPGTnlcGZsMz3PPA76CtXJITiu51a7MfY+k5siiept+FGgBq
+UpWW4b2O9scjzV5fEJ1I5u/UoE/Wxs3lHOkCAwEAAQJAURSWd9lW6ljD/TlcAyS4
+sAtNQJWC55GtJiEGW0/9savXVHPRllN5IlcOJS/L//rD47UzwmGHcxxzsg3p+s51
+YQIhAPaiU+wbUL0cQrxqwIa/TEtNtrU3T21vNvQaFLSHh+uHAiEA4IBuYuPL+xz2
+nDwSB9UWklYV5fKhwqUA96qyMNxTMA8CIGvD6h+Un+bB3nctvgoitFeDEX6FOHN8
+0OpAKyPmxIEpAiEAiwNb1wYhN9QebG3R6GGtQV3m32VXftR3feSHxDOZm0cCIEpA
+7kMFeeQQZb1qYd1PMFSDCG+BkrWh/Fud+VvFAWxI
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIBiDCCATICCQDJ4QeFpYPYljANBgkqhkiG9w0BAQUFADBLMRMwEQYKCZImiZPy
+LGQBGRYDQ09NMRcwFQYKCZImiZPyLGQBGRYHRVhBTVBMRTEbMBkGA1UEAxMSc2Vy
+dmVyLmV4YW1wbGUuY29tMB4XDTExMDExOTAzMTYzOFoXDTIxMDExNjAzMTYzOFow
+SzETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixkARkWB0VYQU1QTEUx
+GzAZBgNVBAMTEnNlcnZlci5leGFtcGxlLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sA
+MEgCQQDYScTxk55XBmbDM9zzwO+grVySE4rudWuzH2PpObIonqbfhRoAalKVluG9
+jvbHI81eXxCdSObv1KBP1sbN5RzpAgMBAAEwDQYJKoZIhvcNAQEFBQADQQAagc2P
+/lCfDwT3max+D2M7++KMDfGqiO3gI+hMarf/jAaQpcKO/9G95AnNo4lTd6W6/7yj
+YYvUupv+0vi4CtQG
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIICJjCCAY+gAwIBAgIBBzANBgkqhkiG9w0BAQUFADCBhjETMBEGCgmSJomT8ixk
+ARkWA0NPTTEXMBUGCgmSJomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRp
+ZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkq
+hkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tMB4XDTExMDExNzE5NDcxN1oXDTIxMDEx
+NDE5NDcxN1owSzETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixkARkW
+B0VYQU1QTEUxGzAZBgNVBAMTEnNlcnZlci5leGFtcGxlLmNvbTBcMA0GCSqGSIb3
+DQEBAQUAA0sAMEgCQQDYScTxk55XBmbDM9zzwO+grVySE4rudWuzH2PpObIonqbf
+hRoAalKVluG9jvbHI81eXxCdSObv1KBP1sbN5RzpAgMBAAGjIjAgMAkGA1UdEwQC
+MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAYx6fMqT1
+Gvo0jq88E8mc+bmp4LfXD4wJ7KxYeadQxt75HFRpj4FhFO3DOpVRFgzHlOEo3Fwk
+PZOKjvkT0cbcoEq5whLH25dHoQxGoVQgFyAP5s+7Vp5AlHh8Y/vAoXeEVyy/RCIH
+QkhUlAflfDMcrrYjsmwoOPSjhx6Mm/AopX4=
+-----END CERTIFICATE-----
--- /dev/null
+/* GIO TLS tests
+ *
+ * Copyright (C) 2011 Collabora, Ltd.
+ *
+ * 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.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include <gio/gio.h>
+
+#include <sys/types.h>
+#include <string.h>
+
+static gchar *source_dir = NULL;
+
+/* -----------------------------------------------------------------------------
+ * CONNECTION AND DATABASE TESTS
+ */
+
+#define TEST_DATA "You win again, gravity!\n"
+#define TEST_DATA_LENGTH 24
+
+typedef struct {
+ GMainLoop *loop;
+ GSocketService *service;
+ GTlsDatabase *database;
+ GIOStream *server_connection;
+ GIOStream *client_connection;
+ GSocketConnectable *identity;
+ GSocketAddress *address;
+ GTlsAuthenticationMode auth_mode;
+ GTlsCertificateFlags accept_flags;
+} TestConnection;
+
+static void
+setup_connection (TestConnection *test, gconstpointer data)
+{
+ GInetAddress *inet;
+ guint16 port;
+
+ test->loop = g_main_loop_new (NULL, FALSE);
+
+ test->auth_mode = G_TLS_AUTHENTICATION_NONE;
+
+ /* This is where the server listens and the client connects */
+ port = g_random_int_range (50000, 65000);
+ inet = g_inet_address_new_from_string ("127.0.0.1");
+ test->address = G_SOCKET_ADDRESS (g_inet_socket_address_new (inet, port));
+ g_object_unref (inet);
+
+ /* The identity matches the server certificate */
+ test->identity = g_network_address_new ("server.example.com", port);
+}
+
+static void
+teardown_connection (TestConnection *test, gconstpointer data)
+{
+ if (test->service)
+ g_object_unref (test->service);
+
+ if (test->server_connection)
+ {
+ g_assert (G_IS_TLS_SERVER_CONNECTION (test->server_connection));
+ g_object_unref (test->server_connection);
+ g_assert (!G_IS_TLS_SERVER_CONNECTION (test->server_connection));
+ }
+
+ if (test->client_connection)
+ {
+ g_assert (G_IS_TLS_CLIENT_CONNECTION (test->client_connection));
+ g_object_unref (test->client_connection);
+ g_assert (!G_IS_TLS_SERVER_CONNECTION (test->client_connection));
+ }
+
+ if (test->database)
+ {
+ g_assert (G_IS_TLS_DATABASE (test->database));
+ g_object_unref (test->database);
+ g_assert (!G_IS_TLS_DATABASE (test->database));
+ }
+
+ g_object_unref (test->address);
+ g_object_unref (test->identity);
+ g_main_loop_unref (test->loop);
+}
+
+static gboolean
+on_accept_certificate (GTlsClientConnection *conn, GTlsCertificate *cert,
+ GTlsCertificateFlags errors, gpointer user_data)
+{
+ TestConnection *test = user_data;
+ return errors == test->accept_flags;
+}
+
+static void
+on_output_close_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ g_output_stream_close_finish (G_OUTPUT_STREAM (object), res, &error);
+ g_assert_no_error (error);
+}
+
+static void
+on_output_write_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ TestConnection *test = user_data;
+ GError *error = NULL;
+ g_output_stream_write_finish (G_OUTPUT_STREAM (object), res, &error);
+ g_assert_no_error (error);
+
+ g_output_stream_close_async (G_OUTPUT_STREAM (object), G_PRIORITY_DEFAULT, NULL,
+ on_output_close_finish, test);
+}
+
+static gboolean
+on_incoming_connection (GSocketService *service,
+ GSocketConnection *connection,
+ GObject *source_object,
+ gpointer user_data)
+{
+ TestConnection *test = user_data;
+ GOutputStream *stream;
+ GTlsCertificate *cert;
+ GError *error = NULL;
+ gchar *path;
+
+ path = g_build_filename (source_dir, "server-and-key.pem", NULL);
+ cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_free (path);
+
+ test->server_connection = g_tls_server_connection_new (G_IO_STREAM (connection),
+ cert, &error);
+ g_assert_no_error (error);
+ g_object_unref (cert);
+
+ g_object_set (test->server_connection, "authentication-mode", test->auth_mode, NULL);
+ g_signal_connect (test->server_connection, "accept-certificate",
+ G_CALLBACK (on_accept_certificate), test);
+
+ if (test->database)
+ g_tls_connection_set_database (G_TLS_CONNECTION (test->server_connection), test->database);
+
+ stream = g_io_stream_get_output_stream (test->server_connection);
+
+ g_output_stream_write_async (stream, TEST_DATA, TEST_DATA_LENGTH,
+ G_PRIORITY_DEFAULT, NULL,
+ on_output_write_finish, test);
+ return FALSE;
+}
+
+static void
+start_server_service (TestConnection *test, GTlsAuthenticationMode auth_mode)
+{
+ GError *error = NULL;
+
+ test->service = g_socket_service_new ();
+ g_socket_listener_add_address (G_SOCKET_LISTENER (test->service),
+ G_SOCKET_ADDRESS (test->address),
+ G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP,
+ NULL, NULL, &error);
+ g_assert_no_error (error);
+
+ test->auth_mode = auth_mode;
+ g_signal_connect (test->service, "incoming", G_CALLBACK (on_incoming_connection), test);
+}
+
+static GIOStream*
+start_server_and_connect_to_it (TestConnection *test, GTlsAuthenticationMode auth_mode)
+{
+ GSocketClient *client;
+ GError *error = NULL;
+ GSocketConnection *connection;
+
+ start_server_service (test, auth_mode);
+
+ client = g_socket_client_new ();
+ connection = g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+ NULL, &error);
+ g_assert_no_error (error);
+ g_object_unref (client);
+
+ return G_IO_STREAM (connection);
+}
+
+static void
+on_input_read_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ TestConnection *test = user_data;
+ GError *error = NULL;
+ gchar *line, *check;
+
+ line = g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (object), res,
+ NULL, &error);
+ g_assert_no_error (error);
+ g_assert (line);
+
+ check = g_strdup (TEST_DATA);
+ g_strstrip (check);
+ g_assert_cmpstr (line, ==, check);
+ g_free (check);
+ g_free (line);
+
+ g_main_loop_quit (test->loop);
+}
+
+static void
+read_test_data_async (TestConnection *test)
+{
+ GDataInputStream *stream;
+
+ stream = g_data_input_stream_new (g_io_stream_get_input_stream (test->client_connection));
+ g_assert (stream);
+
+ g_data_input_stream_read_line_async (stream, G_PRIORITY_DEFAULT, NULL,
+ on_input_read_finish, test);
+ g_object_unref (stream);
+}
+
+static void
+test_basic_connection (TestConnection *test,
+ gconstpointer data)
+{
+ GIOStream *connection;
+ GError *error = NULL;
+
+ connection = start_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+ test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+ g_assert_no_error (error);
+ g_object_unref (connection);
+
+ /* No validation at all in this test */
+ g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+ 0);
+
+ read_test_data_async (test);
+ g_main_loop_run (test->loop);
+}
+
+static void
+test_verified_connection (TestConnection *test,
+ gconstpointer data)
+{
+ GIOStream *connection;
+ GError *error = NULL;
+ gchar *path;
+
+ path = g_build_filename (source_dir, "ca-roots.pem", NULL);
+ test->database = g_tls_file_database_new (path, &error);
+ g_assert_no_error (error);
+ g_assert (test->database);
+ g_free (path);
+
+ connection = start_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+ test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+ g_assert_no_error (error);
+ g_assert (test->client_connection);
+
+ g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+ /* All validation in this test */
+ g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+ G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+ read_test_data_async (test);
+ g_main_loop_run (test->loop);
+}
+
+static void
+test_client_auth_connection (TestConnection *test,
+ gconstpointer data)
+{
+ GIOStream *connection;
+ GError *error = NULL;
+ GTlsCertificate *cert;
+ gchar *path;
+
+ path = g_build_filename (source_dir, "ca-roots.pem", NULL);
+ test->database = g_tls_file_database_new (path, &error);
+ g_assert_no_error (error);
+ g_assert (test->database);
+ g_free (path);
+
+ connection = start_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED);
+ test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+ g_assert_no_error (error);
+ g_assert (test->client_connection);
+
+ g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+ path = g_build_filename (source_dir, "client-and-key.pem", NULL);
+ cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_free (path);
+
+ g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert);
+ g_object_unref (cert);
+
+ /* All validation in this test */
+ g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+ G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+ read_test_data_async (test);
+ g_main_loop_run (test->loop);
+}
+
+static void
+test_connection_no_database (TestConnection *test,
+ gconstpointer data)
+{
+ GIOStream *connection;
+ GError *error = NULL;
+
+ connection = start_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+ test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+ g_assert_no_error (error);
+ g_assert (test->client_connection);
+
+ /* Overrides loading of the default database */
+ g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), NULL);
+
+ /* All validation in this test */
+ g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+ G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+ test->accept_flags = G_TLS_CERTIFICATE_UNKNOWN_CA;
+ g_signal_connect (test->client_connection, "accept-certificate",
+ G_CALLBACK (on_accept_certificate), test);
+
+ read_test_data_async (test);
+ g_main_loop_run (test->loop);
+}
+
+/* -----------------------------------------------------------------------------
+ * CERTIFICATE TESTS
+ */
+
+typedef struct {
+ gchar *pem;
+ gsize pem_length;
+ GByteArray *der;
+} TestCertificate;
+
+static void
+setup_certificate (TestCertificate *test, gconstpointer data)
+{
+ GError *error = NULL;
+ gchar *path;
+ gchar *contents;
+ gsize length;
+
+ path = g_build_filename (source_dir, "server.pem", NULL);
+ g_file_get_contents (path, &test->pem, &test->pem_length, &error);
+ g_assert_no_error (error);
+ g_free (path);
+
+ path = g_build_filename (source_dir, "server.der", NULL);
+ g_file_get_contents (path, &contents, &length, &error);
+ g_assert_no_error (error);
+ g_free (path);
+
+ test->der = g_byte_array_new ();
+ g_byte_array_append (test->der, (guint8*)contents, length);
+ g_free (contents);
+}
+
+static void
+teardown_certificate (TestCertificate *test, gconstpointer data)
+{
+ g_free (test->pem);
+ g_byte_array_free (test->der, TRUE);
+}
+
+static void
+test_create_destroy_certificate_pem (TestCertificate *test, gconstpointer data)
+{
+ GTlsCertificate *cert;
+ gchar *pem = NULL;
+ GError *error = NULL;
+
+ cert = g_tls_certificate_new_from_pem (test->pem, test->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->pem);
+ g_free (pem);
+
+ g_object_unref (cert);
+ g_assert (!G_IS_TLS_CERTIFICATE (cert));
+}
+
+static void
+test_create_destroy_certificate_der (TestCertificate *test, gconstpointer data)
+{
+ GTlsCertificate *cert;
+ GByteArray *der = NULL;
+ GError *error = NULL;
+ GTlsBackend *backend;
+
+ backend = g_tls_backend_get_default ();
+ cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+ NULL, &error,
+ "certificate", test->der,
+ NULL);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (cert));
+
+ g_object_get (cert, "certificate", &der, NULL);
+ g_assert (der);
+ g_assert_cmpuint (der->len, ==, test->der->len);
+ g_assert (memcmp (der->data, test->der->data, der->len) == 0);
+ g_byte_array_unref (der);
+
+ g_object_unref (cert);
+ g_assert (!G_IS_TLS_CERTIFICATE (cert));
+}
+
+static void
+test_create_certificate_with_issuer (TestCertificate *test,
+ gconstpointer data)
+{
+ GTlsCertificate *cert, *issuer, *check;
+ GError *error = NULL;
+ GTlsBackend *backend;
+ gchar *path;
+
+ path = g_build_filename (source_dir, "ca.pem", NULL);
+ issuer = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (issuer));
+ g_free (path);
+
+ backend = g_tls_backend_get_default ();
+ cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+ NULL, &error,
+ "certificate-pem", test->pem,
+ "issuer", issuer,
+ NULL);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (cert));
+
+ g_object_unref (issuer);
+ g_assert (G_IS_TLS_CERTIFICATE (issuer));
+
+ check = g_tls_certificate_get_issuer (cert);
+ g_assert (check == issuer);
+
+ g_object_unref (cert);
+ g_assert (!G_IS_TLS_CERTIFICATE (cert));
+ g_assert (!G_IS_TLS_CERTIFICATE (issuer));
+}
+
+/* -----------------------------------------------------------------------------
+ * CERTIFICATE VERIFY
+ */
+
+typedef struct {
+ GTlsCertificate *cert;
+ GTlsCertificate *anchor;
+ GSocketConnectable *identity;
+ GTlsDatabase *database;
+} TestVerify;
+
+static void
+setup_verify (TestVerify *test,
+ gconstpointer data)
+{
+ GError *error = NULL;
+ gchar *path;
+
+ path = g_build_filename (source_dir, "server.pem", NULL);
+ test->cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (test->cert));
+ g_free (path);
+
+ test->identity = g_network_address_new ("server.example.com", 80);
+
+ path = g_build_filename (source_dir, "ca.pem", NULL);
+ test->anchor = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (test->anchor));
+ test->database = g_tls_file_database_new (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_DATABASE (test->database));
+ g_free (path);
+}
+
+static void
+teardown_verify (TestVerify *test,
+ gconstpointer data)
+{
+ g_assert (G_IS_TLS_CERTIFICATE (test->cert));
+ g_object_unref (test->cert);
+ g_assert (!G_IS_TLS_CERTIFICATE (test->cert));
+
+ g_assert (G_IS_TLS_CERTIFICATE (test->anchor));
+ g_object_unref (test->anchor);
+ g_assert (!G_IS_TLS_CERTIFICATE (test->anchor));
+
+ g_assert (G_IS_TLS_DATABASE (test->database));
+ g_object_unref (test->database);
+ g_assert (!G_IS_TLS_DATABASE (test->database));
+}
+
+static void
+test_verify_certificate_good (TestVerify *test,
+ gconstpointer data)
+{
+ GTlsCertificateFlags errors;
+
+ errors = g_tls_certificate_verify (test->cert, test->identity, test->anchor);
+ g_assert_cmpuint (errors, ==, 0);
+
+ errors = g_tls_certificate_verify (test->cert, NULL, test->anchor);
+ g_assert_cmpuint (errors, ==, 0);
+}
+
+static void
+test_verify_certificate_bad_identity (TestVerify *test,
+ gconstpointer data)
+{
+ GSocketConnectable *identity;
+ GTlsCertificateFlags errors;
+
+ identity = g_network_address_new ("other.example.com", 80);
+
+ errors = g_tls_certificate_verify (test->cert, identity, test->anchor);
+ g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_BAD_IDENTITY);
+
+ g_object_unref (identity);
+}
+
+static void
+test_verify_certificate_bad_ca (TestVerify *test,
+ gconstpointer data)
+{
+ GTlsCertificateFlags errors;
+ GTlsCertificate *cert;
+ GError *error = NULL;
+ gchar *path;
+
+ /* Use a client certificate as the CA, which is wrong */
+ path = g_build_filename (source_dir, "client.pem", NULL);
+ cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (cert));
+ g_free (path);
+
+ errors = g_tls_certificate_verify (test->cert, test->identity, cert);
+ g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA);
+
+ g_object_unref (cert);
+}
+
+static void
+test_verify_certificate_bad_before (TestVerify *test,
+ gconstpointer data)
+{
+ GTlsCertificateFlags errors;
+ GTlsCertificate *cert;
+ GError *error = NULL;
+ gchar *path;
+
+ /* This is a certificate in the future */
+ path = g_build_filename (source_dir, "client-future.pem", NULL);
+ cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (cert));
+ g_free (path);
+
+ errors = g_tls_certificate_verify (cert, NULL, test->anchor);
+ g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_NOT_ACTIVATED);
+
+ g_object_unref (cert);
+}
+
+static void
+test_verify_certificate_bad_expired (TestVerify *test,
+ gconstpointer data)
+{
+ GTlsCertificateFlags errors;
+ GTlsCertificate *cert;
+ GError *error = NULL;
+ gchar *path;
+
+ /* This is a certificate in the future */
+ path = g_build_filename (source_dir, "client-past.pem", NULL);
+ cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (cert));
+ g_free (path);
+
+ errors = g_tls_certificate_verify (cert, NULL, test->anchor);
+ g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_EXPIRED);
+
+ g_object_unref (cert);
+}
+
+static void
+test_verify_certificate_bad_combo (TestVerify *test,
+ gconstpointer data)
+{
+ GTlsCertificate *cert;
+ GTlsCertificate *cacert;
+ GSocketConnectable *identity;
+ GTlsCertificateFlags errors;
+ GError *error = NULL;
+ gchar *path;
+
+ path = g_build_filename (source_dir, "client-past.pem", NULL);
+ cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (cert));
+ g_free (path);
+
+ /* Unrelated cert used as certificate authority */
+ path = g_build_filename (source_dir, "server-self.pem", NULL);
+ cacert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (cacert));
+ g_free (path);
+
+ /*
+ * - Use unrelated cert as CA
+ * - Use wrong identity.
+ * - Use expired certificate.
+ */
+
+ identity = g_network_address_new ("other.example.com", 80);
+
+ errors = g_tls_certificate_verify (cert, identity, cacert);
+ g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA |
+ G_TLS_CERTIFICATE_BAD_IDENTITY | G_TLS_CERTIFICATE_EXPIRED);
+
+ g_object_unref (cert);
+ g_object_unref (cacert);
+ g_object_unref (identity);
+}
+
+static void
+test_verify_database_good (TestVerify *test,
+ gconstpointer data)
+{
+ GTlsCertificateFlags errors;
+ GError *error = NULL;
+
+ errors = g_tls_database_verify_chain (test->database, test->cert,
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+ test->identity, NULL, 0, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpuint (errors, ==, 0);
+
+ errors = g_tls_database_verify_chain (test->database, test->cert,
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+ NULL, NULL, 0, NULL, &error);
+ g_assert_cmpuint (errors, ==, 0);
+}
+
+static void
+test_verify_database_bad_identity (TestVerify *test,
+ gconstpointer data)
+{
+ GSocketConnectable *identity;
+ GTlsCertificateFlags errors;
+ GError *error = NULL;
+
+ identity = g_network_address_new ("other.example.com", 80);
+
+ errors = g_tls_database_verify_chain (test->database, test->cert,
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+ identity, NULL, 0, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_BAD_IDENTITY);
+
+ g_object_unref (identity);
+}
+
+static void
+test_verify_database_bad_ca (TestVerify *test,
+ gconstpointer data)
+{
+ GTlsCertificateFlags errors;
+ GTlsCertificate *cert;
+ GError *error = NULL;
+ gchar *path;
+
+ /* Use another certificate which isn't in our CA list */
+ path = g_build_filename (source_dir, "server-self.pem", NULL);
+ cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (cert));
+ g_free (path);
+
+ errors = g_tls_database_verify_chain (test->database, cert,
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+ test->identity, NULL, 0, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA);
+
+ g_object_unref (cert);
+}
+
+static void
+test_verify_database_bad_before (TestVerify *test,
+ gconstpointer data)
+{
+ GTlsCertificateFlags errors;
+ GTlsCertificate *cert;
+ GError *error = NULL;
+ gchar *path;
+
+ /* This is a certificate in the future */
+ path = g_build_filename (source_dir, "client-future.pem", NULL);
+ cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (cert));
+ g_free (path);
+
+ errors = g_tls_database_verify_chain (test->database, cert,
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+ NULL, NULL, 0, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_NOT_ACTIVATED);
+
+ g_object_unref (cert);
+}
+
+static void
+test_verify_database_bad_expired (TestVerify *test,
+ gconstpointer data)
+{
+ GTlsCertificateFlags errors;
+ GTlsCertificate *cert;
+ GError *error = NULL;
+ gchar *path;
+
+ /* This is a certificate in the future */
+ path = g_build_filename (source_dir, "client-past.pem", NULL);
+ cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (cert));
+ g_free (path);
+
+ errors = g_tls_database_verify_chain (test->database, cert,
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+ NULL, NULL, 0, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_EXPIRED);
+
+ g_object_unref (cert);
+}
+
+static void
+test_verify_database_bad_combo (TestVerify *test,
+ gconstpointer data)
+{
+ GTlsCertificate *cert;
+ GSocketConnectable *identity;
+ GTlsCertificateFlags errors;
+ GError *error = NULL;
+ gchar *path;
+
+ path = g_build_filename (source_dir, "server-self.pem", NULL);
+ cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (cert));
+ g_free (path);
+
+ /*
+ * - Use is self signed
+ * - Use wrong identity.
+ */
+
+ identity = g_network_address_new ("other.example.com", 80);
+
+ errors = g_tls_database_verify_chain (test->database, cert,
+ G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,
+ identity, NULL, 0, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpuint (errors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA |
+ G_TLS_CERTIFICATE_BAD_IDENTITY);
+
+ g_object_unref (cert);
+ g_object_unref (identity);
+}
+
+/* -----------------------------------------------------------------------------
+ * FILE DATABASE
+ */
+
+typedef struct {
+ GTlsDatabase *database;
+ gchar *path;
+} TestFileDatabase;
+
+static void
+setup_file_database (TestFileDatabase *test,
+ gconstpointer data)
+{
+ GError *error = NULL;
+
+ test->path = g_build_filename (source_dir, "ca-roots.pem", NULL);
+ test->database = g_tls_file_database_new (test->path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_DATABASE (test->database));
+}
+
+static void
+teardown_file_database (TestFileDatabase *test,
+ gconstpointer data)
+{
+ g_assert (G_IS_TLS_DATABASE (test->database));
+ g_object_unref (test->database);
+ g_assert (!G_IS_TLS_DATABASE (test->database));
+
+ g_free (test->path);
+}
+
+static void
+test_file_database_handle (TestFileDatabase *test,
+ gconstpointer unused)
+{
+ GTlsCertificate *certificate;
+ GTlsCertificate *check;
+ GError *error = NULL;
+ gchar *handle;
+ gchar *path;
+
+ /*
+ * ca.pem is in the ca-roots.pem that the test->database represents.
+ * So it should be able to create a handle for it and treat it as if it
+ * is 'in' the database.
+ */
+
+ path = g_build_filename (source_dir, "ca.pem", NULL);
+ certificate = g_tls_certificate_new_from_file (path, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (certificate));
+ g_free (path);
+
+ handle = g_tls_database_create_certificate_handle (test->database, certificate);
+ g_assert (handle != NULL);
+ g_assert (g_str_has_prefix (handle, "file:///"));
+
+ check = g_tls_database_lookup_certificate_for_handle (test->database, handle,
+ NULL, G_TLS_DATABASE_LOOKUP_NONE,
+ NULL, &error);
+ g_assert_no_error (error);
+ g_assert (G_IS_TLS_CERTIFICATE (check));
+
+ g_free (handle);
+ g_object_unref (check);
+ g_object_unref (certificate);
+}
+
+static void
+test_file_database_handle_invalid (TestFileDatabase *test,
+ gconstpointer unused)
+{
+ GTlsCertificate *certificate;
+ GError *error = NULL;
+
+ certificate = g_tls_database_lookup_certificate_for_handle (test->database, "blah:blah",
+ NULL, G_TLS_DATABASE_LOOKUP_NONE,
+ NULL, &error);
+ g_assert_no_error (error);
+ g_assert (certificate == NULL);
+}
+
+/* -----------------------------------------------------------------------------
+ * BACKEND
+ */
+
+static void
+test_default_database_is_singleton (void)
+{
+ GTlsBackend *backend;
+ GTlsDatabase *database;
+ GTlsDatabase *check;
+
+ backend = g_tls_backend_get_default ();
+ g_assert (G_IS_TLS_BACKEND (backend));
+
+ database = g_tls_backend_get_default_database (backend);
+ g_assert (G_IS_TLS_DATABASE (database));
+
+ check = g_tls_backend_get_default_database (backend);
+ g_assert (database == check);
+
+ g_object_unref (database);
+ g_object_unref (check);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ gchar *dir;
+ int ret;
+
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ /* Use the gnutls database */
+ if (!g_getenv ("GIO_USE_TLS"))
+ g_setenv ("GIO_USE_TLS", "gnutls", TRUE);
+
+ /* Create an absolute path for test files */
+ if (g_path_is_absolute (SRCDIR))
+ {
+ source_dir = g_build_filename (SRCDIR, "files", NULL);
+ }
+ else
+ {
+ dir = g_get_current_dir ();
+ source_dir = g_build_filename (dir, SRCDIR, "files", NULL);
+ g_free (dir);
+ }
+
+ g_test_add ("/tls/connection/basic", TestConnection, NULL,
+ setup_connection, test_basic_connection, teardown_connection);
+ g_test_add ("/tls/connection/verified", TestConnection, NULL,
+ setup_connection, test_verified_connection, teardown_connection);
+ g_test_add ("/tls/connection/client-auth", TestConnection, NULL,
+ setup_connection, test_client_auth_connection, teardown_connection);
+ g_test_add ("/tls/connection/no-database", TestConnection, NULL,
+ setup_connection, test_connection_no_database, teardown_connection);
+
+ g_test_add_func ("/tls/backend/default-database-is-singleton",
+ test_default_database_is_singleton);
+
+ g_test_add ("/tls/certificate/create-destroy-pem", TestCertificate, NULL,
+ setup_certificate, test_create_destroy_certificate_pem, teardown_certificate);
+ g_test_add ("/tls/certificate/create-destroy-der", TestCertificate, NULL,
+ setup_certificate, test_create_destroy_certificate_der, teardown_certificate);
+ g_test_add ("/tls/certificate/create-with-issuer", TestCertificate, NULL,
+ setup_certificate, test_create_certificate_with_issuer, teardown_certificate);
+
+ g_test_add ("/tls/certificate/verify-good", TestVerify, NULL,
+ setup_verify, test_verify_certificate_good, teardown_verify);
+ g_test_add ("/tls/certificate/verify-bad-identity", TestVerify, NULL,
+ setup_verify, test_verify_certificate_bad_identity, teardown_verify);
+ g_test_add ("/tls/certificate/verify-bad-ca", TestVerify, NULL,
+ setup_verify, test_verify_certificate_bad_ca, teardown_verify);
+ g_test_add ("/tls/certificate/verify-bad-before", TestVerify, NULL,
+ setup_verify, test_verify_certificate_bad_before, teardown_verify);
+ g_test_add ("/tls/certificate/verify-bad-expired", TestVerify, NULL,
+ setup_verify, test_verify_certificate_bad_expired, teardown_verify);
+ g_test_add ("/tls/certificate/verify-bad-combo", TestVerify, NULL,
+ setup_verify, test_verify_certificate_bad_combo, teardown_verify);
+ g_test_add ("/tls/database/verify-good", TestVerify, NULL,
+ setup_verify, test_verify_database_good, teardown_verify);
+ g_test_add ("/tls/database/verify-bad-identity", TestVerify, NULL,
+ setup_verify, test_verify_database_bad_identity, teardown_verify);
+ g_test_add ("/tls/database/verify-bad-ca", TestVerify, NULL,
+ setup_verify, test_verify_database_bad_ca, teardown_verify);
+ g_test_add ("/tls/database/verify-bad-before", TestVerify, NULL,
+ setup_verify, test_verify_database_bad_before, teardown_verify);
+ g_test_add ("/tls/database/verify-bad-expired", TestVerify, NULL,
+ setup_verify, test_verify_database_bad_expired, teardown_verify);
+ g_test_add ("/tls/database/verify-bad-combo", TestVerify, NULL,
+ setup_verify, test_verify_database_bad_combo, teardown_verify);
+
+ g_test_add ("/tls/file-database/test-handle", TestFileDatabase, NULL,
+ setup_file_database, test_file_database_handle, teardown_file_database);
+ g_test_add ("/tls/file-database/test-handle-invalid", TestFileDatabase, NULL,
+ setup_file_database, test_file_database_handle_invalid, teardown_file_database);
+
+ ret = g_test_run();
+
+ g_free (source_dir);
+ return ret;
+}