Commit 8a882949 authored by Mark Salisbury's avatar Mark Salisbury Committed by Yang Tse
Browse files

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.
parent 1e4c57fa
Loading
Loading
Loading
Loading
+79 −12
Original line number Diff line number Diff line
@@ -41,7 +41,6 @@
/*
 * TODO list for TLS/SSL implementation:
 * - implement write buffering
 * - implement SSL/TLS shutdown
 * - implement client certificate authentication
 * - implement custom server certificate validation
 * - implement cipher/algorithm option
@@ -946,18 +945,91 @@ bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)

void Curl_schannel_close(struct connectdata *conn, int sockindex)
{
  if(conn->ssl[sockindex].use)
    /* if the SSL channel hasn't been shut down yet, do that now. */
    Curl_ssl_shutdown(conn, sockindex);
}

int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
{
/* 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: Closing connection with %s:%hu\n",
  infof(data, "schannel: shutting down SSL connection with %s:%hu\n",
        conn->host.name, conn->remote_port);

  if(connssl->ctxt) {
    SecBufferDesc BuffDesc;
    SecBuffer Buffer;
    SECURITY_STATUS sspi_status;
    SecBuffer outbuf;
    SecBufferDesc outbuf_desc;
    CURLcode code;
    TCHAR *host_name;
    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
    host_name = Curl_convert_UTF8_to_wchar(conn->host.name);
    if(!host_name)
      return CURLE_OUT_OF_MEMORY;
#else
    host_name = conn->host.name;
#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,
         host_name,
         connssl->req_flags,
         0,
         0,
         NULL,
         0,
         &connssl->ctxt->ctxt_handle,
         &outbuf_desc,
         &connssl->ret_flags,
         &connssl->ctxt->time_stamp);

#ifdef UNICODE
    free(host_name);
#endif

    if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
      /* send close message which is in output buffer */
      ssize_t written;
      code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
                              outbuf.cbBuffer, &written);

      s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
      if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) {
        infof(data, "schannel: failed to send close msg: %s"
              " (bytes written: %zd)\n", curl_easy_strerror(code), written);
      }
    }
    /* 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 */
  if(connssl->encdata_buffer != NULL) {
@@ -974,13 +1046,8 @@ void Curl_schannel_close(struct connectdata *conn, int sockindex)
    connssl->decdata_length = 0;
    connssl->decdata_offset = 0;
  }
}

int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
{
  (void)conn;
  (void)sockindex;
  return CURLE_NOT_BUILT_IN; /* TODO: implement SSL/TLS shutdown */
  return CURLE_OK;
}

void Curl_schannel_session_free(void *ptr)