Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3273 lines
100 KiB

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