From 125302094326ad5eb0ea87f2d2ece6ceab1b1e59 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Fri, 22 Apr 2022 08:19:18 +0200
Subject: [PATCH] http: avoid auth/cookie on redirects same host diff port

CVE-2022-27776

Reported-by: Harry Sintonen
Bug: https://curl.se/docs/CVE-2022-27776.html
---
 lib/http.c    | 34 ++++++++++++++++++++++------------
 lib/urldata.h | 16 +++++++++-------
 2 files changed, 31 insertions(+), 19 deletions(-)

Index: curl-7.60.0/lib/http.c
===================================================================
--- curl-7.60.0.orig/lib/http.c
+++ curl-7.60.0/lib/http.c
@@ -642,6 +642,20 @@ output_auth_headers(struct connectdata *
   return CURLE_OK;
 }
 
+/*
+ * allow_auth_to_host() tells if autentication, cookies or other "sensitive
+ * data" can (still) be sent to this host.
+ */
+static bool allow_auth_to_host(struct Curl_easy *data, struct connectdata *conn)
+{
+  return (!data->state.this_is_a_follow ||
+          data->set.allow_auth_to_other_hosts ||
+          (data->state.first_host &&
+           strcasecompare(data->state.first_host, conn->host.name) &&
+           (data->state.first_remote_port == conn->remote_port) &&
+           (data->state.first_remote_protocol == conn->handler->protocol)));
+}
+
 /**
  * Curl_http_output_auth() setups the authentication headers for the
  * host/proxy and the correct authentication
@@ -710,15 +724,14 @@ Curl_http_output_auth(struct connectdata
        with it */
     authproxy->done = TRUE;
 
-  /* To prevent the user+password to get sent to other than the original
-     host due to a location-follow, we do some weirdo checks here */
-  if(!data->state.this_is_a_follow ||
-     conn->bits.netrc ||
-     !data->state.first_host ||
-     data->set.allow_auth_to_other_hosts ||
-     strcasecompare(data->state.first_host, conn->host.name)) {
+  /* To prevent the user+password to get sent to other than the original host
+     due to a location-follow */
+  if(allow_auth_to_host(data, conn)
+#ifndef CURL_DISABLE_NETRC
+     || conn->bits.netrc
+#endif
+    )
     result = output_auth_headers(conn, authhost, request, path, FALSE);
-  }
   else
     authhost->done = TRUE;
 
@@ -1714,10 +1727,7 @@ CURLcode Curl_add_custom_headers(struct
           else if(checkprefix("Authorization:", headers->data) &&
                   /* be careful of sending this potentially sensitive header to
                      other hosts */
-                  (data->state.this_is_a_follow &&
-                   data->state.first_host &&
-                   !data->set.allow_auth_to_other_hosts &&
-                   !strcasecompare(data->state.first_host, conn->host.name)))
+                  !allow_auth_to_host(data, conn))
             ;
           else {
             result = Curl_add_bufferf(req_buffer, "%s\r\n", headers->data);
@@ -1873,6 +1883,7 @@ CURLcode Curl_http(struct connectdata *c
       return CURLE_OUT_OF_MEMORY;
 
     data->state.first_remote_port = conn->remote_port;
+    data->state.first_remote_protocol = conn->handler->protocol;
   }
   http->writebytecount = http->readbytecount = 0;
 
Index: curl-7.60.0/lib/urldata.h
===================================================================
--- curl-7.60.0.orig/lib/urldata.h
+++ curl-7.60.0/lib/urldata.h
@@ -1227,13 +1227,15 @@ struct UrlState {
                                 bytes / second */
   bool this_is_a_follow; /* this is a followed Location: request */
   bool refused_stream; /* this was refused, try again */
-  char *first_host; /* host name of the first (not followed) request.
-                       if set, this should be the host name that we will
-                       sent authorization to, no else. Used to make Location:
-                       following not keep sending user+password... This is
-                       strdup() data.
-                    */
-  int first_remote_port; /* remote port of the first (not followed) request */
+
+  /* host name, port number and protocol of the first (not followed) request.
+     if set, this should be the host name that we will sent authorization to,
+     no else. Used to make Location: following not keep sending user+password.
+     This is strdup()ed data. */
+  char *first_host;
+  int first_remote_port;
+  unsigned int first_remote_protocol;
+
   struct curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
   long sessionage;                  /* number of the most recent session */
   unsigned int tempcount; /* number of entries in use in tempwrite, 0 - 3 */
