[libsoup] Add SoupMessage:tls-certificate and SoupMessage:tls-errors



commit 12238c7d3afd751b98fe3204092c254310bf1e69
Author: Dan Winship <danw gnome org>
Date:   Mon Dec 6 14:05:15 2010 +0100

    Add SoupMessage:tls-certificate and SoupMessage:tls-errors
    
    These provide more information about the certificate on the other end of
    a TLS connection.

 libsoup/soup-message-io.c      |   29 ++++++++-----
 libsoup/soup-message-private.h |    3 +
 libsoup/soup-message.c         |   88 ++++++++++++++++++++++++++++++++++++++++
 libsoup/soup-message.h         |   14 +++++-
 libsoup/soup-socket.c          |   71 +++++++++++++++++++++++++-------
 libsoup/soup-socket.h          |    2 +
 6 files changed, 177 insertions(+), 30 deletions(-)
---
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index e824b00..a2d2ec5 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -352,17 +352,6 @@ read_metadata (SoupMessage *msg, gboolean to_blank)
 		}
 	}
 
-	if (soup_socket_is_ssl (io->sock)) {
-		gboolean trusted_certificate;
-
-		g_object_get (io->sock,
-			      SOUP_SOCKET_TRUSTED_CERTIFICATE, &trusted_certificate,
-			      NULL);
-
-		if (trusted_certificate)
-			soup_message_set_flags (msg, priv->msg_flags | SOUP_MESSAGE_CERTIFICATE_TRUSTED);
-	}
-
 	return TRUE;
 }
 
@@ -831,6 +820,24 @@ io_read (SoupSocket *sock, SoupMessage *msg)
 		if (!read_metadata (msg, TRUE))
 			return;
 
+		if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
+		    soup_socket_is_ssl (io->sock)) {
+			GTlsCertificate *certificate;
+			GTlsCertificateFlags errors;
+
+			g_object_get (io->sock,
+				      SOUP_SOCKET_TLS_CERTIFICATE, &certificate,
+				      SOUP_SOCKET_TLS_ERRORS, &errors,
+				      NULL);
+			if (certificate) {
+				g_object_set (msg,
+					      SOUP_MESSAGE_TLS_CERTIFICATE, certificate,
+					      SOUP_MESSAGE_TLS_ERRORS, errors,
+					      NULL);
+				g_object_unref (certificate);
+			}
+		}
+
 		/* We need to "rewind" io->read_meta_buf back one line.
 		 * That SHOULD be two characters (CR LF), but if the
 		 * web server was stupid, it might only be one.
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 2ea2d42..ce866dc 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -34,6 +34,9 @@ typedef struct {
 	GSList            *decoders;
 
 	SoupURI           *first_party;
+
+	GTlsCertificate      *tls_certificate;
+	GTlsCertificateFlags  tls_errors;
 } SoupMessagePrivate;
 #define SOUP_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_MESSAGE, SoupMessagePrivate))
 
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index cd136a3..7d6626a 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -124,6 +124,8 @@ enum {
 	PROP_REQUEST_HEADERS,
 	PROP_RESPONSE_BODY,
 	PROP_RESPONSE_HEADERS,
+	PROP_TLS_CERTIFICATE,
+	PROP_TLS_ERRORS,
 
 	LAST_PROP
 };
@@ -644,6 +646,32 @@ soup_message_class_init (SoupMessageClass *message_class)
 				     "The HTTP response headers",
 				    SOUP_TYPE_MESSAGE_HEADERS,
 				    G_PARAM_READABLE));
+	/**
+	 * SOUP_MESSAGE_TLS_CERTIFICATE:
+	 *
+	 * Alias for the #SoupMessage:tls-certificate property. (The
+	 * TLS certificate associated with the message, if any.)
+	 **/
+	g_object_class_install_property (
+		object_class, PROP_TLS_CERTIFICATE,
+		g_param_spec_object (SOUP_MESSAGE_TLS_CERTIFICATE,
+				     "TLS Certificate",
+				     "The TLS certificate associated with the message",
+				     G_TYPE_TLS_CERTIFICATE,
+				     G_PARAM_READWRITE));
+	/**
+	 * SOUP_MESSAGE_TLS_ERRORS:
+	 *
+	 * Alias for the #SoupMessage:tls-errors property. (The
+	 * verification errors on #SoupMessage:tls-certificate.)
+	 **/
+	g_object_class_install_property (
+		object_class, PROP_TLS_ERRORS,
+		g_param_spec_flags (SOUP_MESSAGE_TLS_ERRORS,
+				    "TLS Errors",
+				    "The verification errors on the message's TLS certificate",
+				    G_TYPE_TLS_CERTIFICATE_FLAGS, 0,
+				    G_PARAM_READWRITE));
 }
 
 static void
