[glib-networking/mcatanzaro/tls-info] tls: Implement protocol version and ciphersuite name accessors




commit eaeca8acd650567080c5f57869802ab8c5675fa5
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Thu Apr 29 16:19:48 2021 -0500

    tls: Implement protocol version and ciphersuite name accessors
    
    This allows the application to display protocol version and ciphersuite
    name.
    
    However, there is a problem. It is not implemented correctly for GnuTLS,
    because GnuTLS only supports returning a ciphersuite name for TLS 1.2
    and older ciphersuites. For TLS 1.3 we have to use
    gnutls_session_get_desc(), which provides much more information than
    just the ciphersuite name. So this commit is not correct, and we need to
    think harder about what we really want to do here.
    
    The OpenSSL implementation works fine, though.

 meson.build                                |  4 +--
 tls/base/gtlsconnection-base.c             | 46 +++++++++++++++++++++++++-----
 tls/base/gtlsconnection-base.h             |  2 ++
 tls/gnutls/gtlsclientconnection-gnutls.c   | 17 +++++++----
 tls/gnutls/gtlsconnection-gnutls.c         | 45 +++++++++++++++++++++++++----
 tls/openssl/gtlsclientconnection-openssl.c | 17 +++++++----
 tls/openssl/gtlsconnection-openssl.c       | 45 +++++++++++++++++++++++++----
 tls/tests/connection.c                     | 33 +++++++++++++++++++++
 8 files changed, 178 insertions(+), 31 deletions(-)
---
diff --git a/meson.build b/meson.build
index 768cdf2..41e6366 100644
--- a/meson.build
+++ b/meson.build
@@ -29,7 +29,7 @@ common_flags = [
   '-DG_LOG_USE_STRUCTURED',
   '-DLOCALE_DIR="@0@"'.format(localedir),
   '-DG_DISABLE_DEPRECATED',
-  '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_56'
+  '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_70'
 ]
 
 add_project_arguments(common_flags, language: 'c')
@@ -48,7 +48,7 @@ if host_system.contains('linux')
 endif
 
 # *** Check GLib GIO        ***
-glib_dep = dependency('glib-2.0', version: '>= 2.67.0',
+glib_dep = dependency('glib-2.0', version: '>= 2.69.0',
   fallback: ['glib', 'libglib_dep'])
 gio_dep = dependency('gio-2.0',
   fallback: ['glib', 'libgio_dep'])
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index 294728f..688b72d 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -99,7 +99,9 @@ typedef struct
   gboolean               peer_certificate_examined;
 
   gboolean               require_close_notify;
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
   GTlsRehandshakeMode    rehandshake_mode;
+G_GNUC_END_IGNORE_DEPRECATIONS
 
   /* need_handshake means the next claim_op() will get diverted into
    * an implicit handshake (unless it's an OP_HANDSHAKE or OP_CLOSE*).
@@ -158,6 +160,9 @@ typedef struct
 
   gchar        **advertised_protocols;
   gchar         *negotiated_protocol;
+
+  GTlsProtocolVersion  protocol_version;
+  gchar               *ciphersuite_name;
 } GTlsConnectionBasePrivate;
 
 static void g_tls_connection_base_dtls_connection_iface_init (GDtlsConnectionInterface *iface);
@@ -209,6 +214,8 @@ enum
   PROP_PEER_CERTIFICATE_ERRORS,
   PROP_ADVERTISED_PROTOCOLS,
   PROP_NEGOTIATED_PROTOCOL,
+  PROP_PROTOCOL_VERSION,
+  PROP_CIPHERSUITE_NAME
 };
 
 gboolean
@@ -279,6 +286,8 @@ g_tls_connection_base_finalize (GObject *object)
   g_clear_pointer (&priv->advertised_protocols, g_strfreev);
   g_clear_pointer (&priv->negotiated_protocol, g_free);
 
+  g_clear_pointer (&priv->ciphersuite_name, g_free);
+
   G_OBJECT_CLASS (g_tls_connection_base_parent_class)->finalize (object);
 }
 
@@ -348,6 +357,14 @@ g_tls_connection_base_get_property (GObject    *object,
       g_value_set_string (value, priv->negotiated_protocol);
       break;
 
+    case PROP_PROTOCOL_VERSION:
+      g_value_set_enum (value, priv->protocol_version);
+      break;
+
+    case PROP_CIPHERSUITE_NAME:
+      g_value_set_string (value, priv->ciphersuite_name);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -1593,12 +1610,16 @@ finish_handshake (GTlsConnectionBase  *tls,
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
   GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
   gchar *original_negotiated_protocol;
+  gchar *original_ciphersuite_name;
+  GTlsProtocolVersion original_protocol_version;
   gboolean success;
   GError *my_error = NULL;
 
   g_tls_log_debug (tls, "finishing TLS handshake");
 
   original_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
+  original_ciphersuite_name = g_steal_pointer (&priv->ciphersuite_name);
+  original_protocol_version = priv->protocol_version;
 
   success = g_task_propagate_boolean (task, &my_error);
   if (success)
@@ -1636,16 +1657,25 @@ finish_handshake (GTlsConnectionBase  *tls,
         }
     }
 
-  if (tls_class->complete_handshake)
-    {
-      /* If we already have an error, ignore further errors. */
-      tls_class->complete_handshake (tls, success, &priv->negotiated_protocol, my_error ? NULL : &my_error);
+  tls_class->complete_handshake (tls,
+                                 success, 
+                                 &priv->negotiated_protocol,
+                                 &priv->protocol_version,
+                                 &priv->ciphersuite_name,
+                                 /* If we already have an error, ignore further errors. */
+                                 my_error ? NULL : &my_error);
 
-      if (g_strcmp0 (original_negotiated_protocol, priv->negotiated_protocol) != 0)
-        g_object_notify (G_OBJECT (tls), "negotiated-protocol");
-    }
+  if (g_strcmp0 (original_negotiated_protocol, priv->negotiated_protocol) != 0)
+    g_object_notify (G_OBJECT (tls), "negotiated-protocol");
   g_free (original_negotiated_protocol);
 
