summaryrefslogtreecommitdiffstats
path: root/Utilities/cmcurl/lib/vtls/schannel.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/lib/vtls/schannel.c')
-rw-r--r--Utilities/cmcurl/lib/vtls/schannel.c297
1 files changed, 213 insertions, 84 deletions
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c
index 2174e21..f991ec9 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.c
+++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -5,13 +5,13 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2015, Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
* Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
- * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2016, 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
- * are also available at http://curl.haxx.se/docs/copyright.html.
+ * are also available at https://curl.haxx.se/docs/copyright.html.
*
* 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
@@ -56,11 +56,23 @@
#include "inet_pton.h" /* for IP addr SNI check */
#include "curl_multibyte.h"
#include "warnless.h"
+#include "x509asn1.h"
#include "curl_printf.h"
+#include "system_win32.h"
+
+ /* The last #include file should be: */
#include "curl_memory.h"
-/* The last #include file should be: */
#include "memdebug.h"
+/* 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
+*/
+#if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)
+# define HAS_ALPN 1
+#endif
+
/* Uncomment to force verbose output
* #define infof(x, y, ...) printf(y, __VA_ARGS__)
* #define failf(x, y, ...) printf(y, __VA_ARGS__)
@@ -93,10 +105,15 @@ static CURLcode
schannel_connect_step1(struct connectdata *conn, int sockindex)
{
ssize_t written = -1;
- struct SessionHandle *data = conn->data;
+ struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
SecBuffer outbuf;
SecBufferDesc outbuf_desc;
+ SecBuffer inbuf;
+ SecBufferDesc inbuf_desc;
+#ifdef HAS_ALPN
+ unsigned char alpn_buffer[128];
+#endif
SCHANNEL_CRED schannel_cred;
SECURITY_STATUS sspi_status = SEC_E_OK;
struct curl_schannel_cred *old_cred = NULL;
@@ -110,12 +127,24 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
conn->host.name, conn->remote_port);
+ connssl->cred = NULL;
+
/* check for an existing re-usable credential handle */
- if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)) {
- connssl->cred = old_cred;
- infof(data, "schannel: re-using existing credential handle\n");
+ if(conn->ssl_config.sessionid) {
+ Curl_ssl_sessionid_lock(conn);
+ if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)) {
+ connssl->cred = old_cred;
+ infof(data, "schannel: re-using existing credential handle\n");
+
+ /* increment the reference counter of the credential/session handle */
+ connssl->cred->refcount++;
+ infof(data, "schannel: incremented credential handle refcount = %d\n",
+ connssl->cred->refcount);
+ }
+ Curl_ssl_sessionid_unlock(conn);
}
- else {
+
+ if(!connssl->cred) {
/* setup Schannel API options */
memset(&schannel_cred, 0, sizeof(schannel_cred));
schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
@@ -188,8 +217,10 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
return CURLE_OUT_OF_MEMORY;
}
memset(connssl->cred, 0, sizeof(struct curl_schannel_cred));
+ connssl->cred->refcount = 1;
- /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
+ /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx
+ */
sspi_status =
s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
SECPKG_CRED_OUTBOUND, NULL,
@@ -218,6 +249,63 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
}
+#ifdef HAS_ALPN
+ /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above */
+ if(conn->bits.tls_enable_alpn &&
+ Curl_verify_windows_version(6, 3, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+ int cur = 0;
+ int list_start_index = 0;
+ unsigned int* extension_len = NULL;
+ 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. */
+ extension_len = (unsigned int*)(&alpn_buffer[cur]);
+ cur += sizeof(unsigned int);
+
+ /* The next four bytes are an indicator that this buffer will contain
+ ALPN data, as opposed to NPN, for example. */
+ *(unsigned int*)&alpn_buffer[cur] =
+ 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);
+
+ *list_len = curlx_uitous(cur - list_start_index);
+ *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
+
/* setup output buffer */
InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
@@ -240,11 +328,11 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
if(!host_name)
return CURLE_OUT_OF_MEMORY;
- /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
+ /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
sspi_status = s_pSecFn->InitializeSecurityContext(
&connssl->cred->cred_handle, NULL, host_name,
- connssl->req_flags, 0, 0, NULL, 0, &connssl->ctxt->ctxt_handle,
+ connssl->req_flags, 0, 0, &inbuf_desc, 0, &connssl->ctxt->ctxt_handle,
&outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
Curl_unicodefree(host_name);
@@ -291,7 +379,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
{
int i;
ssize_t nread = -1, written = -1;
- struct SessionHandle *data = conn->data;
+ struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
unsigned char *reallocated_buffer;
size_t reallocated_length;
@@ -407,8 +495,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
if(!host_name)
return CURLE_OUT_OF_MEMORY;
- /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
-
+ /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
+ */
sspi_status = s_pSecFn->InitializeSecurityContext(
&connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
host_name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
@@ -531,10 +619,13 @@ static CURLcode
schannel_connect_step3(struct connectdata *conn, int sockindex)
{
CURLcode result = CURLE_OK;
- struct SessionHandle *data = conn->data;
+ struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- struct curl_schannel_cred *old_cred = NULL;
- bool incache;
+ SECURITY_STATUS sspi_status = SEC_E_OK;
+ CERT_CONTEXT *ccert_context = NULL;
+#ifdef HAS_ALPN
+ SecPkgContext_ApplicationProtocol alpn_result;
+#endif
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
@@ -559,34 +650,98 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
return CURLE_SSL_CONNECT_ERROR;
}
- /* increment the reference counter of the credential/session handle */
- if(connssl->cred && connssl->ctxt) {
- connssl->cred->refcount++;
- infof(data, "schannel: incremented credential handle refcount = %d\n",
- connssl->cred->refcount);
+#ifdef HAS_ALPN
+ /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above */
+ if(conn->bits.tls_enable_alpn &&
+ Curl_verify_windows_version(6, 3, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+ sspi_status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
+ 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
/* save the current session data for possible re-use */
- 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);
- incache = FALSE;
+ if(conn->ssl_config.sessionid) {
+ bool incache;
+ struct curl_schannel_cred *old_cred = NULL;
+
+ Curl_ssl_sessionid_lock(conn);
+ 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");
+ /* we're not taking old_cred ownership here, no refcount++ is needed */
+ Curl_ssl_delsessionid(conn, (void *)old_cred);
+ incache = FALSE;
+ }
}
+ if(!incache) {
+ result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
+ sizeof(struct curl_schannel_cred));
+ 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 */
+ connssl->cred->refcount++;
+ infof(data, "schannel: stored credential handle in session cache\n");
+ }
+ }
+ Curl_ssl_sessionid_unlock(conn);
}
- if(!incache) {
- result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
- sizeof(struct curl_schannel_cred));
- if(result) {
- failf(data, "schannel: failed to store credential handle");
- return result;
+ if(data->set.ssl.certinfo) {
+ sspi_status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
+ 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;
}
- else {
- connssl->cred->cached = TRUE;
- infof(data, "schannel: stored credential handle in session cache\n");
+
+ 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;
}
connssl->connecting_state = ssl_connect_done;
@@ -599,7 +754,7 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
bool nonblocking, bool *done)
{
CURLcode result;
- struct SessionHandle *data = conn->data;
+ struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
long timeout_ms;
@@ -759,7 +914,7 @@ schannel_send(struct connectdata *conn, int sockindex,
/* copy data into output buffer */
memcpy(outbuf[1].pvBuffer, buf, len);
- /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
+ /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
&outbuf_desc, 0);
@@ -858,7 +1013,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
{
size_t size = 0;
ssize_t nread = -1;
- struct SessionHandle *data = conn->data;
+ struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
unsigned char *reallocated_buffer;
size_t reallocated_length;
@@ -973,7 +1128,8 @@ schannel_recv(struct connectdata *conn, int sockindex,
InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&inbuf_desc, inbuf, 4);
- /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
+ /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx
+ */
sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
&inbuf_desc, 0, NULL);
@@ -1120,23 +1276,8 @@ cleanup:
*/
if(len && !connssl->decdata_offset && connssl->recv_connection_closed &&
!connssl->recv_sspi_close_notify) {
- BOOL isWin2k;
- ULONGLONG cm;
- OSVERSIONINFOEX osver;
-
- memset(&osver, 0, sizeof(osver));
- osver.dwOSVersionInfoSize = sizeof(osver);
- osver.dwMajorVersion = 5;
-
- cm = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
- cm = VerSetConditionMask(cm, VER_MINORVERSION, VER_EQUAL);
- cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
- cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
-
- isWin2k = VerifyVersionInfo(&osver,
- (VER_MAJORVERSION | VER_MINORVERSION |
- VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR),
- cm);
+ bool isWin2k = Curl_verify_windows_version(5, 0, PLATFORM_WINNT,
+ VERSION_EQUAL);
if(isWin2k && sspi_status == SEC_E_OK)
connssl->recv_sspi_close_notify = true;
@@ -1204,7 +1345,7 @@ bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
if(connssl->use) /* SSL/TLS is in use */
return (connssl->encdata_offset > 0 ||
- connssl->decdata_offset > 0 ) ? TRUE : FALSE;
+ connssl->decdata_offset > 0) ? TRUE : FALSE;
else
return FALSE;
}
@@ -1218,10 +1359,10 @@ void Curl_schannel_close(struct connectdata *conn, int sockindex)
int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
{
- /* See http://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
+ /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
* Shutting Down an Schannel Connection
*/
- struct SessionHandle *data = conn->data;
+ struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
@@ -1294,19 +1435,10 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
/* 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);
- }
+ Curl_ssl_sessionid_lock(conn);
+ Curl_schannel_session_free(connssl->cred);
+ Curl_ssl_sessionid_unlock(conn);
+ connssl->cred = NULL;
}
/* free internal buffer for received encrypted data */
@@ -1328,16 +1460,13 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
void Curl_schannel_session_free(void *ptr)
{
+ /* this is expected to be called under sessionid lock */
struct curl_schannel_cred *cred = ptr;
- if(cred && cred->cached) {
- if(cred->refcount == 0) {
- s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
- Curl_safefree(cred);
- }
- else {
- cred->cached = FALSE;
- }
+ cred->refcount--;
+ if(cred->refcount == 0) {
+ s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
+ Curl_safefree(cred);
}
}
@@ -1379,7 +1508,7 @@ int Curl_schannel_random(unsigned char *entropy, size_t length)
static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
{
SECURITY_STATUS status;
- struct SessionHandle *data = conn->data;
+ struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
CURLcode result = CURLE_OK;
CERT_CONTEXT *pCertContextServer = NULL;