libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit d6444b0ad99797c7fe80ede9ebe7f55682b880ba
parent 6f4fd04beddf92fe6f893336fa5b2e712193666a
Author: Christian Grothoff <grothoff@gnunet.org>
Date:   Sat, 20 Sep 2025 12:15:51 +0200

fix #9634: ensure http header field names are restricted to 'token' character set as per RFC 9110

Diffstat:
Msrc/microhttpd/connection.c | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 126 insertions(+), 2 deletions(-)

diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -358,6 +358,21 @@ #define ERR_RSP_WSP_IN_FOOTER_NAME "" #endif + +/** + * Response text used when the whitespace found before colon (inside header + * name or between header name and colon). + */ +#ifdef HAVE_MESSAGES +#define ERR_RSP_INVALID_CHAR_IN_FIELD_NAME \ + "<html>" \ + "<head><title>Request broken</title></head>" \ + "<body>HTTP request has invalid character in field name.</body>" \ + "</html>" +#else +#define ERR_RSP_INVALID_CHAR_IN_FIELD_NAME "" +#endif + /** * Response text used when request header has invalid character. */ @@ -5703,6 +5718,102 @@ enum MHD_HdrLineReadRes_ /** + * Check if a character is legal inside of a field + * name according to RFC 9110. + * + * @param chr character to test + * @return true if character is allowed + */ +static bool +char_legal_in_field_name (char chr) +{ + switch (chr) + { + case '!': + case '#': + case '$': + case '%': + case '&': + case '\'': + case '*': + case '+': + case '-': + case '.': + case '^': + case '_': + case '`': + case '|': + case '~': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return true; + default: + return false; + } +} + + +/** * Find the end of the request header line and make basic header parsing. * Handle errors and header folding. * @param c the connection to process @@ -5749,6 +5860,9 @@ get_req_header (struct MHD_Connection *c, /* Allow zero-length header (field) name. Violates RFC 9110, section 5.1-2 */ const bool allow_empty_name = (-2 >= discp_lvl); + /* Allow non-tchar characters in header (field) name. + Violates RFC 9110, section 5.1 */ + const bool allow_extended_charset = (-2 >= discp_lvl); /* Allow whitespace before colon. Violates RFC 9112, section 5.1-2 */ const bool allow_wsp_before_colon = (-3 >= discp_lvl); @@ -6080,10 +6194,20 @@ get_req_header (struct MHD_Connection *c, mhd_assert ('\r' != chr); mhd_assert ('\n' != chr); mhd_assert ('\0' != chr); - if ((! c->rq.hdrs.hdr.name_end_found) && - (! c->rq.hdrs.hdr.starts_with_ws)) + if ( (! c->rq.hdrs.hdr.name_end_found) && + (! c->rq.hdrs.hdr.starts_with_ws) ) { /* Processing the header (field) name */ + if ( (! allow_extended_charset) && + (':' != chr) && + (! char_legal_in_field_name (chr)) ) + { + transmit_error_response_static (c, + MHD_HTTP_BAD_REQUEST, + ERR_RSP_INVALID_CHAR_IN_FIELD_NAME); + return MHD_HDR_LINE_READING_DATA_ERROR; + } + if (':' == chr) { if (0 == c->rq.hdrs.hdr.ws_start)