Revert "Imported Upstream version 7.53.1"
[platform/upstream/curl.git] / lib / vtls / schannel.c
index bd92399..511bd11 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
  * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
- * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -59,7 +59,6 @@
 #include "x509asn1.h"
 #include "curl_printf.h"
 #include "system_win32.h"
-#include "hostcheck.h"
 
  /* The last #include file should be: */
 #include "curl_memory.h"
@@ -124,19 +123,9 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
 #endif
   TCHAR *host_name;
   CURLcode result;
-  const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
-    conn->host.name;
 
   infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
-        hostname, conn->remote_port);
-
-  if(Curl_verify_windows_version(5, 1, PLATFORM_WINNT,
-                                 VERSION_LESS_THAN_EQUAL)) {
-     /* SChannel in Windows XP (OS version 5.1) uses legacy handshakes and
-        algorithms that may not be supported by all servers. */
-     infof(data, "schannel: WinSSL version is old and may not be able to "
-           "connect to some servers due to lack of SNI, algorithms, etc.\n");
-  }
+        conn->host.name, conn->remote_port);
 
 #ifdef HAS_ALPN
   /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
@@ -153,9 +142,9 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
   connssl->cred = NULL;
 
   /* check for an existing re-usable credential handle */
-  if(data->set.general_ssl.sessionid) {
+  if(conn->ssl_config.sessionid) {
     Curl_ssl_sessionid_lock(conn);
-    if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL, sockindex)) {
+    if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)) {
       connssl->cred = old_cred;
       infof(data, "schannel: re-using existing credential handle\n");
 
@@ -172,7 +161,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
     memset(&schannel_cred, 0, sizeof(schannel_cred));
     schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
 
-    if(conn->ssl_config.verifypeer) {
+    if(data->set.ssl.verifypeer) {
 #ifdef _WIN32_WCE
       /* certificate validation on CE doesn't seem to work right; we'll
          do it following a more manual process. */
@@ -181,14 +170,13 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
         SCH_CRED_IGNORE_REVOCATION_OFFLINE;
 #else
       schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
-      /* TODO s/data->set.ssl.no_revoke/SSL_SET_OPTION(no_revoke)/g */
-      if(data->set.ssl.no_revoke)
+      if(data->set.ssl_no_revoke)
         schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
                                  SCH_CRED_IGNORE_REVOCATION_OFFLINE;
       else
         schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
 #endif
-      if(data->set.ssl.no_revoke)
+      if(data->set.ssl_no_revoke)
         infof(data, "schannel: disabled server certificate revocation "
                     "checks\n");
       else
@@ -201,14 +189,15 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
       infof(data, "schannel: disabled server certificate revocation checks\n");
     }
 
-    if(!conn->ssl_config.verifyhost) {
+    if(!data->set.ssl.verifyhost) {
       schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
       infof(data, "schannel: verifyhost setting prevents Schannel from "
             "comparing the supplied target name with the subject "
-            "names in server certificates.\n");
+            "names in server certificates. Also disables SNI.\n");
     }
 
-    switch(conn->ssl_config.version) {
+    switch(data->set.ssl.version) {
+    default:
     case CURL_SSLVERSION_DEFAULT:
     case CURL_SSLVERSION_TLSv1:
       schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
@@ -224,18 +213,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
     case CURL_SSLVERSION_TLSv1_2:
       schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
       break;
-    case CURL_SSLVERSION_TLSv1_3:
-      failf(data, "Schannel: TLS 1.3 is not yet supported");
-      return CURLE_SSL_CONNECT_ERROR;
     case CURL_SSLVERSION_SSLv3:
       schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
       break;
     case CURL_SSLVERSION_SSLv2:
       schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
       break;
-    default:
-      failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
-      return CURLE_SSL_CONNECT_ERROR;
     }
 
     /* allocate memory for the re-usable credential handle */
@@ -270,9 +253,9 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
   }
 
   /* Warn if SNI is disabled due to use of an IP address */
-  if(Curl_inet_pton(AF_INET, hostname, &addr)
+  if(Curl_inet_pton(AF_INET, conn->host.name, &addr)
 #ifdef ENABLE_IPV6
-     || Curl_inet_pton(AF_INET6, hostname, &addr6)
+     || Curl_inet_pton(AF_INET6, conn->host.name, &addr6)
 #endif
     ) {
     infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
@@ -282,17 +265,17 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
   if(connssl->use_alpn) {
     int cur = 0;
     int list_start_index = 0;
-    unsigned int *extension_len = NULL;
+    unsigned intextension_len = NULL;
     unsigned short* list_len = NULL;
 
     /* The first four bytes will be an unsigned int indicating number
        of bytes of data in the rest of the the buffer. */
-    extension_len = (unsigned int *)(&alpn_buffer[cur]);
+    extension_len = (unsigned int*)(&alpn_buffer[cur]);
     cur += sizeof(unsigned int);
 
     /* The next four bytes are an indicator that this buffer will contain
        ALPN data, as opposed to NPN, for example. */
-    *(unsigned int *)&alpn_buffer[cur] =
+    *(unsigned int*)&alpn_buffer[cur] =
       SecApplicationProtocolNegotiationExt_ALPN;
     cur += sizeof(unsigned int);
 
@@ -350,7 +333,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
   }
   memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt));
 
-  host_name = Curl_convert_UTF8_to_tchar(hostname);
+  host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
   if(!host_name)
     return CURLE_OUT_OF_MEMORY;
 
@@ -423,13 +406,11 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
   TCHAR *host_name;
   CURLcode result;
   bool doread;
-  const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
-    conn->host.name;
 
   doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
 
   infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
-        hostname, conn->remote_port);
+        conn->host.name, conn->remote_port);
 
   if(!connssl->cred || !connssl->ctxt)
     return CURLE_SSL_CONNECT_ERROR;
