From 63230e60e75fbc39981965ddc809eae5b3cb294f Mon Sep 17 00:00:00 2001
From: Mark Salisbury <mark.salisbury@hp.com>
Date: Wed, 13 Jun 2012 13:10:04 -0600
Subject: [PATCH 5/8] schannel SSL: Handle extra data buffer during handshake better

curl_schannel.c - added loop to allow extra data to be processed
immediately; without this handshake hangs when I tested on WinCE.
curl_schannel.c - cleaned up some failf statements.
curl_sasl.c - include curl_memory.h to use correct memory functions.
ftplistparser.c - do not compile if FTP protocol is not enabled.
---
 lib/curl_sasl.c     |    1 +
 lib/curl_schannel.c |  248 +++++++++++++++++++++++++++------------------------
 lib/ftplistparser.c |    4 +
 3 files changed, 137 insertions(+), 116 deletions(-)

diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c
index c5793f9..ccb54a8 100644
--- a/lib/curl_sasl.c
+++ b/lib/curl_sasl.c
@@ -36,6 +36,7 @@
 #include "curl_ntlm_msgs.h"
 #include "curl_sasl.h"
 #include "warnless.h"
+#include "curl_memory.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index 6e2cf5e..ca09325 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -182,10 +182,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
 
     if(sspi_status != SEC_E_OK) {
       if(sspi_status == SEC_E_WRONG_PRINCIPAL)
-        failf(data, "schannel: SNI or certificate check failed: %s\n",
+        failf(data, "schannel: SNI or certificate check failed: %s",
               Curl_sspi_strerror(conn, sspi_status));
       else
-        failf(data, "schannel: AcquireCredentialsHandle failed: %s\n",
+        failf(data, "schannel: AcquireCredentialsHandle failed: %s",
               Curl_sspi_strerror(conn, sspi_status));
       free(connssl->cred);
       connssl->cred = NULL;
@@ -232,10 +232,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
 
   if(sspi_status != SEC_I_CONTINUE_NEEDED) {
     if(sspi_status == SEC_E_WRONG_PRINCIPAL)
-      failf(data, "schannel: SNI or certificate check failed: %s\n",
+      failf(data, "schannel: SNI or certificate check failed: %s",
             Curl_sspi_strerror(conn, sspi_status));
     else
-      failf(data, "schannel: initial InitializeSecurityContext failed: %s\n",
+      failf(data, "schannel: initial InitializeSecurityContext failed: %s",
             Curl_sspi_strerror(conn, sspi_status));
     free(connssl->ctxt);
     connssl->ctxt = NULL;
@@ -250,7 +250,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
       &write);
   s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
   if(write != outbuf.cbBuffer) {
-    failf(data, "schannel: failed to send initial handshake data: %d\n",
+    failf(data, "schannel: failed to send initial handshake data: %d",
           write);
     return CURLE_SSL_CONNECT_ERROR;
   }
@@ -294,132 +294,149 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
     }
   }
 
-  /* read encrypted handshake data from socket */
-  result = Curl_read_plain(conn->sock[sockindex],
-                           connssl->encdata_buffer + connssl->encdata_offset,
-                           connssl->encdata_length - connssl->encdata_offset,
-                           &read);
-
-  if(result == CURLE_OK) {
-    /* increase encrypted data buffer offset */
-    connssl->encdata_offset += read;
-  }
-  else if(connssl->connecting_state != ssl_connect_2_writing) {
-    if(result == CURLE_AGAIN) {
-      connssl->connecting_state = ssl_connect_2_reading;
-      infof(data, "schannel: failed to receive handshake, need more data\n");
-      return CURLE_OK; // or is CURLE_AGAIN more appropriate?
-    }
-    else {
-      failf(data, "schannel: failed to receive handshake, connection "
-            "failed\n");
-      return CURLE_SSL_CONNECT_ERROR;
+  while (true) {
+    if (connssl->encdata_offset == 0 || sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
+      /* read encrypted handshake data from socket */
+      result = Curl_read_plain(conn->sock[sockindex],
+                               connssl->encdata_buffer + connssl->encdata_offset,
+                               connssl->encdata_length - connssl->encdata_offset,
+                               &read);
+
+      if(result == CURLE_OK) {
+        /* increase encrypted data buffer offset */
+        connssl->encdata_offset += read;
+      }
+      else if(connssl->connecting_state != ssl_connect_2_writing) {
+        if(result == CURLE_AGAIN) {
+          connssl->connecting_state = ssl_connect_2_reading;
+          infof(data, "schannel: failed to receive handshake, need more data\n");
+          return CURLE_OK; // or is CURLE_AGAIN more appropriate?
+        }
+        else {
+          failf(data, "schannel: failed to receive handshake, connection "
+                "failed (result: %s)", curl_easy_strerror(result));
+          return CURLE_SSL_CONNECT_ERROR;
+        }
+      }
     }
-  }
 
-  infof(data, "schannel: encrypted data buffer %d/%d\n",
-    connssl->encdata_offset, connssl->encdata_length);
+    infof(data, "schannel: encrypted data buffer %d/%d\n",
+      connssl->encdata_offset, connssl->encdata_length);
 
-  /* setup input buffers */
-  InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset),
-      connssl->encdata_offset);
-  InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
-  InitSecBufferDesc(&inbuf_desc, inbuf, 2);
+    /* setup input buffers */
+    InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset),
+        connssl->encdata_offset);
+    InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
+    InitSecBufferDesc(&inbuf_desc, inbuf, 2);
 
-  /* setup output buffers */
-  InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
-  InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
-  InitSecBufferDesc(&outbuf_desc, outbuf, 2);
+    /* setup output buffers */
+    InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
+    InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
+    InitSecBufferDesc(&outbuf_desc, outbuf, 2);
 
-  if(inbuf[0].pvBuffer == NULL) {
-    failf(data, "schannel: unable to allocate memory");
-    return CURLE_OUT_OF_MEMORY;
-  }
+    if(inbuf[0].pvBuffer == NULL) {
+      failf(data, "schannel: unable to allocate memory");
+      return CURLE_OUT_OF_MEMORY;
+    }
 
-  /* copy received handshake data into input buffer */
-  memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer, connssl->encdata_offset);
+    /* copy received handshake data into input buffer */
+    memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer, connssl->encdata_offset);
 
 #ifdef UNICODE
-  whost = _curl_win32_UTF8_to_wchar(conn->host.name);
-  if (whost == NULL)
-    return CURLE_OUT_OF_MEMORY;
+    whost = _curl_win32_UTF8_to_wchar(conn->host.name);
+    if (whost == NULL)
+      return CURLE_OUT_OF_MEMORY;
 #endif
 
-  /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
-  sspi_status = s_pSecFn->InitializeSecurityContext(
-    &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
+    /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
+    sspi_status = s_pSecFn->InitializeSecurityContext(
+      &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
 #ifdef UNICODE
-    whost,
+      whost,
 #else
-    conn->host.name,
+      conn->host.name,
 #endif
-    connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
-    &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
+      connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
+      &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
 
 #ifdef UNICODE
-  free(whost);
+    free(whost);
 #endif
 
-  /* free buffer for received handshake data */
-  free(inbuf[0].pvBuffer);
+    /* free buffer for received handshake data */
+    free(inbuf[0].pvBuffer);
 
-  /* check if the handshake was incomplete */
-  if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
-    connssl->connecting_state = ssl_connect_2_reading;
-    infof(data, "schannel: received incomplete message, need more data\n");
-    return CURLE_OK;
-  }
+    /* check if the handshake was incomplete */
+    if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
+      connssl->connecting_state = ssl_connect_2_reading;
+      infof(data, "schannel: received incomplete message, need more data\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 < 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: %d ...\n",
+    /* check if the handshake needs to be continued */
+    if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
+      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) {
+          CURLcode write_result;
+          infof(data, "schannel: sending next handshake data: %d ...\n",
               outbuf[i].cbBuffer);
 
-        /* send handshake token to server */
-        Curl_write_plain(conn, conn->sock[sockindex], outbuf[i].pvBuffer,
-            outbuf[i].cbBuffer, &write);
-        if(write != outbuf[i].cbBuffer) {
-          failf(data, "schannel: failed to send next handshake data: %d\n",
-                write);
-          return CURLE_SSL_CONNECT_ERROR;
+          /* send handshake token to server */
+          write_result = Curl_write_plain(conn, conn->sock[sockindex], outbuf[i].pvBuffer,
+              outbuf[i].cbBuffer, &write);
+          if(write != outbuf[i].cbBuffer) {
+            failf(data, "schannel: failed to send next handshake data."
+               " curl result: %s, bytes written: %d",
+               curl_easy_strerror(write_result), write);
+            return CURLE_SSL_CONNECT_ERROR;
+          }
         }
-      }
 
-      /* free obsolete buffer */
-      if(outbuf[i].pvBuffer != NULL) {
-        s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
+        /* free obsolete buffer */
+        if(outbuf[i].pvBuffer != NULL) {
+          s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
+        }
       }
     }
