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, ...) ...@@ -633,19 +633,23 @@ VRT_SetHdr(VRT_CTX , VCL_HEADER hs, const char *p, ...)
AN(hs->what); AN(hs->what);
hp = VRT_selecthttp(ctx, hs->where); hp = VRT_selecthttp(ctx, hs->where);
CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
va_start(ap, p);
if (p == vrt_magic_string_unset) { if (p == vrt_magic_string_unset) {
http_Unset(hp, hs->what); http_Unset(hp, hs->what);
} else { } else {
va_start(ap, p);
b = VRT_String(hp->ws, hs->what + 1, p, ap); b = VRT_String(hp->ws, hs->what + 1, p, ap);
va_end(ap);
if (b == NULL) { if (b == NULL) {
VSLb(ctx->vsl, SLT_LostHeader, "%s", hs->what + 1); VSLb(ctx->vsl, SLT_LostHeader, "%s", hs->what + 1);
} else { return;
http_Unset(hp, hs->what); }
http_SetHeader(hp, b); 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) ...@@ -222,7 +222,12 @@ tweak_feature(struct vsb *vsb, const struct parspec *par, const char *arg)
(void)par; (void)par;
if (arg != NULL && arg != JSON_FMT) { 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, memset(mgt_param.feature_bits,
0, sizeof mgt_param.feature_bits); 0, sizeof mgt_param.feature_bits);
} else { } else {
...@@ -271,9 +276,10 @@ struct parspec VSL_parspec[] = { ...@@ -271,9 +276,10 @@ struct parspec VSL_parspec[] = {
#undef DEBUG_BIT #undef DEBUG_BIT
}, },
{ "feature", tweak_feature, NULL, { "feature", tweak_feature, NULL,
NULL, NULL, "none", NULL, NULL, "default",
NULL, NULL,
"Enable/Disable various minor features.\n" "Enable/Disable various minor features.\n"
"\tdefault\tSet default value\n"
"\tnone\tDisable all features.\n\n" "\tnone\tDisable all features.\n\n"
"Use +/- prefix to enable/disable individual feature:" "Use +/- prefix to enable/disable individual feature:"
#define FEATURE_BIT(U, l, d) "\n\t" #l "\t" d #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 { server s1 {
rxreq rxreq
expect req.url == /4 expect req.url == /4
txresp txresp
rxreq
expect req.url == /9
txresp
} -start } -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 { logexpect l1 -v v1 -g raw {
expect * 1001 BogoHeader {1st header has white space:.*} expect * 1001 BogoHeader {1st header has white space:.*}
...@@ -16,6 +26,7 @@ logexpect l1 -v v1 -g raw { ...@@ -16,6 +26,7 @@ logexpect l1 -v v1 -g raw {
expect * 1012 BogoHeader {Header has ctrl char 0x0d} expect * 1012 BogoHeader {Header has ctrl char 0x0d}
expect * 1014 BogoHeader {Header has ctrl char 0x0d} expect * 1014 BogoHeader {Header has ctrl char 0x0d}
expect * 1016 BogoHeader {Missing header name:.*} expect * 1016 BogoHeader {Missing header name:.*}
expect * 1018 VCL_Error {Bad header foo:}
} -start } -start
client c1 { client c1 {
...@@ -80,5 +91,18 @@ client c1 { ...@@ -80,5 +91,18 @@ client c1 {
expect resp.status == 400 expect resp.status == 400
} -run } -run
client c1 {
txreq -url /9
rxresp
expect resp.status == 503
} -run
logexpect l1 -wait 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) ...@@ -33,6 +33,10 @@ Varnish Cache Next (2021-03-15)
* counters MAIN.s_req_bodybytes and VBE.*.tools.beresp_bodybytes * counters MAIN.s_req_bodybytes and VBE.*.tools.beresp_bodybytes
are now always the number of bodybytes moved on the wire. 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) Varnish Cache 6.5.1 (2020-09-25)
================================ ================================
......
...@@ -74,6 +74,10 @@ FEATURE_BIT(WAIT_SILO, wait_silo, ...@@ -74,6 +74,10 @@ FEATURE_BIT(WAIT_SILO, wait_silo,
"Wait for persistent silos to completely load before serving requests." "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 #undef FEATURE_BIT
/*lint -restore */ /*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