Limit the number of http headers when receiving the http response
Change introduces a default limit on HTTP headers we expect to receive from server to 256. If limit is exceeded http client library indicates HTTP_R_RESPONSE_TOO_MANY_HDRLINES error. Application can use OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines() to change default. Setting limit to 0 implies no limit (current behavior). Fixes #22264 Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/23781)
This commit is contained in:
parent
bc930bed20
commit
7f8aba2f44
@ -155,6 +155,14 @@ OpenSSL 3.3
|
||||
|
||||
*Hugo Landau*
|
||||
|
||||
* New limit on HTTP response headers is introduced to HTTP client. The
|
||||
default limit is set to 256 header lines. If limit is exceeded the
|
||||
response processing stops with error HTTP_R_RESPONSE_TOO_MANY_HDRLINES.
|
||||
Application may call OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines(3)
|
||||
to change the default. Setting the value to 0 disables the limit.
|
||||
|
||||
*Alexandr Nedvedicky*
|
||||
|
||||
OpenSSL 3.2
|
||||
-----------
|
||||
|
||||
|
@ -837,6 +837,7 @@ HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP:112:redirection from https to http
|
||||
HTTP_R_REDIRECTION_NOT_ENABLED:116:redirection not enabled
|
||||
HTTP_R_RESPONSE_LINE_TOO_LONG:113:response line too long
|
||||
HTTP_R_RESPONSE_PARSE_ERROR:104:response parse error
|
||||
HTTP_R_RESPONSE_TOO_MANY_HDRLINES:130:response too many hdrlines
|
||||
HTTP_R_RETRY_TIMEOUT:129:retry timeout
|
||||
HTTP_R_SERVER_CANCELED_CONNECTION:127:server canceled connection
|
||||
HTTP_R_SOCK_NOT_SUPPORTED:122:sock not supported
|
||||
|
@ -67,6 +67,7 @@ struct ossl_http_req_ctx_st {
|
||||
time_t max_time; /* Maximum end time of current transfer, or 0 */
|
||||
time_t max_total_time; /* Maximum end time of total transfer, or 0 */
|
||||
char *redirection_url; /* Location obtained from HTTP status 301/302 */
|
||||
size_t max_hdr_lines; /* Max. number of http hdr lines, or 0 */
|
||||
};
|
||||
|
||||
/* HTTP states */
|
||||
@ -106,6 +107,7 @@ OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int buf_size)
|
||||
rctx->buf = OPENSSL_malloc(rctx->buf_size);
|
||||
rctx->wbio = wbio;
|
||||
rctx->rbio = rbio;
|
||||
rctx->max_hdr_lines = OSSL_HTTP_DEFAULT_MAX_RESP_HDR_LINES;
|
||||
if (rctx->buf == NULL) {
|
||||
OPENSSL_free(rctx);
|
||||
return NULL;
|
||||
@ -355,6 +357,16 @@ int OSSL_HTTP_REQ_CTX_set1_req(OSSL_HTTP_REQ_CTX *rctx, const char *content_type
|
||||
return res;
|
||||
}
|
||||
|
||||
void OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines(OSSL_HTTP_REQ_CTX *rctx,
|
||||
size_t count)
|
||||
{
|
||||
if (rctx == NULL) {
|
||||
ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
|
||||
return;
|
||||
}
|
||||
rctx->max_hdr_lines = count;
|
||||
}
|
||||
|
||||
static int add1_headers(OSSL_HTTP_REQ_CTX *rctx,
|
||||
const STACK_OF(CONF_VALUE) *headers, const char *host)
|
||||
{
|
||||
@ -537,6 +549,7 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
|
||||
size_t resp_len;
|
||||
const unsigned char *p;
|
||||
char *buf, *key, *value, *line_end = NULL;
|
||||
size_t resp_hdr_lines = 0;
|
||||
|
||||
if (rctx == NULL) {
|
||||
ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
|
||||
@ -682,6 +695,14 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
resp_hdr_lines++;
|
||||
if (rctx->max_hdr_lines != 0 && rctx->max_hdr_lines < resp_hdr_lines) {
|
||||
ERR_raise(ERR_LIB_HTTP, HTTP_R_RESPONSE_TOO_MANY_HDRLINES);
|
||||
OSSL_TRACE(HTTP, "Received too many headers\n");
|
||||
rctx->state = OHS_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Don't allow excessive lines */
|
||||
if (n == rctx->buf_size) {
|
||||
ERR_raise(ERR_LIB_HTTP, HTTP_R_RESPONSE_LINE_TOO_LONG);
|
||||
@ -786,6 +807,8 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
|
||||
if (OSSL_TRACE_ENABLED(HTTP))
|
||||
OSSL_TRACE(HTTP, "]\n");
|
||||
|
||||
resp_hdr_lines = 0;
|
||||
|
||||
if (rctx->keep_alive != 0 /* do not let server initiate keep_alive */
|
||||
&& !found_keep_alive /* otherwise there is no change */) {
|
||||
if (rctx->keep_alive == 2) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Generated by util/mkerr.pl DO NOT EDIT
|
||||
* Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
|
||||
* Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
@ -12,7 +12,9 @@
|
||||
#include <openssl/httperr.h>
|
||||
#include "crypto/httperr.h"
|
||||
|
||||
#ifndef OPENSSL_NO_ERR
|
||||
#ifndef OPENSSL_NO_HTTP
|
||||
|
||||
# ifndef OPENSSL_NO_ERR
|
||||
|
||||
static const ERR_STRING_DATA HTTP_str_reasons[] = {
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ASN1_LEN_EXCEEDS_MAX_RESP_LEN),
|
||||
@ -55,6 +57,8 @@ static const ERR_STRING_DATA HTTP_str_reasons[] = {
|
||||
"response line too long"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_RESPONSE_PARSE_ERROR),
|
||||
"response parse error"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_RESPONSE_TOO_MANY_HDRLINES),
|
||||
"response too many hdrlines"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_RETRY_TIMEOUT), "retry timeout"},
|
||||
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_SERVER_CANCELED_CONNECTION),
|
||||
"server canceled connection"},
|
||||
@ -70,13 +74,16 @@ static const ERR_STRING_DATA HTTP_str_reasons[] = {
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
#endif
|
||||
# endif
|
||||
|
||||
int ossl_err_load_HTTP_strings(void)
|
||||
{
|
||||
#ifndef OPENSSL_NO_ERR
|
||||
# ifndef OPENSSL_NO_ERR
|
||||
if (ERR_reason_error_string(HTTP_str_reasons[0].error) == NULL)
|
||||
ERR_load_strings_const(HTTP_str_reasons);
|
||||
#endif
|
||||
# endif
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
NON_EMPTY_TRANSLATION_UNIT
|
||||
#endif
|
||||
|
@ -15,7 +15,8 @@ OSSL_HTTP_REQ_CTX_exchange,
|
||||
OSSL_HTTP_REQ_CTX_get0_mem_bio,
|
||||
OSSL_HTTP_REQ_CTX_get_resp_len,
|
||||
OSSL_HTTP_REQ_CTX_set_max_response_length,
|
||||
OSSL_HTTP_is_alive
|
||||
OSSL_HTTP_is_alive,
|
||||
OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines
|
||||
- HTTP client low-level functions
|
||||
|
||||
=head1 SYNOPSIS
|
||||
@ -50,6 +51,9 @@ OSSL_HTTP_is_alive
|
||||
|
||||
int OSSL_HTTP_is_alive(const OSSL_HTTP_REQ_CTX *rctx);
|
||||
|
||||
void OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines(OSSL_HTTP_REQ_CTX *rctx,
|
||||
size_t count);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
B<OSSL_HTTP_REQ_CTX> is a context structure for an HTTP request and response,
|
||||
@ -191,6 +195,11 @@ In case the client application keeps I<rctx> but the connection then dies
|
||||
for any reason at the server side, it will notice this obtaining an
|
||||
I/O error when trying to send the next request via I<rctx>.
|
||||
|
||||
The OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines() function changes the limit
|
||||
for the number of HTTP headers which can be received in a response. The default
|
||||
value is 256. If the number of HTTP headers in a response exceeds the limit,
|
||||
then the HTTP_R_RESPONSE_TOO_MANY_HDRLINES error is indicated.
|
||||
|
||||
=head1 WARNINGS
|
||||
|
||||
The server's response may be unexpected if the hostname that was used to
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Generated by util/mkerr.pl DO NOT EDIT
|
||||
* Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
|
||||
* Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
@ -19,7 +19,10 @@
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
# ifndef OPENSSL_NO_HTTP
|
||||
|
||||
int ossl_err_load_HTTP_strings(void);
|
||||
# endif
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ extern "C" {
|
||||
|
||||
#define OSSL_HTTP_DEFAULT_MAX_LINE_LEN (4 * 1024)
|
||||
#define OSSL_HTTP_DEFAULT_MAX_RESP_LEN (100 * 1024)
|
||||
#define OSSL_HTTP_DEFAULT_MAX_RESP_HDR_LINES 256
|
||||
|
||||
|
||||
/* Low-level HTTP API */
|
||||
OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int buf_size);
|
||||
@ -105,6 +107,8 @@ int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
|
||||
const char *OSSL_HTTP_adapt_proxy(const char *proxy, const char *no_proxy,
|
||||
const char *server, int use_ssl);
|
||||
|
||||
void OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines(OSSL_HTTP_REQ_CTX *rctx,
|
||||
size_t count);
|
||||
|
||||
# endif /* !defined(OPENSSL_NO_HTTP) */
|
||||
# ifdef __cplusplus
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Generated by util/mkerr.pl DO NOT EDIT
|
||||
* Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
|
||||
* Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
@ -44,6 +44,7 @@
|
||||
# define HTTP_R_REDIRECTION_NOT_ENABLED 116
|
||||
# define HTTP_R_RESPONSE_LINE_TOO_LONG 113
|
||||
# define HTTP_R_RESPONSE_PARSE_ERROR 104
|
||||
# define HTTP_R_RESPONSE_TOO_MANY_HDRLINES 130
|
||||
# define HTTP_R_RETRY_TIMEOUT 129
|
||||
# define HTTP_R_SERVER_CANCELED_CONNECTION 127
|
||||
# define HTTP_R_SOCK_NOT_SUPPORTED 122
|
||||
|
@ -416,6 +416,66 @@ static int test_http_keep_alive_1_require_no(void)
|
||||
return test_http_keep_alive('1', 2, 0);
|
||||
}
|
||||
|
||||
static int test_http_resp_hdr_limit(size_t limit)
|
||||
{
|
||||
BIO *wbio = BIO_new(BIO_s_mem());
|
||||
BIO *rbio = BIO_new(BIO_s_mem());
|
||||
BIO *mem = NULL;
|
||||
server_args mock_args = { NULL, NULL, NULL, '0', 0 };
|
||||
int res = 0;
|
||||
OSSL_HTTP_REQ_CTX *rctx = NULL;
|
||||
|
||||
if (TEST_ptr(wbio) == 0 || TEST_ptr(rbio) == 0)
|
||||
goto err;
|
||||
|
||||
mock_args.txt = text1;
|
||||
mock_args.content_type = "text/plain";
|
||||
mock_args.version = '1';
|
||||
mock_args.out = rbio;
|
||||
mock_args.content_type = "text/plain";
|
||||
|
||||
BIO_set_callback_ex(wbio, http_bio_cb_ex);
|
||||
BIO_set_callback_arg(wbio, (char *)&mock_args);
|
||||
|
||||
rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, 8192);
|
||||
if (TEST_ptr(rctx) == 0)
|
||||
goto err;
|
||||
|
||||
if (!TEST_true(OSSL_HTTP_REQ_CTX_set_request_line(rctx, 0 /* GET */,
|
||||
NULL, NULL, RPATH)))
|
||||
goto err;
|
||||
|
||||
OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines(rctx, limit);
|
||||
mem = OSSL_HTTP_REQ_CTX_exchange(rctx);
|
||||
|
||||
if (limit == 1)
|
||||
res = TEST_ptr_null(mem);
|
||||
else
|
||||
res = TEST_ptr(mem);
|
||||
|
||||
err:
|
||||
BIO_free(wbio);
|
||||
BIO_free(rbio);
|
||||
OSSL_HTTP_REQ_CTX_free(rctx);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int test_hdr_resp_hdr_limit_none(void)
|
||||
{
|
||||
return test_http_resp_hdr_limit(0);
|
||||
}
|
||||
|
||||
static int test_hdr_resp_hdr_limit_short(void)
|
||||
{
|
||||
return (test_http_resp_hdr_limit(1));
|
||||
}
|
||||
|
||||
static int test_hdr_resp_hdr_limit_256(void)
|
||||
{
|
||||
return test_http_resp_hdr_limit(256);
|
||||
}
|
||||
|
||||
void cleanup_tests(void)
|
||||
{
|
||||
X509_free(x509);
|
||||
@ -452,5 +512,8 @@ int setup_tests(void)
|
||||
ADD_TEST(test_http_keep_alive_1_require_yes);
|
||||
ADD_TEST(test_http_keep_alive_0_require_no);
|
||||
ADD_TEST(test_http_keep_alive_1_require_no);
|
||||
ADD_TEST(test_hdr_resp_hdr_limit_none);
|
||||
ADD_TEST(test_hdr_resp_hdr_limit_short);
|
||||
ADD_TEST(test_hdr_resp_hdr_limit_256);
|
||||
return 1;
|
||||
}
|
||||
|
@ -5547,3 +5547,4 @@ ERR_pop ? 3_3_0 EXIST::FUNCTION:
|
||||
X509_STORE_get1_objects ? 3_3_0 EXIST::FUNCTION:
|
||||
OPENSSL_LH_set_thunks ? 3_3_0 EXIST::FUNCTION:
|
||||
OPENSSL_LH_doall_arg_thunk ? 3_3_0 EXIST::FUNCTION:
|
||||
OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines ? 3_3_0 EXIST::FUNCTION:HTTP
|
||||
|
Loading…
x
Reference in New Issue
Block a user