From c3f8e4e2b4a334697e7ce3168495f20a46b398bd Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Tue, 3 May 2022 09:31:11 +0200
Subject: [PATCH 1/2] tls: check more TLS details for connection reuse

CVE-2022-27782

Reported-by: Harry Sintonen
Bug: https://curl.se/docs/CVE-2022-27782.html
---
 lib/setopt.c       | 29 +++++++++++++++++------------
 lib/url.c          | 23 ++++++++++++++++-------
 lib/urldata.h      | 13 +++++++------
 lib/vtls/gtls.c    | 32 +++++++++++++++++---------------
 lib/vtls/openssl.c | 10 +++++-----
 lib/vtls/vtls.c    | 20 ++++++++++++++++++++
 6 files changed, 82 insertions(+), 45 deletions(-)

Index: curl-7.37.0/lib/url.c
===================================================================
--- curl-7.37.0.orig/lib/url.c
+++ curl-7.37.0/lib/url.c
@@ -150,6 +150,7 @@ static CURLcode parse_url_login(struct S
 CURLcode parse_login_details(const char *login, const size_t len,
                              char **userptr, char **passwdptr,
                              char **optionsptr);
+static unsigned int get_protocol_family(unsigned int protocol);
 
 /*
  * Protocol table.
@@ -2133,6 +2134,7 @@ CURLcode Curl_setopt(struct SessionHandl
 
   case CURLOPT_SSL_OPTIONS:
     arg = va_arg(param, long);
+    data->set.ssl.ssl_options = (unsigned char)(arg & 0xff);
     data->set.ssl_enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE;
     break;
 
@@ -2922,6 +2924,17 @@ find_oldest_idle_connection_in_bundle(st
   return conn_candidate;
 }
 
+static bool ssh_config_matches(struct connectdata *one,
+                               struct connectdata *two)
+{
+#ifdef USE_LIBSSH2
+  return (Curl_safecmp(one->proto.sshc.rsa, two->proto.sshc.rsa) &&
+          Curl_safecmp(one->proto.sshc.rsa_pub, two->proto.sshc.rsa_pub));
+#else
+  return TRUE;
+#endif
+}
+
 /*
  * Given one filled in connection struct (named needle), this function should
  * detect if there already is one that has all the significant details
@@ -3062,6 +3075,12 @@ ConnectionExists(struct SessionHandle *d
         }
       }
 
+      if(get_protocol_family(needle->handler) == CURLPROTO_SFTP ||
+         get_protocol_family(needle->handler) == CURLPROTO_SCP ) {
+        if(!ssh_config_matches(needle, check))
+          continue;
+      }
+
       if((needle->handler->flags&PROTOPT_SSL) !=
          (check->handler->flags&PROTOPT_SSL))
         /* don't do mixed SSL and non-SSL connections */
@@ -3673,6 +3692,8 @@ static struct connectdata *allocate_conn
   conn->verifypeer = data->set.ssl.verifypeer;
   conn->verifyhost = data->set.ssl.verifyhost;
 
+  conn->ssl_config.ssl_options = data->set.ssl.ssl_options;
+
   conn->ip_version = data->set.ipver;
 
 #if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
@@ -6042,3 +6063,112 @@ CURLcode Curl_do_more(struct connectdata
   return result;
 }
 
+/*
+* get_protocol_family()
+*
+* This is used to return the protocol family for a given protocol.
+*
+* Parameters:
+*
+* protocol  [in]  - A single bit protocol identifier such as HTTP or HTTPS.
+*
+* Returns the family as a single bit protocol identifier.
+*/
+
+static unsigned int get_protocol_family(unsigned int protocol)
+{
+  unsigned int family;
+
+  switch(protocol) {
+  case CURLPROTO_HTTP:
+  case CURLPROTO_HTTPS:
+    family = CURLPROTO_HTTP;
+    break;
+
+  case CURLPROTO_FTP:
+  case CURLPROTO_FTPS:
+    family = CURLPROTO_FTP;
+    break;
+
+  case CURLPROTO_SCP:
+    family = CURLPROTO_SCP;
+    break;
+
+  case CURLPROTO_SFTP:
+    family = CURLPROTO_SFTP;
+    break;
+
+  case CURLPROTO_TELNET:
+    family = CURLPROTO_TELNET;
+    break;
+
+  case CURLPROTO_LDAP:
+  case CURLPROTO_LDAPS:
+    family = CURLPROTO_LDAP;
+    break;
+
+  case CURLPROTO_DICT:
+    family = CURLPROTO_DICT;
+    break;
+
+  case CURLPROTO_FILE:
+    family = CURLPROTO_FILE;
+    break;
+
+  case CURLPROTO_TFTP:
+    family = CURLPROTO_TFTP;
+    break;
+
+  case CURLPROTO_IMAP:
+  case CURLPROTO_IMAPS:
+    family = CURLPROTO_IMAP;
+    break;
+
+  case CURLPROTO_POP3:
+  case CURLPROTO_POP3S:
+    family = CURLPROTO_POP3;
+    break;
+
+  case CURLPROTO_SMTP:
+  case CURLPROTO_SMTPS:
+      family = CURLPROTO_SMTP;
+      break;
+
+  case CURLPROTO_RTSP:
+    family = CURLPROTO_RTSP;
+    break;
+
+  case CURLPROTO_RTMP:
+  case CURLPROTO_RTMPS:
+    family = CURLPROTO_RTMP;
+    break;
+
+  case CURLPROTO_RTMPT:
+  case CURLPROTO_RTMPTS:
+    family = CURLPROTO_RTMPT;
+    break;
+
+  case CURLPROTO_RTMPE:
+    family = CURLPROTO_RTMPE;
+    break;
+
+  case CURLPROTO_RTMPTE:
+    family = CURLPROTO_RTMPTE;
+    break;
+
+  case CURLPROTO_GOPHER:
+    family = CURLPROTO_GOPHER;
+    break;
+
+  // case CURLPROTO_SMB:
+  // case CURLPROTO_SMBS:
+  //   family = CURLPROTO_SMB;
+  //   break;
+
+  default:
+      family = 0;
+      break;
+  }
+
+  return family;
+}
\ No newline at end of file
Index: curl-7.37.0/lib/urldata.h
===================================================================
--- curl-7.37.0.orig/lib/urldata.h
+++ curl-7.37.0/lib/urldata.h
@@ -370,6 +370,7 @@ struct ssl_config_data {
   char *CAfile;          /* certificate to verify peer against */
   const char *CRLfile;   /* CRL to check certificate revocation */
   const char *issuercert;/* optional issuer certificate filename */
+  unsigned char ssl_options;  /* the CURLOPT_SSL_OPTIONS bitmask */
   char *clientcert;
   char *random_file;     /* path to file containing "random" data */
   char *egdsocket;       /* path to file containing the EGD daemon socket */
Index: curl-7.37.0/lib/vtls/vtls.c
===================================================================
--- curl-7.37.0.orig/lib/vtls/vtls.c
+++ curl-7.37.0/lib/vtls/vtls.c
@@ -105,6 +105,7 @@ Curl_ssl_config_matches(struct ssl_confi
                         struct ssl_config_data* needle)
 {
   if((data->version == needle->version) &&
+     (data->ssl_options == needle->ssl_options) &&
      (data->verifypeer == needle->verifypeer) &&
      (data->verifyhost == needle->verifyhost) &&
      safe_strequal(data->CApath, needle->CApath) &&
@@ -113,6 +114,12 @@ Curl_ssl_config_matches(struct ssl_confi
      safe_strequal(data->clientcert, needle->clientcert) &&
      safe_strequal(data->random_file, needle->random_file) &&
      safe_strequal(data->egdsocket, needle->egdsocket) &&
+#ifdef USE_TLS_SRP
+     safe_strequal(data->username, needle->username) &&
+     safe_strequal(data->password, needle->password) &&
+     (data->authtype == needle->authtype) &&
+#endif
+     safe_strequal(data->CRLfile, needle->CRLfile) &&
      safe_strequal(data->cipher_list, needle->cipher_list))
     return TRUE;
 
@@ -127,6 +134,10 @@ Curl_clone_ssl_config(struct ssl_config_
   dest->verifyhost = source->verifyhost;
   dest->verifypeer = source->verifypeer;
   dest->version = source->version;
+  dest->ssl_options = source->ssl_options;
+#ifdef USE_TLS_SRP
+  dest->authtype = source->authtype;
+#endif
 
   if(source->CAfile) {
     dest->CAfile = strdup(source->CAfile);
@@ -185,6 +196,32 @@ Curl_clone_ssl_config(struct ssl_config_
   else
     dest->clientcert = NULL;
 
+  if(source->CRLfile) {
+    dest->CRLfile = strdup(source->CRLfile);
+    if(!dest->CRLfile)
+      return FALSE;
+  }
+  else
+    dest->CRLfile = NULL;
+
+#ifdef USE_TLS_SRP
+  if(source->username) {
+    dest->username = strdup(source->username);
+    if(!dest->username)
+      return FALSE;
+  }
+  else
+    dest->username = NULL;
+
+  if(source->password) {
+    dest->password = strdup(source->password);
+    if(!dest->password)
+      return FALSE;
+  }
+  else
+    dest->password = NULL;
+#endif
+
   return TRUE;
 }
 
@@ -197,6 +234,12 @@ void Curl_free_ssl_config(struct ssl_con
   Curl_safefree(sslc->egdsocket);
   Curl_safefree(sslc->random_file);
   Curl_safefree(sslc->clientcert);
+
+  Curl_safefree(sslc->CRLfile);
+#ifdef USE_TLS_SRP
+  Curl_safefree(sslc->username);
+  Curl_safefree(sslc->password);
+#endif
 }
 
 
Index: curl-7.37.0/lib/ssh.h
===================================================================
--- curl-7.37.0.orig/lib/ssh.h
+++ curl-7.37.0/lib/ssh.h
@@ -108,8 +108,8 @@ struct ssh_conn {
   const char *authlist;       /* List of auth. methods, managed by libssh2 */
 #ifdef USE_LIBSSH2
   const char *passphrase;     /* pass-phrase to use */
-  char *rsa_pub;              /* path name */
-  char *rsa;                  /* path name */
+  char *rsa_pub;              /* strdup'ed public key file */
+  char *rsa;                  /* strdup'ed private key file */
   bool authed;                /* the connection has been authenticated fine */
   sshstate state;             /* always use ssh.c:state() to change state! */
   sshstate nextstate;         /* the state to goto after stopping */
