AST-2014-007: Fix DOS by consuming the number of allowed HTTP connections.

Simply establishing a TCP connection and never sending anything to the
configured HTTP port in http.conf will tie up a HTTP connection.  Since
there is a maximum number of open HTTP sessions allowed at a time you can
block legitimate connections.

A similar problem exists if a HTTP request is started but never finished.

* Added http.conf session_inactivity timer option to close HTTP
connections that aren't doing anything.  Defaults to 30000 ms.

* Removed the undocumented manager.conf block-sockets option.  It
interferes with TCP/TLS inactivity timeouts.

* AMI and SIP TLS connections now have better authentication timeout
protection.  Though I didn't remove the bizzare TLS timeout polling code
from chan_sip.

* chan_sip can now handle SSL certificate renegotiations in the middle of
a session.  It couldn't do that before because the socket was non-blocking
and the SSL calls were not restarted as documented by the OpenSSL
documentation.

* Fixed an off nominal leak of the ssl struct in
handle_tcptls_connection() if the FILE stream failed to open and the SSL
certificate negotiations failed.

The patch creates a custom FILE stream handler to give the created FILE
streams inactivity timeout and timeout after a specific moment in time
capability.  This approach eliminates the need for code using the FILE
stream to be redesigned to deal with the timeouts.

This patch indirectly fixes most of ASTERISK-18345 by fixing the usage of
the SSL_read/SSL_write operations.

ASTERISK-23673 #close
Reported by: Richard Mudgett
........

Merged revisions 415841 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 415854 from http://svn.asterisk.org/svn/asterisk/branches/11


git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/12@415896 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Richard Mudgett
2014-06-12 16:41:56 +00:00
parent 5825dec823
commit 44dd7898b2
10 changed files with 605 additions and 130 deletions

View File

@@ -71,6 +71,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define DEFAULT_PORT 8088
#define DEFAULT_TLS_PORT 8089
#define DEFAULT_SESSION_LIMIT 100
#define DEFAULT_SESSION_INACTIVITY 30000 /* (ms) Idle time waiting for data. */
/* See http.h for more information about the SSL implementation */
#if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
@@ -78,6 +79,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#endif
static int session_limit = DEFAULT_SESSION_LIMIT;
static int session_inactivity = DEFAULT_SESSION_INACTIVITY;
static int session_count = 0;
static struct ast_tls_config http_tls_cfg;
@@ -1240,6 +1242,7 @@ static void *httpd_helper_thread(void *data)
enum ast_http_method http_method = AST_HTTP_UNKNOWN;
const char *transfer_encoding;
int remaining_headers;
int flags;
struct protoent *p;
if (ast_atomic_fetchadd_int(&session_count, +1) >= session_limit) {
@@ -1261,7 +1264,14 @@ static void *httpd_helper_thread(void *data)
ast_log(LOG_WARNING, "Some HTTP requests may be slow to respond.\n");
}
if (!fgets(buf, sizeof(buf), ser->f)) {
/* make sure socket is non-blocking */
flags = fcntl(ser->fd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(ser->fd, F_SETFL, flags);
ast_tcptls_stream_set_timeout_inactivity(ser->stream_cookie, session_inactivity);
if (!fgets(buf, sizeof(buf), ser->f) || feof(ser->f)) {
goto done;
}
@@ -1301,12 +1311,19 @@ static void *httpd_helper_thread(void *data)
/* process "Request Headers" lines */
remaining_headers = MAX_HTTP_REQUEST_HEADERS;
while (fgets(header_line, sizeof(header_line), ser->f)) {
char *name, *value;
for (;;) {
char *name;
char *value;
if (!fgets(header_line, sizeof(header_line), ser->f) || feof(ser->f)) {
ast_http_error(ser, 400, "Bad Request", "Timeout");
goto done;
}
/* Trim trailing characters */
ast_trim_blanks(header_line);
if (ast_strlen_zero(header_line)) {
/* A blank line ends the request header section. */
break;
}
@@ -1374,7 +1391,7 @@ done:
ast_variables_destroy(headers);
if (ser->f) {
fclose(ser->f);
ast_tcptls_close_session_file(ser);
}
ao2_ref(ser, -1);
ser = NULL;
@@ -1484,6 +1501,9 @@ static int __ast_http_load(int reload)
ast_sockaddr_setnull(&https_desc.local_address);
session_limit = DEFAULT_SESSION_LIMIT;
session_inactivity = DEFAULT_SESSION_INACTIVITY;
if (cfg) {
v = ast_variable_browse(cfg, "general");
for (; v; v = v->next) {
@@ -1529,6 +1549,12 @@ static int __ast_http_load(int reload)
ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
v->name, v->value, v->lineno);
}
} else if (!strcasecmp(v->name, "session_inactivity")) {
if (ast_parse_arg(v->value, PARSE_INT32 |PARSE_DEFAULT | PARSE_IN_RANGE,
&session_inactivity, DEFAULT_SESSION_INACTIVITY, 1, INT_MAX)) {
ast_log(LOG_WARNING, "Invalid %s '%s' at line %d of http.conf\n",
v->name, v->value, v->lineno);
}
} else {
ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
}