From 21d7aca1c09c45a9d961027d05ba1daa000a0e5d Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Mon, 9 Apr 2012 15:19:54 +0200
Subject: [PATCH 01/18] Replaced specific SSL libraries list in https_getsock
 fallback

---
 lib/http.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/lib/http.c b/lib/http.c
index a139894..2726165 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -1398,8 +1398,7 @@ static int https_getsock(struct connectdata *conn,
   return CURLE_OK;
 }
 #else
-#if defined(USE_NSS) || defined(USE_QSOSSL) || \
-  defined(USE_POLARSSL) || defined(USE_AXTLS) || defined(USE_CYASSL)
+#ifdef USE_SSL
 static int https_getsock(struct connectdata *conn,
                          curl_socket_t *socks,
                          int numsocks)
@@ -1409,7 +1408,7 @@ static int https_getsock(struct connectdata *conn,
   (void)numsocks;
   return GETSOCK_BLANK;
 }
-#endif /* USE_AXTLS || USE_POLARSSL || USE_QSOSSL || USE_NSS */
+#endif /* USE_SSL */
 #endif /* USE_SSLEAY || USE_GNUTLS */
 
 /*
-- 
1.7.10.msysgit.1


From cf9c22d522a7371122c1ca14b1b1f1ab0ff911b1 Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Mon, 9 Apr 2012 15:40:06 +0200
Subject: [PATCH 02/18] Added SSL/TLS support using Microsoft Windows Schannel
 SSPI

---
 lib/curl_schannel.c |  848 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/curl_schannel.h |   65 ++++
 lib/sslgen.c        |    2 +
 lib/urldata.h       |   17 ++
 4 files changed, 932 insertions(+)
 create mode 100644 lib/curl_schannel.c
 create mode 100644 lib/curl_schannel.h

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
new file mode 100644
index 0000000..cfac6ef
--- /dev/null
+++ b/lib/curl_schannel.c
@@ -0,0 +1,848 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all SChannel-specific code for the TLS/SSL layer. No code
+ * but sslgen.c should ever call or use these functions.
+ *
+ */
+
+/*
+ * Based upon the PolarSSL implementation in polarssl.c and polarssl.h:
+ *   Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ *
+ * Based upon the CyaSSL implementation in cyassl.c and cyassl.h:
+ *   Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * Thanks for code and inspiration!
+ */
+
+/*
+ * TODO list for TLS/SSL implementation:
+ * - implement session handling and re-use
+ * - implement write buffering
+ * - implement verification options
+ * - implement verification results
+ * - implement SSL/TLS shutdown
+ * - special cases: negotiation, certificates, algorithms
+ */
+
+#include "setup.h"
+
+#ifdef USE_WINDOWS_SSPI
+#ifdef USE_SCHANNEL
+
+#include <schnlsp.h>
+
+#include "urldata.h"
+#include "curl_sspi.h"
+#include "curl_schannel.h"
+#include "sslgen.h"
+#include "sendf.h"
+#include "connect.h" /* for the connect timeout */
+#include "select.h" /* for the socket readyness */
+#include "inet_pton.h" /* for IP addr SNI check */
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* Uncomment to force verbose output
+ * #define infof(x, y, ...) printf(y, __VA_ARGS__)
+ * #define failf(x, y, ...) printf(y, __VA_ARGS__)
+ */
+
+static Curl_recv schannel_recv;
+static Curl_send schannel_send;
+
+static CURLcode
+schannel_connect_step1(struct connectdata *conn, int sockindex) {
+  ssize_t write = -1;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data* connssl = &conn->ssl[sockindex];
+  SecBuffer outbuf;
+  SecBufferDesc outbuf_desc;
+  SCHANNEL_CRED schannel_cred;
+  SECURITY_STATUS sspi_status = SEC_E_OK;
+  struct in_addr addr;
+#ifdef ENABLE_IPV6
+  struct in6_addr addr6;
+#endif
+
+  infof(data, "schannel: Connecting to %s:%d (step 1/3)\n",
+        conn->host.name, conn->remote_port);
+
+  /* setup Schannel API options */
+  memset(&schannel_cred, 0, sizeof(schannel_cred));
+  schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
+  schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
+                          SCH_CRED_REVOCATION_CHECK_CHAIN;
+
+  if(Curl_inet_pton(AF_INET, conn->host.name, &addr) ||
+#ifdef ENABLE_IPV6
+     Curl_inet_pton(AF_INET6, conn->host.name, &addr6) ||
+#endif
+     data->set.ssl.verifyhost < 2) {
+    schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
+    infof(data, "schannel: using IP address, disable SNI servername check\n");
+  }
+
+  switch(data->set.ssl.version) {
+    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_SSLv3:
+      schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
+      break;
+    case CURL_SSLVERSION_SSLv2:
+      schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
+      break;
+  }
+
+  /* TODO: implement verification options */
+
+  /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
+  sspi_status = s_pSecFn->AcquireCredentialsHandleA(NULL,
+    UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &schannel_cred,
+    NULL, NULL, &connssl->cred_handle, &connssl->time_stamp);
+
+  if(sspi_status != SEC_E_OK) {
+    if(sspi_status == SEC_E_WRONG_PRINCIPAL)
+      failf(data, "schannel: SNI or certificate check failed\n");
+    else
+      failf(data, "schannel: AcquireCredentialsHandleA failed: %d\n",
+            sspi_status);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  connssl->schannel = TRUE;
+
+  /* setup output buffer */
+  outbuf.pvBuffer = NULL;
+  outbuf.cbBuffer = 0;
+  outbuf.BufferType = SECBUFFER_EMPTY;
+
+  outbuf_desc.pBuffers = &outbuf;
+  outbuf_desc.cBuffers = 1;
+  outbuf_desc.ulVersion = SECBUFFER_VERSION;
+
+  /* setup request flags */
+  connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
+                       ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY |
+                       ISC_REQ_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY |
+                       ISC_REQ_STREAM;
+
+  /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
+  sspi_status = s_pSecFn->InitializeSecurityContextA(&connssl->cred_handle,
+    NULL, conn->host.name, connssl->req_flags, 0, 0, NULL, 0,
+    &connssl->ctxt_handle, &outbuf_desc,
+    &connssl->ret_flags, &connssl->time_stamp);
+
+  if(sspi_status != SEC_I_CONTINUE_NEEDED) {
+    if(sspi_status == SEC_E_WRONG_PRINCIPAL)
+      failf(data, "schannel: SNI or certificate check failed\n");
+    else
+      failf(data, "schannel: initial InitializeSecurityContextA failed: %d\n",
+            sspi_status);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  infof(data, "schannel: sending initial handshake data: %d ...\n",
+        outbuf.cbBuffer);
+
+  /* send initial handshake data which is now stored in output buffer */
+  write = swrite(conn->sock[sockindex], outbuf.pvBuffer, outbuf.cbBuffer);
+  s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
+  if(write != outbuf.cbBuffer) {
+    failf(data, "schannel: failed to send initial handshake data: %d\n", write);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  infof(data, "schannel: sent initial handshake data: %d\n", write);
+
+  /* continue to second handshake step */
+  connssl->connecting_state = ssl_connect_2;
+
+  return CURLE_OK;
+}
+
+static CURLcode
+schannel_connect_step2(struct connectdata *conn, int sockindex) {
+  int i;
+  ssize_t read = -1, write = -1;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data* connssl = &conn->ssl[sockindex];
+  SecBuffer outbuf[2];
+  SecBufferDesc outbuf_desc;
+  SecBuffer inbuf[2];
+  SecBufferDesc inbuf_desc;
+  SECURITY_STATUS sspi_status = SEC_E_OK;
+
+  infof(data, "schannel: Connecting to %s:%d (step 2/3)\n",
+        conn->host.name, conn->remote_port);
+
+  connssl->connecting_state = ssl_connect_2;
+
+  /* buffer to store previously received and encrypted data */
+  if(connssl->encdata_buffer == NULL) {
+    connssl->encdata_offset = 0;
+    connssl->encdata_length = 4096;
+    connssl->encdata_buffer = malloc(connssl->encdata_length);
+    if(connssl->encdata_buffer == NULL) {
+      failf(data, "schannel: unable to allocate memory");
+      return CURLE_OUT_OF_MEMORY;
+    }
+  }
+
+  /* read encrypted handshake data from socket */
+  read = sread(conn->sock[sockindex],
+               connssl->encdata_buffer + connssl->encdata_offset,
+               connssl->encdata_length - connssl->encdata_offset);
+  if(read < 0) {
+    connssl->connecting_state = ssl_connect_2_reading;
+    infof(data, "schannel: failed to receive handshake, waiting for more: %d\n",
+          read);
+    return CURLE_OK;
+  }
+  else if(read == 0) {
+    failf(data, "schannel: failed to receive handshake, connection failed\n");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+  else if(read > 0) {
+    /* increase encrypted data buffer offset */
+    connssl->encdata_offset += read;
+  }
+
+  infof(data, "schannel: encrypted data buffer %d/%d\n",
+    connssl->encdata_offset, connssl->encdata_length);
+
+  /* setup input buffers */
+  inbuf[0].pvBuffer = malloc(connssl->encdata_offset);
+  inbuf[0].cbBuffer = connssl->encdata_offset;
+  inbuf[0].BufferType = SECBUFFER_TOKEN;
+
+  inbuf[1].pvBuffer = NULL;
+  inbuf[1].cbBuffer = 0;
+  inbuf[1].BufferType = SECBUFFER_EMPTY;
+
+  inbuf_desc.pBuffers = &inbuf[0];
+  inbuf_desc.cBuffers = 2;
+  inbuf_desc.ulVersion = SECBUFFER_VERSION;
+
+  /* setup output buffers */
+  outbuf[0].pvBuffer = NULL;
+  outbuf[0].cbBuffer = 0;
+  outbuf[0].BufferType = SECBUFFER_TOKEN;
+
+  outbuf[1].pvBuffer = NULL;
+  outbuf[1].cbBuffer = 0;
+  outbuf[1].BufferType = SECBUFFER_ALERT;
+
+  outbuf_desc.pBuffers = &outbuf[0];
+  outbuf_desc.cBuffers = 2;
+  outbuf_desc.ulVersion = SECBUFFER_VERSION;
+
+  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);
+
+  /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
+  sspi_status = s_pSecFn->InitializeSecurityContextA(
+    &connssl->cred_handle, &connssl->ctxt_handle, conn->host.name,
+    connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL, &outbuf_desc,
+    &connssl->ret_flags, &connssl->time_stamp);
+
+  /* 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: %d\n",
+          sspi_status);
+    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",
+              outbuf[i].cbBuffer);
+
+        /* send handshake token to server */
+        write = swrite(conn->sock[sockindex],
+                       outbuf[i].pvBuffer, outbuf[i].cbBuffer);
+        if(write != outbuf[i].cbBuffer) {
+          failf(data, "schannel: failed to send next handshake data: %d\n",
+                write);
+          return CURLE_SSL_CONNECT_ERROR;
+        }
+      }
+
+      /* 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\n");
+    else
+      failf(data, "schannel: next InitializeSecurityContextA failed: %d\n",
+            sspi_status);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  /* check if there was additional remaining encrypted data */
+  if(inbuf[1].BufferType = SECBUFFER_EXTRA) {
+    infof(data, "schannel: encrypted data length: %d\n", inbuf[1].cbBuffer);
+
+    /* 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;
+    }
+  }
+  else {
+    connssl->encdata_offset = 0;
+  }
+
+  /* check if the handshake needs to be continued */
+  if(sspi_status == SEC_I_CONTINUE_NEEDED) {
+    connssl->connecting_state = ssl_connect_2_reading;
+    return CURLE_OK;
+  }
+
+  /* check if the handshake is complete */
+  if(sspi_status == SEC_E_OK) {
+    infof(data, "schannel: handshake complete\n");
+
+    /* TODO: implement verification results */
+
+    connssl->connecting_state = ssl_connect_3;
+    infof(data, "SSL connected\n");
+  }
+
+  return CURLE_OK;
+}
+
+static CURLcode
+schannel_connect_step3(struct connectdata *conn, int sockindex) {
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+
+  connssl->connecting_state = ssl_connect_done;
+
+  return CURLE_OK;
+}
+
+static CURLcode
+schannel_connect_common(struct connectdata *conn, int sockindex,
+                        bool nonblocking, bool *done) {
+  CURLcode retcode;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  curl_socket_t sockfd = conn->sock[sockindex];
+  long timeout_ms;
+  int what;
+
+  /* check if the connection has already been established */
+  if(ssl_connection_complete == connssl->state) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  if(ssl_connect_1 == connssl->connecting_state) {
+    /* check out how much more time we're allowed */
+    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+
+    retcode = schannel_connect_step1(conn, sockindex);
+    if(retcode)
+      return retcode;
+  }
+
+  while(ssl_connect_2 == connssl->connecting_state ||
+        ssl_connect_2_reading == connssl->connecting_state ||
+        ssl_connect_2_writing == connssl->connecting_state) {
+
+    /* check out how much more time we're allowed */
+    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "SSL connection timeout");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+
+    /* if ssl is expecting something, check if it's available. */
+    if(connssl->connecting_state == ssl_connect_2_reading
+       || connssl->connecting_state == ssl_connect_2_writing) {
+
+      curl_socket_t writefd = ssl_connect_2_writing ==
+        connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
+      curl_socket_t readfd = ssl_connect_2_reading ==
+        connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
+
+      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);
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+      else if(0 == what) {
+        if(nonblocking) {
+          *done = FALSE;
+          return CURLE_OK;
+        }
+        else {
+          /* timeout */
+          failf(data, "SSL connection timeout");
+          return CURLE_OPERATION_TIMEDOUT;
+        }
+      }
+      /* socket is readable or writable */
+    }
+
+    /* Run transaction, and return to the caller if it failed or if
+     * this connection is part of a multi handle and this loop would
+     * execute again. This permits the owner of a multi handle to
+     * abort a connection attempt before step2 has completed while
+     * ensuring that a client using select() or epoll() will always
+     * have a valid fdset to wait on.
+     */
+    retcode = schannel_connect_step2(conn, sockindex);
+    if(retcode || (nonblocking &&
+                   (ssl_connect_2 == connssl->connecting_state ||
+                    ssl_connect_2_reading == connssl->connecting_state ||
+                    ssl_connect_2_writing == connssl->connecting_state)))
+      return retcode;
+
+  } /* repeat step2 until all transactions are done. */
+
+  if(ssl_connect_3 == connssl->connecting_state) {
+    retcode = schannel_connect_step3(conn, sockindex);
+    if(retcode)
+      return retcode;
+  }
+
+  if(ssl_connect_done == connssl->connecting_state) {
+    connssl->state = ssl_connection_complete;
+    conn->recv[sockindex] = schannel_recv;
+    conn->send[sockindex] = schannel_send;
+    *done = TRUE;
+  }
+  else
+    *done = FALSE;
+
+  /* reset our connection state machine */
+  connssl->connecting_state = ssl_connect_1;
+
+  return CURLE_OK;
+}
+
+static ssize_t
+schannel_send(struct connectdata *conn, int sockindex,
+              const void *buf, size_t len, CURLcode *err) {
+  ssize_t ret = -1;
+  size_t data_len = 0;
+  unsigned char *data = NULL;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  SecBuffer outbuf[4];
+  SecBufferDesc outbuf_desc;
+  SECURITY_STATUS sspi_status = SEC_E_OK;
+
+  /* check if the maximum stream sizes were queried */
+  if(connssl->stream_sizes.cbMaximumMessage == 0) {
+    sspi_status = s_pSecFn->QueryContextAttributesA(&connssl->ctxt_handle,
+                                                    SECPKG_ATTR_STREAM_SIZES,
+                                                    &connssl->stream_sizes);
+    if(sspi_status != SEC_E_OK) {
+      *err = CURLE_SEND_ERROR;
+      return -1;
+    }
+  }
+
+  /* check if the buffer is longer than the maximum message length */
+  if(len > connssl->stream_sizes.cbMaximumMessage) {
+    *err = CURLE_SEND_ERROR;
+    return -1;
+  }
+
+  /* calculate the complete message length and allocate a buffer for it */
+  data_len = connssl->stream_sizes.cbHeader + len +
+              connssl->stream_sizes.cbTrailer;
+  data = (unsigned char*) malloc(data_len);
+  if(data == NULL) {
+    *err = CURLE_OUT_OF_MEMORY;
+    return -1;
+  }
+
+  /* setup output buffers (header, data, trailer, empty) */
+  outbuf[0].pvBuffer = data;
+  outbuf[0].cbBuffer = connssl->stream_sizes.cbHeader;
+  outbuf[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+  outbuf[1].pvBuffer = data + connssl->stream_sizes.cbHeader;
+  outbuf[1].cbBuffer = len;
+  outbuf[1].BufferType = SECBUFFER_DATA;
+
+  outbuf[2].pvBuffer = data + connssl->stream_sizes.cbHeader + len;
+  outbuf[2].cbBuffer = connssl->stream_sizes.cbTrailer;
+  outbuf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+  outbuf[3].pvBuffer = NULL;
+  outbuf[3].cbBuffer = 0;
+  outbuf[3].BufferType = SECBUFFER_EMPTY;
+
+  outbuf_desc.pBuffers = &outbuf[0];
+  outbuf_desc.cBuffers = 4;
+  outbuf_desc.ulVersion = SECBUFFER_VERSION;
+
+  /* copy data into output buffer */
+  memcpy(outbuf[1].pvBuffer, buf, len);
+
+  /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
+  sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt_handle, 0,
+                                         &outbuf_desc, 0);
+
+  /* check if the message was encrypted */
+  if(sspi_status == SEC_E_OK) {
+    /* send the encrypted message including header, data and trailer */
+    len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
+    ret = swrite(conn->sock[sockindex], data, len);
+    /* TODO: implement write buffering */
+  }
+  else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
+    *err = CURLE_OUT_OF_MEMORY;
+  }
+  else{
+    *err = CURLE_SEND_ERROR;
+  }
+
+  free(data);
+
+  return ret;
+}
+
+static ssize_t
+schannel_recv(struct connectdata *conn, int sockindex,
+              char *buf, size_t len, CURLcode *err) {
+  int i = 0;
+  size_t size = 0;
+  ssize_t read = 0, ret = -1;
+  CURLcode retcode;
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  bool done = FALSE;
+  SecBuffer inbuf[4];
+  SecBufferDesc inbuf_desc;
+  SECURITY_STATUS sspi_status = SEC_E_OK;
+
+  infof(data, "schannel: client wants to read %d\n", len);
+  *err = CURLE_OK;
+
+  /* buffer to store previously received and decrypted data */
+  if(connssl->decdata_buffer == NULL) {
+    connssl->decdata_offset = 0;
+    connssl->decdata_length = 4096;
+    connssl->decdata_buffer = malloc(connssl->decdata_length);
+    if(connssl->decdata_buffer == NULL) {
+      failf(data, "schannel: unable to allocate memory");
+      return CURLE_OUT_OF_MEMORY;
+    }
+  }
+
+  /* increase buffers in order to fit the requested amount of data */
+  while(connssl->encdata_length - connssl->encdata_offset < 2048 ||
+        connssl->encdata_length < len) {
+    /* increase internal encrypted data buffer */
+    connssl->encdata_length += 2048;
+    connssl->encdata_buffer = realloc(connssl->encdata_buffer,
+                                      connssl->encdata_length);
+    if(connssl->encdata_buffer == NULL) {
+      failf(data, "schannel: unable to re-allocate memory");
+      *err = CURLE_OUT_OF_MEMORY;
+      return -1;
+    }
+
+    /* increase internal decrypted data buffer */
+    connssl->decdata_length += 2048;
+    connssl->decdata_buffer = realloc(connssl->decdata_buffer,
+                                      connssl->decdata_length);
+    if(connssl->decdata_buffer == NULL) {
+      failf(data, "schannel: unable to re-allocate memory");
+      *err = CURLE_OUT_OF_MEMORY;
+      return -1;
+    }
+  }
+
+  /* read encrypted data from socket */
+  infof(data, "schannel: encrypted data buffer %d/%d\n",
+        connssl->encdata_offset, connssl->encdata_length);
+  size = connssl->encdata_length - connssl->encdata_offset;
+  if(size > 0) {
+    read = sread(conn->sock[sockindex],
+                 connssl->encdata_buffer + connssl->encdata_offset, size);
+    infof(data, "schannel: encrypted data received %d\n", read);
+
+    /* check for received data */
+    if(read > 0) {
+      /* increase encrypted data buffer offset */
+      connssl->encdata_offset += read;
+    }
+  }
+
+  infof(data, "schannel: encrypted data buffer %d/%d\n",
+    connssl->encdata_offset, connssl->encdata_length);
+
+  /* check if we still have some data in our buffers */
+  while(connssl->encdata_offset > 0 &&
+        sspi_status != SEC_E_INCOMPLETE_MESSAGE) {
+
+    /* prepare data buffer for DecryptMessage call */
+    inbuf[0].pvBuffer = connssl->encdata_buffer;
+    inbuf[0].cbBuffer = connssl->encdata_offset;
+    inbuf[0].BufferType = SECBUFFER_DATA;
+
+    /* we need 3 more empty input buffers for possible output */
+    inbuf[1].pvBuffer = NULL;
+    inbuf[1].cbBuffer = 0;
+    inbuf[1].BufferType = SECBUFFER_EMPTY;
+
+    inbuf[2].pvBuffer = NULL;
+    inbuf[2].cbBuffer = 0;
+    inbuf[2].BufferType = SECBUFFER_EMPTY;
+
+    inbuf[3].pvBuffer = NULL;
+    inbuf[3].cbBuffer = 0;
+    inbuf[3].BufferType = SECBUFFER_EMPTY;
+
+    inbuf_desc.pBuffers = &inbuf[0];
+    inbuf_desc.cBuffers = 4;
+    inbuf_desc.ulVersion = SECBUFFER_VERSION;
+
+    /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
+    sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt_handle,
+                                           &inbuf_desc, 0, NULL);
+    infof(data, "schannel: DecryptMessage %d\n", sspi_status);
+
+    /* check if everything went fine (server may want to renegotiate context) */
+    if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE) {
+      /* check for successfully decrypted data */
+      if(inbuf[1].BufferType == SECBUFFER_DATA) {
+        infof(data, "schannel: decrypted data length: %d\n", inbuf[1].cbBuffer);
+
+        /* copy decrypted data to internal buffer */
+        size = connssl->decdata_length - connssl->decdata_offset;
+        size = size < inbuf[1].cbBuffer ? size : inbuf[1].cbBuffer;
+        if(size > 0) {
+          memcpy(connssl->decdata_buffer + connssl->decdata_offset,
+                 inbuf[1].pvBuffer, size);
+          connssl->decdata_offset += size;
+        }
+
+        infof(data, "schannel: decrypted data added: %d\n", size);
+        infof(data, "schannel: decrypted data cached: %d/%d\n",
+              connssl->decdata_offset, connssl->decdata_length);
+      }
+
+      /* check for remaining encrypted data */
+      if(inbuf[3].BufferType = SECBUFFER_EXTRA) {
+        infof(data, "schannel: encrypted data length: %d\n", inbuf[3].cbBuffer);
+
+        /* 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);
+          connssl->encdata_offset = inbuf[3].cbBuffer;
+        }
+
+        infof(data, "schannel: encrypted data cached: %d/%d\n",
+              connssl->encdata_offset, connssl->encdata_length);
+      }
+      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: client needs to renegotiate with server\n");
+
+      /* begin renegotiation */
+      connssl->state = ssl_connection_negotiating;
+      retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
+      if(retcode)
+        *err = retcode;
+    }
+  }
+
+  /* copy requested decrypted data to supplied buffer */
+  size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
+  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;
+  }
+
+  /* reduce internal buffer length to reduce memory usage */
+  if(connssl->encdata_length > 4096) {
+    connssl->encdata_length = connssl->encdata_offset > 0 ?
+                              connssl->encdata_offset + 2048 : 4096;
+    connssl->encdata_buffer = realloc(connssl->encdata_buffer,
+                                      connssl->encdata_length);
+  }
+  if(connssl->decdata_length > 4096) {
+    connssl->decdata_length = connssl->decdata_offset > 0 ?
+                              connssl->decdata_offset + 2048 : 4096;
+    connssl->decdata_buffer = realloc(connssl->decdata_buffer,
+                                      connssl->decdata_length);
+  }
+
+  /* check if something went wrong and we need to return an error */
+  if(ret < 0) {
+    if(sspi_status == SEC_E_INCOMPLETE_MESSAGE)
+      *err = CURLE_AGAIN;
+    else if(sspi_status != SEC_E_OK)
+      *err = CURLE_RECV_ERROR;
+    return -1;
+  }
+
+  /* everything went fine */
+  infof(data, "schannel: read returns %d with error code %d\n", ret, *err);
+
+  return ret;
+}
+
+CURLcode
+Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
+                                  bool *done) {
+  return schannel_connect_common(conn, sockindex, TRUE, done);
+}
+
+CURLcode
+Curl_schannel_connect(struct connectdata *conn, int sockindex) {
+  CURLcode retcode;
+  bool done = FALSE;
+
+  retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
+  if(retcode)
+    return retcode;
+
+  DEBUGASSERT(done);
+
+  return CURLE_OK;
+}
+
+bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex) {
+  const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  if(connssl->schannel) /* SSL is in use */
+    return (connssl->encdata_offset > 0 ||
+            connssl->decdata_offset > 0 ) ? TRUE : FALSE;
+  else
+    return FALSE;
+}
+
+void Curl_schannel_close(struct connectdata *conn, int sockindex) {
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  infof(data, "schannel: Closing connection with %s:%d\n",
+        conn->host.name, conn->remote_port);
+
+  /* free SSPI Schannel API context and handle */
+  if(connssl->schannel) {
+    s_pSecFn->DeleteSecurityContext(&connssl->ctxt_handle);
+    s_pSecFn->FreeCredentialsHandle(&connssl->cred_handle);
+  }
+
+  /* free internal buffer for received encrypted data */
+  if(connssl->encdata_buffer != NULL) {
+    free(connssl->encdata_buffer);
+    connssl->encdata_buffer = NULL;
+    connssl->encdata_length = 0;
+    connssl->encdata_offset = 0;
+  }
+
+  /* free internal buffer for received decrypted data */
+  if(connssl->decdata_buffer != NULL) {
+    free(connssl->decdata_buffer);
+    connssl->decdata_buffer = NULL;
+    connssl->decdata_length = 0;
+    connssl->decdata_offset = 0;
+  }
+}
+
+int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) {
+  return CURLE_NOT_BUILT_IN; /* TODO: implement SSL/TLS shutdown */
+}
+
+int Curl_schannel_init() {
+  return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
+}
+
+void Curl_schannel_cleanup() {
+  Curl_sspi_global_cleanup();
+}
+
+size_t Curl_schannel_version(char *buffer, size_t size)
+{
+  unsigned long version = s_pSecFn ? s_pSecFn->dwVersion : 0;
+  return snprintf(buffer, size, "Schannel/%d.%d.%d.%d",
+                  (version>>0)&0xff, (version>>8)&0xff,
+                  (version>>16)&0xff, (version>>24)&0xff);
+}
+
+#endif /* USE_SCHANNEL */
+#endif /* USE_WINDOWS_SSPI */
diff --git a/lib/curl_schannel.h b/lib/curl_schannel.h
new file mode 100644
index 0000000..3de932b
--- /dev/null
+++ b/lib/curl_schannel.h
@@ -0,0 +1,65 @@
+#ifndef HEADER_SCHANNEL_H
+#define HEADER_SCHANNEL_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#include "setup.h"
+
+#ifdef USE_WINDOWS_SSPI
+#ifdef USE_SCHANNEL
+
+#ifndef UNISP_NAME_A
+#define UNISP_NAME_A "Microsoft Unified Security Protocol Provider"
+#endif
+
+CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex);
+
+CURLcode Curl_schannel_connect_nonblocking(struct connectdata *conn,
+                                           int sockindex,
+                                           bool *done);
+
+bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex);
+void Curl_schannel_close(struct connectdata *conn, int sockindex);
+int Curl_schannel_shutdown(struct connectdata *conn, int sockindex);
+
+int Curl_schannel_init();
+void Curl_schannel_cleanup();
+size_t Curl_schannel_version(char *buffer, size_t size);
+
+/* API setup for Schannel */
+#define curlssl_init Curl_schannel_init
+#define curlssl_cleanup Curl_schannel_cleanup
+#define curlssl_connect Curl_schannel_connect
+#define curlssl_connect_nonblocking Curl_schannel_connect_nonblocking
+#define curlssl_session_free(x)  (x=x, CURLE_NOT_BUILT_IN)
+#define curlssl_close_all(x) (x=x, CURLE_NOT_BUILT_IN)
+#define curlssl_close Curl_schannel_close
+#define curlssl_shutdown Curl_schannel_shutdown
+#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_NOT_BUILT_IN)
+#define curlssl_set_engine_default(x) (x=x, CURLE_NOT_BUILT_IN)
+#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL)
+#define curlssl_version Curl_schannel_version
+#define curlssl_check_cxn(x) (x=x, -1)
+#define curlssl_data_pending Curl_schannel_data_pending
+
+#endif /* USE_SCHANNEL */
+#endif /* USE_WINDOWS_SSPI */
+#endif /* HEADER_SCHANNEL_H */
diff --git a/lib/sslgen.c b/lib/sslgen.c
index a77fd78..5d2ead0 100644
--- a/lib/sslgen.c
+++ b/lib/sslgen.c
@@ -33,6 +33,7 @@
    Curl_nss_ - prefix for NSS ones
    Curl_polarssl_ - prefix for PolarSSL ones
    Curl_cyassl_ - prefix for CyaSSL ones
