[glib-networking/glib-2-54] gnutls: Fix using different client certs for different connections



commit c188f74cfc874833b79c457bac318f2a440580d4
Author: Martin Pitt <martin piware de>
Date:   Mon Apr 24 16:23:28 2017 +0200

    gnutls: Fix using different client certs for different connections
    
    Up to now, a GTlsClientConnectionGnutls' session ID was built only from
    the address and port. This led to overly aggressive caching of the TLS
    session data and ignored the set client certificate of any subsequent
    connection to the same server/port.
    
    Move computation of the session ID from _constructed() to
    _begin_handshake() when we actually need it; at that point we have the
    client certificate already set. Append the certificate's hash to the
    session ID to disambiguate connections with different client
    certificates while still retaining the caching for multiple connections
    with the same cert.
    
    Add a second client certificate with a different modulus to the test
    files and expand the connection /tls/connection/client-auth* tests to
    cover this case.
    
    Also extend /tls/connection/client-auth-failure to do a connection with
    a good certificate after a failed attempt without a cert, to ensure that
    our session caching doesn't attempt to re-use the failed session for
    that.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=781578

 tls/gnutls/gtlsclientconnection-gnutls.c |   33 +++++++++---
 tls/tests/connection.c                   |   80 ++++++++++++++++++++++++++++++
 tls/tests/files/client2-and-key.pem      |   45 +++++++++++++++++
 tls/tests/files/client2-key.pem          |   27 ++++++++++
 tls/tests/files/client2.pem              |   18 +++++++
 tls/tests/files/create-files.sh          |    8 +++
 6 files changed, 203 insertions(+), 8 deletions(-)
---
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index d5d63fa..26424ee 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -100,9 +100,8 @@ get_server_identity (GTlsClientConnectionGnutls *gnutls)
 }
 
 static void
-g_tls_client_connection_gnutls_constructed (GObject *object)
+g_tls_client_connection_gnutls_compute_session_id (GTlsClientConnectionGnutls *gnutls)
 {
-  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (object);
   GSocketConnection *base_conn;
   GSocketAddress *remote_addr;
   GInetAddress *iaddr;
@@ -124,24 +123,41 @@ g_tls_client_connection_gnutls_constructed (GObject *object)
          GInetSocketAddress *isaddr = G_INET_SOCKET_ADDRESS (remote_addr);
          const gchar *server_hostname;
          gchar *addrstr, *session_id;
+         GTlsCertificate *cert = NULL;
+         gchar *cert_hash = NULL;
 
          iaddr = g_inet_socket_address_get_address (isaddr);
          port = g_inet_socket_address_get_port (isaddr);
 
          addrstr = g_inet_address_to_string (iaddr);
          server_hostname = get_server_identity (gnutls);
-         session_id = g_strdup_printf ("%s/%s/%d", addrstr,
+
+         /* If we have a certificate, make its hash part of the session ID, so
+          * that different connections to the same server can use different
+          * certificates. */
+         g_object_get (G_OBJECT (gnutls), "certificate", &cert, NULL);
+         if (cert)
+           {
+             GByteArray *der = NULL;
+             g_object_get (G_OBJECT (cert), "certificate", &der, NULL);
+             if (der)
+               {
+                 cert_hash = g_compute_checksum_for_data (G_CHECKSUM_SHA256, der->data, der->len);
+                 g_byte_array_unref (der);
+               }
+             g_object_unref (cert);
+           }
+         session_id = g_strdup_printf ("%s/%s/%d/%s", addrstr,
                                        server_hostname ? server_hostname : "",
-                                       port);
+                                       port,
+                                       cert_hash ?: "");
          gnutls->priv->session_id = g_bytes_new_take (session_id, strlen (session_id));
          g_free (addrstr);
+         g_free (cert_hash);
        }
       g_object_unref (remote_addr);
     }
   g_object_unref (base_conn);
-
-  if (G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->constructed)
-    G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->constructed (object);
 }
 
 static void
