/*++ 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 #include #include } #include #include #include #include #define USE_CAPI2 #if defined(USE_CAPI2) #include #endif extern "C" { #define SECURITY_WIN32 #include } #include #include #include #include #include #include #include #include #include #include #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 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 : :(instance_cert_information)? where instance_cert_information = =,, .... | End of instance cert information is marked by "|" Identifying info for CTL : [CTL_INFO for CTL stored under /LM/W3SVC]:(=CTL_INFO)? where CTL_INFO = ,()+, 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 //> ":""|" #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 = ":""|" = = "" | "CERTS=" 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=" 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 : :| 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 // ':' #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= 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= 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>... 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= 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=" 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= 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 : 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; }