summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2010-12-06 14:05:15 +0100
committerDan Winship <danw@gnome.org>2010-12-07 11:23:06 +0100
commit12238c7d3afd751b98fe3204092c254310bf1e69 (patch)
tree37c5ec2fb43eca31f731c9e95f00ba6d0dda6589
parent97bacf3057b7c45c29a6d025c67a6dd588144e05 (diff)
downloadlibsoup-12238c7d3afd751b98fe3204092c254310bf1e69.tar.gz
Add SoupMessage:tls-certificate and SoupMessage:tls-errors
These provide more information about the certificate on the other end of a TLS connection.
-rw-r--r--libsoup/soup-message-io.c29
-rw-r--r--libsoup/soup-message-private.h3
-rw-r--r--libsoup/soup-message.c88
-rw-r--r--libsoup/soup-message.h14
-rw-r--r--libsoup/soup-socket.c71
-rw-r--r--libsoup/soup-socket.h2
6 files changed, 177 insertions, 30 deletions
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index e824b00a..a2d2ec57 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 2ea2d429..ce866dc8 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 cd136a3c..7d6626ac 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 4f2d66dd..1750fcd4 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 c7d5716c..96545ea4 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 058e9303..87610715 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,