-
Notifications
You must be signed in to change notification settings - Fork 467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Denial of Service in SSL negotiation #2091
Comments
CUPS.org User: nathanw I also wanted to note that this occurs in CUPS 1.1.23 if you connect on an SSL port (where sending any single byte at all causes the connection to hang), so it's really an issue in the SSL initiation code, not the auto-detection logic. CUPS 1.2's SSL auto-detection (and automatic enabling of SSL) just provides more ports on which this can happen. |
CUPS.org User: nathanw I've also verified this behavior with CUPS 1.2.4 on the current release of Ubuntu Linux, so this appears to affect all versions of CUPS on all operating systems. |
CUPS.org User: mike Thanks for the report. I'm not sure how we are going to address this issue - I need to do some research on the GNU TLS and OpenSSL libraries to see if they support a timeout mechanism. If not, we'll have to change the wrapping of the transport stuff to do the timeouts for us. I'll also check with Apple to see if CDSA is similarly affected. Unfortunately, we can't know how much data is required outside of those libraries, and using non-blocking mode on the socket will just cause a quick failure... :( |
CUPS.org User: mike Try the attached patch; it sets the OpenSSL timeout parameter and adds pull/push functions for GNU TLS to provide a 10-second timeout for SSL reads. |
CUPS.org User: nathanw The patch causes cupsd to segfault on connect. I tried reducing it to just adding the SSL_set_timeout() command in client.c, and adding or removing that line causes the crash (I haven't tried it with GnuTLS -- I'll give that a shot later). Here's the backtrace: Program received signal SIGSEGV, Segmentation fault. This patch, while it would solve my problem of accidental DoS attacks, seems like it would still allow a malicious attacker to retry at 10 second intervals. Is it possible to put the SSL BIO into non-blocking mode just for the duration of encrypt_client(), then try to leave the socket in whatever state it was in case of an error indicating it needs more data? We still have the problem that an attacker could trigger an in-session renegotiation, then leave it hanging, which would make SSL_read() block somewhere else in the daemon, but it at least would make this problem harder to trigger. |
CUPS.org User: mike Basically, the current OpenSSL code for timeouts is completely broken, and the SSL_set_timeout was mapping to SSL_SESSION_set_timeout without getting the session pointer associated with the SSL structure, so in the end it clobbered some of the SSL structure data... :( We can't use non-blocking sockets because the corresponding interfaces are not 100% state-driven, so there is no way to do an async negotiation, etc. without erroring out due to even a tiny break in the data. As for the 10-second timeout, this is also used for HTTP and IPP partial-read timeouts, and there really isn't anything we can do to avoid it. The key is that a 10 second timeout prevents a complete DoS, and the admin can quickly determine where the attack is coming from and respond accordingly. FWIW, 10 seconds is a compromise between "safety" and support for slow links - the timeout used to be 1 second but didn't work well over WAN links... See the attached patch (you need to apply both patches) which implements OpenSSL BIO methods layered on top of the http_t interface. I've tested this on FC5 - let me know how things work on your systems. |
CUPS.org User: mike and here is a patch for the CDSA code... |
"str2091.patch": Index: cups/http.c--- cups/http.c (revision 6078)
+#if defined(HAVE_SSL) && defined(HAVE_GNUTLS)
+/_
@@ -1830,7 +1866,7 @@
@@ -1977,7 +2013,7 @@ #if defined(HAVE_SSL) && defined(HAVE_CDSASSL)
+#if defined(HAVE_SSL) && defined(HAVE_GNUTLS)
+/_
@@ -2268,6 +2320,10 @@ SSL_set_fd(conn, http->fd);
if (SSL_connect(conn) != 1) ifdef DEBUG@@ -2316,8 +2372,9 @@
if ((gnutls_handshake(conn->session)) != GNUTLS_E_SUCCESS) static int /* O - 1 if data is available, 0 otherwise /
ifdef HAVE_LIBSSL
Index: cups/http-private.h--- cups/http-private.h (revision 6078) +extern ssize_t _httpReadGNUTLS(gnutls_transport_ptr ptr, void *data,
if (SSL_accept(conn) != 1)
error = gnutls_handshake(conn->session); @@ -2743,7 +2751,7 @@
|
"str2091p2.patch": Index: cups/http.c--- cups/http.c (revision 6080)
+#if defined(HAVE_SSL) && defined(HAVE_LIBSSL)
+/_
@@ -2071,7 +2117,144 @@ +#if defined(HAVE_SSL) && defined(HAVE_LIBSSL)
+/*
+/*
+/*
+/_
+/*
+/_
@@ -2299,6 +2482,7 @@ ifdef HAVE_LIBSSLSSL_CTX context; / Context for encryption /
elif defined(HAVE_GNUTLS)http_tls_t conn; / TLS session object */ SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
conn = SSL_new(context);
- SSL_set_fd(conn, http->fd);
- SSL_set_timeout(conn, 10); /* 10-second data timeout */if (SSL_connect(conn) != 1) ifdef DEBUGIndex: cups/http-private.h--- cups/http-private.h (revision 6080) if defined HAVE_LIBSSL/*
+extern BIO_METHOD __httpBIOMethods(void); elif defined HAVE_GNUTLS/_
@@ -2619,11 +2620,12 @@
conn = SSL_new(context);
- SSL_set_timeout(conn, 10); /* 10-second data timeout */if (SSL_accept(conn) != 1) |
"str2091p3.patch": Index: cups/http.c--- cups/http.c (revision 6107)
do
if (bytes == *dataLength)
@@ -2125,19 +2142,23 @@
do
if (bytes == *dataLength)
@@ -2562,7 +2583,6 @@ elif defined(HAVE_CDSASSL)OSStatus error; /* Error code /
endif /_ HAVE_LIBSSL */@@ -2660,9 +2680,7 @@
if (!error) Index: cups/http-private.h--- cups/http-private.h (revision 6107) -typedef union _cdsa_conn_ref_u /**** CDSA Connection reference union
extern OSStatus _httpReadCDSA(SSLConnectionRef connection, void *data,
|
Version: 1.2.2
CUPS.org User: nathanw
(This is marked operating-system specific because it's only tested on FreeBSD 6.1, but I see no reason it shouldn't apply to other operating systems)
In encrypt_client(), SSL_accept() is called on a blocking socket. If nothing further occurs, the scheduler hangs until the socket is closed. (This also happens with GnuTLS) As a result, if you open a connection to the server on a port where SSL auto-detection is attempted, and the first character transmitted is anything not in "DGHOPT", the scheduler will hang waiting for SSL initialization until the socket is closed.
To reproduce this, one can do the following:
$ nc server 443
g
Until the connection is closed, no other requests will be served and the listen queue will grow without bound. I've been seeing this happen at random for the past few weeks on my server (presumably from port scanners), and it brings CUPS to a screeching halt until I reset it by hand.
The text was updated successfully, but these errors were encountered: