[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