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

cupsd denial of service attack #75

Closed
michaelrsweet opened this Issue May 8, 2003 · 10 comments

Comments

Projects
None yet
1 participant
@michaelrsweet
Collaborator

michaelrsweet commented May 8, 2003

Version: 1.1.17
CUPS.org User: twaugh.redhat

A single client behaving badly can cause the entire server to hang. For example:

$ telnet <your_favorite_cups_server> ipp
POST /printers/<your_favorite_printer> HTTP/1.1

Don't enter the second carriage return to complete the headers, just the POST line and one carriage return. At this point the CUPS server will no longer respond to any other client.

@michaelrsweet

This comment has been minimized.

Collaborator

michaelrsweet commented May 8, 2003

CUPS.org User: mike

Tim,

Have you tested this with 1.1.19? This doesn't seem to hang or otherwise prevent the scheduler from doing its job with the current release - it correctly times out after 300 seconds and does not affect other clients (e.g. other requests are still handled just fine.)

@michaelrsweet

This comment has been minimized.

Collaborator

michaelrsweet commented May 9, 2003

CUPS.org User: mike

Patch for 1.1.19rc4 added to this report; patch for 1.1.18 to follow shortly...

@michaelrsweet

This comment has been minimized.

Collaborator

michaelrsweet commented May 9, 2003

CUPS.org User: mike

OK, there is now an updated 1.1.19rc4 patch (fixes for the SSL timeout support - can't depend on httpWait() knowing when more encrypted data is available, at least not yet - we need to query the SSL library...) as well as a patch against 1.1.18.

@michaelrsweet

This comment has been minimized.

Collaborator

michaelrsweet commented May 12, 2003

CUPS.org User: mike

Updated the 1.1.18 patch to include the SSL library "pending data" check so that timeouts are applied to SSL connections properly.

@michaelrsweet

This comment has been minimized.

Collaborator

michaelrsweet commented May 13, 2003

CUPS.org User: twaugh.redhat

Thanks. The 1.1.19rc4 patch seems to have disappeared though.

@michaelrsweet

This comment has been minimized.

Collaborator

michaelrsweet commented May 13, 2003

CUPS.org User: mike

Yes, we're doing a 1.1.19rc5 release today which contains the fixes (without the announcement - they are bundled with the first bunch of DoS fixes that we made in 1.1.19 prior to your report), so we won't be including the patch for 1.1.19rc4...

@michaelrsweet

This comment has been minimized.

Collaborator

michaelrsweet commented May 27, 2003

CUPS.org User: mike

Closed and made public.

@michaelrsweet

This comment has been minimized.

Collaborator

michaelrsweet commented May 27, 2003

"cups-1.1.19rc4-str75.patch":

Index: cups/http.c

RCS file: /development/cvs/cups/cups/http.c,v
retrieving revision 1.116
retrieving revision 1.117
diff -u -r1.116 -r1.117
--- cups/http.c 2003/04/08 03:45:15 1.116
+++ cups/http.c 2003/05/09 16:05:27 1.117
@@ -914,6 +914,9 @@
else
bytes = http->data_remaining;

  •  if (!http->blocking && !httpWait(http, 1000))
    
  •    return (0);
    
    #ifdef HAVE_SSL
    if (http->tls)
    bytes = http_read_ssl(http, http->buffer, bytes);
    @@ -944,8 +947,11 @@
    }
    #endif /* WIN32 */
    }
  • else
  • else if (bytes == 0)
  • {
  •  http->error = EPIPE;
    
    return (0);
  • }
    }

