Add SoupMessage:tls-certificate and SoupMessage:tls-errors
authorDan Winship <danw@gnome.org>
Mon, 6 Dec 2010 13:05:15 +0000 (14:05 +0100)
committerDan Winship <danw@gnome.org>
Tue, 7 Dec 2010 10:23:06 +0000 (11:23 +0100)
These provide more information about the certificate on the other end of
a TLS connection.

libsoup/soup-message-io.c
libsoup/soup-message-private.h
libsoup/soup-message.c
libsoup/soup-message.h
libsoup/soup-socket.c
libsoup/soup-socket.h

index e824b00..a2d2ec5 100644 (file)
@@ -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.
index 2ea2d42..ce866dc 100644 (file)
@@ -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))
 
index cd136a3..7d6626a 100644 (file)
@@ -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;
+}
index 4f2d66d..1750fcd 100644 (file)
@@ -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,
index c7d5716..96545ea 100644 (file)
@@ -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);
index 058e930..8761071 100644 (file)
@@ -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,