+  if (original_protocol_version != priv->protocol_version)
+    g_object_notify (G_OBJECT (tls), "protocol-version");
+
+  if (g_strcmp0 (original_ciphersuite_name, priv->ciphersuite_name) != 0)
+    g_object_notify (G_OBJECT (tls), "ciphersuite-name");
+  g_free (original_ciphersuite_name);
+
   if (my_error && priv->started_handshake)
     priv->handshake_error = g_error_copy (my_error);
 
@@ -2738,6 +2768,8 @@ g_tls_connection_base_class_init (GTlsConnectionBaseClass *klass)
   g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE_ERRORS, "peer-certificate-errors");
   g_object_class_override_property (gobject_class, PROP_ADVERTISED_PROTOCOLS, "advertised-protocols");
   g_object_class_override_property (gobject_class, PROP_NEGOTIATED_PROTOCOL, "negotiated-protocol");
+  g_object_class_override_property (gobject_class, PROP_PROTOCOL_VERSION, "protocol-version");
+  g_object_class_override_property (gobject_class, PROP_CIPHERSUITE_NAME, "ciphersuite-name");
 }
 
 static void
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index 7312683..d837da4 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -75,6 +75,8 @@ struct _GTlsConnectionBaseClass
   void                        (*complete_handshake)         (GTlsConnectionBase   *tls,
                                                              gboolean              handshake_succeeded,
                                                              gchar               **negotiated_protocol,
+                                                             GTlsProtocolVersion  *protocol_version,
+                                                             gchar               **ciphersuite_name,
                                                              GError              **error);
 
   gboolean                    (*is_session_resumed)         (GTlsConnectionBase   *tls);
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index 6f4cd23..7df0488 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -493,16 +493,23 @@ g_tls_client_connection_gnutls_prepare_handshake (GTlsConnectionBase  *tls,
 }
 
 static void