if (http->used > 0)
@@ -965,10 +971,16 @@
}
#ifdef HAVE_SSL
else if (http->tls)

  • {
  • if (!http->blocking && !httpWait(http, 1000))
  •  return (0);
    
    bytes = http_read_ssl(http, buffer, length);
  • }
    #endif /* HAVE_SSL */
    else
    {
  • if (!http->blocking && !httpWait(http, 1000))
  •  return (0);
    
    DEBUG_printf(("httpRead: reading %d bytes from socket...\n", length));
    bytes = recv(http->fd, buffer, length, 0);
    DEBUG_printf(("httpRead: read %d bytes from socket...\n", bytes));
    @@ -987,6 +999,11 @@
    http->error = errno;
    #endif /* WIN32 */
    }
  • else if (bytes == 0)
  • {
  • http->error = EPIPE;
  • return 0;
  • }

if (http->data_remaining == 0)
{
@@ -1358,7 +1375,6 @@
}
else if (bytes == 0)
{

  •    if (http->blocking)
    

    http->error = EPIPE;

     return (NULL);
    

    Index: cups/ipp.c

    RCS file: /development/cvs/cups/cups/ipp.c,v
    retrieving revision 1.90
    retrieving revision 1.91
    diff -u -r1.90 -r1.91
    --- cups/ipp.c 2003/04/27 19:01:26 1.90
    +++ cups/ipp.c 2003/05/09 16:05:28 1.91
    @@ -2467,7 +2467,14 @@
    if (http->data_remaining == 0)
    {
    if (http->data_encoding == HTTP_ENCODE_CHUNKED)

  • httpGets(len, sizeof(len), http);
    
  • {

  • /*

  • \* Get the trailing CR LF after the chunk...
    
  • */
    
  • if (!httpGets(len, sizeof(len), http))
    
  •   return (-1);
    
  • }

if (http->data_encoding != HTTP_ENCODE_CHUNKED)
{

Index: scheduler/client.c

RCS file: /development/cvs/cups/scheduler/client.c,v
retrieving revision 1.158
retrieving revision 1.160
diff -u -r1.158 -r1.160
--- scheduler/client.c 2003/04/10 20:14:05 1.158
+++ scheduler/client.c 2003/05/09 16:10:22 1.160
@@ -1845,6 +1845,10 @@
}
}
}

  • else if (con->http.state == HTTP_POST_RECV)
    
  • {
    
  •        return (0);
    
  • }
    
    else if (con->http.state != HTTP_POST_SEND)
    {
    CloseClient(con);
@michaelrsweet

This comment has been minimized.

Collaborator

michaelrsweet commented May 27, 2003

"cups-1.1.18-str65.patch":

diff -ur cups-1.1.18/cups/http.c cups-1.1.18.patched/cups/http.c
--- cups-1.1.18/cups/http.c Tue Dec 17 13:56:42 2002
+++ cups-1.1.18.patched/cups/http.c Fri May 9 14:30:43 2003
@@ -29,6 +29,7 @@

  •                      default HTTP proxy (if any).
    
  • httpCheck() - Check to see if there is a pending response from
  •                      the server.
    
    • * httpWait() - Wait for data available on a connection.
  • httpClose() - Close an HTTP connection...
  • httpConnect() - Connect to a HTTP server.
  • httpConnectEncrypt() - Connect to a HTTP server using encryption.
    @@ -240,6 +241,18 @@
    int /* O - 0 = no data, 1 = data available /
    httpCheck(http_t *http) /
    I - HTTP connection */
    {
  • return (httpWait(http, 0));
    +}
    +
    +
    +/*
  • * 'httpWait()' - Wait for data available on a connection.
  • /
    +
    +int /
    O - 0 = no data, 1 = data available /
    +httpWait(http_t *http, /
    I - HTTP connection */
  •     int    msec)      /\* I - Milliseconds to wait _/
    
    +{
    fd_set input; /_ Input set for select() /
    struct timeval timeout; /
    Timeout */

@@ -261,10 +274,15 @@
FD_ZERO(&input);
FD_SET(http->fd, &input);

  • timeout.tv_sec = 0;
  • timeout.tv_usec = 0;
  • if (msec >= 0)
  • {
  • timeout.tv_sec = msec / 1000;
  • timeout.tv_usec = (msec % 1000) * 1000;
  • return (select(http->fd + 1, &input, NULL, NULL, &timeout) > 0);
  • return (select(http->fd + 1, &input, NULL, NULL, &timeout) > 0);
  • }
  • else
  • return (select(http->fd + 1, &input, NULL, NULL, NULL) > 0);
    }

