]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
http: only accept ';' as a separator for custom headers
authorDaniel Stenberg <daniel@haxx.se>
Thu, 23 Oct 2025 09:16:03 +0000 (11:16 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 23 Oct 2025 12:18:11 +0000 (14:18 +0200)
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

lib/http.c

index 44d36b9a847ad3077d1d68b8995c2932409b3af5..0b55796dc529a5d5f7dca89767617deb305c15b1 100644 (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;
 }