|
|
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000-2002 Microsoft Corporation
//
// Module Name:
// EncryptedBSTRSrc.cpp
//
// Description:
// Class to encrypt and decrypt BSTRs.
//
// Maintained By:
// John Franco (jfranco) 15-APR-2002
//
//////////////////////////////////////////////////////////////////////////////
#include "EncryptedBSTR.h"
//////////////////////////////////////////////////////////////////////////////
// Type Definitions
//////////////////////////////////////////////////////////////////////////////
typedef BOOL (*PFNCRYPTPROTECTMEMORY)( LPVOID, DWORD, DWORD ); typedef BOOL (*PFNCRYPTUNPROTECTMEMORY)( LPVOID, DWORD, DWORD );
//////////////////////////////////////////////////////////////////////////////
//++
//
// class CCryptRoutines
//
// Description:
// CryptProtectMemory and CryptUnprotectMemory are not available on early
// releases of XP Client, which the admin pack must support. Therefore,
// we cannot link to those routines implicitly. CCryptRoutines wraps the
// work of loading crypt32.dll dynamically and looking for the exported
// functions.
//
//--
//////////////////////////////////////////////////////////////////////////////
class CCryptRoutines { private: PFNCRYPTPROTECTMEMORY m_pfnCryptProtectMemory; PFNCRYPTUNPROTECTMEMORY m_pfnCryptUnprotectMemory; HMODULE m_hmodCrypt32; LONG m_nRefCount; CRITICAL_SECTION m_cs; BOOL m_fCritSecInitialized; DWORD m_scLoadStatus;
static BOOL S_FBogusCryptRoutine( LPVOID, DWORD, DWORD );
public: CCryptRoutines( void ) : m_pfnCryptProtectMemory( NULL ) , m_pfnCryptUnprotectMemory( NULL ) , m_hmodCrypt32( NULL ) , m_nRefCount( 0 ) , m_fCritSecInitialized( FALSE ) , m_scLoadStatus( ERROR_SUCCESS ) { m_fCritSecInitialized = InitializeCriticalSectionAndSpinCount( &m_cs, RECOMMENDED_SPIN_COUNT ); if ( m_fCritSecInitialized == FALSE ) { TW32( GetLastError() ); } // if
} //*** CCryptRoutines::CCryptRoutines
~CCryptRoutines( void ) { if ( m_hmodCrypt32 != NULL ) { FreeLibrary( m_hmodCrypt32 ); }
if ( m_fCritSecInitialized ) { DeleteCriticalSection( &m_cs ); }
} //*** CCryptRoutines::~CCryptRoutines
void AddReferenceToRoutines( void ); void ReleaseReferenceToRoutines( void );
BOOL CryptProtectMemory( IN OUT LPVOID pDataIn, // in out data to encrypt
IN DWORD cbDataIn, // multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE
IN DWORD dwFlags // CRYPTPROTECTMEMORY_* flags from wincrypt.h
);
BOOL CryptUnprotectMemory( IN OUT LPVOID pDataIn, // in out data to decrypt
IN DWORD cbDataIn, // multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE
IN DWORD dwFlags // CRYPTPROTECTMEMORY_* flags from wincrypt.h
);
}; //*** class CCryptRoutines
//////////////////////////////////////////////////////////////////////////////
// Global Variables
//////////////////////////////////////////////////////////////////////////////
static CCryptRoutines g_crCryptRoutines;
//****************************************************************************
//
// CCryptRoutines
//
//****************************************************************************
//////////////////////////////////////////////////////////////////////////////
//++
//
// CCryptRoutines::AddReferenceToRoutines
//
// Description:
// Add a reference to the routines and load the addresses of the APIs
// if not already done so.
//
// Arguments:
// None.
//
// Return Values:
// None.
//
//--
//////////////////////////////////////////////////////////////////////////////
void CCryptRoutines::AddReferenceToRoutines( void ) { TraceFunc( "" );
if ( m_fCritSecInitialized ) { EnterCriticalSection( &m_cs );
m_nRefCount += 1;
if ( m_nRefCount == 1 ) { Assert( m_hmodCrypt32 == NULL );
//
// Load the DLL containing the APIs.
//
m_hmodCrypt32 = LoadLibraryW( L"crypt32.dll" ); if ( m_hmodCrypt32 == NULL ) { m_scLoadStatus = TW32( GetLastError() ); goto Cleanup; } // if: error loading the DLL
//
// Get the address of the APIs.
//
m_pfnCryptProtectMemory = reinterpret_cast< PFNCRYPTPROTECTMEMORY >( GetProcAddress( m_hmodCrypt32, "CryptProtectMemory" ) ); if ( m_pfnCryptProtectMemory == NULL ) { m_scLoadStatus = TW32( GetLastError() ); goto Cleanup; } // if
m_pfnCryptUnprotectMemory = reinterpret_cast< PFNCRYPTUNPROTECTMEMORY >( GetProcAddress( m_hmodCrypt32, "CryptUnprotectMemory" ) ); if ( m_pfnCryptProtectMemory == NULL ) { m_scLoadStatus = TW32( GetLastError() ); m_pfnCryptProtectMemory = NULL; goto Cleanup; } // if
} // if: first reference
} // if critical section is initialized.
Cleanup:
if ( m_pfnCryptProtectMemory == NULL ) { m_pfnCryptProtectMemory = S_FBogusCryptRoutine; } // if
if ( m_pfnCryptUnprotectMemory == NULL ) { m_pfnCryptUnprotectMemory = S_FBogusCryptRoutine; } // if
if ( m_fCritSecInitialized ) { LeaveCriticalSection( &m_cs ); } // if
TraceFuncExit();
} //*** CCryptRoutines::AddReferenceToRoutines
//////////////////////////////////////////////////////////////////////////////
//++
//
// CCryptRoutines::ReleaseReferenceToRoutines
//
// Description:
// Release a reference to the routines and free the library if this was
// the last reference.
//
// Arguments:
// None.
//
// Return Values:
// None.
//
//--
//////////////////////////////////////////////////////////////////////////////
void CCryptRoutines::ReleaseReferenceToRoutines( void ) { TraceFunc( "" );
if ( m_fCritSecInitialized ) { EnterCriticalSection( &m_cs );
m_nRefCount -= 1;
if ( m_nRefCount == 0 ) { Assert( m_hmodCrypt32 != NULL ); if ( m_hmodCrypt32 != NULL ) { FreeLibrary( m_hmodCrypt32 ); m_hmodCrypt32 = NULL; m_pfnCryptProtectMemory = NULL; m_pfnCryptUnprotectMemory = NULL; } // if
} // if: last reference was released
LeaveCriticalSection( &m_cs ); } // if
TraceFuncExit();
} //*** CCryptRoutines::ReleaseReferenceToRoutines
//////////////////////////////////////////////////////////////////////////////
//++
//
// CCryptRoutines::CryptProtectMemory
//
// Description:
// Encrypt memory. Required since XP doesn't have CryptProtectMemory.
//
// Arguments:
// pDataIn
// cbDataIn
// dwFlags
//
// Return Values:
// TRUE - Operation was successful.
// FALSE - Operation failed. Call GetLastError().
//
//--
//////////////////////////////////////////////////////////////////////////////
BOOL CCryptRoutines::CryptProtectMemory( IN OUT LPVOID pDataIn, // in out data to encrypt
IN DWORD cbDataIn, // multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE
IN DWORD dwFlags // CRYPTPROTECTMEMORY_* flags from wincrypt.h
) { TraceFunc( "" );
BOOL fSuccess = TRUE; DWORD sc = ERROR_SUCCESS;
fSuccess = (*m_pfnCryptProtectMemory)( pDataIn, cbDataIn, dwFlags ); if ( fSuccess == FALSE ) { sc = TW32( GetLastError() ); }
#ifdef DEBUG
// Only needed for debug builds because TW32 might overwrite the last error.
SetLastError( sc ); #endif
RETURN( fSuccess );
} //*** CCryptRoutines::CryptProtectMemory
//////////////////////////////////////////////////////////////////////////////
//++
//
// CCryptRoutines::CryptUnprotectMemory
//
// Description:
// Decrypt memory. Required since XP doesn't have CryptUnprotectMemory.
//
// Arguments:
// pDataIn
// cbDataIn
// dwFlags
//
// Return Values:
// TRUE - Operation was successful.
// FALSE - Operation failed. Call GetLastError().
//
//--
//////////////////////////////////////////////////////////////////////////////
BOOL CCryptRoutines::CryptUnprotectMemory( IN OUT LPVOID pDataIn, // in out data to decrypt
IN DWORD cbDataIn, // multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE
IN DWORD dwFlags // CRYPTPROTECTMEMORY_* flags from wincrypt.h
) { TraceFunc( "" );
BOOL fSuccess = TRUE; DWORD sc = ERROR_SUCCESS;
fSuccess = (*m_pfnCryptUnprotectMemory)( pDataIn, cbDataIn, dwFlags ); if ( fSuccess == FALSE ) { sc = TW32( GetLastError() ); }
#ifdef DEBUG
// Only needed for debug builds because TW32 might overwrite the last error.
SetLastError( sc ); #endif
RETURN( fSuccess );
} //*** CCryptRoutines::CryptUnprotectMemory
//////////////////////////////////////////////////////////////////////////////
//++
//
// CCryptRoutines::S_FBogusCryptRoutine
//
// Description:
// Stand-in function for when the routines are not available.
//
// Arguments:
// LPVOID
// DWORD
// DWORD
//
// Return Values:
// TRUE - Pretend always to succeed.
//
//--
//////////////////////////////////////////////////////////////////////////////
BOOL CCryptRoutines::S_FBogusCryptRoutine( LPVOID, DWORD, DWORD ) { return TRUE;
} //*** CCryptRoutines::S_FBogusCryptRoutine
//****************************************************************************
//
// CEncryptedBSTR
//
//****************************************************************************
//////////////////////////////////////////////////////////////////////////////
//++
//
// CEncryptedBSTR::CEncryptedBSTR
//
// Description:
// Default constructor.
//
//--
//////////////////////////////////////////////////////////////////////////////
CEncryptedBSTR::CEncryptedBSTR( void ) { TraceFunc( "" );
m_dbBSTR.cbData = 0; m_dbBSTR.pbData = NULL;
g_crCryptRoutines.AddReferenceToRoutines();
TraceFuncExit();
} //*** CEncryptedBSTR::CEncryptedBSTR
//////////////////////////////////////////////////////////////////////////////
//++
//
// CEncryptedBSTR::~CEncryptedBSTR
//
// Description:
// Destructor.
//
//--
//////////////////////////////////////////////////////////////////////////////
CEncryptedBSTR::~CEncryptedBSTR( void ) { TraceFunc( "" );
Erase();
g_crCryptRoutines.ReleaseReferenceToRoutines();
TraceFuncExit();
} //*** CEncryptedBSTR::~CEncryptedBSTR
//////////////////////////////////////////////////////////////////////////////
//++
//
// CEncryptedBSTR::HrSetWSTR
//
// Description:
// Set new data into this object to be stored as encrypted data.
//
// Arguments:
// pcwszIn - String to store.
// cchIn - Number of characters in the string, not including NUL.
//
// Return Values:
// S_OK - Operation completed successfully.
// Other HRESULTs.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CEncryptedBSTR::HrSetWSTR( PCWSTR pcwszIn , size_t cchIn ) { TraceFunc( "" );
HRESULT hr = S_OK; DATA_BLOB dbEncrypted = { 0, NULL }; if ( cchIn > 0 ) { BOOL fSuccess = FALSE; DWORD cbStringAndNull = (DWORD) ( ( cchIn + 1 ) * sizeof( *pcwszIn ) ); DWORD cBlocks = ( cbStringAndNull / CRYPTPROTECTMEMORY_BLOCK_SIZE ) + 1; DWORD cbMemoryRequired = cBlocks * CRYPTPROTECTMEMORY_BLOCK_SIZE;
dbEncrypted.pbData = new BYTE[ cbMemoryRequired ]; if ( dbEncrypted.pbData == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } dbEncrypted.cbData = cbMemoryRequired;
CopyMemory( dbEncrypted.pbData, pcwszIn, cbStringAndNull ); fSuccess = g_crCryptRoutines.CryptProtectMemory( dbEncrypted.pbData, dbEncrypted.cbData, CRYPTPROTECTMEMORY_SAME_PROCESS ); if ( fSuccess == FALSE ) { DWORD scLastError = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scLastError ); goto Cleanup; } // if: error from CryptProtectMemory
Erase(); m_dbBSTR = dbEncrypted; dbEncrypted.pbData = NULL; dbEncrypted.cbData = 0; } // if: input data is not empty
else { Erase(); } // else: input data is empty
Cleanup:
if ( dbEncrypted.pbData != NULL ) { delete [] dbEncrypted.pbData; }
HRETURN( hr );
} //*** CEncryptedBSTR::HrSetWSTR
//////////////////////////////////////////////////////////////////////////////
//++
//
// CEncryptedBSTR::HrGetBSTR
//
// Description:
// Retrieve an unencrypted copy of the data.
//
// Arguments:
// pbstrOut - BSTR to return data in.
//
// Return Values:
// S_OK - Operation completed successfully.
// E_OUTOFMEMORY - Error allocating memory.
// Other HRESULTs.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CEncryptedBSTR::HrGetBSTR( BSTR * pbstrOut ) const { TraceFunc( "" );
HRESULT hr = S_OK; BYTE * pbDecrypted = NULL;
if ( pbstrOut == NULL ) { hr = THR( E_POINTER ); goto Cleanup; } *pbstrOut = NULL;
if ( m_dbBSTR.cbData > 0 ) { BOOL fSuccess = FALSE;
pbDecrypted = new BYTE[ m_dbBSTR.cbData ]; if ( pbDecrypted == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; }
CopyMemory( pbDecrypted, m_dbBSTR.pbData, m_dbBSTR.cbData ); fSuccess = g_crCryptRoutines.CryptUnprotectMemory( pbDecrypted, m_dbBSTR.cbData, CRYPTPROTECTMEMORY_SAME_PROCESS ); if ( fSuccess == FALSE ) { DWORD scLastError = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scLastError ); goto Cleanup; } // if: error from CryptUnprotectMemory
*pbstrOut = TraceSysAllocString( reinterpret_cast< const OLECHAR* >( pbDecrypted ) ); if ( *pbstrOut == NULL ) { hr = E_OUTOFMEMORY; goto Cleanup; } } // if: data is not empty
else // nothing to decrypt
{ hr = S_FALSE; } // else: data is empty
Cleanup:
if ( pbDecrypted != NULL ) { ::SecureZeroMemory( pbDecrypted, m_dbBSTR.cbData ); delete [] pbDecrypted; }
HRETURN( hr );
} //*** CEncryptedBSTR::HrGetBSTR
//////////////////////////////////////////////////////////////////////////////
//++
//
// CEncryptedBSTR::HrAssign
//
// Description:
// Make a copy of another encrypted BSTR object to replace the
// content we are currently holding.
//
// Arguments:
// rSourceIn - Object to copy.
//
// Return Values:
// S_OK - Operation completed successfully.
// E_OUTOFMEMORY - Error allocating memory.
//
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT CEncryptedBSTR::HrAssign( const CEncryptedBSTR & rSourceIn ) { TraceFunc( "" );
HRESULT hr = S_OK; BYTE * pbCopy = NULL;
if ( rSourceIn.m_dbBSTR.cbData > 0 ) { pbCopy = new BYTE[ rSourceIn.m_dbBSTR.cbData ]; if ( pbCopy == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } CopyMemory( pbCopy, rSourceIn.m_dbBSTR.pbData, rSourceIn.m_dbBSTR.cbData );
Erase(); m_dbBSTR.cbData = rSourceIn.m_dbBSTR.cbData; m_dbBSTR.pbData = pbCopy; pbCopy = NULL; } // if: input data is not empty
else { Erase(); } // else: input data is empty
Cleanup:
if ( pbCopy != NULL ) { ::SecureZeroMemory( pbCopy, rSourceIn.m_dbBSTR.cbData ); delete [] pbCopy; }
HRETURN( hr );
} //*** CEncryptedBSTR::HrAssign
|