@@ -857,7 +875,10 @@
char buffer[8192]; /* Junk buffer */

  • while (httpRead(http, buffer, sizeof(buffer)) > 0);
  • if (http->state != HTTP_WAITING)
  • {
  • while (httpRead(http, buffer, sizeof(buffer)) > 0);
  • }
    }

@@ -931,6 +952,9 @@
* Buffer small reads for better performance...
*/

  • if (!http->blocking && !httpWait(http, 1000))
  •  return (0);
    
    if (http->data_remaining > sizeof(http->buffer))
    bytes = sizeof(http->buffer);
    else
    @@ -967,7 +991,10 @@
    #endif /* WIN32 */
    }
    else
  • {
  •  http->error = EPIPE;
    
    return (0);
  • }
    }

if (http->used > 0)
@@ -991,6 +1018,9 @@
#endif /* HAVE_LIBSSL */
else
{

  • if (!http->blocking && !httpWait(http, 1000))
  •  return (0);
    
    DEBUG_printf(("httpRead: reading %d bytes from socket...\n", length));
    bytes = recv(http->fd, buffer, length, 0);
    DEBUG_printf(("httpRead: read %d bytes from socket...\n", bytes));
    @@ -1009,6 +1039,11 @@
    http->error = errno;
    #endif /* WIN32 */
    }
  • else
  • {
  • http->error = EPIPE;
  • return (0);
  • }

if (http->data_remaining == 0)
{
@@ -1253,7 +1288,10 @@
HTTP_MAX_BUFFER - http->used);
else
#endif /* HAVE_LIBSSL */

  •  bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0);
    
  •  if (!http->blocking && !httpWait(http, 1000))
    
  •    return (NULL);
    
  •  else
    
  •    bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0);
    

    if (bytes < 0)
    {
    @@ -1285,8 +1323,7 @@
    }
    else if (bytes == 0)
    {

  •    if (http->blocking)
    
  • http->error = EPIPE;
    
  •    http->error = EPIPE;
    
     return (NULL);
    

    }
    @@ -1554,6 +1591,7 @@
    case HTTP_POST_RECV :
    case HTTP_PUT :
    http->state ++;

  • case HTTP_POST_SEND :
    break;

default :
diff -ur cups-1.1.18/cups/http.h cups-1.1.18.patched/cups/http.h
--- cups-1.1.18/cups/http.h Tue Dec 17 13:56:42 2002
+++ cups-1.1.18.patched/cups/http.h Fri May 9 13:59:10 2003
@@ -338,6 +338,9 @@
char [33]);
extern char *httpMD5String(const md5_byte_t *, char [33]);

+/**** New in CUPS 1.1.19 ****/
+extern int httpWait(http_t *http, int msec);
+