-  }
-  else {
-    if(sspi_status == SEC_E_WRONG_PRINCIPAL)
-      failf(data, "schannel: SNI or certificate check failed: %s\n",
-            Curl_sspi_strerror(conn, sspi_status));
-    else
-      failf(data, "schannel: next InitializeSecurityContext failed: %s\n",
-            Curl_sspi_strerror(conn, sspi_status));
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  /* check if there was additional remaining encrypted data */
-  if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
-    infof(data, "schannel: encrypted data length: %d\n", inbuf[1].cbBuffer);
+    else {
+      if(sspi_status == SEC_E_WRONG_PRINCIPAL)
+        failf(data, "schannel: SNI or certificate check failed: %s",
+              Curl_sspi_strerror(conn, sspi_status));
+      else
+        failf(data, "schannel: next InitializeSecurityContext failed: %s",
+              Curl_sspi_strerror(conn, sspi_status));
+      return CURLE_SSL_CONNECT_ERROR;
+    }
 
-    /* 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);
-      connssl->encdata_offset = inbuf[1].cbBuffer;
+    /* check if there was additional remaining encrypted data */
+    if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
+      /* 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 data that 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);
+        connssl->encdata_offset = inbuf[1].cbBuffer;
+        if (sspi_status == SEC_I_CONTINUE_NEEDED)
+          continue;
+      }
     }
-  }
-  else {
-    connssl->encdata_offset = 0;
+    else {
+      connssl->encdata_offset = 0;
+    }
+
+    break;
   }
 
   /* check if the handshake needs to be continued */
@@ -454,17 +471,17 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
   /* check if the required context attributes are met */
   if(connssl->ret_flags != connssl->req_flags) {
     if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
-      failf(data, "schannel: failed to setup sequence detection\n");
+      failf(data, "schannel: failed to setup sequence detection");
     if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
-      failf(data, "schannel: failed to setup replay detection\n");
+      failf(data, "schannel: failed to setup replay detection");
     if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY))
-      failf(data, "schannel: failed to setup confidentiality\n");
+      failf(data, "schannel: failed to setup confidentiality");
     if(!(connssl->ret_flags & ISC_RET_EXTENDED_ERROR))
-      failf(data, "schannel: failed to setup extended errors\n");
+      failf(data, "schannel: failed to setup extended errors");
     if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY))
-      failf(data, "schannel: failed to setup memory allocation\n");
+      failf(data, "schannel: failed to setup memory allocation");
     if(!(connssl->ret_flags & ISC_RET_STREAM))
-      failf(data, "schannel: failed to setup stream orientation\n");
+      failf(data, "schannel: failed to setup stream orientation");
     return CURLE_SSL_CONNECT_ERROR;
   }
 
@@ -481,7 +498,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
     retcode = Curl_ssl_addsessionid(conn, (void*)connssl->cred,
                                     sizeof(struct curl_schannel_cred));
     if(retcode) {
-      failf(data, "schannel: failed to store credential handle\n");
+      failf(data, "schannel: failed to store credential handle");
       return retcode;
     }
     else {
@@ -517,7 +534,7 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
 
     if(timeout_ms < 0) {
       /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
+      failf(data, "schannel: SSL connection timeout");
       return CURLE_OPERATION_TIMEDOUT;
     }
 
@@ -535,7 +552,7 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
 
     if(timeout_ms < 0) {
       /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
+      failf(data, "schannel: SSL connection timeout");
       return CURLE_OPERATION_TIMEDOUT;
     }
 
@@ -551,7 +568,7 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
       what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
       if(what < 0) {
         /* fatal error */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+        failf(data, "schannel: select/poll on SSL socket, errno: %d", SOCKERRNO);
         return CURLE_SSL_CONNECT_ERROR;
       }
       else if(0 == what) {
@@ -561,7 +578,7 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
         }
         else {
           /* timeout */
-          failf(data, "SSL connection timeout");
+          failf(data, "schannel: SSL connection timeout");
           return CURLE_OPERATION_TIMEDOUT;
         }
       }
@@ -700,7 +717,6 @@ schannel_send(struct connectdata *conn, int sockindex,
           else {
             if (Curl_timeleft(conn->data, NULL, FALSE) < 0) {
               /* no need to continue if time already is up */
-              failf(data, "SSL connection timeout");
               failf(conn->data, "schannel: timed out sending data (bytes sent: %d)",
                     total_written);
               *err = CURLE_OPERATION_TIMEDOUT;
diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c
index bbf6e9e..9709dfa 100644
--- a/lib/ftplistparser.c
+++ b/lib/ftplistparser.c
@@ -37,6 +37,8 @@
 
 #include "setup.h"
 
+#ifndef CURL_DISABLE_FTP
+
 #include "ftplistparser.h"
 #include "curl_fnmatch.h"
 
@@ -1044,3 +1046,5 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
 
   return bufflen;
 }
+
+#endif
-- 
1.7.4.msysgit.0

