Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

5795 lines
149 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name :
comobj.cxx
Abstract:
This module defines DCOM Admin Ex APIs used for certificate information replication.
Author:
Philippe Choquier ( Phillich ) 23-Jun-97
Alex Mallet (amallet) 17-Feb-1998
--*/
#define UNICODE
extern "C" {
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
}
#include <dbgutil.h>
#include <ole2.h>
#include <windows.h>
#include <stdio.h>
#define USE_CAPI2
#if defined(USE_CAPI2)
#include <wincrypt.h>
#endif
extern "C" {
#define SECURITY_WIN32
#include <sspi.h>
}
#include <spseal.h>
#include <issperr.h>
#include <schnlsp.h>
#include <md5.h>
#include <iadmw.h>
#include <admex.h>
#include <iiscnfgp.h>
#include <mdcommsg.h>
#include <replseed.hxx>
#include <iis64.h>
#include "comobj.hxx"
#define RETURNCODETOHRESULT(rc) \
(((rc) < 0x10000) \
? HRESULT_FROM_WIN32(rc) \
: (rc))
#define PAD4(a) (((a)+3)&~3)
#define MSB(a) (BYTE) (((a) & 0xFF00) >> 8)
#define LSB(a) (BYTE) ((a) & 0xFF)
#define LENGTH( msb, lsb ) (DWORD) ( (msb << 8) + lsb )
//
// Globals
//
DECLARE_DEBUG_PRINTS_OBJECT();
ULONG g_dwRefCount = 0;
#include <initguid.h>
DEFINE_GUID(IisADMExsGuid,
0x784d8905, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e);
CADMEXCOM::CADMEXCOM()
{
m_dwRefCount = 0;
m_pIMSAdminReplication = new CADMEXCOM_IMSAdminReplication(this);
m_pIMSAdminCryptoCapabilities = new CADMEXCOM_IMSAdminCryptoCapabilities( this );
}
CADMEXCOM::~CADMEXCOM()
{
if ( m_pIMSAdminReplication )
{
delete m_pIMSAdminReplication;
}
if ( m_pIMSAdminCryptoCapabilities )
{
delete m_pIMSAdminCryptoCapabilities;
}
}
HRESULT
CADMEXCOM::QueryInterface(
REFIID riid,
void **ppObject)
{
if (riid==IID_IUnknown ) {
*ppObject = (IUnknown *) this;
AddRef();
}
else if ( riid==IID_IMSAdminReplication) {
*ppObject = (IMSAdminReplication *)m_pIMSAdminReplication;
AddRef();
}
else if ( riid==IID_IMSAdminCryptoCapabilities) {
*ppObject = (IMSAdminCryptoCapabilities *)m_pIMSAdminCryptoCapabilities;
AddRef();
}
else {
return E_NOINTERFACE;
}
return NO_ERROR;
}
ULONG
CADMEXCOM::AddRef(
)
{
DWORD dwRefCount;
dwRefCount = InterlockedIncrement((long *)&m_dwRefCount);
return dwRefCount;
}
ULONG
CADMEXCOM::Release(
)
{
DWORD dwRefCount;
dwRefCount = InterlockedDecrement((long *)&m_dwRefCount);
if( dwRefCount == 0 ) {
delete this;
return 0;
}
return dwRefCount;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SSL Information Replication Interface
In order to determine whether information needs to be replicated between two machines,
the GetSignature() method is called; this is expected to return a "signature" that can be
compared with the "signature" on the other machine - if the signatures match, they
have the same information, else replication needs to occur.
To replicate, the Serialize() method is called to serialize all the config information
into a buffer; this buffer is encrypted with a generated session key [because it contains
the private keys for all the server certificates]. The data to generate the session key
is stored in the metabase, in encrypted format.
Propagate() doesn't do anything.
DeSerialize() on the target machine regenerates the session key out of the metabase, uses it
to decrypt the buffer generated by Serialize() and creates server certificates and
CTLs in the appropriate places.
The actual configuration information in the metabase itself is replicated during
the (separate) metabase replication process.
Each server instance has the following SSL information associated with it that
needs to be replicated :
1. Server certificate, server private key, cert chain for server certificate
2. Certificate Trust List, signing cert for CTL
GetSignature() :
----------------
Identifying info for server certificates :
<hash of cert stored under LM/W3SVC>:(instance_cert_information)?
where instance_cert_information = <instance #>=<hash of leaf cert>,<hash of issuer 1>,
<hash of issuer of issuer 1> .... |
End of instance cert information is marked by "|"
Identifying info for CTL :
[CTL_INFO for CTL stored under /LM/W3SVC]:(<instance #>=CTL_INFO)?
where CTL_INFO = <CTL list identifier>,(<hash of cert in ctl>)+,<hash of CTL signer>
Signature : MD5(Info for server certificates : Info for CTLs)
-----------------------------------------------------------------------------------------------*/
#define MB_ROOT_PATH L"/LM/W3SVC"
#define TIMEOUT_VALUE 30000 //NOTE - magic number !
#define INITIAL_BUFFER_SIZE 2048
#define MY_STORE_NAME L"MY"
#define CA_STORE_NAME L"CA"
#define ROOT_STORE_NAME L"ROOT"
#define TRUST_STORE_NAME L"TRUST"
#define COLON ':'
#define INSTANCE_TRAILER_BYTE '|'
#define CERT_HEADER "CERTS="
#define CERT_HEADER_SIZE (sizeof(CERT_HEADER) - 1)
#define CTL_HEADER "CTL="
#define CTL_HEADER_SIZE (sizeof(CTL_HEADER) - 1)
#define SIGNATURE 0
#define CONFIGURATION 1
#define REPLICATION_SESSION_KEY_CONTAINER L"IIS Replication Session Key"
#define MIN_REPLICATION_INFO_SIZE 4 //> <MSB><LSB>":"<some info>"|"
#define REPL_INTERNAL_ERROR RETURNCODETOHRESULT( ERROR_INVALID_PARAMETER )
// Array holding properties required to reconstruct cert context
static DWORD adwMetabaseCertProperties[] = { MD_SSL_CERT_HASH, BINARY_METADATA,
MD_SSL_CERT_STORE_NAME, STRING_METADATA };
#define cNumCertMetabaseProperties sizeof(adwMetabaseCertProperties)/sizeof(DWORD)
// Array holding properties necessary to reconstruct CTL context
static DWORD adwMetabaseCTLProperties[] = { MD_SSL_CTL_IDENTIFIER, BINARY_METADATA,
MD_SSL_CTL_STORE_NAME, STRING_METADATA };
#define cNumCTLMetabaseProperties sizeof(adwMetabaseCTLProperties)/sizeof(DWORD)
#if DBG
VOID
Dump(
CHAR *pchMessage,
LPBYTE pb,
DWORD dw
)
{
for ( UINT x = 0 ; x < dw ; ++x )
{
sprintf(pchMessage + x*3, "%02x ", pb[x] );
}
sprintf(pchMessage + x*3,"\n");
}
#endif
CADMEXCOM_IMSAdminReplication::CADMEXCOM_IMSAdminReplication( CADMEXCOM *pAdmExCom ) :
m_pAdmExCom( pAdmExCom ),
m_pMB( NULL ),
m_fGotSeed( FALSE )
{
HRESULT hRes;
COSERVERINFO *pcsiParam = NULL;
IClassFactory * pcsfFactory = NULL;
//
// Retrieve class factory for metabase object
//
hRes = CoGetClassObject(CLSID_MSAdminBase_W, CLSCTX_SERVER, pcsiParam,
IID_IClassFactory, ( void ** ) &pcsfFactory );
if ( FAILED(hRes) )
{
DBGPRINTF((DBG_CONTEXT, "CoGetClassObject failed : hRes : %x, Win32 : 0x%x\n",
hRes, HRESULTTOWIN32(hRes)));
return;
}
//
// Retrieve the actual metabase object interface
//
hRes = pcsfFactory->CreateInstance( NULL,
IID_IMSAdminBase,
(void **) &m_pMB );
if ( FAILED(hRes) )
{
DBGPRINTF((DBG_CONTEXT, "CreateInstance failed : hRes : %x, Win32 : 0x%x\n",
hRes, HRESULTTOWIN32(hRes)));
return;
}
pcsfFactory->Release();
}
CADMEXCOM_IMSAdminReplication::~CADMEXCOM_IMSAdminReplication()
{
if ( m_pMB )
{
m_pMB->Release();
m_pMB = NULL;
}
}
HRESULT STDMETHODCALLTYPE
CADMEXCOM_IMSAdminReplication::GetSignature(
/* [in] */ DWORD dwBufferSize,
/* [size_is][out] */ unsigned char __RPC_FAR *pbBuffer,
/* [out] */ DWORD __RPC_FAR *pdwMDRequiredBufferSize)
/*++
Routine Description:
Used to retrieve a "signature" for the certificate information to be retrieved; if the
signatures on two machines match, then it can be assumed that they have identical
SSL configurations.
Arguments:
dwBufferSize - size of buffer passed in
pbBuffer - buffer to be filled in with signature
*pdwMDRequiredBufferSize - size of buffer required for data
Returns:
HRESULT indicating success/failure.
--*/
{
DBGPRINTF((DBG_CONTEXT,
"GetSignature() called for SSL info replication\n"));
HRESULT hRes = S_OK;
hRes = GetConfigurationInformation( SIGNATURE,
NULL,
m_pMB,
pbBuffer,
dwBufferSize,
pdwMDRequiredBufferSize );
#if DBG
if ( FAILED( hRes ) )
{
DBGPRINTF((DBG_CONTEXT,
"GetSignature() failed with Win32 error : 0x%x\n",
HRESULTTOWIN32( hRes )));
}
else
{
CHAR achSig[200];
Dump( achSig, pbBuffer, *pdwMDRequiredBufferSize );
DBGPRINTF((DBG_CONTEXT,
"GetSignature() returned %s\n",
achSig));
}
#endif
return hRes;
} //::GetSignature
HRESULT STDMETHODCALLTYPE
CADMEXCOM_IMSAdminReplication::Serialize(
/* [in] */ DWORD dwBufferSize,
/* [size_is][out] */ unsigned char __RPC_FAR *pbBuffer,
/* [out] */ DWORD __RPC_FAR *pdwMDRequiredBufferSize)
/*++
Routine Description:
Serializes the SSL information for IIS.
Arguments:
dwBufferSize - size of buffer pointed to by pbBuffer
pbBuffer - buffer to receive serialized SSL configuration; can be NULL if
caller wants to determine size of buffer necessary
pdwMDRequiredBufferSize - pointer to size of buffer necessary to hold the
information; updated if pbBuffer is too small to hold the information
Returns:
HRESULT indicating success/failure.
--*/
{
HRESULT hRes = S_OK;
HCRYPTKEY hSessionKey = NULL;
DBGPRINTF((DBG_CONTEXT,
"Serialize() called for SSL info replication\n"));
hRes = RegenerateSessionKey( m_pMB,
&hSessionKey );
if ( S_OK == hRes )
{
m_fGotSeed = TRUE;
hRes = GetConfigurationInformation( CONFIGURATION,
&hSessionKey,
m_pMB,
pbBuffer,
dwBufferSize,
pdwMDRequiredBufferSize );
#if DBG
if ( FAILED( hRes ) )
{
DBGPRINTF((DBG_CONTEXT,
"Serialize() failed with Win32 error : 0x%x\n",
HRESULTTOWIN32( hRes )));
}
#endif
}
return hRes;
} //::Serialize
HRESULT STDMETHODCALLTYPE
CADMEXCOM_IMSAdminReplication::Propagate(
/* [in] */ DWORD dwBufferSize,
/* [size_is][in] */ unsigned char __RPC_FAR *pszBuffer)
/*++
Routine Description:
Propagates data necessary to re-generate session key from metabase on source machine
to metabase on target machine
Arguments:
dwBufferSize - size of buffer pointed to by pbBuffer
pbBuffer - name of target machine
Returns:
HRESULT indicating success/failure.
--*/
{
DBGPRINTF((DBG_CONTEXT,
"Propagate() called for SSL info replication\n"));
if ( !m_fGotSeed )
{
return S_FALSE;
}
else
{
return S_OK;
}
}//::Propagate
HRESULT STDMETHODCALLTYPE
CADMEXCOM_IMSAdminReplication::Propagate2(
/* [in] */ DWORD dwBufferSize,
/* [size_is][in] */ unsigned char __RPC_FAR *pszBuffer,
/* [in] */ DWORD dwSignatureMismatch )
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE
CADMEXCOM_IMSAdminReplication::DeSerialize(
/* [in] */ DWORD dwBufferSize,
/* [size_is][in] */ unsigned char __RPC_FAR *pbBuffer)
/*++
Routine Description:
Deserializes the SSL information produced by Serialize()
Arguments:
dwBufferSize - size of buffer pointed to by pbBuffer
pbBuffer - buffer holding serialized SSL configuration
Returns:
HRESULT indicating success/failure.
Extended Description :
The information for each instance is separate, with instance data being in the
form
<Instance Data> = <MSB of instance #><LSB of instance #>":"<instance SSL info>"|"
<Instance SSL Info> = <server certificate information><CTL information>
<Server certificate information> = "" |
"CERTS="<SHA1 hash of first cert in chain><MSB of length of private key blob>
<LSB of length of private key blob><private key blob of first cert>
<MSB of length of serialized store containing chain><LSB of length of serialized store
containing chain><serialized store>
The SHA1 hash is added so that it's possible to figure out which cert in the store
is the leaf cert when unserializing the buffer.
<CTL information> = "" |
"CTL="<hash of CTL signer><MSB of length of serialized store><LSB of length of serialized
store><serialized store>
The serialized store contains the CTL, the certs in the CTL and the signer of the CTL.
--*/
{
HRESULT hRes = S_OK;
BYTE *pbReplicationInfo = NULL;
BYTE *pbPosition = NULL;
BYTE *pbEnd = NULL;
DWORD dwInstance = 0;
HCRYPTKEY hSessionKey = NULL;
DBGPRINTF((DBG_CONTEXT,
"DeSerialize() called for SSL info replication\n"));
//
// No information to serialize
//
if ( dwBufferSize == 0 )
{
return S_OK;
}
//
// Invalid buffer or buffer size
//
if ( !pbBuffer || dwBufferSize < MIN_REPLICATION_INFO_SIZE )
{
return REPL_INTERNAL_ERROR;
}
//
// Extract the data for the session key under which the buffer is encrypted
//
hRes = RegenerateSessionKey( m_pMB, &hSessionKey );
if ( hSessionKey )
{
//
// Decrypt the buffer
//
hRes = DecryptBuffer( hSessionKey,
pbBuffer,
dwBufferSize,
&pbReplicationInfo,
&pbEnd );
if( FAILED(hRes) )
{
DeleteSessionKey( &hSessionKey );
hSessionKey = NULL;
}
}
if( !hSessionKey )
{
//
// Some of these functions return S_FALSE for some reason,
// preserve this behavior.
//
return (hRes != S_OK) ? hRes : E_FAIL;
}
//
// Deserialize away
//
pbPosition = pbReplicationInfo;
while ( pbPosition < pbEnd &&
!FAILED( hRes = DeserializeInstanceInfo( &pbPosition,
pbEnd,
&dwInstance ) ) )
{
DBGPRINTF((DBG_CONTEXT,
"Successfully deserialized information for instance %d\n",
dwInstance));
}
if ( FAILED( hRes ) )
{
DBGPRINTF((DBG_CONTEXT,
"Failed deserializing information for instance %d\n",
dwInstance));
}
//
// Cleanup
//
if ( pbReplicationInfo )
{
memset( pbReplicationInfo, 0, DIFF(pbEnd - pbReplicationInfo) );
delete [] pbReplicationInfo;
}
DeleteSessionKey( &hSessionKey );
//
// Q : when should we delete the MB info ? Only on successful DeSerialize ?
//
if ( SUCCEEDED( hRes ) )
{
DeleteMBSessionKeyInfo( m_pMB );
}
return hRes;
}//::DeSerialize
HRESULT
GetConfigurationInformation( DWORD dwInfoType,
HCRYPTKEY *phSessionKey,
IMSAdminBase *pMB,
unsigned char __RPC_FAR *pbBuffer,
DWORD dwBufferSize,
DWORD __RPC_FAR *pdwMDRequiredBufferSize )
/*++
Routine Description:
Used to retrieve replication information
Arguments:
dwInfoType - which kind of information to retrieve - signature or configuration
phSessionKey - pointer to session key to be used to encrypt replication info; not
used if dwInfoType == SIGNATURE
pMB - pointer to metabase object
pbBuffer - buffer to be filled in with information
dwBufferSize - size of pbBuffer
*pdwMDRequiredBufferSize - size of buffer required for data
Returns:
HRESULT indicating success/failure.
--*/
{
HRESULT hRes = S_OK;
METADATA_HANDLE hMDHandle;
DWORD dwObjectIndex = 0;
TCHAR achMDName[METADATA_MAX_NAME_LEN];
DWORD dwRepBufferSize = 0;
PBYTE pbReplicationInfo = NULL;
DWORD dwPosition = 0;
BYTE *pbHashBuffer = NULL;
DWORD cbHashSize = 0;
if ( !pMB )
{
return S_FALSE;
}
//
// Make sure we have a pointer to a session key if we need to use it
//
if ( dwInfoType != SIGNATURE && !phSessionKey )
{
return S_FALSE;
}
//
// Signature is MD5 hash of information, so we know the buffer has to be at least
// MD5_HASH_SIZE bytes in size
//
if ( dwInfoType == SIGNATURE )
{
if ( !pbBuffer || dwBufferSize < MD5_HASH_SIZE )
{
*pdwMDRequiredBufferSize = MD5_HASH_SIZE;
return RETURNCODETOHRESULT( ERROR_INSUFFICIENT_BUFFER );
}
}
hRes = pMB->OpenKey( METADATA_MASTER_ROOT_HANDLE,
MB_ROOT_PATH,
METADATA_PERMISSION_READ,
TIMEOUT_VALUE,
&hMDHandle );
if ( FAILED(hRes) )
{
DBGPRINTF((DBG_CONTEXT,
"Failed to open mb path"));
return hRes;
}
//
// Try to retrieve default information stored at /LM/W3SVC
//
//
// Bogus report of null pointer dereference of pbReplicationInfo
// Windows Bug - 117759
//
/* INTRINSA suppress=null_pointers */
if ( FAILED( hRes = GetInstanceReplicationInfo( dwInfoType,
L"",
pMB,
hMDHandle,
&pbReplicationInfo,
&dwRepBufferSize,
&dwPosition ) ) )
{
if ( pbReplicationInfo )
{
delete [] pbReplicationInfo;
}
pMB->CloseKey( hMDHandle );
return hRes;
}
//
// Iterate through all the subkeys of /LM/W3SVC
//
HRESULT hRes2 = S_OK;
while ( SUCCEEDED(hRes2 = pMB->EnumKeys( hMDHandle,
L"",
achMDName,
dwObjectIndex) ) )
{
//
// If the name of the subkey is a number, it's a server instance.
// Construct cert and CTL information
//
if ( IsNumber( achMDName ) )
{
if ( FAILED( hRes = GetInstanceReplicationInfo( dwInfoType,
achMDName,
pMB,
hMDHandle,
&pbReplicationInfo,
&dwRepBufferSize,
&dwPosition ) ) )
{
if ( pbReplicationInfo )
{
memset( pbReplicationInfo, 0, dwRepBufferSize );
delete [] pbReplicationInfo;
}
pMB->CloseKey( hMDHandle );
return hRes;
}
}
dwObjectIndex++;
}
//
// Error while iterating over keys
//
if ( hRes2 != RETURNCODETOHRESULT( ERROR_NO_MORE_ITEMS ) )
{
if ( pbReplicationInfo )
{
memset( pbReplicationInfo, 0, dwRepBufferSize );
delete [] pbReplicationInfo;
}
pMB->CloseKey( hMDHandle );
return hRes2;
}
if ( dwInfoType == SIGNATURE )
{
//
// We have a buffer containing all the information that needs to be converted
// into a signature by applying the MD5 hash algorithm to it
//
if ( !FAILED( hRes = GenerateHash( NULL,
CALG_MD5,
pbReplicationInfo,
dwPosition,
&pbHashBuffer,
&cbHashSize,
NULL ) ) )
{
DBG_ASSERT( cbHashSize == MD5_HASH_SIZE );
memcpy( pbBuffer, pbHashBuffer, cbHashSize );
*pdwMDRequiredBufferSize = cbHashSize;
}
}
else
{
//
// Because the cert information contains a private key, use the
// session key to encrypt the replication buffer
if ( !FAILED( hRes = EncryptBuffer( *phSessionKey,
&pbReplicationInfo,
&dwRepBufferSize,
&dwPosition ) ) )
{
//
// check that output buffer is big enough to hold the encrypted data
//
if ( dwBufferSize >= dwPosition )
{
memcpy( pbBuffer, pbReplicationInfo, dwPosition );
*pdwMDRequiredBufferSize = dwPosition;
}
else
{
*pdwMDRequiredBufferSize = dwPosition;
hRes = RETURNCODETOHRESULT( ERROR_INSUFFICIENT_BUFFER );
}
}
}
if ( pbReplicationInfo )
{
//
// zero it out, just to be paranoid
//
memset( pbReplicationInfo, 0, dwRepBufferSize );
delete [] pbReplicationInfo;
}
if ( pbHashBuffer )
{
//
// zero it out, just to be paranoid
//
memset( pbHashBuffer, 0, cbHashSize );
delete [] pbHashBuffer;
}
pMB->CloseKey( hMDHandle );
return hRes;
}
HRESULT GetInstanceReplicationInfo( DWORD dwInfoType,
LPCWSTR pszInstanceNum,
IN IMSAdminBase *pMB,
IN METADATA_HANDLE hHandle,
OUT BYTE **ppbReplicationBuffer,
IN OUT DWORD *pdwBufferSize,
IN OUT DWORD *pdwPosition )
/*++
Routine Description:
Used to retrieve a replication information for a particular server instance.
Format of information :
<MSB of instance #><LSB of instance #>:<instance info>|
Arguments:
dwInfoType - type of information - either signature or configuration
pszInstanceNum - string rep of instance number
pMB - metabase object
hHandle - handle to path open for reading
ppbReplicationBuffer - pointer to pointer to buffer that is filled in with info
pdwBufferSize - pointer to current buffer size, updated on success
pdwPosition - pointer to end of data in buffer, updated on success
Returns:
HRESULT indicating success/failure.
--*/
{
#define INSTANCE_HEADER_LEN 3 // <MSB> <LSB> ':'
#define INSTANCE_TRAILER_LEN 1 // '|'
HRESULT hRes = NOERROR;
DWORD dwInstance;
BOOL fHasCTL = FALSE;
BOOL fHasCert = FALSE;
if ( !pMB )
{
return S_FALSE;
}
//
// Special case : null instance string means instance zero ( = /LM/W3SVC )
//
if ( !wcscmp(L"", pszInstanceNum) )
{
dwInstance = 0;
}
else
{
dwInstance = _wtoi( pszInstanceNum );
}
fHasCert = MBPathHasCAPIInfo( pMB,
hHandle,
pszInstanceNum,
adwMetabaseCertProperties,
cNumCertMetabaseProperties );
fHasCTL = MBPathHasCAPIInfo( pMB,
hHandle,
pszInstanceNum,
adwMetabaseCTLProperties,
cNumCTLMetabaseProperties );
if ( fHasCTL || fHasCert )
{
//
// Copy instance information into the buffer
//
if ( ( ( *pdwBufferSize - *pdwPosition ) > INSTANCE_HEADER_LEN ) ||
ResizeBuffer( ppbReplicationBuffer, INSTANCE_HEADER_LEN , pdwBufferSize ) )
{
//
// Instance header
//
(*ppbReplicationBuffer)[(*pdwPosition)++] = MSB(dwInstance);
(*ppbReplicationBuffer)[(*pdwPosition)++] = LSB(dwInstance);
(*ppbReplicationBuffer)[(*pdwPosition)++] = COLON;
//
// Server certificate information
//
if ( fHasCert )
{
if ( FAILED( hRes = GetCertReplicationInfo( dwInfoType,
pMB,
hHandle,
pszInstanceNum,
ppbReplicationBuffer,
pdwBufferSize,
pdwPosition ) ) )
{
return hRes;
}
}
//
// CTL information
//
if ( fHasCTL )
{
if ( FAILED( hRes = GetCTLReplicationInfo( dwInfoType,
pMB,
hHandle,
pszInstanceNum,
ppbReplicationBuffer,
pdwBufferSize,
pdwPosition ) ) )
{
return hRes;
}
}
if ( ( ( *pdwBufferSize - *pdwPosition ) > INSTANCE_TRAILER_LEN ) ||
ResizeBuffer( ppbReplicationBuffer, INSTANCE_TRAILER_LEN, pdwBufferSize ) )
{
//
//Instance trailer
//
(*ppbReplicationBuffer)[(*pdwPosition)++] = INSTANCE_TRAILER_BYTE;
}
else
{
hRes = RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
}
}
else
{
hRes = RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
}
}
return (hRes);
}
HRESULT GetCertReplicationInfo( DWORD dwInfoType,
IN IMSAdminBase *pMB,
IN METADATA_HANDLE hHandle,
IN LPCWSTR pszPath,
OUT BYTE **ppbReplicationBuffer,
IN OUT DWORD *pdwBufferSize,
IN OUT DWORD *pdwPosition )
/*++
Routine Description:
Used to retrieve replication information for a certificate
Arguments:
dwInfoType - type of information to retrieve - signature or configuration
pMB - metabase object
hHandle - handle to path open for reading
pszPath - cert info path relative to hHandle
ppbReplicationBuffer - pointer to pointer to buffer that is filled in with info
pdwBufferSize - pointer to current buffer size, updated on success
pdwPosition - pointer to end of data in buffer, updated on success
Returns:
HRESULT indicating success/failure.
--*/
{
PCCERT_CONTEXT pcServerCert = NULL;
DWORD dwNumCerts = 0;
LIST_ENTRY CertChain;
BOOL fCompleteChain = FALSE;
OPEN_CERT_STORE_INFO *pStoreInfo = NULL;
HRESULT hRes = S_OK;
if ( !pMB )
{
return S_FALSE;
}
InitializeListHead( &CertChain );
//
// try to retrieve the server certificate and its complete cert chain
//
if ( !FAILED( hRes = ReadServerCert( pMB,
hHandle,
pszPath,
&pcServerCert,
&pStoreInfo ) ) &&
pcServerCert &&
IsReplicableCert( pcServerCert ) &&
!FAILED( hRes = ConstructCertChain( pcServerCert,
pStoreInfo->pszStoreName,
&CertChain,
&fCompleteChain ) ) )
{
if ( dwInfoType == SIGNATURE )
{
hRes = GetCertChainSignature( &CertChain,
ppbReplicationBuffer,
pdwBufferSize,
pdwPosition );
}
else
{
hRes = SerializeCertChain( &CertChain,
pStoreInfo,
ppbReplicationBuffer,
pdwBufferSize,
pdwPosition );
}
}
//
// Clean up
//
if ( pcServerCert )
{
CertFreeCertificateContext( pcServerCert );
}
FreeCertChain( &CertChain );
return hRes;
}
HRESULT GetCTLReplicationInfo( DWORD dwInfoType,
IN IMSAdminBase *pMB,
IN METADATA_HANDLE hHandle,
IN LPCWSTR pszPath,
OUT BYTE **ppbReplicationBuffer,
IN OUT DWORD *pdwBufferSize,
IN OUT DWORD *pdwPosition )
/*++
Routine Description:
Used to retrieve replication information for a CTL
Arguments:
dwInfoType - type of information to retrieve - signature or configuration
pMB - metabase object
hHandle - handle to path open for reading
pszPath - cert info path relative to hHandle
ppbReplicationBuffer - pointer to pointer to buffer that is filled in with info
pdwBufferSize - pointer to current buffer size, updated on success
pdwPosition - pointer to end of data in buffer, updated on success
Returns:
HRESULT indicating success/failure.
--*/
{
OPEN_CERT_STORE_INFO *pStoreInfo = NULL;
HRESULT hRes;
PCCTL_CONTEXT pcCTL = NULL;
LIST_ENTRY CtlCerts;
PCCERT_CONTEXT pcSignerCert = NULL;
InitializeListHead( &CtlCerts );
if ( S_OK == ( hRes = ReadServerCTL( pMB,
hHandle,
pszPath,
&pcCTL ) ) &&
!FAILED( hRes = BuildCTLDescription( pcCTL,
&CtlCerts,
&pcSignerCert ) ) )
{
if ( dwInfoType == SIGNATURE )
{
hRes = GetCTLSignature( pcCTL,
&CtlCerts,
pcSignerCert,
ppbReplicationBuffer,
pdwBufferSize,
pdwPosition );
}
else
{
hRes = SerializeCTL( pcCTL,
&CtlCerts,
pcSignerCert,
ppbReplicationBuffer,
pdwBufferSize,
pdwPosition );
}
}
//
// Cleanup
//
if ( pcCTL )
{
CertFreeCTLContext( pcCTL );
}
if ( pcSignerCert )
{
CertFreeCertificateContext( pcSignerCert );
}
FreeCertChain( &CtlCerts );
return (hRes);
}
HRESULT SerializeCertChain( IN LIST_ENTRY *pChain,
IN OPEN_CERT_STORE_INFO *pStoreInfo,
OUT PBYTE *ppbChainBuffer,
IN OUT DWORD *pdwBufferSize,
IN OUT DWORD *pdwPosition )
/*++
Routine Description:
Serializes the metabase information and certificate chain belonging to a server
certificate into a buffer. The first certificate in the chain is the actual server
certificate. It does this by copying the chain into a CAPI store and serializing
the store.
Format of buffer :
CERTS=<SHA1 hash of first cert in chain><MSB of length of private key blob><LSB of length of
private key blob><private key blob of first cert><key container info of private key>
<MSB of length of serialized store containg chain><LSB of length of serialized store
containing chain><serialized store>
The SHA1 hash is added so that it's possible to figure out which cert in the store
is the leaf cert when unserializing the buffer.
Arguments:
pChain - chain of CertChainEntry structures
pStoreInfo - structure containing cert-related metabase information
ppbChainBuffer - pointer to pointer to buffer that will hold the serialized store
on success
pdwBufferSize - pointer to present size of buffer, updated if buffer is resized
pdwPosition - pointer to where information is to be written to in buffer, updated
on success
Returns:
HRESULT indicating success/failure
--*/
{
HCERTSTORE hMemStore;
HRESULT hRes;
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
DWORD cbPrivateKey = 0;
DWORD cbSpaceLeft = 0;
DWORD cbSpaceRequired = 0;
DWORD dwProvSize = 0;
if ( !pChain )
{
return S_FALSE;
}
//
// The certificates are stuffed into an in-memory store, which is then serialized
//
if ( !( hMemStore = CertOpenStore( CERT_STORE_PROV_MEMORY,
0,
NULL,
0,
0 ) ) )
{
return RETURNCODETOHRESULT( GetLastError() );
}
//
// Go through the list and add each cert to the store
//
LIST_ENTRY *pLink;
CertChainEntry *pChainEntry;
DWORD dwSizeNeeded = 0;
DWORD cCerts = 0;
PCCERT_CONTEXT pcFirstCert = NULL;
for ( pLink = pChain->Flink;
pLink != pChain;
pLink = pLink->Flink )
{
pChainEntry = CONTAINING_RECORD( pLink, CertChainEntry, ListEntry );
if ( !CertAddCertificateContextToStore( hMemStore,
pChainEntry->pcCert,
CERT_STORE_ADD_REPLACE_EXISTING,
NULL ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndSerializeChain;
}
cCerts++;
if ( cCerts == 1 )
{
pcFirstCert = pChainEntry->pcCert;
}
}
if ( !pcFirstCert )
{
CertCloseStore( hMemStore,
0 );
return S_FALSE;
}
//
// Calculate space requirements
//
cbSpaceLeft = *pdwBufferSize - *pdwPosition;
cbSpaceRequired = CERT_HEADER_SIZE + SHA1_HASH_SIZE;
if ( cbSpaceRequired > cbSpaceLeft &&
!ResizeBuffer( ppbChainBuffer,
cbSpaceRequired,
pdwBufferSize ) )
{
CertCloseStore( hMemStore,
0 );
return RETURNCODETOHRESULT(ERROR_OUTOFMEMORY);
}
//
// Append cert header
//
memcpy( *ppbChainBuffer + *pdwPosition, CERT_HEADER, CERT_HEADER_SIZE );
(*pdwPosition) += CERT_HEADER_SIZE;
//
// Append the hash of the first cert
//
cbSpaceLeft = *pdwBufferSize - *pdwPosition;
if ( !CertGetCertificateContextProperty( pcFirstCert,
CERT_SHA1_HASH_PROP_ID,
(*ppbChainBuffer) + *pdwPosition,
&cbSpaceLeft ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndSerializeChain;
}
(*pdwPosition) += cbSpaceLeft;
//
// Serialize the private key for the cert
//
if ( FAILED( hRes = ExportAndSerializeServerPK( pcFirstCert,
ppbChainBuffer,
pdwBufferSize,
pdwPosition ) ) )
{
CertCloseStore( hMemStore,
0 );
return (hRes);
}
//
// Figure out how much space we need for the serialized store
//
CRYPT_DATA_BLOB StoreBlob;
StoreBlob.pbData = NULL;
StoreBlob.cbData = 0;
if ( !CertSaveStore( hMemStore,
0,
CERT_STORE_SAVE_AS_STORE,
CERT_STORE_SAVE_TO_MEMORY,
(VOID *) &StoreBlob,
0 ) &&
GetLastError() != ERROR_MORE_DATA )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndSerializeChain;
}
//
// Resize the buffer if necessary - space for bytes indicating length of
// serialized store, serialized store itself
//
cbSpaceRequired = 2 + StoreBlob.cbData;
cbSpaceLeft = *pdwBufferSize - *pdwPosition;
if ( cbSpaceRequired > cbSpaceLeft )
{
if ( !ResizeBuffer( ppbChainBuffer,
cbSpaceRequired,
pdwBufferSize ) )
{
hRes = RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
goto EndSerializeChain;
}
}
//
// Append the length of the serialized store
//
(*ppbChainBuffer)[(*pdwPosition)++] = MSB(StoreBlob.cbData);
(*ppbChainBuffer)[(*pdwPosition)++] = LSB(StoreBlob.cbData);
//
// Actually serialize the store
//
StoreBlob.cbData = *pdwBufferSize - *pdwPosition;
StoreBlob.pbData = *ppbChainBuffer + *pdwPosition;
if ( !CertSaveStore( hMemStore,
0,
CERT_STORE_SAVE_AS_STORE,
CERT_STORE_SAVE_TO_MEMORY,
(VOID *) &StoreBlob,
0 ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndSerializeChain;
}
(*pdwPosition) += StoreBlob.cbData;
EndSerializeChain:
CertCloseStore( hMemStore,
0 );
return (hRes);
}
HRESULT SerializeCTL( PCCTL_CONTEXT pcCTL,
LIST_ENTRY *pCTLCertChain,
PCCERT_CONTEXT pcSigner,
PBYTE *ppbReplicationBuffer,
DWORD *pdwBufferSize,
DWORD *pdwPosition )
/*++
Routine Description:
Serializes a CTL for replication. First, the CTL, the certs in the CTL and the CTL
signer are added to an in-memory CAPI store; the store is then serialized to memory.
Format of buffer :
CTL=<MSB of length of serialized store><LSB of length of serialized
store><serialized store>
Arguments:
pcCTL - CTL to serialize
pCTLCertChain - chain of certs in CTL
pcSigner - cert that signed the CTL
ppbSignature - pointer to pointer to buffer that will hold the signature on success
pdwBufferSize - pointer to present size of buffer, updated if buffer is resized
pdwPosition - pointer to where information is to be written to in buffer, updated
on success
Returns:
HRESULT indicating success/failure
--*/
{
HRESULT hRes = S_OK;
HCERTSTORE hMemStore = NULL;
//
// Create the memory store
//
if ( !(hMemStore = CertOpenStore( CERT_STORE_PROV_MEMORY,
0,
NULL,
0,
0 ) ) )
{
return RETURNCODETOHRESULT( GetLastError() );
}
//
// Add the CTL to the store
//
if ( !CertAddCTLContextToStore( hMemStore,
pcCTL,
CERT_STORE_ADD_ALWAYS,
NULL ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
CertCloseStore( hMemStore,
0 );
return hRes;
}
//
// Add the signer to the store
//
if ( !CertAddCertificateContextToStore( hMemStore,
pcSigner,
CERT_STORE_ADD_ALWAYS,
NULL ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
CertCloseStore( hMemStore,
0 );
return hRes;
}
//
// Add all the certs in the chain to the store
//
LIST_ENTRY *pLink;
CertChainEntry *pChainEntry;
PCCERT_CONTEXT pcFirstCert = NULL;
for ( pLink = pCTLCertChain->Flink;
pLink != pCTLCertChain;
pLink = pLink->Flink )
{
pChainEntry = CONTAINING_RECORD( pLink, CertChainEntry, ListEntry );
if ( !CertAddCertificateContextToStore( hMemStore,
pChainEntry->pcCert,
CERT_STORE_ADD_REPLACE_EXISTING,
NULL ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
CertCloseStore( hMemStore,
0 );
return ( hRes );
}
}
//
// Figure out the size of the serialized store
//
CRYPT_DATA_BLOB StoreBlob;
DWORD cbSpaceLeft = 0;
DWORD cbSpaceNeeded = 0;
StoreBlob.pbData = NULL;
StoreBlob.cbData = 0;
if ( !CertSaveStore( hMemStore,
0,
CERT_STORE_SAVE_AS_STORE,
CERT_STORE_SAVE_TO_MEMORY,
(VOID *) &StoreBlob,
0 ) &&
GetLastError() != ERROR_MORE_DATA )
{
CertCloseStore( hMemStore,
0 );
return RETURNCODETOHRESULT( GetLastError() );
}
//
// need space for the CTL header, the bytes indicating the size of the store
// and the store itself
//
cbSpaceNeeded = StoreBlob.cbData + CTL_HEADER_SIZE + 2;
cbSpaceLeft = *pdwBufferSize - *pdwPosition;
if ( ( cbSpaceLeft > cbSpaceNeeded ) ||
ResizeBuffer( ppbReplicationBuffer,
cbSpaceNeeded,
pdwBufferSize ) )
{
//
// CTL header
//
memcpy( *ppbReplicationBuffer + *pdwPosition, CTL_HEADER, CTL_HEADER_SIZE );
(*pdwPosition) += CTL_HEADER_SIZE;
//
// Bytes indicating length of serialized store
//
(*ppbReplicationBuffer)[(*pdwPosition)++] = MSB(StoreBlob.cbData);
(*ppbReplicationBuffer)[(*pdwPosition)++] = LSB(StoreBlob.cbData);
//
// Actually serialize the store
//
StoreBlob.cbData = *pdwBufferSize - *pdwPosition;
StoreBlob.pbData = *ppbReplicationBuffer + *pdwPosition;
if ( CertSaveStore( hMemStore,
0,
CERT_STORE_SAVE_AS_STORE,
CERT_STORE_SAVE_TO_MEMORY,
(VOID *) &StoreBlob,
0 ) )
{
(*pdwPosition) += StoreBlob.cbData;
}
else
{
DBGPRINTF((DBG_CONTEXT,
"Error 0x%x\n", GetLastError()));
hRes = RETURNCODETOHRESULT( GetLastError() );
}
}
else
{
hRes = RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
}
if ( hMemStore )
{
CertCloseStore( hMemStore,
0 );
}
return (hRes);
}
HRESULT GetCertChainSignature( IN LIST_ENTRY *pChain,
OUT PBYTE *ppbSignature,
IN OUT DWORD *pdwBufferSize,
IN OUT DWORD *pdwPosition )
/*++
Routine Description:
Constructs the signature for a chain of certificates. Signature for a chain is :
CERTS=<# of certs><hash of C1><hash of C2>...<hash of CN>
Arguments:
pChain - chain of CertChainEntry structures
ppbSignature - pointer to pointer to buffer that will hold the signature on success
pdwBufferSize - pointer to present size of buffer, updated if buffer is resized
pdwPosition - pointer to where information is to be written to in buffer, updated
on success
Returns:
HRESULT indicating success/failure
--*/
{
if ( !pChain )
{
return S_FALSE;
}
//
// Go through the list and figure out how much space is needed
//
LIST_ENTRY *pLink;
CertChainEntry *pChainEntry;
DWORD dwSizeNeeded = 0;
DWORD cCerts = 0;
for ( pLink = pChain->Flink;
pLink != pChain;
pLink = pLink->Flink )
{
pChainEntry = CONTAINING_RECORD( pLink, CertChainEntry, ListEntry );
dwSizeNeeded += SHA1_HASH_SIZE;
cCerts++;
}
dwSizeNeeded += 1; //extra byte to hold # of certs
dwSizeNeeded += CERT_HEADER_SIZE;
//
// Resize the buffer if necessary
//
if ( (*pdwBufferSize - *pdwPosition) < dwSizeNeeded )
{
if ( !ResizeBuffer( ppbSignature,
dwSizeNeeded,
pdwBufferSize ) )
{
return RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
}
}
//
// First few bytes are cert header
//
memcpy(*ppbSignature + *pdwPosition, CERT_HEADER, CERT_HEADER_SIZE );
(*pdwPosition) += CERT_HEADER_SIZE;
//
// Next byte is # of cert hashes
//
(*ppbSignature)[(*pdwPosition)++] = (BYTE) cCerts;
//
// Walk the chain and stuff in all the cert hashes
//
DWORD dwSize = SHA1_HASH_SIZE;
DWORD cPresentCert = 0;
for ( pLink = pChain->Flink;
pLink != pChain;
pLink = pLink->Flink )
{
pChainEntry = CONTAINING_RECORD( pLink, CertChainEntry, ListEntry );
if ( !CertGetCertificateContextProperty( pChainEntry->pcCert,
CERT_SHA1_HASH_PROP_ID,
(*ppbSignature) + *pdwPosition,
&dwSize ) )
{
return RETURNCODETOHRESULT( GetLastError() );
}
cPresentCert++;
(*pdwPosition) += SHA1_HASH_SIZE;
}
return S_OK;
}
HRESULT GetCTLSignature( PCCTL_CONTEXT pcCTL,
LIST_ENTRY *pCTLCertsChain,
PCCERT_CONTEXT pcSignerCert,
PBYTE *ppbReplicationBuffer,
DWORD *pdwBufferSize,
DWORD *pdwPosition )
/*++
Routine Description:
Constructs the signature for a CTL. Signature for CTL is
CTL=<hash of signer><hash of CTL>
Arguments:
pcCTL - CTL whose signature is to be generated
pCTLCertsChain - chain of certificates in CTL
pcSignerCert - certificate that signed the CTL
ppbReplicationBuffer - pointer to pointer to buffer that will hold the signature on success
pdwBufferSize - pointer to present size of buffer, updated if buffer is resized
pdwPosition - pointer to where information is to be written to in buffer, updated
on success
Returns:
HRESULT indicating success/failure
--*/
{
DWORD cbSpaceLeft = *pdwBufferSize - *pdwPosition;
DWORD cbSpaceNeeded = 2*SHA1_HASH_SIZE + CTL_HEADER_SIZE;
HRESULT hRes = S_OK;
if ( cbSpaceLeft > cbSpaceNeeded ||
ResizeBuffer( ppbReplicationBuffer,
cbSpaceNeeded,
pdwBufferSize ) )
{
//
//CTL header
//
memcpy( *ppbReplicationBuffer + *pdwPosition, CTL_HEADER, CTL_HEADER_SIZE );
(*pdwPosition) += CTL_HEADER_SIZE;
//
// Hash of signer
//
cbSpaceLeft = *pdwBufferSize - *pdwPosition;
if ( !CertGetCertificateContextProperty( pcSignerCert,
CERT_SHA1_HASH_PROP_ID,
*ppbReplicationBuffer + *pdwPosition,
&cbSpaceLeft ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
}
else
{
DBG_ASSERT( cbSpaceLeft == SHA1_HASH_SIZE );
(*pdwPosition) += cbSpaceLeft;
//
// Hash of CTL itself
//
cbSpaceLeft = *pdwBufferSize - *pdwPosition;
if ( !CertGetCTLContextProperty( pcCTL,
CERT_SHA1_HASH_PROP_ID,
*ppbReplicationBuffer + *pdwPosition,
&cbSpaceLeft ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
}
else
{
DBG_ASSERT( cbSpaceLeft == SHA1_HASH_SIZE );
(*pdwPosition) += cbSpaceLeft;
}
}
}
else
{
hRes = RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
}
return (hRes);
}
HRESULT DeserializeInstanceInfo( IN OUT BYTE **ppbPosition,
IN BYTE *pbEnd,
OUT DWORD *pdwInstance )
/*++
Routine Description:
Deserializes the SSL information for a single server instance
Arguments:
ppbPosition - pointer to pointer to buffer to deserialize; updated after info for
instance has been deserialized
pbEnd - pointer to end of serialized information, to avoid running off the end
pdwInstance - pointer to instance for which info is being deserialized; updated on
success
Returns:
HRESULT indicating success/failure.
--*/
{
HRESULT hRes = S_OK;
BYTE *pbPresent = NULL;
BOOL fHasCertInfo = FALSE;
BOOL fHasCTLInfo = FALSE;
//
// Sanity checks on arguments
//
if ( !ppbPosition || !*ppbPosition || !pbEnd ||
pbEnd < *ppbPosition + MIN_REPLICATION_INFO_SIZE )
{
return REPL_INTERNAL_ERROR;
}
pbPresent = *ppbPosition;
BYTE bMSB = *(pbPresent++);
BYTE bLSB = *(pbPresent++);
*pdwInstance = (DWORD) ( (bMSB << 8) + bLSB );
DBGPRINTF((DBG_CONTEXT,
"Deserializing info for instance %d\n",
*pdwInstance));
//
// Next byte should be colon
//
if ( !(*pbPresent == COLON) )
{
return REPL_INTERNAL_ERROR;
}
pbPresent++;
//
// Next bit should either be CERT_HEADER or CTL_HEADER
//
if ( ( pbEnd > pbPresent + CERT_HEADER_SIZE ) &&
!memcmp( pbPresent, CERT_HEADER, CERT_HEADER_SIZE ) )
{
fHasCertInfo = TRUE;
//
//deserialize cert
//
if ( FAILED( hRes = DeserializeServerCert( &pbPresent,
pbEnd ) ) )
{
return hRes;
}
}
if ( ( pbEnd > pbPresent + CTL_HEADER_SIZE ) &&
!memcmp( pbPresent, CTL_HEADER, CTL_HEADER_SIZE ) )
{
fHasCTLInfo = TRUE;
//
//deserialize CTL
//
if ( FAILED( hRes = DeserializeServerCTL( &pbPresent,
pbEnd ) ) )
{
return hRes;
}
}
if ( !fHasCertInfo && !fHasCTLInfo )
{
return REPL_INTERNAL_ERROR;
}
//
// Check for trailer byte
//
if ( !( *pbPresent == INSTANCE_TRAILER_BYTE ) )
{
return REPL_INTERNAL_ERROR;
}
pbPresent++;
//
// Update pointer to indicate where next deserialization has to
// start
//
*ppbPosition = pbPresent;
return hRes;
}
//
// This should be more focused, but the current prefix docs are quite vague
// about some of the suppression options.
//
// The error being suppressed is a memory leak on the internal error path
// see windows bug 48010 for details. Suppressing now, because this is
// a broken an rarely used code path. And the error case is a virtual
// impossibility.
//
/* #pragma INTRINSA suppress=all */
HRESULT DeserializeServerCert( IN OUT BYTE **ppbBuffer,
IN BYTE *pbEnd )
/*++
Routine Description:
Deserializes a server certificate [and its attendant chain] and puts the certs in
the appropriate stores
Format of buffer :
"CERTS="<SHA1 hash of first cert in chain><MSB of length of private key blob>
<LSB of length of private key blob><private key info of first cert>
<MSB of length of serialized store containing chain><LSB of length of serialized store
containing chain><serialized store>
Arguments:
ppbBuffer - pointer to pointer to serialized info, updated on success
pbEnd - pointer to end of buffer, to avoid overruns
Returns:
HRESULT indicating success/failure
--*/
{
HRESULT hRes = S_OK;
BYTE *pbPresent = *ppbBuffer;
BYTE rgbHash[SHA1_HASH_SIZE];
DWORD cbPrivateKey = 0;
DWORD cbStore = 0;
BYTE bMSB = 0;
BYTE bLSB = 0;
HCERTSTORE hMemStore = NULL;
CRYPT_KEY_PROV_INFO CKPI;
pbPresent += CERT_HEADER_SIZE;
if ( pbEnd <= pbPresent + SHA1_HASH_SIZE )
{
return REPL_INTERNAL_ERROR;
}
//
// get the hash of the first cert in the chain [server cert]
//
memcpy( rgbHash, pbPresent, SHA1_HASH_SIZE );
pbPresent += SHA1_HASH_SIZE;
//
// eXtract the private key
//
HCRYPTKEY hPrivateKey = NULL;
if ( FAILED( hRes = DeserializeAndImportServerPK( &pbPresent,
pbEnd,
&hPrivateKey,
&CKPI ) ) )
{
return hRes;
}
//
// figure out length of serialized store
//
if ( pbEnd <= pbPresent + 2 ) //two byte header indicating length of serialized store
{
return REPL_INTERNAL_ERROR;
}
bMSB = *(pbPresent++);
bLSB = *(pbPresent++);
cbStore = (DWORD) ( (bMSB << 8 ) + bLSB );
if ( cbStore <= 0 || pbEnd <= pbPresent + cbStore )
{
return REPL_INTERNAL_ERROR;
}
//
// Unserialize the store containing the certs in the chain
//
CRYPT_DATA_BLOB cdbSerializedStore;
cdbSerializedStore.cbData = cbStore;
cdbSerializedStore.pbData = pbPresent;
if ( !( hMemStore = CertOpenStore( CERT_STORE_PROV_SERIALIZED,
0,
NULL,
0,
(PVOID) &cdbSerializedStore ) ) )
{
return RETURNCODETOHRESULT( GetLastError() );
}
pbPresent += cbStore;
//
// Deal with certs in the store
//
if ( FAILED( hRes = DistributeCerts( hMemStore,
rgbHash,
&CKPI ) ) )
{
CertCloseStore( hMemStore,
0 );
return hRes;
}
//
// Update
//
*ppbBuffer = pbPresent;
//
// cleanup
//
if ( hPrivateKey )
{
CryptDestroyKey( hPrivateKey );
}
if ( hMemStore )
{
CertCloseStore( hMemStore,
0 );
}
if ( CKPI.cProvParam && CKPI.rgProvParam )
{
delete [] CKPI.rgProvParam;
}
return hRes;
}
HRESULT DeserializeServerCTL( IN OUT BYTE **ppbBuffer,
IN BYTE *pbEnd )
/*++
Routine Description:
Deserializes a CTL, the certs in the CTL and the CTL signer and places them into the
appropriate stores.
CTL goes into the TRUST store. Signer goes into ROOT store if self-signed
and MY store if not. Certs in CTL go into CA store.
NOTE : currently, the signer will always be the IIS server cert, which is replicated
separately, so we won't bother doing anything with the signer cert
Format of buffer :
CTL=<MSB of length of serialized store><LSB of length of serialized
store><serialized store>
Arguments:
ppbBuffer - pointer to pointer to serialized info, updated on success
pbEnd - pointer to end of buffer, to avoid overruns
Returns:
HRESULT indicating success/failure
--*/
{
HRESULT hRes = S_OK;
BYTE *pbPresent = *ppbBuffer;
DWORD cbStore = 0;
BYTE bMSB = 0;
BYTE bLSB = 0;
HCERTSTORE hMemStore = NULL;
HCERTSTORE hTrustStore = NULL;
HCERTSTORE hCAStore = NULL;
PCCTL_CONTEXT pCTL = NULL;
PCCTL_CONTEXT pPrevCTL = NULL;
DWORD dwNumCTLs = 0;
PCCERT_CONTEXT pCert = NULL;
PCCERT_CONTEXT pPrevCert = NULL;
pbPresent += CTL_HEADER_SIZE;
if ( pbEnd <= pbPresent + 2 ) //bytes indicating length of serialized store
{
return REPL_INTERNAL_ERROR;
}
//
// figure out length of serialized store
//
bMSB = *(pbPresent++);
bLSB = *(pbPresent++);
cbStore = (DWORD) ( (bMSB << 8 ) + bLSB );
if ( cbStore <= 0 || pbEnd <= pbPresent + cbStore )
{
return REPL_INTERNAL_ERROR;
}
//
// Unserialize the store containing the CTL, certs in the CTL and signer
//
CRYPT_DATA_BLOB cdbSerializedStore;
cdbSerializedStore.cbData = cbStore;
cdbSerializedStore.pbData = pbPresent;
if ( !( hMemStore = CertOpenStore( CERT_STORE_PROV_SERIALIZED,
0,
NULL,
0,
(PVOID) &cdbSerializedStore ) ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto end_deserialize_ctl;
}
pbPresent += cbStore;
if ( !( hTrustStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
TRUST_STORE_NAME ) ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto end_deserialize_ctl;
}
if ( !( hCAStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
CA_STORE_NAME ) ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto end_deserialize_ctl;
}
//
// Add all the CTLs in the deserialized store to the TRUST store
//
while ( ( pCTL = CertEnumCTLsInStore( hMemStore,
pPrevCTL ) ) )
{
if ( !CertAddCTLContextToStore( hTrustStore,
pCTL,
CERT_STORE_ADD_REPLACE_EXISTING,
NULL ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto end_deserialize_ctl;
}
pPrevCTL = pCTL;
dwNumCTLs++;
}
DBG_ASSERT( dwNumCTLs == 1 );
//
// Add all the certs in the CTL to the CA store
//
while ( pCert = CertEnumCertificatesInStore( hMemStore,
pPrevCert ) )
{
if ( !CertAddCertificateContextToStore( hCAStore,
pCert,
CERT_STORE_ADD_NEW,
NULL ) &&
GetLastError() != CRYPT_E_EXISTS )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto end_deserialize_ctl;
}
pPrevCert = pCert;
}
//
// NOTE : in future may want to deal with signer cert as well
//
//
// Update
//
*ppbBuffer = pbPresent;
end_deserialize_ctl:
//
// cleanup
//
if ( hMemStore )
{
CertCloseStore( hMemStore,
0 );
}
if ( hTrustStore )
{
CertCloseStore( hTrustStore,
0 );
}
if ( hCAStore )
{
CertCloseStore( hCAStore,
0 );
}
return (hRes);
}
HRESULT ConstructCertChain( PCCERT_CONTEXT pcLeafCert,
LPWSTR pszLeafCertStore,
LIST_ENTRY *pCertChain,
PBOOL pfCompleteChain )
/*++
Routine Description:
Constructs the complete cert chain for the leaf cert passed in
Arguments:
pcLeafCert - cert for which chain is to be constructed
pszLeafCertStore - name of store from which pcLeafCert came
pCertChain - pointer to linked list containing cert chain. The first cert in the
list is pcLeafCert.
pfCompleteChain - set to TRUE if we constructed a full cert chain ie the constructed chain
ends with a self-signed cert
Returns:
HRESULT indicating success/failure
Note: 2/18/98 - CAPI2 is supposed to have an API Real Soon Now that will construct a cert chain;
until then, we'll roll our own.
--*/
{
DBG_ASSERT( pcLeafCert );
DBG_ASSERT( pCertChain );
HCERTSTORE hMyStore = NULL;
HCERTSTORE hCAStore = NULL;
HCERTSTORE hRootStore = NULL;
HRESULT hr = NOERROR;
CertChainEntry * pLink = NULL;
PCCERT_CONTEXT pcIssuer = NULL;
PCCERT_CONTEXT pcPresentLeaf = pcLeafCert;
DWORD dwFlags = 0;
DWORD dwStoresTried = 0;
HCERTSTORE hPresentStore = pcPresentLeaf->hCertStore;
BOOL fSystemStore = FALSE;
LPWSTR pszPresentStore = pszLeafCertStore;
*pfCompleteChain = FALSE;
//
// Open all the stores we'll search for issuers - MY, CA and ROOT
//
hMyStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
MY_STORE_NAME );
if ( !hMyStore )
{
hr = RETURNCODETOHRESULT( GetLastError() );
goto cleanup;
}
hCAStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
CA_STORE_NAME );
if ( !hCAStore )
{
hr = RETURNCODETOHRESULT( GetLastError() );
goto cleanup;
}
hRootStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
ROOT_STORE_NAME );
if ( !hRootStore )
{
hr = RETURNCODETOHRESULT( GetLastError() );
goto cleanup;
}
//
// First link in the chain is the leaf cert
//
pLink = new CertChainEntry;
if( pLink == NULL )
{
hr = RETURNCODETOHRESULT( GetLastError() );
goto cleanup;
}
pLink->pcCert = CertDuplicateCertificateContext( pcLeafCert );
pLink->pszStoreName = mystrdup( pszLeafCertStore );
InsertHeadList( pCertChain, &pLink->ListEntry );
//
// To build the chain, look for issuers in 4 stores : the store the cert came from,
// and the "MY", "CA" and "ROOT" stores, cycling through the stores as necessary
//
*pfCompleteChain = FALSE;
DBG_ASSERT( SUCCEEDED(hr) );
while ( 1 )
{
//
// Bail when we get to the top of a chain
//
if ( IsSelfSignedCert( pcPresentLeaf ) )
{
*pfCompleteChain = TRUE;
break;
}
pcIssuer = CertGetIssuerCertificateFromStore( hPresentStore,
pcPresentLeaf,
NULL,
&dwFlags );
//
// Got an issuer in this store
//
if ( pcIssuer )
{
//
// Add it to the list
//
pLink = new CertChainEntry;
if( pLink == NULL )
{
hr = RETURNCODETOHRESULT( GetLastError() );
goto cleanup;
}
pLink->pcCert = CertDuplicateCertificateContext( pcIssuer );
pLink->pszStoreName = (fSystemStore ? pszPresentStore :
mystrdup( pszPresentStore ) );
pLink->fDynName = !fSystemStore;
//
// Need to insert at the -end- of the list to keep ordering, where
// first cert is the actual server cert
//
InsertTailList( pCertChain, &pLink->ListEntry );
//
// Set up for next round
//
dwStoresTried = 0;
pcPresentLeaf = pLink->pcCert;
}
//
// No issuer in this store, switch to next store to look in
//
else
{
dwStoresTried++;
if ( dwStoresTried == 4 ) //we've tried all the stores, time to bail
{
break;
}
if ( hPresentStore == hMyStore )
{
hPresentStore = hCAStore;
pszPresentStore = CA_STORE_NAME;
fSystemStore = TRUE;
}
else if ( hPresentStore == hCAStore )
{
hPresentStore = hRootStore;
pszPresentStore = ROOT_STORE_NAME;
fSystemStore = TRUE;
}
else if ( hPresentStore == hRootStore )
{
hPresentStore = pcPresentLeaf->hCertStore;
pszPresentStore = pszLeafCertStore;
fSystemStore = FALSE;
}
else
{
hPresentStore = hMyStore;
pszPresentStore = MY_STORE_NAME;
fSystemStore = TRUE;
}
}
} //while ( 1 )
DBG_ASSERT( SUCCEEDED(hr) );
cleanup:
//
// Cleanup
//
if( hMyStore != NULL )
{
CertCloseStore( hMyStore, 0 );
}
if( hCAStore != NULL )
{
CertCloseStore( hCAStore, 0 );
}
if( hRootStore != NULL )
{
CertCloseStore( hRootStore, 0 );
}
return hr;
}
HRESULT BuildCTLDescription( IN PCCTL_CONTEXT pcCTL,
OUT LIST_ENTRY *pCTLCerts,
OUT PCCERT_CONTEXT *ppcSigner )
/*++
Routine Description:
Builds a full description of a CTL - all the certs in the CTL as well as the CTL
signer
Arguments:
pcCTL - CTL whose description is to be built
pCTLCerts - linked list that will have the certs in the CTL inserted into it
ppcSigner - pointer to pointer to cert that signed the CTL
Returns:
HRESULT indicating success/failure
--*/
{
//
// First try to get all the certs in the CTL
//
PCTL_INFO pCtlInfo = pcCTL->pCtlInfo;
HRESULT hRes;
if ( !pCtlInfo->cCTLEntry )
{
DBGPRINTF((DBG_CONTEXT,
"Herm. Nothing in CTL ? \n"));
return S_FALSE;
}
else
{
HCERTSTORE ahStores[3];
memset( ahStores, 0, sizeof(ahStores) );
DWORD dwNumStores = sizeof(ahStores)/sizeof(HCERTSTORE);
PCCERT_CONTEXT pcCert = NULL;
BOOL fFoundCert = FALSE;
DWORD dwIndex = 0;
DWORD dwStoreIndex = 0;
//
// Try the MY, CA and ROOT stores for local machine
//
if ( !(ahStores[0] = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
MY_STORE_NAME ) ) )
{
DBGPRINTF((DBG_CONTEXT,
"Failed to open MY store : 0x%x\n", GetLastError()));
hRes = RETURNCODETOHRESULT( GetLastError() );
goto ctl_cleanup;
}
if ( !(ahStores[1] = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
CA_STORE_NAME ) ) )
{
DBGPRINTF((DBG_CONTEXT,
"Failed to open CA store : 0x%x\n", GetLastError()));
hRes = RETURNCODETOHRESULT( GetLastError() );
goto ctl_cleanup;
}
if ( !(ahStores[2] = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
ROOT_STORE_NAME ) ) )
{
DBGPRINTF((DBG_CONTEXT,
"Failed to open ROOT store : 0x%x\n", GetLastError()));
hRes = RETURNCODETOHRESULT( GetLastError() );
goto ctl_cleanup;
}
//
// Iterate through the certs in the CTL. For each cert, try to find it
// by SHA1 hash in the supplied stores.
//
for ( dwIndex = 0; dwIndex < pCtlInfo->cCTLEntry; dwIndex++ )
{
fFoundCert = FALSE;
for ( dwStoreIndex = 0; dwStoreIndex < dwNumStores; dwStoreIndex++ )
{
if ( pcCert = CertFindCertificateInStore( ahStores[dwStoreIndex],
X509_ASN_ENCODING,
0,
CERT_FIND_SHA1_HASH,
(VOID *) &(pCtlInfo->rgCTLEntry[dwIndex].SubjectIdentifier),
NULL ) )
{
fFoundCert = TRUE;
break;
}
}
//
// Couldn't find one of the certs in the CTL, error
//
if ( !fFoundCert )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto ctl_cleanup;
}
else
{
CertChainEntry *pEntry = new CertChainEntry;
if( pEntry == NULL )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto ctl_cleanup;
}
pEntry->pcCert = pcCert;
pEntry->fDynName = FALSE;
switch( dwStoreIndex )
{
case 0:
pEntry->pszStoreName = MY_STORE_NAME;
break;
case 1:
pEntry->pszStoreName = CA_STORE_NAME;
break;
case 2:
pEntry->pszStoreName = ROOT_STORE_NAME;
break;
default:
//
// should never get here
DBG_ASSERT( TRUE );
break;
}
InsertTailList( pCTLCerts, &pEntry->ListEntry );
}
}
//
// Find the CTL signer
//
if ( !CryptMsgGetAndVerifySigner( (HCRYPTMSG) pcCTL->hCryptMsg,
dwNumStores,
ahStores,
CMSG_TRUSTED_SIGNER_FLAG | CMSG_SIGNER_ONLY_FLAG,
ppcSigner,
NULL ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
}
ctl_cleanup:
for ( dwStoreIndex = 0; dwStoreIndex < dwNumStores; dwStoreIndex++ )
{
if ( ahStores[dwStoreIndex] )
{
CertCloseStore( ahStores[dwStoreIndex],
0 );
}
}
} //else
return (hRes);
}
VOID FreeCertChain( IN OUT LIST_ENTRY *pChain )
/*++
Routine Description:
Free resources associated with a cert chain
Arguments:
pChain - chain to be cleaned up
Returns:
Nothing
--*/
{
if ( !pChain )
{
return;
}
CertChainEntry *pChainEntry;
while ( !IsListEmpty( pChain ) )
{
pChainEntry = CONTAINING_RECORD ( pChain->Flink,
CertChainEntry,
ListEntry );
RemoveEntryList( &(pChainEntry->ListEntry) );
if ( pChainEntry->pcCert )
{
CertFreeCertificateContext( pChainEntry->pcCert );
}
if ( pChainEntry->pszStoreName && pChainEntry->fDynName )
{
delete [] pChainEntry->pszStoreName;
}
}
}
BOOL IsSelfSignedCert( IN PCCERT_CONTEXT pCertContext )
/*++
Routine Description:
Determines whether a cert is self-signed ie the top of a hierarchy
Arguments:
pCertContext - cert to be checked
Returns:
TRUE if cert is self-signed, FALSE otherwise
--*/
{
//
// Compare subject and issuer.
//
if(pCertContext->pCertInfo->Subject.cbData == pCertContext->pCertInfo->Issuer.cbData)
{
if(memcmp(pCertContext->pCertInfo->Subject.pbData,
pCertContext->pCertInfo->Issuer.pbData,
pCertContext->pCertInfo->Issuer.cbData) == 0)
{
return TRUE;
}
}
return FALSE;
}
BOOL IsReplicableCert( IN PCCERT_CONTEXT pCert )
/*++
Routine Description:
Checks whether a cert can be replicated or not. Eg Fortezza certs can't be replicated
because they use a machine-specific hardware token
Arguments:
pCert - cert to be checked
Returns:
TRUE if cert can be replicated, FALSE if not
--*/
{
if ( IsFortezzaCert( pCert ) )
{
return FALSE;
}
return TRUE;
}
BOOL IsFortezzaCert( IN PCCERT_CONTEXT pCert )
/*++
Routine Description:
Checks whether a cert is a Fortezza certificate.
Arguments:
pCert - cert to be checked
Returns:
TRUE if it is, FALSE if not
--*/
{
PSTR pszOid = NULL;
// Q - is there a better way to do this?
pszOid = pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId;
if(pszOid)
{
if(strcmp(pszOid, szOID_INFOSEC_mosaicUpdatedSig) == 0 ||
strcmp(pszOid, szOID_INFOSEC_mosaicKMandUpdSig) == 0)
{
return TRUE;
}
}
return FALSE;
}
HRESULT ReadServerCTL( IN IMSAdminBase *pMB,
IN METADATA_HANDLE hHandle,
IN LPCWSTR pszPath,
OUT PCCTL_CONTEXT *ppcCTL )
/*++
Routine Description:
Tries to construct a certificate based on information in metabase
Arguments:
pMB - pointer to MB object
hHandle - handle opened for reading
pszPath - path under which info is stored, relative to hHandle
ppcCTL - pointer to pointer to CTL context, updated if CTL was found
Returns:
HRESULT indicating success/failure
--*/
{
DBG_ASSERT( pMB );
METADATA_RECORD mdr;
HRESULT hRes;
LPWSTR pwszListIdentifier = NULL;
DWORD dwIdentifierSize = 0;
OPEN_CERT_STORE_INFO *pCertStoreInfo = NULL;
HCERTSTORE hStore = NULL;
//
// Read CTL list identifier out of metabase
//
MD_SET_DATA_RECORD( &mdr, MD_SSL_CTL_IDENTIFIER,
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER, BINARY_METADATA, NULL,
0 );
if ( FAILED( hRes = RetrieveBlobFromMetabase( pMB,
hHandle,
pszPath,
&mdr,
0 ) ) )
{
return hRes;
}
else
{
pwszListIdentifier = (LPWSTR) mdr.pbMDData;
dwIdentifierSize = mdr.dwMDDataLen;
#if DBG
CHAR achListId[255];
if ( WideCharToMultiByte( CP_ACP,
0,
pwszListIdentifier,
-1,
achListId,
254,
NULL,
NULL ) )
{
DBGPRINTF((DBG_CONTEXT,
"List id : %s\n", achListId));
}
#endif
}
//
// Read cert store info out of MB, and try to reconstruct CTL context
//
if ( (pCertStoreInfo = ReadCertStoreInfoFromMB( pMB,
hHandle,
pszPath,
TRUE ) ) )
{
if ( !( hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
pCertStoreInfo->pszStoreName ) ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
}
else
{
CTL_FIND_USAGE_PARA CtlFindUsagePara;
memset(&CtlFindUsagePara, 0, sizeof(CtlFindUsagePara));
CtlFindUsagePara.cbSize = sizeof(CtlFindUsagePara);
CtlFindUsagePara.ListIdentifier.cbData = dwIdentifierSize;
CtlFindUsagePara.ListIdentifier.pbData = (PBYTE) pwszListIdentifier;
//
// Try to find CTL in specified store
//
*ppcCTL = CertFindCTLInStore( hStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CTL_FIND_USAGE,
(LPVOID) &CtlFindUsagePara,
NULL );
if ( !*ppcCTL )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
}
}
}
else
{
hRes = S_FALSE;
}
if ( hStore )
{
CertCloseStore( hStore,
0 );
}
if ( pCertStoreInfo )
{
DeallocateCertStoreInfo( pCertStoreInfo );
}
if ( pwszListIdentifier )
{
delete [] pwszListIdentifier;
}
return hRes;
}
HRESULT ReadServerCert( IN IMSAdminBase *pMB,
IN METADATA_HANDLE hHandle,
IN LPCWSTR pszPath,
OUT PCCERT_CONTEXT *ppcCertContext,
OUT OPEN_CERT_STORE_INFO **ppStoreInfo )
/*++
Routine Description:
Tries to construct a certificate based on information in metabase
Arguments:
pMB - pointer to MB object
hHandle - handle opened for reading
pszPath - path under which info is stored, relative to hHandle
ppcCertContext - pointer to pointer to cert context, updated if cert was found
ppStoreInfo - pointer to pointer to cert store information stored in metabase; updated if
successful
Returns:
HRESULT indicating success/failure
--*/
{
DBG_ASSERT( pMB );
METADATA_RECORD mdr;
PBYTE pbCertHash = NULL;
DWORD dwHashSize = 0;
HRESULT hRes;
//
// Get the hash of the cert
//
MD_SET_DATA_RECORD( &mdr, MD_SSL_CERT_HASH, METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER, BINARY_METADATA, NULL, 0 );
if ( FAILED( hRes = RetrieveBlobFromMetabase( pMB,
hHandle,
pszPath,
&mdr,
SHA1_HASH_SIZE ) ) )
{
return hRes;
}
else
{
DBG_ASSERT( mdr.dwMDDataLen == SHA1_HASH_SIZE );
pbCertHash = mdr.pbMDData;
dwHashSize = mdr.dwMDDataLen;
}
//
// Get all the info necessary to open the store in which the cert is
//
*ppStoreInfo = ReadCertStoreInfoFromMB( pMB,
hHandle,
pszPath,
FALSE );
if ( !*ppStoreInfo )
{
delete [] pbCertHash;
return S_FALSE;
}
//
// Got all the info, reconstruct the certificate
//
#if 0
//
// Open the appropriate store
//
if ( !CryptAcquireContext( &m_hCryptProv,
m_pszContainer,
m_pszProvider,
m_dwProvType,
m_dwFlags ) )
{
goto EndReadServerCert;
}
if ( !(m_hCertStore = CertOpenSystemStore( m_hCryptProv,
m_pszStoreName )))
{
m_dwStatus = CERT_ERR_CAPI;
goto EndRetrieveCertContext;
}
#else
if ( !((*ppStoreInfo)->hCertStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
(*ppStoreInfo)->pszStoreName ) ) )
{
DeallocateCertStoreInfo( *ppStoreInfo );
delete [] pbCertHash;
return S_FALSE;
}
#endif
//
// Try to find the cert in the store
//
CRYPT_HASH_BLOB HashBlob;
HashBlob.cbData = dwHashSize;
HashBlob.pbData = pbCertHash;
*ppcCertContext = CertFindCertificateInStore( (*ppStoreInfo)->hCertStore,
X509_ASN_ENCODING,
0,
CERT_FIND_SHA1_HASH,
(VOID *) &HashBlob,
NULL );
if ( !*ppcCertContext )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
}
else
{
hRes = S_OK;
}
//
// Clean up
//
delete [] pbCertHash;
return hRes;
}
BOOL ResizeBuffer( IN OUT BYTE **ppbBuffer,
IN DWORD dwMinResize,
IN OUT DWORD *pdwPresentSize )
/*++
Routine Description:
Used to resize a buffer
Arguments:
ppbBuffer - pointer to pointer to buffer to be resized, updated on success
dwMinResize - minimum amount to resize by
pdwPresentSize - pointer to buffer size, updated on success
Returns:
TRUE if resize succeeds, FALSE if it fails
--*/
{
DWORD dwActualResize = 0;
DWORD dwDoubleSize = 2* (*pdwPresentSize);
DWORD dwExtendedSize = *pdwPresentSize + dwMinResize;
BYTE *pbNewBuffer = NULL;
//
// Amt of memory to resize to is maximum of
// present size + dwMinResize, 2 * present size, INITIAL_BUFFER_SIZE
//
dwActualResize = ( INITIAL_BUFFER_SIZE > dwDoubleSize ?
( INITIAL_BUFFER_SIZE > dwExtendedSize ?
INITIAL_BUFFER_SIZE : dwExtendedSize ) :
( dwExtendedSize > dwDoubleSize ? dwExtendedSize : dwDoubleSize ) );
//
// totally new buffer
//
if ( !*ppbBuffer )
{
*ppbBuffer = new BYTE[ dwActualResize ];
if ( !*ppbBuffer )
{
SetLastError( ERROR_OUTOFMEMORY );
return FALSE;
}
}
else // resize of existing buffer
{
pbNewBuffer = new BYTE[ dwActualResize ];
if ( !pbNewBuffer )
{
SetLastError( ERROR_OUTOFMEMORY );
return FALSE;
}
//
// Copy the old buffer
//
memcpy( pbNewBuffer, *ppbBuffer, *pdwPresentSize );
//
// Zero out the old buffer and delete it
//
memset( *ppbBuffer, 0, *pdwPresentSize );
delete [] *ppbBuffer;
*ppbBuffer = pbNewBuffer;
}
*pdwPresentSize = dwActualResize;
return TRUE;
}
HRESULT RetrieveBlobFromMetabase( IN IMSAdminBase *pMB,
IN METADATA_HANDLE hHandle,
IN LPCWSTR pszPath,
IN OUT PMETADATA_RECORD pMDR,
IN DWORD dwSizeHint OPTIONAL)
/*++
Routine Description:
Tries to retrieve a value of variable length from the metabase
Arguments:
pMB - pointer to MB object
hHandle - handle open for reading
pszPath - path relative to hHandle
pMDR - pointer to metadata record to be used when reading the value. The pbMDData member
will be updated on success
dwSizeHint - if caller has idea of how big value might be, can set this to number of
bytes to try first retrieval call with
Returns:
HRESULT indicating whether value was read successfully
--*/
{
HRESULT hRes = S_OK;
DWORD dwRequiredSize = 0;
//
// If caller has a hint about how big the buffer might need to be, let's use it
//
if ( dwSizeHint )
{
pMDR->pbMDData = new UCHAR[dwSizeHint];
if ( !(pMDR->pbMDData) )
{
return RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
}
}
pMDR->dwMDDataLen = (dwSizeHint ? dwSizeHint : 0);
hRes = pMB->GetData( hHandle,
pszPath,
pMDR,
&dwRequiredSize );
if ( FAILED(hRes) )
{
//
// If buffer wasn't big enough, let's try again ...
//
if ( HRESULTTOWIN32(hRes) == ERROR_INSUFFICIENT_BUFFER )
{
//
// We were brought up well, so we'll clean stuff up
//
if ( dwSizeHint )
{
delete [] pMDR->pbMDData;
}
pMDR->pbMDData = new UCHAR[dwRequiredSize];
if ( !(pMDR->pbMDData) )
{
return RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
}
pMDR->dwMDDataLen = dwRequiredSize;
hRes = pMB->GetData( hHandle,
pszPath,
pMDR,
&dwRequiredSize );
if ( FAILED(hRes) )
{
//ah, sod it, can't do anymore
delete [] pMDR->pbMDData;
return hRes;
}
}
}
if ( FAILED(hRes) )
{
DBGPRINTF((DBG_CONTEXT,
"RetrieveBlobFromMB failed, 0x%x\n", HRESULTTOWIN32( hRes )));
}
return (hRes);
} //RetrieveBlobFromMetabase
OPEN_CERT_STORE_INFO* ReadCertStoreInfoFromMB( IN IMSAdminBase *pMB,
IN METADATA_HANDLE hHandle,
IN LPCWSTR pszPath,
IN BOOL fCTL )
/*++
Routine Description:
Read all the information necessary to open a CAPI store out of the metabase
Arguments:
pMDObject - pointer to metabase object
hHandle - handle opened for reading
pszPath - path relative to hHandle
fCTL - bool indicating whether info is to be used to reconstruct a CTL or a cert
Returns:
Pointer to filled out OPEN_CERT_STORE_INFO structure on success, NULL on failure.
Note that only some of the OPEN_CERT_STORE_INFO fields are -required-; currently,
only the store name is required.
--*/
{
DBG_ASSERT( pMB );
DBG_ASSERT( pszPath );
BOOL fSuccess = FALSE;
OPEN_CERT_STORE_INFO *pCertStoreInfo = NULL;
pCertStoreInfo = AllocateCertStoreInfo();
if ( !pCertStoreInfo )
{
return NULL;
}
DWORD dwReqDataLen = 0;
METADATA_RECORD mdr;
//
//Try to retrieve container
//
MD_SET_DATA_RECORD(&mdr,
(fCTL ? MD_SSL_CTL_CONTAINER : MD_SSL_CERT_CONTAINER),
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER, STRING_METADATA,
NULL,
0);
if ( !FAILED( RetrieveBlobFromMetabase(pMB,
hHandle,
pszPath,
&mdr) ) )
{
//
// Metabase will return empty string if NULL string is stored
//
if ( !wcscmp( (LPWSTR) mdr.pbMDData, TEXT("")) )
{
delete [] mdr.pbMDData;
pCertStoreInfo->pszContainer = NULL;
}
else
{
pCertStoreInfo->pszContainer = (LPWSTR) mdr.pbMDData;
}
}
//
//Try to retrieve cert provider
//
MD_SET_DATA_RECORD(&mdr,
(fCTL ? MD_SSL_CTL_PROVIDER : MD_SSL_CERT_PROVIDER),
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER, STRING_METADATA,
NULL,
0);
if ( !FAILED( RetrieveBlobFromMetabase(pMB,
hHandle,
pszPath,
&mdr) ) )
{
//
// Metabase will return empty string if NULL string is stored
//
if ( !wcscmp( (LPWSTR) mdr.pbMDData, TEXT("")) )
{
delete [] mdr.pbMDData;
pCertStoreInfo->pszProvider = NULL;
}
else
{
pCertStoreInfo->pszProvider = (LPWSTR) mdr.pbMDData;
}
}
//
//Try to retrieve provider type
//
MD_SET_DATA_RECORD(&mdr,
(fCTL ? MD_SSL_CTL_PROVIDER_TYPE : MD_SSL_CERT_PROVIDER_TYPE),
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER, DWORD_METADATA,
NULL,
0);
if ( !FAILED( RetrieveBlobFromMetabase( pMB,
hHandle,
pszPath,
&mdr ) ) )
{
pCertStoreInfo->dwProvType = * ( (DWORD * ) mdr.pbMDData );
}
//
//Retrieve open flags
//
MD_SET_DATA_RECORD( &mdr,
(fCTL ? MD_SSL_CTL_OPEN_FLAGS : MD_SSL_CERT_OPEN_FLAGS),
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER, DWORD_METADATA,
NULL,
0 );
if ( !FAILED( RetrieveBlobFromMetabase( pMB,
hHandle,
pszPath,
&mdr ) ) )
{
pCertStoreInfo->dwFlags = * ( (DWORD * ) mdr.pbMDData );
}
//
//Try to retrieve store name
//
MD_SET_DATA_RECORD(&mdr,
(fCTL ? MD_SSL_CTL_STORE_NAME : MD_SSL_CERT_STORE_NAME),
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER, STRING_METADATA,
NULL,
0);
if ( FAILED( RetrieveBlobFromMetabase( pMB,
hHandle,
pszPath,
&mdr ) ) )
{
goto EndReadStoreInfo;
}
else
{
//
// Metabase will return empty string if NULL string is stored, but
// empty name is -NOT- valid !
//
if ( !wcscmp( (LPWSTR) mdr.pbMDData, TEXT("")) )
{
delete [] mdr.pbMDData;
goto EndReadStoreInfo;
}
else
{
pCertStoreInfo->pszStoreName = (LPWSTR) mdr.pbMDData;
}
}
//
// Everything succeeded
//
fSuccess = TRUE;
EndReadStoreInfo:
if ( !fSuccess )
{
DeallocateCertStoreInfo( pCertStoreInfo );
pCertStoreInfo = NULL;
}
return ( pCertStoreInfo );
}
HRESULT RegenerateSessionKey( IMSAdminBase *pMB,
OUT HCRYPTKEY *phKey )
/*++
Routine Description:
Generate a session key to encrypt/decrypt replication information
Arguments:
pMB - pointer to metabase object
phKey - pointer to key, filled in on success
Returns:
HRESULT indicating success/failure
--*/
{
#ifdef NO_ENCRYPTION
return S_OK;
#else
HCRYPTPROV hProv = NULL;
HRESULT hRes = S_OK;
BYTE *pbHash = NULL;
DWORD cbHashSize = 0;
HCRYPTHASH hHash = NULL;
BOOL fOk = TRUE;
METADATA_HANDLE hMDHandle;
ALG_ID aiAlg = 0;
BYTE *pbSeed = NULL;
DWORD cbSeed = 0;
DWORD cbRandomSize = 0;
BYTE *pbRandom = NULL;
BYTE bMajor = 0;
BYTE bMinor = 0;
DWORD i = 0;
//
// Retrieve the key seed
//
if ( FAILED( hRes = ReadSessionKeySeed( pMB,
&pbSeed,
&cbSeed ) ) ||
!cbSeed ||
cbSeed < SEED_HEADER_SIZE + 1 ) //total length must be greater than header
{
hRes = S_FALSE;
goto regenerate_exit;
}
bMajor = pbSeed[i++];
bMinor = pbSeed[i++];
memcpy( &aiAlg, pbSeed + i, sizeof(ALG_ID) );
i += sizeof(ALG_ID);
cbRandomSize = (DWORD) pbSeed[i++];
pbRandom = pbSeed + i;
DBG_ASSERT( i == SEED_HEADER_SIZE );
//
// Make sure encoded size of random seed is same as the random seed we're actually going
// to use, and non-zero
if ( cbRandomSize <= 0 ||
cbRandomSize != (cbSeed - SEED_HEADER_SIZE) )
{
hRes = S_FALSE;
goto regenerate_exit;
}
//
// clear out any old session keys
//
CryptAcquireContext( &hProv,
REPLICATION_SESSION_KEY_CONTAINER,
NULL,
PROV_RSA_FULL,
CRYPT_DELETEKEYSET | CRYPT_MACHINE_KEYSET );
if ( fOk = CryptAcquireContext( &hProv,
REPLICATION_SESSION_KEY_CONTAINER,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET ) )
{
if ( SUCCEEDED( hRes = GenerateHash( &hProv,
aiAlg,
pbRandom,
cbRandomSize,
&pbHash,
&cbHashSize,
&hHash ) ) )
{
fOk = CryptDeriveKey( hProv,
CALG_RC4,
hHash,
0,
phKey );
}
}
if ( SUCCEEDED(hRes) && !fOk )
{
hRes = (GetLastError() != ERROR_SUCCESS) ?
RETURNCODETOHRESULT( GetLastError() ) : E_FAIL;
}
#if 0
//
// Releasing the hProv will invalidate the key
//
if ( hProv )
{
CryptReleaseContext( hProv,
0 );
}
#endif
regenerate_exit:
if ( hHash )
{
CryptDestroyHash( hHash );
}
//
// Clean out seed used to derive key
//
if ( pbSeed )
{
memset( pbSeed, 0, cbSeed );
delete [] pbSeed;
}
//
// Hash buffer contains hash of seed used to derive key, so zero it out as well
//
if ( pbHash )
{
memset( pbHash, 0, cbHashSize );
delete [] pbHash;
}
return hRes;
#endif // NO_ENCRYPTION
}
VOID DeleteSessionKey( IN HCRYPTKEY *phKey )
{
#ifndef NO_ENCRYPTION
DeleteKey( phKey,
REPLICATION_SESSION_KEY_CONTAINER );
#endif
}
VOID DeleteKey( IN HCRYPTKEY *phKey,
IN LPCWSTR pszContainer )
/*++
Routine Description:
Delete a key
Arguments:
phKey - pointer to key to delete
pszContainer - name of key container
Returns:
Nothing
--*/
{
//
// delete the key container
//
HCRYPTPROV hProv;
CryptAcquireContext( &hProv,
pszContainer,
NULL,
PROV_RSA_FULL,
CRYPT_DELETE_KEYSET | CRYPT_MACHINE_KEYSET );
//
// delete the actual key
//
if ( phKey )
{
CryptDestroyKey( *phKey );
*phKey = NULL;
}
}
HRESULT ReadSessionKeySeed( IN IMSAdminBase *pMB,
OUT BYTE **ppbSeed,
OUT DWORD *pcbSeed )
/*++
Routine Description:
Reads session key seed information out of metabase
Arguments:
pMB - pointer to metabase interface to use
ppbSeed - pointer to pointer to buffer updated with session key seed
pcbSeed - pointer to DWORD holding size of seed
Returns:
HRESULT status
--*/
{
HRESULT hRes = S_OK;
METADATA_HANDLE hMDHandle;
*pcbSeed = 0;
if ( FAILED( hRes = pMB->OpenKey( METADATA_MASTER_ROOT_HANDLE,
MB_ROOT_PATH,
METADATA_PERMISSION_READ,
TIMEOUT_VALUE,
&hMDHandle ) ) )
{
return hRes;
}
//
// Figure out the size of the seed
//
METADATA_RECORD mdr;
MD_SET_DATA_RECORD( &mdr,
MD_SSL_REPLICATION_INFO,
METADATA_SECURE,
IIS_MD_UT_SERVER,
BINARY_METADATA,
0,
NULL );
hRes = pMB->GetData( hMDHandle,
MB_REPLICATION_PATH,
&mdr,
pcbSeed );
if ( HRESULTTOWIN32( hRes ) != ERROR_INSUFFICIENT_BUFFER )
{
return hRes;
}
//
// We know the size, allocate some memory
//
DBG_ASSERT( *pcbSeed );
*ppbSeed = NULL;
*ppbSeed = new BYTE[*pcbSeed];
if ( !(*ppbSeed) )
{
return RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
}
//
// Get the random seed out of the metabase
//
MD_SET_DATA_RECORD( &mdr,
MD_SSL_REPLICATION_INFO,
METADATA_SECURE,
IIS_MD_UT_SERVER,
BINARY_METADATA,
*pcbSeed,
*ppbSeed );
hRes = pMB->GetData( hMDHandle,
MB_REPLICATION_PATH,
&mdr,
pcbSeed );
//
// cleanup
//
pMB->CloseKey( hMDHandle );
return hRes;
}
HRESULT DeleteMBSessionKeyInfo ( IN IMSAdminBase *pMB )
/*++
Routine Description:
Delete the metabase information used to derive a session key
Arguments:
None
Returns:
HRESULT status
--*/
{
#ifdef NO_ENCRYPTION
return S_OK;
#else
METADATA_HANDLE hMDHandle;
HRESULT hRes = S_OK;
if ( FAILED( hRes = pMB->OpenKey( METADATA_MASTER_ROOT_HANDLE,
MB_ROOT_PATH,
METADATA_PERMISSION_WRITE,
TIMEOUT_VALUE,
&hMDHandle ) ) )
{
return hRes;
}
hRes = pMB->DeleteKey( hMDHandle,
MB_REPLICATION_PATH );
pMB->CloseKey( hMDHandle );
return hRes;
#endif //NO_ENCRYPTION
}
HRESULT ExportAndSerializeServerPK( IN PCCERT_CONTEXT pcCert,
IN OUT BYTE **ppbChainBuffer,
IN OUT DWORD *pdwBufferSize,
IN OUT DWORD *pdwPosition )
/*++
Routine Description:
Export and serialize a private key for a server certificate. Format of serialized buffer :
<MSB of private key length><LSB of private key length><private key><key container info>
Arguments:
pcCert - certificate whose private key is to be exported
ppbChainBuffer - pointer to pointer to buffer that is to hold serialized key
pdwBufferSize - size of buffer pointed to by *ppbChainBuffer
pdwPosition - position in buffer
Returns:
HRESULT indicating success/failure
--*/
{
HCRYPTKEY hKey = NULL;
HCRYPTPROV hProv = NULL;
DWORD cbSpaceLeft = 0;
DWORD cbPrivateKey = 0;
DWORD cbPKInfo = 0;
DWORD cbSpaceRequired = 0;
HRESULT hRes = S_OK;
//
// Figure out how much space we need for the private key blob; bit of a lengthy
// procedure - first get the arguments needed to get a handle to the key container,
// get a handle to the key container, get a handle to the exchange key in the key
// container and finally figure out the key size.
//
CRYPT_KEY_PROV_INFO *pKeyProvInfo = NULL;
DWORD dwProvSize = 0;
BYTE *pbPKInfo = NULL;
//
// Determine how much space we need
//
if ( !CertGetCertificateContextProperty( pcCert,
CERT_KEY_PROV_INFO_PROP_ID,
NULL,
&dwProvSize ) &&
GetLastError() != ERROR_MORE_DATA )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndSerializePK;
}
pbPKInfo = new BYTE[dwProvSize];
if ( !pbPKInfo )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndSerializePK;
}
pKeyProvInfo = ( CRYPT_KEY_PROV_INFO *) pbPKInfo;
//
// Get the private key info
//
if ( !CertGetCertificateContextProperty( pcCert,
CERT_KEY_PROV_INFO_PROP_ID,
pKeyProvInfo,
&dwProvSize ) )
{
DBGPRINTF((DBG_CONTEXT,
"Error getting key info : 0x%x\n", GetLastError() ) );
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndSerializePK;
}
if ( !CryptAcquireContext( &hProv,
pKeyProvInfo->pwszContainerName,
pKeyProvInfo->pwszProvName,
pKeyProvInfo->dwProvType,
pKeyProvInfo->dwFlags ) )
{
DBGPRINTF((DBG_CONTEXT,
"Error getting handle to CSP : 0x%x\n", GetLastError() ) );
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndSerializePK;
}
if ( !CryptGetUserKey( hProv,
pKeyProvInfo->dwKeySpec,
&hKey ) )
{
DBGPRINTF((DBG_CONTEXT,
"Error getting user key : 0x%x\n", GetLastError() ) );
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndSerializePK;
}
if ( !CryptExportKey( hKey,
NULL, //BUGBUGBUG - this needs to be a real session key !
PRIVATEKEYBLOB,
0,
NULL,
&cbPrivateKey ) &&
GetLastError() != ERROR_MORE_DATA )
{
DBGPRINTF((DBG_CONTEXT,
"Error exporting key : 0x%x\n", GetLastError() ) );
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndSerializePK;
}
cbPKInfo = SizeOfCKPI( pKeyProvInfo );
cbSpaceRequired = cbPrivateKey + 2 + //private key + bytes indicating length of key
cbPKInfo + 2 ; //private key container info + bytes indicating length
cbSpaceLeft = *pdwBufferSize - *pdwPosition;
if ( cbSpaceLeft < cbSpaceRequired && !ResizeBuffer( ppbChainBuffer,
cbSpaceRequired,
pdwPosition ) )
{
hRes = RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
goto EndSerializePK;
}
//
// Append the length of the private key blob
//
(*ppbChainBuffer)[(*pdwPosition)++] = MSB(cbPrivateKey);
(*ppbChainBuffer)[(*pdwPosition)++] = LSB(cbPrivateKey);
//
// Add the private key
//
cbSpaceLeft = *pdwBufferSize - *pdwPosition;
if ( !CryptExportKey( hKey,
NULL, //BUGBUG - this should be a real session key !
PRIVATEKEYBLOB,
0,
*ppbChainBuffer + *pdwPosition,
&cbSpaceLeft ) )
{
DBGPRINTF((DBG_CONTEXT,
"Error exporting key : 0x%x\n", GetLastError() ) );
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndSerializePK;
}
(*pdwPosition) += cbSpaceLeft;
//
// Length of serialized CRYPT_KEY_PROV_INFO structure
//
(*ppbChainBuffer)[(*pdwPosition)++] = MSB(cbPKInfo);
(*ppbChainBuffer)[(*pdwPosition)++] = LSB(cbPKInfo);
//
// Add CRYPT_KEY_PROV_INFO structure
//
SerializeCKPI( *ppbChainBuffer + *pdwPosition,
pKeyProvInfo,
pdwPosition );
EndSerializePK:
//
//Cleanup
//
if ( hKey )
{
CryptDestroyKey( hKey );
}
if ( hProv )
{
CryptReleaseContext( hProv,
0 );
}
if ( pbPKInfo )
{
delete [] pbPKInfo;
}
return hRes;
}
HRESULT DeserializeAndImportServerPK( IN OUT BYTE **ppbBuffer,
IN BYTE *pbEnd,
OUT HCRYPTKEY *phKey,
OUT CRYPT_KEY_PROV_INFO *pCKPI )
/*++
Routine Description:
Deserialize and import the private key for a server certificate.
Arguments:
ppbBuffer - pointer to pointer to buffer containing serialized private key; updated on success
pbEnd - pointer to end of buffer, to avoid overruns
phKey - pointer to key handle, updated on success
pCKPI - pointer to CRYPT_KEY_PROV_INFO structure, updated on success
Returns:
HRESULT indicating success/failure
--*/
{
HRESULT hRes = S_OK;
BYTE *pbPresent = *ppbBuffer;
BYTE bMSB = 0;
BYTE bLSB = 0;
DWORD cbPrivateKey = 0;
DWORD cbPKInfo = 0;
BYTE *pbPrivateKey = NULL;
HCRYPTPROV hProv = NULL;
//
// figure out length of private key
//
if ( pbEnd <= pbPresent + 2 ) //two byte header indicating length of private key
{
return REPL_INTERNAL_ERROR;
}
bMSB = *(pbPresent++);
bLSB = *(pbPresent++);
cbPrivateKey = (DWORD) ( ( bMSB << 8 ) + bLSB );
if ( cbPrivateKey <= 0 || pbEnd <= pbPresent + cbPrivateKey )
{
return REPL_INTERNAL_ERROR;
}
pbPrivateKey = pbPresent;
pbPresent += cbPrivateKey;
//
// extract private key container info
//
bMSB = *(pbPresent++);
bLSB = *(pbPresent++);
cbPKInfo = (DWORD) ( ( bMSB << 8 ) + bLSB );
if ( cbPKInfo <= 0 || pbEnd <= pbPresent + cbPKInfo )
{
return REPL_INTERNAL_ERROR;
}
if ( !DeserializeCKPI( &pbPresent,
pCKPI ) )
{
return RETURNCODETOHRESULT( GetLastError() );
}
//
// clean out old key container
//
CryptAcquireContext( &hProv,
pCKPI->pwszContainerName,
pCKPI->pwszProvName,
pCKPI->dwProvType,
pCKPI->dwFlags | CRYPT_DELETEKEYSET );
//
// import private key
//
if ( CryptAcquireContext( &hProv,
pCKPI->pwszContainerName,
pCKPI->pwszProvName,
pCKPI->dwProvType,
pCKPI->dwFlags | CRYPT_NEWKEYSET ) )
{
if ( !CryptImportKey( hProv,
pbPrivateKey,
cbPrivateKey,
NULL,
CRYPT_EXPORTABLE,
phKey ) )
{
DBGPRINTF((DBG_CONTEXT,
"Error : 0x%x\n", GetLastError()));
hRes = RETURNCODETOHRESULT( GetLastError() );
}
else
{
//
// Set the ACL on the private key
//
if ( !SetLocalSystemSecurityOnKeyContainer( hProv ) )
{
DBGPRINTF((DBG_CONTEXT,
"Error : 0x%x\n", GetLastError()));
hRes = RETURNCODETOHRESULT( GetLastError() );
}
}
}
else
{
DBGPRINTF((DBG_CONTEXT,
"Error : 0x%x\n", GetLastError()));
hRes = RETURNCODETOHRESULT( GetLastError() );
}
//
// Update
//
*ppbBuffer = pbPresent;
return hRes;
}
HRESULT EncryptBuffer( IN HCRYPTKEY hKey,
IN OUT PBYTE *ppbReplicationInfo,
IN OUT DWORD *pdwBufferSize,
IN OUT DWORD *pdwPosition )
/*++
Routine Description:
Encrypt the replication information
Arguments:
hKey - handle to key used to encrypt the data
ppbReplicationInfo - pointer to pointer to buffer that holds data to be encrypted
pdwBufferSize - pointer to present buffer size
pdwPosition - pointer to position in buffer
Returns:
HRESULT indicating success/failure
--*/
{
#ifdef NO_ENCRYPTION
return S_OK;
#else
DWORD cbOut = *pdwPosition;
HRESULT hRes = S_OK;
//
// Figure out the size of the buffer we need
//
CryptEncrypt( hKey,
NULL,
TRUE,
0,
NULL,
&cbOut,
0 );
if ( *pdwBufferSize > cbOut ||
ResizeBuffer( ppbReplicationInfo, cbOut, pdwBufferSize ) )
{
cbOut = *pdwPosition;
if ( !CryptEncrypt( hKey,
NULL,
TRUE,
0,
*ppbReplicationInfo, //encryption is in-place
&cbOut,
*pdwPosition ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
}
else
{
*pdwPosition = cbOut;
}
}
else
{
hRes = RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
}
return hRes;
#endif // NO_ENCRYPTION
}
HRESULT DecryptBuffer( IN HCRYPTKEY hKey,
IN PBYTE pbEncrypted,
IN DWORD cbEncrypted,
OUT BYTE **ppbDecrypted,
OUT BYTE **ppbEndDecrypted )
/*++
Routine Description:
Encrypt the replication information
Arguments:
hKey - handle to key to be used to decrypt the data
pbEncrypted - buffer holding encrypted data
cbEncrypted - size of buffer pointed to by pbEncrypted
ppbDecrypted - pointer to pointer to buffer to hold decrypted data. Allocated by
this function, caller has to clean it up by calling delete []
ppbEndDecrypted - pointer to pointer to end of ppbDecrypted
Returns:
HRESULT indicating success/failure
--*/
{
#ifdef NO_ENCRYPTION
*ppbDecrypted = new BYTE[cbEncrypted];
memcpy( *ppbDecrypted, pbEncrypted, cbEncrypted );
*ppbEndDecrypted = (*ppbDecrypted) + cbEncrypted;
return S_OK;
#else
HRESULT hRes = S_OK;
*ppbDecrypted = NULL;
DWORD cbDecrypted = 0;
//
// Decryption is done in-place, so make a copy of the encrypted buffer
//
*ppbDecrypted = new BYTE[cbEncrypted];
if ( !*ppbDecrypted )
{
return ( RETURNCODETOHRESULT( ERROR_OUTOFMEMORY ) );
}
memcpy( *ppbDecrypted, pbEncrypted, cbEncrypted );
cbDecrypted = cbEncrypted;
//
// Decrypt the data
//
if ( !CryptDecrypt( hKey,
NULL,
TRUE,
0,
*ppbDecrypted,
&cbDecrypted ) )
{
memset( *ppbDecrypted, 0, cbEncrypted );
delete [] *ppbDecrypted;
return (GetLastError() != ERROR_SUCCESS) ?
RETURNCODETOHRESULT( GetLastError() ) : E_FAIL;
}
else
{
*ppbEndDecrypted = *ppbDecrypted + cbDecrypted;
}
return hRes;
#endif //NO_ENCRYPTION
}
OPEN_CERT_STORE_INFO* AllocateCertStoreInfo()
/*++
Routine Description:
Allocate and initialize the structure used to hold info about cert stores
Arguments:
None
Returns:
Allocated and initialized structure that should be cleaned up with a call to
DeallocateCertStoreInfo()
--*/
{
OPEN_CERT_STORE_INFO *pStoreInfo = new OPEN_CERT_STORE_INFO;
if ( pStoreInfo )
{
memset(pStoreInfo, 0, sizeof(OPEN_CERT_STORE_INFO));
}
return pStoreInfo;
}
VOID DeallocateCertStoreInfo( OPEN_CERT_STORE_INFO *pInfo )
/*++
Routine Description:
Clean up the structure used to track information about a cert store
Arguments:
pInfo - pointer to OPEN_CERT_STORE_INFO structure to be cleaned up
Returns:
Nothing
--*/
{
if ( !pInfo )
{
return ;
}
if ( pInfo->pszContainer )
{
delete [] pInfo->pszContainer;
pInfo->pszContainer = NULL;
}
if ( pInfo->pszProvider )
{
delete [] pInfo->pszProvider;
pInfo->pszProvider = NULL;
}
if ( pInfo->pszStoreName )
{
delete [] pInfo->pszStoreName;
pInfo->pszStoreName = NULL;
}
if ( pInfo->hCertStore )
{
CertCloseStore( pInfo->hCertStore,
0 );
pInfo->hCertStore = NULL;
}
delete pInfo;
}
LPWSTR mystrdup(LPWSTR pszIn IN)
/*++
Routine Description:
Makes a copy of string passed in using new[]
Arguments:
pszIn - string to be copied
Returns:
Pointer to copy of string on success, NULL on failure
--*/
{
DBG_ASSERT( pszIn );
LPWSTR pszOut = new WCHAR[ (wcslen(pszIn) + 1 ) * sizeof(WCHAR)];
if ( !pszOut )
{
return NULL;
}
memcpy( pszOut, pszIn, ( wcslen(pszIn) + 1 ) * sizeof( WCHAR ) );
return ( pszOut );
} //mystrdup
BOOL MBPathHasCAPIInfo( IN IMSAdminBase *pMB,
METADATA_HANDLE hHandle,
IN LPCWSTR pszPath,
IN DWORD *adwProperties,
IN DWORD cProperties )
/*++
Routine Description:
Checks whether the given MB path has info associated with it necessary to
reconstruct a particular CAPI structure eg certificate context, CTL context
Arguments:
pMB - pointer to metabase object
hHandle - handle open for reading
pszPath - path to where CAPI info would be stored, relative to hHandle
adwProperties - array of metabase properties that must exist and be readable for the
given CAPI object
cProperties - number of elements in pdwProperties array [ = 2 * # of properties]
Returns:
TRUE if cert info exists, FALSE if not
--*/
{
DBG_ASSERT( pMB );
DBG_ASSERT( pszPath );
BOOL fAllData = TRUE;
HRESULT hRes;
//
// Iterate through each property, trying to retrieve it with a buffer size of zero;
// If retrieving a property fails for any reason other than a buffer that's too
// small, assume the property doesn't exist
//
for (DWORD i = 0; i < cProperties/2; i++)
{
DWORD dwSize = 0;
METADATA_RECORD mdr;
MD_SET_DATA_RECORD( &mdr,
adwProperties[2 * i],
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER,
adwProperties[2*i + 1],
NULL,
0 );
hRes = pMB->GetData( hHandle,
pszPath,
&mdr,
&dwSize );
if ( HRESULTTOWIN32(hRes) != ERROR_INSUFFICIENT_BUFFER )
{
fAllData = FALSE;
break;
}
}
return fAllData;
}
HRESULT GenerateHash( IN OPTIONAL HCRYPTPROV *phProv,
IN ALG_ID aiAlg,
IN PBYTE pbData,
IN DWORD cbData,
OUT BYTE **ppbHashBuffer,
OUT DWORD *pcbHash,
OUT OPTIONAL HCRYPTHASH *phHash )
/*++
Routine Description:
Generates a hash of input data
Arguments:
phProv - pointer to provider to be used; can be set to NULL
aiAlg - hash algorithm to use
pbData - buffer of data to be hashed
cbData - size of data to be hashed
ppbHashBuffer - pointer to pointer to buffer that receives the actual hash
pcbHash - pointer to size of *ppbHashBuffer
phHash - pointer to CRYPTHASH object, updated on sucess, ignored if NULL
Returns:
HRESULT indicating success/failure
--*/
{
HCRYPTPROV hProv = NULL;
HCRYPTHASH hHash = NULL;
HRESULT hRes = S_OK;
DWORD cbSize = 0;
if ( !phProv )
{
//
// Get a handle to the CSP that will create the
// hash
if ( !CryptAcquireContext( &hProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndCreateHash;
}
}
else
{
hProv = *phProv;
}
//
// Get a handle to a hash object
//
if ( !CryptCreateHash( hProv,
aiAlg,
0,
0,
&hHash ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndCreateHash;
}
//
// Hash the data
//
if ( !CryptHashData( hHash,
pbData,
cbData,
0 ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndCreateHash;
}
//
// Retrieve the size of the hash
//
cbSize = sizeof(DWORD);
if ( !CryptGetHashParam( hHash,
HP_HASHSIZE,
(BYTE *) pcbHash,
&cbSize,
0 ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndCreateHash;
}
DBG_ASSERT( *pcbHash > 0 );
*ppbHashBuffer = new BYTE[*pcbHash];
if ( !*ppbHashBuffer )
{
hRes = RETURNCODETOHRESULT( ERROR_OUTOFMEMORY );
goto EndCreateHash;
}
//
// Retrieve the actual hashed data
//
if ( !CryptGetHashParam( hHash,
HP_HASHVAL,
*ppbHashBuffer,
pcbHash,
0 ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndCreateHash;
}
if ( phHash )
{
*phHash = hHash;
}
EndCreateHash:
//
//Cleanup
//
if ( hRes != S_OK )
{
if ( *ppbHashBuffer )
{
delete [] *ppbHashBuffer;
}
}
if ( !phHash && hHash )
{
CryptDestroyHash( hHash );
}
if ( !phProv && hProv )
{
CryptReleaseContext( hProv,
0 );
}
return hRes;
}
HRESULT GenerateMD5Hash( IN OPTIONAL HCRYPTPROV *phProv,
IN PBYTE pbData,
IN DWORD cbData,
OUT PBYTE pbHashBuffer,
IN OUT DWORD *pdwHashBufferSize,
OUT OPTIONAL HCRYPTHASH *pHashObj )
/*++
Routine Description:
Generates MD5 hash of data
Arguments:
phProv - pointer to provider to be used; can be set to NULL
pbData - buffer of data to be hashed
cbData - size of data to be hashed
pbHashBuffer - buffer to receive hash
pdwHashBufferSize - size of pbHashBuffer
pHashObj - pointer to CRYPTHASH object, updated on sucess, ignored if NULL
Returns:
HRESULT indicating success/failure
--*/
{
HCRYPTPROV hProv = NULL;
HCRYPTHASH hHash = NULL;
HRESULT hRes = S_OK;
if ( !phProv )
{
//
// Get a handle to the CSP that will create the
// hash
if ( !CryptAcquireContext( &hProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndCreateHash;
}
}
else
{
hProv = *phProv;
}
//
// Get a handle to an MD5 hash object
//
if ( !CryptCreateHash( hProv,
CALG_MD5,
0,
0,
&hHash ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndCreateHash;
}
//
// Hash the data
//
if ( !CryptHashData( hHash,
pbData,
cbData,
0 ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndCreateHash;
}
//
// Retrieve the hash
//
if ( !CryptGetHashParam( hHash,
HP_HASHVAL,
pbHashBuffer,
pdwHashBufferSize,
0 ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
goto EndCreateHash;
}
if ( pHashObj )
{
*pHashObj = hHash;
}
EndCreateHash:
//
//Cleanup
//
if ( !pHashObj && hHash )
{
CryptDestroyHash( hHash );
}
if ( !phProv && hProv )
{
CryptReleaseContext( hProv,
0 );
}
return hRes;
}
BOOL IsNumber( LPCWSTR pszName )
{
return ( _wtoi( pszName) > 0 ? TRUE : FALSE );
}
VOID SerializeCKPI( OUT BYTE *pbBuffer,
IN CRYPT_KEY_PROV_INFO *pInfo,
OUT DWORD *pdwPosition )
/*++
Routine Description:
Serializes a CRYPT_KEY_PROV_INFO structure
Arguments:
pbBuffer - pointer to buffer that holds serialized info
pInfo - pointer to CRYPT_KEY_PROV_INFO structure to be serialized
pdwPosition - position in pbBuffer, updated after serialization
Returns:
Nothing
--*/
{
DWORD cbPos = 0;
BYTE bMSB = 0;
BYTE bLSB = 0;
DWORD cbStr = 0;
//
// First bit : [MSB of length][LSB of length][pwszContainerName]
//
if ( pInfo->pwszContainerName )
{
cbStr = wcslen( pInfo->pwszContainerName ) * sizeof(WCHAR) + 2 ; //trailing nulls
pbBuffer[cbPos++] = MSB(cbStr);
pbBuffer[cbPos++] = LSB(cbStr);
memcpy( pbBuffer + cbPos, pInfo->pwszContainerName, cbStr);
cbPos += cbStr;
}
else
{
pbBuffer[cbPos++] = 0;
pbBuffer[cbPos++] = 0;
}
//
// [MSB of length][LSB of length][pwszProvName]
//
if ( pInfo->pwszProvName )
{
cbStr = wcslen( pInfo->pwszProvName ) * sizeof(WCHAR) + 2 ; //trailing nulls
pbBuffer[cbPos++] = MSB(cbStr);
pbBuffer[cbPos++] = LSB(cbStr);
memcpy( pbBuffer + cbPos, pInfo->pwszProvName, cbStr);
cbPos += cbStr;
}
else
{
pbBuffer[cbPos++] = 0;
pbBuffer[cbPos++] = 0;
}
//
// dwProvType
//
memcpy( pbBuffer + cbPos, &(pInfo->dwProvType), sizeof(DWORD) );
cbPos += sizeof(DWORD);
//
// dwFlags
//
memcpy( pbBuffer + cbPos, &(pInfo->dwFlags), sizeof(DWORD) );
cbPos += sizeof(DWORD);
//
// cProvParam
//
memcpy( pbBuffer + cbPos, &(pInfo->cProvParam), sizeof(DWORD) );
cbPos += sizeof(DWORD);
//
// Each of the rgProvParam entries
//
for ( DWORD dwIndex = 0; dwIndex < pInfo->cProvParam; dwIndex++ )
{
CRYPT_KEY_PROV_PARAM ckpParam = pInfo->rgProvParam[dwIndex];
//
// dwParam
//
memcpy( pbBuffer + cbPos, &(ckpParam.dwParam), sizeof(DWORD) );
cbPos += sizeof(DWORD);
//
// cbData
//
memcpy( pbBuffer + cbPos, &(ckpParam.cbData), sizeof(DWORD) );
cbPos += sizeof(DWORD);
//
// pbData
//
memcpy( pbBuffer + cbPos, ckpParam.pbData, ckpParam.cbData );
cbPos += ckpParam.cbData;
//
// dwFlags
//
memcpy( pbBuffer + cbPos, &(ckpParam.dwFlags), sizeof(DWORD) );
cbPos += sizeof(DWORD);
}
//
// dwKeySpec
//
memcpy( pbBuffer + cbPos, &(pInfo->dwKeySpec), sizeof(DWORD) );
cbPos += sizeof(DWORD);
(*pdwPosition) += cbPos;
}
BOOL DeserializeCKPI( IN OUT BYTE **ppbBuffer,
OUT CRYPT_KEY_PROV_INFO *pInfo )
/*++
Routine Description:
Deserializes a CRYPT_KEY_PROV_INFO structure
Arguments:
pBuffer - pointer to pointer to buffer containing serialized information, updated while
processing occurs
ppInfo - pointer to CRYPT_KEY_PROV_INFO structure to be deserialized, updated during
processing
Returns:
TRUE if successful, FALSE if not
--*/
{
BYTE bMSB = 0;
BYTE bLSB = 0;
DWORD dwLength;
BYTE *pbPresent = *ppbBuffer;
//
// [MSB of length][LSB of length][pwszContainerName]
//
bMSB = *(pbPresent++);
bLSB = *(pbPresent++);
dwLength = LENGTH( bMSB, bLSB );
if ( dwLength )
{
pInfo->pwszContainerName = (LPWSTR) pbPresent;
pbPresent += dwLength;
}
else
{
pInfo->pwszContainerName = NULL;
}
//
// [MSB of length][LSB of length][pwszProvName]
//
bMSB = *(pbPresent++);
bLSB = *(pbPresent++);
dwLength = LENGTH( bMSB, bLSB );
if ( dwLength )
{
pInfo->pwszProvName = (LPWSTR) pbPresent;
pbPresent += dwLength;
}
else
{
pInfo->pwszProvName = NULL;
}
//
// dwProvType
//
pInfo->dwProvType = *( (DWORD *) pbPresent );
pbPresent += sizeof(DWORD);
//
// dwFlags
//
pInfo->dwFlags = *( (DWORD *) pbPresent );
pbPresent += sizeof(DWORD);
//
// cProvParam
//
pInfo->cProvParam = *( (DWORD *) pbPresent );
pbPresent += sizeof(DWORD);
//
// Each of the rgProvParam entries
//
if ( pInfo->cProvParam )
{
pInfo->rgProvParam = new CRYPT_KEY_PROV_PARAM[pInfo->cProvParam];
if ( !pInfo->rgProvParam )
{
return FALSE;
}
for ( DWORD dwIndex = 0; dwIndex < pInfo->cProvParam; dwIndex++ )
{
CRYPT_KEY_PROV_PARAM ckpParam = pInfo->rgProvParam[dwIndex];
ckpParam.dwParam = *((DWORD *) pbPresent);
pbPresent += sizeof(DWORD);
ckpParam.cbData = *((DWORD *) pbPresent);
pbPresent += sizeof(DWORD);
ckpParam.pbData = pbPresent;
pbPresent += ckpParam.cbData;
ckpParam.dwFlags = *((DWORD *) pbPresent);
pbPresent += sizeof(DWORD);
}
}
else
{
pInfo->rgProvParam = NULL;
}
//
// dwKeySpec
//
pInfo->dwKeySpec = *( (DWORD *) pbPresent);
pbPresent += sizeof(DWORD);
//
// Update
//
*ppbBuffer = pbPresent;
return TRUE;
}
DWORD SizeOfCKPI( IN CRYPT_KEY_PROV_INFO *pInfo )
/*++
Routine Description:
Calculates the amount of space needed to contain the information for
a key container
Arguments:
pInfo - pointer to CRYPT_KEY_PROV_INFO structure whose space requirements are to
be calculated
Returns:
Number of bytes required to hold CRYPT_KEY_PROV_INFO structure
--*/
{
if ( !pInfo )
{
return 0;
}
DWORD cb = 0;
if ( pInfo->pwszContainerName )
{
//trailing nulls, length bytes
cb += ( wcslen(pInfo->pwszContainerName) * sizeof(WCHAR) ) + 2 + 2;
}
else
{
cb += 2; //length bytes
}
if ( pInfo->pwszProvName )
{
//trailing nulls, length bytes
cb += ( wcslen(pInfo->pwszProvName) * sizeof(WCHAR) ) + 2 + 2;
}
else
{
cb += 2; //length bytes
}
cb += 4*sizeof(DWORD); // cProvParam, dwProvType, dwFlags, dwKeySpec
for ( DWORD dwIndex = 0 ; dwIndex < pInfo->cProvParam; dwIndex++ )
{
cb += ( pInfo->rgProvParam[dwIndex].cbData + 3 * sizeof(DWORD) );
}
return cb;
}
HRESULT DistributeCerts( IN HCERTSTORE hMemStore,
IN BYTE *rgbCertHash,
IN PCRYPT_KEY_PROV_INFO pCKPI )
/*++
Routine Description:
Takes the certs in an in-memory store and distributes them to the appropriate
system stores.
Self-signed certs go into the ROOT store, intermediate certs into the CA store and the
server cert into the MY store. It's possible that the server cert is self-signed, in
which case it goes into both the ROOT and MY stores.
Arguments:
hMemStore - handle to in-memory store containing certs to be distributed
rgbCertHash - buffer containing hash of server cert
hServerPK - private key for server certificate
Returns:
HRESULT indicating success/failure
--*/
{
HCERTSTORE hMyStore = NULL;
HCERTSTORE hCAStore = NULL;
HCERTSTORE hRootStore = NULL;
HRESULT hRes = S_OK;
//
// Open all the stores we'll use
//
hMyStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
MY_STORE_NAME );
if ( !hMyStore )
{
return RETURNCODETOHRESULT( GetLastError() );
}
hCAStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
CA_STORE_NAME );
if ( !hCAStore )
{
CertCloseStore( hMyStore,
0 );
return RETURNCODETOHRESULT( GetLastError() );
}
hRootStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W,
0,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
ROOT_STORE_NAME );
if ( !hRootStore )
{
CertCloseStore( hMyStore,
0 );
CertCloseStore( hCAStore,
0 );
return RETURNCODETOHRESULT( GetLastError() );
}
//
// Iterate through all the certs in the in-memory store, putting them into the
// appropriate place
//
PCCERT_CONTEXT pCert = NULL;
PCCERT_CONTEXT pPrevCert = NULL;
while ( pCert = CertEnumCertificatesInStore( hMemStore,
pPrevCert ) )
{
BOOL fSelfSigned = FALSE;
BOOL fServerCert = FALSE;
//
// Self-signed certs go into the Root store
//
if ( IsSelfSignedCert( pCert ) )
{
fSelfSigned = TRUE;
if ( !CertAddCertificateContextToStore( hRootStore,
pCert,
CERT_STORE_ADD_NEW,
NULL ) &&
GetLastError() != CRYPT_E_EXISTS )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
break;
}
}
//
// Server cert goes into MY store
//
if ( CertMatchesHash( pCert,
rgbCertHash,
&fServerCert) )
{
if ( fServerCert )
{
//
// Add private key to cert context and stuff cert into MY store
//
if ( !CertSetCertificateContextProperty( pCert,
CERT_KEY_PROV_INFO_PROP_ID,
0,
(PVOID) pCKPI ) ||
!CertAddCertificateContextToStore( hMyStore,
pCert,
CERT_STORE_ADD_REPLACE_EXISTING,
NULL ) )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
break;
}
}
}
else
{
hRes = RETURNCODETOHRESULT( GetLastError() );
break;
}
//
// Everything else goes into the CA store
//
if ( !fSelfSigned && !fServerCert )
{
if ( !CertAddCertificateContextToStore( hCAStore,
pCert,
CERT_STORE_ADD_NEW,
NULL ) &&
GetLastError() != CRYPT_E_EXISTS )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
break;
}
}
pPrevCert = pCert;
}
//
// clean up
//
if ( hMyStore )
{
CertCloseStore( hMyStore,
0 );
}
if ( hCAStore )
{
CertCloseStore( hCAStore,
0 );
}
if ( hRootStore )
{
CertCloseStore( hRootStore,
0 );
}
return hRes;
}
BOOL CertMatchesHash( IN PCCERT_CONTEXT pCert,
IN BYTE *rgbHash,
OUT BOOL *pfMatch)
/*++
Routine Description:
Checks whether a cert has the same hash as the hash passed in
Arguments:
pCert - cert to be checked
rgbHash - hash pCert is to be checked against
pfMatch - bool that is set to TRUE if cert matches, FALSE if not
Returns:
TRUE if check was successful, FALSE if not
--*/
{
*pfMatch = FALSE;
BYTE rgbCertHash[SHA1_HASH_SIZE];
DWORD dwSize = SHA1_HASH_SIZE;
if ( CertGetCertificateContextProperty( pCert,
CERT_SHA1_HASH_PROP_ID,
rgbCertHash,
&dwSize ) )
{
*pfMatch = ( memcmp( rgbCertHash, rgbHash, SHA1_HASH_SIZE ) ? FALSE : TRUE );
return TRUE;
}
else
{
return FALSE;
}
}
//////////////
CADMEXCOM_IMSAdminCryptoCapabilities::CADMEXCOM_IMSAdminCryptoCapabilities(
CADMEXCOM* pAdmExCom
)
{
m_pAdmExCom = pAdmExCom;
m_hSchannel = NULL;
m_hSspi = NULL;
m_fHasCredHandle = FALSE;
}
CADMEXCOM_IMSAdminCryptoCapabilities::~CADMEXCOM_IMSAdminCryptoCapabilities( )
{
if ( m_fHasCredHandle )
{
m_pfnFreeCredentialsHandle( &m_hCred );
}
if ( m_hSchannel != NULL )
{
FreeLibrary( m_hSchannel );
}
if ( m_hSspi != NULL )
{
FreeLibrary( m_hSspi );
}
}
HRESULT STDMETHODCALLTYPE
CADMEXCOM_IMSAdminCryptoCapabilities::GetProtocols(
/* [in] */ DWORD dwBufferSize,
/* [size_is][out] */ unsigned char __RPC_FAR *pbBuffer,
/* [out] */ DWORD __RPC_FAR *pdwMDRequiredBufferSize)
{
SecPkgCred_SupportedProtocols SupportedProtocols;
SECURITY_STATUS scRet;
*pdwMDRequiredBufferSize = sizeof(DWORD);
if ( dwBufferSize < sizeof(DWORD) )
{
return RETURNCODETOHRESULT( ERROR_INSUFFICIENT_BUFFER );
}
if ( !LoadSchannel() )
{
return RETURNCODETOHRESULT( GetLastError() );
}
if ( FAILED( scRet = m_pfnQueryCredentialsAttributes( &m_hCred,
SECPKG_ATTR_SUPPORTED_PROTOCOLS,
&SupportedProtocols ) ) )
{
return scRet;
}
*(LPDWORD)pbBuffer = SupportedProtocols.grbitProtocol;
return S_OK;
}
BOOL
CADMEXCOM_IMSAdminCryptoCapabilities::LoadSchannel()
{
SECURITY_STATUS scRet;
TimeStamp tsExpiry;
OSVERSIONINFO osv;
BOOL fT;
BOOL fWinNT;
if ( m_hSchannel == NULL )
{
// Check the OS we are running on
osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
fT = GetVersionEx( &osv );
fWinNT = ( osv.dwPlatformId == VER_PLATFORM_WIN32_NT );
if ( (m_hSchannel = LoadLibrary(L"schannel.dll")) != NULL )
{
m_pfnGetDefaultIssuers = (PFN_SSLGETDEFAULTISSUERS)GetProcAddress( m_hSchannel,
"SslGetDefaultIssuers" );
if ( m_pfnGetDefaultIssuers &&
((m_hSspi = LoadLibrary( fWinNT ? L"security.dll" : L"secur32.dll" )) != NULL) )
{
m_pfnAcquireCredentialsHandle = (ACQUIRE_CREDENTIALS_HANDLE_FN_W)
GetProcAddress( m_hSspi, "AcquireCredentialsHandleW" );
m_pfnFreeCredentialsHandle = (FREE_CREDENTIALS_HANDLE_FN)
GetProcAddress( m_hSspi, "FreeCredentialsHandle" );
m_pfnFreeContextBuffer = (FREE_CONTEXT_BUFFER_FN)
GetProcAddress( m_hSspi, "FreeContextBuffer" );
m_pfnQueryCredentialsAttributes = (QUERY_CREDENTIALS_ATTRIBUTES_FN)
GetProcAddress( m_hSspi, "QueryCredentialsAttributesA" );
if ( m_pfnAcquireCredentialsHandle &&
m_pfnFreeCredentialsHandle &&
m_pfnFreeContextBuffer &&
m_pfnQueryCredentialsAttributes )
{
scRet = m_pfnAcquireCredentialsHandle(
NULL, // My name (ignored)
UNISP_NAME_W, // Package
SECPKG_CRED_INBOUND,// Use
NULL, // Logon Id (ign.)
NULL, // auth data
NULL, // dce-stuff
NULL, // dce-stuff
&m_hCred, // Handle
&tsExpiry );
if ( !FAILED(scRet) )
{
m_fHasCredHandle = TRUE;
return TRUE;
}
}
FreeLibrary( m_hSspi );
m_hSspi = NULL;
}
FreeLibrary( m_hSchannel );
m_hSchannel = NULL;
}
return FALSE;
}
return TRUE;
}
HRESULT STDMETHODCALLTYPE
CADMEXCOM_IMSAdminCryptoCapabilities::GetMaximumCipherStrength(
/* [out] */ LPDWORD pdwMaximumCipherStrength )
{
SecPkgCred_CipherStrengths CipherStrengths;
SECURITY_STATUS scRet;
if ( !LoadSchannel() )
{
return RETURNCODETOHRESULT( GetLastError() );
}
if ( FAILED( scRet = m_pfnQueryCredentialsAttributes( &m_hCred,
SECPKG_ATTR_CIPHER_STRENGTHS,
&CipherStrengths ) ) )
{
return scRet;
}
*pdwMaximumCipherStrength = CipherStrengths.dwMaximumCipherStrength;
return S_OK;
}
HRESULT STDMETHODCALLTYPE
CADMEXCOM_IMSAdminCryptoCapabilities::GetRootCertificates(
/* [in] */ DWORD dwBufferSize,
/* [size_is][out] */ unsigned char __RPC_FAR *pbBuffer,
/* [out] */ DWORD __RPC_FAR *pdwMDRequiredBufferSize)
{
DWORD dwLen;
if ( !LoadSchannel() ||
!m_pfnGetDefaultIssuers( NULL, &dwLen ) )
{
return RETURNCODETOHRESULT( GetLastError() );
}
if ( dwBufferSize < dwLen )
{
*pdwMDRequiredBufferSize = dwLen;
return RETURNCODETOHRESULT( ERROR_INSUFFICIENT_BUFFER );
}
if ( m_pfnGetDefaultIssuers( pbBuffer, &dwLen ) )
{
*pdwMDRequiredBufferSize = dwLen;
return S_OK;
}
return RETURNCODETOHRESULT( GetLastError() );
}
HRESULT STDMETHODCALLTYPE
CADMEXCOM_IMSAdminCryptoCapabilities::GetSupportedAlgs(
/* [in] */ DWORD dwBufferSize,
/* [size_is][out] */ DWORD __RPC_FAR *pbBuffer,
/* [out] */ DWORD __RPC_FAR *pdwMDRequiredBufferSize)
{
DWORD dwLen;
SecPkgCred_SupportedAlgs SupportedAlgs;
SECURITY_STATUS scRet;
if ( !LoadSchannel() )
{
return RETURNCODETOHRESULT( GetLastError() );
}
if ( FAILED( scRet = m_pfnQueryCredentialsAttributes( &m_hCred,
SECPKG_ATTR_SUPPORTED_ALGS,
&SupportedAlgs ) ) )
{
return scRet;
}
*pdwMDRequiredBufferSize = sizeof(ALG_ID) * SupportedAlgs.cSupportedAlgs;
if ( dwBufferSize < *pdwMDRequiredBufferSize )
{
return RETURNCODETOHRESULT( ERROR_INSUFFICIENT_BUFFER );
}
memcpy( pbBuffer, SupportedAlgs.palgSupportedAlgs, *pdwMDRequiredBufferSize );
return S_OK;
}
HRESULT STDMETHODCALLTYPE
CADMEXCOM_IMSAdminCryptoCapabilities::SetCAList(
/*[in] */ DWORD dwBufferSize,
/*[in, size_is(dwBufferSize)] */ unsigned char __RPC_FAR *pbBuffer )
{
#if defined(USE_CAPI2)
HCERTSTORE hStore = NULL;
HKEY hKey = NULL;
PCCERT_CONTEXT pCtx;
DWORD cCert;
PCCERT_CONTEXT* rgpCert;
HRESULT hRes = S_OK;
DWORD dwValueLen;
//
// WARNING: this relies on a security hole in CAPI2, as we want to add certs to
// ROOT store w/o any UI, and the only way to do this ( at least currently ) is
// to access the ROOT store as a registry store instead of system store.
// CAPI2 team may change this in the future, but they MUST provide a way to control
// the CA list used by schannel, currently in HKCU ROOT store.
//
if ( RegOpenKeyEx(
HKEY_CURRENT_USER,
L"Software\\Microsoft\\SystemCertificates\\ROOT",
0,
KEY_ALL_ACCESS,
&hKey ) == ERROR_SUCCESS )
{
if ( (hStore = CertOpenStore( CERT_STORE_PROV_REG,
0,
NULL,
0,
(const void*)hKey )) == NULL )
{
RegCloseKey( hKey );
hKey = NULL;
}
}
if ( hStore )
{
//
// delete existing certs
//
for ( pCtx = NULL, cCert = 0 ;
pCtx = CertEnumCertificatesInStore( hStore, pCtx ) ; )
{
++cCert;
}
if ( cCert && ( rgpCert = (PCCERT_CONTEXT*)LocalAlloc( LMEM_FIXED,
cCert * sizeof(PCCERT_CONTEXT) ) ) )
{
for ( pCtx = NULL, cCert = 0 ;
pCtx = CertEnumCertificatesInStore( hStore, pCtx ) ; )
{
rgpCert[cCert++] = pCtx;
}
while ( cCert-- )
{
CertDeleteCertificateFromStore( rgpCert[cCert] );
}
LocalFree( rgpCert );
}
}
else
{
hRes = RETURNCODETOHRESULT( GetLastError() );
}
if ( SUCCEEDED( hRes ) )
{
//
// deserialize certs
//
for ( ; dwBufferSize ; )
{
dwValueLen = *(LPDWORD)pbBuffer;
if ( CertAddSerializedElementToStore( hStore,
pbBuffer + sizeof(DWORD),
dwValueLen,
CERT_STORE_ADD_REPLACE_EXISTING,
0,
CERT_STORE_CERTIFICATE_CONTEXT_FLAG,
NULL,
NULL ) == FALSE )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
break;
}
pbBuffer += sizeof(DWORD) + PAD4(dwValueLen);
dwBufferSize -= sizeof(DWORD) + PAD4(dwValueLen);
}
}
if ( hStore )
{
CertCloseStore( hStore, CERT_CLOSE_STORE_FORCE_FLAG );
RegCloseKey( hKey );
}
#else
HKEY hKey;
int iV;
CHAR achName[128];
DWORD dwNameLen;
DWORD dwValueLen;
LPSTR pszName;
DWORD dwType;
HRESULT hRes = S_OK;
DWORD dwErr;
if ( (dwErr = RegOpenKeyEx( HKEY_CURRENT_USER,
"Software\\Microsoft\\SystemCertificates\\ROOT\\Certificates",
0,
KEY_WRITE|KEY_READ,
&hKey )) == ERROR_SUCCESS )
{
//
// delete existing values
//
for ( iV = 0 ; ; /*++iV*/ )
{
dwNameLen = sizeof( achName );
if ( RegEnumValue( hKey,
iV,
achName,
&dwNameLen,
NULL,
&dwType,
NULL,
NULL ) != ERROR_SUCCESS )
{
break;
}
if ( RegDeleteValue( hKey, achName ) != ERROR_SUCCESS )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
break;
}
}
if ( SUCCEEDED( hRes ) )
{
for ( ; dwBufferSize ; )
{
dwNameLen = *(LPDWORD)pbBuffer;
pszName = (LPSTR)(pbBuffer + sizeof(DWORD));
dwValueLen = *(LPDWORD)(pbBuffer += sizeof(DWORD) + PAD4(dwNameLen));
if ( RegSetValueEx( hKey,
(LPSTR)pszName,
NULL,
REG_BINARY,
pbBuffer + sizeof(DWORD),
dwValueLen ) != ERROR_SUCCESS )
{
hRes = RETURNCODETOHRESULT( GetLastError() );
break;
}
pbBuffer += sizeof(DWORD) + PAD4(dwValueLen);
dwBufferSize -= sizeof(DWORD) + PAD4(dwNameLen) + sizeof(DWORD) + PAD4(dwValueLen);
}
}
RegCloseKey( hKey );
}
else
{
hRes = RETURNCODETOHRESULT( dwErr );
}
#endif
return hRes;
}
BOOL
SetLocalSystemSecurityOnKeyContainer( IN HCRYPTPROV hProv )
/*++
The function applies security to the key containers associated
with the HCRYPTPROV which is passed in such that only
Local System has Full Control. Note that the owner
is not set, which results in a default owner of Administrators.
NB : This function was inherited wholesale from Jeff Spelman [jeffspel] in the CAPI
group.
--*/
{
SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
PSID pLocalSystemSid = NULL;
SECURITY_DESCRIPTOR sd;
PACL pDacl = NULL;
PACCESS_ALLOWED_ACE pAce = NULL;
DWORD dwAclSize = 0;
LONG lRetCode = 0;
BOOL bSuccess = FALSE; // assume this function fails
//
// prepare a Sid representing the Local System account
//
if( !AllocateAndInitializeSid( &sia,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&pLocalSystemSid ) )
{
goto cleanup;
}
//
// compute size of new acl
//
dwAclSize = sizeof(ACL) +
1 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
GetLengthSid(pLocalSystemSid) ;
//
// allocate storage for Acl
//
if ( !(pDacl = (PACL) new BYTE [dwAclSize]) )
{
goto cleanup;
}
if( !InitializeAcl( pDacl,
dwAclSize,
ACL_REVISION ) )
{
goto cleanup;
}
if( !AddAccessAllowedAce( pDacl,
ACL_REVISION,
KEY_ALL_ACCESS,
pLocalSystemSid ) )
{
goto cleanup;
}
//
// make it container inherit.
//
if( !GetAce( pDacl,
0,
(LPVOID *) &pAce ) )
{
goto cleanup;
}
pAce->Header.AceFlags = CONTAINER_INHERIT_ACE;
if( !InitializeSecurityDescriptor( &sd,
SECURITY_DESCRIPTOR_REVISION ) )
{
goto cleanup;
}
if( !SetSecurityDescriptorDacl( &sd,
TRUE,
pDacl,
FALSE ) )
{
goto cleanup;
}
//
// apply the security descriptor to the key container
//
if ( !CryptSetProvParam( hProv,
PP_KEYSET_SEC_DESCR,
(BYTE*)&sd,
(DWORD)DACL_SECURITY_INFORMATION ) )
{
goto cleanup;
}
bSuccess = TRUE; // indicate success
cleanup:
//
// free allocated resources
//
if( pDacl != NULL )
{
delete [] pDacl;
}
if( pLocalSystemSid != NULL )
{
FreeSid( pLocalSystemSid );
}
return bSuccess;
}
extern "C" BOOL WINAPI
DllMain(
HANDLE hModule,
DWORD dwReason,
LPVOID pV
)
/*++
Routine Description:
DLL init/terminate notification function
Arguments:
hModule - DLL handle
dwReason - notification type
LPVOID - not used
Returns:
TRUE if success, FALSE if failure
--*/
{
switch ( dwReason )
{
case DLL_PROCESS_ATTACH:
#ifdef _NO_TRACING_
CREATE_DEBUG_PRINT_OBJECT( "ADMEXS" );
#else
CREATE_DEBUG_PRINT_OBJECT( "ADMEXS" , IisADMExsGuid);
#endif
break;
case DLL_PROCESS_DETACH:
DELETE_DEBUG_PRINT_OBJECT( );
break;
}
return TRUE;
}