/*

  • C++ magic...
    diff -ur cups-1.1.18/cups/ipp.c cups-1.1.18.patched/cups/ipp.c
    --- cups-1.1.18/cups/ipp.c Tue Dec 17 13:56:42 2002
    +++ cups-1.1.18.patched/cups/ipp.c Fri May 9 14:08:44 2003
    @@ -2036,7 +2036,14 @@
    if (http->data_remaining == 0)
    {
    if (http->data_encoding == HTTP_ENCODE_CHUNKED)

    • httpGets(len, sizeof(len), http);
      
    • {
    • /*
    • \* Get the trailing CR LF after the chunk...
      
    • */
      
    • if (!httpGets(len, sizeof(len), http))
      
    •   return (-1);
      
    • }

    if (http->data_encoding != HTTP_ENCODE_CHUNKED)
    {
    diff -ur cups-1.1.18/scheduler/client.c cups-1.1.18.patched/scheduler/client.c
    --- cups-1.1.18/scheduler/client.c Tue Dec 17 14:00:14 2002
    +++ cups-1.1.18.patched/scheduler/client.c Fri May 9 14:25:52 2003
    @@ -82,6 +82,8 @@
    client_t con; / New client pointer /
    unsigned address;/
    Address of client /
    struct hostent *host; /
    Host entry for address */

    • static time_t last_dos = 0;
    •           /\* Time of last DoS attack */
      

    LogMessage(L_DEBUG2, "AcceptClient(%p) %d NumClients = %d",
    @@ -134,8 +136,12 @@

    if (count >= MaxClientsPerHost)
    {

    • LogMessage(L_WARN, "Possible DoS attack - more than %d clients connecting from %s!",
    •           MaxClientsPerHost, Clients[i].http.hostname);
      
    • if ((time(NULL) - last_dos) >= 60)
    • {
    •  last_dos = time(NULL);
      
    •  LogMessage(L_WARN, "Possible DoS attack - more than %d clients connecting from %s!",
      
    •        MaxClientsPerHost, Clients[i].http.hostname);
      
    • }

    #ifdef WIN32
    closesocket(con->http.fd);
    @@ -272,7 +278,7 @@
    setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));

    /*

    • * Add the socket to the select() input mask.
    • * Close this file on all execs...
      */

    fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
    @@ -1438,6 +1444,10 @@
    }
    }
    }

    • else if (con->http.state == HTTP_POST_RECV)
      
    • {
      
    •        return (0);
      
    • }
      
      else if (con->http.state != HTTP_POST_SEND)
      {
      CloseClient(con);
      @@ -1784,6 +1794,14 @@
      shutdown(con->http.fd, 0);
      con->http.used = 0;
  • /*

  • * Update the activity time so that we timeout after 30 seconds rather

  • * then the current Timeout setting (300 by default). This prevents

  • * some DoS situations...

  • */

  • con->http.activity = time(NULL) - Timeout + 30;

LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...",
con->http.fd);

diff -ur cups-1.1.18/test/run-stp-tests.sh cups-1.1.18.patched/test/run-stp-tests.sh
--- cups-1.1.18/test/run-stp-tests.sh Tue Dec 17 14:00:25 2002
+++ cups-1.1.18.patched/test/run-stp-tests.sh Fri May 9 14:18:48 2003
@@ -142,6 +142,7 @@

cat >/tmp/$user/cupsd.conf <<EOF
Browsing Off
+FileDevice Yes
Listen 127.0.0.1:$port
User $user
ServerRoot /tmp/$user
ServerRoot /tmp/$user

@michaelrsweet

This comment has been minimized.

Collaborator

michaelrsweet commented May 27, 2003

"cups-1.1.18-str65v2.patch":

diff -ur cups-1.1.18/cups/http.c cups-1.1.18.patched/cups/http.c
--- cups-1.1.18/cups/http.c Tue Dec 17 13:56:42 2002
+++ cups-1.1.18.patched/cups/http.c Mon May 12 16:41:26 2003
@@ -29,6 +29,7 @@

  •                      default HTTP proxy (if any).
    
  • httpCheck() - Check to see if there is a pending response from
  •                      the server.
    
    • * httpWait() - Wait for data available on a connection.
  • httpClose() - Close an HTTP connection...
  • httpConnect() - Connect to a HTTP server.
  • httpConnectEncrypt() - Connect to a HTTP server using encryption.
    @@ -240,6 +241,18 @@
    int /* O - 0 = no data, 1 = data available /
    httpCheck(http_t *http) /
    I - HTTP connection */
    {
  • return (httpWait(http, 0));
    +}
    +
    +
    +/*
  • * 'httpWait()' - Wait for data available on a connection.
  • /
    +
    +int /
    O - 0 = no data, 1 = data available /
    +httpWait(http_t *http, /
    I - HTTP connection */
  •     int    msec)      /\* I - Milliseconds to wait _/
    
    +{
    fd_set input; /_ Input set for select() /
    struct timeval timeout; /
    Timeout */

@@ -254,6 +267,14 @@
if (http->used)
return (1);

+#ifdef HAVE_LIBSSL

  • if (http->tls)
  • {
  • if (SSL_pending((SSL *)(http->tls)))
  •  return (1);
    
  • }
    +#endif /* HAVE_LIBSSL _/

/_

  • Then try doing a select() to poll the socket...
    */
    @@ -261,10 +282,15 @@
    FD_ZERO(&input);
    FD_SET(http->fd, &input);
  • timeout.tv_sec = 0;
  • timeout.tv_usec = 0;
  • if (msec >= 0)
  • {
  • timeout.tv_sec = msec / 1000;
  • timeout.tv_usec = (msec % 1000) * 1000;
  • return (select(http->fd + 1, &input, NULL, NULL, &timeout) > 0);
  • return (select(http->fd + 1, &input, NULL, NULL, &timeout) > 0);
  • }
  • else
  • return (select(http->fd + 1, &input, NULL, NULL, NULL) > 0);
    }

