]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: Implemented limits on request method and target length.
authorStephan Bosch <stephan@rename-it.nl>
Sun, 15 Sep 2013 00:54:04 +0000 (03:54 +0300)
committerStephan Bosch <stephan@rename-it.nl>
Sun, 15 Sep 2013 00:54:04 +0000 (03:54 +0300)
src/lib-http/http-request-parser.c
src/lib-http/http-request-parser.h
src/lib-http/http-request.h
src/lib-http/test-http-server.c

index 28284d2fa9a39e40c93c2218294fc54d1d820310..dab94ceea07d3a0345f32a6847d1e68de19867fd 100644 (file)
@@ -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:
index cc9406743dd90afc27fd8b22b65d393e22798bc5..2cf079a63a2a95f7f4201be44c671d671eafe09d 100644 (file)
@@ -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,
index fbac406303ed69ac037b2565cf6be261c75bc229..532a8cb3a0798b15894ed6e1e49e490bc50cb0d0 100644 (file)
@@ -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,
index f49e3f526b417063f8956129986e91d1355ba35b..8e5037aea47e4dd2e0f985e71e51fa5f4e2d2264 100644 (file)
@@ -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)