fielding 98/08/09 21:16:16
Modified: src CHANGES src/include http_config.h http_core.h httpd.h src/main http_config.c http_core.c http_protocol.c Log: Fixed request limit change to be more portable. Removed the server_rec variables since compile-time control of the request-line, fieldsize, and number of fields is sufficient. Added a per-dir configuration directive LimitRequestBody for setting a maximum request message body, with the default of 0 meaning no limit. Revision Changes Path 1.1015 +3 -2 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.1014 retrieving revision 1.1015 diff -u -r1.1014 -r1.1015 --- CHANGES 1998/08/10 00:10:18 1.1014 +++ CHANGES 1998/08/10 04:16:11 1.1015 @@ -3,10 +3,11 @@ *) SECURITY: Eliminate O(n^2) space DoS attacks (and other O(n^2) cpu time attacks) in header parsing. [Dean Gaudet] - *) SECURITY: Added default limits for various aspects of reading a + *) SECURITY: Added compile-time limits for various aspects of reading a client request to avoid some simple denial of service attacks, including limits on maximum request-line size, number of header - fields, size of any one header field, and size of the request + fields, and size of any one header field. Also added a configurable + directive LimitRequestBody for limiting the size of the request message body. [Roy Fielding] *) Make status module aware of DNS and logging states, even if 1.93 +1 -1 apache-1.3/src/include/http_config.h Index: http_config.h =================================================================== RCS file: /home/cvs/apache-1.3/src/include/http_config.h,v retrieving revision 1.92 retrieving revision 1.93 diff -u -r1.92 -r1.93 --- http_config.h 1998/08/09 06:37:15 1.92 +++ http_config.h 1998/08/10 04:16:12 1.93 @@ -275,7 +275,7 @@ * handle it back-compatibly, or at least signal an error). */ -#define MODULE_MAGIC_NUMBER 19980808 +#define MODULE_MAGIC_NUMBER 19980809 #define STANDARD_MODULE_STUFF MODULE_MAGIC_NUMBER, -1, __FILE__, NULL, NULL /* Generic accessors for other modules to get at their own module-specific 1.47 +2 -0 apache-1.3/src/include/http_core.h Index: http_core.h =================================================================== RCS file: /home/cvs/apache-1.3/src/include/http_core.h,v retrieving revision 1.46 retrieving revision 1.47 diff -u -r1.46 -r1.47 --- http_core.h 1998/08/06 19:23:43 1.46 +++ http_core.h 1998/08/10 04:16:13 1.47 @@ -131,6 +131,7 @@ API_EXPORT(char *) ap_construct_url(pool *p, const char *uri, const request_rec *r); API_EXPORT(const char *) ap_get_server_name(const request_rec *r); API_EXPORT(unsigned) ap_get_server_port(const request_rec *r); +API_EXPORT(unsigned long) ap_get_limit_req_body(const request_rec *r); API_EXPORT(void) ap_custom_response(request_rec *r, int status, char *string); /* Authentication stuff. This is one of the places where compatibility @@ -236,6 +237,7 @@ #ifdef RLIMIT_NPROC struct rlimit *limit_nproc; #endif + unsigned long limit_req_body; /* limit on bytes in request msg body */ /* logging options */ enum { srv_sig_off, srv_sig_on, srv_sig_withmail } server_signature; 1.233 +18 -27 apache-1.3/src/include/httpd.h Index: httpd.h =================================================================== RCS file: /home/cvs/apache-1.3/src/include/httpd.h,v retrieving revision 1.232 retrieving revision 1.233 diff -u -r1.232 -r1.233 --- httpd.h 1998/08/09 16:57:28 1.232 +++ httpd.h 1998/08/10 04:16:13 1.233 @@ -369,6 +369,24 @@ #define DEFAULT_LISTENBACKLOG 511 #endif +/* Limits on the size of various request items. These limits primarily + * exist to prevent simple denial-of-service attacks on a server based + * on misuse of the protocol. The recommended values will depend on the + * nature of the server resources -- CGI scripts and database backends + * might require large values, but most servers could get by with much + * smaller limits than we use below. The request message body size can + * be limited by the per-dir config directive LimitRequestBody. + */ +#ifndef AP_LIMIT_REQUEST_LINE +#define AP_LIMIT_REQUEST_LINE 8192 +#endif /* default limit on bytes in Request-Line (Method+URI+HTTP-version) */ +#ifndef AP_LIMIT_REQUEST_FIELDS +#define AP_LIMIT_REQUEST_FIELDS 100 +#endif /* default limit on number of request header fields */ +#ifndef AP_LIMIT_REQUEST_FIELDSIZE +#define AP_LIMIT_REQUEST_FIELDSIZE 8192 +#endif /* default limit on bytes in any one header field */ + /* * The below defines the base string of the Server: header. Additional * tokens can be added via the ap_add_version_component() API call. @@ -541,28 +559,6 @@ #define REQUEST_CHUNKED_DECHUNK 2 #define REQUEST_CHUNKED_PASS 3 -/* Limits on the size of various request items. These limits primarily - * exist to prevent simple denial-of-service attacks on a server based - * on misuse of the protocol. The recommended values will depend on the - * nature of the server resources -- CGI scripts and database backends - * might require large values, but most servers could get by with much - * smaller limits than we use below. These limits can be reset on a - * per-server basis using the LimitRequestLine, LimitRequestFields, - * LimitRequestFieldSize, and LimitRequestBody configuration directives. - */ -#ifndef DEFAULT_LIMIT_REQUEST_LINE -#define DEFAULT_LIMIT_REQUEST_LINE 8192 -#endif /* default limit on bytes in Request-Line (Method+URI+HTTP-version) */ -#ifndef DEFAULT_LIMIT_REQUEST_FIELDS -#define DEFAULT_LIMIT_REQUEST_FIELDS 100 -#endif /* default limit on number of header fields */ -#ifndef DEFAULT_LIMIT_REQUEST_FIELDSIZE -#define DEFAULT_LIMIT_REQUEST_FIELDSIZE 8192 -#endif /* default limit on bytes in any one field */ -#ifndef DEFAULT_LIMIT_REQUEST_BODY -#define DEFAULT_LIMIT_REQUEST_BODY 33554432ul -#endif /* default limit on bytes in request body */ - /* Things which may vary per file-lookup WITHIN a request --- * e.g., state of MIME config. Basically, the name of an object, info * about the object, and any other info we may ahve which may need to @@ -846,11 +842,6 @@ uid_t server_uid; /* effective user id when calling exec wrapper */ gid_t server_gid; /* effective group id when calling exec wrapper */ - - unsigned int limit_req_line; /* limit on bytes in Request-Line */ - unsigned int limit_req_fields; /* limit on number of header fields */ - unsigned long limit_req_fieldsize; /* limit on bytes in any one field */ - unsigned long limit_req_body; /* limit on bytes in request body */ }; /* These are more like real hosts than virtual hosts */ 1.121 +0 -4 apache-1.3/src/main/http_config.c Index: http_config.c =================================================================== RCS file: /home/cvs/apache-1.3/src/main/http_config.c,v retrieving revision 1.120 retrieving revision 1.121 diff -u -r1.120 -r1.121 --- http_config.c 1998/08/09 06:37:17 1.120 +++ http_config.c 1998/08/10 04:16:14 1.121 @@ -1409,10 +1409,6 @@ s->loglevel = DEFAULT_LOGLEVEL; s->srm_confname = RESOURCE_CONFIG_FILE; s->access_confname = ACCESS_CONFIG_FILE; - s->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE; - s->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS; - s->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE; - s->limit_req_body = DEFAULT_LIMIT_REQUEST_BODY; s->timeout = DEFAULT_TIMEOUT; s->keep_alive_timeout = DEFAULT_KEEPALIVE_TIMEOUT; s->keep_alive_max = DEFAULT_KEEPALIVE; 1.219 +26 -0 apache-1.3/src/main/http_core.c Index: http_core.c =================================================================== RCS file: /home/cvs/apache-1.3/src/main/http_core.c,v retrieving revision 1.218 retrieving revision 1.219 diff -u -r1.218 -r1.219 --- http_core.c 1998/08/08 13:26:06 1.218 +++ http_core.c 1998/08/10 04:16:14 1.219 @@ -708,6 +708,14 @@ return ap_psprintf(p, "%s://%s:%u%s", ap_http_method(r), host, port, uri); } +API_EXPORT(unsigned long) ap_get_limit_req_body(const request_rec *r) +{ + core_dir_config *d = + (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); + + return d->limit_req_body; +} + /***************************************************************** * * Commands... this module handles almost all of the NCSA httpd.conf @@ -2301,6 +2309,22 @@ return NULL; } +static const char *set_limit_req_body(cmd_parms *cmd, core_dir_config *conf, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + /* WTF: If strtoul is not portable, then write a replacement. + * Instead we have an idiotic define in httpd.h that prevents + * it from being used even when it is available. Sheesh. + */ + conf->limit_req_body = (unsigned long)strtol(arg, (char **)NULL, 10); + return NULL; +} + /* Note --- ErrorDocument will now work from .htaccess files. * The AllowOverride of Fileinfo allows webmasters to turn it off */ @@ -2503,6 +2527,8 @@ #endif { "ServerTokens", set_serv_tokens, NULL, RSRC_CONF, TAKE1, "Determine tokens displayed in the Server: header - Min(imal), OS or Full" }, +{ "LimitRequestBody", set_limit_req_body, NULL, RSRC_CONF|ACCESS_CONF|OR_ALL, + TAKE1, "Limit (in bytes) on maximum size of request message body" }, { NULL }, }; 1.236 +35 -33 apache-1.3/src/main/http_protocol.c Index: http_protocol.c =================================================================== RCS file: /home/cvs/apache-1.3/src/main/http_protocol.c,v retrieving revision 1.235 retrieving revision 1.236 diff -u -r1.235 -r1.236 --- http_protocol.c 1998/08/09 17:36:26 1.235 +++ http_protocol.c 1998/08/10 04:16:15 1.236 @@ -626,17 +626,12 @@ static int read_request_line(request_rec *r) { - char *l; - const char *ll; + char l[AP_LIMIT_REQUEST_LINE + 2]; /* getline needs two extra for \n\0 */ + const char *ll = l; const char *uri; conn_rec *conn = r->connection; int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */ int len; - pool *tmp; - - tmp = ap_make_sub_pool(r->pool); - l = ap_palloc(tmp, r->server->limit_req_line); - ll = l; /* Read past empty lines until we get a real request line, * a read error, the connection closes (EOF), or we timeout. @@ -653,10 +648,9 @@ * have to block during a read. */ ap_bsetflag(conn->client, B_SAFEREAD, 1); - while ((len = getline(l, r->server->limit_req_line, conn->client, 0)) <= 0) { + while ((len = getline(l, sizeof(l), conn->client, 0)) <= 0) { if ((len < 0) || ap_bgetflag(conn->client, B_EOF)) { ap_bsetflag(conn->client, B_SAFEREAD, 0); - ap_destroy_pool(tmp); return 0; } } @@ -696,11 +690,14 @@ ap_parse_uri(r, uri); - if (len >= r->server->limit_req_line - 1) { + /* getline returns (size of max buffer - 1) if it fills up the + * buffer before finding the end-of-line. This is only going to + * happen if it exceeds the configured limit for a request-line. + */ + if (len >= sizeof(l) - 1) { r->status = HTTP_REQUEST_URI_TOO_LARGE; r->proto_num = HTTP_VERSION(1,0); r->protocol = ap_pstrdup(r->pool, "HTTP/1.0"); - ap_destroy_pool(tmp); return 0; } @@ -713,7 +710,6 @@ else r->proto_num = HTTP_VERSION(1,0); - ap_destroy_pool(tmp); return 1; } @@ -742,20 +738,20 @@ /* XXX: could use ap_overlap_tables here... which generalizes this code */ static void get_mime_headers(request_rec *r) { + char field[AP_LIMIT_REQUEST_FIELDSIZE + 2]; /* getline needs two extra */ conn_rec *c = r->connection; - char *copy; - int len; char *value; - unsigned int fields_read = 0; - char *field; - array_header *arr; + char *copy; pool *tmp; + array_header *arr; mime_key *new_key; - unsigned order; mime_key *first; mime_key *last; mime_key *end; char *strp; + unsigned order; + int len; + unsigned int fields_read = 0; /* The array will store the headers in a way that we can merge them * later in O(n*lg(n))... rather than deal with various O(n^2) @@ -765,8 +761,6 @@ arr = ap_make_array(tmp, 50, sizeof(mime_key)); order = 0; - field = ap_palloc(tmp, r->server->limit_req_fieldsize); - /* If headers_in is non-empty (i.e. we're parsing a trailer) then * we have to merge. Have I mentioned that I think this is a lame part * of the HTTP standard? Anyhow, we'll cheat, and just pre-seed our @@ -795,22 +789,25 @@ * Read header lines until we get the empty separator line, a read error, * the connection closes (EOF), reach the server limit, or we timeout. */ - while ((len = getline(field, r->server->limit_req_fieldsize, - c->client, 1)) > 0) { + while ((len = getline(field, sizeof(field), c->client, 1)) > 0) { - if (++fields_read > r->server->limit_req_fields) { + if (++fields_read > AP_LIMIT_REQUEST_FIELDS) { r->status = HTTP_BAD_REQUEST; ap_table_setn(r->notes, "error-notes", "Number of request header fields exceeds server limit.<P>\n"); - ap_destroy_pool(tmp); + ap_destroy_pool(tmp); return; } - if (len >= r->server->limit_req_fieldsize) { + /* getline returns (size of max buffer - 1) if it fills up the + * buffer before finding the end-of-line. This is only going to + * happen if it exceeds the configured limit for a field size. + */ + if (len >= sizeof(field) - 1) { r->status = HTTP_BAD_REQUEST; ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool, "Size of a request header field exceeds server limit.<P>\n" "<PRE>\n", field, "</PRE>\n", NULL)); - ap_destroy_pool(tmp); + ap_destroy_pool(tmp); return; } copy = ap_palloc(r->pool, len + 1); @@ -821,7 +818,7 @@ ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool, "Request header field is missing colon separator.<P>\n" "<PRE>\n", copy, "</PRE>\n", NULL)); - ap_destroy_pool(tmp); + ap_destroy_pool(tmp); return; } @@ -830,7 +827,7 @@ while (*value == ' ' || *value == '\t') ++value; /* Skip to start of value */ - /* XXX: should strip trailing whitespace as well */ + /* XXX: should strip trailing whitespace as well */ /* Notice that key and val are actually in r->pool... this is a slight * optimization to handle the normal case, where we don't have twits @@ -1495,6 +1492,7 @@ { const char *tenc = ap_table_get(r->headers_in, "Transfer-Encoding"); const char *lenp = ap_table_get(r->headers_in, "Content-Length"); + unsigned long max_body; r->read_body = read_policy; r->read_chunked = 0; @@ -1534,10 +1532,12 @@ "%s with body is not allowed for %s", r->method, r->uri); return HTTP_REQUEST_ENTITY_TOO_LARGE; } - if (r->remaining > r->server->limit_req_body) { + + max_body = ap_get_limit_req_body(r); + if (max_body && (r->remaining > max_body)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Request content-length of %s is larger than the configured " - "server limit of %lu", lenp, r->server->limit_req_body); + "limit of %lu", lenp, max_body); return HTTP_REQUEST_ENTITY_TOO_LARGE; } @@ -1601,6 +1601,7 @@ int c; long len_read, len_to_read; long chunk_start = 0; + unsigned long max_body; if (!r->read_chunked) { /* Content-length read */ len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining; @@ -1630,10 +1631,11 @@ * caller read pass, since the limit exists just to stop infinite * length requests and nobody cares if it goes over by one buffer. */ - if (r->read_length > r->server->limit_req_body) { + max_body = ap_get_limit_req_body(r); + if (max_body && (r->read_length > max_body)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, - "Chunked request body is larger than the configured " - "server limit of %lu", r->server->limit_req_body); + "Chunked request body is larger than the configured limit of %lu", + max_body); r->connection->keepalive = -1; return -1; }