diff options
author | Dan Winship <danw@gnome.org> | 2010-12-06 14:05:15 +0100 |
---|---|---|
committer | Dan Winship <danw@gnome.org> | 2010-12-07 11:23:06 +0100 |
commit | 12238c7d3afd751b98fe3204092c254310bf1e69 (patch) | |
tree | 37c5ec2fb43eca31f731c9e95f00ba6d0dda6589 | |
parent | 97bacf3057b7c45c29a6d025c67a6dd588144e05 (diff) | |
download | libsoup-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.c | 29 | ||||
-rw-r--r-- | libsoup/soup-message-private.h | 3 | ||||
-rw-r--r-- | libsoup/soup-message.c | 88 | ||||
-rw-r--r-- | libsoup/soup-message.h | 14 | ||||
-rw-r--r-- | libsoup/soup-socket.c | 71 | ||||
-rw-r--r-- | 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 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, |