@@ -525,7 +506,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
     memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer,
            connssl->encdata_offset);
 
-    host_name = Curl_convert_UTF8_to_tchar(hostname);
+    host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
     if(!host_name)
       return CURLE_OUT_OF_MEMORY;
 
@@ -642,7 +623,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
 #ifdef _WIN32_WCE
   /* Windows CE doesn't do any server certificate validation.
      We have to do it manually. */
-  if(conn->ssl_config.verifypeer)
+  if(data->set.ssl.verifypeer)
     return verify_certificate(conn, sockindex);
 #endif
 
@@ -657,8 +638,6 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   SECURITY_STATUS sspi_status = SEC_E_OK;
   CERT_CONTEXT *ccert_context = NULL;
-  const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
-    conn->host.name;
 #ifdef HAS_ALPN
   SecPkgContext_ApplicationProtocol alpn_result;
 #endif
@@ -666,7 +645,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
 
   infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
-        hostname, conn->remote_port);
+        conn->host.name, conn->remote_port);
 
   if(!connssl->cred)
     return CURLE_SSL_CONNECT_ERROR;
@@ -722,13 +701,12 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
 #endif
 
   /* save the current session data for possible re-use */
-  if(data->set.general_ssl.sessionid) {
+  if(conn->ssl_config.sessionid) {
     bool incache;
     struct curl_schannel_cred *old_cred = NULL;
 
     Curl_ssl_sessionid_lock(conn);
-    incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL,
-                                      sockindex));
+    incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL));
     if(incache) {
       if(old_cred != connssl->cred) {
         infof(data, "schannel: old credential handle is stale, removing\n");
@@ -739,8 +717,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
     }
     if(!incache) {
       result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
-                                     sizeof(struct curl_schannel_cred),
-                                     sockindex);
+                                     sizeof(struct curl_schannel_cred));
       if(result) {
         Curl_ssl_sessionid_unlock(conn);
         failf(data, "schannel: failed to store credential handle");
@@ -792,7 +769,7 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
   struct Curl_easy *data = conn->data;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   curl_socket_t sockfd = conn->sock[sockindex];
-  time_t timeout_ms;
+  long timeout_ms;
   int what;
 
   /* check if the connection has already been established */
@@ -838,8 +815,7 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
       curl_socket_t readfd = ssl_connect_2_reading ==
         connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
 
-      what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
-                               nonblocking ? 0 : timeout_ms);
+      what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
       if(what < 0) {
         /* fatal error */
         failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
@@ -980,7 +956,7 @@ schannel_send(struct connectdata *conn, int sockindex,
     /* send entire message or fail */
     while(len > (size_t)written) {
       ssize_t this_write;
-      time_t timeleft;
+      long timeleft;
       int what;
 
       this_write = 0;
@@ -995,7 +971,8 @@ schannel_send(struct connectdata *conn, int sockindex,
         break;
       }
 
-      what = SOCKET_WRITABLE(conn->sock[sockindex], timeleft);
+      what = Curl_socket_ready(CURL_SOCKET_BAD, conn->sock[sockindex],
+                               timeleft);
       if(what < 0) {
         /* fatal error */
         failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
@@ -1399,11 +1376,9 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
    */
   struct Curl_easy *data = conn->data;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
-    conn->host.name;
 
   infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
-        hostname, conn->remote_port);
+        conn->host.name, conn->remote_port);
 
   if(connssl->cred && connssl->ctxt) {
     SecBufferDesc BuffDesc;
@@ -1425,7 +1400,7 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
       failf(data, "schannel: ApplyControlToken failure: %s",
             Curl_sspi_strerror(conn, sspi_status));
 
-    host_name = Curl_convert_UTF8_to_tchar(hostname);
+    host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
     if(!host_name)
       return CURLE_OUT_OF_MEMORY;
 
@@ -1524,21 +1499,21 @@ size_t Curl_schannel_version(char *buffer, size_t size)
   return size;
 }
 
-CURLcode Curl_schannel_random(unsigned char *entropy, size_t length)
+int Curl_schannel_random(unsigned char *entropy, size_t length)
 {
   HCRYPTPROV hCryptProv = 0;
 
   if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
                           CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
-    return CURLE_FAILED_INIT;
+    return 1;
 
   if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
     CryptReleaseContext(hCryptProv, 0UL);
-    return CURLE_FAILED_INIT;
+    return 1;
   }
 
   CryptReleaseContext(hCryptProv, 0UL);
-  return CURLE_OK;
+  return 0;
 }
 
 #ifdef _WIN32_WCE
@@ -1550,9 +1525,6 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
   CURLcode result = CURLE_OK;
   CERT_CONTEXT *pCertContextServer = NULL;
   const CERT_CHAIN_CONTEXT *pChainContext = NULL;
-  const char * const conn_hostname = SSL_IS_PROXY() ?
-    conn->http_proxy.host.name :
-    conn->host.name;
 
   status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
                                             SECPKG_ATTR_REMOTE_CERT_CONTEXT,
@@ -1574,7 +1546,7 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
                                 NULL,
                                 pCertContextServer->hCertStore,
                                 &ChainPara,
-                                (data->set.ssl.no_revoke ? 0 :
+                                (data->set.ssl_no_revoke ? 0 :
                                  CERT_CHAIN_REVOCATION_CHECK_CHAIN),
                                 NULL,
                                 &pChainContext)) {
@@ -1610,10 +1582,15 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
   }
 
   if(result == CURLE_OK) {
-    if(conn->ssl_config.verifyhost) {
-      TCHAR cert_hostname_buff[256];
+    if(data->set.ssl.verifyhost) {
+      TCHAR cert_hostname_buff[128];
+      xcharp_u hostname;
+      xcharp_u cert_hostname;
       DWORD len;
 
+      cert_hostname.const_tchar_ptr = cert_hostname_buff;
+      hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name);
+
       /* TODO: Fix this for certificates with multiple alternative names.
       Right now we're only asking for the first preferred alternative name.
       Instead we'd need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG
@@ -1624,50 +1601,31 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
       */
       len = CertGetNameString(pCertContextServer,
                               CERT_NAME_DNS_TYPE,
-                              CERT_NAME_DISABLE_IE4_UTF8_FLAG,
+                              0,
                               NULL,
-                              cert_hostname_buff,
-                              256);
-      if(len > 0) {
-        const char *cert_hostname;
-
-        /* Comparing the cert name and the connection hostname encoded as UTF-8
-         * is acceptable since both values are assumed to use ASCII
-         * (or some equivalent) encoding
-         */
-        cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname_buff);
-        if(!cert_hostname) {
-          result = CURLE_OUT_OF_MEMORY;
-        }
-        else{
-          int match_result;
-
-          match_result = Curl_cert_hostcheck(cert_hostname, conn->host.name);
-          if(match_result == CURL_HOST_MATCH) {
-            infof(data,
-                  "schannel: connection hostname (%s) validated "
-                  "against certificate name (%s)\n",
-                  conn->host.name,
-                  cert_hostname);
-            result = CURLE_OK;
-          }
-          else{
-            failf(data,
-                  "schannel: connection hostname (%s) "
-                  "does not match certificate name (%s)",
-                  conn->host.name,
-                  cert_hostname);
-            result = CURLE_PEER_FAILED_VERIFICATION;
-          }
-          Curl_unicodefree(cert_hostname);
-        }
+                              cert_hostname.tchar_ptr,
+                              128);
+      if(len > 0 && *cert_hostname.tchar_ptr == '*') {
+        /* this is a wildcard cert.  try matching the last len - 1 chars */
+        int hostname_len = strlen(conn->host.name);
+        cert_hostname.tchar_ptr++;
+        if(_tcsicmp(cert_hostname.const_tchar_ptr,
+                    hostname.const_tchar_ptr + hostname_len - len + 2) != 0)
+          result = CURLE_PEER_FAILED_VERIFICATION;
       }
-      else {
-        failf(data,
-              "schannel: CertGetNameString did not provide any "
-              "certificate name information");
+      else if(len == 0 || _tcsicmp(hostname.const_tchar_ptr,
+                                   cert_hostname.const_tchar_ptr) != 0) {
         result = CURLE_PEER_FAILED_VERIFICATION;
       }
+      if(result == CURLE_PEER_FAILED_VERIFICATION) {
+        char *_cert_hostname;
+        _cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname.tchar_ptr);
+        failf(data, "schannel: CertGetNameString() certificate hostname "
+              "(%s) did not match connection (%s)",
+              _cert_hostname, conn->host.name);
+        Curl_unicodefree(_cert_hostname);
+      }
+      Curl_unicodefree(hostname.tchar_ptr);
     }
   }