Revert "Imported Upstream version 7.44.0"
[platform/upstream/curl.git] / lib / vtls / curl_schannel.c
similarity index 70%
rename from lib/vtls/schannel.c
rename to lib/vtls/curl_schannel.c
index 2174e21..b3fe526 100644 (file)
@@ -5,9 +5,9 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2012 - 2015, Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) 2012 - 2014, Marc Hoersken, <info@marc-hoersken.de>
  * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
- * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2014, 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
  * Thanks for code and inspiration!
  */
 
+/*
+ * TODO list for TLS/SSL implementation:
+ * - implement client certificate authentication
+ * - implement custom server certificate validation
+ * - implement cipher/algorithm option
+ *
+ * Related articles on MSDN:
+ * - Getting a Certificate for Schannel
+ *   http://msdn.microsoft.com/en-us/library/windows/desktop/aa375447.aspx
+ * - Specifying Schannel Ciphers and Cipher Strengths
+ *   http://msdn.microsoft.com/en-us/library/windows/desktop/aa380161.aspx
+ */
+
 #include "curl_setup.h"
 
 #ifdef USE_SCHANNEL
@@ -47,7 +60,7 @@
 #endif
 
 #include "curl_sspi.h"
-#include "schannel.h"
+#include "curl_schannel.h"
 #include "vtls.h"
 #include "sendf.h"
 #include "connect.h" /* for the connect timeout */
 #include "inet_pton.h" /* for IP addr SNI check */
 #include "curl_multibyte.h"
 #include "warnless.h"
-#include "curl_printf.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
 #include "curl_memory.h"
 /* The last #include file should be: */
 #include "memdebug.h"
@@ -105,7 +121,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
   struct in6_addr addr6;
 #endif
   TCHAR *host_name;
-  CURLcode result;
+  CURLcode code;
 
   infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
         conn->host.name, conn->remote_port);
@@ -125,64 +141,56 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
       /* certificate validation on CE doesn't seem to work right; we'll
          do it following a more manual process. */
       schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
-        SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
-        SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+                              SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+                              SCH_CRED_IGNORE_REVOCATION_OFFLINE;
 #else
-      schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
-      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;
+      schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
+                              SCH_CRED_REVOCATION_CHECK_CHAIN;
 #endif
-      if(data->set.ssl_no_revoke)
-        infof(data, "schannel: disabled server certificate revocation "
-                    "checks\n");
-      else
-        infof(data, "schannel: checking server certificate revocation\n");
+      infof(data, "schannel: checking server certificate revocation\n");
     }
     else {
       schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
-        SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
-        SCH_CRED_IGNORE_REVOCATION_OFFLINE;
-      infof(data, "schannel: disabled server certificate revocation checks\n");
+                              SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+                              SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+      infof(data, "schannel: disable server certificate revocation checks\n");
     }
 
     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. Also disables SNI.\n");
+                  "comparing the supplied target name with the subject "
+                  "names in server certificates. Also disables SNI.\n");
     }
 
     switch(data->set.ssl.version) {
-    default:
-    case CURL_SSLVERSION_DEFAULT:
-    case CURL_SSLVERSION_TLSv1:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
-        SP_PROT_TLS1_1_CLIENT |
-        SP_PROT_TLS1_2_CLIENT;
-      break;
-    case CURL_SSLVERSION_TLSv1_0:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
-      break;
-    case CURL_SSLVERSION_TLSv1_1:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
-      break;
-    case CURL_SSLVERSION_TLSv1_2:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
-      break;
-    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:
+      case CURL_SSLVERSION_DEFAULT:
+      case CURL_SSLVERSION_TLSv1:
+        schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
+                                              SP_PROT_TLS1_1_CLIENT |
+                                              SP_PROT_TLS1_2_CLIENT;
+        break;
+      case CURL_SSLVERSION_TLSv1_0:
+        schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
+        break;
+      case CURL_SSLVERSION_TLSv1_1:
+        schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
+        break;
+      case CURL_SSLVERSION_TLSv1_2:
+        schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
+        break;
+      case CURL_SSLVERSION_SSLv3:
+        schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
+        break;
+      case CURL_SSLVERSION_SSLv2:
+        schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
+        break;
     }
 
     /* allocate memory for the re-usable credential handle */
     connssl->cred = (struct curl_schannel_cred *)
