[gnutls-devel] gnutls_record_send after incomplete gnutls_handshake sends data unencrypted

Andreas Metzler ametzler at bebt.de
Sat Dec 31 13:13:18 CET 2016


Hello,

find attached bug-report http://bugs.debian.org/849807 as reported by
Bernhard R. Link. The related bug against sssd-ldap is
<http://bugs.debian.org/849756>

cu Andreas

----- Forwarded message from "Bernhard R. Link" <brlink at debian.org> -----
Package: libgnutls30
Version: 3.5.7-3
Severity: normal
Tags: security

This bug report is not about wrong behavior if libgnutls is called
correctly but rather about dangerous behaviour if the caller is using
libgnutls incorrectly.

If a handshake has not yet completed (the caller ignoring
gnutls_handshake return code or the caller having a bug in the handling
of GNUTLS_E_AGAIN) then telling libgnutls to send data causes it to send
it unencrypted. Unless there are cases where might be useful, I think a
security relevelant library like libgnutls should rather catch this
mistake and avoid sending stuff unencrypted.

Here's an example:

cat <<'EOF' > example.c
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdio.h>
#include <error.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <gnutls/gnutls.h>

static ssize_t sim_write(gnutls_transport_ptr_t session, const void *data, size_t len) {
	bool found = memmem(data, len, "SECRET", 6) != NULL;
	printf("Would have written %d bytes%s.\n", (int)len, found?" containing unencrypted plain-text secret":"");
	fflush(stdout);
	return len;
}
static ssize_t sim_read(gnutls_transport_ptr_t session, void *data, size_t len) {
	/* simulate non-blocking io with no incoming data arrived yet */
	gnutls_transport_set_errno(session, EAGAIN);
	fflush(stdout);
	return -1;
}

int main() {
	gnutls_certificate_credentials_t xcred;
	gnutls_session_t session;
	int r;

	r = gnutls_global_init();
	assert (r == GNUTLS_E_SUCCESS);
	r = gnutls_certificate_allocate_credentials(&xcred);
	assert (r == GNUTLS_E_SUCCESS);
	r = gnutls_init(&session, GNUTLS_CLIENT);
	assert (r == GNUTLS_E_SUCCESS);
	r = gnutls_set_default_priority(session);
	assert (r == GNUTLS_E_SUCCESS);
	r = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
	assert (r == GNUTLS_E_SUCCESS);
	gnutls_session_set_verify_cert(session, "server", 0);

	gnutls_transport_set_ptr(session, session);
	gnutls_transport_set_push_function(session, sim_write);
	gnutls_transport_set_pull_function(session, sim_read);

	r = gnutls_handshake(session);
	assert (r == GNUTLS_E_AGAIN);
	/* ignoring the return code and doing the sending: */
	r = gnutls_record_send(session, "SECRET\n", 7);
	printf("gnutls_record_send returned %d\n", r);

	return 0;
}
EOF
gcc -Wall -O2 -g example.c -lgnutls && ./a.out

It outputs:

Would have written 238 bytes.
Would have written 12 bytes containing unencrypted plain-text secret.
gnutls_record_send returned 7

i.e. the data is send unencrypted (looking at the output one sees a
CLIENT_HELO followed by an APPLICATION_DATA packet with unencrypted
content).

One example where this happens is libldap, which runs into this if
gotten an non-blocking fd (as currently sssd does, see #849756),
causing sssd-ldap to corrently sending passwords unencrypted.

	Bernhard R. Link
-- 
----- End forwarded message -----



More information about the Gnutls-devel mailing list