Imported Upstream version 7.59.0
[platform/upstream/curl.git] / lib / vtls / schannel.c
index bd92399..b8afe46 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
  * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
- * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -46,6 +46,8 @@
 #  error "Can't compile SCHANNEL support without SSPI."
 #endif
 
+#include <schnlsp.h>
+#include <schannel.h>
 #include "curl_sspi.h"
 #include "schannel.h"
 #include "vtls.h"
 #  define HAS_ALPN 1
 #endif
 
+#ifndef UNISP_NAME_A
+#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
+
+/* Both schannel buffer sizes must be > 0 */
+#define CURL_SCHANNEL_BUFFER_INIT_SIZE   4096
+#define CURL_SCHANNEL_BUFFER_FREE_SIZE   1024
+
 /* Uncomment to force verbose output
  * #define infof(x, y, ...) printf(y, __VA_ARGS__)
  * #define failf(x, y, ...) printf(y, __VA_ARGS__)
  */
 
+#ifndef CALG_SHA_256
+#  define CALG_SHA_256 0x0000800c
+#endif
+
+/* Structs to store Schannel handles */
+struct curl_schannel_cred {
+  CredHandle cred_handle;
+  TimeStamp time_stamp;
+  int refcount;
+};
+
+struct curl_schannel_ctxt {
+  CtxtHandle ctxt_handle;
+  TimeStamp time_stamp;
+};
+
+struct ssl_backend_data {
+  struct curl_schannel_cred *cred;
+  struct curl_schannel_ctxt *ctxt;
+  SecPkgContext_StreamSizes stream_sizes;
+  size_t encdata_length, decdata_length;
+  size_t encdata_offset, decdata_offset;
+  unsigned char *encdata_buffer, *decdata_buffer;
+  /* encdata_is_incomplete: if encdata contains only a partial record that
+     can't be decrypted without another Curl_read_plain (that is, status is
+     SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes
+     more bytes into encdata then set this back to false. */
+  bool encdata_is_incomplete;
+  unsigned long req_flags, ret_flags;
+  CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
+  bool recv_sspi_close_notify; /* true if connection closed by close_notify */
+  bool recv_connection_closed; /* true if connection closed, regardless how */
+  bool use_alpn; /* true if ALPN is used for this connection */
+};
+
+#define BACKEND connssl->backend
+
 static Curl_recv schannel_recv;
 static Curl_send schannel_send;
 
+static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex,
+                                    const char *pinnedpubkey);
+
 #ifdef _WIN32_WCE
 static CURLcode verify_certificate(struct connectdata *conn, int sockindex);
 #endif
@@ -103,6 +193,41 @@ static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
 }
 
 static CURLcode
+set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn)
+{
+  struct Curl_easy *data = conn->data;
+  long ssl_version = SSL_CONN_CONFIG(version);
+  long ssl_version_max = SSL_CONN_CONFIG(version_max);
+  long i = ssl_version;
+
+  switch(ssl_version_max) {
+    case CURL_SSLVERSION_MAX_NONE:
+      ssl_version_max = ssl_version << 16;
+      break;
+    case CURL_SSLVERSION_MAX_DEFAULT:
+      ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+      break;
+  }
+  for(; i <= (ssl_version_max >> 16); ++i) {
+    switch(i) {
+      case CURL_SSLVERSION_TLSv1_0:
+        schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT;
+        break;
+      case CURL_SSLVERSION_TLSv1_1:
+        schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT;
+        break;
+      case CURL_SSLVERSION_TLSv1_2:
+        schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT;
+        break;
+      case CURL_SSLVERSION_TLSv1_3:
+        failf(data, "Schannel: TLS 1.3 is not yet supported");
+        return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+  return CURLE_OK;
+}
+
+static CURLcode
 schannel_connect_step1(struct connectdata *conn, int sockindex)
 {
   ssize_t written = -1;
@@ -124,7 +249,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
 #endif
   TCHAR *host_name;
   CURLcode result;
-  const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+  char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
     conn->host.name;
 
   infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
@@ -141,33 +266,33 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
 #ifdef HAS_ALPN
   /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
      Also it doesn't seem to be supported for Wine, see curl bug #983. */
-  connssl->use_alpn = conn->bits.tls_enable_alpn &&
+  BACKEND->use_alpn = conn->bits.tls_enable_alpn &&
                       !GetProcAddress(GetModuleHandleA("ntdll"),
                                       "wine_get_version") &&
                       Curl_verify_windows_version(6, 3, PLATFORM_WINNT,
                                                   VERSION_GREATER_THAN_EQUAL);
 #else
-  connssl->use_alpn = false;
+  BACKEND->use_alpn = false;
 #endif
 
-  connssl->cred = NULL;
+  BACKEND->cred = NULL;
 
   /* check for an existing re-usable credential handle */
-  if(data->set.general_ssl.sessionid) {
+  if(SSL_SET_OPTION(primary.sessionid)) {
     Curl_ssl_sessionid_lock(conn);
     if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL, sockindex)) {
-      connssl->cred = old_cred;
+      BACKEND->cred = old_cred;
       infof(data, "schannel: re-using existing credential handle\n");
 
       /* increment the reference counter of the credential/session handle */
-      connssl->cred->refcount++;
+      BACKEND->cred->refcount++;
       infof(data, "schannel: incremented credential handle refcount = %d\n",
-            connssl->cred->refcount);
+            BACKEND->cred->refcount);
     }
     Curl_ssl_sessionid_unlock(conn);
   }
 
-  if(!connssl->cred) {
+  if(!BACKEND->cred) {
     /* setup Schannel API options */
     memset(&schannel_cred, 0, sizeof(schannel_cred));
     schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
@@ -216,17 +341,15 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
         SP_PROT_TLS1_2_CLIENT;
       break;
     case CURL_SSLVERSION_TLSv1_0:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
-      break;
     case CURL_SSLVERSION_TLSv1_1:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
-      break;
     case CURL_SSLVERSION_TLSv1_2:
-      schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
-      break;
     case CURL_SSLVERSION_TLSv1_3:
-      failf(data, "Schannel: TLS 1.3 is not yet supported");
-      return CURLE_SSL_CONNECT_ERROR;
+      {
+        result = set_ssl_version_min_max(&schannel_cred, conn);
+        if(result != CURLE_OK)
+          return result;
+        break;
+      }
     case CURL_SSLVERSION_SSLv3:
       schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
       break;
@@ -239,14 +362,14 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
     }
 
     /* allocate memory for the re-usable credential handle */
-    connssl->cred = (struct curl_schannel_cred *)
+    BACKEND->cred = (struct curl_schannel_cred *)
       malloc(sizeof(struct curl_schannel_cred));
-    if(!connssl->cred) {
+    if(!BACKEND->cred) {
       failf(data, "schannel: unable to allocate memory");
       return CURLE_OUT_OF_MEMORY;
     }
-    memset(connssl->cred, 0, sizeof(struct curl_schannel_cred));
-    connssl->cred->refcount = 1;
+    memset(BACKEND->cred, 0, sizeof(struct curl_schannel_cred));
+    BACKEND->cred->refcount = 1;
 
     /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx
        */
@@ -254,8 +377,8 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
       s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
                                          SECPKG_CRED_OUTBOUND, NULL,
                                          &schannel_cred, NULL, NULL,
-                                         &connssl->cred->cred_handle,
-                                         &connssl->cred->time_stamp);
+                                         &BACKEND->cred->cred_handle,
+                                         &BACKEND->cred->time_stamp);
 
     if(sspi_status != SEC_E_OK) {
       if(sspi_status == SEC_E_WRONG_PRINCIPAL)
@@ -264,7 +387,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
       else
         failf(data, "schannel: AcquireCredentialsHandle failed: %s",
               Curl_sspi_strerror(conn, sspi_status));
-      Curl_safefree(connssl->cred);
+      Curl_safefree(BACKEND->cred);
       return CURLE_SSL_CONNECT_ERROR;
     }
   }
