2025-04-27 07:49:33 -04:00

2144 lines
56 KiB
C++

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name :
sslcontext.cxx
Abstract:
SSL stream context
Author:
Bilal Alam (BAlam) 29-March-2000
Environment:
Win32 - User Mode
Project:
Stream Filter Worker Process
--*/
#include "precomp.hxx"
//static
HRESULT
SSL_STREAM_CONTEXT::Initialize(
VOID
)
/*++
Routine Description:
Initialize all SSL global data
Arguments:
None
Return Value:
HRESULT
--*/
{
HRESULT hr = S_OK;
hr = CERT_STORE::Initialize();
if ( FAILED( hr ) )
{
return hr;
}
hr = SERVER_CERT::Initialize();
if ( FAILED( hr ) )
{
CERT_STORE::Terminate();
return hr;
}
hr = SITE_CREDENTIALS::Initialize();
if ( FAILED( hr ) )
{
SERVER_CERT::Terminate();
CERT_STORE::Terminate();
return hr;
}
hr = SITE_BINDING::Initialize();
if ( FAILED( hr ) )
{
SITE_CREDENTIALS::Terminate();
SERVER_CERT::Terminate();
CERT_STORE::Terminate();
return hr;
}
//
// SITE_CONFIG uses
// SERVER_CERT,
// SITE_CREDENTIALS and
//
hr = SITE_CONFIG::Initialize();
if ( FAILED( hr ) )
{
SITE_BINDING::Terminate();
SITE_CREDENTIALS::Terminate();
SERVER_CERT::Terminate();
CERT_STORE::Terminate();
return hr;
}
return hr;
}
//static
VOID
SSL_STREAM_CONTEXT::Terminate(
VOID
)
/*++
Routine Description:
Terminate SSL
Arguments:
None
Return Value:
None
--*/
{
SITE_CONFIG::Terminate();
SITE_BINDING::Terminate();
SITE_CREDENTIALS::Terminate();
SERVER_CERT::Terminate();
CERT_STORE::Terminate();
}
SSL_STREAM_CONTEXT::SSL_STREAM_CONTEXT(
UL_CONTEXT * pUlContext
)
: STREAM_CONTEXT( pUlContext ),
_pSiteConfig( NULL ),
_sslState( SSL_STATE_HANDSHAKE_START ),
_fRenegotiate( FALSE ),
_fValidContext( FALSE ),
_cbReReadOffset( 0 ),
_pClientCert( NULL ),
_fDoCertMap( FALSE ),
_hDSMappedToken( NULL ),
_cbDecrypted( 0 )
{
//
// Initialize security buffer structs
//
//
// Setup buffer to hold incoming raw data
//
_Message.ulVersion = SECBUFFER_VERSION;
_Message.cBuffers = 4;
_Message.pBuffers = _Buffers;
_Buffers[0].BufferType = SECBUFFER_EMPTY;
_Buffers[1].BufferType = SECBUFFER_EMPTY;
_Buffers[2].BufferType = SECBUFFER_EMPTY;
_Buffers[3].BufferType = SECBUFFER_EMPTY;
//
// Setup buffer for ASC to return raw data to be sent to client
//
_MessageOut.ulVersion = SECBUFFER_VERSION;
_MessageOut.cBuffers = 4;
_MessageOut.pBuffers = _OutBuffers;
_OutBuffers[0].BufferType = SECBUFFER_EMPTY;
_OutBuffers[1].BufferType = SECBUFFER_EMPTY;
_OutBuffers[2].BufferType = SECBUFFER_EMPTY;
_OutBuffers[3].BufferType = SECBUFFER_EMPTY;
//
// Setup buffer for app data to be encrypted
//
_EncryptMessage.ulVersion = SECBUFFER_VERSION;
_EncryptMessage.cBuffers = 4;
_EncryptMessage.pBuffers = _EncryptBuffers;
_EncryptBuffers[0].BufferType = SECBUFFER_EMPTY;
_EncryptBuffers[1].BufferType = SECBUFFER_EMPTY;
_EncryptBuffers[2].BufferType = SECBUFFER_EMPTY;
_EncryptBuffers[3].BufferType = SECBUFFER_EMPTY;
ZeroMemory( &_hContext, sizeof( _hContext ) );
ZeroMemory( &_ulSslInfo, sizeof( _ulSslInfo ) );
ZeroMemory( &_ulCertInfo, sizeof( _ulCertInfo ) );
}
SSL_STREAM_CONTEXT::~SSL_STREAM_CONTEXT()
{
if ( _fValidContext )
{
DeleteSecurityContext( &_hContext );
_fValidContext = FALSE;
}
if ( _pSiteConfig != NULL )
{
_pSiteConfig->DereferenceSiteConfig();
_pSiteConfig = NULL;
}
if( _hDSMappedToken != NULL )
{
CloseHandle( _hDSMappedToken );
_hDSMappedToken = NULL;
}
if( _pClientCert != NULL )
{
CertFreeCertificateContext( _pClientCert );
_pClientCert = NULL;
}
}
HRESULT
SSL_STREAM_CONTEXT::ProcessNewConnection(
CONNECTION_INFO * pConnectionInfo
)
/*++
Routine Description:
Handle a new raw connection
Arguments:
pConnectionInfo - The magic connection information
Return Value:
HRESULT
--*/
{
DWORD dwSiteId = 0;
HRESULT hr = S_OK;
SITE_CONFIG * pSiteConfig = NULL;
DBG_ASSERT( _sslState == SSL_STATE_HANDSHAKE_START );
DBG_ASSERT( _pSiteConfig == NULL );
//
// Determine the site for this request
//
hr = SITE_BINDING::GetSiteId( pConnectionInfo->LocalAddress,
pConnectionInfo->LocalPort,
&dwSiteId );
if ( FAILED( hr ) )
{
if ( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
{
//
// Not finding the site just means this is not an SSL site
//
return S_OK;
}
return hr;
}
QueryUlContext()->SetIsSecure( TRUE );
DBG_ASSERT( dwSiteId != 0 );
//
// Now find a server cert. If we can't this connection is toast!
//
hr = SITE_CONFIG::GetSiteConfig( dwSiteId,
&pSiteConfig );
if ( FAILED( hr ) )
{
return hr;
}
//
// Store away the site config for this connection
//
DBG_ASSERT( pSiteConfig != NULL );
_pSiteConfig = pSiteConfig;
return S_OK;
}
HRESULT
SSL_STREAM_CONTEXT::ProcessRawReadData(
RAW_STREAM_INFO * pRawStreamInfo,
BOOL * pfReadMore,
BOOL * pfComplete
)
/*++
Routine Description:
Handle an SSL read completion off the wire
Arguments:
pRawStreamInfo - Points to input stream and size
pfReadMore - Set to TRUE if we should read more
pfComplete - Set to TRUE if we should disconnect
Return Value:
HRESULT
--*/
{
HRESULT hr = S_OK;
BOOL fSecureConnection;
BOOL fExtraData = FALSE;
//
// Do we have site config? If not, then this isn't an SSL connection
//
if ( _pSiteConfig == NULL )
{
return S_OK;
}
//
// Loop for extra data
// Sometimes one RawStreamInfo buffer may contain multiple blobs
// some to be processed by DoHandshake() and some by DoDecrypt()
// The do-while loop enables switching between these 2 functions as needed
//
do
{
fExtraData = FALSE;
*pfReadMore = FALSE;
*pfComplete = FALSE;
//
// Either continue handshake or immediate decrypt data
//
switch ( _sslState )
{
case SSL_STATE_HANDSHAKE_START:
case SSL_STATE_HANDSHAKE_IN_PROGRESS:
hr = DoHandshake( pRawStreamInfo,
pfReadMore,
pfComplete,
&fExtraData );
break;
case SSL_STATE_HANDSHAKE_COMPLETE:
hr = DoDecrypt( pRawStreamInfo,
pfReadMore,
pfComplete,
&fExtraData );
break;
default:
DBG_ASSERT( FALSE );
}
if ( FAILED( hr ) )
{
break;
}
//
// Is there still some extra data to be processed?
//
}while( fExtraData );
return hr;
}
HRESULT
SSL_STREAM_CONTEXT::ProcessRawWriteData(
RAW_STREAM_INFO * pRawStreamInfo,
BOOL * pfComplete
)
/*++
Routine Description:
Called on read completion from app. Data received
from application must be encrypted and sent to client
using RawWrite.
Application may have also requested renegotiation
(with or without mapping) if client certificate
is not yet present
Arguments:
pRawStreamInfo - Points to input stream and size
pfComplete - Set to TRUE if we should disconnect
Return Value:
HRESULT
--*/
{
HRESULT hr;
HTTP_FILTER_BUFFER_TYPE bufferType;
if ( _pSiteConfig == NULL )
{
//
// We never found SSL to be relevent for this connection. Do nothing
//
return S_OK;
}
DBG_ASSERT( _sslState == SSL_STATE_HANDSHAKE_COMPLETE );
bufferType = QueryUlContext()->QueryFilterBufferType();
//
// Is this data from the application, or a request for renegotiation?
//
if ( bufferType == HttpFilterBufferSslRenegotiate ||
bufferType == HttpFilterBufferSslRenegotiateAndMap )
{
//
// If we have already renegotiated a client certificate, then there
// is nothing to do, but read again for stream data
//
if ( _fRenegotiate )
{
hr = S_OK;
}
else
{
if ( bufferType == HttpFilterBufferSslRenegotiateAndMap )
{
_fDoCertMap = TRUE;
}
hr = DoRenegotiate();
}
}
else
{
DBG_ASSERT( bufferType == HttpFilterBufferHttpStream );
hr = DoEncrypt( pRawStreamInfo,
pfComplete );
}
return hr;
}
HRESULT
SSL_STREAM_CONTEXT::SendDataBack(
RAW_STREAM_INFO * pRawStreamInfo
)
/*++
Routine Description:
Send back data (different then ProcessRawWrite because in this case
the data was not received by the application, but rather a raw filter)
Arguments:
pRawStreamInfo - Points to input stream and size
Return Value:
HRESULT
--*/
{
BOOL fComplete;
HRESULT hr;
if ( pRawStreamInfo == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
if ( _pSiteConfig != NULL )
{
//
// We must have completed the handshake to get here, since this path
// is only invoked by ISAPI filters
//
DBG_ASSERT( _sslState == SSL_STATE_HANDSHAKE_COMPLETE );
hr = DoEncrypt( pRawStreamInfo,
&fComplete );
if ( FAILED( hr ) )
{
return hr;
}
}
//
// Send back the data
//
return QueryUlContext()->DoRawWrite( UL_CONTEXT_FLAG_SYNC,
pRawStreamInfo->pbBuffer,
pRawStreamInfo->cbData,
NULL );
}
HRESULT
SSL_STREAM_CONTEXT::DoHandshake(
RAW_STREAM_INFO * pRawStreamInfo,
BOOL * pfReadMore,
BOOL * pfComplete,
BOOL * pfExtraData
)
/*++
Routine Description:
Do the handshake thing with AcceptSecurityContext()
Arguments:
pRawStreamInfo - Raw data buffer
pfReadMore - Set to true if more data should be read
pfComplete - Set to true if we should disconnect
Return Value:
HRESULT
--*/
{
SECURITY_STATUS secStatus = SEC_E_OK;
HRESULT hr = E_FAIL;
SECURITY_STATUS secInfo;
DWORD dwFlags = SSL_ASC_FLAGS;
DWORD dwContextAttributes;
TimeStamp tsExpiry;
CtxtHandle hOutContext;
if ( pRawStreamInfo == NULL ||
pfReadMore == NULL ||
pfComplete == NULL )
{
DBG_ASSERT( FALSE );
hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
goto ExitPoint;
}
*pfReadMore = FALSE;
*pfComplete = FALSE;
*pfExtraData = FALSE;
IF_DEBUG( SCHANNEL_CALLS )
{
DBGPRINTF(( DBG_CONTEXT,
"DoHandshake(): _cbDecrypted = %d, _cbReReadOffset=%d\n",
_cbDecrypted,
_cbReReadOffset
));
}
DBG_ASSERT( _pSiteConfig != NULL );
//
// Setup a call to AcceptSecurityContext()
//
_Buffers[ 0 ].pvBuffer = pRawStreamInfo->pbBuffer + _cbReReadOffset;
_Buffers[ 0 ].cbBuffer = pRawStreamInfo->cbData - _cbReReadOffset;
_Buffers[ 0 ].BufferType = SECBUFFER_TOKEN;
_Buffers[ 1 ].BufferType = SECBUFFER_EMPTY;
_Buffers[ 2 ].BufferType = SECBUFFER_EMPTY;
_Buffers[ 3 ].BufferType = SECBUFFER_EMPTY;
_OutBuffers[ 0 ].pvBuffer = NULL;
//
// Are we renegotiating for client cert?
// if _pSiteConfig->QueryRequireClientCert() is TRUE
// it means that Client certificates are enabled on root level
// of the site. In that case we enable optimization where
// client certificates are negotiated right away to eliminate
// expensive renegotiation
DBG_ASSERT( _pSiteConfig != NULL );
if ( _fRenegotiate || _pSiteConfig->QueryRequireClientCert() )
{
dwFlags |= ASC_REQ_MUTUAL_AUTH;
}
if ( _sslState == SSL_STATE_HANDSHAKE_START )
{
secStatus = AcceptSecurityContext( QueryCredentials(),
NULL,
&_Message,
dwFlags,
SECURITY_NATIVE_DREP,
&_hContext,
&_MessageOut,
&dwContextAttributes,
&tsExpiry );
IF_DEBUG( SCHANNEL_CALLS )
{
DBGPRINTF(( DBG_CONTEXT,
"AcceptSecurityContext() secStatus=0x%x\n",
secStatus
));
}
if ( SUCCEEDED( secStatus ) )
{
_cbHeader = 0;
_cbTrailer = 0;
_cbBlockSize = 0;
_cbMaximumMessage = 0;
_fValidContext = TRUE;
_sslState = SSL_STATE_HANDSHAKE_IN_PROGRESS;
}
}
else
{
DBG_ASSERT( _sslState == SSL_STATE_HANDSHAKE_IN_PROGRESS );
//
// We already have a valid context. We can directly call
// AcceptSecurityContext()!
//
hOutContext = _hContext;
DBG_ASSERT( _fValidContext );
secStatus = AcceptSecurityContext( QueryCredentials(),
&_hContext,
&_Message,
dwFlags,
SECURITY_NATIVE_DREP,
&hOutContext,
&_MessageOut,
&dwContextAttributes,
&tsExpiry );
IF_DEBUG( SCHANNEL_CALLS )
{
DBGPRINTF(( DBG_CONTEXT,
"AcceptSecurityContext() secStatus=0x%x\n",
secStatus
));
}
if ( SUCCEEDED( secStatus ) )
{
//
// Store the context (LAME)
//
_hContext = hOutContext;
}
}
//
// Either way, the secStatus tells us how to proceed
//
if ( SUCCEEDED( secStatus ) )
{
//
// We haven't failed yet. But we may not be complete. First
// send back any data to the client
//
if ( _OutBuffers[ 0 ].pvBuffer )
{
DBG_ASSERT( _OutBuffers[ 0 ].cbBuffer != 0 );
hr = QueryUlContext()->DoRawWrite( UL_CONTEXT_FLAG_SYNC,
_OutBuffers[ 0 ].pvBuffer,
_OutBuffers[ 0 ].cbBuffer,
NULL );
if ( FAILED( hr ) )
{
goto ExitPoint;
}
}
if ( secStatus == SEC_E_OK )
{
//
// We must be done with handshake
//
hr = DoHandshakeCompleted();
if ( FAILED( hr ) )
{
goto ExitPoint;
}
}
//
// If the input buffer has more info to be SChannel'ized, then do
// so now. If we haven't completed handshake, then call DoHandShake
// again. Otherwise, call DoDecrypt
//
if ( _Buffers[ 1 ].BufferType == SECBUFFER_EXTRA )
{
IF_DEBUG( SCHANNEL_CALLS )
{
for ( int i = 1; i < 4; i++ )
{
if( _Buffers[ i ].BufferType != 0 )
{
DBGPRINTF(( DBG_CONTEXT,
"AcceptSecurityContext returned extra buffer"
" - %d bytes buffer type: %d\n",
_Buffers[ i ].cbBuffer,
_Buffers[ i ].BufferType
));
}
}
}
//
// We better have valid extra data
// only cbBuffer is used, pvBuffer is not used with SECBUFFER_EXTRA
//
DBG_ASSERT( _Buffers[ 1 ].cbBuffer != 0 );
//
// Move extra data right after decrypted data (if any)
//
memmove( pRawStreamInfo->pbBuffer + _cbDecrypted,
pRawStreamInfo->pbBuffer + pRawStreamInfo->cbData
- _Buffers[ 1 ].cbBuffer,
_Buffers[ 1 ].cbBuffer
);
//
// Now we have to adjust pRawStreamInfo->cbData and _cbReReadOffset
//
pRawStreamInfo->cbData = ( _cbDecrypted +
_Buffers[ 1 ].cbBuffer );
_cbReReadOffset = _cbDecrypted;
*pfExtraData = TRUE;
//
// caller has to detect that some data is
// still in the buffer not processed and
//
hr = S_OK;
goto ExitPoint;
}
else // no extra buffer
{
//
// There is no extra data to be processed
// If we got here as the result of renegotiation
// there may be some decrypted data in StreamInfo buffer already
//
// (without renegotiation _cbDecryted must always be 0
// because SEC_I_RENEGOTIATE is the only way to get
// from DoDecrypt() to DoHandshake() )
//
DBG_ASSERT ( _fRenegotiate || _cbDecrypted == 0 );
pRawStreamInfo->cbData = _cbDecrypted;
_cbReReadOffset = _cbDecrypted;
if ( _sslState != SSL_STATE_HANDSHAKE_COMPLETE )
{
//
// If we have no more data, and we still haven't completed the
// handshake, then read some more data
//
*pfReadMore = TRUE;
hr = S_OK;
goto ExitPoint;
}
}
//
// final return from DoHandshake on handshake completion
// Cleanup _cbDecrypted and _cbReReadOffset to make
// sure that next ProcessRawReadData() will work fine
//
_cbReReadOffset = 0;
_cbDecrypted = 0;
hr = S_OK;
goto ExitPoint;
}
else
{
//
// AcceptSecurityContext() failed!
//
if ( secStatus == SEC_E_INCOMPLETE_MESSAGE )
{
*pfReadMore = TRUE;
hr = S_OK;
goto ExitPoint;
}
else
{
//
// Maybe we can send a more useful message to the client
//
if ( dwContextAttributes & ASC_RET_EXTENDED_ERROR )
{
if ( _OutBuffers[ 0 ].pvBuffer!= NULL &&
_OutBuffers[ 0 ].cbBuffer != 0 )
{
hr = QueryUlContext()->DoRawWrite( UL_CONTEXT_FLAG_SYNC,
_OutBuffers[ 0 ].pvBuffer,
_OutBuffers[ 0 ].cbBuffer,
NULL );
if( FAILED( hr ) )
{
goto ExitPoint;
}
}
}
}
hr = secStatus;
}
ExitPoint:
if ( _OutBuffers[ 0 ].pvBuffer != NULL )
{
FreeContextBuffer( _OutBuffers[ 0 ].pvBuffer );
_OutBuffers[ 0 ].pvBuffer = NULL;
}
return hr;
}
HRESULT
SSL_STREAM_CONTEXT::DoHandshakeCompleted()
{
HRESULT hr = S_OK;
SECURITY_STATUS secStatus = SEC_E_OK;
SecPkgContext_StreamSizes StreamSizes;
HTTP_FILTER_BUFFER ulFilterBuffer;
_sslState = SSL_STATE_HANDSHAKE_COMPLETE;
//
// Get some buffer size info for this connection. We only need
// to do this on completion of the initial handshake, and NOT
// subsequent renegotiation handshake (if any)
//
if ( !_cbHeader && !_cbTrailer )
{
secStatus = QueryContextAttributes( &_hContext,
SECPKG_ATTR_STREAM_SIZES,
&StreamSizes );
if ( FAILED( secStatus ) )
{
return secStatus;
}
_cbHeader = StreamSizes.cbHeader;
_cbTrailer = StreamSizes.cbTrailer;
_cbBlockSize = StreamSizes.cbBlockSize;
_cbMaximumMessage = StreamSizes.cbMaximumMessage;
}
//
// Build up a message for the application indicating stuff
// about the negotiated connection
//
// If this is a renegotiate request, then we have already sent
// and calculated the SSL_INFO, so just send the CERT_INFO
//
if ( !_fRenegotiate )
{
hr = BuildSslInfo();
if ( FAILED( hr ) )
{
return hr;
}
ulFilterBuffer.BufferType = HttpFilterBufferSslInitInfo;
ulFilterBuffer.pBuffer = (PBYTE) &_ulSslInfo;
ulFilterBuffer.BufferSize = sizeof( _ulSslInfo );
}
else
{
if ( SUCCEEDED( RetrieveClientCertAndToken() ) )
{
hr = BuildClientCertInfo();
if ( FAILED( hr ) )
{
return hr;
}
}
if ( _fDoCertMap )
{
ulFilterBuffer.BufferType = HttpFilterBufferSslClientCertAndMap;
}
else
{
ulFilterBuffer.BufferType = HttpFilterBufferSslClientCert;
}
ulFilterBuffer.pBuffer = (PBYTE) &_ulCertInfo;
ulFilterBuffer.BufferSize = sizeof( _ulCertInfo );
}
//
// Write the message to the application
//
hr = QueryUlContext()->DoAppWrite( UL_CONTEXT_FLAG_SYNC,
&ulFilterBuffer,
NULL );
return hr;
}
HRESULT
SSL_STREAM_CONTEXT::DoRenegotiate(
VOID
)
/*++
Routine Description:
Trigger a renegotiate for a client certificate
Arguments:
None
Return Value:
HRESULT
--*/
{
SECURITY_STATUS secStatus = SEC_E_OK;
CtxtHandle hOutContext;
DWORD dwContextAttributes = 0;
TimeStamp tsExpiry;
DWORD dwFlags = SSL_ASC_FLAGS |
ASC_REQ_MUTUAL_AUTH;
HRESULT hr = S_OK;
STACK_BUFFER( buffOutBuffer, 100 );
DWORD cbOutBuffer = 0;
DBG_ASSERT( _pSiteConfig != NULL );
//
// Remember that we're renegotiating since we now have to pass the
// MUTUAL_AUTH flag into AcceptSecurityContext() from here on out. Also
// we can only request renegotiation once per connection
//
DBG_ASSERT( _fRenegotiate == FALSE );
_fRenegotiate = TRUE;
//
// Try to get the client certificate. If we don't, that's OK. We will
// renegotiate
//
hr = RetrieveClientCertAndToken();
if ( SUCCEEDED ( hr ) )
{
//
// we have client certificate available for this session
// there is no need to continue with renegotiation
//
hr = DoHandshakeCompleted();
return hr;
}
//
// Reset the HRESULT
// Previous error failing to retrieve client certificate is OK,
// it just means that renegotiation is necessary since
// no client certificate is currently available
hr = S_OK;
//
// Restart the handshake
//
_Buffers[ 0 ].BufferType = SECBUFFER_TOKEN;
_Buffers[ 0 ].pvBuffer = "";
_Buffers[ 0 ].cbBuffer = 0;
_Buffers[ 1 ].BufferType = SECBUFFER_EMPTY;
_Buffers[ 2 ].BufferType = SECBUFFER_EMPTY;
_Buffers[ 3 ].BufferType = SECBUFFER_EMPTY;
_OutBuffers[ 0 ].pvBuffer = NULL;
hOutContext = _hContext;
secStatus = AcceptSecurityContext( QueryCredentials(),
&_hContext,
&_Message,
dwFlags,
SECURITY_NATIVE_DREP,
&hOutContext,
&_MessageOut,
&dwContextAttributes,
&tsExpiry );
IF_DEBUG( SCHANNEL_CALLS )
{
DBGPRINTF(( DBG_CONTEXT,
"AcceptSecurityContext() secStatus=0x%x\n",
secStatus
));
}
//
// We need to make local copy of _OutBuffers[0] and call FreeContextBuffer()
// before sending output of AcceptSecurityContext() to client
// by calling DoRawWrite because there is one pending asynchronous
// raw read launched already and after calling DoRawWrite
// there could be other thread using the same SSL_STREAM_CONTEXT
// and SSL Context handle while this thread hasn't finished yet.
// That could cause AV (see NTBug 313774)
//
if ( _OutBuffers[ 0 ].pvBuffer != NULL )
{
if ( !buffOutBuffer.Resize(_OutBuffers[ 0 ].cbBuffer ) )
{
hr = E_OUTOFMEMORY;
}
else
{
memcpy( buffOutBuffer.QueryPtr(),
_OutBuffers[ 0 ].pvBuffer,
_OutBuffers[ 0 ].cbBuffer
);
cbOutBuffer = _OutBuffers[ 0 ].cbBuffer;
}
FreeContextBuffer( _OutBuffers[ 0 ].pvBuffer );
_OutBuffers[ 0 ].pvBuffer = NULL;
}
if ( FAILED (hr ) )
{
return hr;
}
if ( secStatus == SEC_E_UNSUPPORTED_FUNCTION )
{
//
// Renegotiation is not suppported for current protocol
// Change state to HandhakeCompleted
//
hr = DoHandshakeCompleted();
}
else if ( SUCCEEDED( secStatus ) )
{
_hContext = hOutContext;
_cbHeader = 0;
_cbTrailer = 0;
_cbBlockSize = 0;
if ( buffOutBuffer.QueryPtr() != NULL &&
cbOutBuffer != 0 )
{
QueryUlContext()->DoRawWrite( UL_CONTEXT_FLAG_SYNC,
buffOutBuffer.QueryPtr(),
cbOutBuffer,
NULL );
}
hr = secStatus;
}
else
{
if ( dwContextAttributes & ASC_RET_EXTENDED_ERROR )
{
if ( buffOutBuffer.QueryPtr() != NULL &&
cbOutBuffer != 0 )
{
QueryUlContext()->DoRawWrite( UL_CONTEXT_FLAG_SYNC,
buffOutBuffer.QueryPtr(),
cbOutBuffer,
NULL );
}
}
hr = secStatus;
}
return hr;
}
HRESULT
SSL_STREAM_CONTEXT::DoDecrypt(
RAW_STREAM_INFO * pRawStreamInfo,
BOOL * pfReadMore,
BOOL * pfComplete,
BOOL * pfExtraData
)
/*++
Routine Description:
Decrypt some data
Arguments:
pRawStreamInfo - Raw data buffer
pfReadMore - Set to true if we should read more data
pfComplete - Set to true if we should disconnect
Return Value:
HRESULT
--*/
{
SECURITY_STATUS secStatus = SEC_E_OK;
HRESULT hr = S_OK;
INT iExtra;
HTTP_FILTER_BUFFER ulFilterBuffer;
UCHAR FirstByte = 0; //used only for debug output
if ( pRawStreamInfo == NULL ||
pfReadMore == NULL ||
pfComplete == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*pfReadMore = FALSE;
*pfComplete = FALSE;
*pfExtraData = FALSE;
IF_DEBUG( SCHANNEL_CALLS )
{
DBGPRINTF(( DBG_CONTEXT,
"DoDecrypt(): _cbDecrypted = %d, _cbReReadOffset=%d\n",
_cbDecrypted,
_cbReReadOffset
));
}
//
// Setup an DecryptMessage call. The input buffer is the _buffRaw plus
// an offset. The offset is non-zero if we had to do another read to
// get more data for a previously incomplete message
//
DBG_ASSERT( pRawStreamInfo->cbData > _cbReReadOffset );
_Buffers[ 0 ].pvBuffer = pRawStreamInfo->pbBuffer + _cbReReadOffset;
_Buffers[ 0 ].cbBuffer = pRawStreamInfo->cbData - _cbReReadOffset;
_Buffers[ 0 ].BufferType = SECBUFFER_DATA;
_Buffers[ 1 ].BufferType = SECBUFFER_EMPTY;
_Buffers[ 2 ].BufferType = SECBUFFER_EMPTY;
_Buffers[ 3 ].BufferType = SECBUFFER_EMPTY;
DecryptAgain:
IF_DEBUG( SCHANNEL_CALLS )
{
//
// remember first byte because Decrypt will alter it
//
FirstByte = (unsigned char) *((char *)_Buffers[ 0 ].pvBuffer);
}
secStatus = DecryptMessage( &_hContext,
&_Message,
0,
NULL );
IF_DEBUG( SCHANNEL_CALLS )
{
DBGPRINTF(( DBG_CONTEXT,
"DecryptMessage( bytes:%d, first byte:0x%x ) secStatus=0x%x\n",
pRawStreamInfo->cbData - _cbReReadOffset,
FirstByte,
secStatus
));
}
if ( FAILED( secStatus ) )
{
if ( secStatus == SEC_E_INCOMPLETE_MESSAGE )
{
//
// Setup another read since the message is incomplete. Remember
// where the new data is going to since we only pass this data
// to the next DecryptMessage call
//
_cbReReadOffset = DIFF( (BYTE *)_Buffers[ 0 ].pvBuffer -
pRawStreamInfo->pbBuffer );
QueryUlContext()->SetNextRawReadSize( _Buffers[ 1 ].cbBuffer );
*pfReadMore = TRUE;
return S_OK;
}
return secStatus;
}
if ( secStatus == SEC_E_OK )
{
//
// Take decrypted data and fit it into read buffer
//
memmove( pRawStreamInfo->pbBuffer + _cbDecrypted,
_Buffers[ 1 ].pvBuffer,
_Buffers[ 1 ].cbBuffer );
_cbDecrypted += _Buffers[ 1 ].cbBuffer;
}
//
// Locate extra data (may be available)
//
iExtra = 0;
for ( int i = 1; i < 4; i++ )
{
IF_DEBUG( SCHANNEL_CALLS )
{
if( _Buffers[ i ].BufferType != 0 )
{
DBGPRINTF(( DBG_CONTEXT,
"DecryptMessage returned extra buffer"
" - %d bytes buffer type: %d\n",
_Buffers[ i ].cbBuffer,
_Buffers[ i ].BufferType
));
}
}
if ( _Buffers[ i ].BufferType == SECBUFFER_EXTRA )
{
iExtra = i;
break;
}
}
if ( iExtra != 0 )
{
//
// process extra buffer
//
_cbReReadOffset = DIFF( (PBYTE) _Buffers[ iExtra ].pvBuffer -
pRawStreamInfo->pbBuffer );
if ( secStatus != SEC_I_RENEGOTIATE )
{
_Buffers[ 0 ].pvBuffer = _Buffers[ iExtra ].pvBuffer;
_Buffers[ 0 ].cbBuffer = _Buffers[ iExtra ].cbBuffer;
_Buffers[ 0 ].BufferType = SECBUFFER_DATA;
_Buffers[ 1 ].BufferType = SECBUFFER_EMPTY;
_Buffers[ 2 ].BufferType = SECBUFFER_EMPTY;
_Buffers[ 3 ].BufferType = SECBUFFER_EMPTY;
goto DecryptAgain;
}
else // secStatus == SEC_I_RENEGOTIATE
{
//
// If a renegotiation is triggered, resume the handshake state
//
_sslState = SSL_STATE_HANDSHAKE_IN_PROGRESS;
//
// Caller has to detect that some data is
// still in the buffer not processed and
// That will signal to call DoHandshake()
// for that extra data
//
*pfExtraData = TRUE;
return S_OK;
}
}
//
// there would have been extra data with SEC_I_RENEGOTIATE
// so we must never get here when renegotiating
//
DBG_ASSERT( secStatus != SEC_I_RENEGOTIATE );
//
// Adjust cbData to include only decrypted data
//
pRawStreamInfo->cbData = _cbDecrypted;
//
// We have final decrypted buffer and no extra data left
// Cleanup _cbDecrypted and _cbReReadOffset to make sure that
// next ProcessRawReadData() will work fine.
//
_cbDecrypted = 0;
_cbReReadOffset = 0;
return S_OK;
}
HRESULT
SSL_STREAM_CONTEXT::DoEncrypt(
RAW_STREAM_INFO * pRawStreamInfo,
BOOL * pfComplete
)
/*++
Routine Description:
Encrypt data from the application
Arguments:
pRawStreamInfo - Raw data buffer
pfComplete - Set to true if we should disconnect
Return Value:
HRESULT
--*/
{
SECURITY_STATUS secStatus = SEC_E_OK;
HRESULT hr = S_OK;
// number of chunks the data to be encrypted will be split to
DWORD dwChunks = 0;
// current Data chunk size to be encrypted
DWORD cbDataChunk = 0;
// bytes already encrypted from the source
DWORD cbDataProcessed = 0;
// offset to _buffRawWrite where new chunk should be placed
DWORD cbRawWriteOffset = 0;
if ( pRawStreamInfo == NULL ||
pfComplete == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*pfComplete = FALSE;
//
// Each protocol has limit on maximum size of message
// that can be encrypted with one EncryptMessage() call
//
DBG_ASSERT( _cbMaximumMessage != 0 );
//
// Calculate number of chunks based on _cbMaximumMessage
//
dwChunks = pRawStreamInfo->cbData / _cbMaximumMessage;
if ( pRawStreamInfo->cbData % _cbMaximumMessage != 0 )
{
dwChunks++;
}
//
// Allocate a large enough buffer for encrypted data
// ( remember that each chunk needs header and trailer )
//
if ( !_buffRawWrite.Resize( pRawStreamInfo->cbData +
dwChunks * _cbHeader +
dwChunks * _cbTrailer ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
//
// Loop to encrypt required data in chunks each not exceeding _cbMaximumMessage
//
for ( DWORD dwCurrentChunk = 0; dwCurrentChunk < dwChunks; dwCurrentChunk++ )
{
DBG_ASSERT( _buffRawWrite.QuerySize() > cbRawWriteOffset );
cbDataChunk = min( pRawStreamInfo->cbData - cbDataProcessed,
_cbMaximumMessage );
memcpy( (PBYTE) _buffRawWrite.QueryPtr() + _cbHeader + cbRawWriteOffset,
pRawStreamInfo->pbBuffer + cbDataProcessed,
cbDataChunk );
_EncryptBuffers[ 0 ].pvBuffer = (PBYTE) _buffRawWrite.QueryPtr() +
cbRawWriteOffset;
_EncryptBuffers[ 0 ].cbBuffer = _cbHeader;
_EncryptBuffers[ 0 ].BufferType = SECBUFFER_STREAM_HEADER;
_EncryptBuffers[ 1 ].pvBuffer = (PBYTE) _buffRawWrite.QueryPtr() +
_cbHeader +
cbRawWriteOffset;
_EncryptBuffers[ 1 ].cbBuffer = cbDataChunk;
_EncryptBuffers[ 1 ].BufferType = SECBUFFER_DATA;
_EncryptBuffers[ 2 ].pvBuffer = (PBYTE) _buffRawWrite.QueryPtr() +
_cbHeader +
cbDataChunk +
cbRawWriteOffset;
_EncryptBuffers[ 2 ].cbBuffer = _cbTrailer;
_EncryptBuffers[ 2 ].BufferType = SECBUFFER_STREAM_TRAILER;
_EncryptBuffers[ 3 ].BufferType = SECBUFFER_EMPTY;
secStatus = EncryptMessage( &_hContext,
0,
&_EncryptMessage,
0 );
IF_DEBUG( SCHANNEL_CALLS )
{
DBGPRINTF(( DBG_CONTEXT,
"EncryptMessage() secStatus=0x%x\n",
secStatus
));
}
if(SUCCEEDED(secStatus))
{
//
// next chunk was successfully encrypted
//
cbDataProcessed += cbDataChunk;
cbRawWriteOffset += _EncryptBuffers[ 0 ].cbBuffer +
_EncryptBuffers[ 1 ].cbBuffer +
_EncryptBuffers[ 2 ].cbBuffer;
}
else
{
//
// Set cbData to 0 just for the case that caller ignored error
// and tried to send not encrypted data to client
//
pRawStreamInfo->cbData = 0;
return secStatus;
}
}
//
// Replace the raw stream buffer with the encrypted data
//
pRawStreamInfo->pbBuffer = (PBYTE) _buffRawWrite.QueryPtr();
pRawStreamInfo->cbBuffer = _buffRawWrite.QuerySize();
pRawStreamInfo->cbData = cbRawWriteOffset;
return S_OK;
}
HRESULT
SSL_STREAM_CONTEXT::BuildSslInfo(
VOID
)
/*++
Routine Description:
Build UL_SSL_INFO structure based on Schannel context handle
Arguments:
None
Return Value:
HRESULT
--*/
{
SECURITY_STATUS secStatus;
SecPkgContext_KeyInfo keyInfo;
SERVER_CERT * pServerCert = NULL;
HRESULT hr = S_OK;
//
// Negotiated key size
//
if ( _ulSslInfo.ConnectionKeySize == 0 )
{
secStatus = QueryContextAttributes( &_hContext,
SECPKG_ATTR_KEY_INFO,
&keyInfo );
if ( SUCCEEDED( secStatus ) )
{
_ulSslInfo.ConnectionKeySize = (USHORT) keyInfo.KeySize;
if ( keyInfo.sSignatureAlgorithmName != NULL )
{
FreeContextBuffer( keyInfo.sSignatureAlgorithmName );
}
if ( keyInfo.sEncryptAlgorithmName )
{
FreeContextBuffer( keyInfo.sEncryptAlgorithmName );
}
}
}
//
// A bunch of parameters are based off the server certificate. Get that
// cert now
//
DBG_ASSERT( _pSiteConfig != NULL );
pServerCert = _pSiteConfig->QueryServerCert();
DBG_ASSERT( pServerCert != NULL );
//
// Server cert strength
//
if ( _ulSslInfo.ServerCertKeySize == 0 )
{
_ulSslInfo.ServerCertKeySize = pServerCert->QueryPublicKeySize();
}
//
// Server Cert Issuer
//
if ( _ulSslInfo.pServerCertIssuer == NULL )
{
DBG_ASSERT( _ulSslInfo.ServerCertIssuerSize == 0 );
_ulSslInfo.pServerCertIssuer = pServerCert->QueryIssuer()->QueryStr();
_ulSslInfo.ServerCertIssuerSize = pServerCert->QueryIssuer()->QueryCCH();
}
//
// Server Cert subject
//
if ( _ulSslInfo.pServerCertSubject == NULL )
{
DBG_ASSERT( _ulSslInfo.ServerCertSubjectSize == 0 );
_ulSslInfo.pServerCertSubject = pServerCert->QuerySubject()->QueryStr(),
_ulSslInfo.ServerCertSubjectSize = pServerCert->QuerySubject()->QueryCCH();
}
return hr;
}
HRESULT
SSL_STREAM_CONTEXT::RetrieveClientCertAndToken(
VOID
)
/*++
Routine Description:
Query client certificate and token from the SSL context
Arguments:
none
Return Value:
HRESULT
--*/
{
SECURITY_STATUS secStatus = SEC_E_OK;
HRESULT hr = S_OK;
//
// If client certificate has already been retrieved then simply return
// with success
//
if ( _pClientCert != NULL )
{
return SEC_E_OK;
}
secStatus = QueryContextAttributes( &_hContext,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
&_pClientCert );
if ( SUCCEEDED( secStatus ) )
{
DBG_ASSERT( _pClientCert != NULL );
}
else
{
goto ExitPoint;
}
//
// If we got a client cert and mapping is enabled, then
// request Schannel mapping
//
if( _hDSMappedToken != NULL )
{
CloseHandle( _hDSMappedToken );
_hDSMappedToken = NULL;
}
if ( _fDoCertMap )
{
//
// Only DS mapper is executed in streamfilt
// IIS mapper is executed in w3core as part of IISCertmap Auth Provider
//
DBG_ASSERT( _pSiteConfig != NULL );
if ( _pSiteConfig->QueryUseDSMapper() )
{
secStatus = QuerySecurityContextToken( &_hContext,
&_hDSMappedToken );
if ( SUCCEEDED( secStatus ) )
{
DBG_ASSERT( _hDSMappedToken != NULL );
}
}
if ( FAILED ( secStatus ) )
{
//
// if token from mapping is not available
// it is OK, no mapping was found or
// denied access mapping was used
//
// BUGBUG - some errors should probably be logged
//
secStatus = SEC_E_OK;
}
}
ExitPoint:
return secStatus;
}
HRESULT
SSL_STREAM_CONTEXT::BuildClientCertInfo(
VOID
)
/*++
Routine Description:
Get client certificate info
Arguments:
None
Return Value:
HRESULT
--*/
{
HCERTCHAINENGINE hCertEngine = NULL;
HRESULT hr = S_OK;
CERT_CHAIN_PARA chainPara;
BOOL fRet;
DWORD dwRet = ERROR_SUCCESS;
PCCERT_CHAIN_CONTEXT pChainContext = NULL;
CERT_TRUST_STATUS certStatus;
const DWORD cbDefaultEnhKeyUsage = 100;
STACK_BUFFER( buffEnhKeyUsage, cbDefaultEnhKeyUsage );
PCERT_ENHKEY_USAGE pCertEnhKeyUsage = NULL;
DWORD cbEnhKeyUsage = cbDefaultEnhKeyUsage;
BOOL fEnablesClientAuth = FALSE;
DWORD dwRevocationFlags = 0;
DBG_ASSERT( _pClientCert != NULL );
//
// Do the easy stuff!
//
_ulCertInfo.CertEncodedSize = _pClientCert->cbCertEncoded;
_ulCertInfo.pCertEncoded = _pClientCert->pbCertEncoded;
//
// Now for the hard stuff. We need to validate the server does indeed
// accept the client certificate. Accept means we trusted the
// transitive trust chain to the CA, that the cert isn't revoked, etc.
//
// We use CAPI chaining functionality to check the certificate.
//
DBG_ASSERT( _pSiteConfig != NULL );
//
// If there is a CTL configured, then we'll need to build our own
// Certificate Chain engine to do verification. This is incredibly
// expensive (memory/resource) wise so we will avoid it when possible
// and just use the default CAPI engine
//
if ( _pSiteConfig->QueryHasCTL() )
{
hr = _pSiteConfig->GetCTLChainEngine( &hCertEngine );
if ( FAILED( hr ) )
{
goto ExitPoint;;
}
}
else
{
//
// Default chain engine
//
hCertEngine = HCCE_LOCAL_MACHINE;
}
if ( !( _pSiteConfig->QueryCertCheckMode() &
MD_CERT_NO_REVOC_CHECK ) )
{
dwRevocationFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN;
}
if ( _pSiteConfig->QueryCertCheckMode() &
MD_CERT_CACHE_RETRIEVAL_ONLY )
{
dwRevocationFlags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
}
//
// Let'r rip
//
ZeroMemory( &chainPara, sizeof( chainPara ) );
chainPara.cbSize = sizeof( chainPara );
chainPara.dwUrlRetrievalTimeout =
_pSiteConfig->QueryRevocationUrlRetrievalTimeout();
chainPara.dwRevocationFreshnessTime =
_pSiteConfig->QueryRevocationFreshnessTime();
chainPara.fCheckRevocationFreshnessTime =
!!( _pSiteConfig->QueryCertCheckMode() &
MD_CERT_CHECK_REVOCATION_FRESHNESS_TIME );
fRet = CertGetCertificateChain( hCertEngine,
_pClientCert,
NULL,
NULL,
&chainPara,
dwRevocationFlags,
NULL,
&pChainContext );
if ( !fRet )
{
//
// Bad. Couldn't get the chain at all.
//
hr = HRESULT_FROM_WIN32( GetLastError() );
goto ExitPoint;
}
//
// Now validate the chain
//
_ulCertInfo.CertFlags = pChainContext->TrustStatus.dwErrorStatus;
//
// Now verify extended usage flags (only for the end certificate)
//
if ( !(_pSiteConfig->QueryCertCheckMode() &
MD_CERT_NO_USAGE_CHECK ) )
{
fRet = CertGetEnhancedKeyUsage( _pClientCert,
0, //dwFlags,
(PCERT_ENHKEY_USAGE) buffEnhKeyUsage.QueryPtr(),
&cbEnhKeyUsage );
dwRet = GetLastError();
if ( !fRet && ( dwRet == ERROR_MORE_DATA ) )
{
//
// Resize buffer
//
if ( !buffEnhKeyUsage.Resize( cbEnhKeyUsage ) )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto ExitPoint;
}
fRet = CertGetEnhancedKeyUsage( _pClientCert,
0, //dwFlags,
(PCERT_ENHKEY_USAGE) buffEnhKeyUsage.QueryPtr(),
&cbEnhKeyUsage );
dwRet = GetLastError();
}
if ( !fRet )
{
//
// Bad. Couldn't get the Enhanced Key Usage
//
hr = HRESULT_FROM_WIN32( dwRet );
goto ExitPoint;
}
pCertEnhKeyUsage = (PCERT_ENHKEY_USAGE) buffEnhKeyUsage.QueryPtr();
//
// If the cUsageIdentifier member is zero (0), the certificate might be
// valid for all uses or the certificate it might have no valid uses.
// The return from a call to GetLastError can be used to determine
// whether the certificate is good for all uses or for no uses.
// If GetLastError returns CRYPT_E_NOT_FOUND then the certificate
// is good for all uses. If it returns zero (0), the certificate
// has no valid uses
//
if ( pCertEnhKeyUsage->cUsageIdentifier == 0 )
{
if ( dwRet == CRYPT_E_NOT_FOUND )
{
//
// Certificate valid for any use
//
fEnablesClientAuth = TRUE;
}
else
{
//
// Certificate NOT valid for any use
//
}
}
else
{
//
// Find out if pCertEnhKeyUsage enables CLIENT_AUTH
//
for ( DWORD i = 0; i < pCertEnhKeyUsage->cUsageIdentifier; i++ )
{
DBG_ASSERT( pCertEnhKeyUsage->rgpszUsageIdentifier[i] != NULL );
if ( strcmp( pCertEnhKeyUsage->rgpszUsageIdentifier[i],
szOID_PKIX_KP_CLIENT_AUTH ) == 0 )
{
//
// certificate enables CLIENT_AUTH
//
fEnablesClientAuth = TRUE;
break;
}
}
}
//
// If ExtendedKeyUsage doesn't enable CLIENT_AUTH then add
// flag to CertFlags
//
if ( !fEnablesClientAuth )
{
_ulCertInfo.CertFlags = _ulCertInfo.CertFlags |
CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
}
}
IF_DEBUG( CLIENT_CERT_INFO )
{
DBGPRINTF(( DBG_CONTEXT,
"CertFlags = %d\n",
_ulCertInfo.CertFlags ));
//
// Dump out some debug info about the certificate
//
#if DBG
DumpCertDebugInfo();
#endif
}
//
// Finally set the mapped user token if it exists
// (it is accepted to assign NULL to _ulCertInfo.Token
// if mapping didn't succeed
//
_ulCertInfo.Token = NULL;
//
// BUGBUG - remove CertDeniedByMapper from HTTP structure
//
_ulCertInfo.CertDeniedByMapper = FALSE;
if ( _fDoCertMap )
{
if ( _pSiteConfig->QueryUseDSMapper() )
{
_ulCertInfo.Token = _hDSMappedToken;
}
}
hr = S_OK;
ExitPoint:
if ( pChainContext != NULL )
{
CertFreeCertificateChain( pChainContext );
}
return hr;
}
VOID
SSL_STREAM_CONTEXT::DumpCertDebugInfo(
VOID
)
/*++
Routine Description:
On checked builds, dumps certificate (and chain) information to
debugger
Arguments:
None
Return Value:
None
--*/
{
PCERT_PUBLIC_KEY_INFO pPublicKey;
DWORD cbKeyLength;
WCHAR achBuffer[ 512 ];
//
// Get certificate public key size
//
pPublicKey = &(_pClientCert->pCertInfo->SubjectPublicKeyInfo);
cbKeyLength = CertGetPublicKeyLength( X509_ASN_ENCODING,
pPublicKey );
DBGPRINTF(( DBG_CONTEXT,
"Client cert key length = %d bits\n",
cbKeyLength ));
//
// Get issuer string
//
if ( CertGetNameString( _pClientCert,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
achBuffer,
sizeof( achBuffer ) / sizeof( WCHAR ) ) )
{
DBGPRINTF(( DBG_CONTEXT,
"Client cert issuer = %ws\n",
achBuffer ));
}
else
{
DBGPRINTF(( DBG_CONTEXT,
"Error determining client cert issuer. Win32 = %d\n",
GetLastError() ));
}
//
// Get subject string
//
if ( CertGetNameString( _pClientCert,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
achBuffer,
sizeof( achBuffer ) / sizeof( WCHAR ) ) )
{
DBGPRINTF(( DBG_CONTEXT,
"Client cert subject = %ws\n",
achBuffer ));
}
else
{
DBGPRINTF(( DBG_CONTEXT,
"Error determining client cert subject. Win32 = %d\n",
GetLastError() ));
}
//
// Dump chain stats
//
if ( _ulCertInfo.CertFlags & CERT_TRUST_IS_NOT_TIME_VALID )
{
DBGPRINTF(( DBG_CONTEXT,
"Cert is not time valid\n" ));
}
if ( _ulCertInfo.CertFlags & CERT_TRUST_CTL_IS_NOT_TIME_VALID )
{
DBGPRINTF(( DBG_CONTEXT,
"Cert CTL is not time valid\n" ));
}
if ( _ulCertInfo.CertFlags & CERT_TRUST_IS_UNTRUSTED_ROOT )
{
DBGPRINTF(( DBG_CONTEXT,
"Cert is not trusted\n" ));
}
if ( _ulCertInfo.CertFlags & CERT_TRUST_IS_NOT_SIGNATURE_VALID )
{
DBGPRINTF(( DBG_CONTEXT,
"Cert has invalid signature\n" ));
}
if ( _ulCertInfo.CertFlags & CERT_TRUST_IS_REVOKED )
{
DBGPRINTF(( DBG_CONTEXT,
"Cert is revoked\n" ));
}
if ( _ulCertInfo.CertFlags & CERT_TRUST_REVOCATION_STATUS_UNKNOWN )
{
DBGPRINTF(( DBG_CONTEXT,
"Cert revocation status is unknown\n" ));
}
if ( _ulCertInfo.CertFlags & CERT_TRUST_IS_PARTIAL_CHAIN )
{
DBGPRINTF(( DBG_CONTEXT,
"Cert is not trusted\n" ));
}
}
CredHandle *
SSL_STREAM_CONTEXT::QueryCredentials(
VOID
)
/*++
Routine Description:
Get the applicable credentials (depending on whether we're mapping or not)
Arguments:
None
Return Value:
CredHandle *
--*/
{
DBG_ASSERT( _pSiteConfig != NULL );
if ( _fDoCertMap && _pSiteConfig->QueryUseDSMapper() )
{
return _pSiteConfig->QueryDSMapperCredentials();
}
else
{
return _pSiteConfig->QueryCredentials();
}
}