@@ -683,6 +711,20 @@ set_property (GObject *object, guint prop_id,
 	case PROP_FIRST_PARTY:
 		soup_message_set_first_party (msg, g_value_get_boxed (value));
 		break;
+	case PROP_TLS_CERTIFICATE:
+		if (priv->tls_certificate)
+			g_object_unref (priv->tls_certificate);
+		priv->tls_certificate = g_value_dup_object (value);
+		if (priv->tls_certificate && !priv->tls_errors)
+			priv->msg_flags |= SOUP_MESSAGE_CERTIFICATE_TRUSTED;
+		break;
+	case PROP_TLS_ERRORS:
+		priv->tls_errors = g_value_get_flags (value);
+		if (priv->tls_errors)
+			priv->msg_flags &= ~SOUP_MESSAGE_CERTIFICATE_TRUSTED;
+		else if (priv->tls_certificate)
+			priv->msg_flags |= SOUP_MESSAGE_CERTIFICATE_TRUSTED;
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -733,6 +775,12 @@ get_property (GObject *object, guint prop_id,
 	case PROP_RESPONSE_HEADERS:
 		g_value_set_boxed (value, msg->response_headers);
 		break;
+	case PROP_TLS_CERTIFICATE:
+		g_value_set_object (value, priv->tls_certificate);
+		break;
+	case PROP_TLS_ERRORS:
+		g_value_set_flags (value, priv->tls_errors);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1315,10 +1363,18 @@ soup_message_cleanup_response (SoupMessage *req)
 	}
 	priv->http_version = priv->orig_http_version;
 
+	if (priv->tls_certificate) {
+		g_object_unref (priv->tls_certificate);
+		priv->tls_certificate = NULL;
+	}
+	priv->tls_errors = 0;
+
 	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_STATUS_CODE);
 	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_REASON_PHRASE);
 	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_HTTP_VERSION);
 	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_FLAGS);
+	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_TLS_CERTIFICATE);
+	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_TLS_ERRORS);
 }
 
 /**
@@ -1776,3 +1832,35 @@ soup_message_set_first_party (SoupMessage *msg,
 	priv->first_party = soup_uri_copy (first_party);
 	g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_FIRST_PARTY);
 }
+
+/**
+ * soup_message_get_https_status:
+ * @msg: a #SoupMessage
+ * @certificate: (out) (transfer none): @msg's TLS certificate
+ * @errors: (out): the verification status of @certificate
+ *
+ * If @msg is using https, this retrieves the #GTlsCertificate
+ * associated with its connection, and the #GTlsCertificateFlags showing
+ * what problems, if any, have been found with that certificate.
+ *
+ * Return value: %TRUE if @msg uses https, %FALSE if not
+ *
+ * Since: 2.34
+ */
+gboolean
+soup_message_get_https_status (SoupMessage           *msg,
+			       GTlsCertificate      **certificate,
+			       GTlsCertificateFlags  *errors)
+{
+	SoupMessagePrivate *priv;
+
+	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
+
+	priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+	if (certificate)
+		*certificate = priv->tls_certificate;
+	if (errors)
+		*errors = priv->tls_errors;
+	return priv->tls_certificate != NULL;
+}
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 4f2d66d..1750fcd 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -72,6 +72,8 @@ GType soup_message_get_type (void);
 #define SOUP_MESSAGE_REQUEST_HEADERS  "request-headers"
 #define SOUP_MESSAGE_RESPONSE_BODY    "response-body"
 #define SOUP_MESSAGE_RESPONSE_HEADERS "response-headers"
