Source code of Windows XP (NT5)
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.

2021 lines
47 KiB

  1. //+--------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1996 - 1999
  5. //
  6. // File: certsrv.cpp
  7. //
  8. // Contents: Cert Server main & debug support
  9. //
  10. // History: 25-Jul-96 vich created
  11. //
  12. //---------------------------------------------------------------------------
  13. #include <pch.cpp>
  14. #pragma hdrstop
  15. #include <locale.h>
  16. #include <io.h>
  17. #include <fcntl.h>
  18. #include <safeboot.h>
  19. #include <authzi.h>
  20. #include "elog.h"
  21. #include "certlog.h"
  22. #include "certsrvd.h"
  23. #include "resource.h"
  24. #include "csresstr.h"
  25. #define __dwFILE__ __dwFILE_CERTSRV_CERTSRV_CPP__
  26. HKEY g_hkeyCABase = 0;
  27. BOOL g_fCreateDB = FALSE;
  28. BOOL g_fStartAsService = TRUE;
  29. BOOL g_fStarted;
  30. BOOL g_fStartInProgress;
  31. DWORD g_ServiceThreadId;
  32. HWND g_hwndMain;
  33. WCHAR g_wszAppName[] = L"CertSrv";
  34. HINSTANCE g_hInstApp;
  35. DWORD g_dwDelay0;
  36. DWORD g_dwDelay1;
  37. DWORD g_dwDelay2;
  38. BOOL g_fCryptSilent = FALSE;
  39. HANDLE g_hServiceThread = NULL;
  40. HANDLE g_hShutdownEvent = NULL;
  41. CRITICAL_SECTION g_ShutdownCriticalSection;
  42. BOOL g_fShutdownCritSec = FALSE;
  43. BOOL g_fRefuseIncoming = FALSE;
  44. LONG g_cCalls = 0;
  45. LONG g_cCallsActive = 0;
  46. BOOL g_fAdvancedServer = FALSE;
  47. CAutoLPWSTR g_pwszDBFileHash;
  48. SERVICE_TABLE_ENTRY steDispatchTable[] =
  49. {
  50. { const_cast<WCHAR *>(g_wszCertSrvServiceName), ServiceMain },
  51. { NULL, NULL }
  52. };
  53. WCHAR const g_wszRegKeyClassesCLSID[] = L"SOFTWARE\\Classes\\CLSID";
  54. WCHAR const g_wszRegKeyInprocServer32[] = L"InprocServer32";
  55. WCHAR const g_wszRegValueThreadingModel[] = L"ThreadingModel";
  56. WCHAR const g_wszRegKeyAppId[] = L"SOFTWARE\\Classes\\AppId";
  57. WCHAR const g_wszRegRunAs[] = L"RunAs";
  58. WCHAR const g_wszRegValueInteractiveUser[] = L"Interactive User";
  59. WCHAR const g_wszRegLocalService[] = L"LocalService";
  60. // do not change the order, add new audit resources at the end
  61. // g_pwszAllow,
  62. // g_pwszDeny,
  63. // g_pwszCAAdmin,
  64. // g_pwszOfficer,
  65. // g_pwszRead,
  66. // g_pwszEnroll,
  67. LPCWSTR g_pwszAuditResources[6];
  68. using namespace CertSrv;
  69. HRESULT
  70. OpenRegistryComKey(
  71. IN HKEY hKeyParent,
  72. IN CLSID const *pclsid,
  73. IN BOOL fWrite,
  74. OUT HKEY *phKey)
  75. {
  76. HRESULT hr;
  77. WCHAR *pwsz = NULL;
  78. *phKey = NULL;
  79. hr = StringFromCLSID(*pclsid, &pwsz);
  80. _JumpIfError(hr, error, "StringFromCLSID");
  81. hr = RegOpenKeyEx(
  82. hKeyParent,
  83. pwsz,
  84. 0,
  85. fWrite? KEY_ALL_ACCESS : KEY_READ,
  86. phKey);
  87. _JumpIfError(hr, error, "RegOpenKeyEx");
  88. error:
  89. if (NULL != pwsz)
  90. {
  91. CoTaskMemFree(pwsz);
  92. }
  93. return(hr);
  94. }
  95. BOOL
  96. IsMissingRegistryValue(
  97. IN HKEY hKey,
  98. IN WCHAR const *pwszRegValueName)
  99. {
  100. HRESULT hr;
  101. DWORD dwLen;
  102. DWORD dwType;
  103. hr = RegQueryValueEx(hKey, pwszRegValueName, NULL, &dwType, NULL, &dwLen);
  104. if (S_OK != hr)
  105. {
  106. hr = myHError(hr);
  107. }
  108. _JumpIfError2(
  109. hr,
  110. error,
  111. "RegQueryValueEx",
  112. HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
  113. error:
  114. return(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr);
  115. }
  116. BOOL
  117. IsMatchingRegistryValue(
  118. IN HKEY hKey,
  119. IN WCHAR const *pwszRegValueName,
  120. IN WCHAR const *pwszRegValueString)
  121. {
  122. HRESULT hr;
  123. DWORD dwLen;
  124. DWORD dwType;
  125. BOOL fMatch = FALSE;
  126. WCHAR buf[MAX_PATH];
  127. dwLen = sizeof(buf);
  128. hr = RegQueryValueEx(
  129. hKey,
  130. pwszRegValueName,
  131. NULL,
  132. &dwType,
  133. (BYTE *) buf,
  134. &dwLen);
  135. _JumpIfErrorStr(hr, error, "RegQueryValueEx", pwszRegValueName);
  136. if (REG_SZ == dwType && 0 == lstrcmpi(buf, pwszRegValueString))
  137. {
  138. fMatch = TRUE;
  139. }
  140. error:
  141. return(fMatch);
  142. }
  143. HRESULT
  144. SetRegistryStringValue(
  145. IN HKEY hKey,
  146. IN WCHAR const *pwszRegValueName,
  147. IN WCHAR const *pwszRegValueString)
  148. {
  149. HRESULT hr;
  150. hr = RegSetValueEx(
  151. hKey,
  152. pwszRegValueName,
  153. 0,
  154. REG_SZ,
  155. (const BYTE *) pwszRegValueString,
  156. (wcslen(pwszRegValueString) + 1) * sizeof(WCHAR));
  157. return(hr);
  158. }
  159. HRESULT
  160. CertSrvSetRegistryFileTimeValue(
  161. IN BOOL fConfigLevel,
  162. IN WCHAR const *pwszRegValueName,
  163. IN DWORD cpwszDelete,
  164. OPTIONAL IN WCHAR const * const *papwszRegValueNameDelete)
  165. {
  166. HRESULT hr;
  167. HKEY hKey = NULL;
  168. HKEY hKey1 = NULL;
  169. WCHAR *pwszKey;
  170. FILETIME ftCurrent;
  171. DWORD i;
  172. GetSystemTimeAsFileTime(&ftCurrent);
  173. hr = RegOpenKeyEx(
  174. HKEY_LOCAL_MACHINE,
  175. wszREGKEYCONFIGPATH,
  176. 0,
  177. KEY_ALL_ACCESS,
  178. &hKey);
  179. _JumpIfError(hr, error, "RegOpenKeyEx");
  180. if (!fConfigLevel)
  181. {
  182. hKey1 = hKey;
  183. hKey = NULL;
  184. hr = RegOpenKeyEx(
  185. hKey1,
  186. g_wszSanitizedName,
  187. 0,
  188. KEY_ALL_ACCESS,
  189. &hKey);
  190. _JumpIfError(hr, error, "RegOpenKeyEx");
  191. }
  192. hr = RegSetValueEx(
  193. hKey,
  194. pwszRegValueName,
  195. 0,
  196. REG_BINARY,
  197. (BYTE const *) &ftCurrent,
  198. sizeof(ftCurrent));
  199. _JumpIfError(hr, error, "RegSetValueEx");
  200. for (i = 0; i < cpwszDelete; i++)
  201. {
  202. hr = RegDeleteValue(hKey, papwszRegValueNameDelete[i]);
  203. _PrintIfError2(hr, "RegDeleteValue", ERROR_FILE_NOT_FOUND);
  204. }
  205. hr = S_OK;
  206. error:
  207. if (NULL != hKey1)
  208. {
  209. RegCloseKey(hKey1);
  210. }
  211. if (NULL != hKey)
  212. {
  213. RegCloseKey(hKey);
  214. }
  215. return(myHError(hr));
  216. }
  217. HRESULT
  218. SetRegistryDcomConfig(
  219. IN BOOL fConsoleActive)
  220. {
  221. HRESULT hr;
  222. HKEY hKeyAppId = NULL;
  223. HKEY hKeyAdmin = NULL;
  224. HKEY hKeyRequest = NULL;
  225. DWORD cChanged = 0;
  226. hr = RegOpenKeyEx(
  227. HKEY_LOCAL_MACHINE,
  228. g_wszRegKeyAppId,
  229. 0,
  230. KEY_ALL_ACCESS,
  231. &hKeyAppId);
  232. _JumpIfError(hr, error, "RegOpenKeyEx");
  233. hr = OpenRegistryComKey(hKeyAppId, &CLSID_CCertAdminD, TRUE, &hKeyAdmin);
  234. _JumpIfError(hr, error, "OpenRegistryComKey");
  235. hr = OpenRegistryComKey(hKeyAppId, &CLSID_CCertRequestD, TRUE, &hKeyRequest);
  236. _JumpIfError(hr, error, "OpenRegistryComKey");
  237. if (fConsoleActive)
  238. {
  239. // Running in console mode:
  240. // Delete both LocalService registry values
  241. // Create both RunAs = InteractiveUser registry values
  242. if (!IsMissingRegistryValue(hKeyAdmin, g_wszRegLocalService))
  243. {
  244. cChanged++;
  245. hr = RegDeleteValue(hKeyAdmin, g_wszRegLocalService);
  246. _JumpIfError(hr, error, "RegDeleteValue");
  247. }
  248. if (!IsMissingRegistryValue(hKeyRequest, g_wszRegLocalService))
  249. {
  250. cChanged++;
  251. hr = RegDeleteValue(hKeyRequest, g_wszRegLocalService);
  252. _JumpIfError(hr, error, "RegDeleteValue");
  253. }
  254. if (!IsMatchingRegistryValue(
  255. hKeyAdmin,
  256. g_wszRegRunAs,
  257. g_wszRegValueInteractiveUser))
  258. {
  259. cChanged++;
  260. hr = SetRegistryStringValue(
  261. hKeyAdmin,
  262. g_wszRegRunAs,
  263. g_wszRegValueInteractiveUser);
  264. _JumpIfError(hr, error, "SetRegistryStringValue");
  265. }
  266. if (!IsMatchingRegistryValue(
  267. hKeyRequest,
  268. g_wszRegRunAs,
  269. g_wszRegValueInteractiveUser))
  270. {
  271. cChanged++;
  272. hr = SetRegistryStringValue(
  273. hKeyRequest,
  274. g_wszRegRunAs,
  275. g_wszRegValueInteractiveUser);
  276. _JumpIfError(hr, error, "SetRegistryStringValue");
  277. }
  278. if (0 != cChanged)
  279. {
  280. DBGPRINT((
  281. DBG_SS_CERTSRV,
  282. "SetRegistryDcomConfig(%u): setting %ws=%ws\n",
  283. cChanged,
  284. g_wszRegRunAs,
  285. g_wszRegValueInteractiveUser));
  286. }
  287. }
  288. else
  289. {
  290. // Running as a service:
  291. // Delete both RunAs registry values
  292. // Create both LocalService = CertSvc registry values
  293. if (!IsMissingRegistryValue(hKeyAdmin, g_wszRegRunAs))
  294. {
  295. cChanged++;
  296. hr = RegDeleteValue(hKeyAdmin, g_wszRegRunAs);
  297. _JumpIfError(hr, error, "RegDeleteValue");
  298. }
  299. if (!IsMissingRegistryValue(hKeyRequest, g_wszRegRunAs))
  300. {
  301. cChanged++;
  302. hr = RegDeleteValue(hKeyRequest, g_wszRegRunAs);
  303. _JumpIfError(hr, error, "RegDeleteValue");
  304. }
  305. if (!IsMatchingRegistryValue(
  306. hKeyAdmin,
  307. g_wszRegLocalService,
  308. g_wszCertSrvServiceName))
  309. {
  310. cChanged++;
  311. hr = SetRegistryStringValue(
  312. hKeyAdmin,
  313. g_wszRegLocalService,
  314. g_wszCertSrvServiceName);
  315. _JumpIfError(hr, error, "SetRegistryStringValue");
  316. }
  317. if (!IsMatchingRegistryValue(
  318. hKeyRequest,
  319. g_wszRegLocalService,
  320. g_wszCertSrvServiceName))
  321. {
  322. cChanged++;
  323. hr = SetRegistryStringValue(
  324. hKeyRequest,
  325. g_wszRegLocalService,
  326. g_wszCertSrvServiceName);
  327. _JumpIfError(hr, error, "SetRegistryStringValue");
  328. }
  329. if (0 != cChanged)
  330. {
  331. DBGPRINT((
  332. DBG_SS_CERTSRV,
  333. "SetRegistryDcomConfig(%u): setting %ws=%ws\n",
  334. cChanged,
  335. g_wszRegLocalService,
  336. g_wszCertSrvServiceName));
  337. }
  338. }
  339. error:
  340. if (NULL != hKeyRequest)
  341. {
  342. RegCloseKey(hKeyRequest);
  343. }
  344. if (NULL != hKeyAdmin)
  345. {
  346. RegCloseKey(hKeyAdmin);
  347. }
  348. if (NULL != hKeyAppId)
  349. {
  350. RegCloseKey(hKeyAppId);
  351. }
  352. return(myHError(hr));
  353. }
  354. DWORD
  355. GetRegistryDwordValue(
  356. IN WCHAR const *pwszRegValueName)
  357. {
  358. HRESULT hr;
  359. HKEY hKeyConfig = NULL;
  360. DWORD dwVal;
  361. DWORD dwType;
  362. DWORD dwLen;
  363. dwVal = 0;
  364. hr = RegOpenKeyEx(
  365. HKEY_LOCAL_MACHINE,
  366. g_wszRegKeyConfigPath,
  367. 0,
  368. KEY_READ,
  369. &hKeyConfig);
  370. _JumpIfError(hr, error, "RegOpenKeyEx");
  371. dwLen = sizeof(dwVal);
  372. hr = RegQueryValueEx(
  373. hKeyConfig,
  374. pwszRegValueName,
  375. NULL,
  376. &dwType,
  377. (BYTE *) &dwVal,
  378. &dwLen);
  379. if (S_OK != hr || REG_DWORD != dwType || sizeof(dwVal) != dwLen)
  380. {
  381. dwVal = 0;
  382. goto error;
  383. }
  384. error:
  385. if (NULL != hKeyConfig)
  386. {
  387. RegCloseKey(hKeyConfig);
  388. }
  389. return(dwVal);
  390. }
  391. HRESULT
  392. CertSrvResetRegistryWatch(
  393. IN OUT HANDLE *phRegistryModified)
  394. {
  395. HRESULT hr;
  396. CSASSERT(NULL != phRegistryModified);
  397. //////////////////////////////////////
  398. // Initialization of registry events
  399. if (NULL == g_hkeyCABase)
  400. {
  401. DWORD dwDisposition;
  402. LPWSTR pszCAPath;
  403. pszCAPath = (LPWSTR) LocalAlloc(
  404. LMEM_FIXED,
  405. (WSZARRAYSIZE(wszREGKEYCONFIGPATH_BS) +
  406. wcslen(g_wszSanitizedName) +
  407. 1) * sizeof(WCHAR));
  408. if (NULL == pszCAPath)
  409. {
  410. hr = E_OUTOFMEMORY;
  411. _JumpError(hr, error, "LocalAlloc");
  412. }
  413. wcscpy(pszCAPath, wszREGKEYCONFIGPATH_BS);
  414. wcscat(pszCAPath, g_wszSanitizedName);
  415. hr = RegCreateKeyEx(
  416. HKEY_LOCAL_MACHINE,
  417. pszCAPath,
  418. 0, // reserved
  419. NULL, // class
  420. 0, // options
  421. KEY_ALL_ACCESS, // sec desired
  422. NULL, // sec attr
  423. &g_hkeyCABase, // phk
  424. &dwDisposition);
  425. LocalFree(pszCAPath); pszCAPath = NULL;
  426. _JumpIfError(hr, error, "RegCreateKeyEx base key");
  427. }
  428. if (NULL == *phRegistryModified)
  429. {
  430. *phRegistryModified = CreateEvent(
  431. NULL,
  432. TRUE, // manual reset
  433. FALSE, // initial state
  434. L"Registry Modification Event");
  435. if (NULL == *phRegistryModified)
  436. {
  437. hr = myHLastError();
  438. _JumpError(hr, error, "CreateEvent registry watch");
  439. }
  440. }
  441. else
  442. {
  443. // reset registry event
  444. ResetEvent( *phRegistryModified );
  445. }
  446. // register our registry lookout trigger
  447. hr = RegNotifyChangeKeyValue(
  448. g_hkeyCABase,
  449. FALSE,
  450. REG_NOTIFY_CHANGE_LAST_SET,
  451. *phRegistryModified,
  452. TRUE);
  453. _JumpIfError(hr, error, "RegNotifyChangeKeyValue on base key");
  454. error:
  455. return(myHError(hr));
  456. }
  457. HRESULT
  458. CertSrvRegistryModificationEvent(
  459. IN FILETIME const *pftWait,
  460. IN OUT DWORD *pdwTimeOut)
  461. {
  462. HRESULT hr;
  463. DWORD dwVal;
  464. BOOL fDisabledNew;
  465. FILETIME ftCurrent;
  466. BOOL fSetEvent = FALSE;
  467. DWORD dwMSTimeOut;
  468. // see if Base CRL publish enabled state has changed
  469. hr = myGetCertRegDWValue(
  470. g_wszSanitizedName,
  471. NULL,
  472. NULL,
  473. wszREGCRLPERIODCOUNT,
  474. &dwVal);
  475. if (S_OK == hr)
  476. {
  477. fDisabledNew = 0 == dwVal;
  478. if (fDisabledNew != g_fCRLPublishDisabled)
  479. {
  480. fSetEvent = TRUE;
  481. }
  482. }
  483. // see if Delta CRL publish enabled state has changed
  484. hr = myGetCertRegDWValue(
  485. g_wszSanitizedName,
  486. NULL,
  487. NULL,
  488. wszREGCRLDELTAPERIODCOUNT,
  489. &dwVal);
  490. if (S_OK == hr)
  491. {
  492. fDisabledNew = 0 == dwVal;
  493. if (fDisabledNew != g_fDeltaCRLPublishDisabled)
  494. {
  495. fSetEvent = TRUE;
  496. }
  497. }
  498. GetSystemTimeAsFileTime(&ftCurrent);
  499. CRLComputeTimeOut(pftWait, &ftCurrent, &dwMSTimeOut);
  500. if (dwMSTimeOut >= *pdwTimeOut)
  501. {
  502. dwMSTimeOut = *pdwTimeOut;
  503. fSetEvent = TRUE;
  504. }
  505. *pdwTimeOut -= dwMSTimeOut;
  506. if (fSetEvent)
  507. {
  508. SetEvent(g_hCRLManualPublishEvent); // pulse to get up-to-date
  509. }
  510. return(hr);
  511. }
  512. #if DBG_CERTSRV
  513. WCHAR const *
  514. certsrvGetCurrentTimeWsz()
  515. {
  516. HRESULT hr;
  517. FILETIME ft;
  518. WCHAR *pwszTime = NULL;
  519. static WCHAR s_wszTime[128];
  520. GetSystemTimeAsFileTime(&ft);
  521. hr = myGMTFileTimeToWszLocalTime(&ft, TRUE, &pwszTime);
  522. _PrintIfError(hr, "myGMTFileTimeToWszLocalTime");
  523. s_wszTime[0] = L'\0';
  524. if (NULL != pwszTime)
  525. {
  526. wcsncpy(s_wszTime, pwszTime, ARRAYSIZE(s_wszTime));
  527. s_wszTime[ARRAYSIZE(s_wszTime) - 1] = L'\0';
  528. LocalFree(pwszTime);
  529. }
  530. return(s_wszTime);
  531. }
  532. #endif
  533. HRESULT
  534. CertSrvBlockThreadUntilStop()
  535. {
  536. HRESULT hr;
  537. HANDLE hRegistryModified = NULL;
  538. DWORD dwTimeOut;
  539. // check CRL publish, get next timeout interval
  540. hr = CRLPubWakeupEvent(&dwTimeOut);
  541. _PrintIfError(hr, "CRLPubWakeupEvent");
  542. hr = CertSrvResetRegistryWatch(&hRegistryModified);
  543. _PrintIfError(hr, "CertSrvResetRegistryWatch");
  544. while (TRUE)
  545. {
  546. FILETIME ftWait;
  547. DWORD dw;
  548. HANDLE hmultiObjects[] = {
  549. hRegistryModified,
  550. g_hServiceStoppingEvent,
  551. g_hCRLManualPublishEvent
  552. };
  553. #if DBG_CERTSRV
  554. {
  555. LLFILETIME llft;
  556. WCHAR *pwszTimePeriod = NULL;
  557. llft.ll = dwTimeOut;
  558. llft.ll *= (CVT_BASE / 1000); // convert msecs to 100ns
  559. llft.ll = -llft.ll;
  560. hr = myFileTimePeriodToWszTimePeriod(
  561. &llft.ft,
  562. TRUE, // fExact
  563. &pwszTimePeriod);
  564. _PrintIfError(hr, "myFileTimePeriodToWszTimePeriod");
  565. DBGPRINT((
  566. DBG_SS_CERTSRV,
  567. "WaitForMultipleObjects(%u ms) %ws @%ws\n",
  568. dwTimeOut,
  569. pwszTimePeriod,
  570. certsrvGetCurrentTimeWsz()));
  571. if (NULL != pwszTimePeriod)
  572. {
  573. LocalFree(pwszTimePeriod);
  574. }
  575. }
  576. #endif
  577. GetSystemTimeAsFileTime(&ftWait);
  578. dw = WaitForMultipleObjects(
  579. ARRAYSIZE(hmultiObjects),
  580. hmultiObjects,
  581. FALSE, // any object will cause bailout
  582. dwTimeOut);
  583. DBGPRINT((
  584. DBG_SS_CERTSRV,
  585. "WaitForMultipleObjects(%u ms)->%x, %ws\n",
  586. dwTimeOut,
  587. dw,
  588. certsrvGetCurrentTimeWsz()));
  589. if (WAIT_FAILED == dw)
  590. {
  591. hr = GetLastError();
  592. _JumpError(hr, error, "WaitForMultipleObjects worker");
  593. }
  594. if (dw == WAIT_TIMEOUT) // CRL
  595. {
  596. hr = CRLPubWakeupEvent(&dwTimeOut);
  597. _PrintIfError(hr, "Error during CRLPubWakeupEvent");
  598. DBGPRINT((DBG_SS_CERTSRVI, "CRLPub: TimeOut %u ms\n", dwTimeOut));
  599. }
  600. else if (dw == WAIT_OBJECT_0) // Registry modification
  601. {
  602. // In either case, determine if CRL needs to be published
  603. hr = CertSrvRegistryModificationEvent(&ftWait, &dwTimeOut);
  604. _PrintIfError(hr, "Error during CertSrvRegistryModificationEvent");
  605. // in registry case, reset registry trigger
  606. DBGPRINT((
  607. DBG_SS_CERTSRVI,
  608. "CRLPub: Registry change trigger, TimeOut=%u ms\n",
  609. dwTimeOut));
  610. hr = CertSrvResetRegistryWatch(&hRegistryModified);
  611. _PrintIfError(hr, "Error during CertSrvResetRegistryWatch");
  612. }
  613. else if (dw == WAIT_OBJECT_0 + 1)
  614. {
  615. // found "service done" event
  616. DBGPRINT((DBG_SS_CERTSRV, "Service is pending stop request\n"));
  617. break; // exit wait loop
  618. }
  619. else if (dw == WAIT_OBJECT_0 + 2)
  620. {
  621. // found "g_hCRLManualPublishEvent" event: recalc timeout
  622. hr = CRLPubWakeupEvent(&dwTimeOut);
  623. _PrintIfError(hr, "Error during CRLPubWakeupEvent");
  624. DBGPRINT((
  625. DBG_SS_CERTSRVI,
  626. "CRLPub: Manual publish recalc, TimeOut=%u ms\n",
  627. dwTimeOut));
  628. }
  629. else
  630. {
  631. CSASSERT(!"unexpected wait return");
  632. hr = E_UNEXPECTED;
  633. _JumpError(hr, error, "WaitForMultipleObjects");
  634. }
  635. }
  636. hr = S_OK;
  637. error:
  638. CloseHandle(hRegistryModified);
  639. return hr;
  640. }
  641. // returns TRUE if we shutdown correctly
  642. BOOL
  643. CertSrvStopServer(
  644. IN BOOL fConsoleActive)
  645. {
  646. HRESULT hr;
  647. BOOL fCoInit = FALSE;
  648. BOOL fShutDown = FALSE;
  649. if (!g_fStartInProgress) // ignore while starting the server
  650. {
  651. fShutDown = TRUE;
  652. DBGPRINT((
  653. DBG_SS_CERTSRV,
  654. "CertSrvStopServer(fConsoleActive=%u, tid=%d)\n",
  655. fConsoleActive,
  656. GetCurrentThreadId()));
  657. SetEvent(g_hServiceStoppingEvent);
  658. if (g_hkeyCABase)
  659. {
  660. RegCloseKey(g_hkeyCABase);
  661. g_hkeyCABase = NULL;
  662. }
  663. hr = CoInitializeEx(NULL, GetCertsrvComThreadingModel());
  664. if (S_OK != hr && S_FALSE != hr)
  665. {
  666. _JumpError(hr, error, "CoInitializeEx");
  667. }
  668. fCoInit = TRUE;
  669. // don't allow new callers in
  670. hr = RPCTeardown();
  671. _PrintIfError(hr, "RPCTeardown");
  672. CertStopClassFactories();
  673. CoreTerminate();
  674. if (g_fStarted)
  675. {
  676. if (CERTLOG_TERSE <= g_dwLogLevel)
  677. {
  678. LogEventString(
  679. EVENTLOG_INFORMATION_TYPE,
  680. MSG_I_SERVER_STOPPED,
  681. g_wszCommonName);
  682. }
  683. CONSOLEPRINT0((
  684. DBG_SS_CERTSRV,
  685. "Certification Authority Service Stopped\n"));
  686. {
  687. //only perform Hash if the auditing is enabled
  688. if(AUDIT_FILTER_STARTSTOP & g_dwAuditFilter)
  689. {
  690. CertSrv::CAuditEvent event(
  691. SE_AUDITID_CERTSRV_SERVICESTOP,
  692. g_dwAuditFilter);
  693. hr = ComputeMAC(g_wszDatabase, &g_pwszDBFileHash);
  694. _JumpIfErrorStr(hr, error, "ComputeMAC", g_wszDatabase);
  695. hr = event.AddData(g_pwszDBFileHash); // %1 database hash
  696. g_pwszDBFileHash.Cleanup();
  697. _JumpIfError(hr, error, "CAuditEvent::AddData");
  698. //
  699. // ... add code for retrieving key usage count from CSP
  700. //
  701. hr = event.AddData((DWORD)0); // %2 key usage count
  702. _JumpIfError(hr, error, "CAuditEvent::AddData");
  703. hr = event.Report();
  704. _JumpIfError(hr, error, "CAuditEvent::Report");
  705. }
  706. }
  707. }
  708. g_fStarted = FALSE;
  709. AuthzFreeResourceManager(g_AuthzCertSrvRM);
  710. g_AuthzCertSrvRM = NULL;
  711. g_CASD.Uninitialize();
  712. g_OfficerRightsSD.Uninitialize();
  713. // set "completely stopped" event
  714. if (!fConsoleActive)
  715. {
  716. SetEvent(g_hServiceStoppedEvent);
  717. }
  718. }
  719. error:
  720. if (fCoInit)
  721. {
  722. CoUninitialize();
  723. }
  724. return(fShutDown);
  725. }
  726. // Control-C handler
  727. BOOL
  728. StopServer(
  729. IN DWORD dwCtrlType)
  730. {
  731. HRESULT hr;
  732. // if successful shutdown
  733. if (SendMessage(g_hwndMain, WM_STOPSERVER, 0, 0))
  734. {
  735. if (!PostMessage(g_hwndMain, WM_SYNC_CLOSING_THREADS, S_OK, 0))
  736. {
  737. hr = myHLastError();
  738. _PrintError(hr, "PostMessage");
  739. }
  740. SetConsoleCtrlHandler(StopServer, FALSE);
  741. }
  742. return(TRUE);
  743. }
  744. HRESULT
  745. CertSrvEnterServer(
  746. OUT DWORD *pState)
  747. {
  748. HRESULT hr;
  749. BOOL fEntered = FALSE;
  750. *pState = 0; // Caller need not exit server
  751. if (!g_fShutdownCritSec)
  752. {
  753. hr = HRESULT_FROM_WIN32(ERROR_DLL_INIT_FAILED);
  754. _JumpError(hr, error, "InitializeCriticalSection");
  755. }
  756. EnterCriticalSection(&g_ShutdownCriticalSection);
  757. fEntered = TRUE;
  758. hr = CertSrvTestServerState();
  759. _JumpIfError(hr, error, "CertSrvTestServerState");
  760. g_cCalls++;
  761. g_cCallsActive++;
  762. *pState = 1; // Caller must exit server
  763. hr = S_OK;
  764. error:
  765. if (fEntered)
  766. {
  767. LeaveCriticalSection(&g_ShutdownCriticalSection);
  768. }
  769. return(hr);
  770. }
  771. HRESULT
  772. CertSrvTestServerState()
  773. {
  774. HRESULT hr;
  775. if (g_fRefuseIncoming)
  776. {
  777. hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS);
  778. _JumpError(hr, error, "g_fRefuseIncoming");
  779. }
  780. hr = S_OK;
  781. error:
  782. return(hr);
  783. }
  784. HRESULT
  785. CertSrvLockServer(
  786. IN OUT DWORD *pState)
  787. {
  788. HRESULT hr;
  789. BOOL fEntered = FALSE;
  790. // Eliminate this thread from the active thread count
  791. CertSrvExitServer(*pState);
  792. *pState = 0; // Caller no longer needs to exit server
  793. if (!g_fShutdownCritSec)
  794. {
  795. hr = HRESULT_FROM_WIN32(ERROR_DLL_INIT_FAILED);
  796. _JumpError(hr, error, "InitializeCriticalSection");
  797. }
  798. EnterCriticalSection(&g_ShutdownCriticalSection);
  799. fEntered = TRUE;
  800. g_fRefuseIncoming = TRUE;
  801. DBShutDown(TRUE);
  802. DBGPRINT((DBG_SS_CERTSRV, "LockServer(thread count = %u)\n", g_cCallsActive));
  803. while (0 < g_cCallsActive)
  804. {
  805. LeaveCriticalSection(&g_ShutdownCriticalSection);
  806. // Wait 15 seconds plus 2 seconds for each active call.
  807. hr = WaitForSingleObject(
  808. g_hShutdownEvent,
  809. (15 + 2 * g_cCallsActive) * 1000);
  810. EnterCriticalSection(&g_ShutdownCriticalSection);
  811. _PrintIfError(hr, "WaitForSingleObject");
  812. if ((HRESULT) WAIT_OBJECT_0 == hr)
  813. {
  814. DBGPRINT((DBG_SS_CERTSRV, "LockServer(last thread exit event)\n"));
  815. }
  816. else if ((HRESULT) WAIT_TIMEOUT == hr)
  817. {
  818. DBGPRINT((DBG_SS_CERTSRV, "LockServer(timeout)\n"));
  819. break;
  820. }
  821. else if ((HRESULT) WAIT_ABANDONED == hr)
  822. {
  823. DBGPRINT((DBG_SS_CERTSRV, "LockServer(wait abandoned)\n"));
  824. }
  825. DBGPRINT((DBG_SS_CERTSRV, "LockServer(thread count = %u)\n", g_cCallsActive));
  826. }
  827. DBGPRINT((DBG_SS_CERTSRV, "LockServer(done: thread count = %u)\n", g_cCallsActive));
  828. hr = S_OK;
  829. error:
  830. if (fEntered)
  831. {
  832. LeaveCriticalSection(&g_ShutdownCriticalSection);
  833. }
  834. return(hr);
  835. }
  836. VOID
  837. CertSrvExitServer(
  838. IN DWORD State)
  839. {
  840. HRESULT hr;
  841. BOOL fEntered = FALSE;
  842. if (!g_fShutdownCritSec)
  843. {
  844. hr = HRESULT_FROM_WIN32(ERROR_DLL_INIT_FAILED);
  845. _JumpError(hr, error, "InitializeCriticalSection");
  846. }
  847. EnterCriticalSection(&g_ShutdownCriticalSection);
  848. fEntered = TRUE;
  849. if (State)
  850. {
  851. CSASSERT(0 < g_cCallsActive);
  852. if (0 == --g_cCallsActive && g_fRefuseIncoming)
  853. {
  854. DBGPRINT((DBG_SS_CERTSRV, "ExitServer(set last thread exit event)\n"));
  855. SetEvent(g_hShutdownEvent);
  856. }
  857. }
  858. error:
  859. if (fEntered)
  860. {
  861. LeaveCriticalSection(&g_ShutdownCriticalSection);
  862. }
  863. }
  864. // Test for alignment faults in the C runtimes.
  865. // If the bug hasn't been fixed yet, log an event during cert server startup.
  866. VOID
  867. certsrvLogAlignmentFaultStatus()
  868. {
  869. HRESULT hr;
  870. HRESULT hr2;
  871. ULONG_PTR ExceptionAddress;
  872. WCHAR awcAddress[2 + 2 * cwcDWORDSPRINTF];
  873. WCHAR const *apwsz[2];
  874. WORD cpwsz;
  875. WCHAR awchr[cwcHRESULTSTRING];
  876. WCHAR const *pwszStringErr = NULL;
  877. apwsz[1] = NULL;
  878. __try
  879. {
  880. fwprintf(stdout, L"."); // may fault if I/O buffer is odd aligned
  881. fprintf(stdout, ".");
  882. fwprintf(stdout, L".\n"); // may fault if I/O buffer is odd aligned
  883. hr = S_OK;
  884. }
  885. __except(
  886. ExceptionAddress = (ULONG_PTR) (GetExceptionInformation())->ExceptionRecord->ExceptionAddress,
  887. hr = myHEXCEPTIONCODE(),
  888. EXCEPTION_EXECUTE_HANDLER)
  889. {
  890. _PrintError(hr, "certsrvLogAlignmentFaultStatus: Exception");
  891. }
  892. if (S_OK != hr)
  893. {
  894. ALIGNIOB(stdout); // align the stdio buffer
  895. wprintf(L"STDIO exception: 0x%x\n", hr);
  896. wsprintf(awcAddress, L"0x%p", ExceptionAddress);
  897. CSASSERT(wcslen(awcAddress) < ARRAYSIZE(awcAddress));
  898. apwsz[0] = awcAddress;
  899. pwszStringErr = myGetErrorMessageText(hr, TRUE);
  900. apwsz[1] = pwszStringErr;
  901. if (NULL == pwszStringErr)
  902. {
  903. apwsz[1] = myHResultToString(awchr, hr);
  904. }
  905. cpwsz = ARRAYSIZE(apwsz);
  906. hr2 = LogEvent(
  907. EVENTLOG_WARNING_TYPE,
  908. MSG_E_STARTUP_EXCEPTION,
  909. cpwsz,
  910. apwsz);
  911. _JumpIfError(hr2, error, "LogEvent");
  912. }
  913. error:
  914. if (NULL != pwszStringErr)
  915. {
  916. LocalFree(const_cast<WCHAR *>(pwszStringErr));
  917. }
  918. }
  919. #define MSTOSEC(ms) (((ms) + 1000 - 1)/1000)
  920. FNLOGEXCEPTION certsrvLogException;
  921. HRESULT
  922. certsrvStartServer(
  923. IN BOOL fConsoleActive)
  924. {
  925. HRESULT hr;
  926. DWORD TimeStart;
  927. WCHAR awc[ARRAYSIZE(SAFEBOOT_DSREPAIR_STR_W)];
  928. DWORD cwc;
  929. DWORD dwEventType = EVENTLOG_ERROR_TYPE;
  930. DWORD dwIdEvent = 0;
  931. g_fStartInProgress = TRUE;
  932. DBGPRINT((
  933. DBG_SS_CERTSRV,
  934. "StartServer(tid=%d, fConsoleActive=%u)\n",
  935. GetCurrentThreadId(),
  936. fConsoleActive));
  937. TimeStart = GetTickCount();
  938. if (fConsoleActive)
  939. {
  940. g_fStartAsService = FALSE;
  941. SetConsoleCtrlHandler(StopServer, TRUE);
  942. }
  943. if (!FIsServer())
  944. {
  945. // don't allow startup on non-server SKU
  946. hr = HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION);
  947. _JumpError(hr, error, "FIsServer");
  948. }
  949. cwc = GetEnvironmentVariable(L"SAFEBOOT_OPTION", awc, ARRAYSIZE(awc));
  950. if (0 != cwc &&
  951. ARRAYSIZE(awc) > cwc &&
  952. 0 == lstrcmpi(awc, SAFEBOOT_DSREPAIR_STR_W))
  953. {
  954. // log an error to the event log and stop immediately
  955. dwEventType = EVENTLOG_INFORMATION_TYPE;
  956. dwIdEvent = MSG_SAFEBOOT_DETECTED;
  957. hr = HRESULT_FROM_WIN32(ERROR_RETRY);
  958. _JumpError(hr, error, "Not starting service: booted in DSRepair mode");
  959. }
  960. g_fAdvancedServer = FIsAdvancedServer();
  961. if (!AuthzInitializeResourceManager(
  962. 0,
  963. CallbackAccessCheck,
  964. NULL,
  965. NULL,
  966. L"CertSrv",
  967. &g_AuthzCertSrvRM))
  968. {
  969. hr = myHLastError();
  970. _PrintError(hr, "AuthzInitializeResourceManager");
  971. if (E_INVALIDARG != hr || (2 > g_fAdvancedServer && IsWhistler()))
  972. {
  973. goto error;
  974. }
  975. }
  976. hr = CoreInit();
  977. if (S_OK != hr)
  978. {
  979. dwIdEvent = MAXDWORD; // Error event already logged
  980. _JumpError(hr, error, "CoreInit");
  981. }
  982. certsrvLogAlignmentFaultStatus();
  983. myLogExceptionInit(certsrvLogException);
  984. hr = RPCInit();
  985. if (S_OK != hr)
  986. {
  987. dwIdEvent = MSG_E_RPC_INIT;
  988. _JumpError(hr, error, "RPCInit");
  989. }
  990. hr = SetRegistryDcomConfig(fConsoleActive);
  991. if (S_OK != hr)
  992. {
  993. dwIdEvent = MSG_E_REGISTRY_DCOM;
  994. _JumpError(hr, error, "SetRegistryDcomConfig");
  995. }
  996. hr = CertStartClassFactories();
  997. if (S_OK != hr)
  998. {
  999. dwIdEvent = CO_E_WRONG_SERVER_IDENTITY == hr?
  1000. MSG_E_SERVER_IDENTITY : MSG_E_CLASS_FACTORIES;
  1001. _JumpError(hr, error, "CertStartClassFactories");
  1002. }
  1003. {
  1004. //only perform Hash if the auditing is enabled
  1005. if(AUDIT_FILTER_STARTSTOP & g_dwAuditFilter)
  1006. {
  1007. CertSrv::CAuditEvent event(
  1008. SE_AUDITID_CERTSRV_SERVICESTART,
  1009. g_dwAuditFilter);
  1010. hr = event.AddData(g_pwszDBFileHash); // %1 database hash
  1011. g_pwszDBFileHash.Cleanup();
  1012. _JumpIfError(hr, error, "CAuditEvent::AddData");
  1013. //
  1014. // ... add code for retrieving key usage count from CSP
  1015. //
  1016. hr = event.AddData((DWORD)0); // %2 key usage count
  1017. _JumpIfError(hr, error, "CAuditEvent::AddData");
  1018. hr = event.Report();
  1019. _JumpIfError(hr, error, "CAuditEvent::Report");
  1020. }
  1021. }
  1022. {
  1023. CertSrv::CAuditEvent event(
  1024. SE_AUDITID_CERTSRV_ROLESEPARATIONSTATE,
  1025. g_dwAuditFilter);
  1026. hr = event.AddData(CAuditEvent::RoleSeparationIsEnabled()); // %1 is role separation enabled?
  1027. _JumpIfError(hr, error, "CAuditEvent::AddData");
  1028. hr = event.Report();
  1029. _JumpIfError(hr, error, "CAuditEvent::Report");
  1030. }
  1031. if (CERTLOG_TERSE <= g_dwLogLevel)
  1032. {
  1033. LogEventString(
  1034. EVENTLOG_INFORMATION_TYPE,
  1035. MSG_I_SERVER_STARTED,
  1036. g_wszCommonName);
  1037. }
  1038. CONSOLEPRINT1((
  1039. DBG_SS_CERTSRV,
  1040. "Certification Authority Service Ready (%us) ...\n",
  1041. MSTOSEC(GetTickCount() - TimeStart)));
  1042. g_fStarted = TRUE;
  1043. CSASSERT(S_OK == hr);
  1044. error:
  1045. if (S_OK != hr)
  1046. {
  1047. if (MAXDWORD != dwIdEvent)
  1048. {
  1049. if (0 == dwIdEvent)
  1050. {
  1051. dwIdEvent = MSG_E_GENERIC_STARTUP_FAILRE;
  1052. }
  1053. LogEventStringHResult(
  1054. dwEventType,
  1055. dwIdEvent,
  1056. g_wszCommonName,
  1057. EVENTLOG_INFORMATION_TYPE == dwEventType? S_OK : hr);
  1058. }
  1059. CertSrvStopServer(fConsoleActive);
  1060. // returning error here results in repost to scm
  1061. }
  1062. g_fStartInProgress = FALSE;
  1063. return(hr);
  1064. }
  1065. VOID
  1066. certsrvLogException(
  1067. IN HRESULT hrEvent,
  1068. IN EXCEPTION_POINTERS const *pep,
  1069. OPTIONAL IN char const *pszFileName,
  1070. IN DWORD dwFile,
  1071. IN DWORD dwLine)
  1072. {
  1073. HRESULT hr;
  1074. WCHAR awcFile[2 + 3 * cwcDWORDSPRINTF];
  1075. WCHAR awcFlags[3 + cwcDWORDSPRINTF];
  1076. WCHAR awcAddress[2 + 2 * cwcDWORDSPRINTF];
  1077. WCHAR const *apwsz[4];
  1078. WORD cpwsz;
  1079. WCHAR awchr[cwcHRESULTSTRING];
  1080. WCHAR const *pwszStringErr = NULL;
  1081. wsprintf(awcFile, L"%u.%u.%u", dwFile, dwLine, MSG_E_EXCEPTION);
  1082. CSASSERT(wcslen(awcFile) < ARRAYSIZE(awcFile));
  1083. wsprintf(awcFlags, L"0x%08x", pep->ExceptionRecord->ExceptionFlags);
  1084. CSASSERT(wcslen(awcFlags) < ARRAYSIZE(awcFlags));
  1085. wsprintf(awcAddress, L"0x%p", pep->ExceptionRecord->ExceptionAddress);
  1086. CSASSERT(wcslen(awcAddress) < ARRAYSIZE(awcAddress));
  1087. apwsz[0] = awcFile;
  1088. apwsz[1] = awcAddress;
  1089. apwsz[2] = awcFlags;
  1090. pwszStringErr = myGetErrorMessageText(hrEvent, TRUE);
  1091. apwsz[3] = pwszStringErr;
  1092. if (NULL == pwszStringErr)
  1093. {
  1094. apwsz[3] = myHResultToString(awchr, hrEvent);
  1095. }
  1096. cpwsz = ARRAYSIZE(apwsz);
  1097. hr = LogEvent(EVENTLOG_ERROR_TYPE, MSG_E_EXCEPTION, cpwsz, apwsz);
  1098. _JumpIfError(hr, error, "LogEvent");
  1099. error:
  1100. if (NULL != pwszStringErr)
  1101. {
  1102. LocalFree(const_cast<WCHAR *>(pwszStringErr));
  1103. }
  1104. }
  1105. DWORD
  1106. CertSrvStartServerThread(
  1107. IN VOID *pvArg)
  1108. {
  1109. HRESULT hr = S_OK;
  1110. DWORD Flags = (DWORD) (ULONG_PTR) pvArg;
  1111. BOOL b;
  1112. ULONG_PTR ulp;
  1113. // Anatomy of startup code
  1114. // if g_fStartAsService, just registers this new thread as the main
  1115. // thread and blocks until the ServiceMain fxn returns.
  1116. // We're in a non-rpc thread; check if we need to create VRoots. I would
  1117. // have liked to have moved this into CoreInit, but we're limited in where
  1118. // we can do this (can't be calling into RPC during RPC call).
  1119. //
  1120. // If the SetupStatus SETUP_ATTEMPT_VROOT_CREATE registry flag is clear,
  1121. // this call is a nop. A separate thread is created to access the IIS
  1122. // metabase. If it hangs, it will be nuked -- after the specified timeout.
  1123. // This call returns immediately, so the only detectable error is likely
  1124. // to be a thread creation problem.
  1125. // if we're doing anything other than starting the service controller,
  1126. // check to see if the vroots need to be created.
  1127. if (0 == (Flags & CSST_STARTSERVICECONTROLLER))
  1128. {
  1129. WCHAR *pwszPath = NULL;
  1130. DWORD cb = sizeof(ENUM_CATYPES);
  1131. DWORD dwType;
  1132. ENUM_CATYPES CAType = ENUM_UNKNOWN_CA;
  1133. HKEY hkey = NULL;
  1134. hr = myRegOpenRelativeKey(
  1135. NULL,
  1136. L"ca",
  1137. RORKF_CREATESUBKEYS,
  1138. &pwszPath,
  1139. NULL, // ppwszName
  1140. &hkey);
  1141. _PrintIfError(hr, "myRegOpenRelativeKey");
  1142. if (S_OK == hr)
  1143. {
  1144. DBGPRINT((DBG_SS_CERTLIBI, "%ws\n", pwszPath));
  1145. cb = sizeof(CAType);
  1146. hr = RegQueryValueEx(
  1147. hkey,
  1148. wszREGCATYPE,
  1149. NULL,
  1150. &dwType,
  1151. (BYTE *) &CAType,
  1152. &cb);
  1153. _PrintIfErrorStr(hr, "RegQueryValueEx", wszREGCATYPE);
  1154. }
  1155. if (pwszPath)
  1156. LocalFree(pwszPath);
  1157. if (hkey)
  1158. RegCloseKey(hkey);
  1159. hr = myModifyVirtualRootsAndFileShares(
  1160. VFF_CREATEVROOTS | // Create VRoots
  1161. VFF_CREATEFILESHARES | // Create File Shares
  1162. VFF_CHECKREGFLAGFIRST | // Skip if reg flag clear
  1163. VFF_CLEARREGFLAGFIRST, // Clear flag before attempt
  1164. CAType,
  1165. TRUE, // asynch call -- don't block
  1166. VFCSEC_TIMEOUT, // wait this long before giving up
  1167. NULL,
  1168. NULL);
  1169. if (S_OK != hr)
  1170. {
  1171. LogEventHResult(
  1172. EVENTLOG_INFORMATION_TYPE,
  1173. MSG_E_IIS_INTEGRATION_ERROR,
  1174. hr);
  1175. }
  1176. }
  1177. // StartServiceCtrlDispatcher should hang until certsrv terminates
  1178. if ((CSST_STARTSERVICECONTROLLER & Flags) &&
  1179. !StartServiceCtrlDispatcher(steDispatchTable))
  1180. {
  1181. hr = myHLastError();
  1182. if (HRESULT_FROM_WIN32(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) != hr)
  1183. {
  1184. _JumpError(hr, error, "StartServiceCtrlDispatcher");
  1185. }
  1186. CONSOLEPRINT0((
  1187. DBG_SS_CERTSRV,
  1188. "CertSrv: Failed to connect to service controller -- running in standalone mode\n"));
  1189. Flags &= ~CSST_STARTSERVICECONTROLLER;
  1190. Flags |= CSST_CONSOLE;
  1191. }
  1192. if (0 == (CSST_STARTSERVICECONTROLLER & Flags))
  1193. {
  1194. DBGPRINT((
  1195. DBG_SS_CERTSRVI,
  1196. "SendMessageTimeout(tid=%d, hwnd=0x%x, msg=0x%x)\n",
  1197. GetCurrentThreadId(),
  1198. g_hwndMain,
  1199. WM_STARTSERVER));
  1200. b = SendMessageTimeout(
  1201. g_hwndMain,
  1202. WM_STARTSERVER,
  1203. (CSST_CONSOLE & Flags)? TRUE : FALSE, // fConsoleActive
  1204. 0,
  1205. SMTO_BLOCK,
  1206. MAXLONG,
  1207. &ulp) != 0;
  1208. if (!b)
  1209. {
  1210. hr = myHLastError();
  1211. _JumpError(hr, error, "SendMessageTimeout");
  1212. }
  1213. else if (ulp != S_OK)
  1214. {
  1215. hr = (HRESULT) ulp;
  1216. _JumpError(hr, error, "SendMessageTimeout");
  1217. }
  1218. }
  1219. if (Flags & CSST_CONSOLE)
  1220. {
  1221. // we're running as console, and so don't have a CRL publishing thread.
  1222. // Use this one since no one cares if it returns
  1223. // if svc, we do this in the caller of this function
  1224. CertSrvBlockThreadUntilStop();
  1225. }
  1226. error:
  1227. // on return, this thread dies
  1228. return(hr);
  1229. }
  1230. VOID
  1231. Usage(
  1232. IN BOOL fUsageInternal)
  1233. {
  1234. WCHAR awcUsage[2048];
  1235. if (LoadString(NULL, IDS_USAGE, awcUsage, ARRAYSIZE(awcUsage)))
  1236. {
  1237. CONSOLEPRINT1((MAXDWORD, "%ws", awcUsage));
  1238. }
  1239. if (fUsageInternal)
  1240. {
  1241. if (LoadString(NULL, IDS_USAGE_FULL, awcUsage, ARRAYSIZE(awcUsage)))
  1242. {
  1243. CONSOLEPRINT1((MAXDWORD, "%ws", awcUsage));
  1244. }
  1245. #if DBG_COMTEST
  1246. if (LoadString(NULL, IDS_USAGE_COMTEST, awcUsage, ARRAYSIZE(awcUsage)))
  1247. {
  1248. CONSOLEPRINT1((MAXDWORD, "%ws", awcUsage));
  1249. }
  1250. #endif
  1251. }
  1252. }
  1253. int
  1254. ArgvParseCommandLine(
  1255. IN int argc,
  1256. IN WCHAR *argv[])
  1257. {
  1258. HRESULT hr;
  1259. myVerifyResourceStrings(g_hInstApp);
  1260. hr = E_INVALIDARG;
  1261. while (1 < argc && (L'-' == argv[1][0] || L'/' == argv[1][0]))
  1262. {
  1263. WCHAR *pwsz = argv[1];
  1264. BOOL fUsage = FALSE;
  1265. BOOL fUsageInternal = FALSE;
  1266. while (*++pwsz != L'\0')
  1267. {
  1268. switch (*pwsz)
  1269. {
  1270. #if DBG_COMTEST
  1271. case L'C':
  1272. case L'c':
  1273. fComTest = TRUE;
  1274. break;
  1275. #endif
  1276. case L'N':
  1277. case L'n':
  1278. g_fCreateDB = TRUE;
  1279. break;
  1280. case L'Z':
  1281. case L'z':
  1282. g_fStartAsService = FALSE;
  1283. break;
  1284. case L'S':
  1285. case L's':
  1286. g_fCryptSilent = TRUE;
  1287. break;
  1288. case L'?':
  1289. case L'u':
  1290. fUsage = TRUE;
  1291. if (0 == lstrcmp(pwsz, L"uSAGE"))
  1292. {
  1293. fUsageInternal = TRUE;
  1294. }
  1295. // FALLTHROUGH
  1296. default:
  1297. Usage(fUsageInternal);
  1298. if (fUsage)
  1299. {
  1300. goto error;
  1301. }
  1302. _JumpError(hr, error, "bad command line option");
  1303. }
  1304. }
  1305. argc--;
  1306. argv++;
  1307. }
  1308. if (argc != 1)
  1309. {
  1310. Usage(FALSE);
  1311. _JumpError(hr, error, "extra args");
  1312. }
  1313. hr = S_OK;
  1314. error:
  1315. return(hr);
  1316. }
  1317. typedef int (FNARGVMAIN)(
  1318. IN int argc,
  1319. IN WCHAR *argv[]);
  1320. //+------------------------------------------------------------------------
  1321. // FUNCTION: CertArgvMainDispatch
  1322. //
  1323. // NOTES: Takes a WCHAR * command line and chews it up into argc/argv
  1324. // form so it can be passed on to a traditional C-style main.
  1325. //-------------------------------------------------------------------------
  1326. int
  1327. CertArgvMainDispatch(
  1328. IN FNARGVMAIN *pfnMain,
  1329. IN WCHAR *pwszAppName,
  1330. IN WCHAR const *pwszCmdLine)
  1331. {
  1332. WCHAR buf[MAX_PATH];
  1333. WCHAR *apwszArg[20];
  1334. int cArg = 0;
  1335. LPWSTR p = buf;
  1336. WCHAR wcEnd;
  1337. apwszArg[cArg++] = pwszAppName;
  1338. while (*pwszCmdLine != L'\0')
  1339. {
  1340. while (*pwszCmdLine == L' ')
  1341. {
  1342. pwszCmdLine++;
  1343. }
  1344. if (*pwszCmdLine != L'\0')
  1345. {
  1346. wcEnd = L' ';
  1347. if (*pwszCmdLine == L'"')
  1348. {
  1349. wcEnd = *pwszCmdLine++;
  1350. }
  1351. apwszArg[cArg++] = p;
  1352. while (*pwszCmdLine != L'\0' && *pwszCmdLine != wcEnd)
  1353. {
  1354. *p++ = *pwszCmdLine++;
  1355. }
  1356. *p++ = L'\0';
  1357. if (*pwszCmdLine != L'\0')
  1358. {
  1359. pwszCmdLine++; // skip blank or quote character
  1360. }
  1361. }
  1362. }
  1363. apwszArg[cArg] = NULL;
  1364. return((*pfnMain)(cArg, apwszArg));
  1365. }
  1366. //+------------------------------------------------------------------------
  1367. // FUNCTION: MainWndProc(...)
  1368. //-------------------------------------------------------------------------
  1369. LRESULT APIENTRY
  1370. MainWndProc(
  1371. IN HWND hWnd,
  1372. IN UINT msg,
  1373. IN WPARAM wParam,
  1374. IN LPARAM lParam)
  1375. {
  1376. WCHAR *pwszCmdLine;
  1377. HRESULT hr;
  1378. LPARAM lRet = 0;
  1379. DBGPRINT((
  1380. DBG_SS_CERTSRVI,
  1381. "MainWndProc(tid=%d) msg=0x%x, wp=0x%x, lp=0x%x\n",
  1382. GetCurrentThreadId(),
  1383. msg,
  1384. wParam,
  1385. lParam));
  1386. switch (msg)
  1387. {
  1388. case WM_CREATE:
  1389. case WM_SIZE:
  1390. break;
  1391. case WM_DESTROY:
  1392. if (!g_fStartAsService)
  1393. PostQuitMessage(S_OK);
  1394. break;
  1395. case WM_ENDSESSION:
  1396. // only stop on a real shutdown,
  1397. // never look at this msg if running as svc
  1398. if (g_fStartAsService || (0 == wParam) || (0 != lParam))
  1399. {
  1400. break;
  1401. }
  1402. // fall through
  1403. case WM_STOPSERVER:
  1404. lRet = CertSrvStopServer(!g_fStartAsService);
  1405. break;
  1406. case WM_SYNC_CLOSING_THREADS:
  1407. hr = (HRESULT) lParam;
  1408. // sync: wait for SCM to return control to exiting CertSrvStartServerThread
  1409. if (WAIT_OBJECT_0 != WaitForSingleObject(g_hServiceThread, 10 * 1000))
  1410. {
  1411. hr = WAIT_TIMEOUT;
  1412. }
  1413. PostQuitMessage(hr);
  1414. break;
  1415. case WM_STARTSERVER:
  1416. hr = CoInitializeEx(NULL, GetCertsrvComThreadingModel());
  1417. if (S_FALSE == hr)
  1418. {
  1419. hr = S_OK;
  1420. }
  1421. if (S_OK != hr)
  1422. {
  1423. LogEventString(
  1424. EVENTLOG_ERROR_TYPE,
  1425. MSG_E_OLE_INIT_FAILED,
  1426. NULL);
  1427. _PrintError(hr, "CoInitializeEx");
  1428. }
  1429. else
  1430. {
  1431. hr = certsrvStartServer((BOOL) wParam);
  1432. _PrintIfError(hr, "certsrvStartServer");
  1433. }
  1434. if (S_OK != hr)
  1435. {
  1436. if ((BOOL) wParam) // fConsoleActive
  1437. {
  1438. PostQuitMessage(hr);
  1439. }
  1440. lRet = hr; // set this so caller knows we failed
  1441. }
  1442. break;
  1443. case WM_SUSPENDSERVER:
  1444. break;
  1445. case WM_RESTARTSERVER:
  1446. break;
  1447. default:
  1448. lRet = DefWindowProc(hWnd, msg, wParam, lParam);
  1449. }
  1450. return(lRet);
  1451. }
  1452. /*
  1453. Complete anatomy of certificate server startup/shutdown
  1454. WinMain():
  1455. |
  1456. |g_hSvcThread = CreateThread(CertSrvStartServerThread(SVC_CONTROLLER))
  1457. | |
  1458. |[MessageLoop \
  1459. | processing CertSrvStartServerThread(SVC_CONTROLLER):
  1460. | until |StartSvcCtrlDispatcher(ServiceMain)
  1461. | WM_QUIT] ||ServiceMain:
  1462. | ||RegisterSvcCtrlHandler(ServiceControlHandler())
  1463. | ||hStartThread = CreateThread(CertSrvStartServerThread(0))
  1464. | || |
  1465. | || \
  1466. | || CertSrvStartServerThread(0):
  1467. | || |SendMessage(WM_STARTSERVER)
  1468. | || \return // CertSrvStartServerThread(0)
  1469. | || (Thread Terminates)
  1470. | ||WaitForSingleObject(hStartThread), pinging SCM
  1471. | ||CertSrvBlockThreadUntilStop()
  1472. | |||WaitForSingleObject(g_hSvcStoppingEvent) ***steady state***
  1473. | ||\return // CertSrvBlockThreadUntilStop()
  1474. | ||WaitForSingleObject(g_hSvcStoppedEvent), pinging SCM
  1475. | ||PostMessage(WM_SYNC_CLOSING_THREADS)
  1476. | |\return // StartSvcCtrlDispatcher(ServiceMain)
  1477. | \return // CertSrvStartServerThread(SVC_CONTROLLER)
  1478. | (Thread Terminates)
  1479. | WM_QUIT:
  1480. \ return
  1481. (Process Terminates)
  1482. ServiceControlHandler special functions:
  1483. SERVICE_CONTROL_STOP:
  1484. |PostMessage(WM_STOPSERVER)
  1485. \break
  1486. MessageLoop special functions:
  1487. WM_SYNC_CLOSING_THREADS:
  1488. |WaitForSingleObject(g_hSvcThread)
  1489. |PostQuitMessage() // WM_QUIT to msgloop
  1490. \break
  1491. WM_STOPSERVER:
  1492. |CertSrvStopServer():
  1493. || Signal(g_hServiceStoppingEvent)
  1494. || Signal(g_hServiceStoppedEvent)
  1495. |\ return // CertSrvStopServer()
  1496. \break
  1497. */
  1498. //+------------------------------------------------------------------------
  1499. // Function: wWinMain()
  1500. //
  1501. // Synopsis: Entry Point
  1502. //
  1503. // Arguments: [hInstance] -- Instance handle
  1504. // [hPrevInstance] -- Obsolete
  1505. // [lpCmdLine] -- App command line
  1506. // [nCmdShow] -- Starting show state
  1507. //-------------------------------------------------------------------------
  1508. extern "C" int APIENTRY
  1509. wWinMain(
  1510. IN HINSTANCE hInstance,
  1511. IN HINSTANCE hPrevInstance,
  1512. IN LPWSTR lpCmdLine,
  1513. IN int nCmdShow)
  1514. {
  1515. MSG msg;
  1516. WNDCLASSEX wcApp;
  1517. ATOM atomClass;
  1518. HRESULT hr;
  1519. BOOL fCoInit = FALSE;
  1520. WCHAR awchr[cwcHRESULTSTRING];
  1521. WCHAR const *pwszMsgAlloc;
  1522. WCHAR const *pwszMsg;
  1523. #if DBG_CERTSRV
  1524. WCHAR *pwszThreadModel = NULL;
  1525. #endif
  1526. _setmode(_fileno(stdout), _O_TEXT);
  1527. _wsetlocale(LC_ALL, L".OCP");
  1528. DBGPRINTINIT("+certsrv.log");
  1529. DBGPRINT((DBG_SS_CERTSRVI, "Main Thread = %x\n", GetCurrentThreadId()));
  1530. g_dwDelay0 = GetRegistryDwordValue(L"Delay0");
  1531. g_dwDelay1 = GetRegistryDwordValue(L"Delay1");
  1532. g_dwDelay2 = GetRegistryDwordValue(L"Delay2");
  1533. if (0 != g_dwDelay0)
  1534. {
  1535. DBGPRINT((
  1536. DBG_SS_CERTSRV,
  1537. "wWinMain(0): sleeping %u seconds\n",
  1538. g_dwDelay0));
  1539. Sleep(1000 * g_dwDelay0);
  1540. }
  1541. // Save the current instance
  1542. g_hInstApp = hInstance;
  1543. ZeroMemory(&wcApp, sizeof(wcApp));
  1544. // Set up the application's window class
  1545. wcApp.cbSize = sizeof(wcApp);
  1546. wcApp.lpfnWndProc = MainWndProc;
  1547. wcApp.hInstance = hInstance;
  1548. wcApp.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  1549. wcApp.hCursor = LoadCursor(NULL, IDC_ARROW);
  1550. wcApp.hbrBackground = NULL; // try to not pull in GDI32
  1551. wcApp.lpszClassName = g_wszAppName;
  1552. atomClass = RegisterClassEx(&wcApp);
  1553. if (!atomClass)
  1554. {
  1555. hr = myHLastError();
  1556. _JumpError(hr, error, "RegisterClassEx");
  1557. }
  1558. // Create Main Window
  1559. g_hwndMain = CreateWindowEx(
  1560. 0, // dwExStyle
  1561. (WCHAR const *) atomClass, // lpClassName
  1562. L"Certification Authority",// lpWindowName
  1563. WS_OVERLAPPEDWINDOW, // dwStyle
  1564. //0, // dwStyle
  1565. CW_USEDEFAULT, // x
  1566. CW_USEDEFAULT, // y
  1567. CW_USEDEFAULT, // nWidth
  1568. CW_USEDEFAULT, // nHeight
  1569. NULL, // hWndParent
  1570. NULL, // hMenu
  1571. hInstance, // hInstance
  1572. NULL); // lpParam
  1573. if (NULL == g_hwndMain)
  1574. {
  1575. hr = myHLastError();
  1576. _JumpError(hr, error, "CreateWindowEx");
  1577. }
  1578. DBGPRINT((DBG_SS_CERTSRVI, "Main Window = %x\n", g_hwndMain));
  1579. // Make window visible
  1580. // ShowWindow(g_hwndMain,nCmdShow);
  1581. hr = CertArgvMainDispatch(ArgvParseCommandLine, g_wszAppName, lpCmdLine);
  1582. _JumpIfError2(hr, error, "CertArgvMainDispatch", E_INVALIDARG);
  1583. // Update window client area
  1584. // UpdateWindow(g_hwndMain);
  1585. if (0 != g_dwDelay1)
  1586. {
  1587. DBGPRINT((
  1588. DBG_SS_CERTSRV,
  1589. "wWinMain(1): sleeping %u seconds\n",
  1590. g_dwDelay1));
  1591. Sleep(1000 * g_dwDelay1);
  1592. }
  1593. hr = CoInitializeEx(NULL, GetCertsrvComThreadingModel());
  1594. if (S_OK != hr && S_FALSE != hr)
  1595. {
  1596. LogEventStringHResult(
  1597. EVENTLOG_ERROR_TYPE,
  1598. MSG_E_CO_INITIALIZE,
  1599. g_wszCommonName,
  1600. hr);
  1601. _JumpError(hr, error, "CoInitializeEx");
  1602. }
  1603. fCoInit = TRUE;
  1604. g_hServiceStoppingEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1605. if (NULL == g_hServiceStoppingEvent)
  1606. {
  1607. hr = myHLastError();
  1608. _JumpError(hr, error, "CreateEvent");
  1609. }
  1610. g_hServiceStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1611. if (NULL == g_hServiceStoppedEvent)
  1612. {
  1613. hr = myHLastError();
  1614. _JumpError(hr, error, "CreateEvent");
  1615. }
  1616. g_hCRLManualPublishEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1617. if (NULL == g_hCRLManualPublishEvent)
  1618. {
  1619. hr = myHLastError();
  1620. _JumpError(hr, error, "CreateEvent");
  1621. }
  1622. g_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1623. if (NULL == g_hShutdownEvent)
  1624. {
  1625. hr = myHLastError();
  1626. _JumpError(hr, error, "CreateEvent");
  1627. }
  1628. __try
  1629. {
  1630. InitializeCriticalSection(&g_ShutdownCriticalSection);
  1631. g_fShutdownCritSec = TRUE;
  1632. hr = S_OK;
  1633. }
  1634. __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
  1635. {
  1636. }
  1637. _JumpIfError(hr, error, "InitializeCriticalSection");
  1638. g_hServiceThread = CreateThread(
  1639. NULL, // lpThreadAttributes (Security Attr)
  1640. 0, // dwStackSize
  1641. CertSrvStartServerThread,
  1642. (VOID *) UlongToPtr((g_fStartAsService ? CSST_STARTSERVICECONTROLLER : CSST_CONSOLE)), // lpParameter
  1643. 0, // dwCreationFlags
  1644. &g_ServiceThreadId);
  1645. if (NULL == g_hServiceThread)
  1646. {
  1647. hr = myHLastError();
  1648. LogEventStringHResult(
  1649. EVENTLOG_ERROR_TYPE,
  1650. MSG_E_SERVICE_THREAD,
  1651. g_wszCommonName,
  1652. hr);
  1653. _JumpError(hr, error, "CreateThread");
  1654. }
  1655. DBGPRINT((DBG_SS_CERTSRVI, "Service Thread = %x\n", g_ServiceThreadId));
  1656. // Message Loop
  1657. while (TRUE)
  1658. {
  1659. BOOL b;
  1660. b = GetMessage(&msg, NULL, 0, 0);
  1661. if (!b)
  1662. {
  1663. hr = (HRESULT)msg.wParam;
  1664. _JumpIfError(hr, error, "WM_QUIT");
  1665. break;
  1666. }
  1667. if (-1 == (LONG) b)
  1668. {
  1669. hr = myHLastError();
  1670. _JumpError(hr, error, "GetMessage");
  1671. }
  1672. DBGPRINT((
  1673. DBG_SS_CERTSRVI,
  1674. "DispatchMessage(tid=%d) msg=0x%x, wp=0x%x, lp=0x%x\n",
  1675. GetCurrentThreadId(),
  1676. msg.message,
  1677. msg.wParam,
  1678. msg.lParam));
  1679. DispatchMessage(&msg);
  1680. }
  1681. error:
  1682. if (fCoInit)
  1683. {
  1684. CoUninitialize();
  1685. }
  1686. if (g_fShutdownCritSec)
  1687. {
  1688. DeleteCriticalSection(&g_ShutdownCriticalSection);
  1689. g_fShutdownCritSec = FALSE;
  1690. }
  1691. if (NULL != g_hShutdownEvent)
  1692. {
  1693. CloseHandle(g_hShutdownEvent);
  1694. }
  1695. if (NULL != g_hServiceThread)
  1696. {
  1697. CloseHandle(g_hServiceThread);
  1698. }
  1699. if (NULL != g_hServiceStoppingEvent)
  1700. {
  1701. CloseHandle(g_hServiceStoppingEvent);
  1702. }
  1703. if (NULL != g_hServiceStoppedEvent)
  1704. {
  1705. CloseHandle(g_hServiceStoppedEvent);
  1706. }
  1707. if (NULL != g_hCRLManualPublishEvent)
  1708. {
  1709. CloseHandle(g_hCRLManualPublishEvent);
  1710. }
  1711. CAuditEvent::CleanupAuditEventTypeHandles();
  1712. pwszMsgAlloc = NULL;
  1713. pwszMsg = L"S_OK";
  1714. if (S_OK != hr)
  1715. {
  1716. pwszMsgAlloc = myGetErrorMessageText(hr, TRUE);
  1717. if (NULL != pwszMsgAlloc)
  1718. {
  1719. pwszMsg = pwszMsgAlloc;
  1720. }
  1721. else
  1722. {
  1723. pwszMsg = myHResultToString(awchr, hr);
  1724. }
  1725. }
  1726. CONSOLEPRINT1((DBG_SS_CERTSRV, "Exit Status = %ws\n", pwszMsg));
  1727. if (NULL != pwszMsgAlloc)
  1728. {
  1729. LocalFree(const_cast<WCHAR *>(pwszMsgAlloc));
  1730. }
  1731. myFreeResourceStrings("certsrv.exe");
  1732. myFreeColumnDisplayNames();
  1733. myRegisterMemDump();
  1734. return(hr);
  1735. }