[libsoup] Default to TLS for https connections, and fall back to SSLv3 on failure
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup] Default to TLS for https connections, and fall back to SSLv3 on failure
- Date: Sun, 7 Aug 2011 17:07:29 +0000 (UTC)
commit 5ddafe9abc8b594016f401c0b53a481e706e74c6
Author: Dan Winship <danw gnome org>
Date: Sun Aug 7 13:02:51 2011 -0400
Default to TLS for https connections, and fall back to SSLv3 on failure
Rather than always using SSLv3, try proper TLS+extensions first, and
fall back to SSLv3-without-extensions if that gives an error that
looks like it might mean "broken SSLv3-only server". Use
SoupSessionHost to record the fallback status for a host.
https://bugzilla.gnome.org/show_bug.cgi?id=581342
libsoup/soup-connection.c | 55 ++++++++++++++++++++++++++++++++++++++----
libsoup/soup-connection.h | 2 +
libsoup/soup-session-async.c | 13 +++++++--
libsoup/soup-session-sync.c | 6 ++++
libsoup/soup-session.c | 8 +++++-
libsoup/soup-socket.c | 42 ++++++++++++++++++++++++++++----
libsoup/soup-socket.h | 1 +
libsoup/soup-status.c | 3 +-
libsoup/soup-status.h | 1 +
9 files changed, 116 insertions(+), 15 deletions(-)
---
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index 977d3eb..514640f 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -37,6 +37,7 @@ typedef struct {
SoupURI *proxy_uri;
gpointer ssl_creds;
gboolean ssl_strict;
+ gboolean ssl_fallback;
GMainContext *async_context;
@@ -65,6 +66,7 @@ enum {
PROP_PROXY_URI,
PROP_SSL_CREDS,
PROP_SSL_STRICT,
+ PROP_SSL_FALLBACK,
PROP_ASYNC_CONTEXT,
PROP_TIMEOUT,
PROP_IDLE_TIMEOUT,
@@ -192,6 +194,13 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (
+ object_class, PROP_SSL_FALLBACK,
+ g_param_spec_boolean (SOUP_CONNECTION_SSL_FALLBACK,
+ "SSLv3 fallback",
+ "Use SSLv3 instead of TLS",
+ FALSE,
+ 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",
@@ -266,6 +275,9 @@ set_property (GObject *object, guint prop_id,
case PROP_SSL_STRICT:
priv->ssl_strict = g_value_get_boolean (value);
break;
+ case PROP_SSL_FALLBACK:
+ priv->ssl_fallback = g_value_get_boolean (value);
+ break;
case PROP_ASYNC_CONTEXT:
priv->async_context = g_value_get_pointer (value);
if (priv->async_context)
@@ -308,6 +320,9 @@ get_property (GObject *object, guint prop_id,
case PROP_SSL_STRICT:
g_value_set_boolean (value, priv->ssl_strict);
break;
+ case PROP_SSL_FALLBACK:
+ g_value_set_boolean (value, priv->ssl_fallback);
+ break;
case PROP_ASYNC_CONTEXT:
g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
break;
@@ -440,6 +455,9 @@ socket_connect_finished (SoupSocket *socket, guint status, gpointer user_data)
soup_connection_set_state (data->conn, SOUP_CONNECTION_IN_USE);
priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
start_idle_timer (data->conn);
+ } else if (status == SOUP_STATUS_TLS_FAILED) {
+ priv->ssl_fallback = TRUE;
+ status = SOUP_STATUS_TRY_AGAIN;
}
if (data->callback) {
@@ -498,6 +516,7 @@ soup_connection_connect_async (SoupConnection *conn,
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_SSL_FALLBACK, priv->ssl_fallback,
SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
SOUP_SOCKET_TIMEOUT, priv->io_timeout,
"clean-dispose", TRUE,
@@ -522,6 +541,7 @@ soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable)
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_SSL_FALLBACK, priv->ssl_fallback,
SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
SOUP_SOCKET_TIMEOUT, priv->io_timeout,
"clean-dispose", TRUE,
@@ -532,23 +552,29 @@ soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable)
if (!SOUP_STATUS_IS_SUCCESSFUL (status))
goto fail;
- g_signal_connect (priv->socket, "disconnected",
- G_CALLBACK (socket_disconnected), conn);
-
if (priv->ssl_creds && !priv->tunnel_addr) {
if (!soup_socket_start_ssl (priv->socket, cancellable))
status = SOUP_STATUS_SSL_FAILED;
- else
+ else {
status = soup_socket_handshake_sync (priv->socket, cancellable);
+ if (status == SOUP_STATUS_TLS_FAILED) {
+ priv->ssl_fallback = TRUE;
+ status = SOUP_STATUS_TRY_AGAIN;
+ }
+ }
}
if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ g_signal_connect (priv->socket, "disconnected",
+ G_CALLBACK (socket_disconnected), conn);
+
soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
start_idle_timer (conn);
} else {
fail:
if (priv->socket) {
+ soup_socket_disconnect (priv->socket);
g_object_unref (priv->socket);
priv->socket = NULL;
}
@@ -576,6 +602,7 @@ soup_connection_start_ssl_sync (SoupConnection *conn,
{
SoupConnectionPrivate *priv;
const char *server_name;
+ guint status;
g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
priv = SOUP_CONNECTION_GET_PRIVATE (conn);
@@ -587,13 +614,25 @@ soup_connection_start_ssl_sync (SoupConnection *conn,
cancellable))
return SOUP_STATUS_SSL_FAILED;
- return soup_socket_handshake_sync (priv->socket, cancellable);
+ status = soup_socket_handshake_sync (priv->socket, cancellable);
+ if (status == SOUP_STATUS_TLS_FAILED) {
+ priv->ssl_fallback = TRUE;
+ status = SOUP_STATUS_TRY_AGAIN;
+ }
+
+ return status;
}
static void
start_ssl_completed (SoupSocket *socket, guint status, gpointer user_data)
{
SoupConnectionAsyncConnectData *data = user_data;
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+
+ if (status == SOUP_STATUS_TLS_FAILED) {
+ priv->ssl_fallback = TRUE;
+ status = SOUP_STATUS_TRY_AGAIN;
+ }
data->callback (data->conn, status, data->callback_data);
g_object_unref (data->conn);
@@ -756,6 +795,12 @@ soup_connection_get_ever_used (SoupConnection *conn)
return SOUP_CONNECTION_GET_PRIVATE (conn)->unused_timeout == 0;
}
+gboolean
+soup_connection_get_ssl_fallback (SoupConnection *conn)
+{
+ return SOUP_CONNECTION_GET_PRIVATE (conn)->ssl_fallback;
+}
+
void
soup_connection_send_request (SoupConnection *conn,
SoupMessageQueueItem *item,
diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h
index f3b936c..ef304e7 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -85,6 +85,8 @@ void soup_connection_send_request (SoupConnection *conn,
SoupMessageCompletionFn completion_cb,
gpointer user_data);
+gboolean soup_connection_get_ssl_fallback (SoupConnection *conn);
+
G_END_DECLS
#endif /* SOUP_CONNECTION_H */
diff --git a/libsoup/soup-session-async.c b/libsoup/soup-session-async.c
index 94d72de..661883b 100644
--- a/libsoup/soup-session-async.c
+++ b/libsoup/soup-session-async.c
@@ -313,10 +313,17 @@ got_connection (SoupConnection *conn, guint status, gpointer user_data)
}
if (status != SOUP_STATUS_OK) {
- soup_session_set_item_status (session, item, status);
- item->state = SOUP_MESSAGE_FINISHING;
-
soup_connection_disconnect (conn);
+
+ if (status == SOUP_STATUS_TRY_AGAIN) {
+ g_object_unref (item->conn);
+ item->conn = NULL;
+ item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
+ } else {
+ soup_session_set_item_status (session, item, status);
+ item->state = SOUP_MESSAGE_FINISHING;
+ }
+
do_idle_run_queue (session);
soup_message_queue_item_unref (item);
g_object_unref (session);
diff --git a/libsoup/soup-session-sync.c b/libsoup/soup-session-sync.c
index a9498d6..373b1bd 100644
--- a/libsoup/soup-session-sync.c
+++ b/libsoup/soup-session-sync.c
@@ -204,6 +204,12 @@ try_again:
}
status = soup_connection_connect_sync (item->conn, item->cancellable);
+ if (status == SOUP_STATUS_TRY_AGAIN) {
+ soup_connection_disconnect (item->conn);
+ g_object_unref (item->conn);
+ item->conn = NULL;
+ goto try_again;
+ }
if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
if (!msg->status_code)
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 4cd0de3..8c2b354 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -59,13 +59,15 @@
**/
typedef struct {
- SoupURI *uri;
+ SoupURI *uri;
SoupAddress *addr;
GSList *connections; /* CONTAINS: SoupConnection */
guint num_conns;
guint num_messages;
+
+ gboolean ssl_fallback;
} SoupSessionHost;
typedef struct {
@@ -1270,6 +1272,9 @@ connection_disconnected (SoupConnection *conn, gpointer user_data)
g_hash_table_remove (priv->conns, conn);
host->connections = g_slist_remove (host->connections, conn);
host->num_conns--;
+
+ if (soup_connection_get_ssl_fallback (conn))
+ host->ssl_fallback = TRUE;
}
g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
@@ -1390,6 +1395,7 @@ soup_session_get_connection (SoupSession *session,
SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
+ SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
NULL);
g_signal_connect (conn, "disconnected",
G_CALLBACK (connection_disconnected),
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index 935b761..a4d9f54 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -54,6 +54,7 @@ enum {
PROP_IS_SERVER,
PROP_SSL_CREDENTIALS,
PROP_SSL_STRICT,
+ PROP_SSL_FALLBACK,
PROP_ASYNC_CONTEXT,
PROP_TIMEOUT,
PROP_TRUSTED_CERTIFICATE,
@@ -75,6 +76,7 @@ typedef struct {
guint non_blocking:1;
guint is_server:1;
guint ssl_strict:1;
+ guint ssl_fallback:1;
guint ssl_ca_in_creds:1;
guint clean_dispose:1;
gpointer ssl_creds;
@@ -353,7 +355,7 @@ soup_socket_class_init (SoupSocketClass *socket_class)
/**
* SOUP_SOCKET_SSL_STRICT:
*
- * Alias for the #SoupSocket:ignore-ssl-cert-errors property.
+ * Alias for the #SoupSocket:ssl-strict property.
**/
g_object_class_install_property (
object_class, PROP_SSL_STRICT,
@@ -363,6 +365,18 @@ soup_socket_class_init (SoupSocketClass *socket_class)
TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/**
+ * SOUP_SOCKET_SSL_FALLBACK:
+ *
+ * Alias for the #SoupSocket:ssl-fallback property.
+ **/
+ g_object_class_install_property (
+ object_class, PROP_SSL_FALLBACK,
+ g_param_spec_boolean (SOUP_SOCKET_SSL_FALLBACK,
+ "SSLv3 fallback",
+ "Use SSLv3 instead of TLS (client-side only)",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
* SOUP_SOCKET_TRUSTED_CERTIFICATE:
*
* Alias for the #SoupSocket:trusted-certificate
@@ -484,6 +498,9 @@ set_property (GObject *object, guint prop_id,
case PROP_SSL_STRICT:
priv->ssl_strict = g_value_get_boolean (value);
break;
+ case PROP_SSL_FALLBACK:
+ priv->ssl_fallback = 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_STRICT:
g_value_set_boolean (value, priv->ssl_strict);
break;
+ case PROP_SSL_FALLBACK:
+ g_value_set_boolean (value, priv->ssl_fallback);
+ break;
case PROP_TRUSTED_CERTIFICATE:
g_value_set_boolean (value, priv->tls_errors == 0);
break;
@@ -935,7 +955,7 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
"server-identity", identity,
"use-system-certdb", FALSE,
"require-close-notify", FALSE,
- "use-ssl3", TRUE,
+ "use-ssl3", priv->ssl_fallback,
NULL);
g_object_unref (identity);
@@ -979,12 +999,19 @@ soup_socket_handshake_sync (SoupSocket *sock,
GCancellable *cancellable)
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ GError *error = NULL;
if (g_tls_connection_handshake (G_TLS_CONNECTION (priv->conn),
- cancellable, NULL))
+ cancellable, &error))
return SOUP_STATUS_OK;
- else
+ else if (!priv->ssl_fallback &&
+ g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) {
+ g_error_free (error);
+ return SOUP_STATUS_TLS_FAILED;
+ } else {
+ g_error_free (error);
return SOUP_STATUS_SSL_FAILED;
+ }
}
static void
@@ -992,16 +1019,21 @@ handshake_async_ready (GObject *source, GAsyncResult *result, gpointer user_data
{
SoupSocketAsyncConnectData *data = user_data;
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (data->sock);
+ GError *error = NULL;
guint status;
if (priv->async_context)
g_main_context_pop_thread_default (priv->async_context);
if (g_tls_connection_handshake_finish (G_TLS_CONNECTION (priv->conn),
- result, NULL))
+ result, &error))
status = SOUP_STATUS_OK;
+ else if (!priv->ssl_fallback &&
+ g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS))
+ status = SOUP_STATUS_TLS_FAILED;
else
status = SOUP_STATUS_SSL_FAILED;
+ g_clear_error (&error);
data->callback (data->sock, status, data->user_data);
g_object_unref (data->sock);
diff --git a/libsoup/soup-socket.h b/libsoup/soup-socket.h
index 8761071..4d1550f 100644
--- a/libsoup/soup-socket.h
+++ b/libsoup/soup-socket.h
@@ -45,6 +45,7 @@ typedef struct {
#define SOUP_SOCKET_IS_SERVER "is-server"
#define SOUP_SOCKET_SSL_CREDENTIALS "ssl-creds"
#define SOUP_SOCKET_SSL_STRICT "ssl-strict"
+#define SOUP_SOCKET_SSL_FALLBACK "ssl-fallback"
#define SOUP_SOCKET_TRUSTED_CERTIFICATE "trusted-certificate"
#define SOUP_SOCKET_ASYNC_CONTEXT "async-context"
#define SOUP_SOCKET_TIMEOUT "timeout"
diff --git a/libsoup/soup-status.c b/libsoup/soup-status.c
index 8a2653c..11fe51b 100644
--- a/libsoup/soup-status.c
+++ b/libsoup/soup-status.c
@@ -75,12 +75,13 @@
* @SOUP_STATUS_CANT_RESOLVE_PROXY: Unable to resolve proxy host name
* @SOUP_STATUS_CANT_CONNECT: Unable to connect to remote host
* @SOUP_STATUS_CANT_CONNECT_PROXY: Unable to connect to proxy
- * @SOUP_STATUS_SSL_FAILED: SSL negotiation failed
+ * @SOUP_STATUS_SSL_FAILED: SSL/TLS negotiation failed
* @SOUP_STATUS_IO_ERROR: A network error occurred, or the other end
* closed the connection unexpectedly
* @SOUP_STATUS_MALFORMED: Malformed data (usually a programmer error)
* @SOUP_STATUS_TRY_AGAIN: Used internally
* @SOUP_STATUS_TOO_MANY_REDIRECTS: There were too many redirections
+ * @SOUP_STATUS_TLS_FAILED: Used internally
* @SOUP_STATUS_CONTINUE: 100 Continue (HTTP)
* @SOUP_STATUS_SWITCHING_PROTOCOLS: 101 Switching Protocols (HTTP)
* @SOUP_STATUS_PROCESSING: 102 Processing (WebDAV)
diff --git a/libsoup/soup-status.h b/libsoup/soup-status.h
index 0d8b74f..fc8d5a3 100644
--- a/libsoup/soup-status.h
+++ b/libsoup/soup-status.h
@@ -33,6 +33,7 @@ typedef enum {
SOUP_STATUS_MALFORMED,
SOUP_STATUS_TRY_AGAIN,
SOUP_STATUS_TOO_MANY_REDIRECTS,
+ SOUP_STATUS_TLS_FAILED,
/* HTTP Status Codes */
SOUP_STATUS_CONTINUE = 100,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]