@@ -326,6 +342,8 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
 {
   GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
 
+  g_tls_client_connection_gnutls_compute_session_id (gnutls);
+
   /* Try to get a cached session */
   if (gnutls->priv->session_data_override)
     {
@@ -434,7 +452,6 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas
 
   gobject_class->get_property = g_tls_client_connection_gnutls_get_property;
   gobject_class->set_property = g_tls_client_connection_gnutls_set_property;
-  gobject_class->constructed  = g_tls_client_connection_gnutls_constructed;
   gobject_class->finalize     = g_tls_client_connection_gnutls_finalize;
 
   connection_gnutls_class->failed           = g_tls_client_connection_gnutls_failed;
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index d2bf8cb..4aa4944 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -918,6 +918,7 @@ test_client_auth_connection (TestConnection *test,
   GTlsCertificate *cert;
   GTlsCertificate *peer;
   gboolean cas_changed;
+  GSocketClient *client;
 
   test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
   g_assert_no_error (error);
@@ -956,6 +957,36 @@ test_client_auth_connection (TestConnection *test,
   g_assert (cas_changed == TRUE);
 
   g_object_unref (cert);
+  g_object_unref (test->database);
+  g_object_unref (test->client_connection);
+
+  /* Now start a new connection to the same server with a different client cert */
+  client = g_socket_client_new ();
+  connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                                     NULL, &error));
+  g_assert_no_error (error);
+  g_object_unref (client);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_object_unref (connection);
+
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                0);
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("client2-and-key.pem"), &error);
+  g_assert_no_error (error);
+  g_tls_connection_set_certificate (G_TLS_CONNECTION (test->client_connection), cert);
+  g_object_unref (cert);
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+
+  /* peer should see the second client cert */
+  peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (test->server_connection));
+  g_assert (peer != NULL);
+  g_assert (g_tls_certificate_is_same (peer, cert));
 }
 
 static void
@@ -973,6 +1004,10 @@ test_client_auth_failure (TestConnection *test,
   GIOStream *connection;
   GError *error = NULL;
   gboolean accepted_changed;
+  GSocketClient *client;
+  GTlsCertificate *cert;
+  GTlsCertificate *peer;
+  GTlsInteraction *interaction;
 
   test->database = g_tls_file_database_new (tls_test_file_path ("ca-roots.pem"), &error);
   g_assert_no_error (error);
@@ -1003,6 +1038,51 @@ test_client_auth_failure (TestConnection *test,
   g_assert_error (test->server_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED);
 
   g_assert (accepted_changed == TRUE);
+
+  g_object_unref (test->client_connection);
+  g_object_unref (test->database);
+  g_clear_error (&test->read_error);
+  g_clear_error (&test->server_error);
+
+  /* Now start a new connection to the same server with a valid client cert;
+   * this should succeed, and not use the cached failed session from above */
+  client = g_socket_client_new ();
+  connection = G_IO_STREAM (g_socket_client_connect (client, G_SOCKET_CONNECTABLE (test->address),
+                                                     NULL, &error));
+  g_assert_no_error (error);
+  g_object_unref (client);
+  test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+  g_object_unref (connection);
+
+  g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+  /* Have the interaction return a certificate */
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("client-and-key.pem"), &error);
+  g_assert_no_error (error);
+  interaction = mock_interaction_new_static_certificate (cert);
+  g_tls_connection_set_interaction (G_TLS_CONNECTION (test->client_connection), interaction);
+  g_object_unref (interaction);
+
+  /* All validation in this test */
+  g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+                                                G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+  accepted_changed = FALSE;
+  g_signal_connect (test->client_connection, "notify::accepted-cas",
+                    G_CALLBACK (on_notify_accepted_cas), &accepted_changed);
+
+  read_test_data_async (test);
+  g_main_loop_run (test->loop);
+
+  g_assert_no_error (test->read_error);
+  g_assert_no_error (test->server_error);
+
+  peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (test->server_connection));
+  g_assert (peer != NULL);
+  g_assert (g_tls_certificate_is_same (peer, cert));
+  g_assert (accepted_changed == TRUE);
+
+  g_object_unref (cert);
 }
 
 static void
