* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2013, Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) 2012 - 2014, Marc Hoersken, <info@marc-hoersken.de>
* Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
- * Copyright (C) 2012 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
/*
* Source file for all SChannel-specific code for the TLS/SSL layer. No code
- * but sslgen.c should ever call or use these functions.
+ * but vtls.c should ever call or use these functions.
*
*/
#include "curl_sspi.h"
#include "curl_schannel.h"
-#include "sslgen.h"
+#include "vtls.h"
#include "sendf.h"
#include "connect.h" /* for the connect timeout */
#include "strerror.h"
conn->host.name, conn->remote_port);
/* check for an existing re-usable credential handle */
- if(!Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL)) {
+ if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)) {
connssl->cred = old_cred;
infof(data, "schannel: re-using existing credential handle\n");
}
infof(data, "schannel: disable server certificate revocation checks\n");
}
- if(Curl_inet_pton(AF_INET, conn->host.name, &addr)
-#ifdef ENABLE_IPV6
- || Curl_inet_pton(AF_INET6, conn->host.name, &addr6)
-#endif
- ) {
- schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
- infof(data, "schannel: using IP address, SNI is being disabled by "
- "disabling the servername check against the "
- "subject names in server certificates.\n");
- }
-
if(!data->set.ssl.verifyhost) {
schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
infof(data, "schannel: verifyhost setting prevents Schannel from "
}
switch(data->set.ssl.version) {
+ default:
+ 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:
+ 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_SSLv3:
schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
break;
}
/* allocate memory for the re-usable credential handle */
- connssl->cred = malloc(sizeof(struct curl_schannel_cred));
+ connssl->cred = (struct curl_schannel_cred *)
+ malloc(sizeof(struct curl_schannel_cred));
if(!connssl->cred) {
failf(data, "schannel: unable to allocate memory");
return CURLE_OUT_OF_MEMORY;
}
}
+ /* Warn if SNI is disabled due to use of an IP address */
+ if(Curl_inet_pton(AF_INET, conn->host.name, &addr)
+#ifdef ENABLE_IPV6
+ || Curl_inet_pton(AF_INET6, conn->host.name, &addr6)
+#endif
+ ) {
+ infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
+ }
+
/* setup output buffer */
InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
ISC_REQ_STREAM;
/* allocate memory for the security context handle */
- connssl->ctxt = malloc(sizeof(struct curl_schannel_ctxt));
+ connssl->ctxt = (struct curl_schannel_ctxt *)
+ malloc(sizeof(struct curl_schannel_ctxt));
if(!connssl->ctxt) {
failf(data, "schannel: unable to allocate memory");
return CURLE_OUT_OF_MEMORY;
ssize_t nread = -1, written = -1;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ unsigned char *reallocated_buffer;
+ size_t reallocated_length;
SecBuffer outbuf[2];
SecBufferDesc outbuf_desc;
SecBuffer inbuf[2];
infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
conn->host.name, conn->remote_port);
+ if(!connssl->cred || !connssl->ctxt)
+ return CURLE_SSL_CONNECT_ERROR;
+
/* buffer to store previously received and encrypted data */
if(connssl->encdata_buffer == NULL) {
connssl->encdata_offset = 0;
if(connssl->encdata_length - connssl->encdata_offset <
CURL_SCHANNEL_BUFFER_FREE_SIZE) {
/* increase internal encrypted data buffer */
- connssl->encdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR;
- connssl->encdata_buffer = realloc(connssl->encdata_buffer,
- connssl->encdata_length);
+ reallocated_length = connssl->encdata_offset +
+ CURL_SCHANNEL_BUFFER_FREE_SIZE;
+ reallocated_buffer = realloc(connssl->encdata_buffer,
+ reallocated_length);
- if(connssl->encdata_buffer == NULL) {
+ if(reallocated_buffer == NULL) {
failf(data, "schannel: unable to re-allocate memory");
return CURLE_OUT_OF_MEMORY;
}
+ else {
+ connssl->encdata_buffer = reallocated_buffer;
+ connssl->encdata_length = reallocated_length;
+ }
}
for(;;) {
static CURLcode
schannel_connect_step3(struct connectdata *conn, int sockindex)
{
- CURLcode retcode = CURLE_OK;
+ CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct curl_schannel_cred *old_cred = NULL;
- int incache;
+ bool incache;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
conn->host.name, conn->remote_port);
+ if(!connssl->cred)
+ return CURLE_SSL_CONNECT_ERROR;
+
/* check if the required context attributes are met */
if(connssl->ret_flags != connssl->req_flags) {
if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
}
/* save the current session data for possible re-use */
- incache = !(Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL));
+ incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL));
if(incache) {
if(old_cred != connssl->cred) {
infof(data, "schannel: old credential handle is stale, removing\n");
- Curl_ssl_delsessionid(conn, (void*)old_cred);
+ Curl_ssl_delsessionid(conn, (void *)old_cred);
incache = FALSE;
}
}
+
if(!incache) {
- retcode = Curl_ssl_addsessionid(conn, (void*)connssl->cred,
- sizeof(struct curl_schannel_cred));
- if(retcode) {
+ result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
+ sizeof(struct curl_schannel_cred));
+ if(result) {
failf(data, "schannel: failed to store credential handle");
- return retcode;
+ return result;
}
else {
connssl->cred->cached = TRUE;
schannel_connect_common(struct connectdata *conn, int sockindex,
bool nonblocking, bool *done)
{
- CURLcode retcode;
+ CURLcode result;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
return CURLE_OPERATION_TIMEDOUT;
}
- retcode = schannel_connect_step1(conn, sockindex);
- if(retcode)
- return retcode;
+ result = schannel_connect_step1(conn, sockindex);
+ if(result)
+ return result;
}
while(ssl_connect_2 == connssl->connecting_state ||
* ensuring that a client using select() or epoll() will always
* have a valid fdset to wait on.
*/
- retcode = schannel_connect_step2(conn, sockindex);
- if(retcode || (nonblocking &&
- (ssl_connect_2 == connssl->connecting_state ||
- ssl_connect_2_reading == connssl->connecting_state ||
- ssl_connect_2_writing == connssl->connecting_state)))
- return retcode;
+ 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;
} /* repeat step2 until all transactions are done. */
if(ssl_connect_3 == connssl->connecting_state) {
- retcode = schannel_connect_step3(conn, sockindex);
- if(retcode)
- return retcode;
+ result = schannel_connect_step3(conn, sockindex);
+ if(result)
+ return result;
}
if(ssl_connect_done == connssl->connecting_state) {
/* calculate the complete message length and allocate a buffer for it */
data_len = connssl->stream_sizes.cbHeader + len +
connssl->stream_sizes.cbTrailer;
- data = (unsigned char*) malloc(data_len);
+ data = (unsigned char *) malloc(data_len);
if(data == NULL) {
*err = CURLE_OUT_OF_MEMORY;
return -1;
this_write = 0;
- timeleft = Curl_timeleft(conn->data, NULL, TRUE);
+ timeleft = Curl_timeleft(conn->data, NULL, FALSE);
if(timeleft < 0) {
/* we already got the timeout */
failf(conn->data, "schannel: timed out sending data "
{
size_t size = 0;
ssize_t nread = 0, ret = -1;
- CURLcode retcode;
+ CURLcode result;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ unsigned char *reallocated_buffer;
+ size_t reallocated_length;
bool done = FALSE;
SecBuffer inbuf[4];
SecBufferDesc inbuf_desc;
}
/* increase buffer in order to fit the requested amount of data */
- while(connssl->encdata_length - connssl->encdata_offset <
- CURL_SCHANNEL_BUFFER_FREE_SIZE || connssl->encdata_length < len) {
+ if(connssl->encdata_length - connssl->encdata_offset <
+ CURL_SCHANNEL_BUFFER_FREE_SIZE || connssl->encdata_length < len) {
/* increase internal encrypted data buffer */
- connssl->encdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR;
- connssl->encdata_buffer = realloc(connssl->encdata_buffer,
- connssl->encdata_length);
+ reallocated_length = connssl->encdata_offset +
+ CURL_SCHANNEL_BUFFER_FREE_SIZE;
+ /* make sure that the requested amount of data fits */
+ if(reallocated_length < len) {
+ reallocated_length = len;
+ }
+ reallocated_buffer = realloc(connssl->encdata_buffer,
+ reallocated_length);
- if(connssl->encdata_buffer == NULL) {
+ if(reallocated_buffer == NULL) {
failf(data, "schannel: unable to re-allocate memory");
*err = CURLE_OUT_OF_MEMORY;
return -1;
}
+ else {
+ connssl->encdata_buffer = reallocated_buffer;
+ connssl->encdata_length = reallocated_length;
+ }
}
/* read encrypted data from socket */
}
/* check if everything went fine (server may want to renegotiate
- context) */
+ or shutdown the connection context) */
if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
sspi_status == SEC_I_CONTEXT_EXPIRED) {
- /* check for successfully decrypted data */
+ /* check for successfully decrypted data, even before actual
+ renegotiation or shutdown of the connection context */
if(inbuf[1].BufferType == SECBUFFER_DATA) {
infof(data, "schannel: decrypted data length: %lu\n",
inbuf[1].cbBuffer);
/* increase buffer in order to fit the received amount of data */
size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
- while(connssl->decdata_length - connssl->decdata_offset < size ||
- connssl->decdata_length < len) {
+ if(connssl->decdata_length - connssl->decdata_offset < size ||
+ connssl->decdata_length < len) {
/* increase internal decrypted data buffer */
- connssl->decdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR;
- connssl->decdata_buffer = realloc(connssl->decdata_buffer,
- connssl->decdata_length);
+ reallocated_length = connssl->decdata_offset + size;
+ /* make sure that the requested amount of data fits */
+ if(reallocated_length < len) {
+ reallocated_length = len;
+ }
+ reallocated_buffer = realloc(connssl->decdata_buffer,
+ reallocated_length);
- if(connssl->decdata_buffer == NULL) {
+ if(reallocated_buffer == NULL) {
failf(data, "schannel: unable to re-allocate memory");
*err = CURLE_OUT_OF_MEMORY;
return -1;
}
+ else {
+ connssl->decdata_buffer = reallocated_buffer;
+ connssl->decdata_length = reallocated_length;
+ }
}
/* copy decrypted data to internal buffer */
infof(data, "schannel: renegotiating SSL/TLS connection\n");
connssl->state = ssl_connection_negotiating;
connssl->connecting_state = ssl_connect_2_writing;
- retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
- if(retcode)
- *err = retcode;
+ result = schannel_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ *err = result;
else {
infof(data, "schannel: SSL/TLS connection renegotiated\n");
/* now retry receiving data */
infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
connssl->decdata_offset, connssl->decdata_length);
}
+ else
+ ret = 0;
/* check if the server closed the connection */
if(ret <= 0 && ( /* special check for Windows 2000 Professional */
CURLcode
Curl_schannel_connect(struct connectdata *conn, int sockindex)
{
- CURLcode retcode;
+ CURLcode result;
bool done = FALSE;
- retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
- if(retcode)
- return retcode;
+ result = schannel_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
DEBUGASSERT(done);
" (bytes written: %zd)\n", curl_easy_strerror(code), written);
}
}
+ }
- /* free SSPI Schannel API security context handle */
- if(connssl->ctxt) {
- infof(data, "schannel: clear security context handle\n");
- s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
- Curl_safefree(connssl->ctxt);
- }
+ /* free SSPI Schannel API security context handle */
+ if(connssl->ctxt) {
+ infof(data, "schannel: clear security context handle\n");
+ s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
+ Curl_safefree(connssl->ctxt);
+ }
- /* free SSPI Schannel API credential handle */
- if(connssl->cred) {
- /* decrement the reference counter of the credential/session handle */
- if(connssl->cred->refcount > 0) {
- connssl->cred->refcount--;
- infof(data, "schannel: decremented credential handle refcount = %d\n",
- connssl->cred->refcount);
- }
+ /* free SSPI Schannel API credential handle */
+ if(connssl->cred) {
+ /* decrement the reference counter of the credential/session handle */
+ if(connssl->cred->refcount > 0) {
+ connssl->cred->refcount--;
+ infof(data, "schannel: decremented credential handle refcount = %d\n",
+ connssl->cred->refcount);
+ }
- /* if the handle was not cached and the refcount is zero */
- if(!connssl->cred->cached && connssl->cred->refcount == 0) {
- infof(data, "schannel: clear credential handle\n");
- s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle);
- Curl_safefree(connssl->cred);
- }
+ /* if the handle was not cached and the refcount is zero */
+ if(!connssl->cred->cached && connssl->cred->refcount == 0) {
+ infof(data, "schannel: clear credential handle\n");
+ s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle);
+ Curl_safefree(connssl->cred);
}
}
return size;
}
+int Curl_schannel_random(unsigned char *entropy, size_t length)
+{
+ HCRYPTPROV hCryptProv = 0;
+
+ if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+ return 1;
+
+ if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
+ CryptReleaseContext(hCryptProv, 0UL);
+ return 1;
+ }
+
+ CryptReleaseContext(hCryptProv, 0UL);
+ return 0;
+}
+
#ifdef _WIN32_WCE
static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
{