+   Curl_schannel_ - prefix for Schannel SSPI ones
 
    Note that this source code uses curlssl_* functions, and they are all
    defines/macros #defined by the lib-specific header files.
@@ -57,6 +58,7 @@
 #include "polarssl.h" /* PolarSSL versions */
 #include "axtls.h"  /* axTLS versions */
 #include "cyassl.h"  /* CyaSSL versions */
+#include "curl_schannel.h" /* Schannel SSPI version */
 #include "sendf.h"
 #include "rawstr.h"
 #include "url.h"
diff --git a/lib/urldata.h b/lib/urldata.h
index 20519cf..26c0581 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -131,6 +131,11 @@
 #undef realloc
 #endif /* USE_AXTLS */
 
+#ifdef USE_SCHANNEL
+#include <schnlsp.h>
+#include "curl_sspi.h"
+#endif
+
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif
@@ -282,6 +287,18 @@ struct ssl_connect_data {
   SSL_CTX* ssl_ctx;
   SSL*     ssl;
 #endif /* USE_AXTLS */
+#ifdef USE_SCHANNEL
+  bool schannel;
+  TimeStamp time_stamp;
+  CredHandle cred_handle;
+  CtxtHandle ctxt_handle;
+  SecPkgContext_StreamSizes stream_sizes;
+  ssl_connect_state connecting_state;
+  size_t encdata_length, decdata_length;
+  size_t encdata_offset, decdata_offset;
+  unsigned char *encdata_buffer, *decdata_buffer;
+  unsigned long req_flags, ret_flags;
+#endif /* USE_SCHANNEL */
 };
 
 struct ssl_config_data {
-- 
1.7.10.msysgit.1


From dbafcdf20d610772cb07aca6f7d037044d7c70a8 Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Mon, 9 Apr 2012 15:40:51 +0200
Subject: [PATCH 03/18] Updated winbuild scripts to add WITH_SSL=schannel
 option

---
 lib/Makefile.inc          |    4 ++--
 winbuild/Makefile.vc      |    4 ++++
 winbuild/MakefileBuild.vc |   11 +++++++----
 3 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index e39aa84..7ff4aea 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -23,7 +23,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c	\
   curl_rtmp.c openldap.c curl_gethostname.c gopher.c axtls.c		\
   idn_win32.c http_negotiate_sspi.c cyassl.c http_proxy.c non-ascii.c	\
   asyn-ares.c asyn-thread.c curl_gssapi.c curl_ntlm.c curl_ntlm_wb.c	\
-  curl_ntlm_core.c curl_ntlm_msgs.c curl_sasl.c
+  curl_ntlm_core.c curl_ntlm_msgs.c curl_sasl.c curl_schannel.c
 
 HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h	\
   progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h	\
@@ -40,4 +40,4 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h	\
   warnless.h curl_hmac.h polarssl.h curl_rtmp.h curl_gethostname.h	\
   gopher.h axtls.h cyassl.h http_proxy.h non-ascii.h asyn.h curl_ntlm.h	\
   curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h curl_ntlm_msgs.h	\
-  curl_sasl.h
+  curl_sasl.h curl_schannel.h
diff --git a/winbuild/Makefile.vc b/winbuild/Makefile.vc
index 8fafecf..de2488a 100644
--- a/winbuild/Makefile.vc
+++ b/winbuild/Makefile.vc
@@ -67,6 +67,10 @@ SSL=dll
 !ELSEIF "$(WITH_SSL)"=="static"
 USE_SSL      = true
 SSL=static
+!ELSEIF "$(WITH_SSL)"=="schannel"
+USE_SSL      = true
+USE_SSPI     = true
+SSL=schannel
 !ENDIF
 
 !IF "$(WITH_ZLIB)"=="dll"
diff --git a/winbuild/MakefileBuild.vc b/winbuild/MakefileBuild.vc
index d447922..e026135 100644
--- a/winbuild/MakefileBuild.vc
+++ b/winbuild/MakefileBuild.vc
@@ -96,16 +96,19 @@ LFLAGS         = $(LFLAGS) "/LIBPATH:$(DEVEL_LIB)"
 
 !IF "$(WITH_SSL)"=="dll"
 SSL_LIBS     = libeay32.lib ssleay32.lib
+SSL_CFLAGS   = /DUSE_SSLEAY /I"$(DEVEL_INCLUDE)/openssl"
 USE_SSL      = true
 SSL=dll
 !ELSEIF "$(WITH_SSL)"=="static"
 SSL_LIBS     = libeay32.lib ssleay32.lib gdi32.lib user32.lib advapi32.lib
+SSL_CFLAGS   = /DUSE_SSLEAY /I"$(DEVEL_INCLUDE)/openssl"
 USE_SSL      = true
 SSL=static
-!ENDIF
-
-!IFDEF USE_SSL
-SSL_CFLAGS   = /DUSE_SSLEAY /I"$(DEVEL_INCLUDE)/openssl"
+!ELSEIF "$(WITH_SSL)"=="schannel"
+USE_SSL      = true
+USE_SSPI     = yes
+SSL_CFLAGS   = /DUSE_SSL /DUSE_SCHANNEL
+SSL=schannel
 !ENDIF
 
 !IF "$(WITH_ZLIB)"=="dll"
-- 
1.7.10.msysgit.1


From f69d9ff55da03f09f157f0a1421c08749b429529 Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Mon, 9 Apr 2012 18:35:00 +0200
Subject: [PATCH 04/18] Allow certificate and revocation checks being
 deactivated

---
 lib/curl_schannel.c |   14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index cfac6ef..158b30c 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -96,8 +96,18 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
   /* setup Schannel API options */
   memset(&schannel_cred, 0, sizeof(schannel_cred));
   schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
-  schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
-                          SCH_CRED_REVOCATION_CHECK_CHAIN;
+
+  if(data->set.ssl.verifypeer) {
+    schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
+                            SCH_CRED_REVOCATION_CHECK_CHAIN;
+    infof(data, "schannel: checking server certificate and 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: disable server certificate and revocation checks\n");
+  }
 
   if(Curl_inet_pton(AF_INET, conn->host.name, &addr) ||
 #ifdef ENABLE_IPV6
-- 
1.7.10.msysgit.1


From 2f04c5d35609e4bbce3d55b8c6b87449bbda45dd Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Mon, 9 Apr 2012 22:33:58 +0200
Subject: [PATCH 05/18] Check for required context attributes

---
 lib/curl_schannel.c |   22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index 158b30c..2ad0e0d 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -161,9 +161,8 @@ 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_INTEGRITY |
-                       ISC_REQ_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY |
-                       ISC_REQ_STREAM;
+                       ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR |
+                       ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
 
   /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
   sspi_status = s_pSecFn->InitializeSecurityContextA(&connssl->cred_handle,
@@ -372,10 +371,27 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
 
 static CURLcode
 schannel_connect_step3(struct connectdata *conn, int sockindex) {
+  struct SessionHandle *data = conn->data;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
 
+  if (connssl->ret_flags != connssl->req_flags) {
+    if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
+      failf(data, "schannel: failed to setup sequence detection\n");
+    if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
+      failf(data, "schannel: failed to setup replay detection\n");
+    if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY))
+      failf(data, "schannel: failed to setup confidentiality\n");
+    if(!(connssl->ret_flags & ISC_RET_EXTENDED_ERROR))
+      failf(data, "schannel: failed to setup extended errors\n");
+    if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY))
+      failf(data, "schannel: failed to setup memory allocation\n");
+    if(!(connssl->ret_flags & ISC_RET_STREAM))
+      failf(data, "schannel: failed to setup stream orientation\n");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
   connssl->connecting_state = ssl_connect_done;
 
   return CURLE_OK;
-- 
1.7.10.msysgit.1


From 5de4b11d27c464ffc2f00e03197eea6ec3e3e216 Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Mon, 9 Apr 2012 22:43:48 +0200
Subject: [PATCH 06/18] Code cleanup

---
 lib/curl_schannel.c |   14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index 2ad0e0d..7619338 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -40,10 +40,8 @@
  * TODO list for TLS/SSL implementation:
  * - implement session handling and re-use
  * - implement write buffering
- * - implement verification options
- * - implement verification results
  * - implement SSL/TLS shutdown
- * - special cases: negotiation, certificates, algorithms
+ * - special cases: renegotiation, certificates, algorithms
  */
 
 #include "setup.h"
@@ -132,8 +130,6 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
       break;
   }
 