@@ -857,7 +883,10 @@
char buffer[8192]; /* Junk buffer */

  • while (httpRead(http, buffer, sizeof(buffer)) > 0);
  • if (http->state != HTTP_WAITING)
  • {
  • while (httpRead(http, buffer, sizeof(buffer)) > 0);
  • }
    }

@@ -931,6 +960,9 @@
* Buffer small reads for better performance...
*/

  • if (!http->blocking && !httpWait(http, 1000))
  •  return (0);
    
    if (http->data_remaining > sizeof(http->buffer))
    bytes = sizeof(http->buffer);
    else
    @@ -967,7 +999,10 @@
    #endif /* WIN32 */
    }
    else
  • {
  •  http->error = EPIPE;
    
    return (0);
  • }
    }

if (http->used > 0)
@@ -987,10 +1022,18 @@
}
#ifdef HAVE_LIBSSL
else if (http->tls)

  • {
  • if (!http->blocking && !httpWait(http, 1000))
  •  return (0);
    
    bytes = SSL_read((SSL *)(http->tls), buffer, length);
  • }
    #endif /* HAVE_LIBSSL */
    else
    {
  • if (!http->blocking && !httpWait(http, 1000))
  •  return (0);
    
    DEBUG_printf(("httpRead: reading %d bytes from socket...\n", length));
    bytes = recv(http->fd, buffer, length, 0);
    DEBUG_printf(("httpRead: read %d bytes from socket...\n", bytes));
    @@ -1009,6 +1052,11 @@
    http->error = errno;
    #endif /* WIN32 */
    }
  • else
  • {
  • http->error = EPIPE;
  • return (0);
  • }

if (http->data_remaining == 0)
{
@@ -1247,13 +1295,16 @@

  • No newline; see if there is more data to be read...
    */

  •  if (!http->blocking && !httpWait(http, 1000))
    
  •    return (NULL);
    

    #ifdef HAVE_LIBSSL
    if (http->tls)
    bytes = SSL_read((SSL )(http->tls), bufend,
    HTTP_MAX_BUFFER - http->used);
    else
    #endif /
    HAVE_LIBSSL */

  •  bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0);
    
  •    bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0);
    

    if (bytes < 0)
    {
    @@ -1285,8 +1336,7 @@
    }
    else if (bytes == 0)
    {

  •    if (http->blocking)
    
  • http->error = EPIPE;
    
  •    http->error = EPIPE;
    
     return (NULL);
    

    }
    @@ -1554,6 +1604,7 @@
    case HTTP_POST_RECV :
    case HTTP_PUT :
    http->state ++;

  • case HTTP_POST_SEND :
    break;

