On Thu, 28 May 2020 at 22:41:19 +0200, Salvatore Bonaccorso wrote:
> CVE-2020-13645[0]:
> | In GNOME glib-networking through 2.64.2, the implementation of
> | GTlsClientConnection skips hostname verification of the server's TLS
> | certificate if the application fails to specify the expected server
> | identity. This is in contrast to its intended documented behavior, to
> | fail the certificate verification. Applications that fail to provide
> | the server identity, including Balsa before 2.5.11 and 2.6.x before
> | 2.6.1, accept a TLS certificate if the certificate is valid for any
> | host.

A reproducer is available from
https://gitlab.gnome.org/GNOME/balsa/-/issues/34 (look for
test-tls.tar.xz).

Preconditions:
* Compile the test program from test-tls.tar.xz (attached)
* Have a test server with two names, and no valid cert for one of them.
  people.debian.org works: it has a valid cert for people.debian.org,
  but not for paradis.debian.org. (Check this with a browser first,
  in case SNI gets added later.)

Bad result: connecting without specifying a server identity succeeds.

$ sudo apt-get install glib-networking=2.58.0-2
$ ./test_server_valid people.debian.org:443 0
** Message: 09:16:43.133: set remote server_identity: no
** Message: 09:16:43.534: g_tls_connection_handshake(): OK (no error)
$ ./test_server_valid people.debian.org:443 1
** Message: 09:16:02.406: set remote server_identity: yes
** Message: 09:16:02.779: g_tls_connection_handshake(): OK (no error)
$ ./test_server_valid paradis.debian.org:443 0
** Message: 09:18:36.097: set remote server_identity: no
** Message: 09:18:36.495: g_tls_connection_handshake(): OK (no error)
$ ./test_server_valid paradis.debian.org:443 1
** Message: 09:18:37.722: set remote server_identity: yes
** Message: 09:18:38.132: cert_accept_cb: conn=0x561d080d8150, 
cert=0x7fdbcc005eb0 for 'people.debian.org', error=0x2
** Message: 09:18:38.132: g_tls_connection_handshake(): ERROR (Unacceptable TLS 
certificate)

Good result: the only way to avoid G_TLS_CERTIFICATE_BAD_IDENTITY is to
specify the server's name in the form that appears in its certificate.

$ sudo dpkg -i glib-networking_2.58.0-2+deb10u1_amd64.deb
$ ./test_server_valid people.debian.org:443 0
** Message: 09:20:01.640: set remote server_identity: no
** Message: 09:20:02.045: cert_accept_cb: conn=0x55afead25150, 
cert=0x7f1f3c005eb0 for 'people.debian.org', error=0x2
** Message: 09:20:02.045: g_tls_connection_handshake(): ERROR (Unacceptable TLS 
certificate)
$ ./test_server_valid people.debian.org:443 1
** Message: 09:20:03.338: set remote server_identity: yes
** Message: 09:20:03.737: g_tls_connection_handshake(): OK (no error)
$ ./test_server_valid paradis.debian.org:443 0 
** Message: 09:20:08.924: set remote server_identity: no
** Message: 09:20:09.332: cert_accept_cb: conn=0x5620d607a150, 
cert=0x7f38500066b0 for 'people.debian.org', error=0x2
** Message: 09:20:09.332: g_tls_connection_handshake(): ERROR (Unacceptable TLS 
certificate)
$ ./test_server_valid paradis.debian.org:443 1
** Message: 09:20:10.552: set remote server_identity: yes
** Message: 09:20:10.954: cert_accept_cb: conn=0x560a74e26150, 
cert=0x7f88e8005eb0 for 'people.debian.org', error=0x2
** Message: 09:20:10.955: g_tls_connection_handshake(): ERROR (Unacceptable TLS 
certificate)

(0x2 is G_TLS_CERTIFICATE_BAD_IDENTITY.)

    smcv
/*
gcc -Wall -O -g $(pkg-config --cflags glib-2.0 gio-2.0 gcr-3) test_server_valid.c -o test_server_valid \
	$(pkg-config --libs glib-2.0 gio-2.0 gcr-3)
*/

#include <stdlib.h>
#include <glib.h>
#include <gio/gio.h>
#define GCR_API_SUBJECT_TO_CHANGE
#include <gcr/gcr.h>

static gboolean
cert_accept_cb(GTlsConnection *conn, GTlsCertificate *peer_cert, GTlsCertificateFlags errors, gpointer user_data)
{
	GByteArray *der_data;
    GcrCertificate *gcr_cert;
    gchar *subject;

	g_object_get(peer_cert, "certificate", &der_data, NULL);
	gcr_cert = gcr_simple_certificate_new(der_data->data, der_data->len);
	g_byte_array_unref(der_data);
	subject = gcr_certificate_get_subject_name(gcr_cert);
	g_object_unref(gcr_cert);
	g_message("%s: conn=%p, cert=%p for '%s', error=0x%x", __func__, conn, peer_cert, subject, errors);
	g_free(subject);
	return FALSE;
}

int main(int argc, char **argv)
{
	GSocketClient *sock;
	GSocketConnectable *remote_address;
	GSocketConnection *plain_conn;
	GIOStream *tls_conn;
	gboolean set_ident;
	gboolean tls_ok;
	GError *error = NULL;

	if (argc < 3) {
		g_error("usage: %s connect 0|1 [cafile]", argv[0]);
	}
	set_ident = (atoi(argv[2]) != 0);

	sock = g_socket_client_new();
	remote_address = g_network_address_parse(argv[1], 65000, &error);
	if (remote_address == NULL) {
		g_error("g_network_address_parse(%s): %s", argv[1], error->message);
	}
	plain_conn = g_socket_client_connect(sock, remote_address, NULL, &error);
	if (plain_conn == NULL) {
		g_error("g_socket_client_connect(): %s", error->message);
	}
	g_message("set remote server_identity: %s", set_ident ? "yes" : "no");
	tls_conn = g_tls_client_connection_new(G_IO_STREAM(plain_conn), set_ident ? remote_address : NULL, &error);
	if (tls_conn == NULL) {
		g_error("g_tls_client_connection_new(): %s", error->message);
	}
	g_signal_connect(tls_conn, "accept-certificate", G_CALLBACK(cert_accept_cb), NULL);

	if (argc > 3) {
		GTlsDatabase *ca_certs;

		ca_certs = g_tls_file_database_new(argv[3], &error);
		if (ca_certs == NULL) {
			g_error("g_tls_file_database_new(%s): %s", argv[3], error->message);
		}
		g_message("using CA certs %s", argv[3]);
		g_tls_connection_set_database(G_TLS_CONNECTION(tls_conn), ca_certs);
	}

	tls_ok = g_tls_connection_handshake(G_TLS_CONNECTION(tls_conn), NULL, &error);
	g_message("g_tls_connection_handshake(): %s (%s)", tls_ok ? "OK" : "ERROR", (error != NULL) ? error->message : "no error");
	return 0;
}

Reply via email to