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; }