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
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);
|
|
}
|