@@ -279,7 +402,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
   }
 
 #ifdef HAS_ALPN
-  if(connssl->use_alpn) {
+  if(BACKEND->use_alpn) {
     int cur = 0;
     int list_start_index = 0;
     unsigned int *extension_len = NULL;
@@ -337,18 +460,18 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
   InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
 
   /* setup request flags */
-  connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
+  BACKEND->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
     ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
     ISC_REQ_STREAM;
 
   /* allocate memory for the security context handle */
-  connssl->ctxt = (struct curl_schannel_ctxt *)
+  BACKEND->ctxt = (struct curl_schannel_ctxt *)
     malloc(sizeof(struct curl_schannel_ctxt));
-  if(!connssl->ctxt) {
+  if(!BACKEND->ctxt) {
     failf(data, "schannel: unable to allocate memory");
     return CURLE_OUT_OF_MEMORY;
   }
-  memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt));
+  memset(BACKEND->ctxt, 0, sizeof(struct curl_schannel_ctxt));
 
   host_name = Curl_convert_UTF8_to_tchar(hostname);
   if(!host_name)
@@ -362,10 +485,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
      us problems with inbuf regardless. https://github.com/curl/curl/issues/983
   */
   sspi_status = s_pSecFn->InitializeSecurityContext(
-    &connssl->cred->cred_handle, NULL, host_name, connssl->req_flags, 0, 0,
-    (connssl->use_alpn ? &inbuf_desc : NULL),
-    0, &connssl->ctxt->ctxt_handle,
-    &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
+    &BACKEND->cred->cred_handle, NULL, host_name, BACKEND->req_flags, 0, 0,
+    (BACKEND->use_alpn ? &inbuf_desc : NULL),
+    0, &BACKEND->ctxt->ctxt_handle,
+    &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp);
 
   Curl_unicodefree(host_name);
 
@@ -376,7 +499,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
     else
       failf(data, "schannel: initial InitializeSecurityContext failed: %s",
             Curl_sspi_strerror(conn, sspi_status));
-    Curl_safefree(connssl->ctxt);
+    Curl_safefree(BACKEND->ctxt);
     return CURLE_SSL_CONNECT_ERROR;
   }
 
@@ -396,9 +519,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
   infof(data, "schannel: sent initial handshake data: "
         "sent %zd bytes\n", written);
 
-  connssl->recv_unrecoverable_err = CURLE_OK;
-  connssl->recv_sspi_close_notify = false;
-  connssl->recv_connection_closed = false;
+  BACKEND->recv_unrecoverable_err = CURLE_OK;
+  BACKEND->recv_sspi_close_notify = false;
+  BACKEND->recv_connection_closed = false;
+  BACKEND->encdata_is_incomplete = false;
 
   /* continue to second handshake step */
   connssl->connecting_state = ssl_connect_2;
@@ -423,46 +547,48 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
   TCHAR *host_name;
   CURLcode result;
   bool doread;
-  const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+  char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
     conn->host.name;
+  const char *pubkey_ptr;
 
   doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
 
   infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
         hostname, conn->remote_port);
 
-  if(!connssl->cred || !connssl->ctxt)
+  if(!BACKEND->cred || !BACKEND->ctxt)
     return CURLE_SSL_CONNECT_ERROR;
 
   /* buffer to store previously received and decrypted data */
