//+------------------------------------------------------------------------- // 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 #include #include #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> 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) }