validate_headers feature

We now validate all header set operations to conform with the allowed
characters by RFC7230:

* HTAB 0x09
* VCHAR 0x20 to 0x7e
* obs-text 0x80 to 0xff

Ref https://httpwg.org/specs/rfc7230.html#header.fields

See #3407
parent d23db402
......@@ -633,19 +633,23 @@ VRT_SetHdr(VRT_CTX , VCL_HEADER hs, const char *p, ...)
AN(hs->what);
hp = VRT_selecthttp(ctx, hs->where);
CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
va_start(ap, p);
if (p == vrt_magic_string_unset) {
http_Unset(hp, hs->what);
} else {
va_start(ap, p);
b = VRT_String(hp->ws, hs->what + 1, p, ap);
va_end(ap);
if (b == NULL) {
VSLb(ctx->vsl, SLT_LostHeader, "%s", hs->what + 1);
} else {
http_Unset(hp, hs->what);
http_SetHeader(hp, b);
return;
}
if (FEATURE(FEATURE_VALIDATE_HEADERS) && ! validhdr(b)) {
VRT_fail(ctx, "Bad header %s", b);
return;
}
http_Unset(hp, hs->what);
http_SetHeader(hp, b);
}
va_end(ap);
}
/*--------------------------------------------------------------------*/
......
......@@ -222,7 +222,12 @@ tweak_feature(struct vsb *vsb, const struct parspec *par, const char *arg)
(void)par;
if (arg != NULL && arg != JSON_FMT) {
if (!strcmp(arg, "none")) {
if (!strcmp(arg, "default")) {
memset(mgt_param.feature_bits,
0, sizeof mgt_param.feature_bits);
(void)bit(mgt_param.feature_bits,
FEATURE_VALIDATE_HEADERS, BSET);
} else if (!strcmp(arg, "none")) {
memset(mgt_param.feature_bits,
0, sizeof mgt_param.feature_bits);
} else {
......@@ -271,9 +276,10 @@ struct parspec VSL_parspec[] = {
#undef DEBUG_BIT
},
{ "feature", tweak_feature, NULL,
NULL, NULL, "none",
NULL, NULL, "default",
NULL,
"Enable/Disable various minor features.\n"
"\tdefault\tSet default value\n"
"\tnone\tDisable all features.\n\n"
"Use +/- prefix to enable/disable individual feature:"
#define FEATURE_BIT(U, l, d) "\n\t" #l "\t" d
......
varnishtest "test certain mailformed requests"
varnishtest "test certain malformed requests and validate_headers"
server s1 {
rxreq
expect req.url == /4
txresp
rxreq
expect req.url == /9
txresp
} -start
varnish v1 -vcl+backend { } -start
varnish v1 -vcl+backend {
sub vcl_recv {
if (req.url == "/9") {
set req.http.foo = {"
"};
}
}
} -start
logexpect l1 -v v1 -g raw {
expect * 1001 BogoHeader {1st header has white space:.*}
......@@ -16,6 +26,7 @@ logexpect l1 -v v1 -g raw {
expect * 1012 BogoHeader {Header has ctrl char 0x0d}
expect * 1014 BogoHeader {Header has ctrl char 0x0d}
expect * 1016 BogoHeader {Missing header name:.*}
expect * 1018 VCL_Error {Bad header foo:}
} -start
client c1 {
......@@ -80,5 +91,18 @@ client c1 {
expect resp.status == 400
} -run
client c1 {
txreq -url /9
rxresp
expect resp.status == 503
} -run
logexpect l1 -wait
varnish v1 -cliok "param.set feature -validate_headers"
client c1 {
txreq -url /9
rxresp
expect resp.status == 200
} -run
......@@ -33,6 +33,10 @@ Varnish Cache Next (2021-03-15)
* counters MAIN.s_req_bodybytes and VBE.*.tools.beresp_bodybytes
are now always the number of bodybytes moved on the wire.
* Unless the new ``validate_headers`` feature is disabled, all newly
set headers are now validated to contain only characters allowed by
RFC7230. A (runtime) VCL failure is triggered if not.
================================
Varnish Cache 6.5.1 (2020-09-25)
================================
......
......@@ -74,6 +74,10 @@ FEATURE_BIT(WAIT_SILO, wait_silo,
"Wait for persistent silos to completely load before serving requests."
)
FEATURE_BIT(VALIDATE_HEADERS, validate_headers,
"Validate all header set operations to conform to RFC7230."
)
#undef FEATURE_BIT
/*lint -restore */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment