aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Hoersken <info@marc-hoersken.de>2012-04-09 23:24:55 +0200
committerDaniel Stenberg <daniel@haxx.se>2012-06-11 19:00:34 +0200
commit46792af73355a3961216f0af351a2e4508c1c389 (patch)
tree3e93505dfc836a8c0432f744685dd951601dcfdd
parent445245ca85f8fa2ecfa823e2e534244f15874a02 (diff)
schannel: Save session credential handles in session cache
-rw-r--r--lib/curl_schannel.c194
-rw-r--r--lib/curl_schannel.h15
-rw-r--r--lib/urldata.h8
3 files changed, 143 insertions, 74 deletions
diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index 761933820..9157bda56 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -38,7 +38,6 @@
/*
* TODO list for TLS/SSL implementation:
- * - implement session handling and re-use
* - implement write buffering
* - implement SSL/TLS shutdown
* - special cases: renegotiation, certificates, algorithms
@@ -49,8 +48,6 @@
#ifdef USE_WINDOWS_SSPI
#ifdef USE_SCHANNEL
-#include <schnlsp.h>
-
#include "urldata.h"
#include "curl_sspi.h"
#include "curl_schannel.h"
@@ -83,6 +80,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
SecBufferDesc outbuf_desc;
SCHANNEL_CRED schannel_cred;
SECURITY_STATUS sspi_status = SEC_E_OK;
+ curl_schannel_cred *old_cred = NULL;
struct in_addr addr;
#ifdef ENABLE_IPV6
struct in6_addr addr6;
@@ -91,60 +89,75 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
infof(data, "schannel: Connecting to %s:%d (step 1/3)\n",
conn->host.name, conn->remote_port);
- /* setup Schannel API options */
- memset(&schannel_cred, 0, sizeof(schannel_cred));
- schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
-
- if(data->set.ssl.verifypeer) {
- schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
- SCH_CRED_REVOCATION_CHECK_CHAIN;
- infof(data, "schannel: checking server certificate and revocation\n");
+ /* check for an existing re-usable credential handle */
+ if(!Curl_ssl_getsessionid(conn, &old_cred, NULL)) {
+ connssl->cred = old_cred;
+ infof(data, "schannel: re-using existing credential handle\n");
}
else {
- schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
- SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
- SCH_CRED_IGNORE_REVOCATION_OFFLINE;
- infof(data, "schannel: disable server certificate and revocation checks\n");
- }
+ /* setup Schannel API options */
+ memset(&schannel_cred, 0, sizeof(schannel_cred));
+ schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
+
+ if(data->set.ssl.verifypeer) {
+ schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
+ SCH_CRED_REVOCATION_CHECK_CHAIN;
+ infof(data, "schannel: checking server certificate and revocation\n");
+ }
+ else {
+ schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
+ SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+ infof(data, "schannel: disable server certificate and revocation checks\n");
+ }
- if(Curl_inet_pton(AF_INET, conn->host.name, &addr) ||
+ if(Curl_inet_pton(AF_INET, conn->host.name, &addr) ||
#ifdef ENABLE_IPV6
- Curl_inet_pton(AF_INET6, conn->host.name, &addr6) ||
+ Curl_inet_pton(AF_INET6, conn->host.name, &addr6) ||
#endif
- data->set.ssl.verifyhost < 2) {
- schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
- infof(data, "schannel: using IP address, disable SNI servername check\n");
- }
-
- switch(data->set.ssl.version) {
- 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_SSLv3:
- schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
- break;
- case CURL_SSLVERSION_SSLv2:
- schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
- break;
- }
-
- /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
- sspi_status = s_pSecFn->AcquireCredentialsHandleA(NULL,
- UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &schannel_cred,
- NULL, NULL, &connssl->cred_handle, &connssl->time_stamp);
-
- if(sspi_status != SEC_E_OK) {
- if(sspi_status == SEC_E_WRONG_PRINCIPAL)
- failf(data, "schannel: SNI or certificate check failed\n");
- else
- failf(data, "schannel: AcquireCredentialsHandleA failed: %d\n",
- sspi_status);
- return CURLE_SSL_CONNECT_ERROR;
- }
+ data->set.ssl.verifyhost < 2) {
+ schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
+ infof(data, "schannel: using IP address, disable SNI servername check\n");
+ }
+
+ switch(data->set.ssl.version) {
+ 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_SSLv3:
+ schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
+ break;
+ case CURL_SSLVERSION_SSLv2:
+ schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
+ break;
+ }
+
+ /* allocate memory for the re-usable credential handle */
+ connssl->cred = malloc(sizeof(curl_schannel_cred));
+ if (!connssl->cred) {
+ failf(data, "schannel: unable to allocate memory");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memset(connssl->cred, 0, sizeof(curl_schannel_cred));
+
+ /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
+ sspi_status = s_pSecFn->AcquireCredentialsHandleA(NULL,
+ UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL,
+ &connssl->cred->cred_handle, &connssl->cred->time_stamp);
- connssl->schannel = TRUE;
+ if(sspi_status != SEC_E_OK) {
+ if(sspi_status == SEC_E_WRONG_PRINCIPAL)
+ failf(data, "schannel: SNI or certificate check failed\n");
+ else
+ failf(data, "schannel: AcquireCredentialsHandleA failed: %d\n",
+ sspi_status);
+ free(connssl->cred);
+ connssl->cred = NULL;
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
/* setup output buffer */
outbuf.pvBuffer = NULL;
@@ -160,11 +173,19 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR |
ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
+ /* allocate memory for the security context handle */
+ connssl->ctxt = malloc(sizeof(curl_schannel_ctxt));
+ if (!connssl->ctxt) {
+ failf(data, "schannel: unable to allocate memory");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memset(connssl->ctxt, 0, sizeof(curl_schannel_ctxt));
+
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
- sspi_status = s_pSecFn->InitializeSecurityContextA(&connssl->cred_handle,
- NULL, conn->host.name, connssl->req_flags, 0, 0, NULL, 0,
- &connssl->ctxt_handle, &outbuf_desc,
- &connssl->ret_flags, &connssl->time_stamp);
+ sspi_status = s_pSecFn->InitializeSecurityContextA(
+ &connssl->cred->cred_handle, NULL, conn->host.name,
+ connssl->req_flags, 0, 0, NULL, 0, &connssl->ctxt->ctxt_handle,
+ &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
if(sspi_status != SEC_I_CONTINUE_NEEDED) {
if(sspi_status == SEC_E_WRONG_PRINCIPAL)
@@ -172,6 +193,8 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) {
else
failf(data, "schannel: initial InitializeSecurityContextA failed: %d\n",
sspi_status);
+ free(connssl->ctxt);
+ connssl->ctxt = NULL;
return CURLE_SSL_CONNECT_ERROR;
}
@@ -280,9 +303,9 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
sspi_status = s_pSecFn->InitializeSecurityContextA(
- &connssl->cred_handle, &connssl->ctxt_handle, conn->host.name,
- connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL, &outbuf_desc,
- &connssl->ret_flags, &connssl->time_stamp);
+ &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
+ conn->host.name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
+ &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
/* free buffer for received handshake data */
free(inbuf[0].pvBuffer);
@@ -363,11 +386,15 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) {
static CURLcode
schannel_connect_step3(struct connectdata *conn, int sockindex) {
+ CURLcode retcode = CURLE_OK;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ curl_schannel_cred *old_cred = NULL;
+ int incache;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+ /* check if the required context attributes are met */
if(connssl->ret_flags != connssl->req_flags) {
if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
failf(data, "schannel: failed to setup sequence detection\n");
@@ -384,6 +411,27 @@ schannel_connect_step3(struct connectdata *conn, int sockindex) {
return CURLE_SSL_CONNECT_ERROR;
}
+ /* save the current session data for possible re-use */
+ incache = !(Curl_ssl_getsessionid(conn, &old_cred, NULL));
+ if(incache) {
+ if(old_cred != connssl->cred) {
+ infof(data, "schannel: old credential handle is stale, removing\n");
+ Curl_ssl_delsessionid(conn, old_cred);
+ incache = FALSE;
+ }
+ }
+ if(!incache) {
+ retcode = Curl_ssl_addsessionid(conn, connssl->cred,
+ sizeof(curl_schannel_cred));
+ if(retcode) {
+ failf(data, "schannel: failed to store credential handle\n");
+ return retcode;
+ }
+ else {
+ infof(data, "schannel: stored crendential handle\n");
+ }
+ }
+
connssl->connecting_state = ssl_connect_done;
return CURLE_OK;
@@ -512,9 +560,9 @@ schannel_send(struct connectdata *conn, int sockindex,
/* check if the maximum stream sizes were queried */
if(connssl->stream_sizes.cbMaximumMessage == 0) {
- sspi_status = s_pSecFn->QueryContextAttributesA(&connssl->ctxt_handle,
- SECPKG_ATTR_STREAM_SIZES,
- &connssl->stream_sizes);
+ sspi_status = s_pSecFn->QueryContextAttributesA(
+ &connssl->ctxt->ctxt_handle,
+ SECPKG_ATTR_STREAM_SIZES, &connssl->stream_sizes);
if(sspi_status != SEC_E_OK) {
*err = CURLE_SEND_ERROR;
return -1;
@@ -561,7 +609,7 @@ schannel_send(struct connectdata *conn, int sockindex,
memcpy(outbuf[1].pvBuffer, buf, len);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
- sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt_handle, 0,
+ sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
&outbuf_desc, 0);
/* check if the message was encrypted */
@@ -681,7 +729,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
inbuf_desc.ulVersion = SECBUFFER_VERSION;
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
- sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt_handle,
+ sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
&inbuf_desc, 0, NULL);
infof(data, "schannel: DecryptMessage %d\n", sspi_status);
@@ -805,7 +853,7 @@ Curl_schannel_connect(struct connectdata *conn, int sockindex) {
bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex) {
const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- if(connssl->schannel) /* SSL is in use */
+ if(connssl->use) /* SSL is in use */
return (connssl->encdata_offset > 0 ||
connssl->decdata_offset > 0 ) ? TRUE : FALSE;
else
@@ -819,10 +867,11 @@ void Curl_schannel_close(struct connectdata *conn, int sockindex) {
infof(data, "schannel: Closing connection with %s:%d\n",
conn->host.name, conn->remote_port);
- /* free SSPI Schannel API context and handle */
- if(connssl->schannel) {
- s_pSecFn->DeleteSecurityContext(&connssl->ctxt_handle);
- s_pSecFn->FreeCredentialsHandle(&connssl->cred_handle);
+ /* free SSPI Schannel API security context handle */
+ if(connssl->ctxt) {
+ s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
+ free(connssl->ctxt);
+ connssl->ctxt = NULL;
}
/* free internal buffer for received encrypted data */
@@ -846,6 +895,15 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) {
return CURLE_NOT_BUILT_IN; /* TODO: implement SSL/TLS shutdown */
}
+void Curl_schannel_session_free(void *ptr) {
+ curl_schannel_cred *cred = ptr;
+
+ if(cred) {
+ s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
+ free(cred);
+ }
+}
+
int Curl_schannel_init() {
return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
}
diff --git a/lib/curl_schannel.h b/lib/curl_schannel.h
index 3de932b9d..fa6fc90c2 100644
--- a/lib/curl_schannel.h
+++ b/lib/curl_schannel.h
@@ -26,10 +26,22 @@
#ifdef USE_WINDOWS_SSPI
#ifdef USE_SCHANNEL
+#include <schnlsp.h>
+
#ifndef UNISP_NAME_A
#define UNISP_NAME_A "Microsoft Unified Security Protocol Provider"
#endif
+typedef struct curl_schannel_cred {
+ CredHandle cred_handle;
+ TimeStamp time_stamp;
+} curl_schannel_cred;
+
+typedef struct curl_schannel_ctxt {
+ CtxtHandle ctxt_handle;
+ TimeStamp time_stamp;
+} curl_schannel_ctxt;
+
CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex);
CURLcode Curl_schannel_connect_nonblocking(struct connectdata *conn,
@@ -39,6 +51,7 @@ CURLcode Curl_schannel_connect_nonblocking(struct connectdata *conn,
bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex);
void Curl_schannel_close(struct connectdata *conn, int sockindex);
int Curl_schannel_shutdown(struct connectdata *conn, int sockindex);
+void Curl_schannel_session_free(void *ptr);
int Curl_schannel_init();
void Curl_schannel_cleanup();
@@ -49,7 +62,7 @@ size_t Curl_schannel_version(char *buffer, size_t size);
#define curlssl_cleanup Curl_schannel_cleanup
#define curlssl_connect Curl_schannel_connect
#define curlssl_connect_nonblocking Curl_schannel_connect_nonblocking
-#define curlssl_session_free(x) (x=x, CURLE_NOT_BUILT_IN)
+#define curlssl_session_free Curl_schannel_session_free
#define curlssl_close_all(x) (x=x, CURLE_NOT_BUILT_IN)
#define curlssl_close Curl_schannel_close
#define curlssl_shutdown Curl_schannel_shutdown
diff --git a/lib/urldata.h b/lib/urldata.h
index 26c0581fe..2b972d5ee 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -132,8 +132,8 @@
#endif /* USE_AXTLS */
#ifdef USE_SCHANNEL
-#include <schnlsp.h>
#include "curl_sspi.h"
+#include "curl_schannel.h"
#endif
#ifdef HAVE_NETINET_IN_H
@@ -288,10 +288,8 @@ struct ssl_connect_data {
SSL* ssl;
#endif /* USE_AXTLS */
#ifdef USE_SCHANNEL
- bool schannel;
- TimeStamp time_stamp;
- CredHandle cred_handle;
- CtxtHandle ctxt_handle;
+ curl_schannel_cred *cred;
+ curl_schannel_ctxt *ctxt;
SecPkgContext_StreamSizes stream_sizes;
ssl_connect_state connecting_state;
size_t encdata_length, decdata_length;