From 262cc2bcadbf5aff5ffb5d520d16e9ea46a17b10 Mon Sep 17 00:00:00 2001
From: Mark Salisbury <mark.salisbury@hp.com>
Date: Wed, 13 Jun 2012 14:12:16 -0600
Subject: [PATCH 7/8] schannel SSL: Implemented SSL shutdown

curl_schannel.c - implemented graceful SSL shutdown.  If we fail to
shutdown the connection gracefully, I've seen schannel try to use a
session ID for future connects and the server aborts the connection
during the handshake.
---
 lib/curl_schannel.c |   88 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index 936c7d8..296b657 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -6,6 +6,7 @@
  *                             \___|\___/|_| \_\_____|
  *
  * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
+ * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
  * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
@@ -39,7 +40,6 @@
 
 /*
  * TODO list for TLS/SSL implementation:
- * - implement SSL/TLS shutdown
  * - implement client certificate authentication
  * - implement custom server certificate validation
  * - implement cipher/algorithm option
@@ -1117,6 +1117,11 @@ void Curl_schannel_close(struct connectdata *conn, int sockindex)
   struct SessionHandle *data = conn->data;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
 
+  if (conn->ssl[sockindex].use) {
+    /* if the SSL channel hasn't been shut down yet, do that now. */
+    Curl_ssl_shutdown(conn, sockindex);
+  }
+
   infof(data, "schannel: Closing connection with %s:%d\n",
         conn->host.name, conn->remote_port);
 
@@ -1146,7 +1151,86 @@ void Curl_schannel_close(struct connectdata *conn, int sockindex)
 
 int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
 {
-  return CURLE_NOT_BUILT_IN; /* TODO: implement SSL/TLS shutdown */
+  /* See http://msdn.microsoft.com/en-us/library/windows/desktop/aa380138(v=vs.85).aspx
+   * Shutting Down an Schannel Connection
+   */
+  struct SessionHandle *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+  infof(data, "schannel: shutting down SSL connection with %s:%d\n",
+        conn->host.name, conn->remote_port);
+
+  if(connssl->ctxt) {
+    SecBufferDesc BuffDesc;
+    SecBuffer Buffer;
+    SECURITY_STATUS sspi_status;
+    SecBuffer outbuf;
+    SecBufferDesc outbuf_desc;
+    CURLcode result;
+#ifdef UNICODE
+    wchar_t * whost;
+#endif
+    DWORD dwshut = SCHANNEL_SHUTDOWN;
+
+    InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
+    InitSecBufferDesc(&BuffDesc, &Buffer, 1);
+
+    sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle, &BuffDesc);
+
+    if (sspi_status != SEC_E_OK) {
+      failf(data, "schannel: ApplyControlToken failure: %s",
+            Curl_sspi_strerror(conn, sspi_status));
+    }
+
+#ifdef UNICODE
+    whost = _curl_win32_UTF8_to_wchar(conn->host.name);
+    if (whost == NULL) {
+      failf(data, "schannel: unable to allocate memory");
+      return CURLE_OUT_OF_MEMORY;
+    }
+#endif
+
+    /* setup output buffer */
+    InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
+    InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
+
+    sspi_status = s_pSecFn->InitializeSecurityContext(
+         &connssl->cred->cred_handle,
+         &connssl->ctxt->ctxt_handle,
+#ifdef UNICODE
+         whost,
+#else
+         conn->host.name,
+#endif
+         connssl->req_flags,
+         0,
+         0,
+         NULL,
+         0,
+         &connssl->ctxt->ctxt_handle,
+         &outbuf_desc,
+         &connssl->ret_flags,
+         &connssl->ctxt->time_stamp);
+
+#ifdef UNICODE
+    free(whost);
+#endif
+
+    if (sspi_status == SEC_E_OK || sspi_status == SEC_I_CONTEXT_EXPIRED) {
+      /* send close message which is in output buffer */
+      ssize_t write;
+      result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
+                                outbuf.cbBuffer, &write);
+
+      s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
+      if (write != outbuf.cbBuffer) {
+        infof(data, "schannel: failed to send close msg: %s (bytes written: %d)\n",
+              curl_easy_strerror(result), write);
+      }
+    }
+  }
+
+  return CURLE_OK;
 }
 
 void Curl_schannel_session_free(void *ptr)
-- 
1.7.4.msysgit.0

