Commit 8da92fd6 authored by Martin Pitt's avatar Martin Pitt Committed by Michael Catanzaro

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
parent 3cbb931c
......@@ -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;
......
......@@ -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
......
-----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-----
-----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-----
-----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-----
......@@ -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
#######################################################################
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment