mirror of https://github.com/tongzx/nt5src
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
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;
|
|
}
|