default :
diff -ur cups-1.1.18/cups/http.h cups-1.1.18.patched/cups/http.h
--- cups-1.1.18/cups/http.h Tue Dec 17 13:56:42 2002
+++ cups-1.1.18.patched/cups/http.h Fri May 9 13:59:10 2003
@@ -338,6 +338,9 @@
char [33]);
extern char *httpMD5String(const md5_byte_t *, char [33]);

+/**** New in CUPS 1.1.19 ****/
+extern int httpWait(http_t *http, int msec);
+

/*

  • C++ magic...
    diff -ur cups-1.1.18/cups/ipp.c cups-1.1.18.patched/cups/ipp.c
    --- cups-1.1.18/cups/ipp.c Tue Dec 17 13:56:42 2002
    +++ cups-1.1.18.patched/cups/ipp.c Fri May 9 14:08:44 2003
    @@ -2036,7 +2036,14 @@
    if (http->data_remaining == 0)
    {
    if (http->data_encoding == HTTP_ENCODE_CHUNKED)

    • httpGets(len, sizeof(len), http);
      
    • {
    • /*
    • \* Get the trailing CR LF after the chunk...
      
    • */
      
    • if (!httpGets(len, sizeof(len), http))
      
    •   return (-1);
      
    • }

    if (http->data_encoding != HTTP_ENCODE_CHUNKED)
    {
    diff -ur cups-1.1.18/scheduler/client.c cups-1.1.18.patched/scheduler/client.c
    --- cups-1.1.18/scheduler/client.c Tue Dec 17 14:00:14 2002
    +++ cups-1.1.18.patched/scheduler/client.c Fri May 9 14:25:52 2003
    @@ -82,6 +82,8 @@
    client_t con; / New client pointer /
    unsigned address;/
    Address of client /
    struct hostent *host; /
    Host entry for address */

    • static time_t last_dos = 0;
    •           /\* Time of last DoS attack */
      

    LogMessage(L_DEBUG2, "AcceptClient(%p) %d NumClients = %d",
    @@ -134,8 +136,12 @@

    if (count >= MaxClientsPerHost)
    {

    • LogMessage(L_WARN, "Possible DoS attack - more than %d clients connecting from %s!",
    •           MaxClientsPerHost, Clients[i].http.hostname);
      
    • if ((time(NULL) - last_dos) >= 60)
    • {
    •  last_dos = time(NULL);
      
    •  LogMessage(L_WARN, "Possible DoS attack - more than %d clients connecting from %s!",
      
    •        MaxClientsPerHost, Clients[i].http.hostname);
      
    • }

    #ifdef WIN32
    closesocket(con->http.fd);
    @@ -272,7 +278,7 @@
    setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));

    /*

    • * Add the socket to the select() input mask.
    • * Close this file on all execs...
      */

    fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
    @@ -1438,6 +1444,10 @@
    }
    }
    }

    • else if (con->http.state == HTTP_POST_RECV)
      
    • {
      
    •        return (0);
      
    • }
      
      else if (con->http.state != HTTP_POST_SEND)
      {
      CloseClient(con);
      @@ -1784,6 +1794,14 @@
      shutdown(con->http.fd, 0);
      con->http.used = 0;
  • /*

  • * Update the activity time so that we timeout after 30 seconds rather

  • * then the current Timeout setting (300 by default). This prevents

  • * some DoS situations...

  • */

  • con->http.activity = time(NULL) - Timeout + 30;

LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...",
con->http.fd);

diff -ur cups-1.1.18/test/run-stp-tests.sh cups-1.1.18.patched/test/run-stp-tests.sh
--- cups-1.1.18/test/run-stp-tests.sh Tue Dec 17 14:00:25 2002
+++ cups-1.1.18.patched/test/run-stp-tests.sh Fri May 9 14:18:48 2003
@@ -142,6 +142,7 @@

cat >/tmp/$user/cupsd.conf <<EOF
Browsing Off
+FileDevice Yes
Listen 127.0.0.1:$port
User $user
ServerRoot /tmp/$user
ServerRoot /tmp/$user

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment