|
|
//+-------------------------------------------------------------------------
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1995 - 1999
//
// File: protroot.cpp
//
// Contents: Protect Current User (CU) Root Store APIs
//
// Functions: I_ProtectedRootDllMain
// I_CertProtectFunction
// I_CertSrvProtectFunction
// IPR_EnableSecurityPrivilege
// IPR_IsCurrentUserRootsAllowed
// IPR_IsAuthRootsAllowed
// IPR_IsNTAuthRequiredDisabled
// IPR_IsNotDefinedNameConstraintDisabled
// IPR_IsAuthRootAutoUpdateDisabled
// IPR_InitProtectedRootInfo
// IPR_DeleteUnprotectedRootsFromStore
// IPR_ProtectedRootMessageBox
// IPR_LogCrypt32Event
// IPR_LogCrypt32Error
// IPR_LogCertInformation
// IPR_AddCertInAuthRootAutoUpdateCtl
//
// History: 23-Nov-97 philh created
//--------------------------------------------------------------------------
#include "global.hxx"
#include <chain.h>
#include <dbgdef.h>
#include <wininet.h>
#ifdef STATIC
#undef STATIC
#endif
#define STATIC
// Used for "root" system store's message box
static HMODULE hRegStoreInst;
// # of bytes for a hash. Such as, SHA (20) or MD5 (16)
#define MAX_HASH_LEN 20
#define PROT_ROOT_SUBKEY_NAME L"ProtectedRoots"
#define PROT_ROOT_CERT_VALUE_NAME L"Certificates"
#define PROT_ROOT_MAX_CNT 1000000
#define SYSTEM_STORE_REGPATH L"Software\\Microsoft\\SystemCertificates"
#define PROT_ROOT_REGPATH \
SYSTEM_STORE_REGPATH L"\\Root\\" PROT_ROOT_SUBKEY_NAME
//+-------------------------------------------------------------------------
// Protected root information data structure and defines
//
// The protected root information is stored in the "Certificates" value of
// the "root" store's "ProtectedRoots" SubKey.
//--------------------------------------------------------------------------
// In V1, all hashes are SHA1 (length of 20 bytes) and are at the end of
// the info. cbInfo = dwRootOffset + cRoot * 20
typedef struct _PROT_ROOT_INFO { DWORD cbSize; // sizeof(PROT_ROOT_INFO)
DWORD dwVersion; FILETIME LastUpdate; DWORD cRoot; DWORD dwRootOffset; } PROT_ROOT_INFO, *PPROT_ROOT_INFO;
#define PROT_ROOT_V1 1
// SHA1 hash length
#define PROT_ROOT_HASH_LEN 20
//+-------------------------------------------------------------------------
// Predefined SIDs allocated once by GetPredefinedSids. Freed at
// ProcessDetach.
//--------------------------------------------------------------------------
static CRITICAL_SECTION ProtRootCriticalSection; static BOOL fInitializedPredefinedSids = FALSE; static PSID psidLocalSystem = NULL; static PSID psidAdministrators = NULL; static PSID psidEveryone = NULL;
//+-------------------------------------------------------------------------
// SID definitions used to set security on the "ProtectedRoots" SubKey.
//--------------------------------------------------------------------------
// Only enable the following if you want to do special testing without
// going through the LocalSystem service.
// #define TESTING_NO_PROT_ROOT_RPC 1
#define PSID_PROT_OWNER psidAdministrators
#ifdef TESTING_NO_PROT_ROOT_RPC
#define PSID_PROT_SYSTEM psidAdministrators
#else
#define PSID_PROT_SYSTEM psidLocalSystem
#endif
#define PSID_PROT_EVERYONE psidEveryone
//+-------------------------------------------------------------------------
// ACL definitions used to set security on the "ProtectedRoots" SubKey.
//--------------------------------------------------------------------------
#define PROT_SYSTEM_ACE_MASK KEY_ALL_ACCESS
#define PROT_EVERYONE_ACE_MASK KEY_READ
#define PROT_ACE_FLAGS CONTAINER_INHERIT_ACE
#define PROT_ACE_COUNT 2
#define PROT_SYSTEM_ACE_INDEX 0
#define PROT_EVERYONE_ACE_INDEX 1
//+-------------------------------------------------------------------------
// Critical Section to Serialize Access to Crypt32 Event Log Data Structures
//--------------------------------------------------------------------------
CRITICAL_SECTION Crypt32EventLogCriticalSection;
//+-------------------------------------------------------------------------
// Allocate/free predefined SIDs
//--------------------------------------------------------------------------
static BOOL GetPredefinedSids() { if (fInitializedPredefinedSids) return TRUE;
BOOL fResult; SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY siaWorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
EnterCriticalSection(&ProtRootCriticalSection);
if (!fInitializedPredefinedSids) { if (!AllocateAndInitializeSid( &siaNtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidLocalSystem )) goto AllocateAndInitializeSidError;
if (!AllocateAndInitializeSid( &siaNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdministrators )) goto AllocateAndInitializeSidError;
if (!AllocateAndInitializeSid( &siaWorldSidAuthority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidEveryone )) goto AllocateAndInitializeSidError;
fInitializedPredefinedSids = TRUE; } fResult = TRUE; CommonReturn: LeaveCriticalSection(&ProtRootCriticalSection); return fResult;
ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(AllocateAndInitializeSidError) }
static void FreePredefinedSids() { if (fInitializedPredefinedSids) { FreeSid(psidLocalSystem); FreeSid(psidAdministrators); FreeSid(psidEveryone); } }
//+-------------------------------------------------------------------------
// Dll initialization
//--------------------------------------------------------------------------
BOOL WINAPI I_ProtectedRootDllMain( HMODULE hInst, ULONG ulReason, LPVOID lpReserved) { BOOL fRet = TRUE;
switch (ulReason) { case DLL_PROCESS_ATTACH: // Used for "root" system store's message box
hRegStoreInst = hInst;
fRet = Pki_InitializeCriticalSection(&ProtRootCriticalSection); if (fRet) { fRet = Pki_InitializeCriticalSection( &Crypt32EventLogCriticalSection); if (!fRet) DeleteCriticalSection(&ProtRootCriticalSection); }
I_DBLogAttach();
break;
case DLL_PROCESS_DETACH: I_DBLogDetach();
FreePredefinedSids(); DeleteCriticalSection(&ProtRootCriticalSection); DeleteCriticalSection(&Crypt32EventLogCriticalSection); break; case DLL_THREAD_DETACH: default: break; }
return fRet; }
//+=========================================================================
// Protected root registry flags support function
//==========================================================================
//+-------------------------------------------------------------------------
// Get the ProtectedRoots Flags DWORD registry value stored in HKLM.
//--------------------------------------------------------------------------
STATIC DWORD GetProtectedRootFlags() { HKEY hKey = NULL; LONG err; DWORD dwProtRootFlags = 0;
if (ERROR_SUCCESS != (err = RegOpenKeyExU( HKEY_LOCAL_MACHINE, CERT_PROT_ROOT_FLAGS_REGPATH, 0, // dwReserved
KEY_READ, &hKey ))) goto RegOpenKeyError; if (!ILS_ReadDWORDValueFromRegistry( hKey, CERT_PROT_ROOT_FLAGS_VALUE_NAME, &dwProtRootFlags )) goto ReadValueError;
CommonReturn: ILS_CloseRegistryKey(hKey); return dwProtRootFlags; ErrorReturn: dwProtRootFlags = 0; goto CommonReturn;
SET_ERROR_VAR(RegOpenKeyError, err) TRACE_ERROR(ReadValueError) }
//+=========================================================================
// Protected root information support functions
//==========================================================================
//+-------------------------------------------------------------------------
// Open the SubKey containing the protected root information.
//--------------------------------------------------------------------------
STATIC HKEY OpenProtectedRootSubKey( IN HKEY hKeyCU, IN REGSAM samDesired ) { LONG err; HKEY hKeyProtRoot;
if (ERROR_SUCCESS != (err = RegOpenKeyExU( hKeyCU, PROT_ROOT_REGPATH, 0, // dwReserved
samDesired, &hKeyProtRoot))) goto RegOpenKeyError;
CommonReturn: return hKeyProtRoot; ErrorReturn: hKeyProtRoot = NULL; goto CommonReturn;
SET_ERROR_VAR(RegOpenKeyError, err) }
//+-------------------------------------------------------------------------
// Create the SubKey containing the protected root information.
//--------------------------------------------------------------------------
STATIC HKEY CreateProtectedRootSubKey( IN HKEY hKeyCU, IN REGSAM samDesired ) { LONG err; HKEY hKeyProtRoot; DWORD dwDisposition;
if (ERROR_SUCCESS != (err = RegCreateKeyExU( hKeyCU, PROT_ROOT_REGPATH, 0, // dwReserved
NULL, // lpClass
REG_OPTION_NON_VOLATILE, samDesired, NULL, // lpSecurityAttributes
&hKeyProtRoot, &dwDisposition))) goto RegCreateKeyError; CommonReturn: return hKeyProtRoot; ErrorReturn: hKeyProtRoot = NULL; goto CommonReturn;
SET_ERROR_VAR(RegCreateKeyError, err) }
//+-------------------------------------------------------------------------
// Allocate, read from registry and verify the protected root info.
//
// The root hashes are at the end of the info.
//--------------------------------------------------------------------------
STATIC PPROT_ROOT_INFO ReadProtectedRootInfo( IN HKEY hKeyProtRoot ) { PPROT_ROOT_INFO pInfo = NULL; DWORD cbInfo; DWORD cRoot; DWORD dwRootOffset;
if (!ILS_ReadBINARYValueFromRegistry( hKeyProtRoot, PROT_ROOT_CERT_VALUE_NAME, (BYTE **) &pInfo, &cbInfo )) goto ReadCertificatesProtInfoValueError;
if (sizeof(PROT_ROOT_INFO) > cbInfo || sizeof(PROT_ROOT_INFO) > pInfo->cbSize || pInfo->cbSize > cbInfo || PROT_ROOT_V1 != pInfo->dwVersion ) goto InvalidProtectedRootInfo;
// The root hashes must be at the end of the info
cRoot = pInfo->cRoot; dwRootOffset = pInfo->dwRootOffset; if (dwRootOffset < pInfo->cbSize || dwRootOffset > cbInfo || PROT_ROOT_MAX_CNT < cRoot || cRoot * PROT_ROOT_HASH_LEN != cbInfo - dwRootOffset ) goto InvalidProtectedRootInfo;
CommonReturn: return pInfo; ErrorReturn: PkiFree(pInfo); pInfo = NULL; goto CommonReturn;
TRACE_ERROR(ReadCertificatesProtInfoValueError) SET_ERROR(InvalidProtectedRootInfo, ERROR_INVALID_DATA) }
//+-------------------------------------------------------------------------
// Write the protected root info to the registry.
//
// The root hashes are at the end of the info. Updates the info's
// LastUpdate time.
//--------------------------------------------------------------------------
STATIC BOOL WriteProtectedRootInfo( IN HKEY hKeyProtRoot, IN OUT PPROT_ROOT_INFO pInfo ) { BOOL fResult; LONG err; DWORD cbInfo; SYSTEMTIME SystemTime; FILETIME FileTime;
cbInfo = pInfo->dwRootOffset + pInfo->cRoot * PROT_ROOT_HASH_LEN;
GetSystemTime(&SystemTime); SystemTimeToFileTime(&SystemTime, &FileTime); pInfo->LastUpdate = FileTime;
if (ERROR_SUCCESS != (err = RegSetValueExU( hKeyProtRoot, PROT_ROOT_CERT_VALUE_NAME, NULL, REG_BINARY, (BYTE *) pInfo, cbInfo ))) goto RegSetValueError; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
SET_ERROR_VAR(RegSetValueError, err) }
// In the debugger I saw 0x58
#define PROT_ROOT_SD_LEN 0x100
//+-------------------------------------------------------------------------
// Allocate and get the security descriptor information for the specified
// registry key.
//--------------------------------------------------------------------------
static PSECURITY_DESCRIPTOR AllocAndGetSecurityDescriptor( IN HKEY hKey, SECURITY_INFORMATION SecInf ) { LONG err; PSECURITY_DESCRIPTOR psd = NULL; DWORD cbsd;
cbsd = PROT_ROOT_SD_LEN; if (NULL == (psd = (PSECURITY_DESCRIPTOR) PkiNonzeroAlloc(cbsd))) goto OutOfMemory;
err = RegGetKeySecurity( hKey, SecInf, psd, &cbsd ); if (ERROR_SUCCESS == err) goto CommonReturn; if (ERROR_INSUFFICIENT_BUFFER != err) goto RegGetKeySecurityError;
if (0 == cbsd) goto NoSecurityDescriptor;
PkiFree(psd); psd = NULL; if (NULL == (psd = (PSECURITY_DESCRIPTOR) PkiNonzeroAlloc(cbsd))) goto OutOfMemory;
if (ERROR_SUCCESS != (err = RegGetKeySecurity( hKey, SecInf, psd, &cbsd ))) goto RegGetKeySecurityError;
CommonReturn: return psd; ErrorReturn: PkiFree(psd); psd = NULL; goto CommonReturn;
TRACE_ERROR(OutOfMemory) SET_ERROR_VAR(RegGetKeySecurityError, err) SET_ERROR(NoSecurityDescriptor, ERROR_INVALID_SECURITY_DESCR) }
//+-------------------------------------------------------------------------
// Opens the "ProtectedRoots" registry key and verifies its security owner,
// group, DACLs and SACLs. Must match the security set by
// SrvGetProtectedRootInfo().
//
// If the "ProtectedRoots" SubKey has the proper security. Allocates, reads
// and verifies the "Certificates" value to get the protected root info.
//--------------------------------------------------------------------------
STATIC BOOL GetProtectedRootInfo( IN HKEY hKeyCU, IN REGSAM samDesired, OUT OPTIONAL HKEY *phKeyProtRoot, OUT OPTIONAL PPROT_ROOT_INFO *ppInfo ) { BOOL fResult; HKEY hKeyProtRoot = NULL; PSECURITY_DESCRIPTOR psd = NULL; PPROT_ROOT_INFO pInfo = NULL; PSID psidOwner; // not allocated
BOOL fOwnerDefaulted; BOOL fDaclPresent; PACL pAcl; // not allocated
BOOL fDaclDefaulted; DWORD dwAceIndex; PACCESS_ALLOWED_ACE rgpAce[PROT_ACE_COUNT];
if (NULL == (hKeyProtRoot = OpenProtectedRootSubKey(hKeyCU, samDesired))) goto OpenProtectedRootSubKeyError; if (NULL == (psd = AllocAndGetSecurityDescriptor( hKeyProtRoot, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION ))) goto GetSecurityDescriptorError;
if (!GetPredefinedSids()) goto GetPredefinedSidsError;
// Verify owner
if (!GetSecurityDescriptorOwner(psd, &psidOwner, &fOwnerDefaulted)) goto GetSecurityDescriptorOwnerError; if (NULL == psidOwner || !EqualSid(psidOwner, PSID_PROT_OWNER)) goto InvalidProtectedRootOwner;
// Verify DACL
if (!GetSecurityDescriptorDacl(psd, &fDaclPresent, &pAcl, &fDaclDefaulted)) goto GetSecurityDescriptorDaclError; if (!fDaclPresent || NULL == pAcl) goto MissingProtectedRootDaclError; if (PROT_ACE_COUNT != pAcl->AceCount) goto InvalidProtectedRootDacl;
for (dwAceIndex = 0; dwAceIndex < PROT_ACE_COUNT; dwAceIndex++) { PACCESS_ALLOWED_ACE pAce; if (!GetAce(pAcl, dwAceIndex, (void **) &pAce)) goto InvalidProtectedRootDacl; rgpAce[dwAceIndex] = pAce;
if (ACCESS_ALLOWED_ACE_TYPE != pAce->Header.AceType || PROT_ACE_FLAGS != pAce->Header.AceFlags) goto InvalidProtectedRootDacl; }
if (PROT_SYSTEM_ACE_MASK != rgpAce[PROT_SYSTEM_ACE_INDEX]->Mask || !EqualSid(PSID_PROT_SYSTEM, (PSID) &rgpAce[PROT_SYSTEM_ACE_INDEX]->SidStart) || PROT_EVERYONE_ACE_MASK != rgpAce[PROT_EVERYONE_ACE_INDEX]->Mask || !EqualSid(PSID_PROT_EVERYONE, (PSID) &rgpAce[PROT_EVERYONE_ACE_INDEX]->SidStart)) goto InvalidProtectedRootDacl;
// Get verified protected root info
if (NULL == (pInfo = ReadProtectedRootInfo(hKeyProtRoot))) goto ReadProtectedRootInfoError;
fResult = TRUE; CommonReturn: PkiFree(psd); if (phKeyProtRoot) *phKeyProtRoot = hKeyProtRoot; else ILS_CloseRegistryKey(hKeyProtRoot); if (ppInfo) *ppInfo = pInfo; else PkiFree(pInfo); return fResult; ErrorReturn: ILS_CloseRegistryKey(hKeyProtRoot); hKeyProtRoot = NULL; PkiFree(pInfo); pInfo = NULL; fResult = FALSE; goto CommonReturn;
TRACE_ERROR(OpenProtectedRootSubKeyError) TRACE_ERROR(GetSecurityDescriptorError) TRACE_ERROR(GetPredefinedSidsError) TRACE_ERROR(GetSecurityDescriptorOwnerError) TRACE_ERROR(GetSecurityDescriptorDaclError) SET_ERROR(InvalidProtectedRootOwner, ERROR_INVALID_OWNER) SET_ERROR(MissingProtectedRootDaclError, ERROR_INVALID_ACL) SET_ERROR(InvalidProtectedRootDacl, ERROR_INVALID_ACL) TRACE_ERROR(ReadProtectedRootInfoError) }
//+=========================================================================
// Functions to find, add or delete a root hash from the protected root
// info.
//==========================================================================
STATIC BOOL FindProtectedRoot( IN PPROT_ROOT_INFO pInfo, IN BYTE rgbFindRootHash[PROT_ROOT_HASH_LEN], OUT OPTIONAL DWORD *pdwRootIndex = NULL ) { BYTE *pbRoot = (BYTE *) pInfo + pInfo->dwRootOffset; DWORD cRoot = pInfo->cRoot; DWORD dwRootIndex = 0; BYTE bFirst = rgbFindRootHash[0];
for ( ; dwRootIndex < cRoot; dwRootIndex++, pbRoot += PROT_ROOT_HASH_LEN) { if (bFirst == *pbRoot && 0 == memcmp(rgbFindRootHash, pbRoot, PROT_ROOT_HASH_LEN)) { if (pdwRootIndex) *pdwRootIndex = dwRootIndex; return TRUE; } } if (pdwRootIndex) *pdwRootIndex = 0; return FALSE; }
// Root hash is appended to the end of the list
STATIC BOOL AddProtectedRoot( IN OUT PPROT_ROOT_INFO *ppInfo, IN BYTE rgbAddRootHash[PROT_ROOT_HASH_LEN] ) { PPROT_ROOT_INFO pInfo = *ppInfo; DWORD cRoot = pInfo->cRoot; DWORD dwRootOffset = pInfo->dwRootOffset; DWORD cbInfo;
if (PROT_ROOT_MAX_CNT <= cRoot) { SetLastError(ERROR_OUTOFMEMORY); return FALSE; }
cbInfo = dwRootOffset + (cRoot + 1) * PROT_ROOT_HASH_LEN;
if (NULL == (pInfo = (PPROT_ROOT_INFO) PkiRealloc(pInfo, cbInfo))) return FALSE;
memcpy((BYTE *) pInfo + (dwRootOffset + cRoot * PROT_ROOT_HASH_LEN), rgbAddRootHash, PROT_ROOT_HASH_LEN); pInfo->cRoot = cRoot + 1; *ppInfo = pInfo; return TRUE; }
STATIC void DeleteProtectedRoot( IN PPROT_ROOT_INFO pInfo, IN DWORD dwDeleteRootIndex ) { DWORD cRoot = pInfo->cRoot; BYTE *pbRoot = (BYTE *) pInfo + pInfo->dwRootOffset;
assert(0 < cRoot); assert(dwDeleteRootIndex < cRoot); cRoot--;
if (cRoot > dwDeleteRootIndex) { // Move following roots down
BYTE *pbDst = pbRoot + dwDeleteRootIndex * PROT_ROOT_HASH_LEN; BYTE *pbSrc = pbDst + PROT_ROOT_HASH_LEN; DWORD cbMove = (cRoot - dwDeleteRootIndex) * PROT_ROOT_HASH_LEN; while (cbMove--) *pbDst++ = *pbSrc++; } // else
// last root in list
pInfo->cRoot = cRoot; }
//+=========================================================================
// Certificate store support functions
//==========================================================================
//+-------------------------------------------------------------------------
// Opens the SystemRegistry "Root" store unprotected and relative to the
// specifed base SubKey.
//--------------------------------------------------------------------------
STATIC HCERTSTORE OpenUnprotectedRootStore( IN HKEY hKeyCU, IN DWORD dwOpenFlags = 0 ) { CERT_SYSTEM_STORE_RELOCATE_PARA RelocatePara;
RelocatePara.hKeyBase = hKeyCU; RelocatePara.pwszSystemStore = L"Root"; return CertOpenStore( CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, // dwEncodingType
NULL, // hCryptProv
CERT_SYSTEM_STORE_RELOCATE_FLAG | CERT_SYSTEM_STORE_UNPROTECTED_FLAG | CERT_SYSTEM_STORE_CURRENT_USER | dwOpenFlags, (const void *) &RelocatePara ); }
//+-------------------------------------------------------------------------
// Gets the certificate's SHA1 hash property. Rehashes the encoded
// certificate. Returns TRUE if the property matches the regenerated hash.
//--------------------------------------------------------------------------
static BOOL GetVerifiedCertHashProperty( IN PCCERT_CONTEXT pCert, OUT BYTE rgbHash[PROT_ROOT_HASH_LEN] ) { BYTE rgbProp[PROT_ROOT_HASH_LEN]; DWORD cbData; cbData = PROT_ROOT_HASH_LEN; if (!CertGetCertificateContextProperty( pCert, CERT_SHA1_HASH_PROP_ID, rgbProp, &cbData ) || PROT_ROOT_HASH_LEN != cbData) return FALSE;
// Verify the property
cbData = PROT_ROOT_HASH_LEN; if (!CryptHashCertificate( 0, // hProv
CALG_SHA1, 0, //dwFlags
pCert->pbCertEncoded, pCert->cbCertEncoded, rgbHash, &cbData ) || PROT_ROOT_HASH_LEN != cbData) return FALSE; return (0 == memcmp(rgbHash, rgbProp, PROT_ROOT_HASH_LEN)); }
//+=========================================================================
// FormatMsgBox support functions
//==========================================================================
//+-------------------------------------------------------------------------
// Formats multi bytes into WCHAR hex. Includes a space after every 4 bytes.
//
// Needs (cb * 2 + cb/4 + 1) characters in wsz
//--------------------------------------------------------------------------
static void FormatMsgBoxMultiBytes(DWORD cb, BYTE *pb, LPWSTR wsz) { for (DWORD i = 0; i<cb; i++) { int b; if (i && 0 == (i & 3)) *wsz++ = L' '; b = (*pb & 0xF0) >> 4; *wsz++ = (WCHAR)( (b <= 9) ? b + L'0' : (b - 10) + L'A'); b = *pb & 0x0F; *wsz++ = (WCHAR) ((b <= 9) ? b + L'0' : (b - 10) + L'A'); pb++; } *wsz++ = 0; }
//+-------------------------------------------------------------------------
// Format and allocate a single message box item
//
// The formatted item needs to be LocalFree'ed.
//--------------------------------------------------------------------------
static void FormatMsgBoxItem( OUT LPWSTR *ppwszMsg, OUT DWORD *pcchMsg, IN UINT nFormatID, ... ) { // get format string from resources
WCHAR wszFormat[256]; wszFormat[0] = '\0'; LoadStringU(hRegStoreInst, nFormatID, wszFormat, sizeof(wszFormat)/sizeof(wszFormat[0]));
// format message into requested buffer
va_list argList; va_start(argList, nFormatID); *ppwszMsg = NULL; *pcchMsg = FormatMessageU( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING, wszFormat, 0, // dwMessageId
0, // dwLanguageId
(LPWSTR) ppwszMsg, 0, // minimum size to allocate
&argList);
va_end(argList); }
//+=========================================================================
// Protected root functions called from the services process
//==========================================================================
//+-------------------------------------------------------------------------
// Enable the specified security privilege for the current process.
//
// Also, called from logstor.cpp to enable SE_BACKUP_NAME and
// SE_RESTORE_NAME for CERT_STORE_BACKUP_RESTORE_FLAG.
//--------------------------------------------------------------------------
BOOL IPR_EnableSecurityPrivilege( LPCSTR pszPrivilege ) { BOOL fResult; HANDLE hToken = NULL; TOKEN_PRIVILEGES tp; LUID luid; TOKEN_PRIVILEGES tpPrevious; DWORD cbPrevious;
if (!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken )) goto OpenProcessTokenError;
if (!LookupPrivilegeValueA(NULL, pszPrivilege, &luid)) goto LookupPrivilegeValueError;
//
// first pass. get current privilege setting
//
tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = 0;
cbPrevious = sizeof(TOKEN_PRIVILEGES); memset(&tpPrevious, 0, sizeof(TOKEN_PRIVILEGES)); AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious ); if (ERROR_SUCCESS != GetLastError()) goto AdjustTokenPrivilegesError;
//
// second pass. enable privilege
//
if (0 == tpPrevious.PrivilegeCount) tpPrevious.Privileges[0].Attributes = 0;
tpPrevious.PrivilegeCount = 1; tpPrevious.Privileges[0].Luid = luid; tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
AdjustTokenPrivileges( hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL ); if (ERROR_SUCCESS != GetLastError()) goto AdjustTokenPrivilegesError;
fResult = TRUE; CommonReturn: if (hToken) CloseHandle(hToken); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(OpenProcessTokenError) TRACE_ERROR(LookupPrivilegeValueError) TRACE_ERROR(AdjustTokenPrivilegesError) }
//+-------------------------------------------------------------------------
// Take ownership of the "ProtectedRoots" SubKey
//--------------------------------------------------------------------------
STATIC BOOL SetProtectedRootOwner( IN HKEY hKeyCU, OUT BOOL *pfNew ) { BOOL fResult; LONG err; BOOL fNew = FALSE; HKEY hKeyProtRoot = NULL; SECURITY_DESCRIPTOR sd;
if (!IPR_EnableSecurityPrivilege(SE_TAKE_OWNERSHIP_NAME)) goto EnableTakeOwnershipPrivilegeError;
if (hKeyProtRoot = OpenProtectedRootSubKey(hKeyCU, WRITE_OWNER)) fNew = FALSE; else { if (ERROR_FILE_NOT_FOUND == GetLastError()) hKeyProtRoot = CreateProtectedRootSubKey(hKeyCU, WRITE_OWNER); if (NULL == hKeyProtRoot) goto OpenProtectedRootSubKeyError; fNew = TRUE; }
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) goto InitializeSecurityDescriptorError;
if (!SetSecurityDescriptorOwner(&sd, PSID_PROT_OWNER, FALSE)) goto SetSecurityDescriptorOwnerError;
if (ERROR_SUCCESS != (err = RegSetKeySecurity( hKeyProtRoot, OWNER_SECURITY_INFORMATION, &sd ))) goto RegSetKeySecurityError;
fResult = TRUE; CommonReturn: ILS_CloseRegistryKey(hKeyProtRoot); *pfNew = fNew; return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(EnableTakeOwnershipPrivilegeError) TRACE_ERROR(OpenProtectedRootSubKeyError) TRACE_ERROR(InitializeSecurityDescriptorError) TRACE_ERROR(SetSecurityDescriptorOwnerError) SET_ERROR_VAR(RegSetKeySecurityError, err) }
//+-------------------------------------------------------------------------
// Allocate and get the specified token info.
//--------------------------------------------------------------------------
static void * AllocAndGetTokenInfo( IN HANDLE hToken, IN TOKEN_INFORMATION_CLASS tic ) { void *pvInfo = NULL; DWORD cbInfo = 0; DWORD cbInfo2;
if (!GetTokenInformation( hToken, tic, pvInfo, 0, // cbInfo
&cbInfo )) { if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) goto GetTokenInfoError; } if (0 == cbInfo) goto NoTokenInfoError; if (NULL == (pvInfo = PkiNonzeroAlloc(cbInfo))) goto OutOfMemory;
cbInfo2 = cbInfo; if (!GetTokenInformation( hToken, tic, pvInfo, cbInfo, &cbInfo2 )) goto GetTokenInfoError;
CommonReturn: return pvInfo; ErrorReturn: PkiFree(pvInfo); pvInfo = NULL; goto CommonReturn; TRACE_ERROR(GetTokenInfoError) SET_ERROR(NoTokenInfoError, ERROR_NO_TOKEN) TRACE_ERROR(OutOfMemory) }
//+-------------------------------------------------------------------------
// Set the security group, DACLs and SACLs for the "ProtectedRoots" SubKey
//--------------------------------------------------------------------------
STATIC BOOL SetProtectedRootGroupDaclSacl( IN HKEY hKeyCU ) { BOOL fResult; LONG err; HKEY hKeyProtRoot = NULL; SECURITY_DESCRIPTOR sd; HANDLE hToken = NULL; void *pvTokenInfo = NULL;
PACL pDacl = NULL; PACCESS_ALLOWED_ACE pAce; DWORD dwAclSize; DWORD i;
if (!IPR_EnableSecurityPrivilege(SE_SECURITY_NAME)) goto EnableSecurityNamePrivilegeError;
if (NULL == (hKeyProtRoot = OpenProtectedRootSubKey( hKeyCU, WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY ))) goto OpenProtectedRootSubKeyError;
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) goto InitializeSecurityDescriptorError;
// Set group SID using current process token's primary group SID
if (!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken )) goto OpenProcessTokenError; if (NULL == (pvTokenInfo = AllocAndGetTokenInfo(hToken, TokenPrimaryGroup))) goto GetTokenInfoError; else { PTOKEN_PRIMARY_GROUP pTokenPrimaryGroup = (PTOKEN_PRIMARY_GROUP) pvTokenInfo; PSID psidGroup = pTokenPrimaryGroup->PrimaryGroup;
if (!SetSecurityDescriptorGroup(&sd, psidGroup, FALSE)) goto SetSecurityDescriptorGroupError; }
// Set DACL
//
// compute size of ACL
//
dwAclSize = sizeof(ACL) + PROT_ACE_COUNT * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) + GetLengthSid(PSID_PROT_SYSTEM) + GetLengthSid(PSID_PROT_EVERYONE) ;
//
// allocate storage for Acl
//
if (NULL == (pDacl = (PACL) PkiNonzeroAlloc(dwAclSize))) goto OutOfMemory;
if (!InitializeAcl(pDacl, dwAclSize, ACL_REVISION)) goto InitializeAclError;
if (!AddAccessAllowedAce( pDacl, ACL_REVISION, PROT_SYSTEM_ACE_MASK, PSID_PROT_SYSTEM )) goto AddAceError; if (!AddAccessAllowedAce( pDacl, ACL_REVISION, PROT_EVERYONE_ACE_MASK, PSID_PROT_EVERYONE )) goto AddAceError;
//
// make containers inherit.
//
for (i = 0; i < PROT_ACE_COUNT; i++) { if(!GetAce(pDacl, i, (void **) &pAce)) goto GetAceError; pAce->Header.AceFlags = PROT_ACE_FLAGS; }
if (!SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE)) goto SetSecurityDescriptorDaclError;
// Set SACL
if (!SetSecurityDescriptorSacl(&sd, FALSE, NULL, FALSE)) goto SetSecurityDescriptorSaclError;
if (ERROR_SUCCESS != (err = RegSetKeySecurity( hKeyProtRoot, GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, &sd ))) goto RegSetKeySecurityError;
fResult = TRUE; CommonReturn: ILS_CloseRegistryKey(hKeyProtRoot); if (hToken) CloseHandle(hToken); PkiFree(pvTokenInfo); PkiFree(pDacl); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(EnableSecurityNamePrivilegeError) TRACE_ERROR(OpenProtectedRootSubKeyError) TRACE_ERROR(InitializeSecurityDescriptorError) TRACE_ERROR(OpenProcessTokenError) TRACE_ERROR(GetTokenInfoError) TRACE_ERROR(SetSecurityDescriptorGroupError) TRACE_ERROR(OutOfMemory) TRACE_ERROR(InitializeAclError) TRACE_ERROR(AddAceError) TRACE_ERROR(GetAceError) TRACE_ERROR(SetSecurityDescriptorDaclError) TRACE_ERROR(SetSecurityDescriptorSaclError) SET_ERROR_VAR(RegSetKeySecurityError, err) }
//+-------------------------------------------------------------------------
// Create the initial protected root info.
//
// If not inhibited, add all the roots in the unprotected CurrentUser
// "Root" store.
//--------------------------------------------------------------------------
STATIC BOOL InitAndSetProtectedRootInfo( IN HKEY hKeyCU, IN BOOL fNew ) { BOOL fResult; HKEY hKeyProtRoot = NULL; HCERTSTORE hStore = NULL; PPROT_ROOT_INFO pInfo = NULL;
if (NULL == (pInfo = (PPROT_ROOT_INFO) PkiNonzeroAlloc( sizeof(PROT_ROOT_INFO)))) goto OutOfMemory; memset(pInfo, 0, sizeof(PROT_ROOT_INFO)); pInfo->cbSize = sizeof(PROT_ROOT_INFO); pInfo->dwVersion = PROT_ROOT_V1; pInfo->dwRootOffset = sizeof(PROT_ROOT_INFO);
if (fNew && 0 == (GetProtectedRootFlags() & CERT_PROT_ROOT_INHIBIT_ADD_AT_INIT_FLAG)) { if (hStore = OpenUnprotectedRootStore(hKeyCU, CERT_STORE_READONLY_FLAG)) { PCCERT_CONTEXT pCert = NULL; while (pCert = CertEnumCertificatesInStore(hStore, pCert)) { BYTE rgbHash[PROT_ROOT_HASH_LEN]; if (GetVerifiedCertHashProperty(pCert, rgbHash)) { if (!AddProtectedRoot(&pInfo, rgbHash)) goto AddProtectedRootError; } } } }
if (NULL == (hKeyProtRoot = OpenProtectedRootSubKey( hKeyCU, KEY_ALL_ACCESS ))) goto OpenProtectedRootSubKeyError;
if (!WriteProtectedRootInfo(hKeyProtRoot, pInfo)) goto WritedProtectedRootInfoError;
fResult = TRUE; CommonReturn: PkiFree(pInfo); CertCloseStore(hStore, 0); ILS_CloseRegistryKey(hKeyProtRoot); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(OutOfMemory) TRACE_ERROR(AddProtectedRootError) TRACE_ERROR(OpenProtectedRootSubKeyError) TRACE_ERROR(WritedProtectedRootInfoError) }
//+-------------------------------------------------------------------------
// Open the "ProtectedRoots" SubKey and verify its security. Allocate,
// read and verify the protected root information.
//
// If the "ProtectedRoots" SubKey doesn't exist or is invalid, initialize.
//--------------------------------------------------------------------------
STATIC BOOL SrvGetProtectedRootInfo( IN HKEY hKeyCU, OUT OPTIONAL HKEY *phKeyProtRoot, OUT OPTIONAL PPROT_ROOT_INFO *ppProtRootInfo ) { BOOL fNew; if (GetProtectedRootInfo( hKeyCU, KEY_ALL_ACCESS, phKeyProtRoot, ppProtRootInfo )) return TRUE;
if (!GetPredefinedSids()) return FALSE;
if (!SetProtectedRootOwner(hKeyCU, &fNew)) return FALSE; if (!SetProtectedRootGroupDaclSacl(hKeyCU)) return FALSE; if (!InitAndSetProtectedRootInfo(hKeyCU, fNew)) return FALSE;
return GetProtectedRootInfo( hKeyCU, KEY_ALL_ACCESS, phKeyProtRoot, ppProtRootInfo ); }
//+-------------------------------------------------------------------------
// Initialize the protected list of CurrentUser roots
//--------------------------------------------------------------------------
STATIC BOOL SrvInitProtectedRoots( IN HKEY hKeyCU ) { return SrvGetProtectedRootInfo( hKeyCU, NULL, // phKeyProtRoot
NULL // ppProtRootInfo
); }
//+-------------------------------------------------------------------------
// Purge all CurrentUser roots from the protected list that also exist
// in the LocalMachine SystemRegistry "Root" store. Also removes duplicated
// certificates from the CurrentUser SystemRegistry "Root" store.
//--------------------------------------------------------------------------
STATIC BOOL SrvPurgeLocalMachineProtectedRoots( IN HKEY hKeyCU, IN LPCWSTR pwszRootStoreName ) { BOOL fResult; HKEY hKeyProtRoot = NULL; PPROT_ROOT_INFO pInfo = NULL; PCCERT_CONTEXT pCert = NULL; HCERTSTORE hCURootStore = NULL; HCERTSTORE hLMRootStore = NULL; BOOL fProtDeleted; BYTE rgbHash[PROT_ROOT_HASH_LEN]; CRYPT_DATA_BLOB HashBlob; DWORD dwRootIndex;
if (!SrvGetProtectedRootInfo( hKeyCU, &hKeyProtRoot, &pInfo )) goto GetProtectedRootInfoError;
if (GetProtectedRootFlags() & CERT_PROT_ROOT_INHIBIT_PURGE_LM_FLAG) goto AccessDenied;
if (NULL == (hCURootStore = OpenUnprotectedRootStore(hKeyCU))) goto OpenCURootStoreError;
if (NULL == (hLMRootStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, // dwEncodingType
NULL, // hCryptProv
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG, (const void *) pwszRootStoreName ))) goto OpenLMRootStoreError;
HashBlob.pbData = rgbHash; HashBlob.cbData = PROT_ROOT_HASH_LEN; fProtDeleted = FALSE; pCert = NULL; while (pCert = CertEnumCertificatesInStore(hCURootStore, pCert)) { if (GetVerifiedCertHashProperty(pCert, rgbHash)) { PCCERT_CONTEXT pLMCert;
if (pLMCert = CertFindCertificateInStore( hLMRootStore, 0, // dwCertEncodingType
0, // dwFindFlags
CERT_FIND_SHA1_HASH, (const void *) &HashBlob, NULL //pPrevCertContext
)) { // CurrentUser Root also exists in LocalMachine. Delete
// it from the CurrentUser Root store.
PCCERT_CONTEXT pDeleteCert = CertDuplicateCertificateContext(pCert);
CertFreeCertificateContext(pLMCert); if (!CertDeleteCertificateFromStore(pDeleteCert)) goto DeleteCertFromRootStoreError;
if (FindProtectedRoot(pInfo, rgbHash, &dwRootIndex)) { // The CurrentUser Root is in the protected list,
// delete it from there.
DeleteProtectedRoot(pInfo, dwRootIndex); fProtDeleted = TRUE; } } } }
// If a protected root exists in the LocalMachine, then, delete it
// from the protected list. This step is necessary, if the root
// was removed from the CurrentUser unprotected store.
dwRootIndex = pInfo->cRoot; HashBlob.pbData = (BYTE *) pInfo + pInfo->dwRootOffset + PROT_ROOT_HASH_LEN * dwRootIndex; while (dwRootIndex--) { PCCERT_CONTEXT pLMCert;
HashBlob.pbData -= PROT_ROOT_HASH_LEN; if (pLMCert = CertFindCertificateInStore( hLMRootStore, 0, // dwCertEncodingType
0, // dwFindFlags
CERT_FIND_SHA1_HASH, (const void *) &HashBlob, NULL //pPrevCertContext
)) { CertFreeCertificateContext(pLMCert); // Cert exists in the LocalMachine store, delete
// from protected list.
DeleteProtectedRoot(pInfo, dwRootIndex); fProtDeleted = TRUE; } }
if (fProtDeleted) { if (!WriteProtectedRootInfo(hKeyProtRoot, pInfo)) goto WriteProtectedRootInfoError; } fResult = TRUE; CommonReturn: ILS_CloseRegistryKey(hKeyProtRoot); PkiFree(pInfo); CertFreeCertificateContext(pCert); CertCloseStore(hCURootStore, 0); CertCloseStore(hLMRootStore, 0); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
SET_ERROR(AccessDenied, E_ACCESSDENIED) TRACE_ERROR(OpenCURootStoreError) TRACE_ERROR(OpenLMRootStoreError) TRACE_ERROR(GetProtectedRootInfoError) TRACE_ERROR(DeleteCertFromRootStoreError) TRACE_ERROR(WriteProtectedRootInfoError) }
//+-------------------------------------------------------------------------
// Add the specified certificate to the CurrentUser SystemRegistry "Root"
// store and the protected list of roots. The user is prompted before doing
// the add.
//
// Note, CertAddSerializedElementToStore() has __try/__except around
// accessing pbSerializedCert.
//--------------------------------------------------------------------------
STATIC BOOL SrvAddProtectedRoot( IN handle_t hRpc, IN HKEY hKeyCU, IN BYTE *pbSerializedCert, IN DWORD cbSerializedCert ) { BOOL fResult; HKEY hKeyProtRoot = NULL; PPROT_ROOT_INFO pInfo = NULL; PCCERT_CONTEXT pCert = NULL; BYTE rgbCertHash[PROT_ROOT_HASH_LEN]; HCERTSTORE hRootStore = NULL; BOOL fProtExists;
if (!SrvGetProtectedRootInfo( hKeyCU, &hKeyProtRoot, &pInfo )) goto GetProtectedRootInfoError;
if (!CertAddSerializedElementToStore( NULL, // hCertStore, NULL => create context
pbSerializedCert, cbSerializedCert, CERT_STORE_ADD_ALWAYS, 0, // dwFlags
CERT_STORE_CERTIFICATE_CONTEXT_FLAG, NULL, // pdwContextType
(const void **) &pCert )) goto CreateCertContextError;
if (!GetVerifiedCertHashProperty(pCert, rgbCertHash)) goto VerifyHashPropertyError;
fProtExists = FindProtectedRoot(pInfo, rgbCertHash); if (!fProtExists) { if (IDYES != IPR_ProtectedRootMessageBox( hRpc, pCert, IDS_ROOT_MSG_BOX_ADD_ACTION, MB_TOPMOST | MB_SERVICE_NOTIFICATION )) goto Cancelled; }
if (NULL == (hRootStore = OpenUnprotectedRootStore(hKeyCU))) goto OpenRootStoreError;
if (!CertAddSerializedElementToStore( hRootStore, pbSerializedCert, cbSerializedCert, CERT_STORE_ADD_REPLACE_EXISTING, 0, // dwFlags
CERT_STORE_CERTIFICATE_CONTEXT_FLAG, NULL, // pdwContextType
NULL // ppvContext
)) goto AddCertToRootStoreError;
if (!fProtExists) { if (!AddProtectedRoot(&pInfo, rgbCertHash)) goto AddProtectedRootError; if (!WriteProtectedRootInfo(hKeyProtRoot, pInfo)) goto WriteProtectedRootInfoError; } fResult = TRUE; CommonReturn: ILS_CloseRegistryKey(hKeyProtRoot); PkiFree(pInfo); CertFreeCertificateContext(pCert); CertCloseStore(hRootStore, 0); return fResult;
ErrorReturn: fResult = FALSE; goto CommonReturn;
SET_ERROR(Cancelled, ERROR_CANCELLED) TRACE_ERROR(CreateCertContextError) TRACE_ERROR(VerifyHashPropertyError) TRACE_ERROR(GetProtectedRootInfoError) TRACE_ERROR(OpenRootStoreError) TRACE_ERROR(AddCertToRootStoreError) TRACE_ERROR(AddProtectedRootError) TRACE_ERROR(WriteProtectedRootInfoError) }
//+-------------------------------------------------------------------------
// Delete the specified certificate from the CurrentUser SystemRegistry "Root"
// store and the protected list of roots. The user is prompted before doing
// the delete.
//
// __try/__except around memory access to
// rgbUntrustedRootHash[PROT_ROOT_HASH_LEN]
//--------------------------------------------------------------------------
STATIC BOOL SrvDeleteProtectedRoot( IN handle_t hRpc, IN HKEY hKeyCU, IN BYTE rgbUntrustedRootHash[PROT_ROOT_HASH_LEN] ) { BOOL fResult; HKEY hKeyProtRoot = NULL; PPROT_ROOT_INFO pInfo = NULL; PCCERT_CONTEXT pCert = NULL; HCERTSTORE hRootStore = NULL; BYTE rgbCertHash[PROT_ROOT_HASH_LEN]; DWORD dwRootIndex; BOOL fProtExists; BYTE rgbRootHash[PROT_ROOT_HASH_LEN]; CRYPT_DATA_BLOB RootHashBlob; DWORD dwExceptionCode;
if (!SrvGetProtectedRootInfo( hKeyCU, &hKeyProtRoot, &pInfo )) goto GetProtectedRootInfoError;
if (NULL == (hRootStore = OpenUnprotectedRootStore(hKeyCU))) goto OpenRootStoreError;
__try { memcpy(rgbRootHash, rgbUntrustedRootHash, sizeof(rgbRootHash)); } __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; }
RootHashBlob.pbData = rgbRootHash; RootHashBlob.cbData = PROT_ROOT_HASH_LEN; if (NULL == (pCert = CertFindCertificateInStore( hRootStore, 0, // dwCertEncodingType
0, // dwFindFlags
CERT_FIND_SHA1_HASH, (const void *) &RootHashBlob, NULL //pPrevCertContext
))) goto FindCertError;
if (!GetVerifiedCertHashProperty(pCert, rgbCertHash)) goto VerifyHashPropertyError;
fProtExists = FindProtectedRoot(pInfo, rgbCertHash, &dwRootIndex); if (fProtExists) { if (IDYES != IPR_ProtectedRootMessageBox( hRpc, pCert, IDS_ROOT_MSG_BOX_DELETE_ACTION, MB_TOPMOST | MB_SERVICE_NOTIFICATION )) goto Cancelled; }
fResult = CertDeleteCertificateFromStore(pCert); pCert = NULL; if (!fResult) goto DeleteCertFromRootStoreError; if (fProtExists) { DeleteProtectedRoot(pInfo, dwRootIndex); if (!WriteProtectedRootInfo(hKeyProtRoot, pInfo)) goto WriteProtectedRootInfoError; } fResult = TRUE; CommonReturn: ILS_CloseRegistryKey(hKeyProtRoot); PkiFree(pInfo); CertFreeCertificateContext(pCert); CertCloseStore(hRootStore, 0); return fResult;
ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(Cancelled, ERROR_CANCELLED) TRACE_ERROR(OpenRootStoreError) TRACE_ERROR(FindCertError) SET_ERROR_VAR(ExceptionError, dwExceptionCode) TRACE_ERROR(VerifyHashPropertyError) TRACE_ERROR(GetProtectedRootInfoError) TRACE_ERROR(DeleteCertFromRootStoreError) TRACE_ERROR(WriteProtectedRootInfoError) }
//+-------------------------------------------------------------------------
// Delete all CurrentUser roots from the protected list that don't also
// exist in the CurrentUser SystemRegistry "Root" store. The user is
// prompted before doing the delete.
//--------------------------------------------------------------------------
STATIC BOOL SrvDeleteUnknownProtectedRoots( IN handle_t hRpc, IN HKEY hKeyCU ) { BOOL fResult; HKEY hKeyProtRoot = NULL; PPROT_ROOT_INFO pInfo = NULL; HCERTSTORE hRootStore = NULL; DWORD cOrigRoot; CRYPT_DATA_BLOB HashBlob; DWORD dwRootIndex;
if (!SrvGetProtectedRootInfo( hKeyCU, &hKeyProtRoot, &pInfo )) goto GetProtectedRootInfoError; if (NULL == (hRootStore = OpenUnprotectedRootStore(hKeyCU))) goto OpenRootStoreError;
cOrigRoot = pInfo->cRoot;
HashBlob.pbData = (BYTE *) pInfo + pInfo->dwRootOffset + PROT_ROOT_HASH_LEN * cOrigRoot; HashBlob.cbData = PROT_ROOT_HASH_LEN; dwRootIndex = cOrigRoot; while (dwRootIndex--) { PCCERT_CONTEXT pCert;
HashBlob.pbData -= PROT_ROOT_HASH_LEN; if (pCert = CertFindCertificateInStore( hRootStore, 0, // dwCertEncodingType
0, // dwFindFlags
CERT_FIND_SHA1_HASH, (const void *) &HashBlob, NULL //pPrevCertContext
)) CertFreeCertificateContext(pCert); else // Cert doesn't exist in the unprotected store, delete
// from protected list.
DeleteProtectedRoot(pInfo, dwRootIndex); }
if (cOrigRoot > pInfo->cRoot) { // At least one root was deleted above
int id; LPWSTR pwszTitle; LPWSTR pwszText; DWORD cchText; RPC_STATUS RpcStatus = 0;
FormatMsgBoxItem(&pwszTitle, &cchText, IDS_ROOT_MSG_BOX_TITLE); FormatMsgBoxItem(&pwszText, &cchText, IDS_ROOT_MSG_BOX_DELETE_UNKNOWN_PROT_ROOTS, cOrigRoot - pInfo->cRoot);
// Do impersonation for TerminalServer clients
if (hRpc) RpcStatus = RpcImpersonateClient(hRpc); id = MessageBoxU( NULL, // hwndOwner
pwszText, pwszTitle, MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING | MB_TOPMOST | MB_SERVICE_NOTIFICATION ); if (hRpc && ERROR_SUCCESS == RpcStatus) RpcRevertToSelf();
LocalFree((HLOCAL) pwszTitle); LocalFree((HLOCAL) pwszText); if (IDYES != id) goto AccessDenied;
if (!WriteProtectedRootInfo(hKeyProtRoot, pInfo)) goto WriteProtectedRootInfoError; } fResult = TRUE; CommonReturn: ILS_CloseRegistryKey(hKeyProtRoot); PkiFree(pInfo); CertCloseStore(hRootStore, 0); return fResult;
ErrorReturn: fResult = FALSE; goto CommonReturn;
SET_ERROR(AccessDenied, E_ACCESSDENIED) TRACE_ERROR(GetProtectedRootInfoError) TRACE_ERROR(OpenRootStoreError) TRACE_ERROR(WriteProtectedRootInfoError) }
// Forward reference
STATIC BOOL SrvLogCrypt32Event( IN BYTE *pbIn, IN DWORD cbIn );
STATIC BOOL SrvAddCertInCtl( IN BYTE *pbIn, IN DWORD cbIn );
//+-------------------------------------------------------------------------
// Called from the services process to process a protected certificate
// function.
//
// Returns the error status, ie, not returned in LastError.
//--------------------------------------------------------------------------
DWORD WINAPI I_CertSrvProtectFunction( IN handle_t hRpc, IN DWORD dwFuncId, IN DWORD dwFlags, IN LPCWSTR pwszIn, IN BYTE *pbIn, IN DWORD cbIn, OUT BYTE **ppbOut, OUT DWORD *pcbOut, IN PFN_CERT_PROT_MIDL_USER_ALLOC pfnAlloc, IN PFN_CERT_PROT_MIDL_USER_FREE pfnFree ) { DWORD dwErr; BOOL fResult; HKEY hKeyCU = NULL; LONG err; #ifndef TESTING_NO_PROT_ROOT_RPC
RPC_STATUS RpcStatus; #endif
#ifdef TESTING_NO_PROT_ROOT_RPC
// For testing, called from the client's process
err = RegOpenHKCU(&hKeyCU); if (ERROR_SUCCESS != err) goto RegOpenHKCUError; #else
if (NULL == hRpc) goto InvalidArg;
// Get the client's HKCU.
if (ERROR_SUCCESS != (RpcStatus = RpcImpersonateClient(hRpc))) goto ImpersonateClientError; err = RegOpenHKCUEx(&hKeyCU, REG_HKCU_LOCAL_SYSTEM_ONLY_DEFAULT_FLAG); RpcRevertToSelf(); if (ERROR_SUCCESS != err) goto RegOpenHKCUError; #endif
switch (dwFuncId) { case CERT_PROT_INIT_ROOTS_FUNC_ID: fResult = SrvInitProtectedRoots(hKeyCU); break; case CERT_PROT_PURGE_LM_ROOTS_FUNC_ID: fResult = SrvPurgeLocalMachineProtectedRoots(hKeyCU, L"Root"); fResult &= SrvPurgeLocalMachineProtectedRoots(hKeyCU, L"AuthRoot"); break; case CERT_PROT_ADD_ROOT_FUNC_ID: if (NULL == pbIn || 0 == cbIn) goto InvalidArg; fResult = SrvAddProtectedRoot(hRpc, hKeyCU, pbIn, cbIn); break; case CERT_PROT_DELETE_ROOT_FUNC_ID: if (NULL == pbIn || PROT_ROOT_HASH_LEN != cbIn) goto InvalidArg; fResult = SrvDeleteProtectedRoot(hRpc, hKeyCU, pbIn); break; case CERT_PROT_DELETE_UNKNOWN_ROOTS_FUNC_ID: fResult = SrvDeleteUnknownProtectedRoots(hRpc, hKeyCU); break; case CERT_PROT_ADD_ROOT_IN_CTL_FUNC_ID: if (NULL == pbIn || 0 == cbIn) goto InvalidArg; fResult = SrvAddCertInCtl(pbIn, cbIn); break; case CERT_PROT_LOG_EVENT_FUNC_ID: if (NULL == pbIn || 0 == cbIn) goto InvalidArg; fResult = SrvLogCrypt32Event(pbIn, cbIn); break; case CERT_PROT_ROOT_LIST_FUNC_ID: // Removed support for XAddRoot control
default: goto InvalidArg; }
if (!fResult) goto ErrorReturn; dwErr = ERROR_SUCCESS; CommonReturn: if (hKeyCU) RegCloseHKCU(hKeyCU); return dwErr; ErrorReturn: dwErr = GetLastError(); if (0 == dwErr) dwErr = (DWORD) E_UNEXPECTED; goto CommonReturn;
SET_ERROR(InvalidArg, E_INVALIDARG) #ifdef TESTING_NO_PROT_ROOT_RPC
#else
SET_ERROR_VAR(ImpersonateClientError, RpcStatus) #endif
SET_ERROR_VAR(RegOpenHKCUError, err) }
#ifdef TESTING_NO_PROT_ROOT_RPC
// For testing: the server stuff resides in the client process
BOOL WINAPI I_CertProtectFunction( IN DWORD dwFuncId, IN DWORD dwFlags, IN OPTIONAL LPCWSTR pwszIn, IN OPTIONAL BYTE *pbIn, IN DWORD cbIn, OUT OPTIONAL BYTE **ppbOut, OUT OPTIONAL DWORD *pcbOut ) { DWORD dwErr; dwErr = I_CertSrvProtectFunction( NULL, // hRpc
dwFuncId, dwFlags, pwszIn, pbIn, cbIn, NULL, // ppbOut
NULL, // pcbOut
NULL, // pfnAlloc
NULL // pfnFree
);
if (ERROR_SUCCESS == dwErr) return TRUE; else { SetLastError(dwErr); return FALSE; } } #else
BOOL WINAPI I_CertProtectFunction( IN DWORD dwFuncId, IN DWORD dwFlags, IN OPTIONAL LPCWSTR pwszIn, IN OPTIONAL BYTE *pbIn, IN DWORD cbIn, OUT OPTIONAL BYTE **ppbOut, OUT OPTIONAL DWORD *pcbOut ) { return I_CertCltProtectFunction( dwFuncId, dwFlags, pwszIn, pbIn, cbIn, ppbOut, pcbOut ); } #endif
//+=========================================================================
// Protected root functions called from the client process in logstor.cpp
// or in ..\chain\chain.cpp
//==========================================================================
//+-------------------------------------------------------------------------
// Returns TRUE if the protected root flag wasn't set to disable the opening
// of the CurrentUser's "root\.Default" physical store.
//--------------------------------------------------------------------------
BOOL IPR_IsCurrentUserRootsAllowed() { DWORD dwProtRootFlags; dwProtRootFlags = GetProtectedRootFlags(); return 0 == (dwProtRootFlags & CERT_PROT_ROOT_DISABLE_CURRENT_USER_FLAG); }
//+-------------------------------------------------------------------------
// Returns TRUE if the protected root flag wasn't set to disable the opening
// of the LocalMachine's "root\.AuthRoot" physical store.
//--------------------------------------------------------------------------
BOOL IPR_IsAuthRootsAllowed() { DWORD dwProtRootFlags; dwProtRootFlags = GetProtectedRootFlags(); return 0 == (dwProtRootFlags & CERT_PROT_ROOT_DISABLE_LM_AUTH_FLAG); }
//+-------------------------------------------------------------------------
// Returns TRUE if the protected root flag was set to disable the
// requiring of the issuing CA certificate being in the "NTAuth"
// Enterprise store.
//--------------------------------------------------------------------------
BOOL IPR_IsNTAuthRequiredDisabled() { DWORD dwProtRootFlags; dwProtRootFlags = GetProtectedRootFlags(); return 0 != (dwProtRootFlags & CERT_PROT_ROOT_DISABLE_NT_AUTH_REQUIRED_FLAG); }
//+-------------------------------------------------------------------------
// Returns TRUE if the protected root flag was set to disable checking for
// not defined name constraints.
//--------------------------------------------------------------------------
BOOL IPR_IsNotDefinedNameConstraintDisabled() { DWORD dwProtRootFlags; dwProtRootFlags = GetProtectedRootFlags(); return 0 != (dwProtRootFlags & CERT_PROT_ROOT_DISABLE_NOT_DEFINED_NAME_CONSTRAINT_FLAG); }
//+---------------------------------------------------------------------------
// Returns TRUE if Auto Update has been disabled
//----------------------------------------------------------------------------
BOOL IPR_IsAuthRootAutoUpdateDisabled() { HKEY hKey = NULL; DWORD dwInstallFlag = 0;
if (!IPR_IsAuthRootsAllowed()) return TRUE;
if (ERROR_SUCCESS != RegOpenKeyExU( HKEY_LOCAL_MACHINE, CERT_OCM_SUBCOMPONENTS_LOCAL_MACHINE_REGPATH, 0, // dwReserved
KEY_READ, &hKey )) return TRUE;
ILS_ReadDWORDValueFromRegistry( hKey, CERT_OCM_SUBCOMPONENTS_ROOT_AUTO_UPDATE_VALUE_NAME, &dwInstallFlag ); ILS_CloseRegistryKey(hKey);
return 0 == dwInstallFlag; }
//+-------------------------------------------------------------------------
// Gets the protected root information containing the list of protected
// root stores.
//
// If protected root store isn't supported, returns TRUE with
// *ppProtRootInfo set to NULL.
//--------------------------------------------------------------------------
BOOL CltGetProtectedRootInfo( OUT PPROT_ROOT_INFO *ppInfo ) { BOOL fResult; LONG err; HKEY hKeyCU = NULL;
*ppInfo = NULL;
#ifndef TESTING_NO_PROT_ROOT_RPC
if (!FIsWinNT5()) // No protected roots on Win9x or NT4.0
return TRUE; #endif
if (ERROR_SUCCESS != (err = RegOpenHKCU(&hKeyCU))) goto RegOpenHKCUError;
if (GetProtectedRootInfo( hKeyCU, KEY_READ, NULL, // phKeyProtRoot
ppInfo )) goto SuccessReturn;
if (!I_CertProtectFunction( CERT_PROT_INIT_ROOTS_FUNC_ID, 0, // dwFlags
NULL, // pwszIn
NULL, // pbIn
0, // cbIn
NULL, // ppbOut
NULL // pcbOut
)) { DWORD dwErr = GetLastError(); if (ERROR_CALL_NOT_IMPLEMENTED == dwErr || RPC_S_UNKNOWN_IF == dwErr) goto SuccessReturn; goto ProtFuncError; }
if (!GetProtectedRootInfo( hKeyCU, KEY_READ, NULL, // phKeyProtRoot
ppInfo )) goto GetProtectedRootInfoError;
SuccessReturn: fResult = TRUE; CommonReturn: if (hKeyCU) RegCloseHKCU(hKeyCU); return fResult; ErrorReturn: *ppInfo = NULL; fResult = FALSE; goto CommonReturn;
SET_ERROR_VAR(RegOpenHKCUError, err) TRACE_ERROR(GetProtectedRootInfoError) TRACE_ERROR(ProtFuncError) } //+-------------------------------------------------------------------------
// Initializes the protected list of roots.
//--------------------------------------------------------------------------
void IPR_InitProtectedRootInfo() { HKEY hKeyCU;
#ifndef TESTING_NO_PROT_ROOT_RPC
if (!FIsWinNT5()) // No protected roots on Win9x or NT4.0
return; #endif
if (ERROR_SUCCESS == RegOpenHKCU(&hKeyCU)) { HKEY hKeyProtRoot;
if (hKeyProtRoot = OpenProtectedRootSubKey(hKeyCU, KEY_READ)) // Protected root subkey exists
ILS_CloseRegistryKey(hKeyProtRoot); else { I_CertProtectFunction( CERT_PROT_INIT_ROOTS_FUNC_ID, 0, // dwFlags
NULL, // pwszIn
NULL, // pbIn
0, // cbIn
NULL, // ppbOut
NULL // pcbOut
); }
RegCloseHKCU(hKeyCU); } }
//+-------------------------------------------------------------------------
// Delete certificates not in the protected store list.
//--------------------------------------------------------------------------
BOOL IPR_DeleteUnprotectedRootsFromStore( IN HCERTSTORE hStore, OUT BOOL *pfProtected ) { PPROT_ROOT_INFO pInfo; PCCERT_CONTEXT pCert;
if (!CltGetProtectedRootInfo(&pInfo)) { *pfProtected = FALSE; // Delete all certificates from the store's cache.
while (pCert = CertEnumCertificatesInStore(hStore, NULL)) CertDeleteCertificateFromStore(pCert); return FALSE; }
if (NULL == pInfo) // Root store isn't protected.
*pfProtected = FALSE; else { *pfProtected = TRUE; pCert = NULL; while (pCert = CertEnumCertificatesInStore(hStore, pCert)) { BYTE rgbHash[PROT_ROOT_HASH_LEN]; if (!GetVerifiedCertHashProperty(pCert, rgbHash) || !FindProtectedRoot(pInfo, rgbHash)) { PCCERT_CONTEXT pDeleteCert = CertDuplicateCertificateContext(pCert); CertDeleteCertificateFromStore(pDeleteCert); } }
PkiFree(pInfo); } return TRUE; }
// Includes the title
#define MAX_PROT_ROOT_BOX_ITEMS 10
typedef struct _PROT_ROOT_BOX_ITEM { LPWSTR pwszItem; DWORD cchItem; } PROT_ROOT_BOX_ITEM;
// Returns count of items added
DWORD I_FormatRootBoxItems( IN PCCERT_CONTEXT pCert, IN UINT wActionID, IN OUT PROT_ROOT_BOX_ITEM rgItem[MAX_PROT_ROOT_BOX_ITEMS] ) { DWORD cItem = 0; DWORD cchTmp; LPWSTR pwszTmp;
// ACTION:
FormatMsgBoxItem(&rgItem[cItem].pwszItem, &rgItem[cItem].cchItem, wActionID); cItem++;
// SUBJECT
cchTmp = CertNameToStrW( pCert->dwCertEncodingType, &pCert->pCertInfo->Subject, CERT_SIMPLE_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, NULL, // pwsz
0); // cwsz
pwszTmp = (LPWSTR) PkiNonzeroAlloc(cchTmp * sizeof(WCHAR)); if (NULL != pwszTmp) CertNameToStrW( pCert->dwCertEncodingType, &pCert->pCertInfo->Subject, CERT_SIMPLE_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, pwszTmp, cchTmp); FormatMsgBoxItem(&rgItem[cItem].pwszItem, &rgItem[cItem].cchItem, IDS_ROOT_MSG_BOX_SUBJECT, NULL != pwszTmp ? pwszTmp : L""); cItem++; PkiFree(pwszTmp);
// ISSUER. May be self issued
if (CertCompareCertificateName( pCert->dwCertEncodingType, &pCert->pCertInfo->Subject, &pCert->pCertInfo->Issuer )) // Self issued
FormatMsgBoxItem(&rgItem[cItem].pwszItem, &rgItem[cItem].cchItem, IDS_ROOT_MSG_BOX_SELF_ISSUED); else { // Format certificate's issuer
cchTmp = CertNameToStrW( pCert->dwCertEncodingType, &pCert->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, NULL, // pwsz
0); // cwsz
pwszTmp = (LPWSTR) PkiNonzeroAlloc(cchTmp * sizeof(WCHAR)); if (NULL != pwszTmp) CertNameToStrW( pCert->dwCertEncodingType, &pCert->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, pwszTmp, cchTmp); FormatMsgBoxItem(&rgItem[cItem].pwszItem, &rgItem[cItem].cchItem, IDS_ROOT_MSG_BOX_ISSUER, NULL != pwszTmp ? pwszTmp : L"");
PkiFree(pwszTmp); } cItem++;
// TIME VALIDITY
{ FILETIME ftLocal; SYSTEMTIME stLocal; WCHAR wszNotBefore[128]; WCHAR wszNotAfter[128]; wszNotBefore[0] = '\0'; wszNotAfter[0] = '\0';
FileTimeToLocalFileTime(&pCert->pCertInfo->NotBefore, &ftLocal); FileTimeToSystemTime(&ftLocal, &stLocal); GetDateFormatU(LOCALE_USER_DEFAULT, DATE_LONGDATE, &stLocal, NULL, wszNotBefore, 128); FileTimeToLocalFileTime(&pCert->pCertInfo->NotAfter, &ftLocal); FileTimeToSystemTime(&ftLocal, &stLocal); GetDateFormatU(LOCALE_USER_DEFAULT, DATE_LONGDATE, &stLocal, NULL, wszNotAfter, 128);
FormatMsgBoxItem(&rgItem[cItem].pwszItem, &rgItem[cItem].cchItem, IDS_ROOT_MSG_BOX_TIME_VALIDITY, wszNotBefore, wszNotAfter); cItem++; }
// SERIAL NUMBER
if (pCert->pCertInfo->SerialNumber.cbData) { DWORD cb = pCert->pCertInfo->SerialNumber.cbData; BYTE *pb; if (pb = PkiAsn1AllocAndReverseBytes( pCert->pCertInfo->SerialNumber.pbData, cb)) { LPWSTR pwsz; if (pwsz = (LPWSTR) PkiNonzeroAlloc( (cb*2 + cb/4 + 1) * sizeof(WCHAR))) { FormatMsgBoxMultiBytes(cb, pb, pwsz); FormatMsgBoxItem(&rgItem[cItem].pwszItem, &rgItem[cItem].cchItem, IDS_ROOT_MSG_BOX_SERIAL_NUMBER, pwsz); cItem++; PkiFree(pwsz); } PkiAsn1Free(pb); } }
// THUMBPRINTS: sha1 and md5
{ BYTE rgbHash[MAX_HASH_LEN]; DWORD cbHash = MAX_HASH_LEN; WCHAR wszTmp[MAX_HASH_LEN * 3 + 1];
// get the sha1 thumbprint
if (CertGetCertificateContextProperty( pCert, CERT_SHA1_HASH_PROP_ID, rgbHash, &cbHash)) { FormatMsgBoxMultiBytes(cbHash, rgbHash, wszTmp); FormatMsgBoxItem(&rgItem[cItem].pwszItem, &rgItem[cItem].cchItem, IDS_ROOT_MSG_BOX_SHA1_THUMBPRINT, wszTmp); cItem++; }
// get the md5 thumbprint
if (CertGetCertificateContextProperty( pCert, CERT_MD5_HASH_PROP_ID, rgbHash, &cbHash)) { FormatMsgBoxMultiBytes(cbHash, rgbHash, wszTmp); FormatMsgBoxItem(&rgItem[cItem].pwszItem, &rgItem[cItem].cchItem, IDS_ROOT_MSG_BOX_MD5_THUMBPRINT, wszTmp); cItem++; } }
return cItem; }
// Returns count of items added
DWORD I_FormatAddRootBoxItems( IN PCCERT_CONTEXT pCert, IN OUT PROT_ROOT_BOX_ITEM rgItem[MAX_PROT_ROOT_BOX_ITEMS] ) { WCHAR wszIssuer[100];
BYTE rgbHash[MAX_HASH_LEN]; DWORD cbHash = MAX_HASH_LEN; WCHAR wszThumbprint[MAX_HASH_LEN * 3 + 1];
// Issuer Name
CertGetNameStringW( pCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, // dwFlags
NULL, // pvTypePara
wszIssuer, sizeof(wszIssuer) / sizeof(wszIssuer[0]) );
// sha1 Thumbprint
if (CertGetCertificateContextProperty( pCert, CERT_SHA1_HASH_PROP_ID, rgbHash, &cbHash)) FormatMsgBoxMultiBytes(cbHash, rgbHash, wszThumbprint); else wcscpy(wszThumbprint, L"???");
// Format the intro, body and end lines
FormatMsgBoxItem(&rgItem[0].pwszItem, &rgItem[0].cchItem, IDS_ADD_ROOT_MSG_BOX_INTRO, wszIssuer); FormatMsgBoxItem(&rgItem[1].pwszItem, &rgItem[1].cchItem, IDS_ADD_ROOT_MSG_BOX_BODY_0, wszIssuer); FormatMsgBoxItem(&rgItem[2].pwszItem, &rgItem[2].cchItem, IDS_ADD_ROOT_MSG_BOX_BODY_1, wszThumbprint); FormatMsgBoxItem(&rgItem[3].pwszItem, &rgItem[3].cchItem, IDS_ADD_ROOT_MSG_BOX_END_0); FormatMsgBoxItem(&rgItem[4].pwszItem, &rgItem[4].cchItem, IDS_ADD_ROOT_MSG_BOX_END_1);
return 5; }
//+-------------------------------------------------------------------------
// The add/delete root message box.
//
// If protected roots aren't supported, called from the client process.
// Otherwise, called from the services process.
//--------------------------------------------------------------------------
int IPR_ProtectedRootMessageBox( IN handle_t hRpc, IN PCCERT_CONTEXT pCert, IN UINT wActionID, IN UINT uFlags ) { int id;
PROT_ROOT_BOX_ITEM rgItem[MAX_PROT_ROOT_BOX_ITEMS]; DWORD cItem; LPWSTR pwszText = NULL; DWORD cchText = 0; DWORD ItemIdx;
if (wActionID == IDS_ROOT_MSG_BOX_ADD_ACTION) cItem = I_FormatAddRootBoxItems( pCert, rgItem ); else cItem = I_FormatRootBoxItems( pCert, wActionID, rgItem );
// Concatenate all the items into a single allocated string
for (ItemIdx = 0; ItemIdx < cItem; ItemIdx++) cchText += rgItem[ItemIdx].cchItem;
if (NULL != (pwszText = (LPWSTR) PkiNonzeroAlloc( (cchText + 1) * sizeof(WCHAR)))) { LPWSTR pwsz = pwszText; RPC_STATUS RpcStatus = 0;
for (ItemIdx = 0; ItemIdx < cItem; ItemIdx++) { DWORD cch = rgItem[ItemIdx].cchItem; if (cch) { assert(rgItem[ItemIdx].pwszItem); memcpy(pwsz, rgItem[ItemIdx].pwszItem, cch * sizeof(WCHAR)); pwsz += cch; } } assert (pwsz == pwszText + cchText); *pwsz = '\0';
// TITLE
FormatMsgBoxItem(&rgItem[cItem].pwszItem, &rgItem[cItem].cchItem, (IDS_ROOT_MSG_BOX_ADD_ACTION == wActionID) ? IDS_ADD_ROOT_MSG_BOX_TITLE : IDS_ROOT_MSG_BOX_TITLE);
// Do impersonation for TerminalServer clients
if (hRpc) RpcStatus = RpcImpersonateClient(hRpc); id = MessageBoxU( NULL, // hwndOwner
pwszText, rgItem[cItem].pwszItem, MB_TOPMOST | MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING | uFlags ); if (hRpc && ERROR_SUCCESS == RpcStatus) RpcRevertToSelf();
cItem++; PkiFree(pwszText); } else id = IDNO;
// Free up all the individually allocated items
while (cItem--) { if (rgItem[cItem].pwszItem) LocalFree((HLOCAL) rgItem[cItem].pwszItem); }
return id; }
//+=========================================================================
// crypt32 Event Logging Functions
//==========================================================================
#define MAX_CRYPT32_EVENT_LOG_STRINGS 5
#define MAX_CRYPT32_EVENT_LOG_COUNT 50
// 1 hour (in units of seconds)
#define CRYPT32_EVENT_LOG_THRESHOLD_PERIOD (60*60)
// Count of logged events. Gets reset whenever the interval between
// logged events >= CRYPT32_EVENT_LOG_THRESHOLD_PERIOD. If
// MAX_CRYPT32_EVENT_LOG_COUNT is reached, suspend logging for
// CRYPT32_EVENT_LOG_THRESHOLD_PERIOD.
DWORD dwCrypt32EventLogCnt;
// Time of last logged event.
FILETIME ftLastCrypt32EventLog;
// advapi32.dll Event APIs. Not supported on Win9x.
typedef HANDLE (WINAPI *PFN_REGISTER_EVENT_SOURCE_W)( IN LPCWSTR lpUNCServerName, IN LPCWSTR lpSourceName );
typedef BOOL (WINAPI *PFN_DEREGISTER_EVENT_SOURCE)( IN OUT HANDLE hEventLog );
typedef BOOL (WINAPI *PFN_REPORT_EVENT_W)( IN HANDLE hEventLog, IN WORD wType, IN WORD wCategory, IN DWORD dwEventID, IN PSID lpUserSid, IN WORD wNumStrings, IN DWORD dwDataSize, IN LPCWSTR *lpStrings, IN LPVOID lpRawData );
//+-------------------------------------------------------------------------
// Logs crypt32 events. Ensures we don't log more than
// MAX_CRYPT32_EVENT_LOG_COUNT events in any period of
// CRYPT32_EVENT_LOG_THRESHOLD_PERIOD seconds.
//
// Also, dynamically detects if event logging is supported by the version
// of advapi32.dll on the machine.
//--------------------------------------------------------------------------
STATIC BOOL LogCrypt32Event( IN WORD wType, IN WORD wCategory, IN DWORD dwEventID, IN WORD wNumStrings, IN DWORD dwDataSize, IN LPCWSTR *rgpwszStrings, IN BYTE *pbData ) { BOOL fResult; FILETIME ftCurrent; FILETIME ftNext; LONG lThreshold; HMODULE hModule; // No FreeLibary() for GetModuleHandle
DWORD dwExceptionCode; DWORD dwLastErr = 0;
PFN_REGISTER_EVENT_SOURCE_W pfnRegisterEventSourceW; PFN_REPORT_EVENT_W pfnReportEventW; PFN_DEREGISTER_EVENT_SOURCE pfnDeregisterEventSource;
// Check if we have exceeded the crypt32 event log threshold for
// this time period
//
// lThreshold:
// -1 - haven't reached it,
// 0 - reached it this time
// +1 - previously reached, won't log this event
lThreshold = -1; EnterCriticalSection(&Crypt32EventLogCriticalSection);
I_CryptIncrementFileTimeBySeconds(&ftLastCrypt32EventLog, CRYPT32_EVENT_LOG_THRESHOLD_PERIOD, &ftNext); GetSystemTimeAsFileTime(&ftCurrent);
if (0 <= CompareFileTime(&ftCurrent, &ftNext)) dwCrypt32EventLogCnt = 0; else if (MAX_CRYPT32_EVENT_LOG_COUNT <= dwCrypt32EventLogCnt) lThreshold = 1;
if (0 >= lThreshold) { ftLastCrypt32EventLog = ftCurrent; dwCrypt32EventLogCnt++; if (MAX_CRYPT32_EVENT_LOG_COUNT <= dwCrypt32EventLogCnt) lThreshold = 0; }
LeaveCriticalSection(&Crypt32EventLogCriticalSection);
if (0 < lThreshold) goto ExceededCrypt32EventLogThreshold;
// Only supported on systems where the event APIs are exported from
// advapi32.dll. Note, crypt32.dll has a static link dependency on
// advapi32.dll.
if (NULL == (hModule = GetModuleHandleA("advapi32.dll"))) goto GetAdvapi32ModuleError;
pfnRegisterEventSourceW = (PFN_REGISTER_EVENT_SOURCE_W) GetProcAddress(hModule, "RegisterEventSourceW"); pfnReportEventW = (PFN_REPORT_EVENT_W) GetProcAddress(hModule, "ReportEventW"); pfnDeregisterEventSource = (PFN_DEREGISTER_EVENT_SOURCE) GetProcAddress(hModule, "DeregisterEventSource");
if (NULL == pfnRegisterEventSourceW || NULL == pfnReportEventW || NULL == pfnDeregisterEventSource) goto Advapi32EventAPINotSupported;
fResult = TRUE; __try { HANDLE hEventLog;
hEventLog = pfnRegisterEventSourceW( NULL, // lpUNCServerName
L"crypt32" );
if (hEventLog) { if (!pfnReportEventW( hEventLog, wType, wCategory, dwEventID, NULL, // lpUserSid
wNumStrings, dwDataSize, rgpwszStrings, pbData )) { fResult = FALSE; dwLastErr = GetLastError(); }
I_DBLogCrypt32Event( wType, dwEventID, wNumStrings, rgpwszStrings );
if (0 == lThreshold) { WCHAR wszCnt[34]; WCHAR wszPeriod[34]; LPCWSTR rgpwszThresholdStrings[2] = {wszCnt, wszPeriod};
_ltow(MAX_CRYPT32_EVENT_LOG_COUNT, wszCnt, 10); _ltow(CRYPT32_EVENT_LOG_THRESHOLD_PERIOD / 60, wszPeriod, 10);
if (!pfnReportEventW( hEventLog, EVENTLOG_WARNING_TYPE, 0, // wCategory
MSG_CRYPT32_EVENT_LOG_THRESHOLD_WARNING, NULL, // lpUserSid
2, // wNumStrings
0, // dwDataSize
rgpwszThresholdStrings, NULL // pbData
)) { fResult = FALSE; dwLastErr = GetLastError(); }
I_DBLogCrypt32Event( EVENTLOG_WARNING_TYPE, MSG_CRYPT32_EVENT_LOG_THRESHOLD_WARNING, 2, // wNumStrings
rgpwszThresholdStrings ); }
pfnDeregisterEventSource(hEventLog); } else { fResult = FALSE; dwLastErr = GetLastError(); }
} __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; }
if (!fResult) goto ReportEventError;
CommonReturn: return fResult;
ErrorReturn: fResult = FALSE; goto CommonReturn;
SET_ERROR(ExceededCrypt32EventLogThreshold, ERROR_CAN_NOT_COMPLETE) TRACE_ERROR(GetAdvapi32ModuleError) SET_ERROR(Advapi32EventAPINotSupported, ERROR_PROC_NOT_FOUND) SET_ERROR_VAR(ExceptionError, dwExceptionCode) SET_ERROR_VAR(ReportEventError, dwLastErr) }
//+-------------------------------------------------------------------------
// Unmarshals the event logging parameter block passed to the service
// and call's the crypt32 event logging function with the unmarshalled
// parameters.
//
// __try/__except around memory access to pbIn.
//--------------------------------------------------------------------------
STATIC BOOL SrvLogCrypt32Event( IN BYTE *pbIn, IN DWORD cbIn ) { BOOL fResult; PCERT_PROT_EVENT_LOG_PARA pPara = NULL; BYTE *pbExtra; DWORD cbExtra; LPCWSTR rgpwszStrings[MAX_CRYPT32_EVENT_LOG_STRINGS]; LPCWSTR pwszStrings; DWORD cchStrings; WORD i; BYTE *pbData; DWORD dwExceptionCode;
if (sizeof(CERT_PROT_EVENT_LOG_PARA) > cbIn) goto InvalidArg;
// Ensure the PARA is aligned.
pPara = (PCERT_PROT_EVENT_LOG_PARA) PkiNonzeroAlloc(cbIn); if (NULL == pPara) goto OutOfMemory;
__try { memcpy(pPara, pbIn, cbIn); } __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; }
pbExtra = (BYTE *) &pPara[1]; cbExtra = cbIn - sizeof(CERT_PROT_EVENT_LOG_PARA);
// If present, create an array of pointers to the NULL terminated
// UNICODE strings that immediately follow the PARA structure.
if (MAX_CRYPT32_EVENT_LOG_STRINGS < pPara->wNumStrings) goto InvalidArg;
cchStrings = cbExtra / sizeof(WCHAR); // Maximum #, will be less if we
// also have binary data
pwszStrings = (LPCWSTR) pbExtra;
for (i = 0; i < pPara->wNumStrings; i++) { rgpwszStrings[i] = pwszStrings;
for ( ; cchStrings > 0; cchStrings--, pwszStrings++) { if (L'\0' == *pwszStrings) // Have NULL terminated string
break; }
if (0 == cchStrings) // Reached end without a NULL terminator
goto InvalidData;
// Advance past NULL terminator
cchStrings--; pwszStrings++; }
// The optional data immediately follows the above sequence of
// NULL terminated strings
pbData = (BYTE *) pwszStrings; assert(cbExtra >= (DWORD) (pbData - pbExtra)); if ((cbExtra - (pbData - pbExtra)) != pPara->dwDataSize) goto InvalidData;
fResult = LogCrypt32Event( pPara->wType, pPara->wCategory, pPara->dwEventID, pPara->wNumStrings, pPara->dwDataSize, 0 == pPara->wNumStrings ? NULL : rgpwszStrings, 0 == pPara->dwDataSize ? NULL : pbData );
CommonReturn: PkiFree(pPara); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
SET_ERROR(InvalidData, ERROR_INVALID_DATA) TRACE_ERROR(OutOfMemory) SET_ERROR_VAR(ExceptionError, dwExceptionCode) SET_ERROR(InvalidArg, E_INVALIDARG) }
//+-------------------------------------------------------------------------
// Marshals the event logging parameters and does an LRPC to the
// crypt32 service to do the event logging.
//
// Should only be called in the client.
//--------------------------------------------------------------------------
void IPR_LogCrypt32Event( IN WORD wType, IN DWORD dwEventID, IN WORD wNumStrings, IN LPCWSTR *rgpwszStrings ) { DWORD rgcchStrings[MAX_CRYPT32_EVENT_LOG_STRINGS]; LPWSTR pwszStrings; DWORD cchStrings; WORD i;
PCERT_PROT_EVENT_LOG_PARA pPara = NULL; DWORD cbPara;
// Get Strings character count
if (MAX_CRYPT32_EVENT_LOG_STRINGS < wNumStrings) goto InvalidArg;
cchStrings = 0; for (i = 0; i < wNumStrings; i++) { rgcchStrings[i] = wcslen(rgpwszStrings[i]) + 1; cchStrings += rgcchStrings[i]; }
// Create one serialized blob to be passed to the service. This will
// consist of the event log para struct followed immediately by the
// NULL terminated UNICODE strings
cbPara = sizeof(CERT_PROT_EVENT_LOG_PARA) + cchStrings * sizeof(WCHAR);
if (NULL == (pPara = (PCERT_PROT_EVENT_LOG_PARA) PkiZeroAlloc(cbPara))) goto OutOfMemory;
pPara->wType = wType; // pPara->wCategory = 0;
pPara->dwEventID = dwEventID; pPara->wNumStrings = wNumStrings; // pPara->wPad1 = 0;
// pPara->dwDataSize = 0;
pwszStrings = (LPWSTR) &pPara[1]; for (i = 0; i < wNumStrings; i++) { memcpy(pwszStrings, rgpwszStrings[i], rgcchStrings[i] * sizeof(WCHAR)); pwszStrings += rgcchStrings[i]; }
if (!I_CertProtectFunction( CERT_PROT_LOG_EVENT_FUNC_ID, 0, // dwFlags
NULL, // pwszIn
(BYTE *) pPara, cbPara, NULL, // ppbOut
NULL // pcbOut
)) goto ProtFuncError;
CommonReturn: PkiFree(pPara); return; ErrorReturn: goto CommonReturn;
SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(OutOfMemory) TRACE_ERROR(ProtFuncError) }
//+-------------------------------------------------------------------------
// Log a crypt32 error event
//
// Should only be called in the client.
//--------------------------------------------------------------------------
void IPR_LogCrypt32Error( IN DWORD dwEventID, IN LPCWSTR pwszString, // %1
IN DWORD dwErr // %2
) { WCHAR wszErr[80]; const DWORD cchErr = sizeof(wszErr) / sizeof(wszErr[0]); LPCWSTR rgpwszStrings[2]; DWORD cchFormatErr; LPWSTR pwszFormatErr = NULL;
cchFormatErr = FormatMessageU ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPWSTR) &pwszFormatErr, 0, NULL);
if (0 == cchFormatErr && INTERNET_ERROR_BASE <= dwErr && INTERNET_ERROR_LAST >= dwErr) { HMODULE hWininet;
hWininet = LoadLibraryEx( "wininet.dll", NULL, // reserved, must be NULL
LOAD_LIBRARY_AS_DATAFILE );
if (hWininet) { cchFormatErr = FormatMessageU ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE, hWininet, dwErr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPWSTR) &pwszFormatErr, 0, NULL);
FreeLibrary(hWininet); }
}
if (0 != cchFormatErr) rgpwszStrings[1] = pwszFormatErr; else { int cch = 0;
if (HTTP_STATUS_FIRST <= dwErr && HTTP_STATUS_LAST >= dwErr) { WCHAR wszFormat[64]; wszFormat[0] = '\0';
if (0 < LoadStringU(hRegStoreInst, IDS_HTTP_RESPONSE_STATUS, wszFormat, sizeof(wszFormat) / sizeof(wszFormat[0]))) { cch = _snwprintf(wszErr, cchErr - 1, L"%d (%s)", dwErr, wszFormat); } }
if (0 >= cch) cch = _snwprintf(wszErr, cchErr - 1, L"%d (0x%x)", dwErr, dwErr);
if (0 < cch) { wszErr[cchErr - 1] = L'\0'; rgpwszStrings[1] = wszErr; } else rgpwszStrings[1] = L"???"; }
rgpwszStrings[0] = pwszString;
IPR_LogCrypt32Event( EVENTLOG_ERROR_TYPE, dwEventID, 2, // wNumStrings
rgpwszStrings );
if (pwszFormatErr) LocalFree(pwszFormatErr); }
//+-------------------------------------------------------------------------
// Formats the cert's subject or issuer name string and SHA1 thumbprint.
//--------------------------------------------------------------------------
STATIC BOOL FormatLogCertInformation( IN PCCERT_CONTEXT pCert, IN BOOL fFormatIssuerName, OUT WCHAR wszSha1Hash[SHA1_HASH_LEN * 2 + 1], OUT LPWSTR *ppwszName ) { BOOL fResult; DWORD cchName; LPWSTR pwszName = NULL; DWORD cbData; BYTE rgbSha1Hash[SHA1_HASH_LEN];
PCERT_NAME_BLOB pNameBlob;
if (fFormatIssuerName) pNameBlob = &pCert->pCertInfo->Issuer; else pNameBlob = &pCert->pCertInfo->Subject;
cchName = CertNameToStrW( pCert->dwCertEncodingType, pNameBlob, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, NULL, // pwsz
0 // cwsz
);
if (NULL == (pwszName = (LPWSTR) PkiNonzeroAlloc(cchName * sizeof(WCHAR)))) goto OutOfMemory;
CertNameToStrW( pCert->dwCertEncodingType, pNameBlob, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, pwszName, cchName );
cbData = SHA1_HASH_LEN; if (CertGetCertificateContextProperty( pCert, CERT_SHA1_HASH_PROP_ID, rgbSha1Hash, &cbData ) && SHA1_HASH_LEN == cbData) ILS_BytesToWStr(SHA1_HASH_LEN, rgbSha1Hash, wszSha1Hash); else wcscpy(wszSha1Hash, L"???");
fResult = TRUE; CommonReturn: *ppwszName = pwszName; return fResult; ErrorReturn: if (pwszName) { PkiFree(pwszName); pwszName = NULL; } fResult = FALSE; goto CommonReturn;
TRACE_ERROR(OutOfMemory) }
//+-------------------------------------------------------------------------
// Get the cert's subject or issuer name string and SHA1 thumbprint. Logs the
// specified crypt32 event.
//
// This function is called from the client.
//--------------------------------------------------------------------------
void IPR_LogCertInformation( IN DWORD dwEventID, IN PCCERT_CONTEXT pCert, IN BOOL fFormatIssuerName ) { LPWSTR pwszName = NULL; WCHAR wszSha1Hash[SHA1_HASH_LEN * 2 + 1]; LPCWSTR rgpwszStrings[2];
if (!FormatLogCertInformation( pCert, fFormatIssuerName, wszSha1Hash, &pwszName )) return;
rgpwszStrings[0] = pwszName; rgpwszStrings[1] = wszSha1Hash; IPR_LogCrypt32Event( EVENTLOG_INFORMATION_TYPE, dwEventID, 2, // wNumStrings
rgpwszStrings );
PkiFree(pwszName); }
//+-------------------------------------------------------------------------
// Get the cert's subject name string and SHA1 thumbprint. Log the
// MSG_ROOT_AUTO_UPDATE_INFORMATIONAL crypt32 event.
//
// This function is called from the service.
//--------------------------------------------------------------------------
STATIC void LogAddAuthRootEvent( IN PCCERT_CONTEXT pCert ) { LPWSTR pwszName = NULL; WCHAR wszSha1Hash[SHA1_HASH_LEN * 2 + 1]; LPCWSTR rgpwszStrings[2];
if (!FormatLogCertInformation( pCert, FALSE, // fFormatIssuerName
wszSha1Hash, &pwszName )) return;
rgpwszStrings[0] = pwszName; rgpwszStrings[1] = wszSha1Hash; LogCrypt32Event( EVENTLOG_INFORMATION_TYPE, 0, // wCategory
MSG_ROOT_AUTO_UPDATE_INFORMATIONAL, 2, // wNumStrings
0, // dwDataSize
rgpwszStrings, NULL // pbData
);
PkiFree(pwszName); }
//+=========================================================================
// Install Cert into AuthRoot Auto Update CTL Functions
//==========================================================================
//+-------------------------------------------------------------------------
// Function that can be called from either the client or service to
// add the certificate to the specified store.
//
// First validates the CTL. The certificate must
// have an entry in the CTL before it will be added. The CTL entry's
// property attributes are set on the certificate context to be added.
//--------------------------------------------------------------------------
STATIC BOOL AddCertInCtlToStore( IN PCCERT_CONTEXT pCert, IN PCCTL_CONTEXT pCtl, IN OUT HCERTSTORE hStore ) { BOOL fResult; PCTL_ENTRY pCtlEntry;
if (!IRL_VerifyAuthRootAutoUpdateCtl(pCtl)) goto InvalidCtl;
if (NULL == (pCtlEntry = CertFindSubjectInCTL( pCert->dwCertEncodingType, CTL_CERT_SUBJECT_TYPE, (void *) pCert, pCtl, 0 // dwFlags
))) goto CertNotInCtl;
// Check if a remove entry
if (CertFindAttribute( szOID_REMOVE_CERTIFICATE, pCtlEntry->cAttribute, pCtlEntry->rgAttribute )) goto RemoveCertEntry;
// Set Ctl Entry Attribute properties
if (!CertSetCertificateContextPropertiesFromCTLEntry( pCert, pCtlEntry, CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG )) goto AddCtlEntryAttibutePropertiesError;
if (!CertAddCertificateContextToStore( hStore, pCert, CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, NULL // ppStoreContext
)) goto AddCertToStoreError;
LogAddAuthRootEvent(pCert);
fResult = TRUE;
CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(InvalidCtl) SET_ERROR(CertNotInCtl, CRYPT_E_NOT_FOUND) SET_ERROR(RemoveCertEntry, CRYPT_E_NOT_FOUND) TRACE_ERROR(AddCtlEntryAttibutePropertiesError) TRACE_ERROR(AddCertToStoreError) }
//+-------------------------------------------------------------------------
// Unmarshals the ASN.1 encoded X.509 certificate immediately followed by the
// ASN.1 encoded CTL.
//
// If the certificate has an entry in a valid CTL, its added to the
// HKLM "AuthRoot" store.
// __try/__except around memory access to pbIn.
//--------------------------------------------------------------------------
STATIC BOOL SrvAddCertInCtl( IN BYTE *pbIn, IN DWORD cbIn ) { BOOL fResult; DWORD cbCert; PCCERT_CONTEXT pCert = NULL; PCCTL_CONTEXT pCtl = NULL; HCERTSTORE hAuthRootStore = NULL; DWORD dwExceptionCode;
if (IPR_IsAuthRootAutoUpdateDisabled()) goto AuthRootAutoUpdateDisabledError;
__try { // The input consists of the encoded certificate immediately followed
// by the encoded CTL. Extract and create both components.
cbCert = Asn1UtilAdjustEncodedLength(pbIn, cbIn);
assert(cbCert <= cbIn);
if (NULL == (pCert = CertCreateCertificateContext( X509_ASN_ENCODING, pbIn, cbCert ))) goto CreateCertificateContextError;
if (NULL == (pCtl = CertCreateCTLContext( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbIn + cbCert, cbIn - cbCert ))) goto CreateCtlContextError;
} __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; }
if (NULL == (hAuthRootStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, // dwEncodingType
NULL, // hCryptProv
CERT_SYSTEM_STORE_LOCAL_MACHINE, (const void *) L"AuthRoot" ))) goto OpenAuthRootStoreError;
fResult = AddCertInCtlToStore( pCert, pCtl, hAuthRootStore );
CommonReturn: if (pCert) CertFreeCertificateContext(pCert); if (pCtl) CertFreeCTLContext(pCtl); if (hAuthRootStore) CertCloseStore(hAuthRootStore, 0);
return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
SET_ERROR(AuthRootAutoUpdateDisabledError, E_INVALIDARG) TRACE_ERROR(CreateCertificateContextError) TRACE_ERROR(CreateCtlContextError) SET_ERROR_VAR(ExceptionError, dwExceptionCode) TRACE_ERROR(OpenAuthRootStoreError) }
//+-------------------------------------------------------------------------
// For Pre W2K OS's that don't have a crypt32 service, do the add in
// client process.
//--------------------------------------------------------------------------
STATIC BOOL PreW2KAddCertInCtl( IN PCCERT_CONTEXT pCert, IN PCCTL_CONTEXT pCtl ) { BOOL fResult; HCERTSTORE hRootStore = NULL;
// Try opening the HKLM AuthRoot store. If that fails, fall back to
// adding to the HKCU Root (Unprotected) store
if (NULL == (hRootStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, // dwEncodingType
NULL, // hCryptProv
CERT_SYSTEM_STORE_LOCAL_MACHINE, (const void *) L"AuthRoot" ))) { if (NULL == (hRootStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, // dwEncodingType
NULL, // hCryptProv
CERT_SYSTEM_STORE_CURRENT_USER | CERT_SYSTEM_STORE_UNPROTECTED_FLAG, (const void *) L"Root" ))) goto OpenRootStoreError; }
fResult = AddCertInCtlToStore( pCert, pCtl, hRootStore );
CommonReturn: if (hRootStore) CertCloseStore(hRootStore, 0);
return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(OpenRootStoreError) }
//+-------------------------------------------------------------------------
// If the certificate has an entry in a valid CTL, its added to the
// HKLM "AuthRoot" store.
//--------------------------------------------------------------------------
BOOL IPR_AddCertInAuthRootAutoUpdateCtl( IN PCCERT_CONTEXT pCert, IN PCCTL_CONTEXT pCtl ) { BOOL fResult; DWORD cbIn; BYTE *pbIn = NULL;
// Create one serialized blob to be passed to the service. This will
// consist of the encoded certificate followed immediately by the
// encoded CTL.
cbIn = pCert->cbCertEncoded + pCtl->cbCtlEncoded;
if (NULL == (pbIn = (BYTE *) PkiNonzeroAlloc(cbIn))) goto OutOfMemory;
memcpy(pbIn, pCert->pbCertEncoded, pCert->cbCertEncoded); memcpy(pbIn + pCert->cbCertEncoded, pCtl->pbCtlEncoded, pCtl->cbCtlEncoded);
if (!I_CertProtectFunction( CERT_PROT_ADD_ROOT_IN_CTL_FUNC_ID, 0, // dwFlags
NULL, // pwszIn
pbIn, cbIn, NULL, // ppbOut
NULL // pcbOut
)) { DWORD dwErr = GetLastError(); if (ERROR_CALL_NOT_IMPLEMENTED == dwErr || RPC_S_UNKNOWN_IF == dwErr) { if (!PreW2KAddCertInCtl( pCert, pCtl )) goto PreW2KAddCertInCtlError; } else goto ProtFuncError; }
fResult = TRUE; CommonReturn: PkiFree(pbIn); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(OutOfMemory) TRACE_ERROR(PreW2KAddCertInCtlError) TRACE_ERROR(ProtFuncError) }
|