-g_tls_client_connection_gnutls_complete_handshake (GTlsConnectionBase  *tls,
-                                                   gboolean             handshake_succeeded,
-                                                   gchar              **negotiated_protocol,
-                                                   GError             **error)
+g_tls_client_connection_gnutls_complete_handshake (GTlsConnectionBase   *tls,
+                                                   gboolean              handshake_succeeded,
+                                                   gchar               **negotiated_protocol,
+                                                   GTlsProtocolVersion  *protocol_version,
+                                                   gchar               **ciphersuite_name,
+                                                   GError              **error)
 {
   GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (tls);
   gnutls_session_t session;
   gnutls_protocol_t version;
 
-  G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->complete_handshake (tls, 
handshake_succeeded, negotiated_protocol, error);
+  G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->complete_handshake (tls,
+                                                                                                 
handshake_succeeded,
+                                                                                                 
negotiated_protocol,
+                                                                                                 
protocol_version,
+                                                                                                 
ciphersuite_name,
+                                                                                                 error);
 
   /* It may have changed during the handshake, but we have to wait until here
    * because we can't emit notifies on the handshake thread.
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index bc3f9c8..4d284b8 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -901,23 +901,56 @@ g_tls_connection_gnutls_handshake_thread_handshake (GTlsConnectionBase  *tls,
   return status;
 }
 
+static GTlsProtocolVersion
+glib_protocol_version_from_gnutls (gnutls_protocol_t protocol_version)
+{
+  switch (protocol_version)
+    {
+    case GNUTLS_SSL3:
+      return G_TLS_PROTOCOL_VERSION_SSL_3_0;
+    case GNUTLS_TLS1_0:
+      return G_TLS_PROTOCOL_VERSION_TLS_1_0;
+    case GNUTLS_TLS1_1:
+      return G_TLS_PROTOCOL_VERSION_TLS_1_1;
+    case GNUTLS_TLS1_2:
+      return G_TLS_PROTOCOL_VERSION_TLS_1_2;
+    case GNUTLS_TLS1_3:
+      return G_TLS_PROTOCOL_VERSION_TLS_1_3;       
+    case GNUTLS_DTLS0_9:
+      return G_TLS_PROTOCOL_VERSION_UNKNOWN;
+    case GNUTLS_DTLS1_0:
+      return G_TLS_PROTOCOL_VERSION_DTLS_1_0;
+    case GNUTLS_DTLS1_2:
+      return G_TLS_PROTOCOL_VERSION_DTLS_1_2;
+    default:
+      return G_TLS_PROTOCOL_VERSION_UNKNOWN;
+    }
+}
+
 static void
-g_tls_connection_gnutls_complete_handshake (GTlsConnectionBase  *tls,
-                                            gboolean             handshake_succeeded,
-                                            gchar              **negotiated_protocol,
-                                            GError             **error)
+g_tls_connection_gnutls_complete_handshake (GTlsConnectionBase   *tls,
+                                            gboolean              handshake_succeeded,
+                                            gchar               **negotiated_protocol,
+                                            GTlsProtocolVersion  *protocol_version,
+                                            gchar               **ciphersuite_name,
+                                            GError              **error)
 {
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
   gnutls_datum_t protocol;
 
-  if (handshake_succeeded &&
-      gnutls_alpn_get_selected_protocol (priv->session, &protocol) == 0 &&
+  if (!handshake_succeeded)
+    return;
+
+  if (gnutls_alpn_get_selected_protocol (priv->session, &protocol) == 0 &&
       protocol.size > 0)
     {
       g_assert (!*negotiated_protocol);
       *negotiated_protocol = g_strndup ((gchar *)protocol.data, protocol.size);
     }
+
+  *protocol_version = glib_protocol_version_from_gnutls (gnutls_protocol_get_version (priv->session));
+  *ciphersuite_name = gnutls_session_get_desc (priv->session);
 }
 
 static gboolean
diff --git a/tls/openssl/gtlsclientconnection-openssl.c b/tls/openssl/gtlsclientconnection-openssl.c
index 95428c6..8cf32bd 100644
--- a/tls/openssl/gtlsclientconnection-openssl.c
+++ b/tls/openssl/gtlsclientconnection-openssl.c
@@ -183,15 +183,22 @@ g_tls_client_connection_openssl_set_property (GObject      *object,
 }
 
 static void
-g_tls_client_connection_openssl_complete_handshake (GTlsConnectionBase  *tls,
-                                                    gboolean             handshake_succeeded,
-                                                    gchar              **negotiated_protocol,
-                                                    GError             **error)
+g_tls_client_connection_openssl_complete_handshake (GTlsConnectionBase   *tls,
+                                                    gboolean              handshake_succeeded,
+                                                    gchar               **negotiated_protocol,
+                                                    GTlsProtocolVersion  *protocol_version,
+                                                    gchar               **ciphersuite_name,
+                                                    GError              **error)
 {
   GTlsClientConnectionOpenssl *client = G_TLS_CLIENT_CONNECTION_OPENSSL (tls);
 
   if (G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_openssl_parent_class)->complete_handshake)
-    G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_openssl_parent_class)->complete_handshake (tls, 
handshake_succeeded, negotiated_protocol, error);
+    G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_openssl_parent_class)->complete_handshake (tls,
+                                                                                                    
handshake_succeeded,
+                                                                                                    
negotiated_protocol,
+                                                                                                    
protocol_version,
+                                                                                                    
ciphersuite_name,
+                                                                                                    error);
 
   /* It may have changed during the handshake, but we have to wait until here
    * because we can't emit notifies on the handshake thread.
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index 98bbb56..57be18e 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -358,14 +358,42 @@ g_tls_connection_openssl_prepare_handshake (GTlsConnectionBase  *tls,
       g_byte_array_unref (protocols);
     }
 }
+#endif
+
+static GTlsProtocolVersion
+glib_protocol_version_from_openssl (int protocol_version)
+{
+  switch (protocol_version)
+    {
+    case SSL3_VERSION:
+      return G_TLS_PROTOCOL_VERSION_SSL_3_0;
+    case TLS1_VERSION:
+      return G_TLS_PROTOCOL_VERSION_TLS_1_0;
+    case TLS1_1_VERSION:
+      return G_TLS_PROTOCOL_VERSION_TLS_1_1;
+    case TLS1_2_VERSION:
+      return G_TLS_PROTOCOL_VERSION_TLS_1_2;
+    case TLS1_3_VERSION:
+      return G_TLS_PROTOCOL_VERSION_TLS_1_3;
+    case DTLS1_VERSION:
+      return G_TLS_PROTOCOL_VERSION_DTLS_1_0;
+    case DTLS1_2_VERSION:
+      return G_TLS_PROTOCOL_VERSION_DTLS_1_2;
+    default:
+      return G_TLS_PROTOCOL_VERSION_UNKNOWN;
+    }
+}
 
 static void
-g_tls_connection_openssl_complete_handshake (GTlsConnectionBase  *tls,
-                                             gboolean             handshake_succeeded,
-                                             gchar              **negotiated_protocol,
-                                             GError             **error)
+g_tls_connection_openssl_complete_handshake (GTlsConnectionBase   *tls,
+                                             gboolean              handshake_succeeded,
+                                             gchar               **negotiated_protocol,
+                                             GTlsProtocolVersion  *protocol_version,
+                                             gchar               **ciphersuite_name,
+                                             GError              **error)
 {
   SSL *ssl;
+  SSL_SESSION *session;
   unsigned int len = 0;
   const unsigned char *data = NULL;
 
@@ -373,7 +401,9 @@ g_tls_connection_openssl_complete_handshake (GTlsConnectionBase  *tls,
     return;
 
   ssl = g_tls_connection_openssl_get_ssl (G_TLS_CONNECTION_OPENSSL (tls));
+  session = SSL_get_session (ssl);
 
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L || defined (LIBRESSL_VERSION_NUMBER)
   SSL_get0_alpn_selected (ssl, &data, &len);
 
   g_tls_log_debug (tls, "negotiated ALPN protocols: [%d]%p", len, data);
@@ -383,9 +413,12 @@ g_tls_connection_openssl_complete_handshake (GTlsConnectionBase  *tls,
       g_assert (!*negotiated_protocol);
       *negotiated_protocol = g_strndup ((gchar *)data, len);
     }
-}
 #endif
 
+  *protocol_version = glib_protocol_version_from_openssl (SSL_SESSION_get_protocol_version (session));
+  *ciphersuite_name = g_strdup (SSL_get_cipher_name (ssl));
+}
+
 #define BEGIN_OPENSSL_IO(openssl, direction, timeout, cancellable)          \
   do {                                                                      \
     char error_str[256];                                                    \
@@ -855,8 +888,8 @@ g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L || defined (LIBRESSL_VERSION_NUMBER)
   base_class->prepare_handshake                          = g_tls_connection_openssl_prepare_handshake;
-  base_class->complete_handshake                         = g_tls_connection_openssl_complete_handshake;
 #endif
+  base_class->complete_handshake                         = g_tls_connection_openssl_complete_handshake;
   base_class->handshake_thread_safe_renegotiation_status = 
g_tls_connection_openssl_handshake_thread_safe_renegotiation_status;
   base_class->handshake_thread_request_rehandshake       = 
g_tls_connection_openssl_handshake_thread_request_rehandshake;
   base_class->handshake_thread_handshake                 = 
g_tls_connection_openssl_handshake_thread_handshake;
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 036df04..6be8ee1 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -2913,6 +2913,37 @@ test_peer_certificate_notify (TestConnection *test,
 #endif
 }
 
+static void
+test_tls_info (TestConnection *test,
+               gconstpointer   data)
+{
+  GIOStream *connection;
+  GError *error = NULL;
+
+  connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_assert_no_error (error);
+  g_object_unref (connection);
+
+  g_assert_cmpint (g_tls_connection_get_protocol_version (G_TLS_CONNECTION (test->client_connection)), ==, 
G_TLS_PROTOCOL_VERSION_UNKNOWN);
+  g_assert_null (g_tls_connection_get_ciphersuite_name (G_TLS_CONNECTION (test->client_connection)));
+
+  /* No validation at all in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                0);
+
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+  wait_until_server_finished (test);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+
+  g_assert_cmpint (g_tls_connection_get_protocol_version (G_TLS_CONNECTION (test->client_connection)), !=, 
G_TLS_PROTOCOL_VERSION_UNKNOWN);
+  g_assert_nonnull (g_tls_connection_get_ciphersuite_name (G_TLS_CONNECTION (test->client_connection)));
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -3043,6 +3074,8 @@ main (int   argc,
               setup_connection, test_connection_binding_match_tls_server_end_point, teardown_connection);
   g_test_add ("/tls/" BACKEND "/connection/binding/match-tls-exporter", TestConnection, NULL,
               setup_connection, test_connection_binding_match_tls_exporter, teardown_connection);
+  g_test_add ("/tls/" BACKEND "/connection/tls-info", TestConnection, NULL,
+              setup_connection, test_tls_info, teardown_connection);
 
   ret = g_test_run ();
 


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