diff --git a/tls/tests/files/client2-and-key.pem b/tls/tests/files/client2-and-key.pem
new file mode 100644
index 0000000..0f1e98d
--- /dev/null
+++ b/tls/tests/files/client2-and-key.pem
@@ -0,0 +1,45 @@
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAkUCAQEwDQYJKoZIhvcNAQELBQAwgYYxEzARBgoJkiaJk/IsZAEZFgND
+T00xFzAVBgoJkiaJk/IsZAEZFgdFWEFNUExFMR4wHAYDVQQLDBVDZXJ0aWZpY2F0
+ZSBBdXRob3JpdHkxFzAVBgNVBAMMDmNhLmV4YW1wbGUuY29tMR0wGwYJKoZIhvcN
+AQkBFg5jYUBleGFtcGxlLmNvbTAeFw0xNzA0MjEwODUwMjdaFw00MjA0MTUwODUw
+MjdaMGIxEzARBgoJkiaJk/IsZAEZFgNDT00xFzAVBgoJkiaJk/IsZAEZFgdFWEFN
+UExFMQ8wDQYDVQQDDAZDbGllbnQxITAfBgkqhkiG9w0BCQEWEmNsaWVudEBleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALcDxJFF30lE
+s/w8KN7ENjhaXosDqOe/IiNTStg+f1CL+/bKdcSu8wTyQsltv/+fi/jiw4ls1chP
+Eni7r+HtkPdMVG5mvuaGzPFyVCl1YkOihS0v0+YQkm83LcC7jU2+dvrDQYMFWH9d
+ehfFur9Cwmur5EH5Xl2nKehYifMYmljh/jPMPdCj6xWGUN6p/k459hz1JGvfWlqa
+1o/35j9uakjBtsFeOKT5SEZsg2S9DudYsHfC/ncXl1LrJCFzQBY1ifhDr3FHrEsM
+ieoAi4+qNq1ZZxwzppB81MAyggaJmqHUo/IpvJxWF8AhK202skiDYURk7k1kyT7g
+ZWRwl72Wz/kCAwEAATANBgkqhkiG9w0BAQsFAAOBgQAPiq0yRWbFD+17cz5TrRsG
+kapRrVmwqpX4nrv+pvBgzT6aatV5SZDXq+jqw1L2n2B/DEUwDXJ0R8vV0J2hE/KE
++7YRWgM0LyT/iYp5CC1n3GKpirxarz5hwXHVmNsMnYjLRc/4NVGkyDOqTZIPN106
+C8hyhUIMHGLBqWMxCG7myQ==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAtwPEkUXfSUSz/Dwo3sQ2OFpeiwOo578iI1NK2D5/UIv79sp1
+xK7zBPJCyW2//5+L+OLDiWzVyE8SeLuv4e2Q90xUbma+5obM8XJUKXViQ6KFLS/T
+5hCSbzctwLuNTb52+sNBgwVYf116F8W6v0LCa6vkQfleXacp6FiJ8xiaWOH+M8w9
+0KPrFYZQ3qn+Tjn2HPUka99aWprWj/fmP25qSMG2wV44pPlIRmyDZL0O51iwd8L+
+dxeXUuskIXNAFjWJ+EOvcUesSwyJ6gCLj6o2rVlnHDOmkHzUwDKCBomaodSj8im8
+nFYXwCErbTaySINhRGTuTWTJPuBlZHCXvZbP+QIDAQABAoIBAQC2hfJ39BiRiQx8
+Jj+YlFWC9FXQDNFad1wDoSFG82WkHkgnRJoZk2XZbAfBvkw7E5LUoMvk9f9sK7g/
+Yugxye3HRX/7L0t6u7wPnTdktaZPz/lELKwHikWQ15Yw3pw5ihg9VZizpBQzyjVn
+GhN6holCOweF6L79Zic8i3jhUos00lciPtKgH2fMXDL/CMMsHsd+Nr52sWmsXTpG
+Rv5p64O2RnDRP555HTzlgkgrsFUuyrR7/BAGa6j9XyXN+9HjybOpwcMLDlvrXrZH
+pxUeneJZIGRUIrF3c3iSVXbPHfySp/WWPD+zobXGnw1uyeWW7YPjFUu7ABATt84j
+KNS5Uz5RAoGBANnw1TboXUoFKcMp8fUidZT5n1AbqqFj2KgMuMN0lUha2Fnd40a1
+yFZbO8P7RDTlQT++krH6jSV1VHfPaN4CQrfC7XQDkGjEc95Eyi0fNSuHpZirX5Ci
+/fsoryls6Kkd62SPA8jDRvbv+EYRxKT8uIQsy871UDcIaHs8+IBnti+tAoGBANb5
+i+HK9Y65DuA/6W26oBW2yGLyRaIoZDWinE7LA1EiZBWwHmXyw602egjUsKRGPCKI
+DCjFpLts13ZvEprTIev0enhmk/tulZ/p0+lcuY0Dd6nfTZVhejJjaLE8o5SgxXGr
+U3UEemW78T9DEA3tZjt8hu8YzYWRCvLPa+8kPrr9AoGAeM+I0cQjGoocKWSSDKoK
+dged6YE8p/Q6QIW00hxJOG+raL2YZDUWldBDJBOgLpY7AkP4+5IBNheBOF0QK6kj
+JMx4ZownO/xSoo6NaE/ZYIT0JdoxwnKnydc2qgcGPeEpAHhKx7qAFxjVDrqAwFib
+TCGs5M+VpLwTduVId52GH40CgYEAoFVIdeP41zTAmpIwWC2b3fYQaHPHaZT0gGhC
+aiXR2H5s5RwQ3/p65MI/rDxtbmgPy7VqVDJslXktDeDzoFOd9izF9uySrDEjGTy9
+V0xX+4s9gY3RgHtONyybVa0jV+O8vvWH7jujyiKtYIB1Bd4spGtQ/ByklFzELKp1
+FswSmUUCgYA9lZ4bEbfFUVCTYJyLlNCFBvDhD+FaWHZ3fQOM/w0jRQVz1B1Y7BO+
+r9Uih1rcUYHUBKE61GiJh3uFFD2IwCkhICvdof0B0WsnoEuSvoTwe6pAGZbKdonY
+ATM1joBOoLbgDIrE2aESBXztMErJ2Lx+q1kPbULF6cEfvYnDdQTM1Q==
+-----END RSA PRIVATE KEY-----
diff --git a/tls/tests/files/client2-key.pem b/tls/tests/files/client2-key.pem
new file mode 100644
index 0000000..26964c6
--- /dev/null
+++ b/tls/tests/files/client2-key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAtwPEkUXfSUSz/Dwo3sQ2OFpeiwOo578iI1NK2D5/UIv79sp1
+xK7zBPJCyW2//5+L+OLDiWzVyE8SeLuv4e2Q90xUbma+5obM8XJUKXViQ6KFLS/T
+5hCSbzctwLuNTb52+sNBgwVYf116F8W6v0LCa6vkQfleXacp6FiJ8xiaWOH+M8w9
+0KPrFYZQ3qn+Tjn2HPUka99aWprWj/fmP25qSMG2wV44pPlIRmyDZL0O51iwd8L+
+dxeXUuskIXNAFjWJ+EOvcUesSwyJ6gCLj6o2rVlnHDOmkHzUwDKCBomaodSj8im8
+nFYXwCErbTaySINhRGTuTWTJPuBlZHCXvZbP+QIDAQABAoIBAQC2hfJ39BiRiQx8
+Jj+YlFWC9FXQDNFad1wDoSFG82WkHkgnRJoZk2XZbAfBvkw7E5LUoMvk9f9sK7g/
+Yugxye3HRX/7L0t6u7wPnTdktaZPz/lELKwHikWQ15Yw3pw5ihg9VZizpBQzyjVn
+GhN6holCOweF6L79Zic8i3jhUos00lciPtKgH2fMXDL/CMMsHsd+Nr52sWmsXTpG
+Rv5p64O2RnDRP555HTzlgkgrsFUuyrR7/BAGa6j9XyXN+9HjybOpwcMLDlvrXrZH
+pxUeneJZIGRUIrF3c3iSVXbPHfySp/WWPD+zobXGnw1uyeWW7YPjFUu7ABATt84j
+KNS5Uz5RAoGBANnw1TboXUoFKcMp8fUidZT5n1AbqqFj2KgMuMN0lUha2Fnd40a1
+yFZbO8P7RDTlQT++krH6jSV1VHfPaN4CQrfC7XQDkGjEc95Eyi0fNSuHpZirX5Ci
+/fsoryls6Kkd62SPA8jDRvbv+EYRxKT8uIQsy871UDcIaHs8+IBnti+tAoGBANb5
+i+HK9Y65DuA/6W26oBW2yGLyRaIoZDWinE7LA1EiZBWwHmXyw602egjUsKRGPCKI
+DCjFpLts13ZvEprTIev0enhmk/tulZ/p0+lcuY0Dd6nfTZVhejJjaLE8o5SgxXGr
+U3UEemW78T9DEA3tZjt8hu8YzYWRCvLPa+8kPrr9AoGAeM+I0cQjGoocKWSSDKoK
+dged6YE8p/Q6QIW00hxJOG+raL2YZDUWldBDJBOgLpY7AkP4+5IBNheBOF0QK6kj
+JMx4ZownO/xSoo6NaE/ZYIT0JdoxwnKnydc2qgcGPeEpAHhKx7qAFxjVDrqAwFib
+TCGs5M+VpLwTduVId52GH40CgYEAoFVIdeP41zTAmpIwWC2b3fYQaHPHaZT0gGhC
+aiXR2H5s5RwQ3/p65MI/rDxtbmgPy7VqVDJslXktDeDzoFOd9izF9uySrDEjGTy9
+V0xX+4s9gY3RgHtONyybVa0jV+O8vvWH7jujyiKtYIB1Bd4spGtQ/ByklFzELKp1
+FswSmUUCgYA9lZ4bEbfFUVCTYJyLlNCFBvDhD+FaWHZ3fQOM/w0jRQVz1B1Y7BO+
+r9Uih1rcUYHUBKE61GiJh3uFFD2IwCkhICvdof0B0WsnoEuSvoTwe6pAGZbKdonY
+ATM1joBOoLbgDIrE2aESBXztMErJ2Lx+q1kPbULF6cEfvYnDdQTM1Q==
+-----END RSA PRIVATE KEY-----
diff --git a/tls/tests/files/client2.pem b/tls/tests/files/client2.pem
new file mode 100644
index 0000000..f5696e2
--- /dev/null
+++ b/tls/tests/files/client2.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAkUCAQEwDQYJKoZIhvcNAQELBQAwgYYxEzARBgoJkiaJk/IsZAEZFgND
+T00xFzAVBgoJkiaJk/IsZAEZFgdFWEFNUExFMR4wHAYDVQQLDBVDZXJ0aWZpY2F0
+ZSBBdXRob3JpdHkxFzAVBgNVBAMMDmNhLmV4YW1wbGUuY29tMR0wGwYJKoZIhvcN
+AQkBFg5jYUBleGFtcGxlLmNvbTAeFw0xNzA0MjEwODUwMjdaFw00MjA0MTUwODUw
+MjdaMGIxEzARBgoJkiaJk/IsZAEZFgNDT00xFzAVBgoJkiaJk/IsZAEZFgdFWEFN
+UExFMQ8wDQYDVQQDDAZDbGllbnQxITAfBgkqhkiG9w0BCQEWEmNsaWVudEBleGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALcDxJFF30lE
+s/w8KN7ENjhaXosDqOe/IiNTStg+f1CL+/bKdcSu8wTyQsltv/+fi/jiw4ls1chP
+Eni7r+HtkPdMVG5mvuaGzPFyVCl1YkOihS0v0+YQkm83LcC7jU2+dvrDQYMFWH9d
+ehfFur9Cwmur5EH5Xl2nKehYifMYmljh/jPMPdCj6xWGUN6p/k459hz1JGvfWlqa
+1o/35j9uakjBtsFeOKT5SEZsg2S9DudYsHfC/ncXl1LrJCFzQBY1ifhDr3FHrEsM
+ieoAi4+qNq1ZZxwzppB81MAyggaJmqHUo/IpvJxWF8AhK202skiDYURk7k1kyT7g
+ZWRwl72Wz/kCAwEAATANBgkqhkiG9w0BAQsFAAOBgQAPiq0yRWbFD+17cz5TrRsG
+kapRrVmwqpX4nrv+pvBgzT6aatV5SZDXq+jqw1L2n2B/DEUwDXJ0R8vV0J2hE/KE
++7YRWgM0LyT/iYp5CC1n3GKpirxarz5hwXHVmNsMnYjLRc/4NVGkyDOqTZIPN106
+C8hyhUIMHGLBqWMxCG7myQ==
+-----END CERTIFICATE-----
diff --git a/tls/tests/files/create-files.sh b/tls/tests/files/create-files.sh
index 0a7140f..a887562 100755
--- a/tls/tests/files/create-files.sh
+++ b/tls/tests/files/create-files.sh
@@ -124,6 +124,14 @@ openssl x509 -req -in client-csr.pem -days 365 -startdate -enddate -CA ca.pem -C
 sudo hwclock -s
 touch client-future.pem
 
+msg "Creating second client key pair"
+openssl genrsa -out client2-key.pem 2048
+openssl req -config ssl/client.conf -key client2-key.pem -new -out client2-csr.pem
+openssl x509 -req -in client2-csr.pem -days 9125 -CA ca.pem -CAkey ca-key.pem -CAserial serial -out 
client2.pem
+
+msg "Concatenating second client certificate and private key into a single file"
+cat client2.pem client2-key.pem > client2-and-key.pem
+
 #######################################################################
 ### Concatenate all non-CA certificates
 #######################################################################


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