2012-04-09 17:40:06 +04:00
|
|
|
/***************************************************************************
|
|
|
|
* _ _ ____ _
|
|
|
|
* Project ___| | | | _ \| |
|
|
|
|
* / __| | | | |_) | |
|
|
|
|
* | (__| |_| | _ <| |___
|
|
|
|
* \___|\___/|_| \_\_____|
|
|
|
|
*
|
2016-04-03 21:28:34 +03:00
|
|
|
* Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
|
2012-06-19 06:15:02 +04:00
|
|
|
* Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
|
2017-01-12 19:41:26 +03:00
|
|
|
* Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
|
2012-04-09 17:40:06 +04:00
|
|
|
*
|
|
|
|
* This software is licensed as described in the file COPYING, which
|
|
|
|
* you should have received as part of this distribution. The terms
|
2016-02-03 02:19:02 +03:00
|
|
|
* are also available at https://curl.haxx.se/docs/copyright.html.
|
2012-04-09 17:40:06 +04:00
|
|
|
*
|
|
|
|
* 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
|
2013-12-25 14:30:51 +04:00
|
|
|
* but vtls.c should ever call or use these functions.
|
2012-04-09 17:40:06 +04:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2013-01-04 05:50:28 +04:00
|
|
|
/*
|
|
|
|
* 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!
|
|
|
|
*/
|
|
|
|
|
2013-01-06 22:06:49 +04:00
|
|
|
#include "curl_setup.h"
|
2012-04-09 17:40:06 +04:00
|
|
|
|
|
|
|
#ifdef USE_SCHANNEL
|
|
|
|
|
2012-06-13 17:53:51 +04:00
|
|
|
#ifndef USE_WINDOWS_SSPI
|
|
|
|
# error "Can't compile SCHANNEL support without SSPI."
|
|
|
|
#endif
|
|
|
|
|
vtls: encapsulate SSL backend-specific data
So far, all of the SSL backends' private data has been declared as
part of the ssl_connect_data struct, in one big #if .. #elif .. #endif
block.
This can only work as long as the SSL backend is a compile-time option,
something we want to change in the next commits.
Therefore, let's encapsulate the exact data needed by each SSL backend
into a private struct, and let's avoid bleeding any SSL backend-specific
information into urldata.h. This is also necessary to allow multiple SSL
backends to be compiled in at the same time, as e.g. OpenSSL's and
CyaSSL's headers cannot be included in the same .c file.
To avoid too many malloc() calls, we simply append the private structs
to the connectdata struct in allocate_conn().
This requires us to take extra care of alignment issues: struct fields
often need to be aligned on certain boundaries e.g. 32-bit values need to
be stored at addresses that divide evenly by 4 (= 32 bit / 8
bit-per-byte).
We do that by assuming that no SSL backend's private data contains any
fields that need to be aligned on boundaries larger than `long long`
(typically 64-bit) would need. Under this assumption, we simply add a
dummy field of type `long long` to the `struct connectdata` struct. This
field will never be accessed but acts as a placeholder for the four
instances of ssl_backend_data instead. the size of each ssl_backend_data
struct is stored in the SSL backend-specific metadata, to allow
allocate_conn() to know how much extra space to allocate, and how to
initialize the ssl[sockindex]->backend and proxy_ssl[sockindex]->backend
pointers.
This would appear to be a little complicated at first, but is really
necessary to encapsulate the private data of each SSL backend correctly.
And we need to encapsulate thusly if we ever want to allow selecting
CyaSSL and OpenSSL at runtime, as their headers cannot be included within
the same .c file (there are just too many conflicting definitions and
declarations for that).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2017-07-28 23:09:35 +03:00
|
|
|
#include <schnlsp.h>
|
|
|
|
#include <schannel.h>
|
2012-04-09 17:40:06 +04:00
|
|
|
#include "curl_sspi.h"
|
2015-02-07 23:50:30 +03:00
|
|
|
#include "schannel.h"
|
2013-12-25 14:20:39 +04:00
|
|
|
#include "vtls.h"
|
2013-01-04 05:50:28 +04:00
|
|
|
#include "sendf.h"
|
|
|
|
#include "connect.h" /* for the connect timeout */
|
|
|
|
#include "strerror.h"
|
|
|
|
#include "select.h" /* for the socket readyness */
|
|
|
|
#include "inet_pton.h" /* for IP addr SNI check */
|
2012-06-15 20:05:11 +04:00
|
|
|
#include "curl_multibyte.h"
|
2013-01-04 05:50:28 +04:00
|
|
|
#include "warnless.h"
|
2016-06-01 09:48:30 +03:00
|
|
|
#include "x509asn1.h"
|
2015-03-04 01:17:43 +03:00
|
|
|
#include "curl_printf.h"
|
2016-06-04 22:58:39 +03:00
|
|
|
#include "system_win32.h"
|
2016-09-10 00:56:02 +03:00
|
|
|
#include "hostcheck.h"
|
2016-06-04 22:58:39 +03:00
|
|
|
|
|
|
|
/* The last #include file should be: */
|
2012-04-09 17:40:06 +04:00
|
|
|
#include "curl_memory.h"
|
2013-01-04 05:50:28 +04:00
|
|
|
#include "memdebug.h"
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2016-06-06 22:53:30 +03:00
|
|
|
/* ALPN requires version 8.1 of the Windows SDK, which was
|
|
|
|
shipped with Visual Studio 2013, aka _MSC_VER 1800:
|
|
|
|
|
|
|
|
https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
|
|
|
|
*/
|
2016-05-18 12:54:58 +03:00
|
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)
|
2016-03-21 09:59:57 +03:00
|
|
|
# define HAS_ALPN 1
|
|
|
|
#endif
|
|
|
|
|
2017-06-26 18:05:49 +03:00
|
|
|
#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
|
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
/* Uncomment to force verbose output
|
|
|
|
* #define infof(x, y, ...) printf(y, __VA_ARGS__)
|
|
|
|
* #define failf(x, y, ...) printf(y, __VA_ARGS__)
|
|
|
|
*/
|
|
|
|
|
vtls: encapsulate SSL backend-specific data
So far, all of the SSL backends' private data has been declared as
part of the ssl_connect_data struct, in one big #if .. #elif .. #endif
block.
This can only work as long as the SSL backend is a compile-time option,
something we want to change in the next commits.
Therefore, let's encapsulate the exact data needed by each SSL backend
into a private struct, and let's avoid bleeding any SSL backend-specific
information into urldata.h. This is also necessary to allow multiple SSL
backends to be compiled in at the same time, as e.g. OpenSSL's and
CyaSSL's headers cannot be included in the same .c file.
To avoid too many malloc() calls, we simply append the private structs
to the connectdata struct in allocate_conn().
This requires us to take extra care of alignment issues: struct fields
often need to be aligned on certain boundaries e.g. 32-bit values need to
be stored at addresses that divide evenly by 4 (= 32 bit / 8
bit-per-byte).
We do that by assuming that no SSL backend's private data contains any
fields that need to be aligned on boundaries larger than `long long`
(typically 64-bit) would need. Under this assumption, we simply add a
dummy field of type `long long` to the `struct connectdata` struct. This
field will never be accessed but acts as a placeholder for the four
instances of ssl_backend_data instead. the size of each ssl_backend_data
struct is stored in the SSL backend-specific metadata, to allow
allocate_conn() to know how much extra space to allocate, and how to
initialize the ssl[sockindex]->backend and proxy_ssl[sockindex]->backend
pointers.
This would appear to be a little complicated at first, but is really
necessary to encapsulate the private data of each SSL backend correctly.
And we need to encapsulate thusly if we ever want to allow selecting
CyaSSL and OpenSSL at runtime, as their headers cannot be included within
the same .c file (there are just too many conflicting definitions and
declarations for that).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2017-07-28 23:09:35 +03:00
|
|
|
/* 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
|
2017-06-21 13:41:18 +03:00
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
static Curl_recv schannel_recv;
|
|
|
|
static Curl_send schannel_send;
|
|
|
|
|
2012-06-19 06:15:02 +04:00
|
|
|
#ifdef _WIN32_WCE
|
|
|
|
static CURLcode verify_certificate(struct connectdata *conn, int sockindex);
|
|
|
|
#endif
|
|
|
|
|
2012-06-19 06:15:01 +04:00
|
|
|
static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
|
|
|
|
void *BufDataPtr, unsigned long BufByteSize)
|
|
|
|
{
|
|
|
|
buffer->cbBuffer = BufByteSize;
|
|
|
|
buffer->BufferType = BufType;
|
|
|
|
buffer->pvBuffer = BufDataPtr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
|
|
|
|
unsigned long NumArrElem)
|
|
|
|
{
|
|
|
|
desc->ulVersion = SECBUFFER_VERSION;
|
|
|
|
desc->pBuffers = BufArr;
|
|
|
|
desc->cBuffers = NumArrElem;
|
|
|
|
}
|
|
|
|
|
2016-12-13 23:10:00 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
static CURLcode
|
2012-04-15 06:12:26 +04:00
|
|
|
schannel_connect_step1(struct connectdata *conn, int sockindex)
|
|
|
|
{
|
2012-06-14 13:53:46 +04:00
|
|
|
ssize_t written = -1;
|
2016-06-21 16:47:12 +03:00
|
|
|
struct Curl_easy *data = conn->data;
|
2012-04-15 06:12:26 +04:00
|
|
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
2012-04-09 17:40:06 +04:00
|
|
|
SecBuffer outbuf;
|
|
|
|
SecBufferDesc outbuf_desc;
|
2016-03-21 09:59:57 +03:00
|
|
|
SecBuffer inbuf;
|
|
|
|
SecBufferDesc inbuf_desc;
|
|
|
|
#ifdef HAS_ALPN
|
|
|
|
unsigned char alpn_buffer[128];
|
|
|
|
#endif
|
2012-04-09 17:40:06 +04:00
|
|
|
SCHANNEL_CRED schannel_cred;
|
|
|
|
SECURITY_STATUS sspi_status = SEC_E_OK;
|
2012-04-15 06:12:26 +04:00
|
|
|
struct curl_schannel_cred *old_cred = NULL;
|
2012-04-09 17:40:06 +04:00
|
|
|
struct in_addr addr;
|
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
struct in6_addr addr6;
|
|
|
|
#endif
|
2012-06-16 21:20:50 +04:00
|
|
|
TCHAR *host_name;
|
2015-02-04 03:03:05 +03:00
|
|
|
CURLcode result;
|
2017-04-06 21:04:58 +03:00
|
|
|
char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
|
2016-11-21 13:01:25 +03:00
|
|
|
conn->host.name;
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-19 22:28:21 +04:00
|
|
|
infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
|
2016-11-21 13:01:25 +03:00
|
|
|
hostname, conn->remote_port);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2017-02-03 00:40:16 +03:00
|
|
|
if(Curl_verify_windows_version(5, 1, PLATFORM_WINNT,
|
|
|
|
VERSION_LESS_THAN_EQUAL)) {
|
|
|
|
/* SChannel in Windows XP (OS version 5.1) uses legacy handshakes and
|
|
|
|
algorithms that may not be supported by all servers. */
|
|
|
|
infof(data, "schannel: WinSSL version is old and may not be able to "
|
|
|
|
"connect to some servers due to lack of SNI, algorithms, etc.\n");
|
|
|
|
}
|
|
|
|
|
2016-08-26 06:57:56 +03:00
|
|
|
#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. */
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->use_alpn = conn->bits.tls_enable_alpn &&
|
2016-08-26 06:57:56 +03:00
|
|
|
!GetProcAddress(GetModuleHandleA("ntdll"),
|
|
|
|
"wine_get_version") &&
|
|
|
|
Curl_verify_windows_version(6, 3, PLATFORM_WINNT,
|
|
|
|
VERSION_GREATER_THAN_EQUAL);
|
|
|
|
#else
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->use_alpn = false;
|
2016-08-26 06:57:56 +03:00
|
|
|
#endif
|
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->cred = NULL;
|
2016-06-01 10:30:03 +03:00
|
|
|
|
2016-06-13 06:47:12 +03:00
|
|
|
/* check for an existing re-usable credential handle */
|
2017-03-22 08:59:49 +03:00
|
|
|
if(SSL_SET_OPTION(primary.sessionid)) {
|
2016-06-13 06:47:12 +03:00
|
|
|
Curl_ssl_sessionid_lock(conn);
|
2016-11-16 20:49:15 +03:00
|
|
|
if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL, sockindex)) {
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->cred = old_cred;
|
2016-06-13 06:47:12 +03:00
|
|
|
infof(data, "schannel: re-using existing credential handle\n");
|
2016-06-01 10:30:03 +03:00
|
|
|
|
2016-06-13 06:47:12 +03:00
|
|
|
/* increment the reference counter of the credential/session handle */
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->cred->refcount++;
|
2016-06-13 06:47:12 +03:00
|
|
|
infof(data, "schannel: incremented credential handle refcount = %d\n",
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->cred->refcount);
|
2016-06-13 06:47:12 +03:00
|
|
|
}
|
2016-06-01 10:30:03 +03:00
|
|
|
Curl_ssl_sessionid_unlock(conn);
|
2012-04-09 20:35:00 +04:00
|
|
|
}
|
2016-06-01 10:30:03 +03:00
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
if(!BACKEND->cred) {
|
2012-04-10 01:24:55 +04:00
|
|
|
/* setup Schannel API options */
|
|
|
|
memset(&schannel_cred, 0, sizeof(schannel_cred));
|
|
|
|
schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
|
|
|
|
|
2016-11-16 20:49:15 +03:00
|
|
|
if(conn->ssl_config.verifypeer) {
|
2012-06-19 06:15:02 +04:00
|
|
|
#ifdef _WIN32_WCE
|
|
|
|
/* certificate validation on CE doesn't seem to work right; we'll
|
|
|
|
do it following a more manual process. */
|
|
|
|
schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
|
2015-04-26 18:40:40 +03:00
|
|
|
SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
|
|
|
|
SCH_CRED_IGNORE_REVOCATION_OFFLINE;
|
2012-06-19 06:15:02 +04:00
|
|
|
#else
|
2015-07-17 09:40:16 +03:00
|
|
|
schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
|
2016-11-16 20:49:15 +03:00
|
|
|
/* TODO s/data->set.ssl.no_revoke/SSL_SET_OPTION(no_revoke)/g */
|
|
|
|
if(data->set.ssl.no_revoke)
|
2015-07-17 09:40:16 +03:00
|
|
|
schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
|
|
|
|
SCH_CRED_IGNORE_REVOCATION_OFFLINE;
|
|
|
|
else
|
|
|
|
schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
|
2012-06-19 06:15:02 +04:00
|
|
|
#endif
|
2016-11-16 20:49:15 +03:00
|
|
|
if(data->set.ssl.no_revoke)
|
2015-07-17 09:40:16 +03:00
|
|
|
infof(data, "schannel: disabled server certificate revocation "
|
|
|
|
"checks\n");
|
|
|
|
else
|
|
|
|
infof(data, "schannel: checking server certificate revocation\n");
|
2012-04-10 01:24:55 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
|
2015-04-26 18:40:40 +03:00
|
|
|
SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
|
|
|
|
SCH_CRED_IGNORE_REVOCATION_OFFLINE;
|
2015-07-17 09:40:16 +03:00
|
|
|
infof(data, "schannel: disabled server certificate revocation checks\n");
|
2012-04-10 01:24:55 +04:00
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2016-11-16 20:49:15 +03:00
|
|
|
if(!conn->ssl_config.verifyhost) {
|
2012-11-03 05:06:51 +04:00
|
|
|
schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
|
|
|
|
infof(data, "schannel: verifyhost setting prevents Schannel from "
|
2015-04-26 18:40:40 +03:00
|
|
|
"comparing the supplied target name with the subject "
|
2017-02-03 00:40:16 +03:00
|
|
|
"names in server certificates.\n");
|
2012-04-10 01:24:55 +04:00
|
|
|
}
|
|
|
|
|
2016-11-16 20:49:15 +03:00
|
|
|
switch(conn->ssl_config.version) {
|
2015-04-26 18:40:40 +03:00
|
|
|
case CURL_SSLVERSION_DEFAULT:
|
|
|
|
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_TLSv1_0:
|
|
|
|
case CURL_SSLVERSION_TLSv1_1:
|
|
|
|
case CURL_SSLVERSION_TLSv1_2:
|
2016-10-27 15:57:11 +03:00
|
|
|
case CURL_SSLVERSION_TLSv1_3:
|
2016-12-13 23:10:00 +03:00
|
|
|
{
|
2017-03-28 02:16:12 +03:00
|
|
|
result = set_ssl_version_min_max(&schannel_cred, conn);
|
2016-12-13 23:10:00 +03:00
|
|
|
if(result != CURLE_OK)
|
|
|
|
return result;
|
|
|
|
break;
|
|
|
|
}
|
2015-04-26 18:40:40 +03:00
|
|
|
case CURL_SSLVERSION_SSLv3:
|
|
|
|
schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
|
|
|
|
break;
|
|
|
|
case CURL_SSLVERSION_SSLv2:
|
|
|
|
schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
|
|
|
|
break;
|
2016-11-08 05:51:27 +03:00
|
|
|
default:
|
|
|
|
failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
|
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
2012-04-10 01:24:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate memory for the re-usable credential handle */
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->cred = (struct curl_schannel_cred *)
|
2015-04-26 18:40:40 +03:00
|
|
|
malloc(sizeof(struct curl_schannel_cred));
|
2017-06-21 13:41:18 +03:00
|
|
|
if(!BACKEND->cred) {
|
2012-04-10 01:24:55 +04:00
|
|
|
failf(data, "schannel: unable to allocate memory");
|
|
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
}
|
2017-06-21 13:41:18 +03:00
|
|
|
memset(BACKEND->cred, 0, sizeof(struct curl_schannel_cred));
|
|
|
|
BACKEND->cred->refcount = 1;
|
2012-04-10 01:24:55 +04:00
|
|
|
|
2016-02-03 07:09:25 +03:00
|
|
|
/* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx
|
|
|
|
*/
|
2015-04-26 18:40:40 +03:00
|
|
|
sspi_status =
|
|
|
|
s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
|
|
|
|
SECPKG_CRED_OUTBOUND, NULL,
|
|
|
|
&schannel_cred, NULL, NULL,
|
2017-06-21 13:41:18 +03:00
|
|
|
&BACKEND->cred->cred_handle,
|
|
|
|
&BACKEND->cred->time_stamp);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-04-10 01:24:55 +04:00
|
|
|
if(sspi_status != SEC_E_OK) {
|
|
|
|
if(sspi_status == SEC_E_WRONG_PRINCIPAL)
|
2012-06-14 15:32:05 +04:00
|
|
|
failf(data, "schannel: SNI or certificate check failed: %s",
|
2012-06-12 03:06:48 +04:00
|
|
|
Curl_sspi_strerror(conn, sspi_status));
|
2012-04-10 01:24:55 +04:00
|
|
|
else
|
2012-06-15 20:05:11 +04:00
|
|
|
failf(data, "schannel: AcquireCredentialsHandle failed: %s",
|
2012-06-12 03:06:48 +04:00
|
|
|
Curl_sspi_strerror(conn, sspi_status));
|
2017-06-21 13:41:18 +03:00
|
|
|
Curl_safefree(BACKEND->cred);
|
2012-04-10 01:24:55 +04:00
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
}
|
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2014-02-23 21:38:58 +04:00
|
|
|
/* Warn if SNI is disabled due to use of an IP address */
|
2016-11-21 13:01:25 +03:00
|
|
|
if(Curl_inet_pton(AF_INET, hostname, &addr)
|
2014-02-23 21:38:58 +04:00
|
|
|
#ifdef ENABLE_IPV6
|
2016-11-21 13:01:25 +03:00
|
|
|
|| Curl_inet_pton(AF_INET6, hostname, &addr6)
|
2014-02-23 21:38:58 +04:00
|
|
|
#endif
|
|
|
|
) {
|
|
|
|
infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
|
|
|
|
}
|
|
|
|
|
2016-03-21 09:59:57 +03:00
|
|
|
#ifdef HAS_ALPN
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->use_alpn) {
|
2016-03-21 09:59:57 +03:00
|
|
|
int cur = 0;
|
|
|
|
int list_start_index = 0;
|
2016-11-23 09:53:24 +03:00
|
|
|
unsigned int *extension_len = NULL;
|
2016-03-21 09:59:57 +03:00
|
|
|
unsigned short* list_len = NULL;
|
|
|
|
|
|
|
|
/* The first four bytes will be an unsigned int indicating number
|
|
|
|
of bytes of data in the rest of the the buffer. */
|
2016-11-23 10:30:18 +03:00
|
|
|
extension_len = (unsigned int *)(&alpn_buffer[cur]);
|
2016-03-21 09:59:57 +03:00
|
|
|
cur += sizeof(unsigned int);
|
|
|
|
|
|
|
|
/* The next four bytes are an indicator that this buffer will contain
|
|
|
|
ALPN data, as opposed to NPN, for example. */
|
2016-11-23 10:30:18 +03:00
|
|
|
*(unsigned int *)&alpn_buffer[cur] =
|
2016-03-21 09:59:57 +03:00
|
|
|
SecApplicationProtocolNegotiationExt_ALPN;
|
|
|
|
cur += sizeof(unsigned int);
|
|
|
|
|
|
|
|
/* The next two bytes will be an unsigned short indicating the number
|
|
|
|
of bytes used to list the preferred protocols. */
|
|
|
|
list_len = (unsigned short*)(&alpn_buffer[cur]);
|
|
|
|
cur += sizeof(unsigned short);
|
|
|
|
|
|
|
|
list_start_index = cur;
|
|
|
|
|
|
|
|
#ifdef USE_NGHTTP2
|
|
|
|
if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
|
|
|
|
memcpy(&alpn_buffer[cur], NGHTTP2_PROTO_ALPN, NGHTTP2_PROTO_ALPN_LEN);
|
|
|
|
cur += NGHTTP2_PROTO_ALPN_LEN;
|
|
|
|
infof(data, "schannel: ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH;
|
|
|
|
memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
|
|
|
|
cur += ALPN_HTTP_1_1_LENGTH;
|
|
|
|
infof(data, "schannel: ALPN, offering %s\n", ALPN_HTTP_1_1);
|
|
|
|
|
2016-03-26 14:14:07 +03:00
|
|
|
*list_len = curlx_uitous(cur - list_start_index);
|
2016-03-21 09:59:57 +03:00
|
|
|
*extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short);
|
|
|
|
|
|
|
|
InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur);
|
|
|
|
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
|
|
|
|
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
|
|
|
|
}
|
|
|
|
#else /* HAS_ALPN */
|
|
|
|
InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
|
|
|
|
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
|
|
|
|
#endif
|
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
/* setup output buffer */
|
2012-06-19 06:15:01 +04:00
|
|
|
InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
|
|
|
|
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
|
|
|
/* setup request flags */
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
|
2015-04-26 18:40:40 +03:00
|
|
|
ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
|
|
|
|
ISC_REQ_STREAM;
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-04-10 01:24:55 +04:00
|
|
|
/* allocate memory for the security context handle */
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->ctxt = (struct curl_schannel_ctxt *)
|
2015-04-26 18:40:40 +03:00
|
|
|
malloc(sizeof(struct curl_schannel_ctxt));
|
2017-06-21 13:41:18 +03:00
|
|
|
if(!BACKEND->ctxt) {
|
2012-04-10 01:24:55 +04:00
|
|
|
failf(data, "schannel: unable to allocate memory");
|
|
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
}
|
2017-06-21 13:41:18 +03:00
|
|
|
memset(BACKEND->ctxt, 0, sizeof(struct curl_schannel_ctxt));
|
2012-04-10 01:24:55 +04:00
|
|
|
|
2016-11-21 13:01:25 +03:00
|
|
|
host_name = Curl_convert_UTF8_to_tchar(hostname);
|
2012-06-15 20:05:11 +04:00
|
|
|
if(!host_name)
|
|
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
|
2016-08-26 06:57:56 +03:00
|
|
|
/* Schannel InitializeSecurityContext:
|
|
|
|
https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
|
2012-06-15 20:05:11 +04:00
|
|
|
|
2016-08-26 06:57:56 +03:00
|
|
|
At the moment we don't pass inbuf unless we're using ALPN since we only
|
|
|
|
use it for that, and Wine (for which we currently disable ALPN) is giving
|
|
|
|
us problems with inbuf regardless. https://github.com/curl/curl/issues/983
|
|
|
|
*/
|
2012-04-13 15:10:09 +04:00
|
|
|
sspi_status = s_pSecFn->InitializeSecurityContext(
|
2017-06-21 13:41:18 +03:00
|
|
|
&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);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-07-06 00:16:15 +04:00
|
|
|
Curl_unicodefree(host_name);
|
2012-06-15 20:05:11 +04:00
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
if(sspi_status != SEC_I_CONTINUE_NEEDED) {
|
|
|
|
if(sspi_status == SEC_E_WRONG_PRINCIPAL)
|
2012-06-14 15:32:05 +04:00
|
|
|
failf(data, "schannel: SNI or certificate check failed: %s",
|
2012-06-12 03:06:48 +04:00
|
|
|
Curl_sspi_strerror(conn, sspi_status));
|
2012-04-09 17:40:06 +04:00
|
|
|
else
|
2012-06-15 20:05:11 +04:00
|
|
|
failf(data, "schannel: initial InitializeSecurityContext failed: %s",
|
2012-06-12 03:06:48 +04:00
|
|
|
Curl_sspi_strerror(conn, sspi_status));
|
2017-06-21 13:41:18 +03:00
|
|
|
Curl_safefree(BACKEND->ctxt);
|
2012-04-09 17:40:06 +04:00
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
}
|
|
|
|
|
2012-06-14 18:55:26 +04:00
|
|
|
infof(data, "schannel: sending initial handshake data: "
|
|
|
|
"sending %lu bytes...\n", outbuf.cbBuffer);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
|
|
|
/* send initial handshake data which is now stored in output buffer */
|
2015-02-04 03:03:05 +03:00
|
|
|
result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
|
|
|
|
outbuf.cbBuffer, &written);
|
2012-04-09 17:40:06 +04:00
|
|
|
s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
|
2015-02-04 03:03:05 +03:00
|
|
|
if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
|
2012-06-14 18:55:26 +04:00
|
|
|
failf(data, "schannel: failed to send initial handshake data: "
|
|
|
|
"sent %zd of %lu bytes", written, outbuf.cbBuffer);
|
2012-04-09 17:40:06 +04:00
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
}
|
|
|
|
|
2012-06-14 18:55:26 +04:00
|
|
|
infof(data, "schannel: sent initial handshake data: "
|
|
|
|
"sent %zd bytes\n", written);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->recv_unrecoverable_err = CURLE_OK;
|
|
|
|
BACKEND->recv_sspi_close_notify = false;
|
|
|
|
BACKEND->recv_connection_closed = false;
|
|
|
|
BACKEND->encdata_is_incomplete = false;
|
2015-06-17 07:17:03 +03:00
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
/* continue to second handshake step */
|
|
|
|
connssl->connecting_state = ssl_connect_2;
|
|
|
|
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CURLcode
|
2012-04-15 06:12:26 +04:00
|
|
|
schannel_connect_step2(struct connectdata *conn, int sockindex)
|
|
|
|
{
|
2012-04-09 17:40:06 +04:00
|
|
|
int i;
|
2012-06-14 13:53:46 +04:00
|
|
|
ssize_t nread = -1, written = -1;
|
2016-06-21 16:47:12 +03:00
|
|
|
struct Curl_easy *data = conn->data;
|
2012-04-15 06:12:26 +04:00
|
|
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
2014-12-14 19:27:20 +03:00
|
|
|
unsigned char *reallocated_buffer;
|
|
|
|
size_t reallocated_length;
|
2015-04-30 08:43:45 +03:00
|
|
|
SecBuffer outbuf[3];
|
2012-04-09 17:40:06 +04:00
|
|
|
SecBufferDesc outbuf_desc;
|
|
|
|
SecBuffer inbuf[2];
|
|
|
|
SecBufferDesc inbuf_desc;
|
|
|
|
SECURITY_STATUS sspi_status = SEC_E_OK;
|
2012-06-16 21:20:50 +04:00
|
|
|
TCHAR *host_name;
|
2015-02-04 03:03:05 +03:00
|
|
|
CURLcode result;
|
2012-06-21 00:26:51 +04:00
|
|
|
bool doread;
|
2017-04-06 21:04:58 +03:00
|
|
|
char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
|
2016-11-21 13:01:25 +03:00
|
|
|
conn->host.name;
|
2012-06-21 00:26:51 +04:00
|
|
|
|
2012-10-06 15:59:28 +04:00
|
|
|
doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-19 22:28:21 +04:00
|
|
|
infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
|
2016-11-21 13:01:25 +03:00
|
|
|
hostname, conn->remote_port);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
if(!BACKEND->cred || !BACKEND->ctxt)
|
2014-04-19 00:36:12 +04:00
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
2015-06-17 07:17:03 +03:00
|
|
|
/* buffer to store previously received and decrypted data */
|
2017-06-21 13:41:18 +03:00
|
|
|
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) {
|
2015-06-17 07:17:03 +03:00
|
|
|
failf(data, "schannel: unable to allocate memory");
|
|
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
/* buffer to store previously received and encrypted data */
|
2017-06-21 13:41:18 +03:00
|
|
|
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) {
|
2012-04-09 17:40:06 +04:00
|
|
|
failf(data, "schannel: unable to allocate memory");
|
|
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
/* if we need a bigger buffer to read a full message, increase buffer now */
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->encdata_length - BACKEND->encdata_offset <
|
2012-10-06 15:59:28 +04:00
|
|
|
CURL_SCHANNEL_BUFFER_FREE_SIZE) {
|
2012-06-20 02:51:03 +04:00
|
|
|
/* increase internal encrypted data buffer */
|
2017-06-21 13:41:18 +03:00
|
|
|
reallocated_length = BACKEND->encdata_offset +
|
2015-04-26 18:40:40 +03:00
|
|
|
CURL_SCHANNEL_BUFFER_FREE_SIZE;
|
2017-06-21 13:41:18 +03:00
|
|
|
reallocated_buffer = realloc(BACKEND->encdata_buffer,
|
2014-12-14 19:27:20 +03:00
|
|
|
reallocated_length);
|
2012-06-20 09:49:34 +04:00
|
|
|
|
2014-12-14 19:27:20 +03:00
|
|
|
if(reallocated_buffer == NULL) {
|
2012-06-20 02:51:03 +04:00
|
|
|
failf(data, "schannel: unable to re-allocate memory");
|
|
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
}
|
2014-12-14 19:27:20 +03:00
|
|
|
else {
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_buffer = reallocated_buffer;
|
|
|
|
BACKEND->encdata_length = reallocated_length;
|
2014-12-14 19:27:20 +03:00
|
|
|
}
|
2012-06-20 02:51:03 +04:00
|
|
|
}
|
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
for(;;) {
|
2012-06-20 02:51:03 +04:00
|
|
|
if(doread) {
|
|
|
|
/* read encrypted handshake data from socket */
|
2015-02-04 03:03:05 +03:00
|
|
|
result = Curl_read_plain(conn->sock[sockindex],
|
2017-06-21 13:41:18 +03:00
|
|
|
(char *) (BACKEND->encdata_buffer +
|
|
|
|
BACKEND->encdata_offset),
|
|
|
|
BACKEND->encdata_length -
|
|
|
|
BACKEND->encdata_offset,
|
2015-02-04 03:03:05 +03:00
|
|
|
&nread);
|
|
|
|
if(result == CURLE_AGAIN) {
|
2012-06-20 02:51:03 +04:00
|
|
|
if(connssl->connecting_state != ssl_connect_2_writing)
|
|
|
|
connssl->connecting_state = ssl_connect_2_reading;
|
|
|
|
infof(data, "schannel: failed to receive handshake, "
|
|
|
|
"need more data\n");
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
2015-02-04 03:03:05 +03:00
|
|
|
else if((result != CURLE_OK) || (nread == 0)) {
|
2012-06-20 02:51:03 +04:00
|
|
|
failf(data, "schannel: failed to receive handshake, "
|
|
|
|
"SSL/TLS connection failed");
|
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
/* increase encrypted data buffer offset */
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_offset += nread;
|
|
|
|
BACKEND->encdata_is_incomplete = false;
|
2017-04-06 10:27:28 +03:00
|
|
|
infof(data, "schannel: encrypted data got %zd\n", nread);
|
2012-06-20 02:51:03 +04:00
|
|
|
}
|
2012-06-19 22:28:22 +04:00
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_offset, BACKEND->encdata_length);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
/* setup input buffers */
|
2017-06-21 13:41:18 +03:00
|
|
|
InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(BACKEND->encdata_offset),
|
|
|
|
curlx_uztoul(BACKEND->encdata_offset));
|
2012-06-20 02:51:03 +04:00
|
|
|
InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
|
|
|
|
InitSecBufferDesc(&inbuf_desc, inbuf, 2);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
/* setup output buffers */
|
|
|
|
InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
|
|
|
|
InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
|
2015-04-26 18:59:01 +03:00
|
|
|
InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
|
|
|
|
InitSecBufferDesc(&outbuf_desc, outbuf, 3);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
if(inbuf[0].pvBuffer == NULL) {
|
|
|
|
failf(data, "schannel: unable to allocate memory");
|
|
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
/* copy received handshake data into input buffer */
|
2017-06-21 13:41:18 +03:00
|
|
|
memcpy(inbuf[0].pvBuffer, BACKEND->encdata_buffer,
|
|
|
|
BACKEND->encdata_offset);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2016-11-21 13:01:25 +03:00
|
|
|
host_name = Curl_convert_UTF8_to_tchar(hostname);
|
2012-06-20 02:51:03 +04:00
|
|
|
if(!host_name)
|
|
|
|
return CURLE_OUT_OF_MEMORY;
|
2012-06-15 20:05:11 +04:00
|
|
|
|
2016-02-03 07:09:25 +03:00
|
|
|
/* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
|
|
|
|
*/
|
2012-06-20 02:51:03 +04:00
|
|
|
sspi_status = s_pSecFn->InitializeSecurityContext(
|
2017-06-21 13:41:18 +03:00
|
|
|
&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);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-07-06 00:16:15 +04:00
|
|
|
Curl_unicodefree(host_name);
|
2012-06-15 20:05:11 +04:00
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
/* free buffer for received handshake data */
|
2012-06-25 09:16:26 +04:00
|
|
|
Curl_safefree(inbuf[0].pvBuffer);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
/* check if the handshake was incomplete */
|
|
|
|
if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_is_incomplete = true;
|
2012-06-20 02:51:03 +04:00
|
|
|
connssl->connecting_state = ssl_connect_2_reading;
|
|
|
|
infof(data, "schannel: received incomplete message, need more data\n");
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2015-06-11 22:52:25 +03:00
|
|
|
/* If the server has requested a client certificate, attempt to continue
|
|
|
|
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 &&
|
2017-06-21 13:41:18 +03:00
|
|
|
!(BACKEND->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
|
|
|
|
BACKEND->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
|
2015-06-11 22:52:25 +03:00
|
|
|
connssl->connecting_state = ssl_connect_2_writing;
|
|
|
|
infof(data, "schannel: a client certificate has been requested\n");
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
/* check if the handshake needs to be continued */
|
|
|
|
if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
|
2015-05-01 10:39:34 +03:00
|
|
|
for(i = 0; i < 3; i++) {
|
2012-06-20 02:51:03 +04:00
|
|
|
/* 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: "
|
|
|
|
"sending %lu bytes...\n", outbuf[i].cbBuffer);
|
|
|
|
|
|
|
|
/* send handshake token to server */
|
2015-02-04 03:03:05 +03:00
|
|
|
result = Curl_write_plain(conn, conn->sock[sockindex],
|
|
|
|
outbuf[i].pvBuffer, outbuf[i].cbBuffer,
|
|
|
|
&written);
|
|
|
|
if((result != CURLE_OK) ||
|
|
|
|
(outbuf[i].cbBuffer != (size_t) written)) {
|
2012-06-20 02:51:03 +04:00
|
|
|
failf(data, "schannel: failed to send next handshake data: "
|
|
|
|
"sent %zd of %lu bytes", written, outbuf[i].cbBuffer);
|
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
/* free obsolete buffer */
|
|
|
|
if(outbuf[i].pvBuffer != NULL) {
|
|
|
|
s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
|
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
}
|
2012-06-20 02:51:03 +04:00
|
|
|
else {
|
|
|
|
if(sspi_status == SEC_E_WRONG_PRINCIPAL)
|
|
|
|
failf(data, "schannel: SNI or certificate check failed: %s",
|
|
|
|
Curl_sspi_strerror(conn, sspi_status));
|
|
|
|
else
|
|
|
|
failf(data, "schannel: next InitializeSecurityContext failed: %s",
|
|
|
|
Curl_sspi_strerror(conn, sspi_status));
|
2017-05-04 19:19:13 +03:00
|
|
|
return sspi_status == SEC_E_UNTRUSTED_ROOT ?
|
2017-09-04 01:02:29 +03:00
|
|
|
CURLE_SSL_CACERT : CURLE_SSL_CONNECT_ERROR;
|
2012-06-20 02:51:03 +04:00
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-20 02:51:03 +04:00
|
|
|
/* check if there was additional remaining encrypted data */
|
|
|
|
if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
|
|
|
|
infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer);
|
|
|
|
/*
|
2015-04-26 18:40:40 +03:00
|
|
|
There are two cases where we could be getting extra data here:
|
|
|
|
1) If we're renegotiating a connection and the handshake is already
|
|
|
|
complete (from the server perspective), it can encrypted app data
|
|
|
|
(not handshake data) in an extra buffer at this point.
|
|
|
|
2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
|
|
|
|
connection and this extra data is part of the handshake.
|
|
|
|
We should process the data immediately; waiting for the socket to
|
|
|
|
be ready may fail since the server is done sending handshake data.
|
|
|
|
*/
|
2012-06-20 02:51:03 +04:00
|
|
|
/* check if the remaining data is less than the total amount
|
|
|
|
and therefore begins after the already processed data */
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->encdata_offset > inbuf[1].cbBuffer) {
|
|
|
|
memmove(BACKEND->encdata_buffer,
|
|
|
|
(BACKEND->encdata_buffer + BACKEND->encdata_offset) -
|
2015-04-26 18:40:40 +03:00
|
|
|
inbuf[1].cbBuffer, inbuf[1].cbBuffer);
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_offset = inbuf[1].cbBuffer;
|
2012-06-20 02:51:03 +04:00
|
|
|
if(sspi_status == SEC_I_CONTINUE_NEEDED) {
|
|
|
|
doread = FALSE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
2012-06-20 02:51:03 +04:00
|
|
|
else {
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_offset = 0;
|
2012-06-20 02:51:03 +04:00
|
|
|
}
|
|
|
|
break;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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) {
|
|
|
|
connssl->connecting_state = ssl_connect_3;
|
2012-06-19 22:28:21 +04:00
|
|
|
infof(data, "schannel: SSL/TLS handshake complete\n");
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
2012-06-19 06:15:02 +04:00
|
|
|
#ifdef _WIN32_WCE
|
|
|
|
/* Windows CE doesn't do any server certificate validation.
|
|
|
|
We have to do it manually. */
|
2016-11-16 20:49:15 +03:00
|
|
|
if(conn->ssl_config.verifypeer)
|
2012-06-19 06:15:02 +04:00
|
|
|
return verify_certificate(conn, sockindex);
|
|
|
|
#endif
|
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CURLcode
|
2012-04-15 06:12:26 +04:00
|
|
|
schannel_connect_step3(struct connectdata *conn, int sockindex)
|
|
|
|
{
|
2014-12-25 14:50:15 +03:00
|
|
|
CURLcode result = CURLE_OK;
|
2016-06-21 16:47:12 +03:00
|
|
|
struct Curl_easy *data = conn->data;
|
2012-04-09 17:40:06 +04:00
|
|
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
2016-03-21 09:59:57 +03:00
|
|
|
SECURITY_STATUS sspi_status = SEC_E_OK;
|
2016-06-01 09:48:30 +03:00
|
|
|
CERT_CONTEXT *ccert_context = NULL;
|
2017-03-30 11:35:46 +03:00
|
|
|
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
2016-11-21 13:01:25 +03:00
|
|
|
const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
|
|
|
|
conn->host.name;
|
2017-03-30 11:35:46 +03:00
|
|
|
#endif
|
2016-06-01 09:48:30 +03:00
|
|
|
#ifdef HAS_ALPN
|
2016-03-21 09:59:57 +03:00
|
|
|
SecPkgContext_ApplicationProtocol alpn_result;
|
|
|
|
#endif
|
2012-04-09 17:40:06 +04:00
|
|
|
|
|
|
|
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
|
|
|
|
|
2012-06-19 22:28:21 +04:00
|
|
|
infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
|
2016-11-21 13:01:25 +03:00
|
|
|
hostname, conn->remote_port);
|
2012-04-10 23:21:31 +04:00
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
if(!BACKEND->cred)
|
2014-04-19 00:36:12 +04:00
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
2012-04-10 01:24:55 +04:00
|
|
|
/* check if the required context attributes are met */
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->ret_flags != BACKEND->req_flags) {
|
|
|
|
if(!(BACKEND->ret_flags & ISC_RET_SEQUENCE_DETECT))
|
2012-06-14 15:32:05 +04:00
|
|
|
failf(data, "schannel: failed to setup sequence detection");
|
2017-06-21 13:41:18 +03:00
|
|
|
if(!(BACKEND->ret_flags & ISC_RET_REPLAY_DETECT))
|
2012-06-14 15:32:05 +04:00
|
|
|
failf(data, "schannel: failed to setup replay detection");
|
2017-06-21 13:41:18 +03:00
|
|
|
if(!(BACKEND->ret_flags & ISC_RET_CONFIDENTIALITY))
|
2012-06-14 15:32:05 +04:00
|
|
|
failf(data, "schannel: failed to setup confidentiality");
|
2017-06-21 13:41:18 +03:00
|
|
|
if(!(BACKEND->ret_flags & ISC_RET_ALLOCATED_MEMORY))
|
2012-06-14 15:32:05 +04:00
|
|
|
failf(data, "schannel: failed to setup memory allocation");
|
2017-06-21 13:41:18 +03:00
|
|
|
if(!(BACKEND->ret_flags & ISC_RET_STREAM))
|
2012-06-14 15:32:05 +04:00
|
|
|
failf(data, "schannel: failed to setup stream orientation");
|
2012-04-10 00:33:58 +04:00
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
}
|
2016-03-21 09:59:57 +03:00
|
|
|
|
|
|
|
#ifdef HAS_ALPN
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->use_alpn) {
|
|
|
|
sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
|
2016-03-21 09:59:57 +03:00
|
|
|
SECPKG_ATTR_APPLICATION_PROTOCOL, &alpn_result);
|
|
|
|
|
|
|
|
if(sspi_status != SEC_E_OK) {
|
|
|
|
failf(data, "schannel: failed to retrieve ALPN result");
|
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(alpn_result.ProtoNegoStatus ==
|
|
|
|
SecApplicationProtocolNegotiationStatus_Success) {
|
|
|
|
|
|
|
|
infof(data, "schannel: ALPN, server accepted to use %.*s\n",
|
|
|
|
alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
|
|
|
|
|
|
|
|
#ifdef USE_NGHTTP2
|
|
|
|
if(alpn_result.ProtocolIdSize == NGHTTP2_PROTO_VERSION_ID_LEN &&
|
|
|
|
!memcmp(NGHTTP2_PROTO_VERSION_ID, alpn_result.ProtocolId,
|
|
|
|
NGHTTP2_PROTO_VERSION_ID_LEN)) {
|
|
|
|
conn->negnpn = CURL_HTTP_VERSION_2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH &&
|
|
|
|
!memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId,
|
|
|
|
ALPN_HTTP_1_1_LENGTH)) {
|
|
|
|
conn->negnpn = CURL_HTTP_VERSION_1_1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
infof(data, "ALPN, server did not agree to a protocol\n");
|
|
|
|
}
|
|
|
|
#endif
|
2012-04-10 00:33:58 +04:00
|
|
|
|
2012-04-10 01:24:55 +04:00
|
|
|
/* save the current session data for possible re-use */
|
2017-03-22 08:59:49 +03:00
|
|
|
if(SSL_SET_OPTION(primary.sessionid)) {
|
2016-06-13 06:47:12 +03:00
|
|
|
bool incache;
|
|
|
|
struct curl_schannel_cred *old_cred = NULL;
|
|
|
|
|
|
|
|
Curl_ssl_sessionid_lock(conn);
|
2016-11-16 20:49:15 +03:00
|
|
|
incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL,
|
|
|
|
sockindex));
|
2016-06-13 06:47:12 +03:00
|
|
|
if(incache) {
|
2017-06-21 13:41:18 +03:00
|
|
|
if(old_cred != BACKEND->cred) {
|
2016-06-13 06:47:12 +03:00
|
|
|
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);
|
|
|
|
incache = FALSE;
|
|
|
|
}
|
2012-04-10 01:24:55 +04:00
|
|
|
}
|
2016-06-13 06:47:12 +03:00
|
|
|
if(!incache) {
|
2017-06-21 13:41:18 +03:00
|
|
|
result = Curl_ssl_addsessionid(conn, (void *)BACKEND->cred,
|
2016-11-16 20:49:15 +03:00
|
|
|
sizeof(struct curl_schannel_cred),
|
|
|
|
sockindex);
|
2016-06-13 06:47:12 +03:00
|
|
|
if(result) {
|
|
|
|
Curl_ssl_sessionid_unlock(conn);
|
|
|
|
failf(data, "schannel: failed to store credential handle");
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* this cred session is now also referenced by sessionid cache */
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->cred->refcount++;
|
2016-06-13 06:47:12 +03:00
|
|
|
infof(data, "schannel: stored credential handle in session cache\n");
|
|
|
|
}
|
2012-04-10 01:24:55 +04:00
|
|
|
}
|
2016-06-13 06:47:12 +03:00
|
|
|
Curl_ssl_sessionid_unlock(conn);
|
2012-04-10 01:24:55 +04:00
|
|
|
}
|
|
|
|
|
2016-06-01 09:48:30 +03:00
|
|
|
if(data->set.ssl.certinfo) {
|
2017-06-21 13:41:18 +03:00
|
|
|
sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
|
2016-06-01 09:48:30 +03:00
|
|
|
SECPKG_ATTR_REMOTE_CERT_CONTEXT, &ccert_context);
|
|
|
|
|
|
|
|
if((sspi_status != SEC_E_OK) || (ccert_context == NULL)) {
|
|
|
|
failf(data, "schannel: failed to retrieve remote cert context");
|
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = Curl_ssl_init_certinfo(data, 1);
|
|
|
|
if(!result) {
|
|
|
|
if(((ccert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
|
|
|
|
(ccert_context->cbCertEncoded > 0)) {
|
|
|
|
|
|
|
|
const char *beg = (const char *) ccert_context->pbCertEncoded;
|
|
|
|
const char *end = beg + ccert_context->cbCertEncoded;
|
|
|
|
result = Curl_extract_certinfo(conn, 0, beg, end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CertFreeCertificateContext(ccert_context);
|
|
|
|
if(result)
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
connssl->connecting_state = ssl_connect_done;
|
|
|
|
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CURLcode
|
|
|
|
schannel_connect_common(struct connectdata *conn, int sockindex,
|
2012-04-15 06:12:26 +04:00
|
|
|
bool nonblocking, bool *done)
|
|
|
|
{
|
2014-12-25 14:50:15 +03:00
|
|
|
CURLcode result;
|
2016-06-21 16:47:12 +03:00
|
|
|
struct Curl_easy *data = conn->data;
|
2012-04-09 17:40:06 +04:00
|
|
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
|
|
|
curl_socket_t sockfd = conn->sock[sockindex];
|
2016-11-19 16:09:03 +03:00
|
|
|
time_t timeout_ms;
|
2012-04-09 17:40:06 +04:00
|
|
|
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 */
|
2012-06-19 22:28:21 +04:00
|
|
|
failf(data, "SSL/TLS connection timeout");
|
2012-04-09 17:40:06 +04:00
|
|
|
return CURLE_OPERATION_TIMEDOUT;
|
|
|
|
}
|
|
|
|
|
2014-12-25 14:50:15 +03:00
|
|
|
result = schannel_connect_step1(conn, sockindex);
|
|
|
|
if(result)
|
|
|
|
return result;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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 */
|
2012-06-19 22:28:21 +04:00
|
|
|
failf(data, "SSL/TLS connection timeout");
|
2012-04-09 17:40:06 +04:00
|
|
|
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;
|
|
|
|
|
2016-10-18 11:58:58 +03:00
|
|
|
what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
|
|
|
|
nonblocking ? 0 : timeout_ms);
|
2012-04-09 17:40:06 +04:00
|
|
|
if(what < 0) {
|
|
|
|
/* fatal error */
|
2012-06-19 22:28:21 +04:00
|
|
|
failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
|
2012-04-09 17:40:06 +04:00
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
}
|
|
|
|
else if(0 == what) {
|
|
|
|
if(nonblocking) {
|
|
|
|
*done = FALSE;
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* timeout */
|
2012-06-19 22:28:21 +04:00
|
|
|
failf(data, "SSL/TLS connection timeout");
|
2012-04-09 17:40:06 +04:00
|
|
|
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.
|
|
|
|
*/
|
2014-12-25 14:50:15 +03:00
|
|
|
result = schannel_connect_step2(conn, sockindex);
|
|
|
|
if(result || (nonblocking &&
|
|
|
|
(ssl_connect_2 == connssl->connecting_state ||
|
|
|
|
ssl_connect_2_reading == connssl->connecting_state ||
|
|
|
|
ssl_connect_2_writing == connssl->connecting_state)))
|
|
|
|
return result;
|
2012-04-09 17:40:06 +04:00
|
|
|
|
|
|
|
} /* repeat step2 until all transactions are done. */
|
|
|
|
|
|
|
|
if(ssl_connect_3 == connssl->connecting_state) {
|
2014-12-25 14:50:15 +03:00
|
|
|
result = schannel_connect_step3(conn, sockindex);
|
|
|
|
if(result)
|
|
|
|
return result;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2012-04-15 06:12:26 +04:00
|
|
|
const void *buf, size_t len, CURLcode *err)
|
|
|
|
{
|
2012-06-14 13:53:46 +04:00
|
|
|
ssize_t written = -1;
|
2012-04-09 17:40:06 +04:00
|
|
|
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;
|
2015-02-04 03:03:05 +03:00
|
|
|
CURLcode result;
|
2012-04-09 17:40:06 +04:00
|
|
|
|
|
|
|
/* check if the maximum stream sizes were queried */
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->stream_sizes.cbMaximumMessage == 0) {
|
2012-04-13 15:10:09 +04:00
|
|
|
sspi_status = s_pSecFn->QueryContextAttributes(
|
2017-06-21 13:41:18 +03:00
|
|
|
&BACKEND->ctxt->ctxt_handle,
|
2015-04-26 18:40:40 +03:00
|
|
|
SECPKG_ATTR_STREAM_SIZES,
|
2017-06-21 13:41:18 +03:00
|
|
|
&BACKEND->stream_sizes);
|
2012-04-09 17:40:06 +04:00
|
|
|
if(sspi_status != SEC_E_OK) {
|
|
|
|
*err = CURLE_SEND_ERROR;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if the buffer is longer than the maximum message length */
|
2017-06-21 13:41:18 +03:00
|
|
|
if(len > BACKEND->stream_sizes.cbMaximumMessage) {
|
2012-04-09 17:40:06 +04:00
|
|
|
*err = CURLE_SEND_ERROR;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* calculate the complete message length and allocate a buffer for it */
|
2017-06-21 13:41:18 +03:00
|
|
|
data_len = BACKEND->stream_sizes.cbHeader + len +
|
|
|
|
BACKEND->stream_sizes.cbTrailer;
|
2014-12-25 14:52:32 +03:00
|
|
|
data = (unsigned char *) malloc(data_len);
|
2012-04-09 17:40:06 +04:00
|
|
|
if(data == NULL) {
|
|
|
|
*err = CURLE_OUT_OF_MEMORY;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* setup output buffers (header, data, trailer, empty) */
|
2012-06-19 06:15:01 +04:00
|
|
|
InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
|
2017-06-21 13:41:18 +03:00
|
|
|
data, BACKEND->stream_sizes.cbHeader);
|
2012-06-19 06:15:01 +04:00
|
|
|
InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
|
2017-06-21 13:41:18 +03:00
|
|
|
data + BACKEND->stream_sizes.cbHeader, curlx_uztoul(len));
|
2012-06-19 06:15:01 +04:00
|
|
|
InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
|
2017-06-21 13:41:18 +03:00
|
|
|
data + BACKEND->stream_sizes.cbHeader + len,
|
|
|
|
BACKEND->stream_sizes.cbTrailer);
|
2012-06-19 06:15:01 +04:00
|
|
|
InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
|
|
|
|
InitSecBufferDesc(&outbuf_desc, outbuf, 4);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
|
|
|
/* copy data into output buffer */
|
|
|
|
memcpy(outbuf[1].pvBuffer, buf, len);
|
|
|
|
|
2016-02-03 07:09:25 +03:00
|
|
|
/* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
|
2017-06-21 13:41:18 +03:00
|
|
|
sspi_status = s_pSecFn->EncryptMessage(&BACKEND->ctxt->ctxt_handle, 0,
|
2012-04-09 17:40:06 +04:00
|
|
|
&outbuf_desc, 0);
|
|
|
|
|
|
|
|
/* check if the message was encrypted */
|
|
|
|
if(sspi_status == SEC_E_OK) {
|
2012-06-20 02:14:17 +04:00
|
|
|
written = 0;
|
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
/* send the encrypted message including header, data and trailer */
|
|
|
|
len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
|
2012-06-20 02:14:17 +04:00
|
|
|
|
|
|
|
/*
|
2015-04-26 18:40:40 +03:00
|
|
|
It's important to send the full message which includes the header,
|
|
|
|
encrypted payload, and trailer. Until the client receives all the
|
|
|
|
data a coherent message has not been delivered and the client
|
|
|
|
can't read any of it.
|
|
|
|
|
|
|
|
If we wanted to buffer the unwritten encrypted bytes, we would
|
|
|
|
tell the client that all data it has requested to be sent has been
|
|
|
|
sent. The unwritten encrypted bytes would be the first bytes to
|
|
|
|
send on the next invocation.
|
|
|
|
Here's the catch with this - if we tell the client that all the
|
|
|
|
bytes have been sent, will the client call this method again to
|
|
|
|
send the buffered data? Looking at who calls this function, it
|
|
|
|
seems the answer is NO.
|
2012-06-20 02:14:17 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* send entire message or fail */
|
|
|
|
while(len > (size_t)written) {
|
|
|
|
ssize_t this_write;
|
2016-11-19 16:09:03 +03:00
|
|
|
time_t timeleft;
|
2012-06-20 02:14:17 +04:00
|
|
|
int what;
|
|
|
|
|
|
|
|
this_write = 0;
|
|
|
|
|
2014-05-05 02:07:54 +04:00
|
|
|
timeleft = Curl_timeleft(conn->data, NULL, FALSE);
|
2012-06-20 02:14:17 +04:00
|
|
|
if(timeleft < 0) {
|
|
|
|
/* we already got the timeout */
|
|
|
|
failf(conn->data, "schannel: timed out sending data "
|
|
|
|
"(bytes sent: %zd)", written);
|
|
|
|
*err = CURLE_OPERATION_TIMEDOUT;
|
|
|
|
written = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-10-18 11:58:58 +03:00
|
|
|
what = SOCKET_WRITABLE(conn->sock[sockindex], timeleft);
|
2012-06-20 02:14:17 +04:00
|
|
|
if(what < 0) {
|
|
|
|
/* fatal error */
|
|
|
|
failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
|
|
|
|
*err = CURLE_SEND_ERROR;
|
|
|
|
written = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if(0 == what) {
|
|
|
|
failf(conn->data, "schannel: timed out sending data "
|
|
|
|
"(bytes sent: %zd)", written);
|
|
|
|
*err = CURLE_OPERATION_TIMEDOUT;
|
|
|
|
written = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* socket is writable */
|
|
|
|
|
2015-02-04 03:03:05 +03:00
|
|
|
result = Curl_write_plain(conn, conn->sock[sockindex], data + written,
|
|
|
|
len - written, &this_write);
|
|
|
|
if(result == CURLE_AGAIN)
|
2012-06-20 02:14:17 +04:00
|
|
|
continue;
|
2015-02-04 03:03:05 +03:00
|
|
|
else if(result != CURLE_OK) {
|
|
|
|
*err = result;
|
2012-06-20 02:14:17 +04:00
|
|
|
written = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
written += this_write;
|
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
|
|
|
|
*err = CURLE_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
*err = CURLE_SEND_ERROR;
|
|
|
|
}
|
|
|
|
|
2012-06-25 09:16:26 +04:00
|
|
|
Curl_safefree(data);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-20 02:14:17 +04:00
|
|
|
if(len == (size_t)written)
|
|
|
|
/* Encrypted message including header, data and trailer entirely sent.
|
|
|
|
The return value is the number of unencrypted bytes that were sent. */
|
|
|
|
written = outbuf[1].cbBuffer;
|
|
|
|
|
2012-06-14 13:53:46 +04:00
|
|
|
return written;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
schannel_recv(struct connectdata *conn, int sockindex,
|
2012-04-15 06:12:26 +04:00
|
|
|
char *buf, size_t len, CURLcode *err)
|
|
|
|
{
|
2012-04-09 17:40:06 +04:00
|
|
|
size_t size = 0;
|
2015-06-17 07:17:03 +03:00
|
|
|
ssize_t nread = -1;
|
2016-06-21 16:47:12 +03:00
|
|
|
struct Curl_easy *data = conn->data;
|
2012-04-09 17:40:06 +04:00
|
|
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
2014-12-14 19:27:20 +03:00
|
|
|
unsigned char *reallocated_buffer;
|
|
|
|
size_t reallocated_length;
|
2012-04-09 17:40:06 +04:00
|
|
|
bool done = FALSE;
|
|
|
|
SecBuffer inbuf[4];
|
|
|
|
SecBufferDesc inbuf_desc;
|
|
|
|
SECURITY_STATUS sspi_status = SEC_E_OK;
|
2015-06-17 07:17:03 +03:00
|
|
|
/* we want the length of the encrypted buffer to be at least large enough
|
|
|
|
that it can hold all the bytes requested and some TLS record overhead. */
|
|
|
|
size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
|
|
|
|
|
|
|
|
/****************************************************************************
|
2017-06-21 13:41:18 +03:00
|
|
|
* Don't return or set BACKEND->recv_unrecoverable_err unless in the cleanup.
|
2015-06-17 07:17:03 +03:00
|
|
|
* 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
|
|
|
|
* possible, even if an error occurs. The state of the decrypted buffer must
|
|
|
|
* always be valid. Transfer of decrypted data to the caller's buffer is
|
|
|
|
* handled in the cleanup.
|
|
|
|
*/
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-14 18:55:26 +04:00
|
|
|
infof(data, "schannel: client wants to read %zu bytes\n", len);
|
2012-04-09 17:40:06 +04:00
|
|
|
*err = CURLE_OK;
|
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
if(len && len <= BACKEND->decdata_offset) {
|
2015-06-17 07:17:03 +03:00
|
|
|
infof(data, "schannel: enough decrypted data is already available\n");
|
|
|
|
goto cleanup;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
2017-06-21 13:41:18 +03:00
|
|
|
else if(BACKEND->recv_unrecoverable_err) {
|
|
|
|
*err = BACKEND->recv_unrecoverable_err;
|
2015-06-17 07:17:03 +03:00
|
|
|
infof(data, "schannel: an unrecoverable error occurred in a prior call\n");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2017-06-21 13:41:18 +03:00
|
|
|
else if(BACKEND->recv_sspi_close_notify) {
|
2015-06-17 07:17:03 +03:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
else if(!len) {
|
|
|
|
/* It's debatable what to return when !len. Regardless we can't return
|
|
|
|
immediately because there may be data to decrypt (in the case we want to
|
|
|
|
decrypt all encrypted cached data) so handle !len later in cleanup.
|
|
|
|
*/
|
|
|
|
; /* do nothing */
|
|
|
|
}
|
2017-06-21 13:41:18 +03:00
|
|
|
else if(!BACKEND->recv_connection_closed) {
|
2015-06-17 07:17:03 +03:00
|
|
|
/* increase enc buffer in order to fit the requested amount of data */
|
2017-06-21 13:41:18 +03:00
|
|
|
size = BACKEND->encdata_length - BACKEND->encdata_offset;
|
2015-06-17 07:17:03 +03:00
|
|
|
if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE ||
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_length < min_encdata_length) {
|
|
|
|
reallocated_length = BACKEND->encdata_offset +
|
2015-06-17 07:17:03 +03:00
|
|
|
CURL_SCHANNEL_BUFFER_FREE_SIZE;
|
|
|
|
if(reallocated_length < min_encdata_length) {
|
|
|
|
reallocated_length = min_encdata_length;
|
|
|
|
}
|
2017-06-21 13:41:18 +03:00
|
|
|
reallocated_buffer = realloc(BACKEND->encdata_buffer,
|
2015-06-17 07:17:03 +03:00
|
|
|
reallocated_length);
|
|
|
|
if(reallocated_buffer == NULL) {
|
|
|
|
*err = CURLE_OUT_OF_MEMORY;
|
|
|
|
failf(data, "schannel: unable to re-allocate memory");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_buffer = reallocated_buffer;
|
|
|
|
BACKEND->encdata_length = reallocated_length;
|
|
|
|
size = BACKEND->encdata_length - BACKEND->encdata_offset;
|
2015-06-17 07:17:03 +03:00
|
|
|
infof(data, "schannel: encdata_buffer resized %zu\n",
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_length);
|
2014-12-14 19:27:20 +03:00
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2015-06-17 07:17:03 +03:00
|
|
|
infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_offset, BACKEND->encdata_length);
|
2015-06-17 07:17:03 +03:00
|
|
|
|
|
|
|
/* read encrypted data from socket */
|
2012-06-19 22:28:22 +04:00
|
|
|
*err = Curl_read_plain(conn->sock[sockindex],
|
2017-06-21 13:41:18 +03:00
|
|
|
(char *)(BACKEND->encdata_buffer +
|
|
|
|
BACKEND->encdata_offset),
|
2012-06-19 22:28:22 +04:00
|
|
|
size, &nread);
|
2015-06-17 07:17:03 +03:00
|
|
|
if(*err) {
|
|
|
|
nread = -1;
|
|
|
|
if(*err == CURLE_AGAIN)
|
|
|
|
infof(data, "schannel: Curl_read_plain returned CURLE_AGAIN\n");
|
|
|
|
else if(*err == CURLE_RECV_ERROR)
|
|
|
|
infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n");
|
|
|
|
else
|
|
|
|
infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
|
|
|
|
}
|
|
|
|
else if(nread == 0) {
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->recv_connection_closed = true;
|
2015-06-17 07:17:03 +03:00
|
|
|
infof(data, "schannel: server closed the connection\n");
|
2015-05-02 23:21:25 +03:00
|
|
|
}
|
|
|
|
else if(nread > 0) {
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_offset += (size_t)nread;
|
|
|
|
BACKEND->encdata_is_incomplete = false;
|
2015-06-17 07:17:03 +03:00
|
|
|
infof(data, "schannel: encrypted data got %zd\n", nread);
|
2012-04-10 23:21:31 +04:00
|
|
|
}
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
2012-06-14 18:55:26 +04:00
|
|
|
infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_offset, BACKEND->encdata_length);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2015-06-17 07:17:03 +03:00
|
|
|
/* decrypt loop */
|
2017-06-21 13:41:18 +03:00
|
|
|
while(BACKEND->encdata_offset > 0 && sspi_status == SEC_E_OK &&
|
|
|
|
(!len || BACKEND->decdata_offset < len ||
|
|
|
|
BACKEND->recv_connection_closed)) {
|
2012-04-09 17:40:06 +04:00
|
|
|
/* prepare data buffer for DecryptMessage call */
|
2017-06-21 13:41:18 +03:00
|
|
|
InitSecBuffer(&inbuf[0], SECBUFFER_DATA, BACKEND->encdata_buffer,
|
|
|
|
curlx_uztoul(BACKEND->encdata_offset));
|
2012-04-09 17:40:06 +04:00
|
|
|
|
|
|
|
/* we need 3 more empty input buffers for possible output */
|
2012-06-19 06:15:01 +04:00
|
|
|
InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
|
|
|
|
InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
|
|
|
|
InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
|
|
|
|
InitSecBufferDesc(&inbuf_desc, inbuf, 4);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2016-02-03 07:09:25 +03:00
|
|
|
/* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx
|
|
|
|
*/
|
2017-06-21 13:41:18 +03:00
|
|
|
sspi_status = s_pSecFn->DecryptMessage(&BACKEND->ctxt->ctxt_handle,
|
2012-04-09 17:40:06 +04:00
|
|
|
&inbuf_desc, 0, NULL);
|
2012-04-10 23:49:35 +04:00
|
|
|
|
2012-06-11 21:08:46 +04:00
|
|
|
/* check if everything went fine (server may want to renegotiate
|
2014-12-14 18:40:49 +03:00
|
|
|
or shutdown the connection context) */
|
2012-04-10 23:49:35 +04:00
|
|
|
if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
|
2015-04-26 18:40:40 +03:00
|
|
|
sspi_status == SEC_I_CONTEXT_EXPIRED) {
|
2014-12-14 18:40:49 +03:00
|
|
|
/* check for successfully decrypted data, even before actual
|
|
|
|
renegotiation or shutdown of the connection context */
|
2012-04-09 17:40:06 +04:00
|
|
|
if(inbuf[1].BufferType == SECBUFFER_DATA) {
|
2012-06-14 18:55:26 +04:00
|
|
|
infof(data, "schannel: decrypted data length: %lu\n",
|
2012-06-11 21:08:46 +04:00
|
|
|
inbuf[1].cbBuffer);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-04-10 23:49:35 +04:00
|
|
|
/* increase buffer in order to fit the received amount of data */
|
2012-10-06 15:59:28 +04:00
|
|
|
size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
|
2015-06-17 07:17:03 +03:00
|
|
|
inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->decdata_length - BACKEND->decdata_offset < size ||
|
|
|
|
BACKEND->decdata_length < len) {
|
2012-04-10 23:49:35 +04:00
|
|
|
/* increase internal decrypted data buffer */
|
2017-06-21 13:41:18 +03:00
|
|
|
reallocated_length = BACKEND->decdata_offset + size;
|
2014-12-14 19:27:20 +03:00
|
|
|
/* make sure that the requested amount of data fits */
|
|
|
|
if(reallocated_length < len) {
|
|
|
|
reallocated_length = len;
|
|
|
|
}
|
2017-06-21 13:41:18 +03:00
|
|
|
reallocated_buffer = realloc(BACKEND->decdata_buffer,
|
2014-12-14 19:27:20 +03:00
|
|
|
reallocated_length);
|
|
|
|
if(reallocated_buffer == NULL) {
|
2012-04-10 23:49:35 +04:00
|
|
|
*err = CURLE_OUT_OF_MEMORY;
|
2015-06-17 07:17:03 +03:00
|
|
|
failf(data, "schannel: unable to re-allocate memory");
|
|
|
|
goto cleanup;
|
2014-12-14 19:27:20 +03:00
|
|
|
}
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->decdata_buffer = reallocated_buffer;
|
|
|
|
BACKEND->decdata_length = reallocated_length;
|
2012-04-10 23:49:35 +04:00
|
|
|
}
|
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
/* copy decrypted data to internal buffer */
|
2012-04-10 23:49:35 +04:00
|
|
|
size = inbuf[1].cbBuffer;
|
2015-06-17 07:17:03 +03:00
|
|
|
if(size) {
|
2017-06-21 13:41:18 +03:00
|
|
|
memcpy(BACKEND->decdata_buffer + BACKEND->decdata_offset,
|
2012-04-09 17:40:06 +04:00
|
|
|
inbuf[1].pvBuffer, size);
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->decdata_offset += size;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
2012-06-14 18:55:26 +04:00
|
|
|
infof(data, "schannel: decrypted data added: %zu\n", size);
|
|
|
|
infof(data, "schannel: decrypted data cached: offset %zu length %zu\n",
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->decdata_offset, BACKEND->decdata_length);
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* check for remaining encrypted data */
|
2012-04-13 15:02:59 +04:00
|
|
|
if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
|
2012-06-14 18:55:26 +04:00
|
|
|
infof(data, "schannel: encrypted data length: %lu\n",
|
2012-06-11 21:08:46 +04:00
|
|
|
inbuf[3].cbBuffer);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
|
|
|
/* check if the remaining data is less than the total amount
|
|
|
|
* and therefore begins after the already processed data
|
2015-04-26 18:40:40 +03:00
|
|
|
*/
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->encdata_offset > inbuf[3].cbBuffer) {
|
2012-06-11 21:08:46 +04:00
|
|
|
/* move remaining encrypted data forward to the beginning of
|
|
|
|
buffer */
|
2017-06-21 13:41:18 +03:00
|
|
|
memmove(BACKEND->encdata_buffer,
|
|
|
|
(BACKEND->encdata_buffer + BACKEND->encdata_offset) -
|
2015-04-26 18:40:40 +03:00
|
|
|
inbuf[3].cbBuffer, inbuf[3].cbBuffer);
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_offset = inbuf[3].cbBuffer;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
2012-06-14 18:55:26 +04:00
|
|
|
infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_offset, BACKEND->encdata_length);
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
2015-05-02 21:05:22 +03:00
|
|
|
else {
|
2012-04-09 17:40:06 +04:00
|
|
|
/* reset encrypted buffer offset, because there is no data remaining */
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_offset = 0;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
2015-05-02 21:14:53 +03:00
|
|
|
|
|
|
|
/* check if server wants to renegotiate the connection context */
|
|
|
|
if(sspi_status == SEC_I_RENEGOTIATE) {
|
|
|
|
infof(data, "schannel: remote party requests renegotiation\n");
|
2015-06-17 07:17:03 +03:00
|
|
|
if(*err && *err != CURLE_AGAIN) {
|
|
|
|
infof(data, "schannel: can't renogotiate, an error is pending\n");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->encdata_offset) {
|
2015-06-17 07:17:03 +03:00
|
|
|
*err = CURLE_RECV_ERROR;
|
|
|
|
infof(data, "schannel: can't renogotiate, "
|
|
|
|
"encrypted data available\n");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2015-05-02 21:14:53 +03:00
|
|
|
/* begin renegotiation */
|
|
|
|
infof(data, "schannel: renegotiating SSL/TLS connection\n");
|
|
|
|
connssl->state = ssl_connection_negotiating;
|
|
|
|
connssl->connecting_state = ssl_connect_2_writing;
|
2015-06-17 07:17:03 +03:00
|
|
|
*err = schannel_connect_common(conn, sockindex, FALSE, &done);
|
|
|
|
if(*err) {
|
|
|
|
infof(data, "schannel: renegotiation failed\n");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
/* now retry receiving data */
|
|
|
|
sspi_status = SEC_E_OK;
|
|
|
|
infof(data, "schannel: SSL/TLS connection renegotiated\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* check if the server closed the connection */
|
|
|
|
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. */
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->recv_sspi_close_notify = true;
|
|
|
|
if(!BACKEND->recv_connection_closed) {
|
|
|
|
BACKEND->recv_connection_closed = true;
|
2015-06-17 07:17:03 +03:00
|
|
|
infof(data, "schannel: server closed the connection\n");
|
2015-05-02 21:14:53 +03:00
|
|
|
}
|
2015-06-17 07:17:03 +03:00
|
|
|
goto cleanup;
|
2015-05-02 21:14:53 +03:00
|
|
|
}
|
|
|
|
}
|
2015-06-17 07:17:03 +03:00
|
|
|
else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_is_incomplete = true;
|
2015-06-17 07:17:03 +03:00
|
|
|
if(!*err)
|
|
|
|
*err = CURLE_AGAIN;
|
|
|
|
infof(data, "schannel: failed to decrypt data, need more data\n");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2015-05-02 21:05:22 +03:00
|
|
|
else {
|
2015-06-17 07:17:03 +03:00
|
|
|
*err = CURLE_RECV_ERROR;
|
2015-05-02 21:05:22 +03:00
|
|
|
infof(data, "schannel: failed to read data from server: %s\n",
|
|
|
|
Curl_sspi_strerror(conn, sspi_status));
|
2015-06-17 07:17:03 +03:00
|
|
|
goto cleanup;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-17 07:17:03 +03:00
|
|
|
infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->encdata_offset, BACKEND->encdata_length);
|
2015-06-17 07:17:03 +03:00
|
|
|
|
2012-10-06 17:47:14 +04:00
|
|
|
infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->decdata_offset, BACKEND->decdata_length);
|
2012-10-06 17:47:14 +04:00
|
|
|
|
2015-06-17 07:17:03 +03:00
|
|
|
cleanup:
|
|
|
|
/* Warning- there is no guarantee the encdata state is valid at this point */
|
|
|
|
infof(data, "schannel: schannel_recv cleanup\n");
|
|
|
|
|
|
|
|
/* Error if the connection has closed without a close_notify.
|
|
|
|
Behavior here is a matter of debate. We don't want to be vulnerable to a
|
|
|
|
truncation attack however there's some browser precedent for ignoring the
|
|
|
|
close_notify for compatibility reasons.
|
|
|
|
Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't
|
|
|
|
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.
|
|
|
|
*/
|
2017-06-21 13:41:18 +03:00
|
|
|
if(len && !BACKEND->decdata_offset && BACKEND->recv_connection_closed &&
|
|
|
|
!BACKEND->recv_sspi_close_notify) {
|
2016-06-04 22:58:39 +03:00
|
|
|
bool isWin2k = Curl_verify_windows_version(5, 0, PLATFORM_WINNT,
|
|
|
|
VERSION_EQUAL);
|
2015-07-22 00:23:07 +03:00
|
|
|
|
|
|
|
if(isWin2k && sspi_status == SEC_E_OK)
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->recv_sspi_close_notify = true;
|
2015-06-17 07:17:03 +03:00
|
|
|
else {
|
|
|
|
*err = CURLE_RECV_ERROR;
|
|
|
|
infof(data, "schannel: server closed abruptly (missing close_notify)\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Any error other than CURLE_AGAIN is an unrecoverable error. */
|
|
|
|
if(*err && *err != CURLE_AGAIN)
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->recv_unrecoverable_err = *err;
|
2015-06-17 07:17:03 +03:00
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
size = len < BACKEND->decdata_offset ? len : BACKEND->decdata_offset;
|
2015-06-17 07:17:03 +03:00
|
|
|
if(size) {
|
2017-06-21 13:41:18 +03:00
|
|
|
memcpy(buf, BACKEND->decdata_buffer, size);
|
|
|
|
memmove(BACKEND->decdata_buffer, BACKEND->decdata_buffer + size,
|
|
|
|
BACKEND->decdata_offset - size);
|
|
|
|
BACKEND->decdata_offset -= size;
|
2012-10-06 17:47:14 +04:00
|
|
|
|
2015-06-17 07:17:03 +03:00
|
|
|
infof(data, "schannel: decrypted data returned %zu\n", size);
|
2012-10-06 17:47:14 +04:00
|
|
|
infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->decdata_offset, BACKEND->decdata_length);
|
2012-04-10 23:21:31 +04:00
|
|
|
*err = CURLE_OK;
|
2015-06-17 07:17:03 +03:00
|
|
|
return (ssize_t)size;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
if(!*err && !BACKEND->recv_connection_closed)
|
2015-06-17 07:17:03 +03:00
|
|
|
*err = CURLE_AGAIN;
|
|
|
|
|
|
|
|
/* It's debatable what to return when !len. We could return whatever error we
|
|
|
|
got from decryption but instead we override here so the return is consistent.
|
|
|
|
*/
|
|
|
|
if(!len)
|
|
|
|
*err = CURLE_OK;
|
|
|
|
|
|
|
|
return *err ? -1 : 0;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
2017-06-23 14:19:00 +03:00
|
|
|
static CURLcode Curl_schannel_connect_nonblocking(struct connectdata *conn,
|
|
|
|
int sockindex, bool *done)
|
2012-04-15 06:12:26 +04:00
|
|
|
{
|
2012-04-09 17:40:06 +04:00
|
|
|
return schannel_connect_common(conn, sockindex, TRUE, done);
|
|
|
|
}
|
|
|
|
|
2017-06-23 14:19:00 +03:00
|
|
|
static CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex)
|
2012-04-15 06:12:26 +04:00
|
|
|
{
|
2014-12-25 14:50:15 +03:00
|
|
|
CURLcode result;
|
2012-04-09 17:40:06 +04:00
|
|
|
bool done = FALSE;
|
|
|
|
|
2014-12-25 14:50:15 +03:00
|
|
|
result = schannel_connect_common(conn, sockindex, FALSE, &done);
|
|
|
|
if(result)
|
|
|
|
return result;
|
2012-04-09 17:40:06 +04:00
|
|
|
|
|
|
|
DEBUGASSERT(done);
|
|
|
|
|
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
2017-06-23 14:19:00 +03:00
|
|
|
static bool Curl_schannel_data_pending(const struct connectdata *conn,
|
|
|
|
int sockindex)
|
2012-04-15 06:12:26 +04:00
|
|
|
{
|
2012-04-09 17:40:06 +04:00
|
|
|
const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
|
|
|
|
2012-06-19 22:28:21 +04:00
|
|
|
if(connssl->use) /* SSL/TLS is in use */
|
2017-06-21 13:41:18 +03:00
|
|
|
return (BACKEND->decdata_offset > 0 ||
|
|
|
|
(BACKEND->encdata_offset > 0 && !BACKEND->encdata_is_incomplete));
|
2012-04-09 17:40:06 +04:00
|
|
|
else
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2017-06-23 14:19:00 +03:00
|
|
|
static void Curl_schannel_close(struct connectdata *conn, int sockindex)
|
2012-04-15 06:12:26 +04:00
|
|
|
{
|
2012-06-19 06:15:03 +04:00
|
|
|
if(conn->ssl[sockindex].use)
|
2012-06-19 22:28:21 +04:00
|
|
|
/* if the SSL/TLS channel hasn't been shut down yet, do that now. */
|
2012-06-19 06:15:03 +04:00
|
|
|
Curl_ssl_shutdown(conn, sockindex);
|
|
|
|
}
|
|
|
|
|
2017-06-23 14:19:00 +03:00
|
|
|
static void Curl_schannel_session_free(void *ptr)
|
2017-06-25 13:44:26 +03:00
|
|
|
{
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-23 14:19:00 +03:00
|
|
|
static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
|
2012-06-19 06:15:03 +04:00
|
|
|
{
|
2016-02-03 07:09:25 +03:00
|
|
|
/* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
|
2012-06-25 09:16:26 +04:00
|
|
|
* Shutting Down an Schannel Connection
|
|
|
|
*/
|
2016-06-21 16:47:12 +03:00
|
|
|
struct Curl_easy *data = conn->data;
|
2012-04-09 17:40:06 +04:00
|
|
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
2017-04-06 21:04:58 +03:00
|
|
|
char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
|
2016-11-21 13:01:25 +03:00
|
|
|
conn->host.name;
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2012-06-19 22:28:21 +04:00
|
|
|
infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
|
2016-11-21 13:01:25 +03:00
|
|
|
hostname, conn->remote_port);
|
2012-04-09 17:40:06 +04:00
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->cred && BACKEND->ctxt) {
|
2012-06-19 06:15:03 +04:00
|
|
|
SecBufferDesc BuffDesc;
|
|
|
|
SecBuffer Buffer;
|
|
|
|
SECURITY_STATUS sspi_status;
|
|
|
|
SecBuffer outbuf;
|
|
|
|
SecBufferDesc outbuf_desc;
|
2015-02-04 03:03:05 +03:00
|
|
|
CURLcode result;
|
2012-06-19 06:15:03 +04:00
|
|
|
TCHAR *host_name;
|
|
|
|
DWORD dwshut = SCHANNEL_SHUTDOWN;
|
|
|
|
|
|
|
|
InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
|
|
|
|
InitSecBufferDesc(&BuffDesc, &Buffer, 1);
|
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
sspi_status = s_pSecFn->ApplyControlToken(&BACKEND->ctxt->ctxt_handle,
|
2012-06-19 06:15:03 +04:00
|
|
|
&BuffDesc);
|
|
|
|
|
|
|
|
if(sspi_status != SEC_E_OK)
|
|
|
|
failf(data, "schannel: ApplyControlToken failure: %s",
|
|
|
|
Curl_sspi_strerror(conn, sspi_status));
|
|
|
|
|
2016-11-21 13:01:25 +03:00
|
|
|
host_name = Curl_convert_UTF8_to_tchar(hostname);
|
2012-06-19 06:15:03 +04:00
|
|
|
if(!host_name)
|
|
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
/* setup output buffer */
|
|
|
|
InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
|
|
|
|
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
|
|
|
|
|
|
|
|
sspi_status = s_pSecFn->InitializeSecurityContext(
|
2017-06-21 13:41:18 +03:00
|
|
|
&BACKEND->cred->cred_handle,
|
|
|
|
&BACKEND->ctxt->ctxt_handle,
|
2015-04-26 18:40:40 +03:00
|
|
|
host_name,
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->req_flags,
|
2015-04-26 18:40:40 +03:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
0,
|
2017-06-21 13:41:18 +03:00
|
|
|
&BACKEND->ctxt->ctxt_handle,
|
2015-04-26 18:40:40 +03:00
|
|
|
&outbuf_desc,
|
2017-06-21 13:41:18 +03:00
|
|
|
&BACKEND->ret_flags,
|
|
|
|
&BACKEND->ctxt->time_stamp);
|
2012-06-19 06:15:03 +04:00
|
|
|
|
2012-07-06 00:16:15 +04:00
|
|
|
Curl_unicodefree(host_name);
|
2012-06-19 06:15:03 +04:00
|
|
|
|
|
|
|
if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
|
|
|
|
/* send close message which is in output buffer */
|
|
|
|
ssize_t written;
|
2015-02-04 03:03:05 +03:00
|
|
|
result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
|
|
|
|
outbuf.cbBuffer, &written);
|
2012-06-19 06:15:03 +04:00
|
|
|
|
|
|
|
s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
|
2015-02-04 03:03:05 +03:00
|
|
|
if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
|
2012-06-19 06:15:03 +04:00
|
|
|
infof(data, "schannel: failed to send close msg: %s"
|
2015-02-04 03:03:05 +03:00
|
|
|
" (bytes written: %zd)\n", curl_easy_strerror(result), written);
|
2012-06-19 06:15:03 +04:00
|
|
|
}
|
|
|
|
}
|
2014-10-04 20:24:23 +04:00
|
|
|
}
|
2012-06-25 09:16:26 +04:00
|
|
|
|
2014-10-04 20:24:23 +04:00
|
|
|
/* free SSPI Schannel API security context handle */
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->ctxt) {
|
2014-10-04 20:24:23 +04:00
|
|
|
infof(data, "schannel: clear security context handle\n");
|
2017-06-21 13:41:18 +03:00
|
|
|
s_pSecFn->DeleteSecurityContext(&BACKEND->ctxt->ctxt_handle);
|
|
|
|
Curl_safefree(BACKEND->ctxt);
|
2014-10-04 20:24:23 +04:00
|
|
|
}
|
2012-09-09 14:36:54 +04:00
|
|
|
|
2014-10-04 20:24:23 +04:00
|
|
|
/* free SSPI Schannel API credential handle */
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->cred) {
|
2016-06-01 10:30:03 +03:00
|
|
|
Curl_ssl_sessionid_lock(conn);
|
2017-06-21 13:41:18 +03:00
|
|
|
Curl_schannel_session_free(BACKEND->cred);
|
2016-06-01 10:30:03 +03:00
|
|
|
Curl_ssl_sessionid_unlock(conn);
|
2017-06-21 13:41:18 +03:00
|
|
|
BACKEND->cred = NULL;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* free internal buffer for received encrypted data */
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->encdata_buffer != NULL) {
|
|
|
|
Curl_safefree(BACKEND->encdata_buffer);
|
|
|
|
BACKEND->encdata_length = 0;
|
|
|
|
BACKEND->encdata_offset = 0;
|
|
|
|
BACKEND->encdata_is_incomplete = false;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* free internal buffer for received decrypted data */
|
2017-06-21 13:41:18 +03:00
|
|
|
if(BACKEND->decdata_buffer != NULL) {
|
|
|
|
Curl_safefree(BACKEND->decdata_buffer);
|
|
|
|
BACKEND->decdata_length = 0;
|
|
|
|
BACKEND->decdata_offset = 0;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
2012-06-19 06:15:03 +04:00
|
|
|
return CURLE_OK;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
2017-06-23 14:19:00 +03:00
|
|
|
static int Curl_schannel_init(void)
|
2012-04-15 06:12:26 +04:00
|
|
|
{
|
2012-04-09 17:40:06 +04:00
|
|
|
return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
|
|
|
|
}
|
|
|
|
|
2017-06-23 14:19:00 +03:00
|
|
|
static void Curl_schannel_cleanup(void)
|
2012-04-15 06:12:26 +04:00
|
|
|
{
|
2012-04-09 17:40:06 +04:00
|
|
|
Curl_sspi_global_cleanup();
|
|
|
|
}
|
|
|
|
|
2017-06-23 14:19:00 +03:00
|
|
|
static size_t Curl_schannel_version(char *buffer, size_t size)
|
2012-04-09 17:40:06 +04:00
|
|
|
{
|
2012-07-09 17:23:49 +04:00
|
|
|
size = snprintf(buffer, size, "WinSSL");
|
2012-06-10 15:07:45 +04:00
|
|
|
|
2012-04-14 17:00:33 +04:00
|
|
|
return size;
|
2012-04-09 17:40:06 +04:00
|
|
|
}
|
|
|
|
|
2017-06-23 14:19:00 +03:00
|
|
|
static CURLcode Curl_schannel_random(struct Curl_easy *data UNUSED_PARAM,
|
|
|
|
unsigned char *entropy, size_t length)
|
2014-07-31 14:59:46 +04:00
|
|
|
{
|
|
|
|
HCRYPTPROV hCryptProv = 0;
|
|
|
|
|
|
|
|
if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
|
|
|
|
CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
|
2017-01-12 19:41:26 +03:00
|
|
|
return CURLE_FAILED_INIT;
|
2014-07-31 14:59:46 +04:00
|
|
|
|
|
|
|
if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
|
|
|
|
CryptReleaseContext(hCryptProv, 0UL);
|
2017-01-12 19:41:26 +03:00
|
|
|
return CURLE_FAILED_INIT;
|
2014-07-31 14:59:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
CryptReleaseContext(hCryptProv, 0UL);
|
2017-01-12 19:41:26 +03:00
|
|
|
return CURLE_OK;
|
2014-07-31 14:59:46 +04:00
|
|
|
}
|
|
|
|
|
2012-06-19 06:15:02 +04:00
|
|
|
#ifdef _WIN32_WCE
|
|
|
|
static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
|
|
|
|
{
|
|
|
|
SECURITY_STATUS status;
|
2016-06-21 16:47:12 +03:00
|
|
|
struct Curl_easy *data = conn->data;
|
2012-06-19 06:15:02 +04:00
|
|
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
CERT_CONTEXT *pCertContextServer = NULL;
|
2012-06-19 22:28:21 +04:00
|
|
|
const CERT_CHAIN_CONTEXT *pChainContext = NULL;
|
2016-11-21 13:01:25 +03:00
|
|
|
const char * const conn_hostname = SSL_IS_PROXY() ?
|
|
|
|
conn->http_proxy.host.name :
|
|
|
|
conn->host.name;
|
2012-06-19 06:15:02 +04:00
|
|
|
|
2017-06-21 13:41:18 +03:00
|
|
|
status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
|
2012-06-19 06:15:02 +04:00
|
|
|
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));
|
|
|
|
result = CURLE_PEER_FAILED_VERIFICATION;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(result == CURLE_OK) {
|
|
|
|
CERT_CHAIN_PARA ChainPara;
|
|
|
|
memset(&ChainPara, 0, sizeof(ChainPara));
|
|
|
|
ChainPara.cbSize = sizeof(ChainPara);
|
|
|
|
|
|
|
|
if(!CertGetCertificateChain(NULL,
|
|
|
|
pCertContextServer,
|
|
|
|
NULL,
|
|
|
|
pCertContextServer->hCertStore,
|
|
|
|
&ChainPara,
|
2016-11-16 20:49:15 +03:00
|
|
|
(data->set.ssl.no_revoke ? 0 :
|
2015-07-17 09:40:16 +03:00
|
|
|
CERT_CHAIN_REVOCATION_CHECK_CHAIN),
|
2012-06-19 06:15:02 +04:00
|
|
|
NULL,
|
|
|
|
&pChainContext)) {
|
|
|
|
failf(data, "schannel: CertGetCertificateChain failed: %s",
|
|
|
|
Curl_sspi_strerror(conn, GetLastError()));
|
|
|
|
pChainContext = NULL;
|
|
|
|
result = CURLE_PEER_FAILED_VERIFICATION;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(result == CURLE_OK) {
|
|
|
|
CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
|
2015-07-17 09:40:16 +03:00
|
|
|
DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED);
|
2012-06-19 06:15:02 +04:00
|
|
|
dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
|
|
|
|
if(dwTrustErrorMask) {
|
2015-07-17 09:40:16 +03:00
|
|
|
if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED)
|
|
|
|
failf(data, "schannel: CertGetCertificateChain trust error"
|
|
|
|
" CERT_TRUST_IS_REVOKED");
|
|
|
|
else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
|
2012-06-19 06:15:02 +04:00
|
|
|
failf(data, "schannel: CertGetCertificateChain trust error"
|
2015-04-26 18:40:40 +03:00
|
|
|
" CERT_TRUST_IS_PARTIAL_CHAIN");
|
2015-07-17 09:40:16 +03:00
|
|
|
else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
|
2012-06-19 06:15:02 +04:00
|
|
|
failf(data, "schannel: CertGetCertificateChain trust error"
|
2015-04-26 18:40:40 +03:00
|
|
|
" CERT_TRUST_IS_UNTRUSTED_ROOT");
|
2015-07-17 09:40:16 +03:00
|
|
|
else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
|
2012-06-19 06:15:02 +04:00
|
|
|
failf(data, "schannel: CertGetCertificateChain trust error"
|
2015-04-26 18:40:40 +03:00
|
|
|
" CERT_TRUST_IS_NOT_TIME_VALID");
|
2015-07-17 09:40:16 +03:00
|
|
|
else
|
|
|
|
failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
|
|
|
|
dwTrustErrorMask);
|
2012-06-19 06:15:02 +04:00
|
|
|
result = CURLE_PEER_FAILED_VERIFICATION;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(result == CURLE_OK) {
|
2016-11-16 20:49:15 +03:00
|
|
|
if(conn->ssl_config.verifyhost) {
|
2016-09-10 00:56:02 +03:00
|
|
|
TCHAR cert_hostname_buff[256];
|
2012-06-19 06:15:02 +04:00
|
|
|
DWORD len;
|
|
|
|
|
2015-07-17 09:40:16 +03:00
|
|
|
/* TODO: Fix this for certificates with multiple alternative names.
|
|
|
|
Right now we're only asking for the first preferred alternative name.
|
|
|
|
Instead we'd need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG
|
|
|
|
(if WinCE supports that?) and run this section in a loop for each.
|
|
|
|
https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
|
|
|
|
curl: (51) schannel: CertGetNameString() certificate hostname
|
|
|
|
(.google.com) did not match connection (google.com)
|
|
|
|
*/
|
2012-07-06 00:16:15 +04:00
|
|
|
len = CertGetNameString(pCertContextServer,
|
|
|
|
CERT_NAME_DNS_TYPE,
|
2016-09-10 00:56:02 +03:00
|
|
|
CERT_NAME_DISABLE_IE4_UTF8_FLAG,
|
2012-07-06 00:16:15 +04:00
|
|
|
NULL,
|
2016-09-10 00:56:02 +03:00
|
|
|
cert_hostname_buff,
|
|
|
|
256);
|
|
|
|
if(len > 0) {
|
|
|
|
const char *cert_hostname;
|
|
|
|
|
|
|
|
/* Comparing the cert name and the connection hostname encoded as UTF-8
|
|
|
|
* is acceptable since both values are assumed to use ASCII
|
|
|
|
* (or some equivalent) encoding
|
|
|
|
*/
|
|
|
|
cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname_buff);
|
|
|
|
if(!cert_hostname) {
|
|
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
int match_result;
|
|
|
|
|
|
|
|
match_result = Curl_cert_hostcheck(cert_hostname, conn->host.name);
|
|
|
|
if(match_result == CURL_HOST_MATCH) {
|
|
|
|
infof(data,
|
|
|
|
"schannel: connection hostname (%s) validated "
|
|
|
|
"against certificate name (%s)\n",
|
|
|
|
conn->host.name,
|
|
|
|
cert_hostname);
|
|
|
|
result = CURLE_OK;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
failf(data,
|
|
|
|
"schannel: connection hostname (%s) "
|
|
|
|
"does not match certificate name (%s)",
|
|
|
|
conn->host.name,
|
|
|
|
cert_hostname);
|
|
|
|
result = CURLE_PEER_FAILED_VERIFICATION;
|
|
|
|
}
|
|
|
|
Curl_unicodefree(cert_hostname);
|
|
|
|
}
|
2012-06-19 06:15:02 +04:00
|
|
|
}
|
2016-09-10 00:56:02 +03:00
|
|
|
else {
|
|
|
|
failf(data,
|
|
|
|
"schannel: CertGetNameString did not provide any "
|
|
|
|
"certificate name information");
|
2012-06-19 06:15:02 +04:00
|
|
|
result = CURLE_PEER_FAILED_VERIFICATION;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pChainContext)
|
|
|
|
CertFreeCertificateChain(pChainContext);
|
|
|
|
|
|
|
|
if(pCertContextServer)
|
|
|
|
CertFreeCertificateContext(pCertContextServer);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif /* _WIN32_WCE */
|
|
|
|
|
2017-06-23 17:05:26 +03:00
|
|
|
static void *Curl_schannel_get_internals(struct ssl_connect_data *connssl,
|
|
|
|
CURLINFO info UNUSED_PARAM)
|
|
|
|
{
|
|
|
|
(void)info;
|
2017-06-21 13:41:18 +03:00
|
|
|
return &BACKEND->ctxt->ctxt_handle;
|
2017-06-23 17:05:26 +03:00
|
|
|
}
|
|
|
|
|
2017-06-22 17:45:34 +03:00
|
|
|
const struct Curl_ssl Curl_ssl_schannel = {
|
2017-07-15 14:49:30 +03:00
|
|
|
{ CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */
|
2017-06-22 17:45:34 +03:00
|
|
|
|
2017-06-20 12:32:53 +03:00
|
|
|
0, /* have_ca_path */
|
|
|
|
1, /* have_certinfo */
|
|
|
|
0, /* have_pinnedpubkey */
|
|
|
|
0, /* have_ssl_ctx */
|
2017-06-26 19:05:38 +03:00
|
|
|
0, /* support_https_proxy */
|
2017-06-20 12:32:53 +03:00
|
|
|
|
vtls: encapsulate SSL backend-specific data
So far, all of the SSL backends' private data has been declared as
part of the ssl_connect_data struct, in one big #if .. #elif .. #endif
block.
This can only work as long as the SSL backend is a compile-time option,
something we want to change in the next commits.
Therefore, let's encapsulate the exact data needed by each SSL backend
into a private struct, and let's avoid bleeding any SSL backend-specific
information into urldata.h. This is also necessary to allow multiple SSL
backends to be compiled in at the same time, as e.g. OpenSSL's and
CyaSSL's headers cannot be included in the same .c file.
To avoid too many malloc() calls, we simply append the private structs
to the connectdata struct in allocate_conn().
This requires us to take extra care of alignment issues: struct fields
often need to be aligned on certain boundaries e.g. 32-bit values need to
be stored at addresses that divide evenly by 4 (= 32 bit / 8
bit-per-byte).
We do that by assuming that no SSL backend's private data contains any
fields that need to be aligned on boundaries larger than `long long`
(typically 64-bit) would need. Under this assumption, we simply add a
dummy field of type `long long` to the `struct connectdata` struct. This
field will never be accessed but acts as a placeholder for the four
instances of ssl_backend_data instead. the size of each ssl_backend_data
struct is stored in the SSL backend-specific metadata, to allow
allocate_conn() to know how much extra space to allocate, and how to
initialize the ssl[sockindex]->backend and proxy_ssl[sockindex]->backend
pointers.
This would appear to be a little complicated at first, but is really
necessary to encapsulate the private data of each SSL backend correctly.
And we need to encapsulate thusly if we ever want to allow selecting
CyaSSL and OpenSSL at runtime, as their headers cannot be included within
the same .c file (there are just too many conflicting definitions and
declarations for that).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2017-07-28 23:09:35 +03:00
|
|
|
sizeof(struct ssl_backend_data),
|
|
|
|
|
2017-06-22 17:45:34 +03:00
|
|
|
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 */
|
2017-06-23 17:05:26 +03:00
|
|
|
Curl_schannel_get_internals, /* get_internals */
|
2017-06-22 17:45:34 +03:00
|
|
|
Curl_schannel_close, /* close */
|
|
|
|
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 */
|
2017-06-23 02:04:56 +03:00
|
|
|
Curl_none_false_start, /* false_start */
|
2017-06-23 02:04:56 +03:00
|
|
|
Curl_none_md5sum, /* md5sum */
|
|
|
|
NULL /* sha256sum */
|
2017-06-22 17:45:34 +03:00
|
|
|
};
|
|
|
|
|
2012-04-09 17:40:06 +04:00
|
|
|
#endif /* USE_SCHANNEL */
|