From: Stephan Bosch Date: Sun, 15 Sep 2013 00:54:04 +0000 (+0300) Subject: lib-http: Implemented limits on request method and target length. X-Git-Tag: 2.2.6~73 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fa4d00f73d120603a7886aed7433d6c3fceb9bf5;p=thirdparty%2Fdovecot%2Fcore.git lib-http: Implemented limits on request method and target length. --- diff --git a/src/lib-http/http-request-parser.c b/src/lib-http/http-request-parser.c index 28284d2fa9..dab94ceea0 100644 --- a/src/lib-http/http-request-parser.c +++ b/src/lib-http/http-request-parser.c @@ -7,6 +7,8 @@ #include "http-message-parser.h" #include "http-request-parser.h" +#define HTTP_REQUEST_PARSER_MAX_METHOD_LENGTH 32 + enum http_request_parser_state { HTTP_REQUEST_PARSE_STATE_INIT = 0, HTTP_REQUEST_PARSE_STATE_SKIP_LINE, @@ -24,6 +26,8 @@ struct http_request_parser { struct http_message_parser parser; enum http_request_parser_state state; + uoff_t max_target_length; + enum http_request_parse_error error_code; const char *request_method; @@ -34,12 +38,34 @@ struct http_request_parser { struct http_request_parser * http_request_parser_init(struct istream *input, - const struct http_header_limits *hdr_limits) + const struct http_request_limits *limits) { struct http_request_parser *parser; + struct http_header_limits hdr_limits; + uoff_t max_payload_size = limits->max_payload_size; parser = i_new(struct http_request_parser, 1); - http_message_parser_init(&parser->parser, input, hdr_limits, 0); + parser->max_target_length = limits->max_target_length; + + if (limits != NULL) + hdr_limits = limits->header; + else + memset(&hdr_limits, 0, sizeof(hdr_limits)); + + /* substitute default limits */ + if (parser->max_target_length == 0) + parser->max_target_length = HTTP_REQUEST_DEFAULT_MAX_TARGET_LENGTH; + if (hdr_limits.max_size == 0) + hdr_limits.max_size = HTTP_REQUEST_DEFAULT_MAX_HEADER_SIZE; + if (hdr_limits.max_field_size == 0) + hdr_limits.max_field_size = HTTP_REQUEST_DEFAULT_MAX_HEADER_FIELD_SIZE; + if (hdr_limits.max_fields == 0) + hdr_limits.max_fields = HTTP_REQUEST_DEFAULT_MAX_HEADER_FIELDS; + if (max_payload_size == 0) + max_payload_size = HTTP_REQUEST_DEFAULT_MAX_PAYLOAD_SIZE; + + http_message_parser_init + (&parser->parser, input, &hdr_limits, max_payload_size); return parser; } @@ -69,6 +95,11 @@ static int http_request_parse_method(struct http_request_parser *parser) while (p < parser->parser.end && http_char_is_token(*p)) p++; + if ((p - parser->parser.cur) > HTTP_REQUEST_PARSER_MAX_METHOD_LENGTH) { + parser->error_code = HTTP_REQUEST_PARSE_ERROR_METHOD_TOO_LONG; + parser->parser.error = "HTTP request method is too long"; + return -1; + } if (p == parser->parser.end) return 0; parser->request_method = @@ -79,19 +110,32 @@ static int http_request_parse_method(struct http_request_parser *parser) static int http_request_parse_target(struct http_request_parser *parser) { + struct http_message_parser *_parser = &parser->parser; const unsigned char *p = parser->parser.cur; /* We'll just parse anything up to the first SP or a control char. We could also implement workarounds for buggy HTTP clients and parse anything up to the HTTP-version and return 301 with the - target properly encoded. */ - while (p < parser->parser.end && *p > ' ') + target properly encoded (FIXME). */ + while (p < _parser->end && *p > ' ') p++; - if (p == parser->parser.end) + /* target is too long when explicit limit is exceeded or when input buffer + runs out of space */ + /* FIXME: put limit on full request line rather than target and method + separately */ + /* FIXME: is it wise to keep target in stream buffer? It can become very + large for some applications, increasing the stream buffer size */ + if ((uoff_t)(p - _parser->cur) > parser->max_target_length || + (p == _parser->end && ((uoff_t)(p - _parser->cur) >= + i_stream_get_max_buffer_size(_parser->input)))) { + parser->error_code = HTTP_REQUEST_PARSE_ERROR_TARGET_TOO_LONG; + parser->parser.error = "HTTP request target is too long"; + return -1; + } + if (p == _parser->end) return 0; - parser->request_target = - p_strdup_until(parser->parser.msg.pool, parser->parser.cur, p); + parser->request_target = p_strdup_until(_parser->msg.pool, _parser->cur, p); parser->parser.cur = p; return 1; } @@ -272,6 +316,8 @@ http_request_parser_message_error(struct http_request_parser *parser) return HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST; case HTTP_MESSAGE_PARSE_ERROR_NOT_IMPLEMENTED: return HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED; + case HTTP_MESSAGE_PARSE_ERROR_PAYLOAD_TOO_LARGE: + return HTTP_REQUEST_PARSE_ERROR_PAYLOAD_TOO_LARGE; case HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE: return HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST; default: diff --git a/src/lib-http/http-request-parser.h b/src/lib-http/http-request-parser.h index cc9406743d..2cf079a63a 100644 --- a/src/lib-http/http-request-parser.h +++ b/src/lib-http/http-request-parser.h @@ -4,19 +4,20 @@ #include "http-request.h" enum http_request_parse_error { - HTTP_REQUEST_PARSE_ERROR_NONE = 0, /* no error */ - HTTP_REQUEST_PARSE_ERROR_BROKEN_STREAM, /* stream error */ - HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST, /* unrecoverable generic error */ - HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST, /* recoverable generic error */ - HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED, /* used unimplemented feature - (recoverable) */ - HTTP_REQUEST_PARSE_ERROR_METHOD_TOO_LONG, /* method too long (fatal) */ - HTTP_REQUEST_PARSE_ERROR_TARGET_TOO_LONG /* target too long (fatal) */ + HTTP_REQUEST_PARSE_ERROR_NONE = 0, /* no error */ + HTTP_REQUEST_PARSE_ERROR_BROKEN_STREAM, /* stream error */ + HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST, /* unrecoverable generic error */ + HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST, /* recoverable generic error */ + HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED, /* used unimplemented feature + (recoverable) */ + HTTP_REQUEST_PARSE_ERROR_METHOD_TOO_LONG, /* method too long (fatal) */ + HTTP_REQUEST_PARSE_ERROR_TARGET_TOO_LONG, /* target too long (fatal) */ + HTTP_REQUEST_PARSE_ERROR_PAYLOAD_TOO_LARGE /* payload too large (fatal) */ }; struct http_request_parser * http_request_parser_init(struct istream *input, - const struct http_header_limits *hdr_limits) ATTR_NULL(2); + const struct http_request_limits *limits) ATTR_NULL(2); void http_request_parser_deinit(struct http_request_parser **_parser); int http_request_parse_next(struct http_request_parser *parser, diff --git a/src/lib-http/http-request.h b/src/lib-http/http-request.h index fbac406303..532a8cb3a0 100644 --- a/src/lib-http/http-request.h +++ b/src/lib-http/http-request.h @@ -5,6 +5,19 @@ struct http_url; +#define HTTP_REQUEST_DEFAULT_MAX_TARGET_LENGTH (8 * 1024) +#define HTTP_REQUEST_DEFAULT_MAX_HEADER_SIZE (200 * 1024) +#define HTTP_REQUEST_DEFAULT_MAX_HEADER_FIELD_SIZE (8 * 1024) +#define HTTP_REQUEST_DEFAULT_MAX_HEADER_FIELDS 50 +#define HTTP_REQUEST_DEFAULT_MAX_PAYLOAD_SIZE (1 * 1024 * 1024) + +struct http_request_limits { + uoff_t max_target_length; + uoff_t max_payload_size; + + struct http_header_limits header; +}; + enum http_request_target_format { HTTP_REQUEST_TARGET_FORMAT_ORIGIN = 0, HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, diff --git a/src/lib-http/test-http-server.c b/src/lib-http/test-http-server.c index f49e3f526b..8e5037aea4 100644 --- a/src/lib-http/test-http-server.c +++ b/src/lib-http/test-http-server.c @@ -81,11 +81,15 @@ static const struct connection_vfuncs client_vfuncs = { static void client_init(int fd) { struct client *client; + struct http_request_limits req_limits; + + memset(&req_limits, 0, sizeof(req_limits)); + req_limits.max_target_length = 4096; client = i_new(struct client, 1); connection_init_server(clients, &client->conn, "(http client)", fd, fd); - client->parser = http_request_parser_init(client->conn.input, 0); + client->parser = http_request_parser_init(client->conn.input, &req_limits); } static void client_accept(void *context ATTR_UNUSED)