+#define SOUP_MESSAGE_TLS_CERTIFICATE  "tls-certificate"
+#define SOUP_MESSAGE_TLS_ERRORS       "tls-errors"
 
 SoupMessage   *soup_message_new                 (const char        *method,
 						 const char        *uri_string);
@@ -108,6 +110,7 @@ SoupAddress     *soup_message_get_address         (SoupMessage       *msg);
 SoupURI         *soup_message_get_first_party     (SoupMessage       *msg);
 void             soup_message_set_first_party     (SoupMessage       *msg,
 						   SoupURI           *first_party);
+
 typedef enum {
 	SOUP_MESSAGE_NO_REDIRECT          = (1 << 1),
 #ifndef LIBSOUP_DISABLE_DEPRECATED
@@ -117,10 +120,15 @@ typedef enum {
 	SOUP_MESSAGE_CERTIFICATE_TRUSTED  = (1 << 5)
 } SoupMessageFlags;
 
-void           soup_message_set_flags           (SoupMessage        *msg,
-						 SoupMessageFlags    flags);
+void             soup_message_set_flags           (SoupMessage           *msg,
+						   SoupMessageFlags       flags);
+
+SoupMessageFlags soup_message_get_flags           (SoupMessage           *msg);
+
+gboolean         soup_message_get_https_status    (SoupMessage           *msg,
+						   GTlsCertificate      **certificate,
+						   GTlsCertificateFlags  *errors);
 
-SoupMessageFlags soup_message_get_flags         (SoupMessage        *msg);
 
 /* Specialized signal handlers */
 guint          soup_message_add_header_handler  (SoupMessage       *msg,
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index c7d5716..96545ea 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -57,6 +57,8 @@ enum {
 	PROP_TIMEOUT,
 	PROP_TRUSTED_CERTIFICATE,
 	PROP_CLEAN_DISPOSE,
+	PROP_TLS_CERTIFICATE,
+	PROP_TLS_ERRORS,
 
 	LAST_PROP
 };
@@ -67,11 +69,11 @@ typedef struct {
 	GSocket *gsock;
 	GPollableInputStream *istream;
 	GPollableOutputStream *ostream;
+	GTlsCertificateFlags tls_errors;
 
 	guint non_blocking:1;
 	guint is_server:1;
 	guint ssl_strict:1;
-	guint trusted_certificate:1;
 	guint clean_dispose:1;
 	gpointer ssl_creds;
 
@@ -354,9 +356,7 @@ soup_socket_class_init (SoupSocketClass *socket_class)
 	 * SOUP_SOCKET_TRUSTED_CERTIFICATE:
 	 *
 	 * Alias for the #SoupSocket:trusted-certificate
-	 * property. Notice that this property's value is only useful
-	 * if the socket is for an SSL connection, and only reliable
-	 * after some data has been transferred to or from it.
+	 * property.
 	 **/
 	g_object_class_install_property (
 		object_class, PROP_TRUSTED_CERTIFICATE,
@@ -364,7 +364,7 @@ soup_socket_class_init (SoupSocketClass *socket_class)
 				     "Trusted Certificate",
 				     "Whether the server certificate is trusted, if this is an SSL socket",
 				     FALSE,
-				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+				     G_PARAM_READABLE));
 	/**
 	 * SOUP_SOCKET_ASYNC_CONTEXT:
 	 *
@@ -399,6 +399,40 @@ soup_socket_class_init (SoupSocketClass *socket_class)
 				      "Warn on unclean dispose",
 				      FALSE,
 				      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+	/**
+	 * SOUP_SOCKET_TLS_CERTIFICATE:
+	 *
+	 * Alias for the #SoupSocket:tls-certificate
+	 * property. Note that this property's value is only useful
+	 * if the socket is for a TLS connection, and only reliable
+	 * after some data has been transferred to or from it.
+	 *
+	 * Since: 2.34
+	 **/
+	g_object_class_install_property (
+		object_class, PROP_TLS_CERTIFICATE,
+		g_param_spec_object (SOUP_SOCKET_TLS_CERTIFICATE,
+				     "TLS certificate",
+				     "The peer's TLS certificate",
+				     G_TYPE_TLS_CERTIFICATE,
+				     G_PARAM_READABLE));
+	/**
+	 * SOUP_SOCKET_TLS_ERRORS:
+	 *
+	 * Alias for the #SoupSocket:tls-errors
+	 * property. Note that this property's value is only useful
+	 * if the socket is for a TLS connection, and only reliable
+	 * after some data has been transferred to or from it.
+	 *
+	 * Since: 2.34
+	 **/
+	g_object_class_install_property (
+		object_class, PROP_TLS_ERRORS,
+		g_param_spec_flags (SOUP_SOCKET_TLS_ERRORS,
+				    "TLS errors",
+				    "Errors with the peer's TLS certificate",
+				    G_TYPE_TLS_CERTIFICATE_FLAGS, 0,
+				    G_PARAM_READABLE));
 }
 
 
@@ -440,9 +474,6 @@ set_property (GObject *object, guint prop_id,
 	case PROP_SSL_STRICT:
 		priv->ssl_strict = g_value_get_boolean (value);
 		break;
-	case PROP_TRUSTED_CERTIFICATE:
-		priv->trusted_certificate = g_value_get_boolean (value);
-		break;
 	case PROP_ASYNC_CONTEXT:
 		priv->async_context = g_value_get_pointer (value);
 		if (priv->async_context)
@@ -488,7 +519,7 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_boolean (value, priv->ssl_strict);
 		break;
 	case PROP_TRUSTED_CERTIFICATE:
-		g_value_set_boolean (value, priv->trusted_certificate);
+		g_value_set_boolean (value, priv->tls_errors == 0);
 		break;
 	case PROP_ASYNC_CONTEXT:
 		g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
@@ -496,6 +527,15 @@ get_property (GObject *object, guint prop_id,
 	case PROP_TIMEOUT:
 		g_value_set_uint (value, priv->timeout);
 		break;
+	case PROP_TLS_CERTIFICATE:
+		if (G_IS_TLS_CONNECTION (priv->conn))
+			g_value_set_object (value, g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (priv->conn)));
+		else
+			g_value_set_object (value, NULL);
+		break;
+	case PROP_TLS_ERRORS:
+		g_value_set_flags (value, priv->tls_errors);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -809,15 +849,14 @@ soup_socket_accept_certificate (GTlsConnection *conn, GTlsCertificate *cert,
 {
 	SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
 
+	priv->tls_errors = errors;
 	if (soup_ssl_credentials_verify_certificate (priv->ssl_creds,
-						     cert, errors))
-		return TRUE;
-
-	if (!priv->ssl_strict) {
-		priv->trusted_certificate = FALSE;
+						     cert, errors)) {
+		priv->tls_errors &= ~G_TLS_CERTIFICATE_UNKNOWN_CA;
 		return TRUE;
 	}
-	return FALSE;
+
+	return !priv->ssl_strict;
 }
 
 /**
@@ -881,7 +920,7 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
 		g_object_unref (priv->conn);
 		priv->conn = G_IO_STREAM (conn);
 
-		priv->trusted_certificate = TRUE;
+		priv->tls_errors = 0;
 		g_signal_connect (conn, "accept-certificate",
 				  G_CALLBACK (soup_socket_accept_certificate),
 				  sock);
diff --git a/libsoup/soup-socket.h b/libsoup/soup-socket.h
index 058e930..8761071 100644
--- a/libsoup/soup-socket.h
+++ b/libsoup/soup-socket.h
@@ -48,6 +48,8 @@ typedef struct {
 #define SOUP_SOCKET_TRUSTED_CERTIFICATE "trusted-certificate"
 #define SOUP_SOCKET_ASYNC_CONTEXT       "async-context"
 #define SOUP_SOCKET_TIMEOUT             "timeout"
+#define SOUP_SOCKET_TLS_CERTIFICATE     "tls-certificate"
+#define SOUP_SOCKET_TLS_ERRORS          "tls-errors"
 
 typedef void (*SoupSocketCallback)            (SoupSocket         *sock,
 					       guint               status,



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