From: Willy Tarreau Date: Thu, 29 Oct 2026 14:14:45 +0000 (+0100) Subject: MINOR: http: define http_hdng_chk for ambient hardening checks to be run X-Git-Tag: v3.5-dev11~27 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=f22a81c9fd01675832f3fb0f778561aa4a0da3f0;p=haproxy.git MINOR: http: define http_hdng_chk for ambient hardening checks to be run In order to enforce HTTP hardening checks, we have to instrument functions at many different layers of the HTTP parsing code. First tests show that we might need to add extra parameters to numerous functions, just to forward requirements and results. A simpler, less elegant but much more efficient approach consists sharing the parsing checks globally (local to the thread) so that anywhere in the HTTP stack it is possible to know what checks are enforced. It is not different from the global acceptable violations after all, except that this has to be preset, consumed and cleared by the caller. The benefit is that it will then be possible to define the checks before parsing, and let any function anywhere in the chain participate to the operation, including for reporting possible anomalies. The http_harden(flag) function does the repetitive part of the check and returning what final action was performed and should be easy to deploy. --- diff --git a/include/haproxy/http-t.h b/include/haproxy/http-t.h index 684a69325..7635231fb 100644 --- a/include/haproxy/http-t.h +++ b/include/haproxy/http-t.h @@ -270,6 +270,18 @@ struct http_hdng_prf { char *file; /* file of the declaration */ }; +/* description of a check to be done in the HTTP stack: + * - rules = mask of the checks to apply (comes from the profile) ; + * - found = mask of checks met even if not blocked, preset to zero + * before the call and filled by the stack ; + * - fixed = mask of actions performed + */ +struct http_hdng_chk { + uint32_t rules; // rules to apply + uint32_t found; // what was found + uint32_t fixed; // mask of actions performed +}; + #endif /* _HAPROXY_HTTP_T_H */ /* diff --git a/include/haproxy/http.h b/include/haproxy/http.h index d862db6ae..33b73df00 100644 --- a/include/haproxy/http.h +++ b/include/haproxy/http.h @@ -39,6 +39,8 @@ extern const struct http_hardening_kw http_hardening_kws[]; extern struct http_hdng_prf http_hardening_untrusted; extern struct http_hdng_prf http_hardening_trusted; +extern THREAD_LOCAL struct http_hdng_chk http_hdng_chk; + enum http_meth_t find_http_meth(const char *str, const int len); int http_get_status_idx(unsigned int status); const char *http_get_reason(unsigned int status); @@ -104,6 +106,34 @@ static inline int http_is_param_delimiter(char c, char delim) return c == '&' || c == ';' || c == delim; } +/* reset an http hardening check after a call */ +static inline void http_reset_hardening_check(struct http_hdng_chk *chk) +{ + chk->rules = 0; + chk->found = 0; + chk->fixed = 0; +} + +/* marks the global http_hdng_chk.found with the provided check, then checks if this + * test is enabled or not. If so, it will put it in the hardening fixed value + * and return true, otherwise it will return false. This allows to construct + * simple checks like this and have all fields properly filled: + * + * if (unlikely(smiley_in_header_name)) + * if (http_harden(HTTP_HRD_SMILEY_HNAME)) + * goto fail; + * + * Make sure to pass it single-bit options to keep found/fixed easy to grasp. + */ +static inline int http_harden(uint32_t flag) +{ + http_hdng_chk.found |= flag; + if (!(http_hdng_chk.rules & flag)) + return 0; + http_hdng_chk.fixed |= flag; + return 1; +} + /* Match language range with language tag. RFC2616 14.4: * * A language-range matches a language-tag if it exactly equals diff --git a/src/http.c b/src/http.c index daec94fe3..c3d2502ba 100644 --- a/src/http.c +++ b/src/http.c @@ -377,6 +377,11 @@ const struct ist http_known_methods[HTTP_METH_OTHER] = { long http_err_status_codes[512 / sizeof(long)] = { }; long http_fail_status_codes[512 / sizeof(long)] = { }; +/* hardening checks to be run through the HTTP stack, preset by the caller, + * must always be cleared by the one that sets or checks them after use. + */ +THREAD_LOCAL struct http_hdng_chk http_hdng_chk = { }; + /* * returns a known method among HTTP_METH_* or HTTP_METH_OTHER for all unknown * ones.