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




commit 59e0da9e9e8d29ae296845dadca716c66d780702
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. This implements the APIs added in glib!2077.

 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         | 64 +++++++++++++++++++++++++++---
 tls/openssl/gtlsclientconnection-openssl.c | 17 +++++---
 tls/openssl/gtlsconnection-openssl.c       | 45 ++++++++++++++++++---
 tls/tests/connection.c                     | 33 +++++++++++++++
 8 files changed, 197 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..efef0f1 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..c6d6c40 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -901,23 +901,75 @@ 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 gchar *
+get_ciphersuite_name (gnutls_session_t session)
+{
+  gnutls_protocol_t protocol_version = gnutls_protocol_get_version (session);
+
+  if (protocol_version <= GNUTLS_TLS1_2 ||
+      protocol_version >= GNUTLS_DTLS0_9 && protocol_version <= GNUTLS_DTLS1_2)
+    {
+      return g_strdup (gnutls_cipher_suite_get_name (gnutls_kx_get (session),
+                                                     gnutls_cipher_get (session),
+                                                     gnutls_mac_get (session));
+    }
+
+  return g_strdup_printf ("TLS_%s_%s",
+                          gnutls_cipher_get_name (gnutls_cipher_get (session)),
+                          gnutls_digest_get_name (gnutls_prf_hash_get (session)));
+
+}
+
 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 = get_ciphersuite_name (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]