-  if(connssl->decdata_buffer == NULL) {
-    connssl->decdata_offset = 0;
-    connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
-    connssl->decdata_buffer = malloc(connssl->decdata_length);
-    if(connssl->decdata_buffer == NULL) {
+  if(BACKEND->decdata_buffer == NULL) {
+    BACKEND->decdata_offset = 0;
+    BACKEND->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
+    BACKEND->decdata_buffer = malloc(BACKEND->decdata_length);
+    if(BACKEND->decdata_buffer == NULL) {
       failf(data, "schannel: unable to allocate memory");
       return CURLE_OUT_OF_MEMORY;
     }
   }
 
   /* buffer to store previously received and encrypted data */
-  if(connssl->encdata_buffer == NULL) {
-    connssl->encdata_offset = 0;
-    connssl->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
-    connssl->encdata_buffer = malloc(connssl->encdata_length);
-    if(connssl->encdata_buffer == NULL) {
+  if(BACKEND->encdata_buffer == NULL) {
+    BACKEND->encdata_is_incomplete = false;
+    BACKEND->encdata_offset = 0;
+    BACKEND->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
+    BACKEND->encdata_buffer = malloc(BACKEND->encdata_length);
+    if(BACKEND->encdata_buffer == NULL) {
       failf(data, "schannel: unable to allocate memory");
       return CURLE_OUT_OF_MEMORY;
     }
   }
 
   /* if we need a bigger buffer to read a full message, increase buffer now */
-  if(connssl->encdata_length - connssl->encdata_offset <
+  if(BACKEND->encdata_length - BACKEND->encdata_offset <
      CURL_SCHANNEL_BUFFER_FREE_SIZE) {
     /* increase internal encrypted data buffer */
-    reallocated_length = connssl->encdata_offset +
+    reallocated_length = BACKEND->encdata_offset +
       CURL_SCHANNEL_BUFFER_FREE_SIZE;
-    reallocated_buffer = realloc(connssl->encdata_buffer,
+    reallocated_buffer = realloc(BACKEND->encdata_buffer,
                                  reallocated_length);
 
     if(reallocated_buffer == NULL) {
@@ -470,8 +596,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
       return CURLE_OUT_OF_MEMORY;
     }
     else {
-      connssl->encdata_buffer = reallocated_buffer;
-      connssl->encdata_length = reallocated_length;
+      BACKEND->encdata_buffer = reallocated_buffer;
+      BACKEND->encdata_length = reallocated_length;
     }
   }
 
@@ -479,10 +605,10 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
     if(doread) {
       /* read encrypted handshake data from socket */
       result = Curl_read_plain(conn->sock[sockindex],
-                               (char *) (connssl->encdata_buffer +
-                                         connssl->encdata_offset),
-                               connssl->encdata_length -
-                               connssl->encdata_offset,
+                               (char *) (BACKEND->encdata_buffer +
+                                         BACKEND->encdata_offset),
+                               BACKEND->encdata_length -
+                               BACKEND->encdata_offset,
                                &nread);
       if(result == CURLE_AGAIN) {
         if(connssl->connecting_state != ssl_connect_2_writing)
@@ -498,15 +624,17 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
       }
 
       /* increase encrypted data buffer offset */
-      connssl->encdata_offset += nread;
+      BACKEND->encdata_offset += nread;
+      BACKEND->encdata_is_incomplete = false;
+      infof(data, "schannel: encrypted data got %zd\n", nread);
     }
 
     infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
-          connssl->encdata_offset, connssl->encdata_length);
+          BACKEND->encdata_offset, BACKEND->encdata_length);
 
     /* setup input buffers */
-    InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset),
-                  curlx_uztoul(connssl->encdata_offset));
+    InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(BACKEND->encdata_offset),
+                  curlx_uztoul(BACKEND->encdata_offset));
     InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
     InitSecBufferDesc(&inbuf_desc, inbuf, 2);
 
@@ -522,8 +650,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
     }
 
     /* copy received handshake data into input buffer */
-    memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer,
-           connssl->encdata_offset);
+    memcpy(inbuf[0].pvBuffer, BACKEND->encdata_buffer,
+           BACKEND->encdata_offset);
 
     host_name = Curl_convert_UTF8_to_tchar(hostname);
     if(!host_name)
@@ -532,9 +660,9 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
     /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
        */
     sspi_status = s_pSecFn->InitializeSecurityContext(
-      &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
-      host_name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
-      &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
+      &BACKEND->cred->cred_handle, &BACKEND->ctxt->ctxt_handle,
+      host_name, BACKEND->req_flags, 0, 0, &inbuf_desc, 0, NULL,
+      &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp);
 
     Curl_unicodefree(host_name);
 
@@ -543,6 +671,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
 
     /* check if the handshake was incomplete */
     if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
+      BACKEND->encdata_is_incomplete = true;
       connssl->connecting_state = ssl_connect_2_reading;
       infof(data, "schannel: received incomplete message, need more data\n");
       return CURLE_OK;
@@ -552,8 +681,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
        the handshake without one. This will allow connections to servers which
        request a client certificate but do not require it. */
     if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
-       !(connssl->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
-      connssl->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
+       !(BACKEND->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
+      BACKEND->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
       connssl->connecting_state = ssl_connect_2_writing;
       infof(data, "schannel: a client certificate has been requested\n");
       return CURLE_OK;
@@ -592,7 +721,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
       else
         failf(data, "schannel: next InitializeSecurityContext failed: %s",
               Curl_sspi_strerror(conn, sspi_status));
-      return CURLE_SSL_CONNECT_ERROR;
+      return sspi_status == SEC_E_UNTRUSTED_ROOT ?
+          CURLE_SSL_CACERT : CURLE_SSL_CONNECT_ERROR;
     }
 
     /* check if there was additional remaining encrypted data */
@@ -610,11 +740,11 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
       */
       /* check if the remaining data is less than the total amount
          and therefore begins after the already processed data */
-      if(connssl->encdata_offset > inbuf[1].cbBuffer) {
-        memmove(connssl->encdata_buffer,
-                (connssl->encdata_buffer + connssl->encdata_offset) -
+      if(BACKEND->encdata_offset > inbuf[1].cbBuffer) {
+        memmove(BACKEND->encdata_buffer,
+                (BACKEND->encdata_buffer + BACKEND->encdata_offset) -
                 inbuf[1].cbBuffer, inbuf[1].cbBuffer);
-        connssl->encdata_offset = inbuf[1].cbBuffer;
+        BACKEND->encdata_offset = inbuf[1].cbBuffer;
         if(sspi_status == SEC_I_CONTINUE_NEEDED) {
           doread = FALSE;
           continue;
@@ -622,7 +752,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
       }
     }
     else {
-      connssl->encdata_offset = 0;
+      BACKEND->encdata_offset = 0;
     }
     break;
   }
@@ -639,6 +769,17 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
     infof(data, "schannel: SSL/TLS handshake complete\n");
   }
 
+  pubkey_ptr = SSL_IS_PROXY() ?
+    data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
+    data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+  if(pubkey_ptr) {
+    result = pkp_pin_peer_pubkey(conn, sockindex, pubkey_ptr);
+    if(result) {
+      failf(data, "SSL: public key does not match pinned public key!");
+      return result;
+    }
+  }
+
 #ifdef _WIN32_WCE
   /* Windows CE doesn't do any server certificate validation.
      We have to do it manually. */
@@ -657,8 +798,10 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   SECURITY_STATUS sspi_status = SEC_E_OK;
   CERT_CONTEXT *ccert_context = NULL;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
   const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
     conn->host.name;
+#endif
 #ifdef HAS_ALPN
   SecPkgContext_ApplicationProtocol alpn_result;
 #endif
@@ -668,27 +811,27 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
   infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
         hostname, conn->remote_port);
 
-  if(!connssl->cred)
+  if(!BACKEND->cred)
     return CURLE_SSL_CONNECT_ERROR;
 
   /* check if the required context attributes are met */
-  if(connssl->ret_flags != connssl->req_flags) {
-    if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
+  if(BACKEND->ret_flags != BACKEND->req_flags) {
+    if(!(BACKEND->ret_flags & ISC_RET_SEQUENCE_DETECT))
       failf(data, "schannel: failed to setup sequence detection");
-    if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
+    if(!(BACKEND->ret_flags & ISC_RET_REPLAY_DETECT))
       failf(data, "schannel: failed to setup replay detection");
-    if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY))
+    if(!(BACKEND->ret_flags & ISC_RET_CONFIDENTIALITY))
       failf(data, "schannel: failed to setup confidentiality");
-    if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY))
+    if(!(BACKEND->ret_flags & ISC_RET_ALLOCATED_MEMORY))
       failf(data, "schannel: failed to setup memory allocation");
-    if(!(connssl->ret_flags & ISC_RET_STREAM))
+    if(!(BACKEND->ret_flags & ISC_RET_STREAM))
       failf(data, "schannel: failed to setup stream orientation");
     return CURLE_SSL_CONNECT_ERROR;
   }
 
 #ifdef HAS_ALPN
-  if(connssl->use_alpn) {
-    sspi_status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
+  if(BACKEND->use_alpn) {
+    sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
       SECPKG_ATTR_APPLICATION_PROTOCOL, &alpn_result);
 
     if(sspi_status != SEC_E_OK) {
@@ -722,7 +865,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
 #endif
 
   /* save the current session data for possible re-use */
-  if(data->set.general_ssl.sessionid) {
+  if(SSL_SET_OPTION(primary.sessionid)) {
     bool incache;
     struct curl_schannel_cred *old_cred = NULL;
 
@@ -730,7 +873,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
     incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL,
                                       sockindex));
     if(incache) {
-      if(old_cred != connssl->cred) {
+      if(old_cred != BACKEND->cred) {
         infof(data, "schannel: old credential handle is stale, removing\n");
         /* we're not taking old_cred ownership here, no refcount++ is needed */
         Curl_ssl_delsessionid(conn, (void *)old_cred);
@@ -738,7 +881,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
       }
     }
     if(!incache) {
-      result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
+      result = Curl_ssl_addsessionid(conn, (void *)BACKEND->cred,
                                      sizeof(struct curl_schannel_cred),
                                      sockindex);
       if(result) {
@@ -748,7 +891,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
       }
       else {
         /* this cred session is now also referenced by sessionid cache */
-        connssl->cred->refcount++;
+        BACKEND->cred->refcount++;
         infof(data, "schannel: stored credential handle in session cache\n");
       }
     }
@@ -756,7 +899,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
   }
 
   if(data->set.ssl.certinfo) {
-    sspi_status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
+    sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
       SECPKG_ATTR_REMOTE_CERT_CONTEXT, &ccert_context);
 
     if((sspi_status != SEC_E_OK) || (ccert_context == NULL)) {
@@ -910,11 +1053,11 @@ schannel_send(struct connectdata *conn, int sockindex,
   CURLcode result;
 
   /* check if the maximum stream sizes were queried */
-  if(connssl->stream_sizes.cbMaximumMessage == 0) {
+  if(BACKEND->stream_sizes.cbMaximumMessage == 0) {
     sspi_status = s_pSecFn->QueryContextAttributes(
-      &connssl->ctxt->ctxt_handle,
+      &BACKEND->ctxt->ctxt_handle,
       SECPKG_ATTR_STREAM_SIZES,
-      &connssl->stream_sizes);
+      &BACKEND->stream_sizes);
     if(sspi_status != SEC_E_OK) {
       *err = CURLE_SEND_ERROR;
       return -1;
@@ -922,14 +1065,13 @@ schannel_send(struct connectdata *conn, int sockindex,
   }
 
   /* check if the buffer is longer than the maximum message length */
-  if(len > connssl->stream_sizes.cbMaximumMessage) {
-    *err = CURLE_SEND_ERROR;
-    return -1;
+  if(len > BACKEND->stream_sizes.cbMaximumMessage) {
+    len = BACKEND->stream_sizes.cbMaximumMessage;
   }
 
   /* calculate the complete message length and allocate a buffer for it */
-  data_len = connssl->stream_sizes.cbHeader + len +
-    connssl->stream_sizes.cbTrailer;
+  data_len = BACKEND->stream_sizes.cbHeader + len +
+    BACKEND->stream_sizes.cbTrailer;
   data = (unsigned char *) malloc(data_len);
   if(data == NULL) {
     *err = CURLE_OUT_OF_MEMORY;
@@ -938,12 +1080,12 @@ schannel_send(struct connectdata *conn, int sockindex,
 
   /* setup output buffers (header, data, trailer, empty) */
   InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
-                data, connssl->stream_sizes.cbHeader);
+                data, BACKEND->stream_sizes.cbHeader);
   InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
-                data + connssl->stream_sizes.cbHeader, curlx_uztoul(len));
+                data + BACKEND->stream_sizes.cbHeader, curlx_uztoul(len));
   InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
-                data + connssl->stream_sizes.cbHeader + len,
-                connssl->stream_sizes.cbTrailer);
+                data + BACKEND->stream_sizes.cbHeader + len,
+                BACKEND->stream_sizes.cbTrailer);
   InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
   InitSecBufferDesc(&outbuf_desc, outbuf, 4);
 
@@ -951,7 +1093,7 @@ schannel_send(struct connectdata *conn, int sockindex,
   memcpy(outbuf[1].pvBuffer, buf, len);
 
   /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
-  sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
+  sspi_status = s_pSecFn->EncryptMessage(&BACKEND->ctxt->ctxt_handle, 0,
                                          &outbuf_desc, 0);
 
   /* check if the message was encrypted */
@@ -1061,7 +1203,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
   size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
 
   /****************************************************************************
-   * Don't return or set connssl->recv_unrecoverable_err unless in the cleanup.
+   * Don't return or set BACKEND->recv_unrecoverable_err unless in the cleanup.
    * The pattern for return error is set *err, optional infof, goto cleanup.
    *
    * Our priority is to always return as much decrypted data to the caller as
@@ -1073,16 +1215,16 @@ schannel_recv(struct connectdata *conn, int sockindex,
   infof(data, "schannel: client wants to read %zu bytes\n", len);
   *err = CURLE_OK;
 
-  if(len && len <= connssl->decdata_offset) {
+  if(len && len <= BACKEND->decdata_offset) {
     infof(data, "schannel: enough decrypted data is already available\n");
     goto cleanup;
   }
-  else if(connssl->recv_unrecoverable_err) {
-    *err = connssl->recv_unrecoverable_err;
+  else if(BACKEND->recv_unrecoverable_err) {
+    *err = BACKEND->recv_unrecoverable_err;
     infof(data, "schannel: an unrecoverable error occurred in a prior call\n");
     goto cleanup;
   }
-  else if(connssl->recv_sspi_close_notify) {
+  else if(BACKEND->recv_sspi_close_notify) {
     /* once a server has indicated shutdown there is no more encrypted data */
     infof(data, "schannel: server indicated shutdown in a prior call\n");
     goto cleanup;
@@ -1094,17 +1236,17 @@ schannel_recv(struct connectdata *conn, int sockindex,
     */
     ; /* do nothing */
   }
-  else if(!connssl->recv_connection_closed) {
+  else if(!BACKEND->recv_connection_closed) {
     /* increase enc buffer in order to fit the requested amount of data */
-    size = connssl->encdata_length - connssl->encdata_offset;
+    size = BACKEND->encdata_length - BACKEND->encdata_offset;
     if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE ||
-       connssl->encdata_length < min_encdata_length) {
-      reallocated_length = connssl->encdata_offset +
+       BACKEND->encdata_length < min_encdata_length) {
+      reallocated_length = BACKEND->encdata_offset +
                            CURL_SCHANNEL_BUFFER_FREE_SIZE;
       if(reallocated_length < min_encdata_length) {
         reallocated_length = min_encdata_length;
       }
-      reallocated_buffer = realloc(connssl->encdata_buffer,
+      reallocated_buffer = realloc(BACKEND->encdata_buffer,
                                    reallocated_length);
       if(reallocated_buffer == NULL) {
         *err = CURLE_OUT_OF_MEMORY;
@@ -1112,20 +1254,20 @@ schannel_recv(struct connectdata *conn, int sockindex,
         goto cleanup;
       }
 
-      connssl->encdata_buffer = reallocated_buffer;
-      connssl->encdata_length = reallocated_length;
-      size = connssl->encdata_length - connssl->encdata_offset;
+      BACKEND->encdata_buffer = reallocated_buffer;
+      BACKEND->encdata_length = reallocated_length;
+      size = BACKEND->encdata_length - BACKEND->encdata_offset;
       infof(data, "schannel: encdata_buffer resized %zu\n",
-            connssl->encdata_length);
+            BACKEND->encdata_length);
     }
 
     infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
-          connssl->encdata_offset, connssl->encdata_length);
+          BACKEND->encdata_offset, BACKEND->encdata_length);
 
     /* read encrypted data from socket */
     *err = Curl_read_plain(conn->sock[sockindex],
-                           (char *)(connssl->encdata_buffer +
-                                    connssl->encdata_offset),
+                           (char *)(BACKEND->encdata_buffer +
+                                    BACKEND->encdata_offset),
                            size, &nread);
     if(*err) {
       nread = -1;
@@ -1137,25 +1279,26 @@ schannel_recv(struct connectdata *conn, int sockindex,
         infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
     }
     else if(nread == 0) {
-      connssl->recv_connection_closed = true;
+      BACKEND->recv_connection_closed = true;
       infof(data, "schannel: server closed the connection\n");
     }
     else if(nread > 0) {
-      connssl->encdata_offset += (size_t)nread;
+      BACKEND->encdata_offset += (size_t)nread;
+      BACKEND->encdata_is_incomplete = false;
       infof(data, "schannel: encrypted data got %zd\n", nread);
     }
   }
 
   infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
-        connssl->encdata_offset, connssl->encdata_length);
+        BACKEND->encdata_offset, BACKEND->encdata_length);
 
   /* decrypt loop */
-  while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK &&
-        (!len || connssl->decdata_offset < len ||
-         connssl->recv_connection_closed)) {
+  while(BACKEND->encdata_offset > 0 && sspi_status == SEC_E_OK &&
+        (!len || BACKEND->decdata_offset < len ||
+         BACKEND->recv_connection_closed)) {
     /* prepare data buffer for DecryptMessage call */
-    InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer,
-                  curlx_uztoul(connssl->encdata_offset));
+    InitSecBuffer(&inbuf[0], SECBUFFER_DATA, BACKEND->encdata_buffer,
+                  curlx_uztoul(BACKEND->encdata_offset));
 
     /* we need 3 more empty input buffers for possible output */
     InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
@@ -1165,7 +1308,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
 
     /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx
        */
-    sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
+    sspi_status = s_pSecFn->DecryptMessage(&BACKEND->ctxt->ctxt_handle,
                                            &inbuf_desc, 0, NULL);
 
     /* check if everything went fine (server may want to renegotiate
@@ -1181,36 +1324,36 @@ schannel_recv(struct connectdata *conn, int sockindex,
         /* increase buffer in order to fit the received amount of data */
         size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
                inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
-        if(connssl->decdata_length - connssl->decdata_offset < size ||
-           connssl->decdata_length < len) {
+        if(BACKEND->decdata_length - BACKEND->decdata_offset < size ||
+           BACKEND->decdata_length < len) {
           /* increase internal decrypted data buffer */
-          reallocated_length = connssl->decdata_offset + size;
+          reallocated_length = BACKEND->decdata_offset + size;
           /* make sure that the requested amount of data fits */
           if(reallocated_length < len) {
             reallocated_length = len;
           }
-          reallocated_buffer = realloc(connssl->decdata_buffer,
+          reallocated_buffer = realloc(BACKEND->decdata_buffer,
                                        reallocated_length);
           if(reallocated_buffer == NULL) {
             *err = CURLE_OUT_OF_MEMORY;
             failf(data, "schannel: unable to re-allocate memory");
             goto cleanup;
           }
-          connssl->decdata_buffer = reallocated_buffer;
-          connssl->decdata_length = reallocated_length;
+          BACKEND->decdata_buffer = reallocated_buffer;
+          BACKEND->decdata_length = reallocated_length;
         }
 
         /* copy decrypted data to internal buffer */
         size = inbuf[1].cbBuffer;
         if(size) {
-          memcpy(connssl->decdata_buffer + connssl->decdata_offset,
+          memcpy(BACKEND->decdata_buffer + BACKEND->decdata_offset,
                  inbuf[1].pvBuffer, size);
-          connssl->decdata_offset += size;
+          BACKEND->decdata_offset += size;
         }
 
         infof(data, "schannel: decrypted data added: %zu\n", size);
         infof(data, "schannel: decrypted data cached: offset %zu length %zu\n",
-              connssl->decdata_offset, connssl->decdata_length);
+              BACKEND->decdata_offset, BACKEND->decdata_length);
       }
 
       /* check for remaining encrypted data */
@@ -1221,21 +1364,21 @@ schannel_recv(struct connectdata *conn, int sockindex,
         /* check if the remaining data is less than the total amount
          * and therefore begins after the already processed data
          */
-        if(connssl->encdata_offset > inbuf[3].cbBuffer) {
+        if(BACKEND->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) -
+          memmove(BACKEND->encdata_buffer,
+                  (BACKEND->encdata_buffer + BACKEND->encdata_offset) -
                   inbuf[3].cbBuffer, inbuf[3].cbBuffer);
-          connssl->encdata_offset = inbuf[3].cbBuffer;
+          BACKEND->encdata_offset = inbuf[3].cbBuffer;
         }
 
         infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
-              connssl->encdata_offset, connssl->encdata_length);
+              BACKEND->encdata_offset, BACKEND->encdata_length);
       }
       else {
         /* reset encrypted buffer offset, because there is no data remaining */
-        connssl->encdata_offset = 0;
+        BACKEND->encdata_offset = 0;
       }
 
       /* check if server wants to renegotiate the connection context */
@@ -1245,7 +1388,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
           infof(data, "schannel: can't renogotiate, an error is pending\n");
           goto cleanup;
         }
-        if(connssl->encdata_offset) {
+        if(BACKEND->encdata_offset) {
           *err = CURLE_RECV_ERROR;
           infof(data, "schannel: can't renogotiate, "
                       "encrypted data available\n");
@@ -1269,15 +1412,16 @@ schannel_recv(struct connectdata *conn, int sockindex,
       else if(sspi_status == SEC_I_CONTEXT_EXPIRED) {
         /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not
            returned so we have to work around that in cleanup. */
-        connssl->recv_sspi_close_notify = true;
-        if(!connssl->recv_connection_closed) {
-          connssl->recv_connection_closed = true;
+        BACKEND->recv_sspi_close_notify = true;
+        if(!BACKEND->recv_connection_closed) {
+          BACKEND->recv_connection_closed = true;
           infof(data, "schannel: server closed the connection\n");
         }
         goto cleanup;
       }
     }
     else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
+      BACKEND->encdata_is_incomplete = true;
       if(!*err)
         *err = CURLE_AGAIN;
       infof(data, "schannel: failed to decrypt data, need more data\n");
@@ -1292,10 +1436,10 @@ schannel_recv(struct connectdata *conn, int sockindex,
   }
 
   infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
-        connssl->encdata_offset, connssl->encdata_length);
+        BACKEND->encdata_offset, BACKEND->encdata_length);
 
   infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
-        connssl->decdata_offset, connssl->decdata_length);
+        BACKEND->decdata_offset, BACKEND->decdata_length);
 
 cleanup:
   /* Warning- there is no guarantee the encdata state is valid at this point */
@@ -1309,13 +1453,13 @@ cleanup:
   return close_notify. In that case if the connection was closed we assume it
   was graceful (close_notify) since there doesn't seem to be a way to tell.
   */
-  if(len && !connssl->decdata_offset && connssl->recv_connection_closed &&
-     !connssl->recv_sspi_close_notify) {
+  if(len && !BACKEND->decdata_offset && BACKEND->recv_connection_closed &&
+     !BACKEND->recv_sspi_close_notify) {
     bool isWin2k = Curl_verify_windows_version(5, 0, PLATFORM_WINNT,
                                                VERSION_EQUAL);
 
     if(isWin2k && sspi_status == SEC_E_OK)
-      connssl->recv_sspi_close_notify = true;
+      BACKEND->recv_sspi_close_notify = true;
     else {
       *err = CURLE_RECV_ERROR;
       infof(data, "schannel: server closed abruptly (missing close_notify)\n");
@@ -1324,23 +1468,23 @@ cleanup:
 
   /* Any error other than CURLE_AGAIN is an unrecoverable error. */
   if(*err && *err != CURLE_AGAIN)
-      connssl->recv_unrecoverable_err = *err;
+      BACKEND->recv_unrecoverable_err = *err;
 
-  size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
+  size = len < BACKEND->decdata_offset ? len : BACKEND->decdata_offset;
   if(size) {
-    memcpy(buf, connssl->decdata_buffer, size);
-    memmove(connssl->decdata_buffer, connssl->decdata_buffer + size,
-            connssl->decdata_offset - size);
-    connssl->decdata_offset -= size;
+    memcpy(buf, BACKEND->decdata_buffer, size);
+    memmove(BACKEND->decdata_buffer, BACKEND->decdata_buffer + size,
+            BACKEND->decdata_offset - size);
+    BACKEND->decdata_offset -= size;
 
     infof(data, "schannel: decrypted data returned %zu\n", size);
     infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
-          connssl->decdata_offset, connssl->decdata_length);
+          BACKEND->decdata_offset, BACKEND->decdata_length);
     *err = CURLE_OK;
     return (ssize_t)size;
   }
 
-  if(!*err && !connssl->recv_connection_closed)
+  if(!*err && !BACKEND->recv_connection_closed)
       *err = CURLE_AGAIN;
 
   /* It's debatable what to return when !len. We could return whatever error we
@@ -1352,15 +1496,13 @@ cleanup:
   return *err ? -1 : 0;
 }
 
-CURLcode
-Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
-                                  bool *done)
+static 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)
+static CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex)
 {
   CURLcode result;
   bool done = FALSE;
@@ -1374,38 +1516,51 @@ Curl_schannel_connect(struct connectdata *conn, int sockindex)
   return CURLE_OK;
 }
 
-bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
+static bool Curl_schannel_data_pending(const struct connectdata *conn,
+                                       int sockindex)
 {
   const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
 
   if(connssl->use) /* SSL/TLS is in use */
-    return (connssl->encdata_offset > 0 ||
-            connssl->decdata_offset > 0) ? TRUE : FALSE;
+    return (BACKEND->decdata_offset > 0 ||
+            (BACKEND->encdata_offset > 0 && !BACKEND->encdata_is_incomplete));
   else
     return FALSE;
 }
 
-void Curl_schannel_close(struct connectdata *conn, int sockindex)
+static void Curl_schannel_close(struct connectdata *conn, int sockindex)
 {
   if(conn->ssl[sockindex].use)
     /* if the SSL/TLS channel hasn't been shut down yet, do that now. */
     Curl_ssl_shutdown(conn, sockindex);
 }
 
-int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
+static void Curl_schannel_session_free(void *ptr)
+{
+  /* this is expected to be called under sessionid lock */
+  struct curl_schannel_cred *cred = ptr;
+
+  cred->refcount--;
+  if(cred->refcount == 0) {
+    s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
+    Curl_safefree(cred);
+  }
+}
+
+static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
 {
   /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
    * Shutting Down an Schannel Connection
    */
   struct Curl_easy *data = conn->data;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+  char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
     conn->host.name;
 
   infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
         hostname, conn->remote_port);
 
-  if(connssl->cred && connssl->ctxt) {
+  if(BACKEND->cred && BACKEND->ctxt) {
     SecBufferDesc BuffDesc;
     SecBuffer Buffer;
     SECURITY_STATUS sspi_status;
@@ -1418,7 +1573,7 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
     InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
     InitSecBufferDesc(&BuffDesc, &Buffer, 1);
 
-    sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle,
+    sspi_status = s_pSecFn->ApplyControlToken(&BACKEND->ctxt->ctxt_handle,
                                               &BuffDesc);
 
     if(sspi_status != SEC_E_OK)
@@ -1434,18 +1589,18 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
     InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
 
     sspi_status = s_pSecFn->InitializeSecurityContext(
-      &connssl->cred->cred_handle,
-      &connssl->ctxt->ctxt_handle,
+      &BACKEND->cred->cred_handle,
+      &BACKEND->ctxt->ctxt_handle,
       host_name,
-      connssl->req_flags,
+      BACKEND->req_flags,
       0,
       0,
       NULL,
       0,
-      &connssl->ctxt->ctxt_handle,
+      &BACKEND->ctxt->ctxt_handle,
       &outbuf_desc,
-      &connssl->ret_flags,
-      &connssl->ctxt->time_stamp);
+      &BACKEND->ret_flags,
+      &BACKEND->ctxt->time_stamp);
 
     Curl_unicodefree(host_name);
 
@@ -1464,70 +1619,62 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
   }
 
   /* free SSPI Schannel API security context handle */
-  if(connssl->ctxt) {
+  if(BACKEND->ctxt) {
     infof(data, "schannel: clear security context handle\n");
-    s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
-    Curl_safefree(connssl->ctxt);
+    s_pSecFn->DeleteSecurityContext(&BACKEND->ctxt->ctxt_handle);
+    Curl_safefree(BACKEND->ctxt);
   }
 
   /* free SSPI Schannel API credential handle */
-  if(connssl->cred) {
+  if(BACKEND->cred) {
     Curl_ssl_sessionid_lock(conn);
-    Curl_schannel_session_free(connssl->cred);
+    Curl_schannel_session_free(BACKEND->cred);
     Curl_ssl_sessionid_unlock(conn);
-    connssl->cred = NULL;
+    BACKEND->cred = NULL;
   }
 
   /* free internal buffer for received encrypted data */
-  if(connssl->encdata_buffer != NULL) {
-    Curl_safefree(connssl->encdata_buffer);
-    connssl->encdata_length = 0;
-    connssl->encdata_offset = 0;
+  if(BACKEND->encdata_buffer != NULL) {
+    Curl_safefree(BACKEND->encdata_buffer);
+    BACKEND->encdata_length = 0;
+    BACKEND->encdata_offset = 0;
+    BACKEND->encdata_is_incomplete = false;
   }
 
   /* free internal buffer for received decrypted data */
-  if(connssl->decdata_buffer != NULL) {
-    Curl_safefree(connssl->decdata_buffer);
-    connssl->decdata_length = 0;
-    connssl->decdata_offset = 0;
+  if(BACKEND->decdata_buffer != NULL) {
+    Curl_safefree(BACKEND->decdata_buffer);
+    BACKEND->decdata_length = 0;
+    BACKEND->decdata_offset = 0;
   }
 
   return CURLE_OK;
 }
 
-void Curl_schannel_session_free(void *ptr)
-{
-  /* this is expected to be called under sessionid lock */
-  struct curl_schannel_cred *cred = ptr;
-
-  cred->refcount--;
-  if(cred->refcount == 0) {
-    s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
-    Curl_safefree(cred);
-  }
-}
-
-int Curl_schannel_init(void)
+static int Curl_schannel_init(void)
 {
   return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
 }
 
-void Curl_schannel_cleanup(void)
+static void Curl_schannel_cleanup(void)
 {
   Curl_sspi_global_cleanup();
 }
 
-size_t Curl_schannel_version(char *buffer, size_t size)
+static size_t Curl_schannel_version(char *buffer, size_t size)
 {
   size = snprintf(buffer, size, "WinSSL");
 
   return size;
 }
 
-CURLcode Curl_schannel_random(unsigned char *entropy, size_t length)
+static CURLcode Curl_schannel_random(struct Curl_easy *data UNUSED_PARAM,
+                                     unsigned char *entropy, size_t length)
 {
   HCRYPTPROV hCryptProv = 0;
 
+  (void)data;
+
   if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
                           CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
     return CURLE_FAILED_INIT;
@@ -1541,6 +1688,68 @@ CURLcode Curl_schannel_random(unsigned char *entropy, size_t length)
   return CURLE_OK;
 }
 
+static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex,
+                                    const char *pinnedpubkey)
+{
+  SECURITY_STATUS status;
+  struct Curl_easy *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  CERT_CONTEXT *pCertContextServer = NULL;
+  const char *x509_der;
+  DWORD x509_der_len;
+  curl_X509certificate x509_parsed;
+  curl_asn1Element *pubkey;
+
+  /* Result is returned to caller */
+  CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+  /* if a path wasn't specified, don't pin */
+  if(!pinnedpubkey)
+    return CURLE_OK;
+
+  do {
+    status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+                                              SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+                                              &pCertContextServer);
+
+    if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
+      failf(data, "schannel: Failed to read remote certificate context: %s",
+            Curl_sspi_strerror(conn, status));
+      break; /* failed */
+    }
+
+
+    if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
+       (pCertContextServer->cbCertEncoded > 0)))
+      break;
+
+    x509_der = (const char *)pCertContextServer->pbCertEncoded;
+    x509_der_len = pCertContextServer->cbCertEncoded;
+    memset(&x509_parsed, 0, sizeof x509_parsed);
+    if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
+      break;
+
+    pubkey = &x509_parsed.subjectPublicKeyInfo;
+    if(!pubkey->header || pubkey->end <= pubkey->header) {
+      failf(data, "SSL: failed retrieving public key from server certificate");
+      break;
+    }
+
+    result = Curl_pin_peer_pubkey(data,
+                                  pinnedpubkey,
+                                  (const unsigned char *)pubkey->header,
+                                  (size_t)(pubkey->end - pubkey->header));
+    if(result) {
+      failf(data, "SSL: public key does not match pinned public key!");
+    }
+  } while(0);
+
+  if(pCertContextServer)
+    CertFreeCertificateContext(pCertContextServer);
+
+  return result;
+}
+
 #ifdef _WIN32_WCE
 static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
 {
@@ -1554,7 +1763,7 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
     conn->http_proxy.host.name :
     conn->host.name;
 
-  status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
+  status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
                                             SECPKG_ATTR_REMOTE_CERT_CONTEXT,
                                             &pCertContextServer);
 