-      malloc(sizeof(struct curl_schannel_cred));
+                     malloc(sizeof(struct curl_schannel_cred));
     if(!connssl->cred) {
       failf(data, "schannel: unable to allocate memory");
       return CURLE_OUT_OF_MEMORY;
@@ -190,12 +198,9 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
     memset(connssl->cred, 0, sizeof(struct curl_schannel_cred));
 
     /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
-    sspi_status =
-      s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
-                                         SECPKG_CRED_OUTBOUND, NULL,
-                                         &schannel_cred, NULL, NULL,
-                                         &connssl->cred->cred_handle,
-                                         &connssl->cred->time_stamp);
+    sspi_status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
+      SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL,
+      &connssl->cred->cred_handle, &connssl->cred->time_stamp);
 
     if(sspi_status != SEC_E_OK) {
       if(sspi_status == SEC_E_WRONG_PRINCIPAL)
@@ -224,12 +229,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
 
   /* setup request flags */
   connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
-    ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
-    ISC_REQ_STREAM;
+                       ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
+                       ISC_REQ_STREAM;
 
   /* allocate memory for the security context handle */
   connssl->ctxt = (struct curl_schannel_ctxt *)
-    malloc(sizeof(struct curl_schannel_ctxt));
+                   malloc(sizeof(struct curl_schannel_ctxt));
   if(!connssl->ctxt) {
     failf(data, "schannel: unable to allocate memory");
     return CURLE_OUT_OF_MEMORY;
@@ -264,10 +269,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
         "sending %lu bytes...\n", outbuf.cbBuffer);
 
   /* send initial handshake data which is now stored in output buffer */
-  result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
-                            outbuf.cbBuffer, &written);
+  code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
+                          outbuf.cbBuffer, &written);
   s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
