From: Daniel Stenberg Date: Thu, 23 Oct 2025 09:16:03 +0000 (+0200) Subject: http: only accept ';' as a separator for custom headers X-Git-Tag: rc-8_17_0-3~46 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0d1d35fa346907dc3a289a477962ee6b58205cbe;p=thirdparty%2Fcurl.git 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 --- diff --git a/lib/http.c b/lib/http.c index 44d36b9a84..0b55796dc5 100644 --- a/lib/http.c +++ b/lib/http.c @@ -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; }