[libsoup] Add SOUP_SESSION_SSL_STRICT, to control whether SSL cert errors are fatal



commit ab3a4d1eafda14b04454343ad0ba59df3236503d
Author: Dan Winship <danw gnome org>
Date:   Sat Feb 20 12:44:44 2010 -0500

    Add SOUP_SESSION_SSL_STRICT, to control whether SSL cert errors are fatal
    
    Based on a patch from Gustavo Noronha Silva
    
    https://bugzilla.gnome.org/show_bug.cgi?id=610374

 libsoup/soup-connection.c |   17 +++++++++++++++++
 libsoup/soup-connection.h |    1 +
 libsoup/soup-gnutls.c     |    6 ++----
 libsoup/soup-session.c    |   29 +++++++++++++++++++++++++++++
 libsoup/soup-session.h    |    1 +
 libsoup/soup-socket.c     |   44 ++++++++++++++++++++++++++++++++++++++++----
 libsoup/soup-socket.h     |    3 ++-
 tests/test-utils.c        |    5 +++++
 8 files changed, 97 insertions(+), 9 deletions(-)
---
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index 8cb110c..b730b62 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -34,6 +34,7 @@ typedef struct {
 	SoupAddress *remote_addr, *tunnel_addr;
 	SoupURI     *proxy_uri;
 	gpointer     ssl_creds;
+	gboolean     ssl_strict;
 
 	GMainContext      *async_context;
 
@@ -61,6 +62,7 @@ enum {
 	PROP_TUNNEL_ADDRESS,
 	PROP_PROXY_URI,
 	PROP_SSL_CREDS,
+	PROP_SSL_STRICT,
 	PROP_ASYNC_CONTEXT,
 	PROP_TIMEOUT,
 	PROP_IDLE_TIMEOUT,
@@ -175,6 +177,13 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
 				      "Opaque SSL credentials for this connection",
 				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 	g_object_class_install_property (
+		object_class, PROP_SSL_STRICT,
+		g_param_spec_boolean (SOUP_CONNECTION_SSL_STRICT,
+				      "Strictly validate SSL certificates",
+				      "Whether certificate errors should be considered a connection error",
+				      TRUE,
+				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	g_object_class_install_property (
 		object_class, PROP_ASYNC_CONTEXT,
 		g_param_spec_pointer (SOUP_CONNECTION_ASYNC_CONTEXT,
 				      "Async GMainContext",
@@ -246,6 +255,9 @@ set_property (GObject *object, guint prop_id,
 	case PROP_SSL_CREDS:
 		priv->ssl_creds = g_value_get_pointer (value);
 		break;
+	case PROP_SSL_STRICT:
+		priv->ssl_strict = g_value_get_boolean (value);
+		break;
 	case PROP_ASYNC_CONTEXT:
 		priv->async_context = g_value_get_pointer (value);
 		if (priv->async_context)
@@ -285,6 +297,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_SSL_CREDS:
 		g_value_set_pointer (value, priv->ssl_creds);
 		break;
+	case PROP_SSL_STRICT:
+		g_value_set_boolean (value, priv->ssl_strict);
+		break;
 	case PROP_ASYNC_CONTEXT:
 		g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
 		break;
@@ -466,6 +481,7 @@ soup_connection_connect_async (SoupConnection *conn,
 	priv->socket =
 		soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr,
 				 SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
+				 SOUP_SOCKET_SSL_STRICT, priv->ssl_strict,
 				 SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
 				 SOUP_SOCKET_TIMEOUT, priv->io_timeout,
 				 NULL);
@@ -496,6 +512,7 @@ soup_connection_connect_sync (SoupConnection *conn)
 	priv->socket =
 		soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr,
 				 SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
+				 SOUP_SOCKET_SSL_STRICT, priv->ssl_strict,
 				 SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
 				 SOUP_SOCKET_TIMEOUT, priv->io_timeout,
 				 NULL);
diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h
index ae8973e..f2ec40c 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -44,6 +44,7 @@ typedef void  (*SoupConnectionCallback)        (SoupConnection   *conn,
 #define SOUP_CONNECTION_TUNNEL_ADDRESS  "tunnel-address"
 #define SOUP_CONNECTION_PROXY_URI       "proxy-uri"
 #define SOUP_CONNECTION_SSL_CREDENTIALS "ssl-creds"
+#define SOUP_CONNECTION_SSL_STRICT      "ssl-strict"
 #define SOUP_CONNECTION_ASYNC_CONTEXT   "async-context"
 #define SOUP_CONNECTION_TIMEOUT         "timeout"
 #define SOUP_CONNECTION_IDLE_TIMEOUT    "idle-timeout"
diff --git a/libsoup/soup-gnutls.c b/libsoup/soup-gnutls.c
index c77f2b2..1b6e613 100644
--- a/libsoup/soup-gnutls.c
+++ b/libsoup/soup-gnutls.c
@@ -164,6 +164,8 @@ again:
 		return G_IO_STATUS_ERROR;
 	}
 
+	chan->established = TRUE;
+
 	if (chan->type == SOUP_SSL_TYPE_CLIENT && chan->creds->have_ca_file &&
 	    !verify_certificate (chan->session, chan->hostname, err))
 		return G_IO_STATUS_ERROR;
@@ -190,8 +192,6 @@ again:
 		if (result == G_IO_STATUS_AGAIN ||
 		    result == G_IO_STATUS_ERROR)
 			return result;
-
-		chan->established = TRUE;
 	}
 
 	result = gnutls_record_recv (chan->session, buf, count);
@@ -252,8 +252,6 @@ again:
 		if (result == G_IO_STATUS_AGAIN ||
 		    result == G_IO_STATUS_ERROR)
 			return result;
-
-		chan->established = TRUE;
 	}
 
 	result = gnutls_record_send (chan->session, buf, count);
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 2b13228..009d702 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -71,6 +71,7 @@ typedef struct {
 typedef struct {
 	char *ssl_ca_file;
 	SoupSSLCredentials *ssl_creds;
+	gboolean ssl_strict;
 
 	SoupMessageQueue *queue;
 
@@ -140,6 +141,7 @@ enum {
 	PROP_MAX_CONNS_PER_HOST,
 	PROP_USE_NTLM,
 	PROP_SSL_CA_FILE,
+	PROP_SSL_STRICT,
 	PROP_ASYNC_CONTEXT,
 	PROP_TIMEOUT,
 	PROP_USER_AGENT,
@@ -191,6 +193,8 @@ soup_session_init (SoupSession *session)
 	 * so hold a ref on the default GResolver.
 	 */
 	priv->resolver = g_resolver_get_default ();
+
+	priv->ssl_strict = TRUE;
 }
 
 static void
@@ -508,6 +512,24 @@ soup_session_class_init (SoupSessionClass *session_class)
 				     NULL,
 				     G_PARAM_READWRITE));
 	/**
+	 * SOUP_SESSION_SSL_STRICT:
+	 *
+	 * Alias for the #SoupSession:ignore-ssl-cert-errors
+	 * property. By default, when validating certificates against
+	 * a CA file, Soup will consider invalid certificates as a
+	 * connection error. Setting this property to %TRUE makes soup
+	 * ignore the errors, and make the connection.
+	 *
+	 * Since: 2.30
+	 **/
+	g_object_class_install_property (
+		object_class, PROP_SSL_STRICT,
+		g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
+				      "Strictly validate SSL certificates",
+				      "Whether certificate errors should be considered a connection error",
+				      TRUE,
+				      G_PARAM_READWRITE));
+	/**
 	 * SOUP_SESSION_ASYNC_CONTEXT:
 	 *
 	 * Alias for the #SoupSession:async-context property. (The
@@ -843,6 +865,9 @@ set_property (GObject *object, guint prop_id,
 		}
 
 		break;
+	case PROP_SSL_STRICT:
+		priv->ssl_strict = g_value_get_boolean (value);
+		break;
 	case PROP_ASYNC_CONTEXT:
 		priv->async_context = g_value_get_pointer (value);
 		if (priv->async_context)
@@ -936,6 +961,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_SSL_CA_FILE:
 		g_value_set_string (value, priv->ssl_ca_file);
 		break;
+	case PROP_SSL_STRICT:
+		g_value_set_boolean (value, priv->ssl_strict);
+		break;
 	case PROP_ASYNC_CONTEXT:
 		g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
 		break;
@@ -1408,6 +1436,7 @@ soup_session_get_connection (SoupSession *session,
 		SOUP_CONNECTION_TUNNEL_ADDRESS, tunnel_addr,
 		SOUP_CONNECTION_PROXY_URI, item->proxy_uri,
 		SOUP_CONNECTION_SSL_CREDENTIALS, ssl_creds,
+		SOUP_CONNECTION_SSL_STRICT, priv->ssl_strict,
 		SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
 		SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
 		SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index 056799d..fe07892 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -63,6 +63,7 @@ GType soup_session_get_type (void);
 #define SOUP_SESSION_MAX_CONNS_PER_HOST     "max-conns-per-host"
 #define SOUP_SESSION_USE_NTLM               "use-ntlm"
 #define SOUP_SESSION_SSL_CA_FILE            "ssl-ca-file"
+#define SOUP_SESSION_SSL_STRICT             "ssl-strict"
 #define SOUP_SESSION_ASYNC_CONTEXT          "async-context"
 #define SOUP_SESSION_TIMEOUT                "timeout"
 #define SOUP_SESSION_USER_AGENT             "user-agent"
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index 00fd881..2bbf22d 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -66,6 +66,7 @@ enum {
 	PROP_NON_BLOCKING,
 	PROP_IS_SERVER,
 	PROP_SSL_CREDENTIALS,
+	PROP_SSL_STRICT,
 	PROP_ASYNC_CONTEXT,
 	PROP_TIMEOUT,
 
@@ -81,6 +82,7 @@ typedef struct {
 	guint is_server:1;
 	guint timed_out:1;
 	gpointer ssl_creds;
+	gboolean ssl_strict;
 
 	GMainContext   *async_context;
 	GSource        *watch_src;
@@ -354,6 +356,18 @@ soup_socket_class_init (SoupSocketClass *socket_class)
 				      "SSL credential information, passed from the session to the SSL implementation",
 				      G_PARAM_READWRITE));
 	/**
+	 * SOUP_SOCKET_SSL_STRICT:
+	 *
+	 * Alias for the #SoupSocket:ignore-ssl-cert-errors property.
+	 **/
+	g_object_class_install_property (
+		object_class, PROP_SSL_STRICT,
+		g_param_spec_boolean (SOUP_SOCKET_SSL_STRICT,
+				      "Strictly validate SSL certificates",
+				      "Whether certificate errors should be considered a connection error",
+				      TRUE,
+				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	/**
 	 * SOUP_SOCKET_ASYNC_CONTEXT:
 	 *
 	 * Alias for the #SoupSocket:async-context property. (The
@@ -492,6 +506,9 @@ set_property (GObject *object, guint prop_id,
 	case PROP_SSL_CREDENTIALS:
 		priv->ssl_creds = g_value_get_pointer (value);
 		break;
+	case PROP_SSL_STRICT:
+		priv->ssl_strict = g_value_get_boolean (value);
+		break;
 	case PROP_ASYNC_CONTEXT:
 		priv->async_context = g_value_get_pointer (value);
 		if (priv->async_context)
@@ -528,6 +545,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_SSL_CREDENTIALS:
 		g_value_set_pointer (value, priv->ssl_creds);
 		break;
+	case PROP_SSL_STRICT:
+		g_value_set_boolean (value, priv->ssl_strict);
+		break;
 	case PROP_ASYNC_CONTEXT:
 		g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
 		break;
@@ -1218,11 +1238,19 @@ read_from_network (SoupSocket *sock, gpointer buffer, gsize len,
 		return SOUP_SOCKET_ERROR;
 	}
 
+again:
 	status = g_io_channel_read_chars (priv->iochannel,
 					  buffer, len, nread, &my_err);
 	if (my_err) {
-		if (my_err->domain == SOUP_SSL_ERROR &&
-		    my_err->code == SOUP_SSL_ERROR_HANDSHAKE_NEEDS_WRITE)
+		if (g_error_matches (my_err, SOUP_SSL_ERROR,
+				     SOUP_SSL_ERROR_CERTIFICATE) &&
+		    !priv->ssl_strict) {
+			g_clear_error (&my_err);
+			goto again;
+		}
+
+		if (g_error_matches (my_err, SOUP_SSL_ERROR,
+				     SOUP_SSL_ERROR_HANDSHAKE_NEEDS_WRITE))
 			cond = G_IO_OUT;
 		g_propagate_error (error, my_err);
 	}
@@ -1517,11 +1545,19 @@ soup_socket_write (SoupSocket *sock, gconstpointer buffer,
 		return SOUP_SOCKET_WOULD_BLOCK;
 	}
 
+again:
 	status = g_io_channel_write_chars (priv->iochannel,
 					   buffer, len, nwrote, &my_err);
 	if (my_err) {
-		if (my_err->domain == SOUP_SSL_ERROR &&
-		    my_err->code == SOUP_SSL_ERROR_HANDSHAKE_NEEDS_READ)
+		if (g_error_matches (my_err, SOUP_SSL_ERROR,
+				     SOUP_SSL_ERROR_CERTIFICATE) &&
+		    !priv->ssl_strict) {
+			g_clear_error (&my_err);
+			goto again;
+		}
+
+		if (g_error_matches (my_err, SOUP_SSL_ERROR,
+				     SOUP_SSL_ERROR_HANDSHAKE_NEEDS_READ))
 			cond = G_IO_IN;
 		g_propagate_error (error, my_err);
 	}
diff --git a/libsoup/soup-socket.h b/libsoup/soup-socket.h
index 079297b..4106e86 100644
--- a/libsoup/soup-socket.h
+++ b/libsoup/soup-socket.h
@@ -45,8 +45,9 @@ typedef struct {
 #define SOUP_SOCKET_FLAG_NONBLOCKING "non-blocking"
 #define SOUP_SOCKET_IS_SERVER        "is-server"
 #define SOUP_SOCKET_SSL_CREDENTIALS  "ssl-creds"
+#define SOUP_SOCKET_SSL_STRICT       "ssl-strict"
 #define SOUP_SOCKET_ASYNC_CONTEXT    "async-context"
-#define SOUP_SOCKET_TIMEOUT	     "timeout"
+#define SOUP_SOCKET_TIMEOUT          "timeout"
 
 typedef void (*SoupSocketCallback)            (SoupSocket         *sock,
 					       guint               status,
diff --git a/tests/test-utils.c b/tests/test-utils.c
index 0b13ca1..8d7e9f6 100644
--- a/tests/test-utils.c
+++ b/tests/test-utils.c
@@ -231,6 +231,11 @@ soup_test_session_new (GType type, ...)
 	session = (SoupSession *)g_object_new_valist (type, propname, args);
 	va_end (args);
 
+	g_object_set (G_OBJECT (session),
+		      SOUP_SESSION_SSL_CA_FILE, SRCDIR "/test-cert.pem",
+		      SOUP_SESSION_SSL_STRICT, FALSE,
+		      NULL);
+
 	if (http_debug_level && !logger) {
 		SoupLoggerLogLevel level = MIN ((SoupLoggerLogLevel)http_debug_level, SOUP_LOGGER_LOG_BODY);
 



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