-  if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
+  if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) {
     failf(data, "schannel: failed to send initial handshake data: "
           "sent %zd of %lu bytes", written, outbuf.cbBuffer);
     return CURLE_SSL_CONNECT_ERROR;
@@ -276,10 +281,6 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
   infof(data, "schannel: sent initial handshake data: "
         "sent %zd bytes\n", written);
 
-  connssl->recv_unrecoverable_err = CURLE_OK;
-  connssl->recv_sspi_close_notify = false;
-  connssl->recv_connection_closed = false;
-
   /* continue to second handshake step */
   connssl->connecting_state = ssl_connect_2;
 
@@ -295,13 +296,13 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   unsigned char *reallocated_buffer;
   size_t reallocated_length;
-  SecBuffer outbuf[3];
+  SecBuffer outbuf[2];
   SecBufferDesc outbuf_desc;
   SecBuffer inbuf[2];
   SecBufferDesc inbuf_desc;
   SECURITY_STATUS sspi_status = SEC_E_OK;
   TCHAR *host_name;
-  CURLcode result;
+  CURLcode code;
   bool doread;
 
   doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
@@ -312,17 +313,6 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
   if(!connssl->cred || !connssl->ctxt)
     return CURLE_SSL_CONNECT_ERROR;
 
-  /* buffer to store previously received and decrypted data */
-  if(connssl->decdata_buffer == NULL) {
-    connssl->decdata_offset = 0;
-    connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
-    connssl->decdata_buffer = malloc(connssl->decdata_length);
-    if(connssl->decdata_buffer == NULL) {
-      failf(data, "schannel: unable to allocate memory");
-      return CURLE_OUT_OF_MEMORY;
-    }
-  }
-
   /* buffer to store previously received and encrypted data */
   if(connssl->encdata_buffer == NULL) {
     connssl->encdata_offset = 0;
@@ -339,7 +329,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
      CURL_SCHANNEL_BUFFER_FREE_SIZE) {
     /* increase internal encrypted data buffer */
     reallocated_length = connssl->encdata_offset +
-      CURL_SCHANNEL_BUFFER_FREE_SIZE;
+                         CURL_SCHANNEL_BUFFER_FREE_SIZE;
     reallocated_buffer = realloc(connssl->encdata_buffer,
                                  reallocated_length);
 
@@ -356,20 +346,18 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
   for(;;) {
     if(doread) {
       /* read encrypted handshake data from socket */
-      result = Curl_read_plain(conn->sock[sockindex],
-                               (char *) (connssl->encdata_buffer +
-                                         connssl->encdata_offset),
-                               connssl->encdata_length -
-                               connssl->encdata_offset,
-                               &nread);
-      if(result == CURLE_AGAIN) {
+      code = Curl_read_plain(conn->sock[sockindex],
+                (char *) (connssl->encdata_buffer + connssl->encdata_offset),
+                          connssl->encdata_length - connssl->encdata_offset,
+                          &nread);
+      if(code == CURLE_AGAIN) {
         if(connssl->connecting_state != ssl_connect_2_writing)
           connssl->connecting_state = ssl_connect_2_reading;
         infof(data, "schannel: failed to receive handshake, "
               "need more data\n");
         return CURLE_OK;
       }
-      else if((result != CURLE_OK) || (nread == 0)) {
+      else if((code != CURLE_OK) || (nread == 0)) {
         failf(data, "schannel: failed to receive handshake, "
               "SSL/TLS connection failed");
         return CURLE_SSL_CONNECT_ERROR;
@@ -380,7 +368,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
     }
 
     infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
-          connssl->encdata_offset, connssl->encdata_length);
+        connssl->encdata_offset, connssl->encdata_length);
 
     /* setup input buffers */
     InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset),
@@ -391,8 +379,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
     /* setup output buffers */
     InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
     InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
-    InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
-    InitSecBufferDesc(&outbuf_desc, outbuf, 3);
+    InitSecBufferDesc(&outbuf_desc, outbuf, 2);
 
     if(inbuf[0].pvBuffer == NULL) {
       failf(data, "schannel: unable to allocate memory");
@@ -426,31 +413,19 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
       return CURLE_OK;
     }
 
-    /* If the server has requested a client certificate, attempt to continue
-       the handshake without one. This will allow connections to servers which
-       request a client certificate but do not require it. */
-    if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
-       !(connssl->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
-      connssl->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
-      connssl->connecting_state = ssl_connect_2_writing;
-      infof(data, "schannel: a client certificate has been requested\n");
-      return CURLE_OK;
-    }
-
     /* check if the handshake needs to be continued */
     if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
-      for(i = 0; i < 3; i++) {
+      for(i = 0; i < 2; i++) {
         /* search for handshake tokens that need to be send */
         if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
           infof(data, "schannel: sending next handshake data: "
                 "sending %lu bytes...\n", outbuf[i].cbBuffer);
 
           /* send handshake token to server */
-          result = Curl_write_plain(conn, conn->sock[sockindex],
-                                    outbuf[i].pvBuffer, outbuf[i].cbBuffer,
-                                    &written);
-          if((result != CURLE_OK) ||
-             (outbuf[i].cbBuffer != (size_t) written)) {
+          code = Curl_write_plain(conn, conn->sock[sockindex],
+                                  outbuf[i].pvBuffer, outbuf[i].cbBuffer,
+                                  &written);
+          if((code != CURLE_OK) || (outbuf[i].cbBuffer != (size_t)written)) {
             failf(data, "schannel: failed to send next handshake data: "
                   "sent %zd of %lu bytes", written, outbuf[i].cbBuffer);
             return CURLE_SSL_CONNECT_ERROR;
@@ -477,21 +452,21 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
     if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
       infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer);
       /*
-        There are two cases where we could be getting extra data here:
-        1) If we're renegotiating a connection and the handshake is already
-        complete (from the server perspective), it can encrypted app data
-        (not handshake data) in an extra buffer at this point.
-        2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
-        connection and this extra data is part of the handshake.
-        We should process the data immediately; waiting for the socket to
-        be ready may fail since the server is done sending handshake data.
-      */
+         There are two cases where we could be getting extra data here:
+         1) If we're renegotiating a connection and the handshake is already
+            complete (from the server perspective), it can encrypted app data
+            (not handshake data) in an extra buffer at this point.
+         2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
+            connection and this extra data is part of the handshake.
+            We should process the data immediately; waiting for the socket to
+            be ready may fail since the server is done sending handshake data.
+       */
       /* check if the remaining data is less than the total amount
          and therefore begins after the already processed data */
       if(connssl->encdata_offset > inbuf[1].cbBuffer) {
         memmove(connssl->encdata_buffer,
                 (connssl->encdata_buffer + connssl->encdata_offset) -
-                inbuf[1].cbBuffer, inbuf[1].cbBuffer);
+                  inbuf[1].cbBuffer, inbuf[1].cbBuffer);
         connssl->encdata_offset = inbuf[1].cbBuffer;
         if(sspi_status == SEC_I_CONTINUE_NEEDED) {
           doread = FALSE;
@@ -716,14 +691,14 @@ schannel_send(struct connectdata *conn, int sockindex,
   SecBuffer outbuf[4];
   SecBufferDesc outbuf_desc;
   SECURITY_STATUS sspi_status = SEC_E_OK;
-  CURLcode result;
+  CURLcode code;
 
   /* check if the maximum stream sizes were queried */
   if(connssl->stream_sizes.cbMaximumMessage == 0) {
     sspi_status = s_pSecFn->QueryContextAttributes(
-      &connssl->ctxt->ctxt_handle,
-      SECPKG_ATTR_STREAM_SIZES,
-      &connssl->stream_sizes);
+                              &connssl->ctxt->ctxt_handle,
+                              SECPKG_ATTR_STREAM_SIZES,
+                              &connssl->stream_sizes);
     if(sspi_status != SEC_E_OK) {
       *err = CURLE_SEND_ERROR;
       return -1;
@@ -738,7 +713,7 @@ schannel_send(struct connectdata *conn, int sockindex,
 
   /* calculate the complete message length and allocate a buffer for it */
   data_len = connssl->stream_sizes.cbHeader + len +
-    connssl->stream_sizes.cbTrailer;
+              connssl->stream_sizes.cbTrailer;
   data = (unsigned char *) malloc(data_len);
   if(data == NULL) {
     *err = CURLE_OUT_OF_MEMORY;
@@ -771,19 +746,19 @@ schannel_send(struct connectdata *conn, int sockindex,
     len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
 
     /*
-      It's important to send the full message which includes the header,
-      encrypted payload, and trailer.  Until the client receives all the
-      data a coherent message has not been delivered and the client
-      can't read any of it.
-
-      If we wanted to buffer the unwritten encrypted bytes, we would
-      tell the client that all data it has requested to be sent has been
-      sent. The unwritten encrypted bytes would be the first bytes to
-      send on the next invocation.
-      Here's the catch with this - if we tell the client that all the
-      bytes have been sent, will the client call this method again to
-      send the buffered data?  Looking at who calls this function, it
-      seems the answer is NO.
+       It's important to send the full message which includes the header,
+       encrypted payload, and trailer.  Until the client receives all the
+       data a coherent message has not been delivered and the client
+       can't read any of it.
+
+       If we wanted to buffer the unwritten encrypted bytes, we would
+       tell the client that all data it has requested to be sent has been
+       sent. The unwritten encrypted bytes would be the first bytes to
+       send on the next invocation.
+       Here's the catch with this - if we tell the client that all the
+       bytes have been sent, will the client call this method again to
+       send the buffered data?  Looking at who calls this function, it
+       seems the answer is NO.
     */
 
     /* send entire message or fail */
@@ -822,12 +797,12 @@ schannel_send(struct connectdata *conn, int sockindex,
       }
       /* socket is writable */
 
-      result = Curl_write_plain(conn, conn->sock[sockindex], data + written,
-                                len - written, &this_write);
-      if(result == CURLE_AGAIN)
+      code = Curl_write_plain(conn, conn->sock[sockindex], data + written,
+                              len - written, &this_write);
+      if(code == CURLE_AGAIN)
         continue;
-      else if(result != CURLE_OK) {
-        *err = result;
+      else if(code != CURLE_OK) {
+        *err = code;
         written = -1;
         break;
       }
@@ -857,7 +832,8 @@ schannel_recv(struct connectdata *conn, int sockindex,
               char *buf, size_t len, CURLcode *err)
 {
   size_t size = 0;
-  ssize_t nread = -1;
+  ssize_t nread = 0, ret = -1;
+  CURLcode result;
   struct SessionHandle *data = conn->data;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   unsigned char *reallocated_buffer;
@@ -866,103 +842,72 @@ schannel_recv(struct connectdata *conn, int sockindex,
   SecBuffer inbuf[4];
   SecBufferDesc inbuf_desc;
   SECURITY_STATUS sspi_status = SEC_E_OK;
-  /* we want the length of the encrypted buffer to be at least large enough
-     that it can hold all the bytes requested and some TLS record overhead. */
-  size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
-
-  /****************************************************************************
-   * Don't return or set connssl->recv_unrecoverable_err unless in the cleanup.
-   * The pattern for return error is set *err, optional infof, goto cleanup.
-   *
-   * Our priority is to always return as much decrypted data to the caller as
-   * possible, even if an error occurs. The state of the decrypted buffer must
-   * always be valid. Transfer of decrypted data to the caller's buffer is
-   * handled in the cleanup.
-   */
 
   infof(data, "schannel: client wants to read %zu bytes\n", len);
   *err = CURLE_OK;
 
-  if(len && len <= connssl->decdata_offset) {
-    infof(data, "schannel: enough decrypted data is already available\n");
-    goto cleanup;
-  }
-  else if(connssl->recv_unrecoverable_err) {
-    *err = connssl->recv_unrecoverable_err;
-    infof(data, "schannel: an unrecoverable error occurred in a prior call\n");
-    goto cleanup;
-  }
-  else if(connssl->recv_sspi_close_notify) {
-    /* once a server has indicated shutdown there is no more encrypted data */
-    infof(data, "schannel: server indicated shutdown in a prior call\n");
-    goto cleanup;
-  }
-  else if(!len) {
-    /* It's debatable what to return when !len. Regardless we can't return
-    immediately because there may be data to decrypt (in the case we want to
-    decrypt all encrypted cached data) so handle !len later in cleanup.
-    */
-    ; /* do nothing */
+  /* buffer to store previously received and decrypted data */
+  if(connssl->decdata_buffer == NULL) {
+    connssl->decdata_offset = 0;
+    connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
+    connssl->decdata_buffer = malloc(connssl->decdata_length);
+    if(connssl->decdata_buffer == NULL) {
+      failf(data, "schannel: unable to allocate memory");
+      *err = CURLE_OUT_OF_MEMORY;
+      return -1;
+    }
   }
-  else if(!connssl->recv_connection_closed) {
-    /* increase enc buffer in order to fit the requested amount of data */
-    size = connssl->encdata_length - connssl->encdata_offset;
-    if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE ||
-       connssl->encdata_length < min_encdata_length) {
-      reallocated_length = connssl->encdata_offset +
-                           CURL_SCHANNEL_BUFFER_FREE_SIZE;
-      if(reallocated_length < min_encdata_length) {
-        reallocated_length = min_encdata_length;
-      }
-      reallocated_buffer = realloc(connssl->encdata_buffer,
-                                   reallocated_length);
-      if(reallocated_buffer == NULL) {
-        *err = CURLE_OUT_OF_MEMORY;
-        failf(data, "schannel: unable to re-allocate memory");
-        goto cleanup;
-      }
 
+  /* increase buffer in order to fit the requested amount of data */
+  if(connssl->encdata_length - connssl->encdata_offset <
+     CURL_SCHANNEL_BUFFER_FREE_SIZE || connssl->encdata_length < len) {
+    /* increase internal encrypted data buffer */
+    reallocated_length = connssl->encdata_offset +
+                         CURL_SCHANNEL_BUFFER_FREE_SIZE;
+    /* make sure that the requested amount of data fits */
+    if(reallocated_length < len) {
+      reallocated_length = len;
+    }
+    reallocated_buffer = realloc(connssl->encdata_buffer,
+                                 reallocated_length);
+
+    if(reallocated_buffer == NULL) {
+      failf(data, "schannel: unable to re-allocate memory");
+      *err = CURLE_OUT_OF_MEMORY;
+      return -1;
+    }
+    else {
       connssl->encdata_buffer = reallocated_buffer;
       connssl->encdata_length = reallocated_length;
-      size = connssl->encdata_length - connssl->encdata_offset;
-      infof(data, "schannel: encdata_buffer resized %zu\n",
-            connssl->encdata_length);
     }
+  }
 
-    infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
-          connssl->encdata_offset, connssl->encdata_length);
-
-    /* read encrypted data from socket */
+  /* read encrypted data from socket */
+  infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
+        connssl->encdata_offset, connssl->encdata_length);
+  size = connssl->encdata_length - connssl->encdata_offset;
+  if(size > 0) {
     *err = Curl_read_plain(conn->sock[sockindex],
-                           (char *)(connssl->encdata_buffer +
-                                    connssl->encdata_offset),
+                  (char *) (connssl->encdata_buffer + connssl->encdata_offset),
                            size, &nread);
-    if(*err) {
-      nread = -1;
-      if(*err == CURLE_AGAIN)
-        infof(data, "schannel: Curl_read_plain returned CURLE_AGAIN\n");
-      else if(*err == CURLE_RECV_ERROR)
-        infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n");
-      else
-        infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
-    }
-    else if(nread == 0) {
-      connssl->recv_connection_closed = true;
-      infof(data, "schannel: server closed the connection\n");
-    }
-    else if(nread > 0) {
-      connssl->encdata_offset += (size_t)nread;
-      infof(data, "schannel: encrypted data got %zd\n", nread);
+    /* check for received data */
+    if(*err != CURLE_OK)
+      ret = -1;
+    else {
+      if(nread > 0)
+        /* increase encrypted data buffer offset */
+        connssl->encdata_offset += nread;
+      ret = nread;
     }
+    infof(data, "schannel: encrypted data got %zd\n", ret);
   }
 
   infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
         connssl->encdata_offset, connssl->encdata_length);
 
-  /* decrypt loop */
+  /* check if we still have some data in our buffers */
   while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK &&
-        (!len || connssl->decdata_offset < len ||
-         connssl->recv_connection_closed)) {
+        connssl->decdata_offset < len) {
     /* prepare data buffer for DecryptMessage call */
     InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer,
                   curlx_uztoul(connssl->encdata_offset));
@@ -971,16 +916,24 @@ schannel_recv(struct connectdata *conn, int sockindex,
     InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
     InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
     InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
+
     InitSecBufferDesc(&inbuf_desc, inbuf, 4);
 
     /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
     sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
                                            &inbuf_desc, 0, NULL);
 
+    /* check if we need more data */
+    if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
+      infof(data, "schannel: failed to decrypt data, need more data\n");
+      *err = CURLE_AGAIN;
+      return -1;
+    }
+
     /* check if everything went fine (server may want to renegotiate
        or shutdown the connection context) */
     if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
-       sspi_status == SEC_I_CONTEXT_EXPIRED) {
+                                  sspi_status == SEC_I_CONTEXT_EXPIRED) {
       /* check for successfully decrypted data, even before actual
          renegotiation or shutdown of the connection context */
       if(inbuf[1].BufferType == SECBUFFER_DATA) {
@@ -1000,18 +953,21 @@ schannel_recv(struct connectdata *conn, int sockindex,
           }
           reallocated_buffer = realloc(connssl->decdata_buffer,
                                        reallocated_length);
+
           if(reallocated_buffer == NULL) {
-            *err = CURLE_OUT_OF_MEMORY;
             failf(data, "schannel: unable to re-allocate memory");
-            goto cleanup;
+            *err = CURLE_OUT_OF_MEMORY;
+            return -1;
+          }
+          else {
+            connssl->decdata_buffer = reallocated_buffer;
+            connssl->decdata_length = reallocated_length;
           }
-          connssl->decdata_buffer = reallocated_buffer;
-          connssl->decdata_length = reallocated_length;
         }
 
         /* copy decrypted data to internal buffer */
         size = inbuf[1].cbBuffer;
-        if(size) {
+        if(size > 0) {
           memcpy(connssl->decdata_buffer + connssl->decdata_offset,
                  inbuf[1].pvBuffer, size);
           connssl->decdata_offset += size;
@@ -1029,151 +985,83 @@ schannel_recv(struct connectdata *conn, int sockindex,
 
         /* check if the remaining data is less than the total amount
          * and therefore begins after the already processed data
-         */
+        */
         if(connssl->encdata_offset > inbuf[3].cbBuffer) {
           /* move remaining encrypted data forward to the beginning of
              buffer */
           memmove(connssl->encdata_buffer,
                   (connssl->encdata_buffer + connssl->encdata_offset) -
-                  inbuf[3].cbBuffer, inbuf[3].cbBuffer);
+                    inbuf[3].cbBuffer, inbuf[3].cbBuffer);
           connssl->encdata_offset = inbuf[3].cbBuffer;
         }
 
         infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
               connssl->encdata_offset, connssl->encdata_length);
       }
-      else {
+      else{
         /* reset encrypted buffer offset, because there is no data remaining */
         connssl->encdata_offset = 0;
       }
+    }
 
-      /* check if server wants to renegotiate the connection context */
-      if(sspi_status == SEC_I_RENEGOTIATE) {
-        infof(data, "schannel: remote party requests renegotiation\n");
-        if(*err && *err != CURLE_AGAIN) {
-          infof(data, "schannel: can't renogotiate, an error is pending\n");
-          goto cleanup;
-        }
-        if(connssl->encdata_offset) {
-          *err = CURLE_RECV_ERROR;
-          infof(data, "schannel: can't renogotiate, "
-                      "encrypted data available\n");
-          goto cleanup;
-        }
-        /* begin renegotiation */
-        infof(data, "schannel: renegotiating SSL/TLS connection\n");
-        connssl->state = ssl_connection_negotiating;
-        connssl->connecting_state = ssl_connect_2_writing;
-        *err = schannel_connect_common(conn, sockindex, FALSE, &done);
-        if(*err) {
-          infof(data, "schannel: renegotiation failed\n");
-          goto cleanup;
-        }
-        /* now retry receiving data */
-        sspi_status = SEC_E_OK;
+    /* check if server wants to renegotiate the connection context */
+    if(sspi_status == SEC_I_RENEGOTIATE) {
+      infof(data, "schannel: remote party requests SSL/TLS renegotiation\n");
+
+      /* begin renegotiation */
+      infof(data, "schannel: renegotiating SSL/TLS connection\n");
+      connssl->state = ssl_connection_negotiating;
+      connssl->connecting_state = ssl_connect_2_writing;
+      result = schannel_connect_common(conn, sockindex, FALSE, &done);
+      if(result)
+        *err = result;
+      else {
         infof(data, "schannel: SSL/TLS connection renegotiated\n");
-        continue;
-      }
-      /* check if the server closed the connection */
-      else if(sspi_status == SEC_I_CONTEXT_EXPIRED) {
-        /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not
-           returned so we have to work around that in cleanup. */
-        connssl->recv_sspi_close_notify = true;
-        if(!connssl->recv_connection_closed) {
-          connssl->recv_connection_closed = true;
-          infof(data, "schannel: server closed the connection\n");
-        }
-        goto cleanup;
+        /* now retry receiving data */
+        return schannel_recv(conn, sockindex, buf, len, err);
       }
     }
-    else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
-      if(!*err)
-        *err = CURLE_AGAIN;
-      infof(data, "schannel: failed to decrypt data, need more data\n");
-      goto cleanup;
-    }
-    else {
-      *err = CURLE_RECV_ERROR;
-      infof(data, "schannel: failed to read data from server: %s\n",
-            Curl_sspi_strerror(conn, sspi_status));
-      goto cleanup;
-    }
   }
 
-  infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
-        connssl->encdata_offset, connssl->encdata_length);
-
   infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
         connssl->decdata_offset, connssl->decdata_length);
 
-cleanup:
-  /* Warning- there is no guarantee the encdata state is valid at this point */
-  infof(data, "schannel: schannel_recv cleanup\n");
-
-  /* Error if the connection has closed without a close_notify.
-  Behavior here is a matter of debate. We don't want to be vulnerable to a
-  truncation attack however there's some browser precedent for ignoring the
-  close_notify for compatibility reasons.
-  Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't
-  return close_notify. In that case if the connection was closed we assume it
-  was graceful (close_notify) since there doesn't seem to be a way to tell.
-  */
-  if(len && !connssl->decdata_offset && connssl->recv_connection_closed &&
-     !connssl->recv_sspi_close_notify) {
-    BOOL isWin2k;
-    ULONGLONG cm;
-    OSVERSIONINFOEX osver;
-
-    memset(&osver, 0, sizeof(osver));
-    osver.dwOSVersionInfoSize = sizeof(osver);
-    osver.dwMajorVersion = 5;
-
-    cm = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
-    cm = VerSetConditionMask(cm, VER_MINORVERSION, VER_EQUAL);
-    cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
-    cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
-
-    isWin2k = VerifyVersionInfo(&osver,
-                                (VER_MAJORVERSION | VER_MINORVERSION |
-                                 VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR),
-                                cm);
-
-    if(isWin2k && sspi_status == SEC_E_OK)
-      connssl->recv_sspi_close_notify = true;
-    else {
-      *err = CURLE_RECV_ERROR;
-      infof(data, "schannel: server closed abruptly (missing close_notify)\n");
-    }
-  }
-
-  /* Any error other than CURLE_AGAIN is an unrecoverable error. */
-  if(*err && *err != CURLE_AGAIN)
-      connssl->recv_unrecoverable_err = *err;
-
+  /* copy requested decrypted data to supplied buffer */
   size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
-  if(size) {
+  if(size > 0) {
     memcpy(buf, connssl->decdata_buffer, size);
+    ret = size;
+
+    /* move remaining decrypted data forward to the beginning of buffer */
     memmove(connssl->decdata_buffer, connssl->decdata_buffer + size,
             connssl->decdata_offset - size);
     connssl->decdata_offset -= size;
 
-    infof(data, "schannel: decrypted data returned %zu\n", size);
+    infof(data, "schannel: decrypted data returned %zd\n", size);
     infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
           connssl->decdata_offset, connssl->decdata_length);
-    *err = CURLE_OK;
-    return (ssize_t)size;
   }
+  else
+    ret = 0;
 
-  if(!*err && !connssl->recv_connection_closed)
-      *err = CURLE_AGAIN;
-
-  /* It's debatable what to return when !len. We could return whatever error we
-  got from decryption but instead we override here so the return is consistent.
-  */
-  if(!len)
+  /* check if the server closed the connection */
+  if(ret <= 0 && ( /* special check for Windows 2000 Professional */
+      sspi_status == SEC_I_CONTEXT_EXPIRED || (sspi_status == SEC_E_OK &&
+        connssl->encdata_offset > 0 && connssl->encdata_buffer[0] == 0x15))) {
+    infof(data, "schannel: server closed the connection\n");
     *err = CURLE_OK;
+    return 0;
+  }
 
-  return *err ? -1 : 0;
+  /* check if something went wrong and we need to return an error */
+  if(ret < 0 && sspi_status != SEC_E_OK) {
+    infof(data, "schannel: failed to read data from server: %s\n",
+          Curl_sspi_strerror(conn, sspi_status));
+    *err = CURLE_RECV_ERROR;
+    return -1;
+  }
+
+  return ret;
 }
 
 CURLcode
@@ -1233,7 +1121,7 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
     SECURITY_STATUS sspi_status;
     SecBuffer outbuf;
     SecBufferDesc outbuf_desc;
-    CURLcode result;
+    CURLcode code;
     TCHAR *host_name;
     DWORD dwshut = SCHANNEL_SHUTDOWN;
 
@@ -1256,31 +1144,31 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
     InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
 
     sspi_status = s_pSecFn->InitializeSecurityContext(
-      &connssl->cred->cred_handle,
-      &connssl->ctxt->ctxt_handle,
-      host_name,
-      connssl->req_flags,
-      0,
-      0,
-      NULL,
-      0,
-      &connssl->ctxt->ctxt_handle,
-      &outbuf_desc,
-      &connssl->ret_flags,
-      &connssl->ctxt->time_stamp);
+         &connssl->cred->cred_handle,
+         &connssl->ctxt->ctxt_handle,
+         host_name,
+         connssl->req_flags,
+         0,
+         0,
+         NULL,
+         0,
+         &connssl->ctxt->ctxt_handle,
+         &outbuf_desc,
+         &connssl->ret_flags,
+         &connssl->ctxt->time_stamp);
 
     Curl_unicodefree(host_name);
 
     if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
       /* send close message which is in output buffer */
       ssize_t written;
-      result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
-                                outbuf.cbBuffer, &written);
+      code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
+                              outbuf.cbBuffer, &written);
 
       s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
-      if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
+      if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) {
         infof(data, "schannel: failed to send close msg: %s"
-              " (bytes written: %zd)\n", curl_easy_strerror(result), written);
+              " (bytes written: %zd)\n", curl_easy_strerror(code), written);
       }
     }
   }
@@ -1330,14 +1218,9 @@ void Curl_schannel_session_free(void *ptr)
 {
   struct curl_schannel_cred *cred = ptr;
 
-  if(cred && cred->cached) {
-    if(cred->refcount == 0) {
-      s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
-      Curl_safefree(cred);
-    }
-    else {
-      cred->cached = FALSE;
-    }
+  if(cred && cred->cached && cred->refcount == 0) {
+    s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
+    Curl_safefree(cred);
   }
 }
 
@@ -1405,8 +1288,7 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
                                 NULL,
                                 pCertContextServer->hCertStore,
                                 &ChainPara,
-                                (data->set.ssl_no_revoke ? 0 :
-                                 CERT_CHAIN_REVOCATION_CHECK_CHAIN),
+                                0,
                                 NULL,
                                 &pChainContext)) {
       failf(data, "schannel: CertGetCertificateChain failed: %s",
@@ -1417,24 +1299,21 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
 
     if(result == CURLE_OK) {
       CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
-      DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED);
+      DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED|
+                                 CERT_TRUST_REVOCATION_STATUS_UNKNOWN);
       dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
       if(dwTrustErrorMask) {
-        if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED)
-          failf(data, "schannel: CertGetCertificateChain trust error"
-                " CERT_TRUST_IS_REVOKED");
-        else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
+        if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
           failf(data, "schannel: CertGetCertificateChain trust error"
-                " CERT_TRUST_IS_PARTIAL_CHAIN");
-        else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
+                      " CERT_TRUST_IS_PARTIAL_CHAIN");
+        if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
           failf(data, "schannel: CertGetCertificateChain trust error"
-                " CERT_TRUST_IS_UNTRUSTED_ROOT");
-        else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
+                      " CERT_TRUST_IS_UNTRUSTED_ROOT");
+        if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
           failf(data, "schannel: CertGetCertificateChain trust error"
-                " CERT_TRUST_IS_NOT_TIME_VALID");
-        else
-          failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
-                dwTrustErrorMask);
+                      " CERT_TRUST_IS_NOT_TIME_VALID");
+        failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
+              dwTrustErrorMask);
         result = CURLE_PEER_FAILED_VERIFICATION;
       }
     }
@@ -1450,14 +1329,6 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
       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
-      (if WinCE supports that?) and run this section in a loop for each.
-      https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
-      curl: (51) schannel: CertGetNameString() certificate hostname
-      (.google.com) did not match connection (google.com)
-      */
       len = CertGetNameString(pCertContextServer,
                               CERT_NAME_DNS_TYPE,
                               0,