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.
 
 
 
 
 
 

2462 lines
59 KiB

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: certsrv.cpp
//
// Contents: Cert Server main & debug support
//
// History: 25-Jul-96 vich created
//
//---------------------------------------------------------------------------
#include <pch.cpp>
#pragma hdrstop
#include <locale.h>
#include <io.h>
#include <fcntl.h>
#include <safeboot.h>
#include <authzi.h>
#include <common.ver>
#include "elog.h"
#include "certlog.h"
#include "certsrvd.h"
#include "resource.h"
#include "csresstr.h"
#define __dwFILE__ __dwFILE_CERTSRV_CERTSRV_CPP__
HKEY g_hkeyCABase = 0;
BOOL g_fCreateDB = FALSE;
BOOL g_fStartAsService = TRUE;
BOOL g_fStarted;
BOOL g_fStartInProgress;
DWORD g_ServiceThreadId;
HWND g_hwndMain;
WCHAR g_wszAppName[] = L"CertSrv";
HINSTANCE g_hInstApp;
DWORD g_dwDelay0;
DWORD g_dwDelay1;
DWORD g_dwDelay2;
DWORD g_CryptSilent = 0;
HANDLE g_hServiceThread = NULL;
HANDLE g_hShutdownEvent = NULL;
CRITICAL_SECTION g_ShutdownCriticalSection;
BOOL g_fShutdownCritSec = FALSE;
BOOL g_fRefuseIncoming = FALSE;
LONG g_cCalls = 0;
LONG g_cCallsActive = 0;
LONG g_cCallsActiveMax = 0;
BOOL g_fAdvancedServer = FALSE;
CAVIEW *g_pCAViewList = NULL;
DWORD g_cCAView = 0;
BOOL g_fCAViewForceCleanup = FALSE;
CAutoLPWSTR g_pwszDBFileHash;
SERVICE_TABLE_ENTRY steDispatchTable[] =
{
{ const_cast<WCHAR *>(g_wszCertSrvServiceName), ServiceMain },
{ NULL, NULL }
};
WCHAR const g_wszRegKeyClassesCLSID[] = L"SOFTWARE\\Classes\\CLSID";
WCHAR const g_wszRegKeyInprocServer32[] = L"InprocServer32";
WCHAR const g_wszRegValueThreadingModel[] = L"ThreadingModel";
WCHAR const g_wszRegKeyAppId[] = L"SOFTWARE\\Classes\\AppId";
WCHAR const g_wszRegRunAs[] = L"RunAs";
WCHAR const g_wszRegValueInteractiveUser[] = L"Interactive User";
WCHAR const g_wszRegLocalService[] = L"LocalService";
// do not change the order, add new audit resources at the end
// g_pwszAllow,
// g_pwszDeny,
// g_pwszCAAdmin,
// g_pwszOfficer,
// g_pwszRead,
// g_pwszEnroll,
LPCWSTR g_pwszAuditResources[6];
using namespace CertSrv;
HRESULT
OpenRegistryComKey(
IN HKEY hKeyParent,
IN CLSID const *pclsid,
IN BOOL fWrite,
OUT HKEY *phKey)
{
HRESULT hr;
WCHAR *pwsz = NULL;
*phKey = NULL;
hr = StringFromCLSID(*pclsid, &pwsz);
_JumpIfError(hr, error, "StringFromCLSID");
hr = RegOpenKeyEx(
hKeyParent,
pwsz,
0,
fWrite? KEY_ALL_ACCESS : KEY_READ,
phKey);
_JumpIfError(hr, error, "RegOpenKeyEx");
error:
if (NULL != pwsz)
{
CoTaskMemFree(pwsz);
}
return(hr);
}
BOOL
IsMissingRegistryValue(
IN HKEY hKey,
IN WCHAR const *pwszRegValueName)
{
HRESULT hr;
DWORD dwLen;
DWORD dwType;
hr = RegQueryValueEx(hKey, pwszRegValueName, NULL, &dwType, NULL, &dwLen);
if (S_OK != hr)
{
hr = myHError(hr);
}
_JumpIfError2(
hr,
error,
"RegQueryValueEx",
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
error:
return(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr);
}
BOOL
IsMatchingRegistryValue(
IN HKEY hKey,
IN WCHAR const *pwszRegValueName,
IN WCHAR const *pwszRegValueString)
{
HRESULT hr;
DWORD dwLen;
DWORD dwType;
BOOL fMatch = FALSE;
WCHAR buf[MAX_PATH];
dwLen = sizeof(buf);
hr = RegQueryValueEx(
hKey,
pwszRegValueName,
NULL,
&dwType,
(BYTE *) buf,
&dwLen);
_JumpIfErrorStr(hr, error, "RegQueryValueEx", pwszRegValueName);
if (REG_SZ == dwType && 0 == mylstrcmpiL(buf, pwszRegValueString))
{
fMatch = TRUE;
}
error:
return(fMatch);
}
HRESULT
SetRegistryStringValue(
IN HKEY hKey,
IN WCHAR const *pwszRegValueName,
IN WCHAR const *pwszRegValueString)
{
HRESULT hr;
hr = RegSetValueEx(
hKey,
pwszRegValueName,
0,
REG_SZ,
(const BYTE *) pwszRegValueString,
(wcslen(pwszRegValueString) + 1) * sizeof(WCHAR));
return(hr);
}
HRESULT
CertSrvSetRegistryFileTimeValue(
IN BOOL fConfigLevel,
IN WCHAR const *pwszRegValueName,
IN DWORD cpwszDelete,
OPTIONAL IN WCHAR const * const *papwszRegValueNameDelete)
{
HRESULT hr;
HKEY hKey = NULL;
HKEY hKey1 = NULL;
FILETIME ftCurrent;
DWORD i;
GetSystemTimeAsFileTime(&ftCurrent);
hr = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
wszREGKEYCONFIGPATH,
0,
KEY_ALL_ACCESS,
&hKey);
_JumpIfError(hr, error, "RegOpenKeyEx");
if (!fConfigLevel)
{
hKey1 = hKey;
hKey = NULL;
hr = RegOpenKeyEx(
hKey1,
g_wszSanitizedName,
0,
KEY_ALL_ACCESS,
&hKey);
_JumpIfError(hr, error, "RegOpenKeyEx");
}
hr = RegSetValueEx(
hKey,
pwszRegValueName,
0,
REG_BINARY,
(BYTE const *) &ftCurrent,
sizeof(ftCurrent));
_JumpIfError(hr, error, "RegSetValueEx");
for (i = 0; i < cpwszDelete; i++)
{
hr = RegDeleteValue(hKey, papwszRegValueNameDelete[i]);
_PrintIfError2(hr, "RegDeleteValue", ERROR_FILE_NOT_FOUND);
}
hr = S_OK;
error:
if (NULL != hKey1)
{
RegCloseKey(hKey1);
}
if (NULL != hKey)
{
RegCloseKey(hKey);
}
return(myHError(hr));
}
HRESULT
SetRegistryDcomConfig(
IN BOOL fConsoleActive)
{
HRESULT hr;
HKEY hKeyAppId = NULL;
HKEY hKeyRequest = NULL;
DWORD cChanged = 0;
hr = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
g_wszRegKeyAppId,
0,
KEY_ALL_ACCESS,
&hKeyAppId);
_JumpIfError(hr, error, "RegOpenKeyEx");
hr = OpenRegistryComKey(hKeyAppId, &CLSID_CCertRequestD, TRUE, &hKeyRequest);
_JumpIfError(hr, error, "OpenRegistryComKey");
if (fConsoleActive)
{
// Running in console mode:
// Delete both LocalService registry values
// Create both RunAs = InteractiveUser registry values
if (!IsMissingRegistryValue(hKeyRequest, g_wszRegLocalService))
{
cChanged++;
hr = RegDeleteValue(hKeyRequest, g_wszRegLocalService);
_JumpIfError(hr, error, "RegDeleteValue");
}
if (!IsMatchingRegistryValue(
hKeyRequest,
g_wszRegRunAs,
g_wszRegValueInteractiveUser))
{
cChanged++;
hr = SetRegistryStringValue(
hKeyRequest,
g_wszRegRunAs,
g_wszRegValueInteractiveUser);
_JumpIfError(hr, error, "SetRegistryStringValue");
}
if (0 != cChanged)
{
DBGPRINT((
DBG_SS_CERTSRV,
"SetRegistryDcomConfig(%u): setting %ws=%ws\n",
cChanged,
g_wszRegRunAs,
g_wszRegValueInteractiveUser));
}
}
else
{
// Running as a service:
// Delete both RunAs registry values
// Create both LocalService = CertSvc registry values
if (!IsMissingRegistryValue(hKeyRequest, g_wszRegRunAs))
{
cChanged++;
hr = RegDeleteValue(hKeyRequest, g_wszRegRunAs);
_JumpIfError(hr, error, "RegDeleteValue");
}
if (!IsMatchingRegistryValue(
hKeyRequest,
g_wszRegLocalService,
g_wszCertSrvServiceName))
{
cChanged++;
hr = SetRegistryStringValue(
hKeyRequest,
g_wszRegLocalService,
g_wszCertSrvServiceName);
_JumpIfError(hr, error, "SetRegistryStringValue");
}
if (0 != cChanged)
{
DBGPRINT((
DBG_SS_CERTSRV,
"SetRegistryDcomConfig(%u): setting %ws=%ws\n",
cChanged,
g_wszRegLocalService,
g_wszCertSrvServiceName));
}
}
error:
if (NULL != hKeyRequest)
{
RegCloseKey(hKeyRequest);
}
if (NULL != hKeyAppId)
{
RegCloseKey(hKeyAppId);
}
return(myHError(hr));
}
DWORD
GetRegistryDwordValue(
IN WCHAR const *pwszRegValueName)
{
HRESULT hr;
HKEY hKeyConfig = NULL;
DWORD dwVal;
DWORD dwType;
DWORD dwLen;
dwVal = 0;
hr = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
g_wszRegKeyConfigPath,
0,
KEY_READ,
&hKeyConfig);
_JumpIfError(hr, error, "RegOpenKeyEx");
dwLen = sizeof(dwVal);
hr = RegQueryValueEx(
hKeyConfig,
pwszRegValueName,
NULL,
&dwType,
(BYTE *) &dwVal,
&dwLen);
if (S_OK != hr || REG_DWORD != dwType || sizeof(dwVal) != dwLen)
{
dwVal = 0;
goto error;
}
error:
if (NULL != hKeyConfig)
{
RegCloseKey(hKeyConfig);
}
return(dwVal);
}
HRESULT
CertSrvResetRegistryWatch(
IN OUT HANDLE *phRegistryModified)
{
HRESULT hr;
CSASSERT(NULL != phRegistryModified);
//////////////////////////////////////
// Initialization of registry events
if (NULL == g_hkeyCABase)
{
DWORD dwDisposition;
LPWSTR pszCAPath;
pszCAPath = (LPWSTR) LocalAlloc(
LMEM_FIXED,
(WSZARRAYSIZE(wszREGKEYCONFIGPATH_BS) +
wcslen(g_wszSanitizedName) +
1) * sizeof(WCHAR));
if (NULL == pszCAPath)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
wcscpy(pszCAPath, wszREGKEYCONFIGPATH_BS);
wcscat(pszCAPath, g_wszSanitizedName);
hr = RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
pszCAPath,
0, // reserved
NULL, // class
0, // options
KEY_ALL_ACCESS, // sec desired
NULL, // sec attr
&g_hkeyCABase, // phk
&dwDisposition);
LocalFree(pszCAPath); pszCAPath = NULL;
_JumpIfError(hr, error, "RegCreateKeyEx base key");
}
if (NULL == *phRegistryModified)
{
*phRegistryModified = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == *phRegistryModified)
{
hr = myHLastError();
_JumpError(hr, error, "CreateEvent registry watch");
}
}
else
{
// reset registry event
ResetEvent( *phRegistryModified );
}
// register our registry lookout trigger
hr = RegNotifyChangeKeyValue(
g_hkeyCABase,
FALSE,
REG_NOTIFY_CHANGE_LAST_SET,
*phRegistryModified,
TRUE);
_JumpIfError(hr, error, "RegNotifyChangeKeyValue on base key");
error:
return(myHError(hr));
}
VOID
CertSrvLogOpen()
{
BOOL fOpenLog;
static BOOL s_fLogOpened = FALSE;
DbgPrintfInit("+"); // reinitialize debug print mask first
fOpenLog = DbgIsSSActive(DBG_SS_OPENLOG);
if (fOpenLog)
{
if (!s_fLogOpened)
{
DbgPrintfInit("+certsrv.log"); // open the log file
s_fLogOpened = TRUE;
DbgLogFileVersion("certsrv.exe", szCSVER_STR);
}
}
else
{
if (s_fLogOpened)
{
DbgPrintfInit("-"); // close the log file
s_fLogOpened = FALSE;
}
}
}
HRESULT
CertSrvRegistryModificationEvent(
IN FILETIME const *pftWait,
IN OUT DWORD *pdwTimeOut)
{
HRESULT hr;
DWORD dwVal;
BOOL fDisabledNew;
FILETIME ftCurrent;
BOOL fSetEvent = FALSE;
DWORD dwMSTimeOut;
CertSrvLogOpen(); // open log if registry changed to enable logging
// see if Base CRL publish enabled state has changed
hr = myGetCertRegDWValue(
g_wszSanitizedName,
NULL,
NULL,
wszREGCRLPERIODCOUNT,
&dwVal);
if (S_OK == hr)
{
fDisabledNew = 0 == dwVal;
if (fDisabledNew != g_fCRLPublishDisabled)
{
fSetEvent = TRUE;
}
}
// see if Delta CRL publish enabled state has changed
hr = myGetCertRegDWValue(
g_wszSanitizedName,
NULL,
NULL,
wszREGCRLDELTAPERIODCOUNT,
&dwVal);
if (S_OK == hr)
{
fDisabledNew = 0 == dwVal;
if (fDisabledNew != g_fDeltaCRLPublishDisabled)
{
fSetEvent = TRUE;
}
}
GetSystemTimeAsFileTime(&ftCurrent);
CRLComputeTimeOut(pftWait, &ftCurrent, &dwMSTimeOut);
if (dwMSTimeOut >= *pdwTimeOut)
{
dwMSTimeOut = *pdwTimeOut;
fSetEvent = TRUE;
}
*pdwTimeOut -= dwMSTimeOut;
if (fSetEvent)
{
SetEvent(g_hCRLManualPublishEvent); // pulse to get up-to-date
}
return(hr);
}
#if DBG_CERTSRV
WCHAR const *
certsrvGetCurrentTimeWsz()
{
HRESULT hr;
FILETIME ft;
WCHAR *pwszTime = NULL;
static WCHAR s_wszTime[128];
GetSystemTimeAsFileTime(&ft);
hr = myGMTFileTimeToWszLocalTime(&ft, TRUE, &pwszTime);
_PrintIfError(hr, "myGMTFileTimeToWszLocalTime");
s_wszTime[0] = L'\0';
if (NULL != pwszTime)
{
wcsncpy(s_wszTime, pwszTime, ARRAYSIZE(s_wszTime));
s_wszTime[ARRAYSIZE(s_wszTime) - 1] = L'\0';
LocalFree(pwszTime);
}
return(s_wszTime);
}
#endif
HRESULT
CertSrvBlockThreadUntilStop()
{
HRESULT hr;
HANDLE hRegistryModified = NULL;
DWORD dwTimeOut;
// check CRL publish, get next timeout interval
hr = CRLPubWakeupEvent(&dwTimeOut);
_PrintIfError(hr, "CRLPubWakeupEvent");
hr = CertSrvResetRegistryWatch(&hRegistryModified);
_PrintIfError(hr, "CertSrvResetRegistryWatch");
for (;;)
{
FILETIME ftWait;
DWORD dw;
HANDLE hmultiObjects[] = {
hRegistryModified,
g_hServiceStoppingEvent,
g_hCRLManualPublishEvent
};
#if DBG_CERTSRV
{
LLFILETIME llft;
WCHAR *pwszTimePeriod = NULL;
llft.ll = dwTimeOut;
llft.ll *= (CVT_BASE / 1000); // convert msecs to 100ns
llft.ll = -llft.ll;
hr = myFileTimePeriodToWszTimePeriod(
&llft.ft,
TRUE, // fExact
&pwszTimePeriod);
_PrintIfError(hr, "myFileTimePeriodToWszTimePeriod");
DBGPRINT((
DBG_SS_CERTSRV,
"WaitForMultipleObjects(%u ms) %ws @%ws\n",
dwTimeOut,
pwszTimePeriod,
certsrvGetCurrentTimeWsz()));
if (NULL != pwszTimePeriod)
{
LocalFree(pwszTimePeriod);
}
}
#endif
GetSystemTimeAsFileTime(&ftWait);
dw = WaitForMultipleObjects(
ARRAYSIZE(hmultiObjects),
hmultiObjects,
FALSE, // any object will cause bailout
dwTimeOut);
DBGPRINT((
DBG_SS_CERTSRV,
"WaitForMultipleObjects(%u ms)->%x, %ws\n",
dwTimeOut,
dw,
certsrvGetCurrentTimeWsz()));
if (WAIT_FAILED == dw)
{
hr = GetLastError();
_JumpError(hr, error, "WaitForMultipleObjects worker");
}
if (dw == WAIT_TIMEOUT) // CRL
{
hr = CRLPubWakeupEvent(&dwTimeOut);
_PrintIfError(hr, "Error during CRLPubWakeupEvent");
DBGPRINT((DBG_SS_CERTSRVI, "CRLPub: TimeOut %u ms\n", dwTimeOut));
}
else if (dw == WAIT_OBJECT_0) // Registry modification
{
// In either case, determine if CRL needs to be published
hr = CertSrvRegistryModificationEvent(&ftWait, &dwTimeOut);
_PrintIfError(hr, "Error during CertSrvRegistryModificationEvent");
// in registry case, reset registry trigger
DBGPRINT((
DBG_SS_CERTSRVI,
"CRLPub: Registry change trigger, TimeOut=%u ms\n",
dwTimeOut));
hr = CertSrvResetRegistryWatch(&hRegistryModified);
_PrintIfError(hr, "Error during CertSrvResetRegistryWatch");
}
else if (dw == WAIT_OBJECT_0 + 1)
{
// found "service done" event
DBGPRINT((DBG_SS_CERTSRV, "Service is pending stop request\n"));
break; // exit wait loop
}
else if (dw == WAIT_OBJECT_0 + 2)
{
// found "g_hCRLManualPublishEvent" event: recalc timeout
hr = CRLPubWakeupEvent(&dwTimeOut);
_PrintIfError(hr, "Error during CRLPubWakeupEvent");
DBGPRINT((
DBG_SS_CERTSRVI,
"CRLPub: Manual publish recalc, TimeOut=%u ms\n",
dwTimeOut));
}
else
{
CSASSERT(CSExpr(!"unexpected wait return"));
hr = E_UNEXPECTED;
_JumpError(hr, error, "WaitForMultipleObjects");
}
}
hr = S_OK;
error:
CloseHandle(hRegistryModified);
return hr;
}
HRESULT certsrvGetCACertAndKeyHash(
OUT WCHAR **ppwszCertHash,
OUT WCHAR **ppwszPublicKeyHash)
{
HRESULT hr;
WCHAR wszCertHash[CBMAX_CRYPT_HASH_LEN * 3]; // 20 bytes @ 3 WCHARs/byte
DWORD cbCertHashStr;
WCHAR wszPublicKeyHash[CBMAX_CRYPT_HASH_LEN * 3];
DWORD cbPublicKeyHashStr;
BYTE abCertHash[CBMAX_CRYPT_HASH_LEN];
DWORD cbCertHash;
CAutoPBYTE autopbPublicKeyHash;
DWORD cbPublicKeyHash;
*ppwszCertHash = NULL;
*ppwszPublicKeyHash = NULL;
cbCertHash = sizeof(abCertHash);
if (!CertGetCertificateContextProperty(
g_pCAContextCurrent->pccCA,
CERT_SHA1_HASH_PROP_ID,
abCertHash,
&cbCertHash))
{
hr = myHLastError();
_JumpError(hr, error, "CertGetCertificateContextProperty");
}
cbCertHashStr = sizeof(wszCertHash);
hr = MultiByteIntegerToWszBuf(
TRUE, // byte multiple
cbCertHash,
abCertHash,
&cbCertHashStr,
wszCertHash);
_JumpIfError(hr, error, "MultiByteIntegerToWszBuf");
hr = myGetPublicKeyHash(
g_pCAContextCurrent->pccCA->pCertInfo,
&g_pCAContextCurrent->pccCA->pCertInfo->SubjectPublicKeyInfo,
&autopbPublicKeyHash,
&cbPublicKeyHash);
_JumpIfError(hr, error, "myGetPublicKeyHash");
cbPublicKeyHashStr = sizeof(wszPublicKeyHash);
hr = MultiByteIntegerToWszBuf(
TRUE, // byte multiple
cbPublicKeyHash,
autopbPublicKeyHash,
&cbPublicKeyHashStr,
wszPublicKeyHash);
_JumpIfError(hr, error, "MultiByteIntegerToWszBuf");
hr = myDupString(wszCertHash, ppwszCertHash);
_JumpIfError(hr, error, "myDupString");
hr = myDupString(wszPublicKeyHash, ppwszPublicKeyHash);
_JumpIfError(hr, error, "myDupString");
error:
return hr;
}
#define CSECSLEEP 2 // time to sleep each time through the loop
#define CSECSLEEPTOTAL 30 // total time to wait before giving up
#define wsz3QM L"???" // audit data collection failure placeholder
HRESULT
CertSrvAuditShutdown(
IN ULARGE_INTEGER *puliKeyUsageCount,
IN WCHAR const *pwszCertHash,
IN WCHAR const *pwszPublicKeyHash)
{
HRESULT hr;
HRESULT hr2;
DWORD i;
WCHAR const *pwsz;
CertSrv::CAuditEvent event(
SE_AUDITID_CERTSRV_SERVICESTOP,
g_dwAuditFilter);
hr = S_OK;
for (i = 0; i < CSECSLEEPTOTAL / CSECSLEEP; i++)
{
g_pwszDBFileHash.Cleanup();
hr = myComputeMAC(g_wszDatabase, &g_pwszDBFileHash);
if (S_OK == hr || HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) != hr)
{
break;
}
_PrintError(hr, "myComputeMAC");
Sleep(CSECSLEEP * 1000);
}
_PrintIfErrorStr(hr, "myComputeMAC", g_wszDatabase);
hr2 = hr; // save first error
// %1 database hash
pwsz = g_pwszDBFileHash; // avoid CAutoLPWSTR freeing static wsz3QM string!
hr = event.AddData(pwsz != NULL? pwsz : wsz3QM);
g_pwszDBFileHash.Cleanup();
_JumpIfError(hr, error, "CAuditEvent::AddData");
// %2 key usage count
hr = event.AddData(puliKeyUsageCount);
_JumpIfError(hr, error, "CAuditEvent::AddData");
// %3 CA cert hash
hr = event.AddData(NULL != pwszCertHash? pwszCertHash : wsz3QM);
_JumpIfError(hr, error, "CAuditEvent::AddData");
// %4 CA public key hash
hr = event.AddData(NULL != pwszPublicKeyHash? pwszPublicKeyHash : wsz3QM);
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.Report();
_JumpIfError(hr, error, "CAuditEvent::Report");
error:
if (S_OK != hr2)
{
hr = hr2; // return first error
}
return(hr);
}
// returns TRUE if we shutdown correctly
BOOL
CertSrvStopServer(
IN BOOL fConsoleActive)
{
HRESULT hr;
BOOL fCoInit = FALSE;
BOOL fShutDown = FALSE;
ULARGE_INTEGER uliKeyUsageCount;
CAutoLPWSTR autoszCertHash;
CAutoLPWSTR autoszPublicKeyHash;
if (!g_fStartInProgress) // ignore while starting the server
{
fShutDown = TRUE;
DBGPRINT((
DBG_SS_CERTSRV,
"CertSrvStopServer(fConsoleActive=%u, tid=%d)\n",
fConsoleActive,
GetCurrentThreadId()));
SetEvent(g_hServiceStoppingEvent);
if (g_hkeyCABase)
{
RegCloseKey(g_hkeyCABase);
g_hkeyCABase = NULL;
}
hr = CoInitializeEx(NULL, GetCertsrvComThreadingModel());
if (S_OK != hr && S_FALSE != hr)
{
_JumpError(hr, error, "CoInitializeEx");
}
fCoInit = TRUE;
// don't allow new callers in
if (0 == (IF_NORPCICERTREQUEST & g_InterfaceFlags))
{
hr = RPCTeardown();
_PrintIfError(hr, "RPCTeardown");
}
CertStopClassFactories();
// retrieve private key usage count if auditing enabled
if (AUDIT_FILTER_STARTSTOP & g_dwAuditFilter)
{
BOOL fSupported;
BOOL fEnabled;
uliKeyUsageCount.QuadPart = 0;
hr = myGetSigningKeyUsageCount(
g_pCAContextCurrent->hProvCA,
&fSupported,
&fEnabled,
&uliKeyUsageCount);
_PrintIfError(hr, "myGetSigningKeyUsageCount");
hr = certsrvGetCACertAndKeyHash(
&autoszCertHash,
&autoszPublicKeyHash);
_PrintIfError(hr, "certsrvGetCACertAndKeyHash");
}
CoreTerminate();
if (g_fStarted)
{
if (CERTLOG_TERSE <= g_dwLogLevel)
{
LogEventString(
EVENTLOG_INFORMATION_TYPE,
MSG_I_SERVER_STOPPED,
g_wszCommonName);
}
CONSOLEPRINT0((
DBG_SS_CERTSRV,
"Certification Authority Service Stopped\n"));
// only perform Hash if the auditing is enabled
if (AUDIT_FILTER_STARTSTOP & g_dwAuditFilter)
{
hr = CertSrvAuditShutdown(
&uliKeyUsageCount,
autoszCertHash,
autoszPublicKeyHash);
_PrintError(hr, "CertSrvAuditShutdown");
}
}
g_fStarted = FALSE;
AuthzFreeResourceManager(g_AuthzCertSrvRM);
g_AuthzCertSrvRM = NULL;
g_CASD.Uninitialize();
g_OfficerRightsSD.Uninitialize();
// set "completely stopped" event
if (!fConsoleActive)
{
SetEvent(g_hServiceStoppedEvent);
}
}
error:
if (fCoInit)
{
CoUninitialize();
}
return(fShutDown);
}
// Control-C handler
BOOL
StopServer(
IN DWORD /* dwCtrlType */ )
{
HRESULT hr;
// if successful shutdown
if (SendMessage(g_hwndMain, WM_STOPSERVER, 0, 0))
{
if (!PostMessage(g_hwndMain, WM_SYNC_CLOSING_THREADS, S_OK, 0))
{
hr = myHLastError();
_PrintError(hr, "PostMessage");
}
SetConsoleCtrlHandler(StopServer, FALSE);
}
return(TRUE);
}
VOID
ReleaseOldViews()
{
if (0 < g_cCAView)
{
FILETIME ftTooOld;
FILETIME ftTooIdle;
CAVIEW *pCAView;
CAVIEW **ppCAViewLast;
GetSystemTimeAsFileTime(&ftTooIdle);
ftTooOld = ftTooIdle;
myMakeExprDateTime(
&ftTooOld,
-(LONG) g_dwViewAgeMinutes,
ENUM_PERIOD_MINUTES);
myMakeExprDateTime(
&ftTooIdle,
-(LONG) g_dwViewIdleMinutes,
ENUM_PERIOD_MINUTES);
ppCAViewLast = &g_pCAViewList;
pCAView = g_pCAViewList;
for (;;)
{
if (NULL == pCAView)
{
break;
}
//CERTSRVDBGPRINTTIME("ftTooOld", &ftTooOld);
//CERTSRVDBGPRINTTIME("ftCreate", &pCAView->ftCreate);
//CERTSRVDBGPRINTTIME("ftTooIdle", &ftTooIdle);
//CERTSRVDBGPRINTTIME("ftLastAccess", &pCAView->ftLastAccess);
if (g_fCAViewForceCleanup ||
g_fRefuseIncoming ||
0 < CompareFileTime(&ftTooOld, &pCAView->ftCreate) ||
0 < CompareFileTime(&ftTooIdle, &pCAView->ftLastAccess))
{
CAVIEW *pCAViewFree;
// Release this view, then Delink and free the list element.
DBGPRINT((
DBG_SS_CERTSRV,
"ReleaseOldViews(%u: Force=%u Refuse=%u old=%u idle=%u pv=%p View=%p)\n",
g_cCAView,
g_fCAViewForceCleanup,
g_fRefuseIncoming,
0 < CompareFileTime(&ftTooOld, &pCAView->ftCreate),
0 < CompareFileTime(&ftTooIdle, &pCAView->ftLastAccess),
pCAView->pvSearch,
pCAView->pView));
pCAViewFree = pCAView;
*ppCAViewLast = pCAView->pCAViewNext;
pCAView = pCAView->pCAViewNext;
pCAViewFree->pView->Release();
LocalFree(pCAViewFree);
g_cCAView--;
}
else
{
ppCAViewLast = &pCAView->pCAViewNext;
pCAView = pCAView->pCAViewNext;
}
}
g_fCAViewForceCleanup = FALSE;
}
}
HRESULT
CertSrvEnterServer(
OUT DWORD *pState)
{
HRESULT hr;
BOOL fEntered = FALSE;
*pState = 0; // Caller need not exit server
if (!g_fShutdownCritSec)
{
hr = HRESULT_FROM_WIN32(ERROR_DLL_INIT_FAILED);
_JumpError(hr, error, "InitializeCriticalSection");
}
EnterCriticalSection(&g_ShutdownCriticalSection);
fEntered = TRUE;
hr = CertSrvTestServerState();
_JumpIfError(hr, error, "CertSrvTestServerState");
g_cCalls++;
g_cCallsActive++;
if (g_cCallsActiveMax < g_cCallsActive)
{
g_cCallsActiveMax = g_cCallsActive;
}
*pState = 1; // Caller must exit server
hr = S_OK;
error:
if (fEntered)
{
LeaveCriticalSection(&g_ShutdownCriticalSection);
}
return(hr);
}
HRESULT
CertSrvTestServerState()
{
HRESULT hr;
if (g_fRefuseIncoming)
{
hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS);
_JumpError(hr, error, "g_fRefuseIncoming");
}
hr = S_OK;
error:
return(hr);
}
HRESULT
CertSrvLockServer(
IN OUT DWORD *pState)
{
HRESULT hr;
BOOL fEntered = FALSE;
// Eliminate this thread from the active thread count
CertSrvExitServer(*pState, S_OK);
*pState = 0; // Caller no longer needs to exit server
if (!g_fShutdownCritSec)
{
hr = HRESULT_FROM_WIN32(ERROR_DLL_INIT_FAILED);
_JumpError(hr, error, "InitializeCriticalSection");
}
EnterCriticalSection(&g_ShutdownCriticalSection);
fEntered = TRUE;
g_fRefuseIncoming = TRUE;
ReleaseOldViews();
hr = DBShutDown(TRUE);
_PrintIfError(hr, "DBShutDown");
DBGPRINT((DBG_SS_CERTSRV, "LockServer(thread count = %u)\n", g_cCallsActive));
while (0 < g_cCallsActive)
{
LONG cCalls = g_cCallsActive;
LeaveCriticalSection(&g_ShutdownCriticalSection);
// Wait 15 seconds plus 2 seconds for each active call.
hr = WaitForSingleObject(g_hShutdownEvent, (15 + 2 * cCalls) * 1000);
EnterCriticalSection(&g_ShutdownCriticalSection);
_PrintIfError(hr, "WaitForSingleObject");
if ((HRESULT) WAIT_OBJECT_0 == hr)
{
DBGPRINT((DBG_SS_CERTSRV, "LockServer(last thread exit event)\n"));
}
else if ((HRESULT) WAIT_TIMEOUT == hr)
{
DBGPRINT((DBG_SS_CERTSRV, "LockServer(timeout)\n"));
if (cCalls <= g_cCallsActive)
{
break; // no reduction in active threads -- abort anyway
}
}
else if ((HRESULT) WAIT_ABANDONED == hr)
{
DBGPRINT((DBG_SS_CERTSRV, "LockServer(wait abandoned)\n"));
}
DBGPRINT((DBG_SS_CERTSRV, "LockServer(thread count = %u)\n", g_cCallsActive));
}
DBGPRINT((DBG_SS_CERTSRV, "LockServer(done: thread count = %u)\n", g_cCallsActive));
hr = S_OK;
error:
if (fEntered)
{
LeaveCriticalSection(&g_ShutdownCriticalSection);
}
return(hr);
}
VOID
CertSrvExitServer(
IN DWORD State,
IN HRESULT hrExit)
{
HRESULT hr;
BOOL fEntered = FALSE;
if (S_OK != hrExit && g_hrJetVersionStoreOutOfMemory == hrExit)
{
g_fCAViewForceCleanup = TRUE;
}
if (!g_fShutdownCritSec)
{
hr = HRESULT_FROM_WIN32(ERROR_DLL_INIT_FAILED);
_JumpError(hr, error, "InitializeCriticalSection");
}
EnterCriticalSection(&g_ShutdownCriticalSection);
fEntered = TRUE;
ReleaseOldViews();
if (State)
{
CSASSERT(0 < g_cCallsActive);
if (0 == --g_cCallsActive && g_fRefuseIncoming)
{
DBGPRINT((DBG_SS_CERTSRV, "ExitServer(set last thread exit event)\n"));
SetEvent(g_hShutdownEvent);
}
}
error:
if (fEntered)
{
LeaveCriticalSection(&g_ShutdownCriticalSection);
}
}
HRESULT
CertSrvDelinkCAView(
IN VOID *pvSearch,
OPTIONAL OUT CAVIEW **ppCAViewOut)
{
HRESULT hr;
BOOL fEntered = FALSE;
CAVIEW *pCAView;
CAVIEW **ppCAViewLast;
if (NULL != ppCAViewOut)
{
*ppCAViewOut = NULL;
}
if (!g_fShutdownCritSec)
{
hr = HRESULT_FROM_WIN32(ERROR_DLL_INIT_FAILED);
_JumpError(hr, error, "InitializeCriticalSection");
}
EnterCriticalSection(&g_ShutdownCriticalSection);
fEntered = TRUE;
ppCAViewLast = &g_pCAViewList;
pCAView = g_pCAViewList;
for (;;)
{
if (NULL == pCAView)
{
hr = E_HANDLE;
_JumpError2(hr, error, "pvSearch not in list", hr);
}
if (pvSearch == pCAView->pvSearch)
{
break;
}
ppCAViewLast = &pCAView->pCAViewNext;
pCAView = pCAView->pCAViewNext;
}
if (NULL != ppCAViewOut)
{
*ppCAViewLast = pCAView->pCAViewNext;
pCAView->pCAViewNext = NULL;
*ppCAViewOut = pCAView;
g_cCAView--;
}
hr = S_OK;
error:
if (fEntered)
{
LeaveCriticalSection(&g_ShutdownCriticalSection);
}
return(hr);
}
HRESULT
CertSrvLinkCAView(
IN BOOL fNew,
IN VOID *pvSearch,
IN CAVIEW *pCAViewIn)
{
HRESULT hr;
BOOL fEntered = FALSE;
GetSystemTimeAsFileTime(&pCAViewIn->ftLastAccess);
if (fNew)
{
pCAViewIn->ftCreate = pCAViewIn->ftLastAccess;
pCAViewIn->pvSearch = pvSearch;
}
else
{
CSASSERT(pCAViewIn->pvSearch == pvSearch);
}
if (!g_fShutdownCritSec)
{
hr = HRESULT_FROM_WIN32(ERROR_DLL_INIT_FAILED);
_JumpError(hr, error, "InitializeCriticalSection");
}
EnterCriticalSection(&g_ShutdownCriticalSection);
fEntered = TRUE;
pCAViewIn->pCAViewNext = g_pCAViewList;
g_pCAViewList = pCAViewIn;
g_cCAView++;
hr = S_OK;
error:
if (fEntered)
{
LeaveCriticalSection(&g_ShutdownCriticalSection);
}
return(hr);
}
// Test for alignment faults in the C runtimes.
// If the bug hasn't been fixed yet, log an event during cert server startup.
VOID
certsrvLogAlignmentFaultStatus()
{
HRESULT hr;
HRESULT hr2;
ULONG_PTR ExceptionAddress;
WCHAR awcAddress[2 + 2 * cwcDWORDSPRINTF];
WCHAR const *apwsz[2];
WORD cpwsz;
WCHAR awchr[cwcHRESULTSTRING];
WCHAR const *pwszStringErr = NULL;
ExceptionAddress = 0;
apwsz[1] = NULL;
hr = S_OK;
__try
{
fwprintf(stdout, L"."); // may fault if I/O buffer is odd aligned
fprintf(stdout, ".");
fwprintf(stdout, L".\n"); // may fault if I/O buffer is odd aligned
hr = S_OK;
}
__except(
ExceptionAddress = (ULONG_PTR) (GetExceptionInformation())->ExceptionRecord->ExceptionAddress,
hr = myHEXCEPTIONCODE(),
EXCEPTION_EXECUTE_HANDLER)
{
_PrintError(hr, "certsrvLogAlignmentFaultStatus: Exception");
}
if (S_OK != hr)
{
ALIGNIOB(stdout); // align the stdio buffer
wprintf(L"STDIO exception: 0x%x\n", hr);
wsprintf(awcAddress, L"0x%p", (VOID *) ExceptionAddress);
CSASSERT(wcslen(awcAddress) < ARRAYSIZE(awcAddress));
apwsz[0] = awcAddress;
pwszStringErr = myGetErrorMessageText(hr, TRUE);
apwsz[1] = pwszStringErr;
if (NULL == pwszStringErr)
{
apwsz[1] = myHResultToString(awchr, hr);
}
cpwsz = ARRAYSIZE(apwsz);
hr2 = LogEvent(
EVENTLOG_WARNING_TYPE,
MSG_E_STARTUP_EXCEPTION,
cpwsz,
apwsz);
_JumpIfError(hr2, error, "LogEvent");
}
error:
if (NULL != pwszStringErr)
{
LocalFree(const_cast<WCHAR *>(pwszStringErr));
}
}
#define MSTOSEC(ms) (((ms) + 1000 - 1)/1000)
FNLOGEXCEPTION certsrvLogException;
HRESULT
certsrvStartServer(
IN BOOL fConsoleActive)
{
HRESULT hr;
DWORD TimeStart;
WCHAR awc[ARRAYSIZE(SAFEBOOT_DSREPAIR_STR_W)];
DWORD cwc;
DWORD dwEventType = EVENTLOG_ERROR_TYPE;
DWORD dwIdEvent = 0;
bool fAuditPrivilegeEnabled = false;
BOOL fAuditEnabled = FALSE;
WCHAR const *pwszDC0;
g_fStartInProgress = TRUE;
DBGPRINT((
DBG_SS_CERTSRV,
"StartServer(tid=%d, fConsoleActive=%u)\n",
GetCurrentThreadId(),
fConsoleActive));
TimeStart = GetTickCount();
if (fConsoleActive)
{
g_fStartAsService = FALSE;
SetConsoleCtrlHandler(StopServer, TRUE);
}
else
{
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
}
if (!FIsServer())
{
// don't allow startup on non-server SKU
hr = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
_JumpError(hr, error, "FIsServer");
}
cwc = GetEnvironmentVariable(L"SAFEBOOT_OPTION", awc, ARRAYSIZE(awc));
if (0 != cwc &&
ARRAYSIZE(awc) > cwc &&
0 == LSTRCMPIS(awc, SAFEBOOT_DSREPAIR_STR_W))
{
// log an error to the event log and stop immediately
dwEventType = EVENTLOG_INFORMATION_TYPE;
dwIdEvent = MSG_SAFEBOOT_DETECTED;
hr = HRESULT_FROM_WIN32(ERROR_RETRY);
_JumpError(hr, error, "Not starting service: booted in DSRepair mode");
}
g_fAdvancedServer = FIsAdvancedServer();
if (fConsoleActive)
{
hr = myEnablePrivilege(SE_AUDIT_NAME, TRUE);
if (S_OK != hr)
{
_PrintError(hr, "myEnablePrivilege(SE_AUDIT_NAME)");
if (E_ACCESSDENIED != hr || 2 > g_fAdvancedServer)
{
goto error;
}
}
else
{
fAuditPrivilegeEnabled = true;
}
}
if (!AuthzInitializeResourceManager(
0,
CallbackAccessCheck,
NULL,
NULL,
L"CertSrv",
&g_AuthzCertSrvRM))
{
hr = myHLastError();
_PrintError(hr, "AuthzInitializeResourceManager");
if (E_INVALIDARG != hr || (2 > g_fAdvancedServer && IsWhistler()))
{
if (HRESULT_FROM_WIN32(ERROR_PRIVILEGE_NOT_HELD) != hr ||
!fConsoleActive ||
fAuditPrivilegeEnabled)
{
goto error;
}
}
}
else
{
fAuditEnabled = TRUE;
}
if (fAuditPrivilegeEnabled)
{
hr = myEnablePrivilege(SE_AUDIT_NAME, FALSE);
_JumpIfError(hr, error, "myDisablePrivilege(SE_AUDIT_NAME)");
fAuditPrivilegeEnabled = false;
}
hr = CoreInit(fAuditEnabled);
if (S_OK != hr)
{
dwIdEvent = MAXDWORD; // Error event already logged
_JumpError(hr, error, "CoreInit");
}
certsrvLogAlignmentFaultStatus();
myLogExceptionInit(certsrvLogException);
if (0 == (IF_NORPCICERTREQUEST & g_InterfaceFlags))
{
hr = RPCInit();
if (S_OK != hr)
{
dwIdEvent = MSG_E_RPC_INIT;
_JumpError(hr, error, "RPCInit");
}
}
hr = SetRegistryDcomConfig(fConsoleActive);
if (S_OK != hr)
{
dwIdEvent = MSG_E_REGISTRY_DCOM;
_JumpError(hr, error, "SetRegistryDcomConfig");
}
hr = CertStartClassFactories();
if (S_OK != hr)
{
dwIdEvent = CO_E_WRONG_SERVER_IDENTITY == hr?
MSG_E_SERVER_IDENTITY : MSG_E_CLASS_FACTORIES;
_JumpError(hr, error, "CertStartClassFactories");
}
{
// only perform Hash if the auditing is enabled
if (AUDIT_FILTER_STARTSTOP & g_dwAuditFilter)
{
BOOL fSupported;
BOOL fEnabled;
CertSrv::CAuditEvent event(
SE_AUDITID_CERTSRV_SERVICESTART,
g_dwAuditFilter);
ULARGE_INTEGER uliKeyUsageCount;
CAutoLPWSTR autoszCertHash;
CAutoLPWSTR autoszPublicKeyHash;
hr = event.AddData(g_pwszDBFileHash); // %1 database hash
_JumpIfError(hr, error, "CAuditEvent::AddData");
// retrieve private key usage count if auditing enabled
uliKeyUsageCount.QuadPart = 0;
hr = myGetSigningKeyUsageCount(
g_pCAContextCurrent->hProvCA,
&fSupported,
&fEnabled,
&uliKeyUsageCount);
_PrintIfError(hr, "myGetSigningKeyUsageCount");
hr = event.AddData(&uliKeyUsageCount); // %2 key usage count
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = certsrvGetCACertAndKeyHash(
&autoszCertHash,
&autoszPublicKeyHash);
_JumpIfError(hr, error, "certsrvGetCACertAndKeyHash");
hr = event.AddData((LPCWSTR)autoszCertHash); // %3 CA cert hash
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.AddData((LPCWSTR)autoszPublicKeyHash); // %4 CA public key hash
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.Report();
_JumpIfError(hr, error, "CAuditEvent::Report");
}
}
{
CertSrv::CAuditEvent event(
SE_AUDITID_CERTSRV_ROLESEPARATIONSTATE,
g_dwAuditFilter);
hr = event.AddData(CAuditEvent::RoleSeparationIsEnabled()); // %1 is role separation enabled?
_JumpIfError(hr, error, "CAuditEvent::AddData");
hr = event.Report();
_JumpIfError(hr, error, "CAuditEvent::Report");
}
pwszDC0 = (g_fUseDS || L'\0' != g_wszPolicyDCName[0])? L" DC=" : L"";
if (CERTLOG_TERSE <= g_dwLogLevel)
{
WCHAR const *apwsz[3];
apwsz[0] = g_wszCommonName;
apwsz[1] = pwszDC0;
apwsz[2] = g_wszPolicyDCName;
LogEvent(
EVENTLOG_INFORMATION_TYPE,
MSG_I_SERVER_STARTED,
ARRAYSIZE(apwsz),
apwsz);
}
CONSOLEPRINT1((
DBG_SS_CERTSRV,
"Certification Authority Service Ready (%us)%ws%ws ...\n",
MSTOSEC(GetTickCount() - TimeStart),
pwszDC0,
g_wszPolicyDCName));
g_fStarted = TRUE;
CSASSERT(S_OK == hr);
error:
g_pwszDBFileHash.Cleanup();
if (fAuditPrivilegeEnabled)
{
myEnablePrivilege(SE_AUDIT_NAME, FALSE);
}
if (S_OK != hr)
{
if (MAXDWORD != dwIdEvent)
{
if (0 == dwIdEvent)
{
dwIdEvent = MSG_E_GENERIC_STARTUP_FAILURE;
}
LogEventStringHResult(
dwEventType,
dwIdEvent,
g_wszCommonName,
EVENTLOG_INFORMATION_TYPE == dwEventType? S_OK : hr);
}
CertSrvStopServer(fConsoleActive);
// returning error here results in repost to scm
}
g_fStartInProgress = FALSE;
return(hr);
}
VOID
certsrvLogException(
IN HRESULT hrEvent,
IN EXCEPTION_POINTERS const *pep,
OPTIONAL IN char const *, // pszFileName
IN DWORD dwFile,
IN DWORD dwLine)
{
HRESULT hr;
WCHAR awcFile[2 + 3 * cwcDWORDSPRINTF];
WCHAR awcFlags[3 + cwcDWORDSPRINTF];
WCHAR awcAddress[2 + 2 * cwcDWORDSPRINTF];
WCHAR const *apwsz[4];
WORD cpwsz;
WCHAR awchr[cwcHRESULTSTRING];
WCHAR const *pwszStringErr = NULL;
wsprintf(awcFile, L"%u.%u.%u", dwFile, dwLine, MSG_E_EXCEPTION);
CSASSERT(wcslen(awcFile) < ARRAYSIZE(awcFile));
wsprintf(awcFlags, L"0x%08x", pep->ExceptionRecord->ExceptionFlags);
CSASSERT(wcslen(awcFlags) < ARRAYSIZE(awcFlags));
wsprintf(awcAddress, L"0x%p", pep->ExceptionRecord->ExceptionAddress);
CSASSERT(wcslen(awcAddress) < ARRAYSIZE(awcAddress));
apwsz[0] = awcFile;
apwsz[1] = awcAddress;
apwsz[2] = awcFlags;
pwszStringErr = myGetErrorMessageText(hrEvent, TRUE);
apwsz[3] = pwszStringErr;
if (NULL == pwszStringErr)
{
apwsz[3] = myHResultToString(awchr, hrEvent);
}
cpwsz = ARRAYSIZE(apwsz);
hr = LogEvent(EVENTLOG_ERROR_TYPE, MSG_E_EXCEPTION, cpwsz, apwsz);
_JumpIfError(hr, error, "LogEvent");
error:
if (NULL != pwszStringErr)
{
LocalFree(const_cast<WCHAR *>(pwszStringErr));
}
}
DWORD
CertSrvStartServerThread(
IN VOID *pvArg)
{
HRESULT hr = S_OK;
DWORD Flags = (DWORD) (ULONG_PTR) pvArg;
BOOL b;
ULONG_PTR ulp;
// Anatomy of startup code
// if g_fStartAsService, just registers this new thread as the main
// thread and blocks until the ServiceMain fxn returns.
// We're in a non-rpc thread; check if we need to create VRoots. I would
// have liked to have moved this into CoreInit, but we're limited in where
// we can do this (can't be calling into RPC during RPC call).
//
// If the SetupStatus SETUP_ATTEMPT_VROOT_CREATE registry flag is clear,
// this call is a nop. A separate thread is created to access the IIS
// metabase. If it hangs, it will be nuked -- after the specified timeout.
// This call returns immediately, so the only detectable error is likely
// to be a thread creation problem.
// if we're doing anything other than starting the service controller,
// check to see if the vroots need to be created.
if (0 == (Flags & CSST_STARTSERVICECONTROLLER))
{
WCHAR *pwszPath = NULL;
DWORD cb = sizeof(ENUM_CATYPES);
DWORD dwType;
ENUM_CATYPES CAType = ENUM_UNKNOWN_CA;
HKEY hkey = NULL;
hr = myRegOpenRelativeKey(
NULL,
L"ca",
RORKF_CREATESUBKEYS,
&pwszPath,
NULL, // ppwszName
&hkey);
_PrintIfError(hr, "myRegOpenRelativeKey");
if (S_OK == hr)
{
DBGPRINT((DBG_SS_CERTLIBI, "%ws\n", pwszPath));
cb = sizeof(CAType);
hr = RegQueryValueEx(
hkey,
wszREGCATYPE,
NULL,
&dwType,
(BYTE *) &CAType,
&cb);
_PrintIfErrorStr(hr, "RegQueryValueEx", wszREGCATYPE);
}
if (pwszPath)
LocalFree(pwszPath);
if (hkey)
RegCloseKey(hkey);
hr = myModifyVirtualRootsAndFileShares(
VFF_CREATEVROOTS | // Create VRoots
VFF_CREATEFILESHARES | // Create File Shares
VFF_CHECKREGFLAGFIRST | // Skip if reg flag clear
VFF_CLEARREGFLAGFIRST, // Clear flag before attempt
CAType,
TRUE, // asynch call -- don't block
VFCSEC_TIMEOUT, // wait this long before giving up
NULL,
NULL);
if (S_OK != hr)
{
LogEventHResult(
EVENTLOG_INFORMATION_TYPE,
MSG_E_IIS_INTEGRATION_ERROR,
hr);
}
}
// StartServiceCtrlDispatcher should hang until certsrv terminates
if ((CSST_STARTSERVICECONTROLLER & Flags) &&
!StartServiceCtrlDispatcher(steDispatchTable))
{
hr = myHLastError();
if (HRESULT_FROM_WIN32(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) != hr)
{
_JumpError(hr, error, "StartServiceCtrlDispatcher");
}
CONSOLEPRINT0((
DBG_SS_CERTSRV,
"CertSrv: Failed to connect to service controller -- running in standalone mode\n"));
Flags &= ~CSST_STARTSERVICECONTROLLER;
Flags |= CSST_CONSOLE;
}
if (0 == (CSST_STARTSERVICECONTROLLER & Flags))
{
DBGPRINT((
DBG_SS_CERTSRVI,
"SendMessageTimeout(tid=%d, hwnd=0x%x, msg=0x%x)\n",
GetCurrentThreadId(),
g_hwndMain,
WM_STARTSERVER));
b = SendMessageTimeout(
g_hwndMain,
WM_STARTSERVER,
(CSST_CONSOLE & Flags)? TRUE : FALSE, // fConsoleActive
0,
SMTO_BLOCK,
MAXLONG,
&ulp) != 0;
if (!b)
{
hr = myHLastError();
_JumpError(hr, error, "SendMessageTimeout");
}
else if (ulp != S_OK)
{
hr = (HRESULT) ulp;
_JumpError(hr, error, "SendMessageTimeout");
}
}
if (Flags & CSST_CONSOLE)
{
// we're running as console, and so don't have a CRL publishing thread.
// Use this one since no one cares if it returns
// if svc, we do this in the caller of this function
CertSrvBlockThreadUntilStop();
}
error:
// on return, this thread dies
return(hr);
}
VOID
DisplayUsage(
IN DWORD idsMsg)
{
WCHAR *pwsz = myLoadResourceStringNoCache(g_hInstApp, idsMsg);
if (NULL != pwsz)
{
wprintf(L"%ws", pwsz);
LocalFree(pwsz);
}
}
VOID
Usage(
IN BOOL fUsageInternal)
{
DisplayUsage(IDS_USAGE);
if (fUsageInternal)
{
DisplayUsage(IDS_USAGE_FULL);
#if DBG_COMTEST
DisplayUsage(IDS_USAGE_COMTEST);
#endif
}
}
int
ArgvParseCommandLine(
IN int argc,
IN WCHAR *argv[])
{
HRESULT hr;
myVerifyResourceStrings(g_hInstApp);
hr = E_INVALIDARG;
while (1 < argc && myIsSwitchChar(argv[1][0]))
{
WCHAR *pwsz = argv[1];
BOOL fUsage = FALSE;
BOOL fUsageInternal = FALSE;
while (*++pwsz != L'\0')
{
switch (*pwsz)
{
#if DBG_COMTEST
case L'C':
case L'c':
fComTest = TRUE;
break;
#endif
case L'N':
case L'n':
g_fCreateDB = TRUE;
break;
case L'Z':
case L'z':
g_fStartAsService = FALSE;
break;
case L'S':
case L's':
g_CryptSilent = CRYPT_SILENT;
break;
case L'?':
case L'u':
fUsage = TRUE;
if (0 == lstrcmp(pwsz, L"uSAGE"))
{
fUsageInternal = TRUE;
}
// FALLTHROUGH
default:
Usage(fUsageInternal);
if (fUsage)
{
goto error;
}
_JumpError(hr, error, "bad command line option");
}
}
argc--;
argv++;
}
if (argc != 1)
{
Usage(FALSE);
_JumpError(hr, error, "extra args");
}
if (g_fStartAsService)
{
BOOL fSilent;
hr = ServiceQueryInteractiveFlag(&fSilent);
_PrintIfError(hr, "ServiceQueryInteractiveFlag");
if (S_OK == hr && fSilent)
{
g_CryptSilent = CRYPT_SILENT;
}
}
hr = S_OK;
error:
return(hr);
}
typedef int (FNARGVMAIN)(
IN int argc,
IN WCHAR *argv[]);
//+------------------------------------------------------------------------
// FUNCTION: CertArgvMainDispatch
//
// NOTES: Takes a WCHAR * command line and chews it up into argc/argv
// form so it can be passed on to a traditional C-style main.
//-------------------------------------------------------------------------
HRESULT
CertArgvMainDispatch(
IN FNARGVMAIN *pfnMain,
IN WCHAR *pwszAppName,
IN WCHAR const *pwszCmdLine)
{
HRESULT hr;
WCHAR *pwcBuf = NULL;
WCHAR *apwszArg[20];
int cArg;
WCHAR *p;
WCHAR wcEnd;
WCHAR const *pwszT;
pwcBuf = (WCHAR *) LocalAlloc(
LMEM_FIXED,
(wcslen(pwszCmdLine) + 1) * sizeof(WCHAR));
if (NULL == pwcBuf)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
p = pwcBuf;
cArg = 0;
apwszArg[cArg++] = pwszAppName;
pwszT = pwszCmdLine;
while (*pwszT != L'\0')
{
while (*pwszT == L' ')
{
pwszT++;
}
if (*pwszT != L'\0')
{
wcEnd = L' ';
if (*pwszT == L'"')
{
wcEnd = *pwszT++;
}
apwszArg[cArg++] = p;
if (ARRAYSIZE(apwszArg) <= cArg)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "Too many args");
}
while (*pwszT != L'\0' && *pwszT != wcEnd)
{
*p++ = *pwszT++;
}
*p++ = L'\0';
if (*pwszT != L'\0')
{
pwszT++; // skip blank or quote character
}
}
}
CSASSERT(
L'\0' == *pwszCmdLine ||
wcslen(pwszCmdLine) + 1 >= SAFE_SUBTRACT_POINTERS(p, pwcBuf));
CSASSERT(ARRAYSIZE(apwszArg) > cArg);
apwszArg[cArg] = NULL;
hr = (*pfnMain)(cArg, apwszArg);
error:
if (NULL != pwcBuf)
{
LocalFree(pwcBuf);
}
return(hr);
}
//+------------------------------------------------------------------------
// FUNCTION: MainWndProc(...)
//-------------------------------------------------------------------------
LRESULT APIENTRY
MainWndProc(
IN HWND hWnd,
IN UINT msg,
IN WPARAM wParam,
IN LPARAM lParam)
{
HRESULT hr;
LPARAM lRet = 0;
DBGPRINT((
DBG_SS_CERTSRVI,
"MainWndProc(tid=%d) msg=0x%x, wp=0x%x, lp=0x%x\n",
GetCurrentThreadId(),
msg,
wParam,
lParam));
switch (msg)
{
case WM_CREATE:
case WM_SIZE:
break;
case WM_DESTROY:
if (!g_fStartAsService)
{
PostQuitMessage(S_OK);
}
break;
case WM_ENDSESSION:
// only stop on a real shutdown,
// never look at this msg if running as svc
if (g_fStartAsService || (0 == wParam) || (0 != lParam))
{
break;
}
// fall through
case WM_STOPSERVER:
lRet = CertSrvStopServer(!g_fStartAsService);
break;
case WM_SYNC_CLOSING_THREADS:
hr = (HRESULT) lParam;
// sync: wait for SCM to return control to exiting CertSrvStartServerThread
if (WAIT_OBJECT_0 != WaitForSingleObject(g_hServiceThread, 10 * 1000))
{
hr = WAIT_TIMEOUT;
}
PostQuitMessage(hr);
break;
case WM_STARTSERVER:
hr = CoInitializeEx(NULL, GetCertsrvComThreadingModel());
if (S_FALSE == hr)
{
hr = S_OK;
}
if (S_OK != hr)
{
LogEventString(
EVENTLOG_ERROR_TYPE,
MSG_E_OLE_INIT_FAILED,
NULL);
_PrintError(hr, "CoInitializeEx");
}
else
{
hr = certsrvStartServer((BOOL) wParam);
_PrintIfError(hr, "certsrvStartServer");
}
if (S_OK != hr)
{
if ((BOOL) wParam) // fConsoleActive
{
PostQuitMessage(hr);
}
lRet = hr; // set this so caller knows we failed
}
break;
case WM_SUSPENDSERVER:
break;
case WM_RESTARTSERVER:
break;
default:
lRet = DefWindowProc(hWnd, msg, wParam, lParam);
}
return(lRet);
}
/*
Complete anatomy of certificate server startup/shutdown
WinMain():
|
|g_hSvcThread = CreateThread(CertSrvStartServerThread(SVC_CONTROLLER))
| |
|[MessageLoop \
| processing CertSrvStartServerThread(SVC_CONTROLLER):
| until |StartSvcCtrlDispatcher(ServiceMain)
| WM_QUIT] ||ServiceMain:
| ||RegisterSvcCtrlHandler(ServiceControlHandler())
| ||hStartThread = CreateThread(CertSrvStartServerThread(0))
| || |
| || \
| || CertSrvStartServerThread(0):
| || |SendMessage(WM_STARTSERVER)
| || \return // CertSrvStartServerThread(0)
| || (Thread Terminates)
| ||WaitForSingleObject(hStartThread), pinging SCM
| ||CertSrvBlockThreadUntilStop()
| |||WaitForSingleObject(g_hSvcStoppingEvent) ***steady state***
| ||\return // CertSrvBlockThreadUntilStop()
| ||WaitForSingleObject(g_hSvcStoppedEvent), pinging SCM
| ||PostMessage(WM_SYNC_CLOSING_THREADS)
| |\return // StartSvcCtrlDispatcher(ServiceMain)
| \return // CertSrvStartServerThread(SVC_CONTROLLER)
| (Thread Terminates)
| WM_QUIT:
\ return
(Process Terminates)
ServiceControlHandler special functions:
SERVICE_CONTROL_STOP:
|PostMessage(WM_STOPSERVER)
\break
MessageLoop special functions:
WM_SYNC_CLOSING_THREADS:
|WaitForSingleObject(g_hSvcThread)
|PostQuitMessage() // WM_QUIT to msgloop
\break
WM_STOPSERVER:
|CertSrvStopServer():
|| Signal(g_hServiceStoppingEvent)
|| Signal(g_hServiceStoppedEvent)
|\ return // CertSrvStopServer()
\break
*/
//+------------------------------------------------------------------------
// Function: wWinMain()
//
// Synopsis: Entry Point
//
// Arguments: [hInstance] -- Instance handle
// [hPrevInstance] -- Obsolete
// [lpCmdLine] -- App command line
// [nCmdShow] -- Starting show state
//-------------------------------------------------------------------------
extern "C" int APIENTRY
wWinMain(
IN HINSTANCE hInstance,
IN HINSTANCE, // hPrevInstance
IN LPWSTR lpCmdLine,
IN int /* nCmdShow */ )
{
MSG msg;
WNDCLASSEX wcApp;
ATOM atomClass;
HRESULT hr;
BOOL fCoInit = FALSE;
WCHAR awchr[cwcHRESULTSTRING];
WCHAR const *pwszMsgAlloc;
WCHAR const *pwszMsg;
_setmode(_fileno(stdout), _O_TEXT);
_wsetlocale(LC_ALL, L".OCP");
mySetThreadUILanguage(0);
CertSrvLogOpen();
DBGPRINT((DBG_SS_CERTSRVI, "Main Thread = %x\n", GetCurrentThreadId()));
g_dwDelay0 = GetRegistryDwordValue(L"Delay0");
g_dwDelay1 = GetRegistryDwordValue(L"Delay1");
g_dwDelay2 = GetRegistryDwordValue(L"Delay2");
if (0 != g_dwDelay0)
{
DBGPRINT((
DBG_SS_CERTSRV,
"wWinMain(0): sleeping %u seconds\n",
g_dwDelay0));
Sleep(1000 * g_dwDelay0);
}
// Save the current instance
g_hInstApp = hInstance;
ZeroMemory(&wcApp, sizeof(wcApp));
// Set up the application's window class
wcApp.cbSize = sizeof(wcApp);
wcApp.lpfnWndProc = MainWndProc;
wcApp.hInstance = hInstance;
wcApp.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcApp.hCursor = LoadCursor(NULL, IDC_ARROW);
wcApp.hbrBackground = NULL; // try to not pull in GDI32
wcApp.lpszClassName = g_wszAppName;
atomClass = RegisterClassEx(&wcApp);
if (!atomClass)
{
hr = myHLastError();
_JumpError(hr, error, "RegisterClassEx");
}
// Create Main Window
g_hwndMain = CreateWindowEx(
0, // dwExStyle
(WCHAR const *) atomClass, // lpClassName
L"Certification Authority",// lpWindowName
WS_OVERLAPPEDWINDOW, // dwStyle
//0, // dwStyle
CW_USEDEFAULT, // x
CW_USEDEFAULT, // y
CW_USEDEFAULT, // nWidth
CW_USEDEFAULT, // nHeight
NULL, // hWndParent
NULL, // hMenu
hInstance, // hInstance
NULL); // lpParam
if (NULL == g_hwndMain)
{
hr = myHLastError();
_JumpError(hr, error, "CreateWindowEx");
}
DBGPRINT((DBG_SS_CERTSRVI, "Main Window = %x\n", g_hwndMain));
// Make window visible
// ShowWindow(g_hwndMain,nCmdShow);
hr = CertArgvMainDispatch(ArgvParseCommandLine, g_wszAppName, lpCmdLine);
_JumpIfError2(hr, error, "CertArgvMainDispatch", E_INVALIDARG);
// Update window client area
// UpdateWindow(g_hwndMain);
if (0 != g_dwDelay1)
{
DBGPRINT((
DBG_SS_CERTSRV,
"wWinMain(1): sleeping %u seconds\n",
g_dwDelay1));
Sleep(1000 * g_dwDelay1);
}
hr = CoInitializeEx(NULL, GetCertsrvComThreadingModel());
if (S_OK != hr && S_FALSE != hr)
{
LogEventStringHResult(
EVENTLOG_ERROR_TYPE,
MSG_E_CO_INITIALIZE,
g_wszCommonName,
hr);
_JumpError(hr, error, "CoInitializeEx");
}
fCoInit = TRUE;
g_hServiceStoppingEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == g_hServiceStoppingEvent)
{
hr = myHLastError();
_JumpError(hr, error, "CreateEvent");
}
g_hServiceStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == g_hServiceStoppedEvent)
{
hr = myHLastError();
_JumpError(hr, error, "CreateEvent");
}
g_hCRLManualPublishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == g_hCRLManualPublishEvent)
{
hr = myHLastError();
_JumpError(hr, error, "CreateEvent");
}
g_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == g_hShutdownEvent)
{
hr = myHLastError();
_JumpError(hr, error, "CreateEvent");
}
__try
{
InitializeCriticalSection(&g_ShutdownCriticalSection);
g_fShutdownCritSec = TRUE;
hr = S_OK;
}
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
{
}
_JumpIfError(hr, error, "InitializeCriticalSection");
g_hServiceThread = CreateThread(
NULL, // lpThreadAttributes (Security Attr)
0, // dwStackSize
CertSrvStartServerThread,
(VOID *) UlongToPtr((g_fStartAsService? CSST_STARTSERVICECONTROLLER : CSST_CONSOLE)), // lpParameter
0, // dwCreationFlags
&g_ServiceThreadId);
if (NULL == g_hServiceThread)
{
hr = myHLastError();
LogEventStringHResult(
EVENTLOG_ERROR_TYPE,
MSG_E_SERVICE_THREAD,
g_wszCommonName,
hr);
_JumpError(hr, error, "CreateThread");
}
DBGPRINT((DBG_SS_CERTSRVI, "Service Thread = %x\n", g_ServiceThreadId));
// Message Loop
for (;;)
{
BOOL b;
b = GetMessage(&msg, NULL, 0, 0);
if (!b)
{
hr = (HRESULT)msg.wParam;
_JumpIfError(hr, error, "WM_QUIT");
break;
}
if (-1 == (LONG) b)
{
hr = myHLastError();
_JumpError(hr, error, "GetMessage");
}
DBGPRINT((
DBG_SS_CERTSRVI,
"DispatchMessage(tid=%d) msg=0x%x, wp=0x%x, lp=0x%x\n",
GetCurrentThreadId(),
msg.message,
msg.wParam,
msg.lParam));
DispatchMessage(&msg);
}
error:
if (fCoInit)
{
CoUninitialize();
}
if (g_fShutdownCritSec)
{
DeleteCriticalSection(&g_ShutdownCriticalSection);
g_fShutdownCritSec = FALSE;
}
if (NULL != g_hShutdownEvent)
{
CloseHandle(g_hShutdownEvent);
}
if (NULL != g_hServiceThread)
{
CloseHandle(g_hServiceThread);
}
if (NULL != g_hServiceStoppingEvent)
{
CloseHandle(g_hServiceStoppingEvent);
}
if (NULL != g_hServiceStoppedEvent)
{
CloseHandle(g_hServiceStoppedEvent);
}
if (NULL != g_hCRLManualPublishEvent)
{
CloseHandle(g_hCRLManualPublishEvent);
}
CAuditEvent::CleanupAuditEventTypeHandles();
pwszMsgAlloc = NULL;
pwszMsg = L"S_OK";
if (S_OK != hr)
{
pwszMsgAlloc = myGetErrorMessageText(hr, TRUE);
if (NULL != pwszMsgAlloc)
{
pwszMsg = pwszMsgAlloc;
}
else
{
pwszMsg = myHResultToString(awchr, hr);
}
}
_PrintError(hr, "Exit Status");
CONSOLEPRINT1((DBG_SS_CERTSRV, "Exit Status = %ws\n", pwszMsg));
if (NULL != pwszMsgAlloc)
{
LocalFree(const_cast<WCHAR *>(pwszMsgAlloc));
}
myFreeResourceStrings("certsrv.exe");
myFreeColumnDisplayNames();
myRegisterMemDump();
return(hr);
}