|
|
/*++
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; }
|