//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: certsrv.cpp // // Contents: Cert Server main & debug support // // History: 25-Jul-96 vich created // //--------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include #include #include #include #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(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(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(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(pwszMsgAlloc)); } myFreeResourceStrings("certsrv.exe"); myFreeColumnDisplayNames(); myRegisterMemDump(); return(hr); }