-  /* TODO: implement verification options */
-
   /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
   sspi_status = s_pSecFn->AcquireCredentialsHandleA(NULL,
     UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &schannel_cred,
@@ -358,12 +354,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
 
   /* check if the handshake is complete */
   if(sspi_status == SEC_E_OK) {
-    infof(data, "schannel: handshake complete\n");
-
-    /* TODO: implement verification results */
-
     connssl->connecting_state = ssl_connect_3;
-    infof(data, "SSL connected\n");
+    infof(data, "schannel: handshake complete\n");
   }
 
   return CURLE_OK;
@@ -376,7 +368,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) {
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
 
-  if (connssl->ret_flags != connssl->req_flags) {
+  if(connssl->ret_flags != connssl->req_flags) {
     if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
       failf(data, "schannel: failed to setup sequence detection\n");
     if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
-- 
1.7.10.msysgit.1


From ea32ed41217f8bc82e389d3d48ebf70d1d690f66 Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Mon, 9 Apr 2012 23:24:55 +0200
Subject: [PATCH 07/18] Cache credential handles in session cache for SSL/TLS
 session re-use

---
 lib/curl_schannel.c |  194 +++++++++++++++++++++++++++++++++------------------
 lib/curl_schannel.h |   15 +++-
 lib/urldata.h       |    8 +--
 3 files changed, 143 insertions(+), 74 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index 7619338..9157bda 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -38,7 +38,6 @@
 
 /*
  * TODO list for TLS/SSL implementation:
- * - implement session handling and re-use
  * - implement write buffering
  * - implement SSL/TLS shutdown
  * - special cases: renegotiation, certificates, algorithms
@@ -49,8 +48,6 @@
 #ifdef USE_WINDOWS_SSPI
 #ifdef USE_SCHANNEL
 
-#include <schnlsp.h>
-
 #include "urldata.h"
 #include "curl_sspi.h"
 #include "curl_schannel.h"
@@ -83,6 +80,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
   SecBufferDesc outbuf_desc;
   SCHANNEL_CRED schannel_cred;
   SECURITY_STATUS sspi_status = SEC_E_OK;
+  curl_schannel_cred *old_cred = NULL;
   struct in_addr addr;
 #ifdef ENABLE_IPV6
   struct in6_addr addr6;
@@ -91,60 +89,75 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
   infof(data, "schannel: Connecting to %s:%d (step 1/3)\n",
         conn->host.name, conn->remote_port);
 
-  /* setup Schannel API options */
-  memset(&schannel_cred, 0, sizeof(schannel_cred));
-  schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
-
-  if(data->set.ssl.verifypeer) {
-    schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
-                            SCH_CRED_REVOCATION_CHECK_CHAIN;
-    infof(data, "schannel: checking server certificate and revocation\n");
+  /* check for an existing re-usable credential handle */
+  if(!Curl_ssl_getsessionid(conn, &old_cred, NULL)) {
+    connssl->cred = old_cred;
+    infof(data, "schannel: re-using existing credential handle\n");
   }
   else {
-    schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
-                            SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
-                            SCH_CRED_IGNORE_REVOCATION_OFFLINE;
-    infof(data, "schannel: disable server certificate and revocation checks\n");
-  }
+    /* setup Schannel API options */
+    memset(&schannel_cred, 0, sizeof(schannel_cred));
+    schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
+
+    if(data->set.ssl.verifypeer) {
+      schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
+                              SCH_CRED_REVOCATION_CHECK_CHAIN;
+      infof(data, "schannel: checking server certificate and 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: disable server certificate and revocation checks\n");
+    }
 
-  if(Curl_inet_pton(AF_INET, conn->host.name, &addr) ||
+    if(Curl_inet_pton(AF_INET, conn->host.name, &addr) ||
 #ifdef ENABLE_IPV6
-     Curl_inet_pton(AF_INET6, conn->host.name, &addr6) ||
+       Curl_inet_pton(AF_INET6, conn->host.name, &addr6) ||
 #endif
-     data->set.ssl.verifyhost < 2) {
-    schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
-    infof(data, "schannel: using IP address, disable SNI servername check\n");
-  }
-
-  switch(data->set.ssl.version) {
-    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_SSLv3:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
-      break;
-    case CURL_SSLVERSION_SSLv2:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
-      break;
-  }
-
-  /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
-  sspi_status = s_pSecFn->AcquireCredentialsHandleA(NULL,
-    UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &schannel_cred,
-    NULL, NULL, &connssl->cred_handle, &connssl->time_stamp);
-
-  if(sspi_status != SEC_E_OK) {
-    if(sspi_status == SEC_E_WRONG_PRINCIPAL)
-      failf(data, "schannel: SNI or certificate check failed\n");
-    else
-      failf(data, "schannel: AcquireCredentialsHandleA failed: %d\n",
-            sspi_status);
-    return CURLE_SSL_CONNECT_ERROR;
-  }
+       data->set.ssl.verifyhost < 2) {
+      schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
+      infof(data, "schannel: using IP address, disable SNI servername check\n");
+    }
+
+    switch(data->set.ssl.version) {
+      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_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 = malloc(sizeof(curl_schannel_cred));
+    if (!connssl->cred) {
+      failf(data, "schannel: unable to allocate memory");
+      return CURLE_OUT_OF_MEMORY;
+    }
+    memset(connssl->cred, 0, sizeof(curl_schannel_cred));
+
+    /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
+    sspi_status = s_pSecFn->AcquireCredentialsHandleA(NULL,
+      UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL,
+      &connssl->cred->cred_handle, &connssl->cred->time_stamp);
 
-  connssl->schannel = TRUE;
+    if(sspi_status != SEC_E_OK) {
+      if(sspi_status == SEC_E_WRONG_PRINCIPAL)
+        failf(data, "schannel: SNI or certificate check failed\n");
+      else
+        failf(data, "schannel: AcquireCredentialsHandleA failed: %d\n",
+              sspi_status);
+      free(connssl->cred);
+      connssl->cred = NULL;
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
 
   /* setup output buffer */
   outbuf.pvBuffer = NULL;
@@ -160,11 +173,19 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
                        ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR |
                        ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
 
+  /* allocate memory for the security context handle */
+  connssl->ctxt = malloc(sizeof(curl_schannel_ctxt));
+  if (!connssl->ctxt) {
+    failf(data, "schannel: unable to allocate memory");
+    return CURLE_OUT_OF_MEMORY;
+  }
+  memset(connssl->ctxt, 0, sizeof(curl_schannel_ctxt));
+
   /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
-  sspi_status = s_pSecFn->InitializeSecurityContextA(&connssl->cred_handle,
-    NULL, conn->host.name, connssl->req_flags, 0, 0, NULL, 0,
-    &connssl->ctxt_handle, &outbuf_desc,
-    &connssl->ret_flags, &connssl->time_stamp);
+  sspi_status = s_pSecFn->InitializeSecurityContextA(
+    &connssl->cred->cred_handle, NULL, conn->host.name,
+    connssl->req_flags, 0, 0, NULL, 0, &connssl->ctxt->ctxt_handle,
+    &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
 
   if(sspi_status != SEC_I_CONTINUE_NEEDED) {
     if(sspi_status == SEC_E_WRONG_PRINCIPAL)
@@ -172,6 +193,8 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
     else
       failf(data, "schannel: initial InitializeSecurityContextA failed: %d\n",
             sspi_status);
+    free(connssl->ctxt);
+    connssl->ctxt = NULL;
     return CURLE_SSL_CONNECT_ERROR;
   }
 
@@ -280,9 +303,9 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
 
   /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
   sspi_status = s_pSecFn->InitializeSecurityContextA(
-    &connssl->cred_handle, &connssl->ctxt_handle, conn->host.name,
-    connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL, &outbuf_desc,
-    &connssl->ret_flags, &connssl->time_stamp);
+    &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
+    conn->host.name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
+    &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
 
   /* free buffer for received handshake data */
   free(inbuf[0].pvBuffer);
@@ -363,11 +386,15 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
 
 static CURLcode
 schannel_connect_step3(struct connectdata *conn, int sockindex) {
+  CURLcode retcode = CURLE_OK;
   struct SessionHandle *data = conn->data;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  curl_schannel_cred *old_cred = NULL;
+  int incache;
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
 
+  /* 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");
@@ -384,6 +411,27 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) {
     return CURLE_SSL_CONNECT_ERROR;
   }
 
+  /* save the current session data for possible re-use */
+  incache = !(Curl_ssl_getsessionid(conn, &old_cred, NULL));
+  if(incache) {
+    if(old_cred != connssl->cred) {
+      infof(data, "schannel: old credential handle is stale, removing\n");
+      Curl_ssl_delsessionid(conn, old_cred);
+      incache = FALSE;
+    }
+  }
+  if(!incache) {
+    retcode = Curl_ssl_addsessionid(conn, connssl->cred,
+                                    sizeof(curl_schannel_cred));
+    if(retcode) {
+      failf(data, "schannel: failed to store credential handle\n");
+      return retcode;
+    }
+    else {
+      infof(data, "schannel: stored crendential handle\n");
+    }
+  }
+
   connssl->connecting_state = ssl_connect_done;
 
   return CURLE_OK;
@@ -512,9 +560,9 @@ schannel_send(struct connectdata *conn, int sockindex,
 
   /* check if the maximum stream sizes were queried */
   if(connssl->stream_sizes.cbMaximumMessage == 0) {
-    sspi_status = s_pSecFn->QueryContextAttributesA(&connssl->ctxt_handle,
-                                                    SECPKG_ATTR_STREAM_SIZES,
-                                                    &connssl->stream_sizes);
+    sspi_status = s_pSecFn->QueryContextAttributesA(
+                              &connssl->ctxt->ctxt_handle,
+                              SECPKG_ATTR_STREAM_SIZES, &connssl->stream_sizes);
     if(sspi_status != SEC_E_OK) {
       *err = CURLE_SEND_ERROR;
       return -1;
@@ -561,7 +609,7 @@ schannel_send(struct connectdata *conn, int sockindex,
   memcpy(outbuf[1].pvBuffer, buf, len);
 
   /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
-  sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt_handle, 0,
+  sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
                                          &outbuf_desc, 0);
 
   /* check if the message was encrypted */
@@ -681,7 +729,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
     inbuf_desc.ulVersion = SECBUFFER_VERSION;
 
     /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
-    sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt_handle,
+    sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
                                            &inbuf_desc, 0, NULL);
     infof(data, "schannel: DecryptMessage %d\n", sspi_status);
 
@@ -805,7 +853,7 @@ Curl_schannel_connect(struct connectdata *conn, int sockindex) {
 bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex) {
   const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
 
-  if(connssl->schannel) /* SSL is in use */
+  if(connssl->use) /* SSL is in use */
     return (connssl->encdata_offset > 0 ||
             connssl->decdata_offset > 0 ) ? TRUE : FALSE;
   else
@@ -819,10 +867,11 @@ void Curl_schannel_close(struct connectdata *conn, int sockindex) {
   infof(data, "schannel: Closing connection with %s:%d\n",
         conn->host.name, conn->remote_port);
 
-  /* free SSPI Schannel API context and handle */
-  if(connssl->schannel) {
-    s_pSecFn->DeleteSecurityContext(&connssl->ctxt_handle);
-    s_pSecFn->FreeCredentialsHandle(&connssl->cred_handle);
+  /* free SSPI Schannel API security context handle */
+  if(connssl->ctxt) {
+    s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
+    free(connssl->ctxt);
+    connssl->ctxt = NULL;
   }
 
   /* free internal buffer for received encrypted data */
@@ -846,6 +895,15 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) {
   return CURLE_NOT_BUILT_IN; /* TODO: implement SSL/TLS shutdown */
 }
 
+void Curl_schannel_session_free(void *ptr) {
+  curl_schannel_cred *cred = ptr;
+
+  if(cred) {
+    s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
+    free(cred);
+  }
+}
+
 int Curl_schannel_init() {
   return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
 }
diff --git a/lib/curl_schannel.h b/lib/curl_schannel.h
index 3de932b..fa6fc90 100644
--- a/lib/curl_schannel.h
+++ b/lib/curl_schannel.h
@@ -26,10 +26,22 @@
 #ifdef USE_WINDOWS_SSPI
 #ifdef USE_SCHANNEL
 
+#include <schnlsp.h>
+
 #ifndef UNISP_NAME_A
 #define UNISP_NAME_A "Microsoft Unified Security Protocol Provider"
 #endif
 
+typedef struct curl_schannel_cred {
+  CredHandle cred_handle;
+  TimeStamp time_stamp;
+} curl_schannel_cred;
+
+typedef struct curl_schannel_ctxt {
+  CtxtHandle ctxt_handle;
+  TimeStamp time_stamp;
+} curl_schannel_ctxt;
+
 CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex);
 
 CURLcode Curl_schannel_connect_nonblocking(struct connectdata *conn,
@@ -39,6 +51,7 @@ CURLcode Curl_schannel_connect_nonblocking(struct connectdata *conn,
 bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex);
 void Curl_schannel_close(struct connectdata *conn, int sockindex);
 int Curl_schannel_shutdown(struct connectdata *conn, int sockindex);
+void Curl_schannel_session_free(void *ptr);
 
 int Curl_schannel_init();
 void Curl_schannel_cleanup();
@@ -49,7 +62,7 @@ size_t Curl_schannel_version(char *buffer, size_t size);
 #define curlssl_cleanup Curl_schannel_cleanup
 #define curlssl_connect Curl_schannel_connect
 #define curlssl_connect_nonblocking Curl_schannel_connect_nonblocking
-#define curlssl_session_free(x)  (x=x, CURLE_NOT_BUILT_IN)
+#define curlssl_session_free Curl_schannel_session_free
 #define curlssl_close_all(x) (x=x, CURLE_NOT_BUILT_IN)
 #define curlssl_close Curl_schannel_close
 #define curlssl_shutdown Curl_schannel_shutdown
diff --git a/lib/urldata.h b/lib/urldata.h
index 26c0581..2b972d5 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -132,8 +132,8 @@
 #endif /* USE_AXTLS */
 
 #ifdef USE_SCHANNEL
-#include <schnlsp.h>
 #include "curl_sspi.h"
+#include "curl_schannel.h"
 #endif
 
 #ifdef HAVE_NETINET_IN_H
@@ -288,10 +288,8 @@ struct ssl_connect_data {
   SSL*     ssl;
 #endif /* USE_AXTLS */
 #ifdef USE_SCHANNEL
-  bool schannel;
-  TimeStamp time_stamp;
-  CredHandle cred_handle;
-  CtxtHandle ctxt_handle;
+  curl_schannel_cred *cred;
+  curl_schannel_ctxt *ctxt;
   SecPkgContext_StreamSizes stream_sizes;
   ssl_connect_state connecting_state;
   size_t encdata_length, decdata_length;
-- 
1.7.10.msysgit.1


From f109fe511b5c87c6ef655872c37864a88dc37d7b Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Tue, 10 Apr 2012 21:21:31 +0200
Subject: [PATCH 08/18] Implemented SSL/TLS renegotiation

Updated TODO information and added related MSDN articles
---
 lib/curl_schannel.c |   43 +++++++++++++++++++++++++++++++++----------
 1 file changed, 33 insertions(+), 10 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index 9157bda..ab3c611 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -40,7 +40,15 @@
  * TODO list for TLS/SSL implementation:
  * - implement write buffering
  * - implement SSL/TLS shutdown
- * - special cases: renegotiation, certificates, algorithms
+ * - 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 "setup.h"
@@ -86,7 +94,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
   struct in6_addr addr6;
 #endif
 
-  infof(data, "schannel: Connecting to %s:%d (step 1/3)\n",
+  infof(data, "schannel: connecting to %s:%d (step 1/3)\n",
         conn->host.name, conn->remote_port);
 
   /* check for an existing re-usable credential handle */
@@ -229,11 +237,9 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
   SecBufferDesc inbuf_desc;
   SECURITY_STATUS sspi_status = SEC_E_OK;
 
-  infof(data, "schannel: Connecting to %s:%d (step 2/3)\n",
+  infof(data, "schannel: connecting to %s:%d (step 2/3)\n",
         conn->host.name, conn->remote_port);
 
-  connssl->connecting_state = ssl_connect_2;
-
   /* buffer to store previously received and encrypted data */
   if(connssl->encdata_buffer == NULL) {
     connssl->encdata_offset = 0;
@@ -249,13 +255,13 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
   read = sread(conn->sock[sockindex],
                connssl->encdata_buffer + connssl->encdata_offset,
                connssl->encdata_length - connssl->encdata_offset);
-  if(read < 0) {
+  if(read < 0 && connssl->connecting_state != ssl_connect_2_writing) {
     connssl->connecting_state = ssl_connect_2_reading;
     infof(data, "schannel: failed to receive handshake, waiting for more: %d\n",
           read);
     return CURLE_OK;
   }
-  else if(read == 0) {
+  else if(read == 0 && connssl->connecting_state != ssl_connect_2_writing) {
     failf(data, "schannel: failed to receive handshake, connection failed\n");
     return CURLE_SSL_CONNECT_ERROR;
   }
@@ -394,6 +400,9 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) {
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
 
+  infof(data, "schannel: connecting to %s:%d (step 3/3)\n",
+        conn->host.name, conn->remote_port);
+
   /* check if the required context attributes are met */
   if(connssl->ret_flags != connssl->req_flags) {
     if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
@@ -697,15 +706,19 @@ schannel_recv(struct connectdata *conn, int sockindex,
       /* increase encrypted data buffer offset */
       connssl->encdata_offset += read;
     }
+    else if(connssl->encdata_offset == 0) {
+      if(read == 0)
+        ret = 0;
+      else
+        *err = CURLE_AGAIN;
+    }
   }
 
   infof(data, "schannel: encrypted data buffer %d/%d\n",
     connssl->encdata_offset, connssl->encdata_length);
 
   /* check if we still have some data in our buffers */
-  while(connssl->encdata_offset > 0 &&
-        sspi_status != SEC_E_INCOMPLETE_MESSAGE) {
-
+  while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK) {
     /* prepare data buffer for DecryptMessage call */
     inbuf[0].pvBuffer = connssl->encdata_buffer;
     inbuf[0].cbBuffer = connssl->encdata_offset;
@@ -783,9 +796,12 @@ schannel_recv(struct connectdata *conn, int sockindex,
 
       /* begin renegotiation */
       connssl->state = ssl_connection_negotiating;
+      connssl->connecting_state = ssl_connect_2_writing;
       retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
       if(retcode)
         *err = retcode;
+      else /* now retry receiving data */
+        return schannel_recv(conn, sockindex, buf, len, err);
     }
   }
 
@@ -815,6 +831,13 @@ schannel_recv(struct connectdata *conn, int sockindex,
                                       connssl->decdata_length);
   }
 
+  /* check if the server closed the connection */
+  if(ret <= 0 && sspi_status == SEC_I_CONTEXT_EXPIRED) {
+    infof(data, "schannel: server closed the connection\n");
+    *err = CURLE_OK;
+    return 0;
+  }
+
   /* check if something went wrong and we need to return an error */
   if(ret < 0) {
     if(sspi_status == SEC_E_INCOMPLETE_MESSAGE)
-- 
1.7.10.msysgit.1


From 7a2bc3a357fd3c41ddcf1835f0adbdf820e043cf Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Tue, 10 Apr 2012 21:49:35 +0200
Subject: [PATCH 09/18] Added special shutdown check for Windows 2000
 Professional

Windows 2000 Professional:  Schannel returns SEC_E_OK instead
of SEC_I_CONTEXT_EXPIRED. If the length of the output buffer
is zero and the first byte of the encrypted packet is 0x15,
the application can safely assume that the message was a
close_notify message and change the return value to
SEC_I_CONTEXT_EXPIRED.

Connection shutdown does not mean that there is no data to read
Correctly handle incomplete message and ask curl to re-read
Fixed buffer for decrypted being to small
Re-structured read condition to be more effective
Removed obsolete verbose messages
Changed memory reduction method to keep a minimum buffer of size 4096
---
 lib/curl_schannel.c |   88 ++++++++++++++++++++++++++++-----------------------
 1 file changed, 48 insertions(+), 40 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index ab3c611..0ad1145 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -255,20 +255,21 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
   read = sread(conn->sock[sockindex],
                connssl->encdata_buffer + connssl->encdata_offset,
                connssl->encdata_length - connssl->encdata_offset);
-  if(read < 0 && connssl->connecting_state != ssl_connect_2_writing) {
-    connssl->connecting_state = ssl_connect_2_reading;
-    infof(data, "schannel: failed to receive handshake, waiting for more: %d\n",
-          read);
-    return CURLE_OK;
-  }
-  else if(read == 0 && connssl->connecting_state != ssl_connect_2_writing) {
-    failf(data, "schannel: failed to receive handshake, connection failed\n");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-  else if(read > 0) {
+  if(read > 0) {
     /* increase encrypted data buffer offset */
     connssl->encdata_offset += read;
   }
+  else if(connssl->connecting_state != ssl_connect_2_writing) {
+    if(read < 0) {
+      connssl->connecting_state = ssl_connect_2_reading;
+      infof(data, "schannel: failed to receive handshake, need more data\n");
+      return CURLE_OK;
+    }
+    else if(read == 0) {
+      failf(data, "schannel: failed to receive handshake, connection failed\n");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
 
   infof(data, "schannel: encrypted data buffer %d/%d\n",
     connssl->encdata_offset, connssl->encdata_length);
@@ -643,7 +644,6 @@ schannel_send(struct connectdata *conn, int sockindex,
 static ssize_t
 schannel_recv(struct connectdata *conn, int sockindex,
               char *buf, size_t len, CURLcode *err) {
-  int i = 0;
   size_t size = 0;
   ssize_t read = 0, ret = -1;
   CURLcode retcode;
@@ -668,7 +668,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
     }
   }
 
-  /* increase buffers in order to fit the requested amount of data */
+  /* increase buffer in order to fit the requested amount of data */
   while(connssl->encdata_length - connssl->encdata_offset < 2048 ||
         connssl->encdata_length < len) {
     /* increase internal encrypted data buffer */
@@ -680,16 +680,6 @@ schannel_recv(struct connectdata *conn, int sockindex,
       *err = CURLE_OUT_OF_MEMORY;
       return -1;
     }
-
-    /* increase internal decrypted data buffer */
-    connssl->decdata_length += 2048;
-    connssl->decdata_buffer = realloc(connssl->decdata_buffer,
-                                      connssl->decdata_length);
-    if(connssl->decdata_buffer == NULL) {
-      failf(data, "schannel: unable to re-allocate memory");
-      *err = CURLE_OUT_OF_MEMORY;
-      return -1;
-    }
   }
 
   /* read encrypted data from socket */
@@ -744,17 +734,38 @@ schannel_recv(struct connectdata *conn, int sockindex,
     /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
     sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
                                            &inbuf_desc, 0, NULL);
-    infof(data, "schannel: DecryptMessage %d\n", sspi_status);
+
+    /* 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 context) */
-    if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE) {
+    if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
+                                  sspi_status == SEC_I_CONTEXT_EXPIRED) {
       /* check for successfully decrypted data */
       if(inbuf[1].BufferType == SECBUFFER_DATA) {
         infof(data, "schannel: decrypted data length: %d\n", inbuf[1].cbBuffer);
 
+        /* increase buffer in order to fit the received amount of data */
+        size = inbuf[1].cbBuffer > 2048 ? inbuf[1].cbBuffer : 2048;
+        while(connssl->decdata_length - connssl->decdata_offset < size ||
+              connssl->decdata_length < len) {
+          /* increase internal decrypted data buffer */
+          connssl->decdata_length += size;
+          connssl->decdata_buffer = realloc(connssl->decdata_buffer,
+                                            connssl->decdata_length);
+          if(connssl->decdata_buffer == NULL) {
+            failf(data, "schannel: unable to re-allocate memory");
+            *err = CURLE_OUT_OF_MEMORY;
+            return -1;
+          }
+        }
+
         /* copy decrypted data to internal buffer */
-        size = connssl->decdata_length - connssl->decdata_offset;
-        size = size < inbuf[1].cbBuffer ? size : inbuf[1].cbBuffer;
+        size = inbuf[1].cbBuffer;
         if(size > 0) {
           memcpy(connssl->decdata_buffer + connssl->decdata_offset,
                  inbuf[1].pvBuffer, size);
@@ -819,37 +830,34 @@ schannel_recv(struct connectdata *conn, int sockindex,
 
   /* reduce internal buffer length to reduce memory usage */
   if(connssl->encdata_length > 4096) {
-    connssl->encdata_length = connssl->encdata_offset > 0 ?
-                              connssl->encdata_offset + 2048 : 4096;
+    connssl->encdata_length = connssl->encdata_offset > 4096 ?
+                              connssl->encdata_offset : 4096;
     connssl->encdata_buffer = realloc(connssl->encdata_buffer,
                                       connssl->encdata_length);
   }
   if(connssl->decdata_length > 4096) {
-    connssl->decdata_length = connssl->decdata_offset > 0 ?
-                              connssl->decdata_offset + 2048 : 4096;
+    connssl->decdata_length = connssl->decdata_offset > 4096 ?
+                              connssl->decdata_offset : 4096;
     connssl->decdata_buffer = realloc(connssl->decdata_buffer,
                                       connssl->decdata_length);
   }
 
   /* check if the server closed the connection */
-  if(ret <= 0 && sspi_status == SEC_I_CONTEXT_EXPIRED) {
+  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;
   }
 
   /* check if something went wrong and we need to return an error */
-  if(ret < 0) {
-    if(sspi_status == SEC_E_INCOMPLETE_MESSAGE)
-      *err = CURLE_AGAIN;
-    else if(sspi_status != SEC_E_OK)
-      *err = CURLE_RECV_ERROR;
+  if(ret < 0 && sspi_status != SEC_E_OK) {
+    infof(data, "schannel: failed to read data from server\n");
+    *err = CURLE_RECV_ERROR;
     return -1;
   }
 
-  /* everything went fine */
-  infof(data, "schannel: read returns %d with error code %d\n", ret, *err);
-
   return ret;
 }
 
-- 
1.7.10.msysgit.1


From d72c20240f7da7da1e55d45eb886cf2e276e91c3 Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Wed, 11 Apr 2012 17:25:26 +0200
Subject: [PATCH 10/18] Refactored socks_sspi and curl_schannel to use same
 error message functions

Moved the error constant switch to curl_sspi.c and added two new helper
functions to curl_sspi.[ch] which either return the constant or a fully
translated message representing the SSPI security status.
Updated socks_sspi.c and curl_schannel.c to use the new functions.
---
 lib/curl_schannel.c |   37 ++++++++-----
 lib/curl_sspi.c     |  149 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/curl_sspi.h     |    2 +
 lib/socks_sspi.c    |   90 ++-----------------------------
 4 files changed, 180 insertions(+), 98 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index 0ad1145..3de8e7c 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -89,6 +89,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
   SCHANNEL_CRED schannel_cred;
   SECURITY_STATUS sspi_status = SEC_E_OK;
   curl_schannel_cred *old_cred = NULL;
+  char *sspi_msg = NULL;
   struct in_addr addr;
 #ifdef ENABLE_IPV6
   struct in6_addr addr6;
@@ -156,11 +157,14 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
       &connssl->cred->cred_handle, &connssl->cred->time_stamp);
 
     if(sspi_status != SEC_E_OK) {
+      sspi_msg = Curl_sspi_status_msg(sspi_status);
       if(sspi_status == SEC_E_WRONG_PRINCIPAL)
-        failf(data, "schannel: SNI or certificate check failed\n");
+        failf(data, "schannel: SNI or certificate check failed: %s\n",
+              sspi_msg);
       else
-        failf(data, "schannel: AcquireCredentialsHandleA failed: %d\n",
-              sspi_status);
+        failf(data, "schannel: AcquireCredentialsHandleA failed: %s\n",
+              sspi_msg);
+      free(sspi_msg);
       free(connssl->cred);
       connssl->cred = NULL;
       return CURLE_SSL_CONNECT_ERROR;
@@ -196,11 +200,14 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
     &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
 
   if(sspi_status != SEC_I_CONTINUE_NEEDED) {
+    sspi_msg = Curl_sspi_status_msg(sspi_status);
     if(sspi_status == SEC_E_WRONG_PRINCIPAL)
-      failf(data, "schannel: SNI or certificate check failed\n");
+      failf(data, "schannel: SNI or certificate check failed: %s\n",
+            sspi_msg);
     else
-      failf(data, "schannel: initial InitializeSecurityContextA failed: %d\n",
-            sspi_status);
+      failf(data, "schannel: initial InitializeSecurityContextA failed: %s\n",
+            sspi_msg);
+    free(sspi_msg);
     free(connssl->ctxt);
     connssl->ctxt = NULL;
     return CURLE_SSL_CONNECT_ERROR;
@@ -236,6 +243,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
   SecBuffer inbuf[2];
   SecBufferDesc inbuf_desc;
   SECURITY_STATUS sspi_status = SEC_E_OK;
+  char *sspi_msg = NULL;
 
   infof(data, "schannel: connecting to %s:%d (step 2/3)\n",
         conn->host.name, conn->remote_port);
@@ -320,8 +328,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
   /* 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: %d\n",
-          sspi_status);
+    infof(data, "schannel: received incomplete message, need more data\n");
     return CURLE_OK;
   }
 
@@ -350,11 +357,14 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
     }
   }
   else {
+    sspi_msg = Curl_sspi_status_msg(sspi_status);
     if(sspi_status == SEC_E_WRONG_PRINCIPAL)
-      failf(data, "schannel: SNI or certificate check failed\n");
+      failf(data, "schannel: SNI or certificate check failed: %s\n",
+            sspi_msg);
     else
-      failf(data, "schannel: next InitializeSecurityContextA failed: %d\n",
-            sspi_status);
+      failf(data, "schannel: next InitializeSecurityContextA failed: %s\n",
+            sspi_msg);
+    free(sspi_msg);
     return CURLE_SSL_CONNECT_ERROR;
   }
 
@@ -653,6 +663,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
   SecBuffer inbuf[4];
   SecBufferDesc inbuf_desc;
   SECURITY_STATUS sspi_status = SEC_E_OK;
+  char *sspi_msg = NULL;
 
   infof(data, "schannel: client wants to read %d\n", len);
   *err = CURLE_OK;
@@ -853,7 +864,9 @@ schannel_recv(struct connectdata *conn, int sockindex,
 
   /* 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\n");
+    sspi_msg = Curl_sspi_status_msg(sspi_status);
+    infof(data, "schannel: failed to read data from server: %s\n", sspi_msg);
+    free(sspi_msg);
     *err = CURLE_RECV_ERROR;
     return -1;
   }
diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c
index b985dbc..d915710 100644
--- a/lib/curl_sspi.c
+++ b/lib/curl_sspi.c
@@ -118,4 +118,153 @@ Curl_sspi_global_cleanup(void)
   }
 }
 
+
+/*
+ * Curl_sspi_status(SECURIY_STATUS status)
+ *
+ * This function returns a string representing an SSPI status.
+ * It will in any case return a usable string pointer which needs to be freed.
+ */
+char*
+Curl_sspi_status(SECURITY_STATUS status)
+{
+  const char* status_const;
+
+  switch(status) {
+    case SEC_I_COMPLETE_AND_CONTINUE:
+      status_const = "SEC_I_COMPLETE_AND_CONTINUE";
+      break;
+    case SEC_I_COMPLETE_NEEDED:
+      status_const = "SEC_I_COMPLETE_NEEDED";
+      break;
+    case SEC_I_CONTINUE_NEEDED:
+      status_const = "SEC_I_CONTINUE_NEEDED";
+      break;
+    case SEC_I_CONTEXT_EXPIRED:
+      status_const = "SEC_I_CONTEXT_EXPIRED";
+      break;
+    case SEC_I_INCOMPLETE_CREDENTIALS:
+      status_const = "SEC_I_INCOMPLETE_CREDENTIALS";
+      break;
+    case SEC_I_RENEGOTIATE:
+      status_const = "SEC_I_RENEGOTIATE";
+      break;
+    case SEC_E_BUFFER_TOO_SMALL:
+      status_const = "SEC_E_BUFFER_TOO_SMALL";
+      break;
+    case SEC_E_CONTEXT_EXPIRED:
+      status_const = "SEC_E_CONTEXT_EXPIRED";
+      break;
+    case SEC_E_CRYPTO_SYSTEM_INVALID:
+      status_const = "SEC_E_CRYPTO_SYSTEM_INVALID";
+      break;
+    case SEC_E_INCOMPLETE_MESSAGE:
+      status_const = "SEC_E_INCOMPLETE_MESSAGE";
+      break;
+    case SEC_E_INSUFFICIENT_MEMORY:
+      status_const = "SEC_E_INSUFFICIENT_MEMORY";
+      break;
+    case SEC_E_INTERNAL_ERROR:
+      status_const = "SEC_E_INTERNAL_ERROR";
+      break;
+    case SEC_E_INVALID_HANDLE:
+      status_const = "SEC_E_INVALID_HANDLE";
+      break;
+    case SEC_E_INVALID_TOKEN:
+      status_const = "SEC_E_INVALID_TOKEN";
+      break;
+    case SEC_E_LOGON_DENIED:
+      status_const = "SEC_E_LOGON_DENIED";
+      break;
+    case SEC_E_MESSAGE_ALTERED:
+      status_const = "SEC_E_MESSAGE_ALTERED";
+      break;
+    case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+      status_const = "SEC_E_NO_AUTHENTICATING_AUTHORITY";
+      break;
+    case SEC_E_NO_CREDENTIALS:
+      status_const = "SEC_E_NO_CREDENTIALS";
+      break;
+    case SEC_E_NOT_OWNER:
+      status_const = "SEC_E_NOT_OWNER";
+      break;
+    case SEC_E_OK:
+      status_const = "SEC_E_OK";
+      break;
+    case SEC_E_OUT_OF_SEQUENCE:
+      status_const = "SEC_E_OUT_OF_SEQUENCE";
+      break;
+    case SEC_E_QOP_NOT_SUPPORTED:
+      status_const = "SEC_E_QOP_NOT_SUPPORTED";
+      break;
+    case SEC_E_SECPKG_NOT_FOUND:
+      status_const = "SEC_E_SECPKG_NOT_FOUND";
+      break;
+    case SEC_E_TARGET_UNKNOWN:
+      status_const = "SEC_E_TARGET_UNKNOWN";
+      break;
+    case SEC_E_UNKNOWN_CREDENTIALS:
+      status_const = "SEC_E_UNKNOWN_CREDENTIALS";
+      break;
+    case SEC_E_UNSUPPORTED_FUNCTION:
+      status_const = "SEC_E_UNSUPPORTED_FUNCTION";
+      break;
+    case SEC_E_WRONG_PRINCIPAL:
+      status_const = "SEC_E_WRONG_PRINCIPAL";
+      break;
+    default:
+      status_const = "Unknown error";
+  }
+
+  return curl_maprintf("%s (0x%08X)", status_const, status);
+}
+
+/*
+ * Curl_sspi_status_msg(SECURITY_STATUS status)
+ *
+ * This function returns a message representing an SSPI status.
+ * It will in any case return a usable string pointer which needs to be freed.
+ */
+
+char*
+Curl_sspi_status_msg(SECURITY_STATUS status)
+{
+  LPSTR format_msg = NULL;
+  char *status_msg = NULL, *status_const = NULL;
+  int status_len = 0;
+
+  status_len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                             FORMAT_MESSAGE_FROM_SYSTEM |
+                             FORMAT_MESSAGE_IGNORE_INSERTS,
+                             NULL, status, 0, (LPTSTR)&format_msg, 0, NULL);
+
+  if(status_len > 0 && format_msg) {
+    status_msg = strdup(format_msg);
+    LocalFree(format_msg);
+
+    /* remove trailing CR+LF */
+    if(status_len > 0) {
+      if(status_msg[status_len-1] == '\n') {
+        status_msg[status_len-1] = '\0';
+        if(status_len > 1) {
+          if(status_msg[status_len-2] == '\r') {
+            status_msg[status_len-2] = '\0';
+          }
+        }
+      }
+    }
+  }
+
+  status_const = Curl_sspi_status(status);
+  if(status_msg) {
+    status_msg = curl_maprintf("%s [%s]", status_msg, status_const);
+    free(status_const);
+  }
+  else {
+    status_msg = status_const;
+  }
+
+  return status_msg;
+}
+
 #endif /* USE_WINDOWS_SSPI */
diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h
index c0e4f36..8174878 100644
--- a/lib/curl_sspi.h
+++ b/lib/curl_sspi.h
@@ -63,6 +63,8 @@
 
 CURLcode Curl_sspi_global_init(void);
 void Curl_sspi_global_cleanup(void);
+char* Curl_sspi_status(SECURITY_STATUS status);
+char* Curl_sspi_status_msg(SECURITY_STATUS status);
 
 /* Forward-declaration of global variables defined in curl_sspi.c */
 
diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c
index d96a82e..1e724bb 100644
--- a/lib/socks_sspi.c
+++ b/lib/socks_sspi.c
@@ -53,98 +53,16 @@ static int check_sspi_err(struct SessionHandle *data,
                           SECURITY_STATUS minor_status,
                           const char* function)
 {
-  const char *txt;
+  char *sspi_msg = NULL;
   (void)minor_status;
 
   if(major_status != SEC_E_OK &&
      major_status != SEC_I_COMPLETE_AND_CONTINUE &&
      major_status != SEC_I_COMPLETE_NEEDED &&
      major_status != SEC_I_CONTINUE_NEEDED) {
-    failf(data, "SSPI error: %s failed: %d\n", function, major_status);
-    switch (major_status) {
-    case SEC_I_COMPLETE_AND_CONTINUE:
-      txt="SEC_I_COMPLETE_AND_CONTINUE";
-      break;
-    case SEC_I_COMPLETE_NEEDED:
-      txt="SEC_I_COMPLETE_NEEDED";
-      break;
-    case SEC_I_CONTINUE_NEEDED:
-      txt="SEC_I_CONTINUE_NEEDED";
-      break;
-    case SEC_I_CONTEXT_EXPIRED:
-      txt="SEC_I_CONTEXT_EXPIRED";
-      break;
-    case SEC_I_INCOMPLETE_CREDENTIALS:
-      txt="SEC_I_INCOMPLETE_CREDENTIALS";
-      break;
-    case SEC_I_RENEGOTIATE:
-      txt="SEC_I_RENEGOTIATE";
-      break;
-    case SEC_E_BUFFER_TOO_SMALL:
-      txt="SEC_E_BUFFER_TOO_SMALL";
-      break;
-    case SEC_E_CONTEXT_EXPIRED:
-      txt="SEC_E_CONTEXT_EXPIRED";
-      break;
-    case SEC_E_CRYPTO_SYSTEM_INVALID:
-      txt="SEC_E_CRYPTO_SYSTEM_INVALID";
-      break;
-    case SEC_E_INCOMPLETE_MESSAGE:
-      txt="SEC_E_INCOMPLETE_MESSAGE";
-      break;
-    case SEC_E_INSUFFICIENT_MEMORY:
-      txt="SEC_E_INSUFFICIENT_MEMORY";
-      break;
-    case SEC_E_INTERNAL_ERROR:
-      txt="SEC_E_INTERNAL_ERROR";
-      break;
-    case SEC_E_INVALID_HANDLE:
-      txt="SEC_E_INVALID_HANDLE";
-      break;
-    case SEC_E_INVALID_TOKEN:
-      txt="SEC_E_INVALID_TOKEN";
-      break;
-    case SEC_E_LOGON_DENIED:
-      txt="SEC_E_LOGON_DENIED";
-      break;
-    case SEC_E_MESSAGE_ALTERED:
-      txt="SEC_E_MESSAGE_ALTERED";
-      break;
-    case SEC_E_NO_AUTHENTICATING_AUTHORITY:
-      txt="SEC_E_NO_AUTHENTICATING_AUTHORITY";
-      break;
-    case SEC_E_NO_CREDENTIALS:
-      txt="SEC_E_NO_CREDENTIALS";
-      break;
-    case SEC_E_NOT_OWNER:
-      txt="SEC_E_NOT_OWNER";
-      break;
-    case SEC_E_OUT_OF_SEQUENCE:
-      txt="SEC_E_OUT_OF_SEQUENCE";
-      break;
-    case SEC_E_QOP_NOT_SUPPORTED:
-      txt="SEC_E_QOP_NOT_SUPPORTED";
-      break;
-    case SEC_E_SECPKG_NOT_FOUND:
-      txt="SEC_E_SECPKG_NOT_FOUND";
-      break;
-    case SEC_E_TARGET_UNKNOWN:
-      txt="SEC_E_TARGET_UNKNOWN";
-      break;
-    case SEC_E_UNKNOWN_CREDENTIALS:
-      txt="SEC_E_UNKNOWN_CREDENTIALS";
-      break;
-    case SEC_E_UNSUPPORTED_FUNCTION:
-      txt="SEC_E_UNSUPPORTED_FUNCTION";
-      break;
-    case SEC_E_WRONG_PRINCIPAL:
-      txt="SEC_E_WRONG_PRINCIPAL";
-      break;
-    default:
-      txt="Unknown error";
-
-    }
-    failf(data, "SSPI error: %s failed: %s\n", function, txt);
+    sspi_msg = Curl_sspi_status_msg(major_status);
+    failf(data, "SSPI error: %s failed: %s\n", function, sspi_msg);
+    free(sspi_msg);
     return 1;
   }
   return 0;
-- 
1.7.10.msysgit.1


From a65bf363f4881a2d0104ea1282c96da3bdf10e15 Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Fri, 13 Apr 2012 13:02:59 +0200
Subject: [PATCH 11/18] Fixed critical typo in conditions and added buffer
 length checks

---
 lib/curl_schannel.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index 3de8e7c..6bc7682 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -336,7 +336,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
   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) {
+      if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
         infof(data, "schannel: sending next handshake data: %d ...\n",
               outbuf[i].cbBuffer);
 
@@ -369,7 +369,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
   }
 
   /* check if there was additional remaining encrypted data */
-  if(inbuf[1].BufferType = SECBUFFER_EXTRA) {
+  if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
     infof(data, "schannel: encrypted data length: %d\n", inbuf[1].cbBuffer);
 
     /* check if the remaining data is less than the total amount
@@ -789,7 +789,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
       }
 
       /* check for remaining encrypted data */
-      if(inbuf[3].BufferType = SECBUFFER_EXTRA) {
+      if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
         infof(data, "schannel: encrypted data length: %d\n", inbuf[3].cbBuffer);
 
         /* check if the remaining data is less than the total amount
-- 
1.7.10.msysgit.1


From d78ecb66953e08c8048735073b98ccca5cf8879a Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Fri, 13 Apr 2012 13:05:26 +0200
Subject: [PATCH 12/18] Fixed compiler warnings about pointer type assignments

---
 lib/curl_schannel.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index 6bc7682..d4e3559 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -99,7 +99,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
         conn->host.name, conn->remote_port);
 
   /* check for an existing re-usable credential handle */
-  if(!Curl_ssl_getsessionid(conn, &old_cred, NULL)) {
+  if(!Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL)) {
     connssl->cred = old_cred;
     infof(data, "schannel: re-using existing credential handle\n");
   }
@@ -432,16 +432,16 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) {
   }
 
   /* save the current session data for possible re-use */
-  incache = !(Curl_ssl_getsessionid(conn, &old_cred, NULL));
+  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");
-      Curl_ssl_delsessionid(conn, old_cred);
+      Curl_ssl_delsessionid(conn, (void*)old_cred);
       incache = FALSE;
     }
   }
   if(!incache) {
-    retcode = Curl_ssl_addsessionid(conn, connssl->cred,
+    retcode = Curl_ssl_addsessionid(conn, (void*)connssl->cred,
                                     sizeof(curl_schannel_cred));
     if(retcode) {
       failf(data, "schannel: failed to store credential handle\n");
-- 
1.7.10.msysgit.1


From a1e4cd08baedcd84ac44028008fea89803737c02 Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Fri, 13 Apr 2012 13:04:53 +0200
Subject: [PATCH 13/18] Moved interal struct types to urldata.h in order to
 avoid inclusion loop

---
 lib/curl_schannel.c |    1 -
 lib/curl_schannel.h |   10 +---------
 lib/urldata.h       |   16 +++++++++++++++-
 3 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index d4e3559..d4d7629 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -56,7 +56,6 @@
 #ifdef USE_WINDOWS_SSPI
 #ifdef USE_SCHANNEL
 
-#include "urldata.h"
 #include "curl_sspi.h"
 #include "curl_schannel.h"
 #include "sslgen.h"
diff --git a/lib/curl_schannel.h b/lib/curl_schannel.h
index fa6fc90..aea47b2 100644
--- a/lib/curl_schannel.h
+++ b/lib/curl_schannel.h
@@ -26,21 +26,13 @@
 #ifdef USE_WINDOWS_SSPI
 #ifdef USE_SCHANNEL
 
-#include <schnlsp.h>
+#include "urldata.h"
 
 #ifndef UNISP_NAME_A
 #define UNISP_NAME_A "Microsoft Unified Security Protocol Provider"
 #endif
 
-typedef struct curl_schannel_cred {
-  CredHandle cred_handle;
-  TimeStamp time_stamp;
-} curl_schannel_cred;
 
-typedef struct curl_schannel_ctxt {
-  CtxtHandle ctxt_handle;
-  TimeStamp time_stamp;
-} curl_schannel_ctxt;
 
 CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex);
 
diff --git a/lib/urldata.h b/lib/urldata.h
index 2b972d5..7327482 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -133,7 +133,8 @@
 
 #ifdef USE_SCHANNEL
 #include "curl_sspi.h"
-#include "curl_schannel.h"
+#include <schnlsp.h>
+#include <schannel.h>
 #endif
 
 #ifdef HAVE_NETINET_IN_H
@@ -219,6 +220,19 @@ enum protection_level {
 };
 #endif
 
+#ifdef USE_SCHANNEL
+/* Structs to store Schannel handles */
+typedef struct curl_schannel_cred {
+  CredHandle cred_handle;
+  TimeStamp time_stamp;
+} curl_schannel_cred;
+
+typedef struct curl_schannel_ctxt {
+  CtxtHandle ctxt_handle;
+  TimeStamp time_stamp;
+} curl_schannel_ctxt;
+#endif
+
 /* enum for the nonblocking SSL connection state machine */
 typedef enum {
   ssl_connect_1,
-- 
1.7.10.msysgit.1


From 12331597e7eefac484dc6f80cf3dde34514b64f8 Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Fri, 13 Apr 2012 13:09:24 +0200
Subject: [PATCH 14/18] Added definitions which are missing in mingw32

---
 lib/curl_schannel.h |   59 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/lib/curl_schannel.h b/lib/curl_schannel.h
index aea47b2..3e24fad 100644
--- a/lib/curl_schannel.h
+++ b/lib/curl_schannel.h
@@ -32,6 +32,65 @@
 #define UNISP_NAME_A "Microsoft Unified Security Protocol Provider"
 #endif
 
+#ifndef UNISP_NAME_W
+#define UNISP_NAME_W L"Microsoft Unified Security Protocol Provider"
+#endif
+
+#ifndef UNISP_NAME
+#ifdef UNICODE
+#define UNISP_NAME  UNISP_NAME_W
+#else
+#define UNISP_NAME  UNISP_NAME_A
+#endif
+#endif
+
+#ifndef SP_PROT_SSL2_CLIENT
+#define SP_PROT_SSL2_CLIENT             0x00000008
+#endif
+
+#ifndef SP_PROT_SSL3_CLIENT
+#define SP_PROT_SSL3_CLIENT             0x00000008
+#endif
+
+#ifndef SP_PROT_TLS1_CLIENT
+#define SP_PROT_TLS1_CLIENT             0x00000080
+#endif
+
+#ifndef SP_PROT_TLS1_0_CLIENT
+#define SP_PROT_TLS1_0_CLIENT           SP_PROT_TLS1_CLIENT
+#endif
+
+#ifndef SP_PROT_TLS1_1_CLIENT
+#define SP_PROT_TLS1_1_CLIENT           0x00000200
+#endif
+
+#ifndef SP_PROT_TLS1_2_CLIENT
+#define SP_PROT_TLS1_2_CLIENT           0x00000800
+#endif
+
+#ifndef SECBUFFER_ALERT
+#define SECBUFFER_ALERT                 17
+#endif
+
+#ifndef ISC_RET_REPLAY_DETECT
+#define ISC_RET_REPLAY_DETECT           0x00000004
+#endif
+
+#ifndef ISC_RET_SEQUENCE_DETECT
+#define ISC_RET_SEQUENCE_DETECT         0x00000008
+#endif
+
+#ifndef ISC_RET_CONFIDENTIALITY
+#define ISC_RET_CONFIDENTIALITY         0x00000010
+#endif
+
+#ifndef ISC_RET_ALLOCATED_MEMORY
+#define ISC_RET_ALLOCATED_MEMORY        0x00000100
+#endif
+
+#ifndef ISC_RET_STREAM
+#define ISC_RET_STREAM                  0x00008000
+#endif
 
 
 CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex);
-- 
1.7.10.msysgit.1


From 523549b6702f530d58347d06323626a0e19b87b6 Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Fri, 13 Apr 2012 13:10:09 +0200
Subject: [PATCH 15/18] Replace ASCII specific code with general defines

---
 lib/curl_schannel.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index d4d7629..c9a3906 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -151,8 +151,8 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
     memset(connssl->cred, 0, sizeof(curl_schannel_cred));
 
     /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
-    sspi_status = s_pSecFn->AcquireCredentialsHandleA(NULL,
-      UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL,
+    sspi_status = s_pSecFn->AcquireCredentialsHandle(NULL,
+      UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL,
       &connssl->cred->cred_handle, &connssl->cred->time_stamp);
 
     if(sspi_status != SEC_E_OK) {
@@ -193,7 +193,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
   memset(connssl->ctxt, 0, sizeof(curl_schannel_ctxt));
 
   /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
-  sspi_status = s_pSecFn->InitializeSecurityContextA(
+  sspi_status = s_pSecFn->InitializeSecurityContext(
     &connssl->cred->cred_handle, NULL, conn->host.name,
     connssl->req_flags, 0, 0, NULL, 0, &connssl->ctxt->ctxt_handle,
     &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
@@ -316,7 +316,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
   memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer, connssl->encdata_offset);
 
   /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
-  sspi_status = s_pSecFn->InitializeSecurityContextA(
+  sspi_status = s_pSecFn->InitializeSecurityContext(
     &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
     conn->host.name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
     &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
@@ -579,7 +579,7 @@ schannel_send(struct connectdata *conn, int sockindex,
 
   /* check if the maximum stream sizes were queried */
   if(connssl->stream_sizes.cbMaximumMessage == 0) {
-    sspi_status = s_pSecFn->QueryContextAttributesA(
+    sspi_status = s_pSecFn->QueryContextAttributes(
                               &connssl->ctxt->ctxt_handle,
                               SECPKG_ATTR_STREAM_SIZES, &connssl->stream_sizes);
     if(sspi_status != SEC_E_OK) {
-- 
1.7.10.msysgit.1


From 01406c1a1cff95a8193ad75f62a00399ec1855a2 Mon Sep 17 00:00:00 2001
From: Guenter Knauf <lists@gknw.net>
Date: Fri, 13 Apr 2012 13:17:57 +0200
Subject: [PATCH 16/18] Updated mingw32 makefiles

---
 lib/Makefile.m32 |    9 +++++++++
 src/Makefile.m32 |    9 +++++++++
 2 files changed, 18 insertions(+)

diff --git a/lib/Makefile.m32 b/lib/Makefile.m32
index 988427e..abbcca3 100644
--- a/lib/Makefile.m32
+++ b/lib/Makefile.m32
@@ -115,6 +115,11 @@ endif
 ifeq ($(findstring -ipv6,$(CFG)),-ipv6)
 IPV6 = 1
 endif
+ifeq ($(findstring -schannel,$(CFG)),-schannel)
+SCHANNEL = 1
+SSPI = 1
+SSL = 1
+endif
 
 INCLUDES = -I. -I../include
 CFLAGS += -DBUILDING_LIBCURL
@@ -136,6 +141,9 @@ ifdef SSH2
   DLL_LIBS += -L"$(LIBSSH2_PATH)/win32" -lssh2
 endif
 ifdef SSL
+ifdef SCHANNEL
+  CFLAGS += -DUSE_SSL -DUSE_SCHANNEL
+else
   ifndef OPENSSL_INCLUDE
     ifeq "$(wildcard $(OPENSSL_PATH)/outinc)" "$(OPENSSL_PATH)/outinc"
       OPENSSL_INCLUDE = $(OPENSSL_PATH)/outinc
@@ -163,6 +171,7 @@ ifdef SSL
             -DCURL_WANTS_CA_BUNDLE_ENV
   DLL_LIBS += -L"$(OPENSSL_LIBPATH)" $(OPENSSL_LIBS)
 endif
+endif
 ifdef ZLIB
   INCLUDES += -I"$(ZLIB_PATH)"
   CFLAGS += -DHAVE_LIBZ -DHAVE_ZLIB_H
diff --git a/src/Makefile.m32 b/src/Makefile.m32
index decfaa2..5e3dc62 100644
--- a/src/Makefile.m32
+++ b/src/Makefile.m32
@@ -116,6 +116,11 @@ endif
 ifeq ($(findstring -ipv6,$(CFG)),-ipv6)
 IPV6 = 1
 endif
+ifeq ($(findstring -schannel,$(CFG)),-schannel)
+SCHANNEL = 1
+SSPI = 1
+SSL = 1
+endif
 
 INCLUDES = -I. -I.. -I../include -I../lib
 
@@ -144,6 +149,9 @@ ifdef SSH2
   curl_LDADD += -L"$(LIBSSH2_PATH)/win32" -lssh2
 endif
 ifdef SSL
+ifdef SCHANNEL
+  CFLAGS += -DUSE_SSL -DUSE_SCHANNEL
+else
   ifndef OPENSSL_LIBPATH
     OPENSSL_LIBS = -lssl -lcrypto
     ifeq "$(wildcard $(OPENSSL_PATH)/out)" "$(OPENSSL_PATH)/out"
@@ -162,6 +170,7 @@ ifdef SSL
   CFLAGS += -DUSE_SSLEAY
   curl_LDADD += -L"$(OPENSSL_LIBPATH)" $(OPENSSL_LIBS)
 endif
+endif
 ifdef ZLIB
   INCLUDES += -I"$(ZLIB_PATH)"
   CFLAGS += -DHAVE_LIBZ -DHAVE_ZLIB_H
-- 
1.7.10.msysgit.1


From 36c109120ee7c6d2f3091c4a1b0bb79485a8e1ab Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Sat, 14 Apr 2012 15:00:33 +0200
Subject: [PATCH 17/18] curl_sspi: Added Curl_sspi_version function

Added new function to get SSPI version as string.
Added required library version.lib to makefiles.
Changed curl_schannel.c to use Curl_sspi_version.
---
 lib/Makefile.m32          |    1 +
 lib/curl_schannel.c       |    8 ++++----
 lib/curl_sspi.c           |   50 +++++++++++++++++++++++++++++++++++++++++++++
 lib/curl_sspi.h           |    1 +
 src/Makefile.m32          |    1 +
 winbuild/MakefileBuild.vc |    2 ++
 6 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/lib/Makefile.m32 b/lib/Makefile.m32
index abbcca3..11f6d71 100644
--- a/lib/Makefile.m32
+++ b/lib/Makefile.m32
@@ -190,6 +190,7 @@ endif
 endif
 ifdef SSPI
   CFLAGS += -DUSE_WINDOWS_SSPI
+  DLL_LIBS += -lversion
 endif
 ifdef SPNEGO
   CFLAGS += -DHAVE_SPNEGO
diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index c9a3906..c7b51cb 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -957,10 +957,10 @@ void Curl_schannel_cleanup() {
 
 size_t Curl_schannel_version(char *buffer, size_t size)
 {
-  unsigned long version = s_pSecFn ? s_pSecFn->dwVersion : 0;
-  return snprintf(buffer, size, "Schannel/%d.%d.%d.%d",
-                  (version>>0)&0xff, (version>>8)&0xff,
-                  (version>>16)&0xff, (version>>24)&0xff);
+  char* version = Curl_sspi_version();
+  size = snprintf(buffer, size, "Schannel-%s", version);
+  free(version);
+  return size;
 }
 
 #endif /* USE_SCHANNEL */
diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c
index d915710..d3533a2 100644
--- a/lib/curl_sspi.c
+++ b/lib/curl_sspi.c
@@ -120,6 +120,55 @@ Curl_sspi_global_cleanup(void)
 
 
 /*
+ * Curl_sspi_version()
+ *
+ * This function returns a string representing the SSPI library version.
+ * It will in any case return a usable string pointer which needs to be freed.
+ */
+char *
+Curl_sspi_version()
+{
+  VS_FIXEDFILEINFO *version_info = NULL;
+  LPTSTR version = NULL;
+  LPTSTR path = NULL;
+  LPVOID data = NULL;
+  DWORD size, handle;
+
+  if(s_hSecDll) {
+    path = malloc(MAX_PATH);
+    if(path) {
+      if(GetModuleFileName(s_hSecDll, path, MAX_PATH)) {
+        size = GetFileVersionInfoSize(path, &handle);
+        if(size) {
+          data = malloc(size);
+          if(data) {
+            if(GetFileVersionInfo(path, handle, size, data)) {
+              if(VerQueryValue(data, "\\", &version_info, &handle)) {
+                version = curl_maprintf("SSPI/%d.%d.%d.%d",
+                  (version_info->dwProductVersionMS>>16)&0xffff,
+                  (version_info->dwProductVersionMS>>0)&0xffff,
+                  (version_info->dwProductVersionLS>>16)&0xffff,
+                  (version_info->dwProductVersionLS>>0)&0xffff);
+              }
+            }
+            free(data);
+          }
+        }
+      }
+      free(path);
+    }
+    if(!version)
+      version = strdup("SSPI/Unknown");
+  }
+
+  if(!version)
+    version = strdup("");
+
+  return version;
+}
+
+
+/*
  * Curl_sspi_status(SECURIY_STATUS status)
  *
  * This function returns a string representing an SSPI status.
@@ -219,6 +268,7 @@ Curl_sspi_status(SECURITY_STATUS status)
   return curl_maprintf("%s (0x%08X)", status_const, status);
 }
 
+
 /*
  * Curl_sspi_status_msg(SECURITY_STATUS status)
  *
diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h
index 8174878..1865ee4 100644
--- a/lib/curl_sspi.h
+++ b/lib/curl_sspi.h
@@ -63,6 +63,7 @@
 
 CURLcode Curl_sspi_global_init(void);
 void Curl_sspi_global_cleanup(void);
+char* Curl_sspi_version();
 char* Curl_sspi_status(SECURITY_STATUS status);
 char* Curl_sspi_status_msg(SECURITY_STATUS status);
 
diff --git a/src/Makefile.m32 b/src/Makefile.m32
index 5e3dc62..543accb 100644
--- a/src/Makefile.m32
+++ b/src/Makefile.m32
@@ -187,6 +187,7 @@ endif
 endif
 ifdef SSPI
   CFLAGS += -DUSE_WINDOWS_SSPI
+  curl_LDADD += -lversion
 endif
 ifdef SPNEGO
   CFLAGS += -DHAVE_SPNEGO
diff --git a/winbuild/MakefileBuild.vc b/winbuild/MakefileBuild.vc
index e026135..45834e4 100644
--- a/winbuild/MakefileBuild.vc
+++ b/winbuild/MakefileBuild.vc
@@ -148,6 +148,7 @@ USE_SSPI=yes
 
 !IF "$(USE_SSPI)"=="yes"
 CFLAGS_SSPI = /DUSE_WINDOWS_SSPI
+LFLAGS_SSPI = version.lib
 USE_SSPI=true
 !ENDIF
 
@@ -286,6 +287,7 @@ CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-ipv6
 
 !IF "$(USE_SSPI)"=="true"
 CFLAGS = $(CFLAGS) $(CFLAGS_SSPI)
+LFLAGS = $(LFLAGS) $(LFLAGS_SSPI)
 CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-sspi
 !ENDIF
 
-- 
1.7.10.msysgit.1


From 5eb51d01d9b4f5c02275633a4450de970e86c03f Mon Sep 17 00:00:00 2001
From: Marc Hoersken <info@marc-hoersken.de>
Date: Sun, 15 Apr 2012 04:12:26 +0200
Subject: [PATCH 18/18] Code cleanup and bug fixes

curl_sspi.c: Fixed mingw32-gcc compiler warnings
curl_sspi.c: Fixed length of error code hex output

The hex value was printed as signed 64-bit value on 64-bit systems:
SEC_E_WRONG_PRINCIPAL (0xFFFFFFFF80090322)

It is now correctly printed as the following:
SEC_E_WRONG_PRINCIPAL (0x80090322)

curl_sspi.c: Fallback to security function table version number
Instead of reporting an unknown version, the interface version is used.

curl_sspi.c: Removed SSPI/ version prefix from Curl_sspi_version
curl_schannel: Replaced static buffer sizes with defined names
curl_schannel.c: First brace when declaring functions on column 0
curl_schannel.c: Put the pointer sign directly at variable name
curl_schannel.c: Use structs directly instead of typedef'ed structs
curl_schannel.c: Removed space before opening brace
curl_schannel.c: Fixed lines being longer than 80 chars
---
 lib/curl_schannel.c |  101 ++++++++++++++++++++++++++++++---------------------
 lib/curl_schannel.h |    4 ++
 lib/curl_sspi.c     |   10 +++--
 lib/urldata.h       |   12 +++---
 4 files changed, 75 insertions(+), 52 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index c7b51cb..7bca903 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -79,15 +79,16 @@ static Curl_recv schannel_recv;
 static Curl_send schannel_send;
 
 static CURLcode
-schannel_connect_step1(struct connectdata *conn, int sockindex) {
+schannel_connect_step1(struct connectdata *conn, int sockindex)
+{
   ssize_t write = -1;
   struct SessionHandle *data = conn->data;
-  struct ssl_connect_data* connssl = &conn->ssl[sockindex];
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   SecBuffer outbuf;
   SecBufferDesc outbuf_desc;
   SCHANNEL_CRED schannel_cred;
   SECURITY_STATUS sspi_status = SEC_E_OK;
-  curl_schannel_cred *old_cred = NULL;
+  struct curl_schannel_cred *old_cred = NULL;
   char *sspi_msg = NULL;
   struct in_addr addr;
 #ifdef ENABLE_IPV6
@@ -110,13 +111,13 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
     if(data->set.ssl.verifypeer) {
       schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
                               SCH_CRED_REVOCATION_CHECK_CHAIN;
-      infof(data, "schannel: checking server certificate and 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: disable server certificate and revocation checks\n");
+      infof(data, "schannel: disable server certificate revocation checks\n");
     }
 
     if(Curl_inet_pton(AF_INET, conn->host.name, &addr) ||
@@ -143,12 +144,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
     }
 
     /* allocate memory for the re-usable credential handle */
-    connssl->cred = malloc(sizeof(curl_schannel_cred));
-    if (!connssl->cred) {
+    connssl->cred = malloc(sizeof(struct curl_schannel_cred));
+    if(!connssl->cred) {
       failf(data, "schannel: unable to allocate memory");
       return CURLE_OUT_OF_MEMORY;
     }
-    memset(connssl->cred, 0, sizeof(curl_schannel_cred));
+    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,
@@ -185,12 +186,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
                        ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
 
   /* allocate memory for the security context handle */
-  connssl->ctxt = malloc(sizeof(curl_schannel_ctxt));
-  if (!connssl->ctxt) {
+  connssl->ctxt = malloc(sizeof(struct curl_schannel_ctxt));
+  if(!connssl->ctxt) {
     failf(data, "schannel: unable to allocate memory");
     return CURLE_OUT_OF_MEMORY;
   }
-  memset(connssl->ctxt, 0, sizeof(curl_schannel_ctxt));
+  memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt));
 
   /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
   sspi_status = s_pSecFn->InitializeSecurityContext(
@@ -232,11 +233,12 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
 }
 
 static CURLcode
-schannel_connect_step2(struct connectdata *conn, int sockindex) {
+schannel_connect_step2(struct connectdata *conn, int sockindex)
+{
   int i;
   ssize_t read = -1, write = -1;
   struct SessionHandle *data = conn->data;
-  struct ssl_connect_data* connssl = &conn->ssl[sockindex];
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   SecBuffer outbuf[2];
   SecBufferDesc outbuf_desc;
   SecBuffer inbuf[2];
@@ -250,7 +252,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
   /* buffer to store previously received and encrypted data */
   if(connssl->encdata_buffer == NULL) {
     connssl->encdata_offset = 0;
-    connssl->encdata_length = 4096;
+    connssl->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
     connssl->encdata_buffer = malloc(connssl->encdata_length);
     if(connssl->encdata_buffer == NULL) {
       failf(data, "schannel: unable to allocate memory");
@@ -401,11 +403,12 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
 }
 
 static CURLcode
-schannel_connect_step3(struct connectdata *conn, int sockindex) {
+schannel_connect_step3(struct connectdata *conn, int sockindex)
+{
   CURLcode retcode = CURLE_OK;
   struct SessionHandle *data = conn->data;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  curl_schannel_cred *old_cred = NULL;
+  struct curl_schannel_cred *old_cred = NULL;
   int incache;
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
@@ -441,7 +444,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) {
   }
   if(!incache) {
     retcode = Curl_ssl_addsessionid(conn, (void*)connssl->cred,
-                                    sizeof(curl_schannel_cred));
+                                    sizeof(struct curl_schannel_cred));
     if(retcode) {
       failf(data, "schannel: failed to store credential handle\n");
       return retcode;
@@ -458,7 +461,8 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) {
 
 static CURLcode
 schannel_connect_common(struct connectdata *conn, int sockindex,
-                        bool nonblocking, bool *done) {
+                        bool nonblocking, bool *done)
+{
   CURLcode retcode;
   struct SessionHandle *data = conn->data;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
@@ -568,7 +572,8 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
 
 static ssize_t
 schannel_send(struct connectdata *conn, int sockindex,
-              const void *buf, size_t len, CURLcode *err) {
+              const void *buf, size_t len, CURLcode *err)
+{
   ssize_t ret = -1;
   size_t data_len = 0;
   unsigned char *data = NULL;
@@ -652,7 +657,8 @@ schannel_send(struct connectdata *conn, int sockindex,
 
 static ssize_t
 schannel_recv(struct connectdata *conn, int sockindex,
-              char *buf, size_t len, CURLcode *err) {
+              char *buf, size_t len, CURLcode *err)
+{
   size_t size = 0;
   ssize_t read = 0, ret = -1;
   CURLcode retcode;
@@ -670,7 +676,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
   /* buffer to store previously received and decrypted data */
   if(connssl->decdata_buffer == NULL) {
     connssl->decdata_offset = 0;
-    connssl->decdata_length = 4096;
+    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");
@@ -679,10 +685,10 @@ schannel_recv(struct connectdata *conn, int sockindex,
   }
 
   /* increase buffer in order to fit the requested amount of data */
-  while(connssl->encdata_length - connssl->encdata_offset < 2048 ||
-        connssl->encdata_length < len) {
+  while(connssl->encdata_length - connssl->encdata_offset <
+        CURL_SCHANNEL_BUFFER_STEP_SIZE || connssl->encdata_length < len) {
     /* increase internal encrypted data buffer */
-    connssl->encdata_length += 2048;
+    connssl->encdata_length += CURL_SCHANNEL_BUFFER_STEP_SIZE;
     connssl->encdata_buffer = realloc(connssl->encdata_buffer,
                                       connssl->encdata_length);
     if(connssl->encdata_buffer == NULL) {
@@ -760,7 +766,8 @@ schannel_recv(struct connectdata *conn, int sockindex,
         infof(data, "schannel: decrypted data length: %d\n", inbuf[1].cbBuffer);
 
         /* increase buffer in order to fit the received amount of data */
-        size = inbuf[1].cbBuffer > 2048 ? inbuf[1].cbBuffer : 2048;
+        size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_STEP_SIZE ?
+               inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_STEP_SIZE;
         while(connssl->decdata_length - connssl->decdata_offset < size ||
               connssl->decdata_length < len) {
           /* increase internal decrypted data buffer */
@@ -839,15 +846,17 @@ schannel_recv(struct connectdata *conn, int sockindex,
   }
 
   /* reduce internal buffer length to reduce memory usage */
-  if(connssl->encdata_length > 4096) {
-    connssl->encdata_length = connssl->encdata_offset > 4096 ?
-                              connssl->encdata_offset : 4096;
+  if(connssl->encdata_length > CURL_SCHANNEL_BUFFER_INIT_SIZE) {
+    connssl->encdata_length =
+      connssl->encdata_offset > CURL_SCHANNEL_BUFFER_INIT_SIZE ?
+      connssl->encdata_offset : CURL_SCHANNEL_BUFFER_INIT_SIZE;
     connssl->encdata_buffer = realloc(connssl->encdata_buffer,
                                       connssl->encdata_length);
   }
-  if(connssl->decdata_length > 4096) {
-    connssl->decdata_length = connssl->decdata_offset > 4096 ?
-                              connssl->decdata_offset : 4096;
+  if(connssl->decdata_length > CURL_SCHANNEL_BUFFER_INIT_SIZE) {
+    connssl->decdata_length =
+      connssl->decdata_offset > CURL_SCHANNEL_BUFFER_INIT_SIZE ?
+      connssl->decdata_offset : CURL_SCHANNEL_BUFFER_INIT_SIZE;
     connssl->decdata_buffer = realloc(connssl->decdata_buffer,
                                       connssl->decdata_length);
   }
@@ -875,12 +884,14 @@ schannel_recv(struct connectdata *conn, int sockindex,
 
 CURLcode
 Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
-                                  bool *done) {
+                                  bool *done)
+{
   return schannel_connect_common(conn, sockindex, TRUE, done);
 }
 
 CURLcode
-Curl_schannel_connect(struct connectdata *conn, int sockindex) {
+Curl_schannel_connect(struct connectdata *conn, int sockindex)
+{
   CURLcode retcode;
   bool done = FALSE;
 
@@ -893,7 +904,8 @@ Curl_schannel_connect(struct connectdata *conn, int sockindex) {
   return CURLE_OK;
 }
 
-bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex) {
+bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
+{
   const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
 
   if(connssl->use) /* SSL is in use */
@@ -903,7 +915,8 @@ bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex) {
     return FALSE;
 }
 
-void Curl_schannel_close(struct connectdata *conn, int sockindex) {
+void Curl_schannel_close(struct connectdata *conn, int sockindex)
+{
   struct SessionHandle *data = conn->data;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
 
@@ -934,12 +947,14 @@ void Curl_schannel_close(struct connectdata *conn, int sockindex) {
   }
 }
 
-int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) {
+int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
+{
   return CURLE_NOT_BUILT_IN; /* TODO: implement SSL/TLS shutdown */
 }
 
-void Curl_schannel_session_free(void *ptr) {
-  curl_schannel_cred *cred = ptr;
+void Curl_schannel_session_free(void *ptr)
+{
+  struct curl_schannel_cred *cred = ptr;
 
   if(cred) {
     s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
@@ -947,18 +962,20 @@ void Curl_schannel_session_free(void *ptr) {
   }
 }
 
-int Curl_schannel_init() {
+int Curl_schannel_init()
+{
   return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
 }
 
-void Curl_schannel_cleanup() {
+void Curl_schannel_cleanup()
+{
   Curl_sspi_global_cleanup();
 }
 
 size_t Curl_schannel_version(char *buffer, size_t size)
 {
-  char* version = Curl_sspi_version();
-  size = snprintf(buffer, size, "Schannel-%s", version);
+  char *version = Curl_sspi_version();
+  size = snprintf(buffer, size, "Schannel/%s", version);
   free(version);
   return size;
 }
diff --git a/lib/curl_schannel.h b/lib/curl_schannel.h
index 3e24fad..fa19a02 100644
--- a/lib/curl_schannel.h
+++ b/lib/curl_schannel.h
@@ -93,6 +93,10 @@
 #endif
 
 
+#define CURL_SCHANNEL_BUFFER_INIT_SIZE  4096
+#define CURL_SCHANNEL_BUFFER_STEP_SIZE  2048
+
+
 CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex);
 
 CURLcode Curl_schannel_connect_nonblocking(struct connectdata *conn,
diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c
index d3533a2..29f8a43 100644
--- a/lib/curl_sspi.c
+++ b/lib/curl_sspi.c
@@ -133,6 +133,7 @@ Curl_sspi_version()
   LPTSTR path = NULL;
   LPVOID data = NULL;
   DWORD size, handle;
+  UINT length;
 
   if(s_hSecDll) {
     path = malloc(MAX_PATH);
@@ -143,8 +144,8 @@ Curl_sspi_version()
           data = malloc(size);
           if(data) {
             if(GetFileVersionInfo(path, handle, size, data)) {
-              if(VerQueryValue(data, "\\", &version_info, &handle)) {
-                version = curl_maprintf("SSPI/%d.%d.%d.%d",
+              if(VerQueryValue(data, "\\", (LPVOID*)&version_info, &length)) {
+                version = curl_maprintf("%d.%d.%d.%d",
                   (version_info->dwProductVersionMS>>16)&0xffff,
                   (version_info->dwProductVersionMS>>0)&0xffff,
                   (version_info->dwProductVersionLS>>16)&0xffff,
@@ -158,7 +159,7 @@ Curl_sspi_version()
       free(path);
     }
     if(!version)
-      version = strdup("SSPI/Unknown");
+      version = curl_maprintf("%d", s_pSecFn ? s_pSecFn->dwVersion : 0);
   }
 
   if(!version)
@@ -265,7 +266,8 @@ Curl_sspi_status(SECURITY_STATUS status)
       status_const = "Unknown error";
   }
 
-  return curl_maprintf("%s (0x%08X)", status_const, status);
+  return curl_maprintf("%s (0x%04X%04X)", status_const,
+                       (status>>16)&0xffff, status&0xffff);
 }
 
 
diff --git a/lib/urldata.h b/lib/urldata.h
index 7327482..20e339b 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -222,15 +222,15 @@ enum protection_level {
 
 #ifdef USE_SCHANNEL
 /* Structs to store Schannel handles */
-typedef struct curl_schannel_cred {
+struct curl_schannel_cred {
   CredHandle cred_handle;
   TimeStamp time_stamp;
-} curl_schannel_cred;
+};
 
-typedef struct curl_schannel_ctxt {
+struct curl_schannel_ctxt {
   CtxtHandle ctxt_handle;
   TimeStamp time_stamp;
-} curl_schannel_ctxt;
+};
 #endif
 
 /* enum for the nonblocking SSL connection state machine */
@@ -302,8 +302,8 @@ struct ssl_connect_data {
   SSL*     ssl;
 #endif /* USE_AXTLS */
 #ifdef USE_SCHANNEL
-  curl_schannel_cred *cred;
-  curl_schannel_ctxt *ctxt;
+  struct curl_schannel_cred *cred;
+  struct curl_schannel_ctxt *ctxt;
   SecPkgContext_StreamSizes stream_sizes;
   ssl_connect_state connecting_state;
   size_t encdata_length, decdata_length;
-- 
1.7.10.msysgit.1