@@ -1681,4 +1890,112 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
 }
 #endif /* _WIN32_WCE */
 
+static void Curl_schannel_checksum(const unsigned char *input,
+                      size_t inputlen,
+                      unsigned char *checksum,
+                      size_t checksumlen,
+                      DWORD provType,
+                      const unsigned int algId)
+{
+  HCRYPTPROV hProv = 0;
+  HCRYPTHASH hHash = 0;
+  DWORD cbHashSize = 0;
+  DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize);
+  DWORD dwChecksumLen = (DWORD)checksumlen;
+
+  /* since this can fail in multiple ways, zero memory first so we never
+   * return old data
+   */
+  memset(checksum, 0, checksumlen);
+
+  if(!CryptAcquireContext(&hProv, NULL, NULL, provType,
+                          CRYPT_VERIFYCONTEXT))
+    return; /* failed */
+
+  do {
+    if(!CryptCreateHash(hProv, algId, 0, 0, &hHash))
+      break; /* failed */
+
+    if(!CryptHashData(hHash, (const BYTE*)input, (DWORD)inputlen, 0))
+      break; /* failed */
+
+    /* get hash size */
+    if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize,
+                          &dwHashSizeLen, 0))
+      break; /* failed */
+
+    /* check hash size */
+    if(checksumlen < cbHashSize)
+      break; /* failed */
+
+    if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0))
+      break; /* failed */
+  } while(0);
+
+  if(hHash)
+    CryptDestroyHash(hHash);
+
+  if(hProv)
+    CryptReleaseContext(hProv, 0);
+}
+
+static CURLcode Curl_schannel_md5sum(unsigned char *input,
+                                     size_t inputlen,
+                                     unsigned char *md5sum,
+                                     size_t md5len)
+{
+    Curl_schannel_checksum(input, inputlen, md5sum, md5len,
+                           PROV_RSA_FULL, CALG_MD5);
+    return CURLE_OK;
+}
+
+static void Curl_schannel_sha256sum(const unsigned char *input,
+                                    size_t inputlen,
+                                    unsigned char *sha256sum,
+                                    size_t sha256len)
+{
+    Curl_schannel_checksum(input, inputlen, sha256sum, sha256len,
+                           PROV_RSA_AES, CALG_SHA_256);
+}
+
+static void *Curl_schannel_get_internals(struct ssl_connect_data *connssl,
+                                         CURLINFO info UNUSED_PARAM)
+{
+  (void)info;
+  return &BACKEND->ctxt->ctxt_handle;
+}
+
+const struct Curl_ssl Curl_ssl_schannel = {
+  { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */
+
+  0, /* have_ca_path */
+  1, /* have_certinfo */
+  1, /* have_pinnedpubkey */
+  0, /* have_ssl_ctx */
+  0, /* support_https_proxy */
+
+  sizeof(struct ssl_backend_data),
+
+  Curl_schannel_init,                /* init */
+  Curl_schannel_cleanup,             /* cleanup */
+  Curl_schannel_version,             /* version */
+  Curl_none_check_cxn,               /* check_cxn */
+  Curl_schannel_shutdown,            /* shutdown */
+  Curl_schannel_data_pending,        /* data_pending */
+  Curl_schannel_random,              /* random */
+  Curl_none_cert_status_request,     /* cert_status_request */
+  Curl_schannel_connect,             /* connect */
+  Curl_schannel_connect_nonblocking, /* connect_nonblocking */
+  Curl_schannel_get_internals,       /* get_internals */
+  Curl_schannel_close,               /* close_one */
+  Curl_none_close_all,               /* close_all */
+  Curl_schannel_session_free,        /* session_free */
+  Curl_none_set_engine,              /* set_engine */
+  Curl_none_set_engine_default,      /* set_engine_default */
+  Curl_none_engines_list,            /* engines_list */
+  Curl_none_false_start,             /* false_start */
+  Curl_schannel_md5sum,              /* md5sum */
+  Curl_schannel_sha256sum            /* sha256sum */
+};
+
 #endif /* USE_SCHANNEL */