http: only accept ';' as a separator for custom headers

When parsing incoming headers, they need to have a plain normal colon.

Previously out of convenience we used the same parser function for both
cases (incoming + custom set headers via the API) which made the
function too liberal for incoming HTTP traffic.

Closes #19200
This commit is contained in:
Daniel Stenberg 2025-10-23 11:16:03 +02:00
parent 2edce44065
commit 0d1d35fa34
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2

View file

@ -277,14 +277,13 @@ static bool http_header_is_empty(const char *header)
/*
* Strip off leading and trailing whitespace from the value in the given HTTP
* header line and return a strdup()ed copy. Returns NULL in case of
* allocation failure or bad input. Returns an empty string if the header
* value consists entirely of whitespace.
* header line and return a strdup()ed copy in 'valp' - returns an empty
* string if the header value consists entirely of whitespace.
*
* If the header is provided as "name;", ending with a semicolon, it must
* return a blank string.
* If the header is provided as "name;", ending with a semicolon, it returns a
* blank string.
*/
char *Curl_copy_header_value(const char *header)
static CURLcode copy_custom_value(const char *header, char **valp)
{
struct Curl_str out;
@ -294,9 +293,37 @@ char *Curl_copy_header_value(const char *header)
curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE);
curlx_str_trimblanks(&out);
return Curl_memdup0(curlx_str(&out), curlx_strlen(&out));
*valp = Curl_memdup0(curlx_str(&out), curlx_strlen(&out));
if(*valp)
return CURLE_OK;
return CURLE_OUT_OF_MEMORY;
}
/* bad input */
*valp = NULL;
return CURLE_BAD_FUNCTION_ARGUMENT;
}
/*
* Strip off leading and trailing whitespace from the value in the given HTTP
* header line and return a strdup()ed copy in 'valp' - returns an empty
* string if the header value consists entirely of whitespace.
*
* This function MUST be used after the header has already been confirmed to
* lead with "word:".
*/
char *Curl_copy_header_value(const char *header)
{
struct Curl_str out;
/* find the end of the header name */
if(!curlx_str_until(&header, &out, MAX_HTTP_RESP_HEADER_SIZE, ':') &&
!curlx_str_single(&header, ':')) {
curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE);
curlx_str_trimblanks(&out);
return Curl_memdup0(curlx_str(&out), curlx_strlen(&out));
}
/* bad input, should never happen */
DEBUGASSERT(0);
return NULL;
}
@ -1906,9 +1933,10 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data)
custom Host: header if this is NOT a redirect, as setting Host: in the
redirected request is being out on thin ice. Except if the hostname
is the same as the first one! */
char *cookiehost = Curl_copy_header_value(ptr);
if(!cookiehost)
return CURLE_OUT_OF_MEMORY;
char *cookiehost;
CURLcode result = copy_custom_value(ptr, &cookiehost);
if(result)
return result;
if(!*cookiehost)
/* ignore empty data */
free(cookiehost);
@ -2657,7 +2685,6 @@ static CURLcode http_add_connection_hd(struct Curl_easy *data,
const char *sep = "Connection: ";
CURLcode result = CURLE_OK;
size_t rlen = curlx_dyn_len(req);
char *value;
bool skip;
/* Add the 1st custom "Connection: " header, if there is one */
@ -2665,9 +2692,10 @@ static CURLcode http_add_connection_hd(struct Curl_easy *data,
if(curl_strnequal(head->data, "Connection", 10) &&
Curl_headersep(head->data[10]) &&
!http_header_is_empty(head->data)) {
value = Curl_copy_header_value(head->data);
if(!value)
return CURLE_OUT_OF_MEMORY;
char *value;
result = copy_custom_value(head->data, &value);
if(result)
return result;
result = curlx_dyn_addf(req, "%s%s", sep, value);
sep = ", ";
free(value);
@ -3346,11 +3374,11 @@ static CURLcode http_header_p(struct Curl_easy *data,
#endif
if((407 == k->httpcode) && HD_IS(hd, hdlen, "Proxy-authenticate:")) {
char *auth = Curl_copy_header_value(hd);
CURLcode result;
if(!auth)
return CURLE_OUT_OF_MEMORY;
result = Curl_http_input_auth(data, TRUE, auth);
free(auth);
CURLcode result = auth ? CURLE_OK : CURLE_OUT_OF_MEMORY;
if(!result) {
result = Curl_http_input_auth(data, TRUE, auth);
free(auth);
}
return result;
}
#ifdef USE_SPNEGO
@ -3525,9 +3553,11 @@ static CURLcode http_header_w(struct Curl_easy *data,
if((401 == k->httpcode) && HD_IS(hd, hdlen, "WWW-Authenticate:")) {
char *auth = Curl_copy_header_value(hd);
if(!auth)
return CURLE_OUT_OF_MEMORY;
result = Curl_http_input_auth(data, FALSE, auth);
free(auth);
result = CURLE_OUT_OF_MEMORY;
else {
result = Curl_http_input_auth(data, FALSE, auth);
free(auth);
}
}
return result;
}