1450 lines
32 KiB
C++
1450 lines
32 KiB
C++
/*++
|
||
|
||
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:
|
||
|
||
--*/
|
||
|
||
|
||
extern "C" {
|
||
|
||
#include <nt.h>
|
||
#include <ntrtl.h>
|
||
#include <nturtl.h>
|
||
#include <ole2.h>
|
||
#include <windows.h>
|
||
#include <dbgutil.h>
|
||
|
||
} // extern "C"
|
||
|
||
#include <iadm.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_SecureDataListHead;
|
||
CRITICAL_SECTION ADM_SECURE_DATA::sm_SecureDataLock;
|
||
#if DBG
|
||
DWORD ADM_SECURE_DATA::sm_SecureDataLockOwner;
|
||
PTRACE_LOG ADM_SECURE_DATA::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
|
||
) :
|
||
m_Object( Object ),
|
||
m_KeyExchangeClient( NULL ),
|
||
m_KeyExchangeServer( NULL ),
|
||
m_SendCryptoStorage( NULL ),
|
||
m_ReceiveCryptoStorage( NULL ),
|
||
m_ReferenceCount( 1 )
|
||
/*++
|
||
|
||
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 );
|
||
|
||
//
|
||
// Initialize any complex data members.
|
||
//
|
||
|
||
InitializeCriticalSection( &m_ObjectLock );
|
||
|
||
//
|
||
// Put ourselves on the list.
|
||
//
|
||
|
||
AcquireDataLock();
|
||
InsertHeadList( &sm_SecureDataListHead, &m_SecureDataListEntry );
|
||
ReleaseDataLock();
|
||
|
||
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.
|
||
//
|
||
|
||
m_Object = NULL;
|
||
|
||
CleanupCryptoData();
|
||
|
||
AcquireDataLock();
|
||
RemoveEntryList( &m_SecureDataListEntry );
|
||
ReleaseDataLock();
|
||
|
||
DeleteCriticalSection( &m_ObjectLock );
|
||
UpdateObjectsDestroyed();
|
||
|
||
} // ADM_SECURE_DATA::~ADM_SECURE_DATA
|
||
|
||
|
||
BOOL
|
||
ADM_SECURE_DATA::Initialize(
|
||
HINSTANCE hDll
|
||
)
|
||
/*++
|
||
|
||
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) ) {
|
||
|
||
InitializeCriticalSection( &sm_SecureDataLock );
|
||
|
||
#if DBG
|
||
sm_SecureDataLockOwner = 0;
|
||
sm_RefTraceLog = CreateRefTraceLog( 1024, 0 );
|
||
#endif
|
||
|
||
InitializeListHead( &sm_SecureDataListHead );
|
||
|
||
} 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.
|
||
//
|
||
|
||
AcquireDataLock();
|
||
|
||
entry = sm_SecureDataListHead.Flink;
|
||
|
||
while( entry != &sm_SecureDataListHead ) {
|
||
|
||
data = CONTAINING_RECORD(
|
||
entry,
|
||
ADM_SECURE_DATA,
|
||
m_SecureDataListEntry
|
||
);
|
||
entry = entry->Flink;
|
||
|
||
data->Dereference();
|
||
|
||
}
|
||
|
||
ReleaseDataLock();
|
||
|
||
DBG_ASSERT( IsListEmpty( &sm_SecureDataListHead ) );
|
||
|
||
//
|
||
// Terminate the crypto stuff.
|
||
//
|
||
|
||
if( sm_ServerCryptoProvider != CRYPT_NULL ) {
|
||
result = ::IISCryptoCloseContainer( sm_ServerCryptoProvider );
|
||
DBG_ASSERT( SUCCEEDED(result) );
|
||
sm_ServerCryptoProvider = CRYPT_NULL;
|
||
}
|
||
|
||
if( sm_ClientCryptoProvider != CRYPT_NULL ) {
|
||
result = ::IISCryptoCloseContainer( sm_ClientCryptoProvider );
|
||
DBG_ASSERT( SUCCEEDED(result) );
|
||
sm_ClientCryptoProvider = CRYPT_NULL;
|
||
}
|
||
|
||
result = ::IISCryptoTerminate();
|
||
DBG_ASSERT( SUCCEEDED(result) );
|
||
|
||
//
|
||
// Final cleanup.
|
||
//
|
||
|
||
DeleteCriticalSection( &sm_SecureDataLock );
|
||
|
||
#if DBG
|
||
if( sm_RefTraceLog != NULL ) {
|
||
DestroyRefTraceLog( sm_RefTraceLog );
|
||
}
|
||
#endif
|
||
|
||
} // ADM_SECURE_DATA::Terminate
|
||
|
||
|
||
ADM_SECURE_DATA *
|
||
ADM_SECURE_DATA::FindAndReferenceSecureData(
|
||
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;
|
||
|
||
AcquireDataLock();
|
||
|
||
for( entry = sm_SecureDataListHead.Flink ;
|
||
entry != &sm_SecureDataListHead ;
|
||
entry = entry->Flink ) {
|
||
|
||
data = CONTAINING_RECORD(
|
||
entry,
|
||
ADM_SECURE_DATA,
|
||
m_SecureDataListEntry
|
||
);
|
||
|
||
if( data->m_Object == Object ) {
|
||
|
||
data->Reference();
|
||
ReleaseDataLock();
|
||
return data;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if( CreateIfNotFound ) {
|
||
|
||
data = new ADM_SECURE_DATA( Object );
|
||
|
||
if( data == NULL ) {
|
||
|
||
DBGPRINTF((
|
||
DBG_CONTEXT,
|
||
"ADM_SECURE_DATA::FindAndReferenceSecureData: out of memory\n"
|
||
));
|
||
|
||
} else {
|
||
|
||
data->Reference();
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
data = NULL;
|
||
}
|
||
|
||
ReleaseDataLock();
|
||
return data;
|
||
|
||
} // ADM_SECURE_DATA::FindAndReferenceSecureData
|
||
|
||
|
||
HRESULT
|
||
ADM_SECURE_DATA::GetClientSendCryptoStorage(
|
||
OUT IIS_CRYPTO_STORAGE ** SendCryptoStorage
|
||
)
|
||
/*++
|
||
|
||
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();
|
||
|
||
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
|
||
)
|
||
/*++
|
||
|
||
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();
|
||
|
||
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()
|
||
/*++
|
||
|
||
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;
|
||
PIIS_CRYPTO_BLOB clientKeyExchangeKeyBlob;
|
||
PIIS_CRYPTO_BLOB clientSignatureKeyBlob;
|
||
PIIS_CRYPTO_BLOB clientSessionKeyBlob;
|
||
PIIS_CRYPTO_BLOB clientHashBlob;
|
||
PIIS_CRYPTO_BLOB serverKeyExchangeKeyBlob;
|
||
PIIS_CRYPTO_BLOB serverSignatureKeyBlob;
|
||
PIIS_CRYPTO_BLOB serverSessionKeyBlob;
|
||
PIIS_CRYPTO_BLOB serverHashBlob;
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
DBG_ASSERT( m_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;
|
||
|
||
//
|
||
// 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
|
||
FALSE // fUseMachineKeyset
|
||
);
|
||
|
||
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 = IMSAdminBaseA_R_KeyExchangePhase1_Proxy(
|
||
(IMSAdminBase *)m_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 = IMSAdminBaseA_R_KeyExchangePhase2_Proxy(
|
||
(IMSAdminBase *)m_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
|
||
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(
|
||
keyExchangeClient->QueryProviderHandle(), // hProv
|
||
keyExchangeClient->AssumeServerSessionKey(), // hSessionKey
|
||
CRYPT_NULL, // hKeyExchangeKey
|
||
keyExchangeClient->AssumeServerSignatureKey(), // hSignatureKey
|
||
FALSE // fUseMachineKeyset
|
||
);
|
||
|
||
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();
|
||
|
||
}
|
||
|
||
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 );
|
||
|
||
//
|
||
// 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;
|
||
|
||
}
|
||
|
||
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 );
|
||
|
||
//
|
||
// 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();
|
||
|
||
}
|
||
|
||
return result;
|
||
|
||
} // ADM_SECURE_DATA::DoServerSideKeyExchangePhase2
|
||
|
||
|
||
HRESULT
|
||
ADM_SECURE_DATA::GetCryptoProviderHelper(
|
||
OUT HCRYPTPROV * UserProvider,
|
||
OUT HCRYPTPROV * CachedProvider,
|
||
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.
|
||
|
||
CryptoFlags - Flags to pass into IISCryptoGetStandardContainer().
|
||
|
||
Return Value:
|
||
|
||
HRESULT - 0 if successful, !0 otherwise.
|
||
|
||
--*/
|
||
{
|
||
|
||
HRESULT result = NO_ERROR;
|
||
HANDLE token = NULL;
|
||
LONG forcedFlags = 0;
|
||
HCRYPTPROV hprov;
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
DBG_ASSERT( UserProvider != NULL );
|
||
DBG_ASSERT( CachedProvider != NULL );
|
||
|
||
LockThis();
|
||
|
||
hprov = *CachedProvider;
|
||
|
||
if( hprov == CRYPT_NULL ) {
|
||
|
||
//
|
||
// Before we can get the provider, we must play some security
|
||
// games.
|
||
//
|
||
// The problem is that an ISAPI application (like, say, ASP)
|
||
// could be trying to access metadata via the ADMCOM object.
|
||
// In this situation, the current thread is impersonating a
|
||
// logged-on user, and that logged-on user probably doesn't have
|
||
// access to the necessary Win32 crypto registry keys. And,
|
||
// without access to these keys, we cannot establish a secure
|
||
// channel for the DCOM object.
|
||
//
|
||
// This is the infamous Sophia Bug.
|
||
//
|
||
// The solution implemented here is to get the current thread's
|
||
// impersonation token. If that token is !NULL, then set the
|
||
// token to NULL ("revert to self") before calling the crypto
|
||
// APIs. After calling the crypto APIs, reset the impersonation
|
||
// token to its original value.
|
||
//
|
||
// We'll start by retrieving the current impersonation token.
|
||
//
|
||
|
||
result = GetThreadImpersonationToken( &token );
|
||
|
||
if( SUCCEEDED(result) ) {
|
||
|
||
//
|
||
// If we actually have a token, then force the "machine
|
||
// keyset flag" in the crypto flags, then remove the
|
||
// impersonation token from this thread before calling
|
||
// the crypto routines.
|
||
//
|
||
|
||
if( token != NULL ) {
|
||
forcedFlags |= CRYPT_MACHINE_KEYSET;
|
||
result = SetThreadImpersonationToken( NULL );
|
||
}
|
||
|
||
if( SUCCEEDED(result) ) {
|
||
|
||
result = ::IISCryptoGetStandardContainer(
|
||
&hprov,
|
||
CryptoFlags | forcedFlags
|
||
);
|
||
|
||
//
|
||
// It's possible that the current thread had an
|
||
// impersonation token, but the current process is
|
||
// not a service (i.e. it's not running in the local
|
||
// system context).
|
||
//
|
||
// If the above call failed with NET_BAD_KEYSET (which,
|
||
// in this context, generally means the current thread
|
||
// did not have access to the necessary parts of the
|
||
// registry) AND we forced the CRYPT_MACHINE_KEYSET flag
|
||
// because of the impersonation token AND the caller
|
||
// didn't ask for the CRYPT_MACHINE_KEYSET flag, then
|
||
// retry the call without the CRYPT_MACHINE_KEYSET flag.
|
||
//
|
||
|
||
if( result == NTE_BAD_KEYSET &&
|
||
( ( forcedFlags & CRYPT_MACHINE_KEYSET ) != 0 ) &&
|
||
( ( CryptoFlags & CRYPT_MACHINE_KEYSET ) == 0 ) ) {
|
||
|
||
result = ::IISCryptoGetStandardContainer(
|
||
&hprov,
|
||
CryptoFlags
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if( token != NULL ) {
|
||
|
||
HRESULT result2;
|
||
|
||
//
|
||
// The thread had an impersonation token before we
|
||
// removed it, so restore it now. If the restoration
|
||
// fails, we're in trouble.
|
||
//
|
||
|
||
result2 = SetThreadImpersonationToken( token );
|
||
|
||
if( FAILED(result2) ) {
|
||
|
||
//
|
||
// This is really, really, really bad news. The current
|
||
// thread, which does not have an impersonation token
|
||
// (and is therefore running in the system context)
|
||
// cannot reset its impersonation token to the original
|
||
// value.
|
||
//
|
||
|
||
DBG_ASSERT( !"SetThreadImpersonationToken() failed!!!" );
|
||
|
||
}
|
||
|
||
//
|
||
// Regardless of the completion status, we must close
|
||
// the token handle before proceeding.
|
||
//
|
||
|
||
DBG_REQUIRE( CloseHandle( token ) );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if( SUCCEEDED(result) ) {
|
||
DBG_ASSERT( hprov != CRYPT_NULL );
|
||
*CachedProvider = hprov;
|
||
*UserProvider = hprov;
|
||
}
|
||
|
||
UnlockThis();
|
||
return result;
|
||
|
||
} // ADM_SECURE_DATA::GetCryptoProviderHelper
|
||
|
||
|
||
HRESULT
|
||
ADM_SECURE_DATA::GetThreadImpersonationToken(
|
||
OUT HANDLE * Token
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Gets the impersonation token for the current thread.
|
||
|
||
Arguments:
|
||
|
||
Token - Receives a handle to the impersonation token if successful.
|
||
If successful, it is the caller's responsibility to CloseHandle()
|
||
the token when no longer needed.
|
||
|
||
Return Value:
|
||
|
||
HRESULT - 0 if successful, !0 otherwise.
|
||
|
||
--*/
|
||
{
|
||
|
||
HRESULT result = NO_ERROR;
|
||
|
||
DBG_ASSERT( Token != NULL );
|
||
|
||
//
|
||
// Open the token.
|
||
//
|
||
|
||
if( !OpenThreadToken(
|
||
GetCurrentThread(),
|
||
TOKEN_ALL_ACCESS,
|
||
TRUE,
|
||
Token
|
||
) ) {
|
||
|
||
DWORD err = GetLastError();
|
||
|
||
//
|
||
// There are a couple of "expected" errors here:
|
||
//
|
||
// ERROR_NO_TOKEN - The thread has no impersonation token.
|
||
// ERROR_CALL_NOT_IMPLEMENTED - We're probably on Win9x.
|
||
// ERROR_NOT_SUPPORTED - Ditto.
|
||
//
|
||
// If OpenThreadToken() failed with any of the above error codes,
|
||
// then succeed the call, but return a NULL token handle.
|
||
//
|
||
|
||
if( err != ERROR_NO_TOKEN &&
|
||
err != ERROR_CALL_NOT_IMPLEMENTED &&
|
||
err != ERROR_NOT_SUPPORTED ) {
|
||
|
||
result = HRESULT_FROM_WIN32(err);
|
||
|
||
}
|
||
|
||
*Token = NULL;
|
||
}
|
||
|
||
return result;
|
||
|
||
} // ADM_SECURE_DATA::GetThreadImpersonationToken
|
||
|
||
|
||
HRESULT
|
||
ADM_SECURE_DATA::SetThreadImpersonationToken(
|
||
IN HANDLE Token
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets the impersonation token for the current thread.
|
||
|
||
Arguments:
|
||
|
||
Token - A handle to the impersonation token.
|
||
|
||
Return Value:
|
||
|
||
HRESULT - 0 if successful, !0 otherwise.
|
||
|
||
--*/
|
||
{
|
||
|
||
HRESULT result = NO_ERROR;
|
||
|
||
//
|
||
// Set it.
|
||
//
|
||
|
||
if( !SetThreadToken(
|
||
NULL,
|
||
Token
|
||
) ) {
|
||
DWORD err = GetLastError();
|
||
result = HRESULT_FROM_WIN32(err);
|
||
}
|
||
|
||
return result;
|
||
|
||
} // ADM_SECURE_DATA::SetThreadImpersonationToken
|
||
|
||
|
||
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
|
||
|