/* Low-level HTTP API implementation */
+static int no_crlf(const char *component, const char *value)
+{
+ if (value != NULL && strpbrk(value, "\r\n") != NULL) {
+ ERR_raise_data(ERR_LIB_HTTP, ERR_R_PASSED_INVALID_ARGUMENT,
+ "CR or LF character in %s", component);
+ return 0;
+ }
+ return 1;
+}
+
OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int buf_size)
{
OSSL_HTTP_REQ_CTX *rctx;
ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
+ if (!no_crlf("server", server)
+ || !no_crlf("port", port)
+ || !no_crlf("path", path))
+ return 0;
BIO_free(rctx->mem);
if ((rctx->mem = BIO_new(BIO_s_mem())) == NULL)
return 0;
ERR_raise(ERR_LIB_HTTP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
+ if (!no_crlf("header name", name)
+ || !no_crlf("header value", value))
+ return 0;
if (BIO_puts(rctx->mem, name) <= 0)
return 0;
} else {
if (HAS_CASE_PREFIX(content_type, "text/"))
rctx->text = 1;
- if (BIO_printf(rctx->mem, "Content-Type: %s\r\n", content_type) <= 0)
+ if (!OSSL_HTTP_REQ_CTX_add1_header(rctx, "Content-Type", content_type))
return 0;
}
{
#undef BUF_SIZE
#define BUF_SIZE (8 * 1024)
- char *mbuf = OPENSSL_malloc(BUF_SIZE);
+ char *mbuf = NULL;
char *mbufp;
int read_len = 0;
int ret = 0;
- BIO *fbio = BIO_new(BIO_f_buffer());
+ BIO *fbio = NULL;
int rv;
time_t max_time = timeout > 0 ? time(NULL) + timeout : 0;
}
if (port == NULL || *port == '\0')
port = OSSL_HTTPS_PORT;
+ if (!no_crlf("server", server) || !no_crlf("port", port))
+ goto end;
- if (mbuf == NULL || fbio == NULL) {
+ if ((mbuf = OPENSSL_malloc(BUF_SIZE)) == NULL
+ || (fbio = BIO_new(BIO_f_buffer())) == NULL) {
BIO_printf(bio_err /* may be NULL */, "%s: out of memory", prog);
goto end;
}
an absoluteURI. In this case it indicates HTTP proxy use and provides also the
server (and optionally the port) that the proxy shall forward the request to.
In this case the I<server> and I<port> arguments must be NULL.
+The I<server>, I<port>, and I<path> arguments must not contain CR or LF
+characters.
OSSL_HTTP_REQ_CTX_add1_header() adds header I<name> with value I<value> to the
context I<rctx>. It can be called more than once to add multiple header lines.
+The I<name> and I<value> arguments must not contain CR or LF characters.
For example, to add a C<Host> header for C<example.com> you would call:
OSSL_HTTP_REQ_CTX_add1_header(ctx, "Host", "example.com");
I<content_type> must be NULL if I<req> is NULL.
If I<content_type> isn't NULL,
the HTTP header C<Content-Type> is also added with the given string value.
+The I<content_type> argument must not contain CR or LF characters.
The header lines are added to the internal memory B<BIO> for the request header.
OSSL_HTTP_REQ_CTX_nbio() attempts to send the request prepared in I<rctx>
optionally using proxy client credentials I<proxyuser> and I<proxypass>,
to connect with TLS protection ultimately to I<server> and I<port>.
If the I<port> argument is NULL or the empty string it defaults to "443".
+The I<server> and I<port> arguments must not contain CR or LF characters.
If the I<timeout> parameter is > 0 this indicates the maximum number of
seconds the connection setup is allowed to take.
A value <= 0 enables waiting indefinitely, i.e., no timeout.
the length of the data in I<req> does not need to be determined in advance: the
BIO will be read on-the-fly while sending the request, which supports streaming.
The optional list I<headers> may contain additional custom HTTP header lines.
+The I<path>, I<headers> names and values, and I<content_type> must not contain
+CR or LF characters.
The I<max_resp_len> parameter specifies the maximum allowed
response content length, where the value 0 indicates no limit.
For the meaning of the I<expected_content_type>, I<expect_asn1>, I<timeout>,
#include <openssl/http.h>
#include <openssl/pem.h>
#include <openssl/x509v3.h>
+#include <openssl/err.h>
#include <string.h>
#include "testutil.h"
return test_http_url_invalid("https://[FF01::101]pkix");
}
+static int test_http_crlf_rejected(void)
+{
+ BIO *wbio = BIO_new(BIO_s_mem());
+ BIO *rbio = BIO_new(BIO_s_mem());
+ BIO *req = BIO_new(BIO_s_mem());
+ BIO *proxy_bio = BIO_new(BIO_s_mem());
+ OSSL_HTTP_REQ_CTX *rctx = NULL;
+ int res = 0;
+
+ if (!TEST_ptr(wbio)
+ || !TEST_ptr(rbio)
+ || !TEST_ptr(req)
+ || !TEST_ptr(proxy_bio)
+ || !TEST_int_eq(BIO_puts(req, "x"), 1)
+ || !TEST_ptr(rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, 0)))
+ goto err;
+
+ ERR_clear_error();
+ res = TEST_false(OSSL_HTTP_REQ_CTX_set_request_line(rctx, 0 /* GET */,
+ NULL, NULL, "/path\r\nInjected: value"))
+ && TEST_false(OSSL_HTTP_REQ_CTX_set_request_line(rctx, 0 /* GET */,
+ "server\r\nInjected: value", "80", RPATH))
+ && TEST_false(OSSL_HTTP_REQ_CTX_set_request_line(rctx, 0 /* GET */,
+ "server", "80\r\nInjected: value", RPATH))
+ && TEST_true(OSSL_HTTP_REQ_CTX_set_request_line(rctx, 0 /* GET */,
+ NULL, NULL, RPATH))
+ && TEST_false(OSSL_HTTP_REQ_CTX_add1_header(rctx,
+ "X-Test\r\nInjected", "value"))
+ && TEST_false(OSSL_HTTP_REQ_CTX_add1_header(rctx,
+ "X-Test", "value\r\nInjected: value"))
+ && TEST_false(OSSL_HTTP_set1_request(rctx, RPATH, NULL,
+ "text/plain\r\nInjected: value", req,
+ NULL, 0 /* expect_asn1 */, 0 /* max_resp_len */,
+ 0 /* timeout */, 0 /* keep_alive */))
+ && TEST_false(OSSL_HTTP_proxy_connect(proxy_bio,
+ "server\r\nInjected: value", "443", NULL, NULL,
+ 0 /* timeout */, NULL, NULL))
+ && TEST_false(OSSL_HTTP_proxy_connect(proxy_bio,
+ "server", "443\r\nInjected: value", NULL, NULL,
+ 0 /* timeout */, NULL, NULL));
+
+err:
+ ERR_clear_error();
+ OSSL_HTTP_REQ_CTX_free(rctx);
+ BIO_free(wbio);
+ BIO_free(rbio);
+ BIO_free(req);
+ BIO_free(proxy_bio);
+ return res;
+}
+
static int test_http_get_txt(void)
{
return test_http_method(1 /* GET */, 1, HTTP_STATUS_CODE_OK);
ADD_TEST(test_http_url_invalid_prefix);
ADD_TEST(test_http_url_invalid_port);
ADD_TEST(test_http_url_invalid_path);
+ ADD_TEST(test_http_crlf_rejected);
ADD_TEST(test_http_get_txt);
ADD_TEST(test_http_get_txt_redirected);