Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2035 lines
46 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
secdat.cxx
Abstract:
This module implements the ADM_SECURE_DATA class.
Author:
Keith Moore (keithmo) 17-Feb-1997
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ole2.h>
#include <windows.h>
#define DEFAULT_TRACE_FLAGS (DEBUG_ERROR)
#include <dbgutil.h>
#include <iadmw.h>
#include <icrypt.hxx>
#include <secdat.hxx>
//
// Private constants.
//
#define FREE_BLOB(blob) \
if( blob != NULL ) { \
HRESULT _res; \
_res = ::IISCryptoFreeBlob( blob ); \
DBG_ASSERT( SUCCEEDED(_res) ); \
} else
#if DBG
#define ENABLE_ADM_COUNTERS 1
#define ADM_NOISY 0
#else
#define ENABLE_ADM_COUNTERS 0
#define ADM_NOISY 0
#endif
#if ADM_NOISY
#define NOISYPRINTF(x) DBGPRINTF(x)
#else
#define NOISYPRINTF(x)
#endif
//
// Private types.
//
#if ENABLE_ADM_COUNTERS
typedef struct _ADM_COUNTERS {
LONG ObjectsCreated;
LONG ObjectsDestroyed;
} ADM_COUNTERS, *PADM_COUNTERS;
#define DECLARE_ADM_COUNTERS() ADM_COUNTERS g_AdmCounters
#define UpdateObjectsCreated() InterlockedIncrement( &g_AdmCounters.ObjectsCreated )
#define UpdateObjectsDestroyed() InterlockedIncrement( &g_AdmCounters.ObjectsDestroyed )
#else
#define DECLARE_ADM_COUNTERS()
#define UpdateObjectsCreated()
#define UpdateObjectsDestroyed()
#endif
//
// Private globals.
//
LIST_ENTRY ADM_SECURE_DATA::sm_ServerSecureDataListHead;
LIST_ENTRY ADM_SECURE_DATA::sm_ClientSecureDataListHead;
LIST_ENTRY ADM_GUID_MAP::sm_GuidMapListHead;
CRITICAL_SECTION ADM_SECURE_DATA::sm_ServerSecureDataLock;
CRITICAL_SECTION ADM_SECURE_DATA::sm_ClientSecureDataLock;
CRITICAL_SECTION ADM_GUID_MAP::sm_GuidMapDataLock;
#if DBG
DWORD ADM_SECURE_DATA::sm_ServerSecureDataLockOwner;
DWORD ADM_SECURE_DATA::sm_ClientSecureDataLockOwner;
DWORD ADM_GUID_MAP::sm_GuidMapLockOwner;
PTRACE_LOG ADM_SECURE_DATA::sm_RefTraceLog;
PTRACE_LOG ADM_GUID_MAP::sm_RefTraceLog;
#endif
HCRYPTPROV ADM_SECURE_DATA::sm_ServerCryptoProvider;
HCRYPTPROV ADM_SECURE_DATA::sm_ClientCryptoProvider;
DECLARE_ADM_COUNTERS();
//
// ADM_SECURE_DATA methods.
//
ADM_SECURE_DATA::ADM_SECURE_DATA(
IN IUnknown * Object,
IN GUID guidServer,
IN BOOL bServer
) :
m_Object( Object ),
m_KeyExchangeClient( NULL ),
m_KeyExchangeServer( NULL ),
m_SendCryptoStorage( NULL ),
m_ReceiveCryptoStorage( NULL ),
m_ReferenceCount( 1 ),
m_guidServer(guidServer),
m_bIsServer(bServer)
/*++
Routine Description:
ADM_SECURE_DATA object constructor.
Arguments:
Object - Pointer to the object to associate.
Return Value:
None.
--*/
{
NOISYPRINTF((
DBG_CONTEXT,
"ADM_SECURE_DATA: creating %08lx,%08lx\n",
this,
Object
));
//
// Sanity check.
//
DBG_ASSERT( (Object != NULL) || !bServer );
//
// Initialize any complex data members.
//
INITIALIZE_CRITICAL_SECTION( &m_ObjectLock );
//
// Put ourselves on the list.
//
if (bServer) {
AcquireServerDataLock();
InsertHeadList( &sm_ServerSecureDataListHead, &m_SecureDataListEntry );
ReleaseServerDataLock();
}
else {
AcquireClientDataLock();
InsertHeadList( &sm_ClientSecureDataListHead, &m_SecureDataListEntry );
ReleaseClientDataLock();
//
// Clients lifespan is controlled by ADM_GUID_MAP
//
}
UpdateObjectsCreated();
} // ADM_SECURE_DATA::ADM_SECURE_DATA
ADM_SECURE_DATA::~ADM_SECURE_DATA()
/*++
Routine Description:
ADM_SECURE_DATA object destructor.
Arguments:
None.
Return Value:
None.
--*/
{
NOISYPRINTF((
DBG_CONTEXT,
"~ADM_SECURE_DATA: destroying %08lx,%08lx\n",
this,
m_Object
));
//
// Sanity check.
//
DBG_ASSERT( m_ReferenceCount == 0 );
//
// Cleanup.
//
CleanupCryptoData();
if (m_bIsServer) {
AcquireServerDataLock();
RemoveEntryList( &m_SecureDataListEntry );
ReleaseServerDataLock();
}
else {
AcquireClientDataLock();
RemoveEntryList( &m_SecureDataListEntry );
ReleaseClientDataLock();
}
DeleteCriticalSection( &m_ObjectLock );
UpdateObjectsDestroyed();
m_Object = NULL;
} // ADM_SECURE_DATA::~ADM_SECURE_DATA
BOOL
ADM_SECURE_DATA::Initialize(
HINSTANCE
)
/*++
Routine Description:
Initializes static global data private to ADM_SECURE_DATA.
Arguments:
hDll - Handle to this DLL.
Return Value:
None.
--*/
{
HRESULT result;
//
// Initialize the crypto stuff.
//
result = ::IISCryptoInitialize();
if( SUCCEEDED(result) ) {
INITIALIZE_CRITICAL_SECTION( &sm_ServerSecureDataLock );
INITIALIZE_CRITICAL_SECTION( &sm_ClientSecureDataLock );
#if DBG
sm_ServerSecureDataLockOwner = 0;
sm_ClientSecureDataLockOwner = 0;
sm_RefTraceLog = CreateRefTraceLog( 1024, 0 );
#endif
InitializeListHead( &sm_ServerSecureDataListHead );
InitializeListHead( &sm_ClientSecureDataListHead );
} else {
DBGPRINTF((
DBG_CONTEXT,
"ADM_SECURE_DATA::Initialize: error %lx\n",
result
));
}
return SUCCEEDED(result);
} // ADM_SECURE_DATA::Initialize
VOID
ADM_SECURE_DATA::Terminate()
/*++
Routine Description:
Terminates static global data private to ADM_SECURE_DATA.
Arguments:
None.
Return Value:
None.
--*/
{
PLIST_ENTRY entry;
ADM_SECURE_DATA * data;
HRESULT result;
//
// Free any secure data objects on our list.
//
AcquireServerDataLock();
entry = sm_ServerSecureDataListHead.Flink;
while( entry != &sm_ServerSecureDataListHead ) {
data = CONTAINING_RECORD(
entry,
ADM_SECURE_DATA,
m_SecureDataListEntry
);
data->Dereference();
entry = sm_ServerSecureDataListHead.Flink;
}
ReleaseServerDataLock();
AcquireClientDataLock();
entry = sm_ClientSecureDataListHead.Flink;
while( entry != &sm_ClientSecureDataListHead ) {
data = CONTAINING_RECORD(
entry,
ADM_SECURE_DATA,
m_SecureDataListEntry
);
data->Dereference();
entry = sm_ClientSecureDataListHead.Flink;
}
ReleaseClientDataLock();
DBG_ASSERT( IsListEmpty( &sm_ServerSecureDataListHead ) );
DBG_ASSERT( IsListEmpty( &sm_ClientSecureDataListHead ) );
//
// Terminate the crypto stuff.
//
if( sm_ServerCryptoProvider != CRYPT_NULL ) {
result = ::IISCryptoCloseContainer( sm_ServerCryptoProvider );
DBG_ASSERT( SUCCEEDED(result) || (result == RETURNCODETOHRESULT(ERROR_BUSY)) );
sm_ServerCryptoProvider = CRYPT_NULL;
}
if( sm_ClientCryptoProvider != CRYPT_NULL ) {
result = ::IISCryptoCloseContainer( sm_ClientCryptoProvider );
DBG_ASSERT( SUCCEEDED(result) || (result == RETURNCODETOHRESULT(ERROR_BUSY)) );
sm_ClientCryptoProvider = CRYPT_NULL;
}
result = ::IISCryptoTerminate();
DBG_ASSERT( SUCCEEDED(result) );
//
// Final cleanup.
//
DeleteCriticalSection( &sm_ServerSecureDataLock );
DeleteCriticalSection( &sm_ClientSecureDataLock );
#if DBG
if( sm_RefTraceLog != NULL ) {
DestroyRefTraceLog( sm_RefTraceLog );
}
#endif
} // ADM_SECURE_DATA::Terminate
ADM_SECURE_DATA *
ADM_SECURE_DATA::FindAndReferenceServerSecureData(
IN IUnknown * Object,
IN BOOL CreateIfNotFound
)
/*++
Routine Description:
Finds the ADM_SECURE_DATA object associated with Object. If it
cannot be found, then a new ADM_SECURE_DATA object is created
and put on the global list.
Arguments:
Object - The object to search for.
CreateIfNotFound - If the object is not on the list, a new association
will only be created if this flag is TRUE.
Return Value:
ADM_SECURE_DATA * - Pointer to the ADM_SECURE_DATA object if
successful, NULL otherwise.
--*/
{
PLIST_ENTRY entry;
ADM_SECURE_DATA * data;
AcquireServerDataLock();
for( entry = sm_ServerSecureDataListHead.Flink ;
entry != &sm_ServerSecureDataListHead ;
entry = entry->Flink ) {
data = CONTAINING_RECORD(
entry,
ADM_SECURE_DATA,
m_SecureDataListEntry
);
if( data->m_Object == Object ) {
data->Reference();
ReleaseServerDataLock();
return data;
}
}
data = NULL;
if( CreateIfNotFound ) {
GUID guidServer;
HRESULT hresError;
hresError = CoCreateGuid(&guidServer);
if (SUCCEEDED(hresError)) {
data = new ADM_SECURE_DATA( Object,
guidServer,
TRUE );
if( data == NULL ) {
DBGPRINTF((
DBG_CONTEXT,
"ADM_SECURE_DATA::FindAndReferenceServerSecureData: out of memory\n"
));
} else {
data->Reference();
}
}
else {
DBGPRINTF((
DBG_CONTEXT,
"ADM_SECURE_DATA::FindAndReferenceServerSecureData: CoCreateGuid failed\n"
));
}
}
ReleaseServerDataLock();
return data;
} // ADM_SECURE_DATA::FindAndReferenceServerSecureData
ADM_SECURE_DATA *
ADM_SECURE_DATA::FindOrAddAndReferenceClientSecureData(
IN IUnknown *Object )
/*++
Routine Description:
Finds the ADM_SECURE_DATA object associated with Object. If it
cannot be found, then a new ADM_SECURE_DATA object is created
and put on the global list.
Arguments:
Object - The object to search for.
Return Value:
ADM_SECURE_DATA * - Pointer to the ADM_SECURE_DATA object if
successful, NULL otherwise.
--*/
{
HRESULT hr;
ADM_SECURE_DATA *psecdatData = NULL;
ADM_GUID_MAP *pguidmapData = NULL;
GUID guidServer = { 0 };
AcquireClientDataLock();
pguidmapData = ADM_GUID_MAP::FindAndReferenceGuidMap( Object );
if ( pguidmapData == NULL )
{
// IVANPASH: 591269.
// We cannot keep the critical section locked while making the R_ call
ReleaseClientDataLock();
hr = IMSAdminBaseW_R_GetServerGuid_Proxy( (IMSAdminBaseW *)Object,
&guidServer );
AcquireClientDataLock();
if ( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT,
"ADM_SECURE_DATA::FindOrAddAndReferenceClientSecureData: IMSAdminBaseW_R_GetServerGuid_Proxy failed\n" ));
goto exit;
}
// Retry
pguidmapData = ADM_GUID_MAP::FindAndReferenceGuidMap( Object );
if ( pguidmapData == NULL )
{
pguidmapData = new ADM_GUID_MAP( Object,
guidServer );
if( pguidmapData == NULL )
{
DBGPRINTF(( DBG_CONTEXT,
"ADM_SECURE_DATA::FindOrAddAndReferenceClientSecureData: out of memory\n" ));
goto exit;
}
pguidmapData->Reference();
}
}
DBG_ASSERT( pguidmapData != NULL );
psecdatData = FindAndReferenceClientSecureData( pguidmapData );
if ( psecdatData == NULL )
{
//
// AddRef the ADM_SECURE_DATA
// Do this here instead of constructor to
// all handing of errors.
// this may create the ADM_SECURE_DATA
//
psecdatData = new ADM_SECURE_DATA( Object,
pguidmapData->GetGuid(),
FALSE );
if ( psecdatData == NULL )
{
DBGPRINTF(( DBG_CONTEXT,
"ADM_SECURE_DATA::FindOrAddAndReferenceClientSecureData: out of memory\n" ));
goto exit;
}
psecdatData->Reference();
}
exit:
if ( pguidmapData != NULL )
{
pguidmapData->Dereference();
pguidmapData = NULL;
}
ReleaseClientDataLock();
return psecdatData;
} // ADM_SECURE_DATA::FindAndReferenceSecureData
ADM_SECURE_DATA *
ADM_SECURE_DATA::FindAndReferenceClientSecureData(
IN ADM_GUID_MAP *pguidmapRelated )
/*++
Routine Description:
Finds the ADM_SECURE_DATA object associated with Object.
Arguments:
pguidmapRelated - The GUID to search for.
Return Value:
ADM_SECURE_DATA * - Pointer to the ADM_SECURE_DATA object if
successful, NULL otherwise.
--*/
{
PLIST_ENTRY entry;
ADM_SECURE_DATA *psecdatData = NULL;
AcquireClientDataLock();
if ( pguidmapRelated == NULL )
{
goto exit;
}
for ( entry = sm_ClientSecureDataListHead.Flink ;
entry != &sm_ClientSecureDataListHead ;
entry = entry->Flink )
{
psecdatData = CONTAINING_RECORD( entry,
ADM_SECURE_DATA,
m_SecureDataListEntry );
DBG_ASSERT( psecdatData->m_guidServer != GUID_NULL );
if ( psecdatData->m_guidServer == pguidmapRelated->GetGuid() )
{
psecdatData->Reference();
goto exit;
}
}
psecdatData = NULL;
exit:
ReleaseClientDataLock();
return psecdatData;
} // ADM_SECURE_DATA::FindAndReferenceSecureData
ADM_SECURE_DATA *
ADM_SECURE_DATA::FindAndReferenceClientSecureData(
IN IUnknown *Object )
/*++
Routine Description:
Finds the ADM_SECURE_DATA object associated with Object.
Arguments:
Object - The object to search for.
Return Value:
ADM_SECURE_DATA * - Pointer to the ADM_SECURE_DATA object if
successful, NULL otherwise.
--*/
{
ADM_SECURE_DATA *psecdatData = NULL;
ADM_GUID_MAP *pguidmapData = NULL;
AcquireClientDataLock();
pguidmapData = ADM_GUID_MAP::FindAndReferenceGuidMap( Object );
if ( pguidmapData == NULL )
{
goto exit;
}
psecdatData = FindAndReferenceClientSecureData( pguidmapData );
exit:
if ( pguidmapData != NULL )
{
pguidmapData->Dereference();
pguidmapData = NULL;
}
ReleaseClientDataLock();
return psecdatData;
} // ADM_SECURE_DATA::FindAndReferenceSecureData
HRESULT
ADM_SECURE_DATA::GetClientSendCryptoStorage(
OUT IIS_CRYPTO_STORAGE ** SendCryptoStorage,
IN IUnknown * Object
)
/*++
Routine Description:
Retrieves the client-side IIS_CRYPTO_STORAGE object to be used for
sending data to the server. This routine will perform the client-side
key exchange if necessary.
Arguments:
SendCryptoStorage - Receives a pointer to the newly created
IIS_CRYPTO_STORAGE object if successful.
Return Value:
HRESULT - 0 if successful, !0 otherwise.
--*/
{
HRESULT result;
//
// Sanity check.
//
DBG_ASSERT( SendCryptoStorage != NULL );
//
// Do that key exchange thang if we don't yet have it.
//
if( m_KeyExchangeClient == NULL ) {
LockThis();
if( m_KeyExchangeClient == NULL ) {
result = DoClientSideKeyExchange(Object);
if( FAILED(result) ) {
UnlockThis();
return result;
}
}
UnlockThis();
}
DBG_ASSERT( m_SendCryptoStorage != NULL );
*SendCryptoStorage = m_SendCryptoStorage;
return NO_ERROR;
} // ADM_SECURE_DATA::GetClientSendCryptoStorage
HRESULT
ADM_SECURE_DATA::GetClientReceiveCryptoStorage(
OUT IIS_CRYPTO_STORAGE ** ReceiveCryptoStorage,
IUnknown * Object
)
/*++
Routine Description:
Retrieves the client-side IIS_CRYPTO_STORAGE object to be used for
receiving data from the server. This routine will perform the
client-side key exchange if necessary.
Arguments:
ReceiveCryptoStorage - Receives a pointer to the newly created
IIS_CRYPTO_STORAGE object if successful.
Return Value:
HRESULT - 0 if successful, !0 otherwise.
--*/
{
HRESULT result;
//
// Sanity check.
//
DBG_ASSERT( ReceiveCryptoStorage != NULL );
//
// Do that key exchange thang if we don't yet have it.
//
if( m_KeyExchangeClient == NULL ) {
LockThis();
if( m_KeyExchangeClient == NULL ) {
result = DoClientSideKeyExchange(Object);
if( FAILED(result) ) {
UnlockThis();
return result;
}
}
UnlockThis();
}
DBG_ASSERT( m_ReceiveCryptoStorage != NULL );
*ReceiveCryptoStorage = m_ReceiveCryptoStorage;
return NO_ERROR;
} // ADM_SECURE_DATA::GetClientReceiveCryptoStorage
HRESULT
ADM_SECURE_DATA::GetServerSendCryptoStorage(
OUT IIS_CRYPTO_STORAGE ** SendCryptoStorage
)
/*++
Routine Description:
Retrieves the server-side IIS_CRYPTO_STORAGE object to be used for
sending data to the client.
Arguments:
SendCryptoStorage - Receives a pointer to the newly created
IIS_CRYPTO_STORAGE object if successful.
Return Value:
HRESULT - 0 if successful, !0 otherwise.
--*/
{
if( m_SendCryptoStorage != NULL ) {
*SendCryptoStorage = m_SendCryptoStorage;
return NO_ERROR;
}
return MD_ERROR_SECURE_CHANNEL_FAILURE;
} // ADM_SECURE_DATA::GetServerSendCryptoStorage
HRESULT
ADM_SECURE_DATA::GetServerReceiveCryptoStorage(
OUT IIS_CRYPTO_STORAGE ** ReceiveCryptoStorage
)
/*++
Routine Description:
Retrieves the server-side IIS_CRYPTO_STORAGE object to be used for
receiving data from the client.
Arguments:
ReceiveCryptoStorage - Receives a pointer to the newly created
IIS_CRYPTO_STORAGE object if successful.
Return Value:
HRESULT - 0 if successful, !0 otherwise.
--*/
{
if( m_ReceiveCryptoStorage != NULL ) {
*ReceiveCryptoStorage = m_ReceiveCryptoStorage;
return NO_ERROR;
}
return MD_ERROR_SECURE_CHANNEL_FAILURE;
} // ADM_SECURE_DATA::GetServerReceiveCryptoStorage
HRESULT
ADM_SECURE_DATA::DoClientSideKeyExchange(
IUnknown * Object
)
/*++
Routine Description:
Performs all the client-side magic necessary to exchange session
keys with the server.
Arguments:
None.
Return Value:
HRESULT - 0 if successful, !0 otherwise.
--*/
{
HRESULT result;
HCRYPTPROV prov;
IIS_CRYPTO_EXCHANGE_CLIENT * keyExchangeClient;
IIS_CRYPTO_BLOB *clientKeyExchangeKeyBlob;
IIS_CRYPTO_BLOB *clientSignatureKeyBlob;
IIS_CRYPTO_BLOB *clientSessionKeyBlob;
IIS_CRYPTO_BLOB *clientHashBlob;
IIS_CRYPTO_BLOB *serverKeyExchangeKeyBlob;
IIS_CRYPTO_BLOB *serverSignatureKeyBlob;
IIS_CRYPTO_BLOB *serverSessionKeyBlob;
IIS_CRYPTO_BLOB *serverHashBlob;
//
// Sanity check.
//
DBG_ASSERT( Object != NULL );
DBG_ASSERT( m_KeyExchangeClient == NULL );
DBG_ASSERT( m_SendCryptoStorage == NULL );
DBG_ASSERT( m_ReceiveCryptoStorage == NULL );
//
// Setup locals so we know how to cleanup on exit.
//
keyExchangeClient = NULL;
clientKeyExchangeKeyBlob = NULL;
clientSignatureKeyBlob = NULL;
clientSessionKeyBlob = NULL;
clientHashBlob = NULL;
serverKeyExchangeKeyBlob = NULL;
serverSignatureKeyBlob = NULL;
serverSessionKeyBlob = NULL;
serverHashBlob = NULL;
HANDLE hToken = NULL;
if (OpenThreadToken( GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hToken ) ) {
RevertToSelf();
}
//
// Get the crypto provider to use.
//
DBG_CODE( prov = CRYPT_NULL );
result = GetClientCryptoProvider( &prov );
if( FAILED(result) ) {
goto cleanup;
}
DBG_ASSERT( prov != CRYPT_NULL );
//
// Create & initialize the client-side key exchange object.
//
// N.B. Do not set the m_KeyExchangeClient to a non-NULL value
// until key exchange is complete.
//
keyExchangeClient = new IIS_CRYPTO_EXCHANGE_CLIENT;
if( keyExchangeClient == NULL ) {
result = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto cleanup;
}
result = keyExchangeClient->Initialize(
prov, // hProv
CRYPT_NULL, // hKeyExchangeKey
CRYPT_NULL, // hSignatureKey
TRUE // fUseMachineKeyset was FALSE before fix for 213126
);
if( FAILED(result) ) {
goto cleanup;
}
//
// Phase 1: Get the client's key exchange and signature key blobs,
// send them to the server, and get the server's key exchange,
// signature, and session key blobs.
//
result = keyExchangeClient->ClientPhase1(
&clientKeyExchangeKeyBlob,
&clientSignatureKeyBlob
);
if( FAILED(result) ) {
goto cleanup;
}
result = IMSAdminBaseW_R_KeyExchangePhase1_Proxy(
(IMSAdminBaseW *)Object,
clientKeyExchangeKeyBlob,
clientSignatureKeyBlob,
&serverKeyExchangeKeyBlob,
&serverSignatureKeyBlob,
&serverSessionKeyBlob
);
if( FAILED(result) ) {
goto cleanup;
}
//
// Phase 2: Import the server's key exchange, signature, and session
// key blobs, get the client's session key and hash blobs, send them
// to the server, and get the server's hash blob.
//
result = keyExchangeClient->ClientPhase2(
serverKeyExchangeKeyBlob,
serverSignatureKeyBlob,
serverSessionKeyBlob,
&clientSessionKeyBlob,
&clientHashBlob
);
if( FAILED(result) ) {
goto cleanup;
}
result = IMSAdminBaseW_R_KeyExchangePhase2_Proxy(
(IMSAdminBaseW *)Object,
clientSessionKeyBlob,
clientHashBlob,
&serverHashBlob
);
if( FAILED(result) ) {
goto cleanup;
}
//
// Phase 3: Import the server's hash blob.
//
result = keyExchangeClient->ClientPhase3(
serverHashBlob
);
if( FAILED(result) ) {
goto cleanup;
}
//
// OK, the key exchange is complete. We just need to create the
// appropriate storage objects.
//
m_SendCryptoStorage = new IIS_CRYPTO_STORAGE;
if( m_SendCryptoStorage == NULL ) {
result = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto cleanup;
}
result = m_SendCryptoStorage->Initialize(
keyExchangeClient->QueryProviderHandle(), // hProv
keyExchangeClient->AssumeClientSessionKey(), // hSessionKey
CRYPT_NULL, // hKeyExchangeKey
CRYPT_NULL, // hSignatureKey
TRUE // fUseMachineKeyset was FALSE before fix for 213126
);
if( FAILED(result) ) {
goto cleanup;
}
m_ReceiveCryptoStorage = new IIS_CRYPTO_STORAGE;
if( m_ReceiveCryptoStorage == NULL ) {
result = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto cleanup;
}
result = m_ReceiveCryptoStorage->Initialize(
keyExchangeClient->QueryProviderHandle(), // hProv
keyExchangeClient->AssumeServerSessionKey(), // hSessionKey
CRYPT_NULL, // hKeyExchangeKey
keyExchangeClient->AssumeServerSignatureKey(), // hSignatureKey
TRUE // fUseMachineKeyset was FALSE before fix for 213126
);
if( FAILED(result) ) {
goto cleanup;
}
//
// Success!
//
m_KeyExchangeClient = keyExchangeClient;
cleanup:
//
// Free any blobs we allocated.
//
FREE_BLOB( clientKeyExchangeKeyBlob );
FREE_BLOB( clientSignatureKeyBlob );
FREE_BLOB( clientSessionKeyBlob );
FREE_BLOB( clientHashBlob );
FREE_BLOB( serverKeyExchangeKeyBlob );
FREE_BLOB( serverSignatureKeyBlob );
FREE_BLOB( serverSessionKeyBlob );
FREE_BLOB( serverHashBlob );
//
// If we're failing the call, then free the associated objects we
// created.
//
if( FAILED(result) ) {
delete keyExchangeClient;
CleanupCryptoData();
}
if (hToken) {
if( ImpersonateLoggedOnUser( hToken ) )
{
// Nothing needs to be done here
}
CloseHandle(hToken);
hToken = NULL;
}
return result;
} // ADM_SECURE_DATA::DoClientSideKeyExchange
HRESULT
ADM_SECURE_DATA::DoServerSideKeyExchangePhase1(
IN PIIS_CRYPTO_BLOB pClientKeyExchangeKeyBlob,
IN PIIS_CRYPTO_BLOB pClientSignatureKeyBlob,
OUT PIIS_CRYPTO_BLOB * ppServerKeyExchangeKeyBlob,
OUT PIIS_CRYPTO_BLOB * ppServerSignatureKeyBlob,
OUT PIIS_CRYPTO_BLOB * ppServerSessionKeyBlob
)
/*++
Routine Description:
Performs the first phase of server-side key exchange.
Arguments:
pClientKeyExchangeKeyBlob - The client-side key exchange key blob.
pClientSignatureKeyBlob - The client-side signature key blob.
ppServerKeyExchangeKeyBlob - Receives a pointer to the server-side
key exchange key blob if successful.
ppServerSignatureKeyBlob - Receives a pointer to the server-side
signature key blob if successful.
ppServerSessionKeyBlob - Receives a pointer to the server-side session
key blob if successful.
Return Value:
HRESULT - 0 if successful, !0 otherwise.
--*/
{
HRESULT result;
HCRYPTPROV prov;
IIS_CRYPTO_EXCHANGE_SERVER * keyExchangeServer;
//
// Sanity check.
//
DBG_ASSERT( m_Object != NULL );
DBG_ASSERT( m_KeyExchangeServer == NULL );
DBG_ASSERT( m_SendCryptoStorage == NULL );
DBG_ASSERT( m_ReceiveCryptoStorage == NULL );
DBG_ASSERT( pClientKeyExchangeKeyBlob != NULL );
DBG_ASSERT( pClientSignatureKeyBlob != NULL );
DBG_ASSERT( ppServerKeyExchangeKeyBlob != NULL );
DBG_ASSERT( ppServerSignatureKeyBlob != NULL );
DBG_ASSERT( ppServerSessionKeyBlob != NULL );
HANDLE hToken = NULL;
if (OpenThreadToken( GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hToken ) ) {
RevertToSelf();
}
//
// Setup locals so we know how to cleanup on exit.
//
keyExchangeServer = NULL;
//
// Get the crypto provider to use.
//
DBG_CODE( prov = CRYPT_NULL );
result = GetServerCryptoProvider( &prov );
if( FAILED(result) ) {
goto cleanup;
}
DBG_ASSERT( prov != CRYPT_NULL );
//
// Create & initialize the server-side key exchange object.
//
// N.B. Do not set the m_KeyExchangeServer to a non-NULL value
// until key exchange is complete.
//
keyExchangeServer = new IIS_CRYPTO_EXCHANGE_SERVER;
if( keyExchangeServer == NULL ) {
result = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto cleanup;
}
result = keyExchangeServer->Initialize(
prov, // hProv
CRYPT_NULL, // hKeyExchangeKey
CRYPT_NULL, // hSignatureKey
FALSE // fUseMachineKeyset
);
if( FAILED(result) ) {
goto cleanup;
}
//
// Do the first phase of the key exchange.
//
result = keyExchangeServer->ServerPhase1(
pClientKeyExchangeKeyBlob,
pClientSignatureKeyBlob,
ppServerKeyExchangeKeyBlob,
ppServerSignatureKeyBlob,
ppServerSessionKeyBlob
);
if( FAILED(result) ) {
goto cleanup;
}
//
// Success!
//
m_KeyExchangeServer = keyExchangeServer;
cleanup:
//
// If we're failing the call, then free the associated objects we
// created.
//
if( FAILED(result) ) {
delete keyExchangeServer;
}
if (hToken) {
if( ImpersonateLoggedOnUser( hToken ) )
{
// Don't need to anything here
}
CloseHandle(hToken);
hToken = NULL;
}
return result;
} // ADM_SECURE_DATA::DoServerSideKeyExchangePhase1
HRESULT
ADM_SECURE_DATA::DoServerSideKeyExchangePhase2(
IN PIIS_CRYPTO_BLOB pClientSessionKeyBlob,
IN PIIS_CRYPTO_BLOB pClientHashBlob,
OUT PIIS_CRYPTO_BLOB * ppServerHashBlob
)
/*++
Routine Description:
Performs the second phase of server-side key exchange.
Arguments:
pClientSessionKeyBlob - The client-side session key blob.
pClientHashBlob - The client-side hash blob.
ppServerHashBlob - Receives a pointer to the server-side hash blob
if successful.
Return Value:
HRESULT - 0 if successful, !0 otherwise.
--*/
{
HRESULT result;
//
// Sanity check.
//
DBG_ASSERT( m_Object != NULL );
DBG_ASSERT( m_KeyExchangeServer != NULL );
DBG_ASSERT( m_SendCryptoStorage == NULL );
DBG_ASSERT( m_ReceiveCryptoStorage == NULL );
DBG_ASSERT( pClientSessionKeyBlob != NULL );
DBG_ASSERT( pClientHashBlob != NULL );
DBG_ASSERT( ppServerHashBlob != NULL );
HANDLE hToken = NULL;
if (OpenThreadToken( GetCurrentThread(), MAXIMUM_ALLOWED, TRUE, &hToken ) ) {
RevertToSelf();
}
//
// Do the second phase of the key exchange.
//
result = m_KeyExchangeServer->ServerPhase2(
pClientSessionKeyBlob,
pClientHashBlob,
ppServerHashBlob
);
if( FAILED(result) ) {
goto cleanup;
}
//
// OK, the key exchange is complete. We just need to create the
// appropriate storage objects.
//
m_SendCryptoStorage = new IIS_CRYPTO_STORAGE;
if( m_SendCryptoStorage == NULL ) {
result = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto cleanup;
}
result = m_SendCryptoStorage->Initialize(
m_KeyExchangeServer->QueryProviderHandle(), // hProv
m_KeyExchangeServer->AssumeServerSessionKey(), // hSessionKey
CRYPT_NULL, // hKeyExchangeKey
CRYPT_NULL, // hSignatureKey
FALSE // fUseMachineKeyset
);
if( FAILED(result) ) {
goto cleanup;
}
m_ReceiveCryptoStorage = new IIS_CRYPTO_STORAGE;
if( m_ReceiveCryptoStorage == NULL ) {
result = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto cleanup;
}
result = m_ReceiveCryptoStorage->Initialize(
m_KeyExchangeServer->QueryProviderHandle(), // hProv
m_KeyExchangeServer->AssumeClientSessionKey(), // hSessionKey
CRYPT_NULL, // hKeyExchangeKey
m_KeyExchangeServer->AssumeClientSignatureKey(), // hSignatureKey
FALSE // fUseMachineKeyset
);
if( FAILED(result) ) {
goto cleanup;
}
//
// Success!
//
cleanup:
//
// If we're failing the call, then free the associated objects we
// created.
//
if( FAILED(result) ) {
CleanupCryptoData();
}
if (hToken) {
if( ImpersonateLoggedOnUser( hToken ) )
{
// Don't need to do anything here
}
CloseHandle(hToken);
hToken = NULL;
}
return result;
} // ADM_SECURE_DATA::DoServerSideKeyExchangePhase2
HRESULT
ADM_SECURE_DATA::GetCryptoProviderHelper(
OUT HCRYPTPROV * UserProvider,
OUT HCRYPTPROV * CachedProvider,
IN LPTSTR ContainerName,
IN DWORD CryptoFlags
)
/*++
Routine Description:
Helper routine for GetServerCryptoProvider and GetClientCryptoProvider().
Provides necessary locking to ensure exactly one provider of each type
is opened.
Arguments:
UserProvider - Receives the handle to the provider.
CachedProvider - Also receives the handle to the provider.
ContainerName - The name of the container to open/create.
CryptoFlags - Flags to pass into IISCryptoGetStandardContainer().
Return Value:
HRESULT - 0 if successful, !0 otherwise.
--*/
{
HRESULT result = NO_ERROR;
HCRYPTPROV hprov;
//
// Sanity check.
//
DBG_ASSERT( UserProvider != NULL );
DBG_ASSERT( CachedProvider != NULL );
LockThis();
//
// Recheck the provider handle under the guard of the lock, just
// in case another thread has already created it.
//
hprov = *CachedProvider;
if( hprov == CRYPT_NULL ) {
//
// Open/create the container.
//
result = IIS_CRYPTO_BASE::GetCryptoContainerByName(
&hprov,
ContainerName,
CryptoFlags,
FALSE // fApplyAcl
);
}
if( SUCCEEDED(result) ) {
DBG_ASSERT( hprov != CRYPT_NULL );
*CachedProvider = hprov;
*UserProvider = hprov;
}
UnlockThis();
return result;
} // ADM_SECURE_DATA::GetCryptoProviderHelper
VOID
ADM_SECURE_DATA::CleanupCryptoData()
/*++
Routine Description:
Frees any crypto data associated with the current object.
Arguments:
None.
Return Value:
None.
--*/
{
delete m_KeyExchangeClient;
m_KeyExchangeClient = NULL;
delete m_KeyExchangeServer;
m_KeyExchangeServer = NULL;
delete m_SendCryptoStorage;
m_SendCryptoStorage = NULL;
delete m_ReceiveCryptoStorage;
m_ReceiveCryptoStorage = NULL;
} // ADM_SECURE_DATA::CleanupCryptoData
HRESULT
ADM_SECURE_DATA::GetClientCryptoProvider(
OUT HCRYPTPROV * Provider
)
/*++
Routine Description:
This method calls down to GetCryptoProviderHelper to create key
and before that it prepares a name for key container
Arguments:
Provider - returns handle to provider
Return Value:
HRESULT error code if fail
--*/
{
HRESULT retVal = NO_ERROR;
if( sm_ClientCryptoProvider != CRYPT_NULL ) {
*Provider = sm_ClientCryptoProvider;
return retVal;
}
//
// Previously each user had a unique container name generated
// and keys would be stored persistently.
// Currently temporary container will be created to store temporary
// keys. This way the race condition will be prevented where multiple
// processes running under the same user identity would try to create
// container of the same name (and one would eventually fail)
//
retVal = GetCryptoProviderHelper( Provider,
&sm_ClientCryptoProvider,
NULL, // CREATE temporary container
0
);
return retVal;
}
#define BUFSIZE_FOR_TOKEN 256
HRESULT
ADM_SECURE_DATA::GenerateNameForContainer(
IN OUT PCHAR pszContainerName,
IN DWORD dwBufferLen
)
/*++
Routine Description:
This method generates the name for crypoto container by taking some string
which has 'IIS client ' appending that with suer name and user SID for uniqueness
Arguments:
pszContainerName - pointer to a buffer which will receive a generated string
dwBufferLen - size of buffer suplied for receiving string
Return Value:
HRESULT
--*/
{
HANDLE hToken;
BYTE buf[BUFSIZE_FOR_TOKEN];
PTOKEN_USER ptgUser = (PTOKEN_USER)buf;
DWORD cbBuffer=BUFSIZE_FOR_TOKEN;
DWORD dwLenOfBuffer, dwLenOfBuffAvailable;
BOOL bSuccess;
PCHAR pszPlaceToAppend;
DWORD dwError;
//
// initialize string with some name for IIS client container
//
if (sizeof (DCOM_CLIENT_CONTAINER) >= dwBufferLen)
{
return RETURNCODETOHRESULT(ERROR_INSUFFICIENT_BUFFER);
}
strcpy (pszContainerName, DCOM_CLIENT_CONTAINER);
dwLenOfBuffer = (DWORD)strlen(pszContainerName);
pszPlaceToAppend = pszContainerName + dwLenOfBuffer;
dwLenOfBuffAvailable = dwBufferLen - dwLenOfBuffer;
if ( !GetUserName(pszPlaceToAppend,&dwLenOfBuffAvailable) )
{
return RETURNCODETOHRESULT(GetLastError());
}
dwLenOfBuffer += dwLenOfBuffAvailable;
pszPlaceToAppend = pszContainerName + dwLenOfBuffer - 1;
dwLenOfBuffAvailable = dwBufferLen - dwLenOfBuffer - 1;
//
// obtain current process token
//
if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken ))
{
if(GetLastError() != ERROR_NO_TOKEN)
{
return RETURNCODETOHRESULT(GetLastError());
}
//
// retry against process token if no thread token exists
//
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
return RETURNCODETOHRESULT(GetLastError());
}
}
//
// obtain user identified by current process' access token
//
bSuccess = GetTokenInformation( hToken, // identifies access token
TokenUser, // TokenUser info type
ptgUser, // retrieved info buffer
cbBuffer, // size of buffer passed-in
&cbBuffer // required buffer size
);
if (!bSuccess)
{
dwError= GetLastError();
// close token handle. do this even if error above
CloseHandle(hToken);
return RETURNCODETOHRESULT(dwError);
}
// close token handle
CloseHandle(hToken);
//
// obtain the textual representaion of the Sid
//
return GetTextualSid( ptgUser->User.Sid, // user binary Sid
pszPlaceToAppend, // buffer for TextualSid
&dwLenOfBuffAvailable // size/required buffer
);
}
HRESULT
ADM_SECURE_DATA::GetTextualSid(
PSID pSid, // binary Sid
LPTSTR TextualSid, // buffer for Textual representaion of Sid
LPDWORD cchSidSize // required/provided TextualSid buffersize
)
/*++
Routine Description:
Thsi method converts bianry SID to text representation
Arguments:
pSid - binary SID
TextualSid - buffer for Textual representaiton of SID
cchSidSize - required/provided TextualSid buffersize
Return Value:
HRESULT
--*/
{
PSID_IDENTIFIER_AUTHORITY psia;
DWORD dwSubAuthorities;
DWORD dwCounter;
DWORD cchSidCopy;
//
// test if Sid passed in is valid
//
if(!IsValidSid(pSid))
{
return RETURNCODETOHRESULT(ERROR_INVALID_SID);
}
// obtain SidIdentifierAuthority
psia = GetSidIdentifierAuthority(pSid);
// obtain sidsubauthority count
dwSubAuthorities = *GetSidSubAuthorityCount(pSid);
//
// compute approximate buffer length
// S-SID_REVISION- + identifierauthority- + subauthorities- + NULL
//
cchSidCopy = (15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);
//
// check provided buffer length.
// If not large enough, indicate proper size and setlasterror
//
if(*cchSidSize < cchSidCopy)
{
*cchSidSize = cchSidCopy;
return RETURNCODETOHRESULT(ERROR_INSUFFICIENT_BUFFER);
}
//
// prepare S-SID_REVISION-
//
cchSidCopy = wsprintf(TextualSid, "S-%lu-", SID_REVISION );
//
// prepare SidIdentifierAuthority
//
if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) )
{
cchSidCopy += wsprintf(TextualSid + cchSidCopy,
"0x%02hx%02hx%02hx%02hx%02hx%02hx",
(USHORT)psia->Value[0],
(USHORT)psia->Value[1],
(USHORT)psia->Value[2],
(USHORT)psia->Value[3],
(USHORT)psia->Value[4],
(USHORT)psia->Value[5]);
}
else
{
cchSidCopy += wsprintf(TextualSid + cchSidCopy,
"%lu",
(ULONG)(psia->Value[5] ) +
(ULONG)(psia->Value[4] << 8) +
(ULONG)(psia->Value[3] << 16) +
(ULONG)(psia->Value[2] << 24) );
}
//
// loop through SidSubAuthorities
//
for(dwCounter = 0 ; dwCounter < dwSubAuthorities ; dwCounter++)
{
cchSidCopy += wsprintf(TextualSid + cchSidCopy, "-%lu",
*GetSidSubAuthority(pSid, dwCounter) );
}
//
// tell the caller how many chars we provided, not including NULL
//
*cchSidSize = cchSidCopy;
return NO_ERROR;
}
ADM_GUID_MAP::ADM_GUID_MAP(
IN IUnknown * Object,
IN GUID guidServer
):m_Object( Object ),
m_ReferenceCount( 1 ),
m_guidServer(guidServer)
/*++
Routine Description:
ADM_GUID_MAP object constructor.
Arguments:
Object - Pointer to the object to associate.
Return Value:
None.
--*/
{
//
// Sanity check.
//
DBG_ASSERT( Object != NULL );
DBG_ASSERT( guidServer != GUID_NULL );
//
// Initialize any complex data members.
//
INITIALIZE_CRITICAL_SECTION( &m_ObjectLock );
//
// Put ourselves on the list.
//
AcquireDataLock();
InsertHeadList( &sm_GuidMapListHead, &m_GuidMapListEntry );
ReleaseDataLock();
} // ADM_GUID_MAP::ADM_GUID_MAP
ADM_GUID_MAP::~ADM_GUID_MAP()
/*++
Routine Description:
ADM_SECURE_DATA object destructor.
Arguments:
None.
Return Value:
None.
--*/
{
//
// Sanity check.
//
DBG_ASSERT( m_ReferenceCount == 0 );
//
// AddRef the ADM_SECURE_DATA
// Do this here instead of constructor to
// all handing of errors.
//
ADM_SECURE_DATA *psecdatData;
psecdatData = ADM_SECURE_DATA::FindAndReferenceClientSecureData( this );
if (psecdatData != NULL) {
psecdatData->Dereference();
psecdatData->Dereference();
}
//
// Cleanup.
//
AcquireDataLock();
RemoveEntryList( &m_GuidMapListEntry );
ReleaseDataLock();
DeleteCriticalSection( &m_ObjectLock );
} // ADM_SECURE_DATA::~ADM_SECURE_DATA
VOID
ADM_GUID_MAP::Initialize( VOID
)
/*++
Routine Description:
Initializes static global data private to ADM_SECURE_DATA.
Arguments:
hDll - Handle to this DLL.
Return Value:
None.
--*/
{
INITIALIZE_CRITICAL_SECTION( &sm_GuidMapDataLock );
#if DBG
sm_GuidMapLockOwner = 0;
sm_RefTraceLog = CreateRefTraceLog( 1024, 0 );
#endif
InitializeListHead( &sm_GuidMapListHead );
} // ADM_SECURE_DATA::Initialize
VOID
ADM_GUID_MAP::Terminate()
{
PLIST_ENTRY entry;
ADM_GUID_MAP * data;
//
// Free any secure data objects on our list.
//
AcquireDataLock();
entry = sm_GuidMapListHead.Flink;
while( entry != &sm_GuidMapListHead ) {
data = CONTAINING_RECORD(
entry,
ADM_GUID_MAP,
m_GuidMapListEntry
);
data->Dereference();
entry = sm_GuidMapListHead.Flink;
}
ReleaseDataLock();
DBG_ASSERT( IsListEmpty( &sm_GuidMapListHead ) );
//
// Final cleanup.
//
DeleteCriticalSection( &sm_GuidMapDataLock );
#if DBG
if( sm_RefTraceLog != NULL ) {
DestroyRefTraceLog( sm_RefTraceLog );
}
#endif
}
//
// Find and reference the ADM_GUID_MAP object associatd with the
// given object; create if not found.
//
ADM_GUID_MAP *
ADM_GUID_MAP::FindAndReferenceGuidMap(
IN IUnknown *Object )
{
PLIST_ENTRY entry;
ADM_GUID_MAP *pguidmapData;
AcquireDataLock();
for( entry = sm_GuidMapListHead.Flink ;
entry != &sm_GuidMapListHead ;
entry = entry->Flink )
{
pguidmapData = CONTAINING_RECORD( entry,
ADM_GUID_MAP,
m_GuidMapListEntry );
if( pguidmapData->m_Object == Object )
{
pguidmapData->Reference();
goto exit;
}
}
pguidmapData = NULL;
exit:
ReleaseDataLock();
return pguidmapData;
}