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

3219 lines
83 KiB

  1. //+--------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1996 - 1999
  5. //
  6. // File: exit.cpp
  7. //
  8. // Contents: CCertExit implementation
  9. //
  10. //---------------------------------------------------------------------------
  11. #include "pch.cpp"
  12. #pragma hdrstop
  13. #include <ntdsapi.h>
  14. #include <lm.h>
  15. #include <security.h>
  16. #include <dsgetdc.h>
  17. #include <userenv.h>
  18. #include "cainfop.h"
  19. #include "csdisp.h"
  20. #include "cspelog.h"
  21. #include "exitlog.h"
  22. #include "exit.h"
  23. #include "cdosys_i.c"
  24. #include "cdosysstr.h"
  25. #include "cdosys.h"
  26. #include <atlbase.h>
  27. #include <atlimpl.cpp>
  28. #define __dwFILE__ __dwFILE_EXIT_DEFAULT_EXIT_CPP__
  29. // begin_sdksample
  30. #ifndef DBG_CERTSRV
  31. #error -- DBG_CERTSRV not defined!
  32. #endif
  33. #define myEXITEVENTS \
  34. EXITEVENT_CERTISSUED | \
  35. EXITEVENT_CERTPENDING | \
  36. EXITEVENT_CERTDENIED | \
  37. EXITEVENT_CERTREVOKED | \
  38. EXITEVENT_CERTRETRIEVEPENDING | \
  39. EXITEVENT_CRLISSUED | \
  40. EXITEVENT_SHUTDOWN
  41. extern HINSTANCE g_hInstance;
  42. HRESULT
  43. GetServerCallbackInterface(
  44. OUT ICertServerExit** ppServer,
  45. IN LONG Context)
  46. {
  47. HRESULT hr;
  48. if (NULL == ppServer)
  49. {
  50. hr = E_POINTER;
  51. _JumpError(hr, error, "Exit:NULL pointer");
  52. }
  53. hr = CoCreateInstance(
  54. CLSID_CCertServerExit,
  55. NULL, // pUnkOuter
  56. CLSCTX_INPROC_SERVER,
  57. IID_ICertServerExit,
  58. (VOID **) ppServer);
  59. _JumpIfError(hr, error, "Exit:CoCreateInstance");
  60. if (*ppServer == NULL)
  61. {
  62. hr = E_UNEXPECTED;
  63. _JumpError(hr, error, "Exit:NULL *ppServer");
  64. }
  65. // only set context if nonzero
  66. if (0 != Context)
  67. {
  68. hr = (*ppServer)->SetContext(Context);
  69. _JumpIfError(hr, error, "Exit: SetContext");
  70. }
  71. error:
  72. return(hr);
  73. }
  74. //+--------------------------------------------------------------------------
  75. // CCertExit::~CCertExit -- destructor
  76. //
  77. // free memory associated with this instance
  78. //+--------------------------------------------------------------------------
  79. CCertExit::~CCertExit()
  80. {
  81. if (NULL != m_strCAName)
  82. {
  83. SysFreeString(m_strCAName);
  84. }
  85. if (NULL != m_pwszRegStorageLoc)
  86. {
  87. LocalFree(m_pwszRegStorageLoc);
  88. }
  89. if (NULL != m_hExitKey)
  90. {
  91. RegCloseKey(m_hExitKey);
  92. }
  93. if (NULL != m_strDescription)
  94. {
  95. SysFreeString(m_strDescription);
  96. }
  97. }
  98. //+--------------------------------------------------------------------------
  99. // CCertExit::Initialize -- initialize for a CA & return interesting Event Mask
  100. //
  101. // Returns S_OK on success.
  102. //+--------------------------------------------------------------------------
  103. STDMETHODIMP
  104. CCertExit::Initialize(
  105. /* [in] */ BSTR const strConfig,
  106. /* [retval][out] */ LONG __RPC_FAR *pEventMask)
  107. {
  108. HRESULT hr = S_OK;
  109. DWORD cbbuf;
  110. DWORD dwType;
  111. ENUM_CATYPES CAType;
  112. ICertServerExit* pServer = NULL;
  113. VARIANT varValue;
  114. WCHAR sz[MAX_PATH];
  115. VariantInit(&varValue);
  116. #ifdef IDS_MODULE_NAME // no_sdksample
  117. if (!LoadString(g_hInstance, IDS_MODULE_NAME, sz, ARRAYSIZE(sz))) // no_sdksample
  118. { // no_sdksample
  119. sz[0] = L'\0'; // no_sdksample
  120. } // no_sdksample
  121. #else // no_sdksample
  122. CSASSERT(wcslen(wsz_SAMPLE_DESCRIPTION) < ARRAYSIZE(sz));
  123. wcsncpy(sz, wsz_SAMPLE_DESCRIPTION, ARRAYSIZE(sz));
  124. sz[ARRAYSIZE(sz) - 1] = L'\0';
  125. #endif // no_sdksample
  126. m_strDescription = SysAllocString(sz);
  127. if (NULL == m_strDescription)
  128. {
  129. hr = E_OUTOFMEMORY;
  130. _JumpError(hr, error, "Exit:SysAllocString");
  131. }
  132. m_strCAName = SysAllocString(strConfig);
  133. if (NULL == m_strCAName)
  134. {
  135. hr = E_OUTOFMEMORY;
  136. _JumpError(hr, error, "Exit:SysAllocString");
  137. }
  138. *pEventMask = myEXITEVENTS;
  139. DBGPRINT((DBG_SS_CERTEXIT, "Exit:Initialize(%ws) ==> %x\n", m_strCAName, *pEventMask));
  140. // get server callbacks
  141. hr = GetServerCallbackInterface(&pServer, 0);
  142. _JumpIfError(hr, error, "Exit:GetServerCallbackInterface");
  143. // get storage location
  144. hr = exitGetProperty(
  145. pServer,
  146. FALSE, // fRequest
  147. wszPROPMODULEREGLOC,
  148. PROPTYPE_STRING,
  149. &varValue);
  150. _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszPROPMODULEREGLOC);
  151. m_pwszRegStorageLoc = (LPWSTR)LocalAlloc(LMEM_FIXED, (wcslen(varValue.bstrVal)+1) *sizeof(WCHAR));
  152. if (NULL == m_pwszRegStorageLoc)
  153. {
  154. hr = E_OUTOFMEMORY;
  155. _JumpError(hr, error, "Exit:LocalAlloc");
  156. }
  157. wcscpy(m_pwszRegStorageLoc, varValue.bstrVal);
  158. VariantClear(&varValue);
  159. // get CA type
  160. hr = exitGetProperty(
  161. pServer,
  162. FALSE, // fRequest
  163. wszPROPCATYPE,
  164. PROPTYPE_LONG,
  165. &varValue);
  166. _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszPROPCATYPE);
  167. CAType = (ENUM_CATYPES) varValue.lVal;
  168. VariantClear(&varValue);
  169. hr = RegOpenKeyEx(
  170. HKEY_LOCAL_MACHINE,
  171. m_pwszRegStorageLoc,
  172. 0, // dwReserved
  173. KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE,
  174. &m_hExitKey);
  175. if (S_OK != hr)
  176. {
  177. if ((HRESULT) ERROR_FILE_NOT_FOUND == hr)
  178. {
  179. hr = S_OK;
  180. goto error;
  181. }
  182. _JumpError(hr, error, "Exit:RegOpenKeyEx");
  183. }
  184. hr = exitGetProperty(
  185. pServer,
  186. FALSE, // fRequest
  187. wszPROPCERTCOUNT,
  188. PROPTYPE_LONG,
  189. &varValue);
  190. _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszPROPCERTCOUNT);
  191. m_cCACert = varValue.lVal;
  192. cbbuf = sizeof(m_dwExitPublishFlags);
  193. hr = RegQueryValueEx(
  194. m_hExitKey,
  195. wszREGCERTPUBLISHFLAGS,
  196. NULL, // lpdwReserved
  197. &dwType,
  198. (BYTE *) &m_dwExitPublishFlags,
  199. &cbbuf);
  200. if (S_OK != hr)
  201. {
  202. m_dwExitPublishFlags = 0;
  203. }
  204. // end_sdksample
  205. if (FIsAdvancedServer())
  206. {
  207. hr = m_EmailNotifyObj.Init(m_hExitKey, m_strDescription);
  208. _PrintIfError(hr, "CEmailNotify::Init");
  209. }
  210. // begin_sdksample
  211. hr = S_OK;
  212. error:
  213. VariantClear(&varValue);
  214. if (NULL != pServer)
  215. {
  216. pServer->Release();
  217. }
  218. return(myHError(hr));
  219. }
  220. //+--------------------------------------------------------------------------
  221. // CCertExit::_ExpandEnvironmentVariables -- Expand environment variables
  222. //
  223. //+--------------------------------------------------------------------------
  224. HRESULT
  225. CCertExit::_ExpandEnvironmentVariables(
  226. IN WCHAR const *pwszIn,
  227. OUT WCHAR *pwszOut,
  228. IN DWORD cwcOut)
  229. {
  230. HRESULT hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
  231. WCHAR awcVar[MAX_PATH];
  232. WCHAR const *pwszSrc;
  233. WCHAR *pwszDst;
  234. WCHAR *pwszDstEnd;
  235. WCHAR *pwszVar;
  236. DWORD cwc;
  237. pwszSrc = pwszIn;
  238. pwszDst = pwszOut;
  239. pwszDstEnd = &pwszOut[cwcOut];
  240. while (L'\0' != (*pwszDst = *pwszSrc++))
  241. {
  242. if ('%' == *pwszDst)
  243. {
  244. *pwszDst = L'\0';
  245. pwszVar = awcVar;
  246. while (L'\0' != *pwszSrc)
  247. {
  248. if ('%' == *pwszSrc)
  249. {
  250. pwszSrc++;
  251. break;
  252. }
  253. *pwszVar++ = *pwszSrc++;
  254. if (pwszVar >= &awcVar[sizeof(awcVar)/sizeof(awcVar[0]) - 1])
  255. {
  256. _JumpError(hr, error, "Exit:overflow 1");
  257. }
  258. }
  259. *pwszVar = L'\0';
  260. cwc = GetEnvironmentVariable(awcVar, pwszDst, SAFE_SUBTRACT_POINTERS(pwszDstEnd, pwszDst));
  261. if (0 == cwc)
  262. {
  263. hr = myHLastError();
  264. _JumpError(hr, error, "Exit:GetEnvironmentVariable");
  265. }
  266. if ((DWORD) (pwszDstEnd - pwszDst) <= cwc)
  267. {
  268. _JumpError(hr, error, "Exit:overflow 2");
  269. }
  270. pwszDst += cwc;
  271. }
  272. else
  273. {
  274. pwszDst++;
  275. }
  276. if (pwszDst >= pwszDstEnd)
  277. {
  278. _JumpError(hr, error, "Exit:overflow 3");
  279. }
  280. }
  281. hr = S_OK;
  282. error:
  283. return(hr);
  284. }
  285. HRESULT
  286. exitGetRequestAttribute(
  287. IN ICertServerExit *pServer,
  288. IN WCHAR const *pwszAttributeName,
  289. OUT BSTR *pstrOut)
  290. {
  291. HRESULT hr;
  292. BSTR strName = NULL;
  293. *pstrOut = NULL;
  294. strName = SysAllocString(pwszAttributeName);
  295. if (NULL == strName)
  296. {
  297. hr = E_OUTOFMEMORY;
  298. _JumpError(hr, error, "Exit:SysAllocString");
  299. }
  300. hr = pServer->GetRequestAttribute(strName, pstrOut);
  301. _JumpIfErrorStr2(
  302. hr,
  303. error,
  304. "Exit:GetRequestAttribute",
  305. pwszAttributeName,
  306. CERTSRV_E_PROPERTY_EMPTY);
  307. error:
  308. if (NULL != strName)
  309. {
  310. SysFreeString(strName);
  311. }
  312. return(hr);
  313. }
  314. //+--------------------------------------------------------------------------
  315. // CCertExit::_WriteCertToFile -- write binary certificate to a file
  316. //
  317. //+--------------------------------------------------------------------------
  318. HRESULT
  319. CCertExit::_WriteCertToFile(
  320. IN ICertServerExit *pServer,
  321. IN BYTE const *pbCert,
  322. IN DWORD cbCert)
  323. {
  324. HRESULT hr;
  325. BSTR strCertFile = NULL;
  326. DWORD cbWritten;
  327. HANDLE hFile = INVALID_HANDLE_VALUE;
  328. WCHAR wszDir[MAX_PATH];
  329. WCHAR *pwszPath = NULL;
  330. WCHAR wszFile[cwcDWORDSPRINTF+5]; //format "requestid.cer"
  331. VARIANT varRequestID;
  332. VariantInit(&varRequestID);
  333. // Old functionality asked requester to pass in a CertFile attribute with
  334. // the output request file name. After the security review we decided
  335. // to not allow file name from user but to build it at server. Still, we
  336. // don't want to start publishing all certificates, so we'll maintain the
  337. // CertFile property; if present, we'll just ignore its content, if not
  338. // present we won't publish.
  339. hr = exitGetRequestAttribute(pServer, wszPROPEXITCERTFILE, &strCertFile);
  340. if (S_OK != hr)
  341. {
  342. DBGPRINT((
  343. DBG_SS_CERTEXIT,
  344. "Exit:exitGetRequestAttribute(%ws): %x%hs\n",
  345. wszPROPEXITCERTFILE,
  346. hr,
  347. CERTSRV_E_PROPERTY_EMPTY == hr? " EMPTY VALUE" : ""));
  348. if (CERTSRV_E_PROPERTY_EMPTY == hr)
  349. {
  350. hr = S_OK;
  351. }
  352. goto error;
  353. }
  354. // build file name as "requestid.cer"
  355. hr = exitGetProperty(
  356. pServer,
  357. TRUE, // fRequest,
  358. wszPROPREQUESTREQUESTID,
  359. PROPTYPE_LONG,
  360. &varRequestID);
  361. _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszPROPREQUESTREQUESTID);
  362. wsprintf(wszFile, L"%d.cer", V_I4(&varRequestID));
  363. hr = _ExpandEnvironmentVariables(
  364. L"%SystemRoot%\\System32\\" wszCERTENROLLSHAREPATH L"\\",
  365. wszDir,
  366. ARRAYSIZE(wszDir));
  367. _JumpIfError(hr, error, "_ExpandEnvironmentVariables");
  368. hr = myBuildPathAndExt(wszDir, wszFile, NULL, &pwszPath);
  369. _JumpIfError(hr, error, "myBuildPathAndExt");
  370. // open file & write binary cert out.
  371. hFile = CreateFile(
  372. pwszPath,
  373. GENERIC_WRITE,
  374. 0, // dwShareMode
  375. NULL, // lpSecurityAttributes
  376. CREATE_NEW,
  377. FILE_ATTRIBUTE_NORMAL,
  378. NULL); // hTemplateFile
  379. if (INVALID_HANDLE_VALUE == hFile)
  380. {
  381. hr = myHLastError();
  382. _JumpErrorStr(hr, error, "Exit:CreateFile", pwszPath);
  383. }
  384. if (!WriteFile(hFile, pbCert, cbCert, &cbWritten, NULL))
  385. {
  386. hr = myHLastError();
  387. _JumpErrorStr(hr, error, "Exit:WriteFile", pwszPath);
  388. }
  389. if (cbWritten != cbCert)
  390. {
  391. hr = STG_E_WRITEFAULT;
  392. DBGPRINT((
  393. DBG_SS_CERTEXIT,
  394. "Exit:WriteFile(%ws): attempted %x, actual %x bytes: %x\n",
  395. pwszPath,
  396. cbCert,
  397. cbWritten,
  398. hr));
  399. goto error;
  400. }
  401. error:
  402. // end_sdksample
  403. VariantClear(&varRequestID);
  404. if (hr != S_OK)
  405. {
  406. LPCWSTR wszStrings[1];
  407. wszStrings[0] = pwszPath;
  408. ::LogModuleStatus(
  409. g_hInstance,
  410. hr,
  411. MSG_UNABLE_TO_WRITEFILE,
  412. FALSE, // fPolicy
  413. m_strDescription,
  414. wszStrings,
  415. NULL);
  416. }
  417. // begin_sdksample
  418. if (INVALID_HANDLE_VALUE != hFile)
  419. {
  420. CloseHandle(hFile);
  421. }
  422. if (NULL != pwszPath)
  423. {
  424. LocalFree(pwszPath);
  425. }
  426. if (NULL != strCertFile)
  427. {
  428. SysFreeString(strCertFile);
  429. }
  430. return(hr);
  431. }
  432. //+--------------------------------------------------------------------------
  433. // CCertExit::_NotifyNewCert -- Notify the exit module of a new certificate
  434. //
  435. //+--------------------------------------------------------------------------
  436. HRESULT
  437. CCertExit::_NotifyNewCert(
  438. /* [in] */ LONG Context)
  439. {
  440. HRESULT hr;
  441. VARIANT varCert;
  442. ICertServerExit *pServer = NULL;
  443. VariantInit(&varCert);
  444. // only call write fxns if server policy allows
  445. if (m_dwExitPublishFlags & EXITPUB_FILE)
  446. {
  447. hr = CoCreateInstance(
  448. CLSID_CCertServerExit,
  449. NULL, // pUnkOuter
  450. CLSCTX_INPROC_SERVER,
  451. IID_ICertServerExit,
  452. (VOID **) &pServer);
  453. _JumpIfError(hr, error, "Exit:CoCreateInstance");
  454. hr = pServer->SetContext(Context);
  455. _JumpIfError(hr, error, "Exit:SetContext");
  456. hr = exitGetProperty(
  457. pServer,
  458. FALSE, // fRequest,
  459. wszPROPRAWCERTIFICATE,
  460. PROPTYPE_BINARY,
  461. &varCert);
  462. _JumpIfErrorStr(
  463. hr,
  464. error,
  465. "Exit:exitGetProperty",
  466. wszPROPRAWCERTIFICATE);
  467. if (VT_BSTR != varCert.vt)
  468. {
  469. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  470. _JumpError(hr, error, "Exit:BAD cert var type");
  471. }
  472. hr = _WriteCertToFile(
  473. pServer,
  474. (BYTE const *) varCert.bstrVal,
  475. SysStringByteLen(varCert.bstrVal));
  476. _JumpIfError(hr, error, "_WriteCertToFile");
  477. }
  478. hr = S_OK;
  479. error:
  480. VariantClear(&varCert);
  481. if (NULL != pServer)
  482. {
  483. pServer->Release();
  484. }
  485. return(hr);
  486. }
  487. //+--------------------------------------------------------------------------
  488. // CCertExit::_NotifyCRLIssued -- Notify the exit module of a new certificate
  489. //
  490. //+--------------------------------------------------------------------------
  491. HRESULT
  492. CCertExit::_NotifyCRLIssued(
  493. /* [in] */ LONG Context)
  494. {
  495. HRESULT hr;
  496. ICertServerExit *pServer = NULL;
  497. DWORD i;
  498. VARIANT varBaseCRL;
  499. VARIANT varDeltaCRL;
  500. BOOL fDeltaCRLsDisabled;
  501. VariantInit(&varBaseCRL);
  502. VariantInit(&varDeltaCRL);
  503. hr = CoCreateInstance(
  504. CLSID_CCertServerExit,
  505. NULL, // pUnkOuter
  506. CLSCTX_INPROC_SERVER,
  507. IID_ICertServerExit,
  508. (VOID **) &pServer);
  509. _JumpIfError(hr, error, "Exit:CoCreateInstance");
  510. hr = pServer->SetContext(Context);
  511. _JumpIfError(hr, error, "Exit:SetContext");
  512. hr = exitGetProperty(
  513. pServer,
  514. FALSE, // fRequest,
  515. wszPROPDELTACRLSDISABLED,
  516. PROPTYPE_LONG,
  517. &varBaseCRL);
  518. _JumpIfErrorStr(
  519. hr,
  520. error,
  521. "Exit:exitGetProperty",
  522. wszPROPDELTACRLSDISABLED);
  523. fDeltaCRLsDisabled = varBaseCRL.lVal;
  524. // How many CRLs are there?
  525. // Loop for each CRL
  526. for (i = 0; i < m_cCACert; i++)
  527. {
  528. // array size for wsprintf("%s.%u")
  529. #define MAX_CRL_PROP ( \
  530. max( max( ARRAYSIZE(wszPROPCRLSTATE), \
  531. ARRAYSIZE(wszPROPRAWCRL) ), \
  532. ARRAYSIZE(wszPROPRAWDELTACRL) ) + 1 + cwcDWORDSPRINTF)
  533. WCHAR wszCRLPROP[MAX_CRL_PROP];
  534. // Verify the CRL State says we should update this CRL
  535. wsprintf(wszCRLPROP, wszPROPCRLSTATE L".%u", i);
  536. hr = exitGetProperty(
  537. pServer,
  538. FALSE, // fRequest,
  539. wszCRLPROP,
  540. PROPTYPE_LONG,
  541. &varBaseCRL);
  542. _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszCRLPROP);
  543. if (CA_DISP_VALID != varBaseCRL.lVal)
  544. {
  545. continue;
  546. }
  547. // Grab the raw base CRL
  548. wsprintf(wszCRLPROP, wszPROPRAWCRL L".%u", i);
  549. hr = exitGetProperty(
  550. pServer,
  551. FALSE, // fRequest,
  552. wszCRLPROP,
  553. PROPTYPE_BINARY,
  554. &varBaseCRL);
  555. _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszCRLPROP);
  556. // Grab the raw delta CRL (which may not exist)
  557. wsprintf(wszCRLPROP, wszPROPRAWDELTACRL L".%u", i);
  558. hr = exitGetProperty(
  559. pServer,
  560. FALSE, // fRequest,
  561. wszCRLPROP,
  562. PROPTYPE_BINARY,
  563. &varDeltaCRL);
  564. _PrintIfErrorStr2(
  565. hr,
  566. "Exit:exitGetProperty",
  567. wszCRLPROP,
  568. fDeltaCRLsDisabled?
  569. HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) : S_OK);
  570. if (S_OK != hr && !fDeltaCRLsDisabled)
  571. {
  572. goto error;
  573. }
  574. // Publish the CRL(s) ...
  575. }
  576. hr = S_OK;
  577. error:
  578. if (NULL != pServer)
  579. {
  580. pServer->Release();
  581. }
  582. VariantClear(&varBaseCRL);
  583. VariantClear(&varDeltaCRL);
  584. return(hr);
  585. }
  586. //+--------------------------------------------------------------------------
  587. // CCertExit::Notify -- Notify the exit module of an event
  588. //
  589. // Returns S_OK.
  590. //+--------------------------------------------------------------------------
  591. STDMETHODIMP
  592. CCertExit::Notify(
  593. /* [in] */ LONG ExitEvent,
  594. /* [in] */ LONG Context)
  595. {
  596. char *psz = "UNKNOWN EVENT";
  597. HRESULT hr = S_OK;
  598. switch (ExitEvent)
  599. {
  600. case EXITEVENT_CERTISSUED:
  601. hr = _NotifyNewCert(Context);
  602. psz = "certissued";
  603. break;
  604. case EXITEVENT_CERTPENDING:
  605. psz = "certpending";
  606. break;
  607. case EXITEVENT_CERTDENIED:
  608. psz = "certdenied";
  609. break;
  610. case EXITEVENT_CERTREVOKED:
  611. psz = "certrevoked";
  612. break;
  613. case EXITEVENT_CERTRETRIEVEPENDING:
  614. psz = "retrievepending";
  615. break;
  616. case EXITEVENT_CRLISSUED:
  617. #if 0 // no_sdksample
  618. hr = _NotifyCRLIssued(Context);
  619. #endif // no_sdksample
  620. psz = "crlissued";
  621. break;
  622. case EXITEVENT_SHUTDOWN:
  623. psz = "shutdown";
  624. break;
  625. }
  626. // end_sdksample
  627. {
  628. HRESULT hr2 = m_EmailNotifyObj.Notify(
  629. ExitEvent,
  630. Context,
  631. m_strDescription);
  632. if(S_OK != hr2)
  633. {
  634. _PrintError(hr2, "Email notification");
  635. if(S_OK == hr)
  636. hr = hr2;
  637. }
  638. }
  639. // begin_sdksample
  640. DBGPRINT((
  641. DBG_SS_CERTEXIT,
  642. "Exit:Notify(%hs=%x, ctx=%x) rc=%x\n",
  643. psz,
  644. ExitEvent,
  645. Context,
  646. hr));
  647. return(hr);
  648. }
  649. STDMETHODIMP
  650. CCertExit::GetDescription(
  651. /* [retval][out] */ BSTR *pstrDescription)
  652. {
  653. HRESULT hr = S_OK;
  654. WCHAR sz[MAX_PATH];
  655. #ifdef IDS_MODULE_NAME // no_sdksample
  656. LoadString(g_hInstance, IDS_MODULE_NAME, sz, ARRAYSIZE(sz));// no_sdksample
  657. #else // no_sdksample
  658. CSASSERT(wcslen(wsz_SAMPLE_DESCRIPTION) < ARRAYSIZE(sz));
  659. wcscpy(sz, wsz_SAMPLE_DESCRIPTION);
  660. #endif // no_sdksample
  661. *pstrDescription = SysAllocString(sz);
  662. if (NULL == *pstrDescription)
  663. {
  664. hr = E_OUTOFMEMORY;
  665. _JumpError(hr, error, "Exit:SysAllocString");
  666. }
  667. error:
  668. return(hr);
  669. }
  670. //+--------------------------------------------------------------------------
  671. // CCertExit::GetManageModule
  672. //
  673. // Returns S_OK on success.
  674. //+--------------------------------------------------------------------------
  675. STDMETHODIMP
  676. CCertExit::GetManageModule(
  677. /* [out, retval] */ ICertManageModule **ppManageModule)
  678. {
  679. HRESULT hr;
  680. *ppManageModule = NULL;
  681. hr = CoCreateInstance(
  682. CLSID_CCertManageExitModule,
  683. NULL, // pUnkOuter
  684. CLSCTX_INPROC_SERVER,
  685. IID_ICertManageModule,
  686. (VOID **) ppManageModule);
  687. _JumpIfError(hr, error, "CoCreateInstance");
  688. error:
  689. return(hr);
  690. }
  691. /////////////////////////////////////////////////////////////////////////////
  692. //
  693. STDMETHODIMP
  694. CCertExit::InterfaceSupportsErrorInfo(REFIID riid)
  695. {
  696. int i;
  697. static const IID *arr[] =
  698. {
  699. &IID_ICertExit,
  700. };
  701. for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
  702. {
  703. if (IsEqualGUID(*arr[i],riid))
  704. {
  705. return(S_OK);
  706. }
  707. }
  708. return(S_FALSE);
  709. }
  710. HRESULT
  711. exitGetProperty(
  712. IN ICertServerExit *pServer,
  713. IN BOOL fRequest,
  714. IN WCHAR const *pwszPropertyName,
  715. IN DWORD PropType,
  716. OUT VARIANT *pvarOut)
  717. {
  718. HRESULT hr;
  719. BSTR strName = NULL;
  720. VariantInit(pvarOut);
  721. strName = SysAllocString(pwszPropertyName);
  722. if (NULL == strName)
  723. {
  724. hr = E_OUTOFMEMORY;
  725. _JumpError(hr, error, "Exit:SysAllocString");
  726. }
  727. if (fRequest)
  728. {
  729. hr = pServer->GetRequestProperty(strName, PropType, pvarOut);
  730. _JumpIfErrorStr2(
  731. hr,
  732. error,
  733. "Exit:GetRequestProperty",
  734. pwszPropertyName,
  735. CERTSRV_E_PROPERTY_EMPTY);
  736. }
  737. else
  738. {
  739. hr = pServer->GetCertificateProperty(strName, PropType, pvarOut);
  740. _JumpIfErrorStr2(
  741. hr,
  742. error,
  743. "Exit:GetCertificateProperty",
  744. pwszPropertyName,
  745. CERTSRV_E_PROPERTY_EMPTY);
  746. }
  747. error:
  748. if (NULL != strName)
  749. {
  750. SysFreeString(strName);
  751. }
  752. return(hr);
  753. }
  754. // end_sdksample
  755. /////////////////////////////////////////////////////////////////////////////
  756. HRESULT
  757. exitGetStringProperty(
  758. IN ICertServerExit *pServer,
  759. IN BOOL fRequest,
  760. IN BOOL fAllowUnknown,
  761. OPTIONAL IN WCHAR *pwszProp,
  762. OUT BSTR *pstr)
  763. {
  764. HRESULT hr;
  765. VARIANT var;
  766. WCHAR awc[64];
  767. VariantInit(&var);
  768. CSASSERT(NULL == *pstr);
  769. if (NULL == pwszProp)
  770. {
  771. hr = CERTSRV_E_PROPERTY_EMPTY;
  772. }
  773. else
  774. {
  775. hr = exitGetProperty(
  776. pServer,
  777. fRequest,
  778. pwszProp,
  779. PROPTYPE_STRING,
  780. &var);
  781. }
  782. if (!fAllowUnknown || CERTSRV_E_PROPERTY_EMPTY != hr)
  783. {
  784. _JumpIfErrorStr(hr, error, "Exit:GetProperty", pwszProp);
  785. if (VT_BSTR != var.vt)
  786. {
  787. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  788. _JumpErrorStr(hr, error, "Exit:BAD var type", pwszProp);
  789. }
  790. *pstr = var.bstrVal;
  791. var.vt = VT_EMPTY;
  792. }
  793. else
  794. {
  795. if (!LoadString(g_hInstance, IDS_MAPI_UNKNOWN, awc, ARRAYSIZE(awc)))
  796. {
  797. hr = myHLastError();
  798. _JumpError(hr, error, "Exit:LoadString");
  799. }
  800. *pstr = SysAllocString(awc);
  801. if (NULL == *pstr)
  802. {
  803. hr = E_OUTOFMEMORY;
  804. _JumpError(hr, error, "Exit:SysAllocString");
  805. }
  806. }
  807. hr = S_OK;
  808. error:
  809. VariantClear(&var);
  810. return(hr);
  811. }
  812. HRESULT
  813. LoadAnsiResourceString(
  814. IN LONG idmsg,
  815. OUT char **ppszString)
  816. {
  817. HRESULT hr;
  818. WCHAR awc[4096];
  819. if (!LoadString(g_hInstance, idmsg, awc, ARRAYSIZE(awc)))
  820. {
  821. hr = myHLastError();
  822. _JumpError(hr, error, "Exit:LoadString");
  823. }
  824. if (!ConvertWszToSz(ppszString, awc, MAXDWORD))
  825. {
  826. hr = E_OUTOFMEMORY;
  827. _JumpError(hr, error, "Exit:ConvertWszToSz");
  828. }
  829. hr = S_OK;
  830. error:
  831. return(hr);
  832. }
  833. /////////////////////////////////////////////////////////////////////////////
  834. HRESULT RegGetValue(
  835. HKEY hkey,
  836. LPCWSTR pcwszValName,
  837. VARIANT* pvarValue)
  838. {
  839. HRESULT hr;
  840. DWORD dwType;
  841. DWORD cbVal;
  842. BYTE* pbVal = NULL;
  843. hr = myRegQueryValueEx(
  844. hkey,
  845. pcwszValName,
  846. &dwType,
  847. &pbVal,
  848. &cbVal);
  849. _JumpIfError2(hr, error, "myRegQueryValueEx",
  850. HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
  851. hr = myRegValueToVariant(
  852. dwType,
  853. cbVal,
  854. pbVal,
  855. pvarValue);
  856. _JumpIfError(hr, error, "myRegValueToVariant");
  857. error:
  858. if(pbVal)
  859. {
  860. LocalFree(pbVal);
  861. }
  862. return hr;
  863. }
  864. /////////////////////////////////////////////////////////////////////////////
  865. HRESULT RegSetValue(
  866. HKEY hkey,
  867. LPCWSTR pcwszValName,
  868. VARIANT* pvarValue)
  869. {
  870. HRESULT hr;
  871. DWORD dwType;
  872. DWORD cbVal;
  873. BYTE* pbVal = NULL;
  874. hr = myVariantToRegValue(
  875. pvarValue,
  876. &dwType,
  877. &cbVal,
  878. &pbVal);
  879. _JumpIfError(hr, error, "myVariantToRegValue");
  880. hr = RegSetValueEx(
  881. hkey,
  882. pcwszValName,
  883. 0,
  884. dwType,
  885. pbVal,
  886. cbVal);
  887. if (S_OK != hr)
  888. {
  889. hr = myHError(hr);
  890. _JumpErrorStr(hr, error, "RegSetValueEx", pcwszValName);
  891. }
  892. error:
  893. if(pbVal)
  894. {
  895. LocalFree(pbVal);
  896. }
  897. return hr;
  898. }
  899. /////////////////////////////////////////////////////////////////////////////
  900. HRESULT
  901. GetCertTypeFriendlyName(
  902. IN LPCWSTR pcwszCertType,
  903. OUT LPWSTR *ppwszFriendlyName)
  904. {
  905. HRESULT hr;
  906. CAutoHCERTTYPE hCertType;
  907. WCHAR **apwszNames = NULL;
  908. hr = CAFindCertTypeByName(
  909. pcwszCertType,
  910. NULL, // hCAInfo
  911. CT_FIND_LOCAL_SYSTEM |
  912. CT_ENUM_MACHINE_TYPES |
  913. CT_ENUM_USER_TYPES, // dwFlags
  914. &hCertType);
  915. if(HRESULT_FROM_WIN32(ERROR_NOT_FOUND) == hr)
  916. {
  917. // try with the OID
  918. hr = CAFindCertTypeByName(
  919. pcwszCertType,
  920. NULL,
  921. CT_FIND_LOCAL_SYSTEM |
  922. CT_ENUM_MACHINE_TYPES |
  923. CT_ENUM_USER_TYPES |
  924. CT_FIND_BY_OID,
  925. &hCertType);
  926. }
  927. _JumpIfErrorStr(hr, error, "Exit:CAFindCertTypeByName", pcwszCertType);
  928. hr = CAGetCertTypeProperty(
  929. hCertType,
  930. CERTTYPE_PROP_FRIENDLY_NAME,
  931. &apwszNames);
  932. _JumpIfError(hr, error, "Exit:CAGetCertTypeProperty");
  933. if (NULL == apwszNames[0])
  934. {
  935. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  936. _JumpError(hr, error, "Exit:NULL friendly name");
  937. }
  938. *ppwszFriendlyName = (LPWSTR) LocalAlloc(LMEM_FIXED,
  939. sizeof(WCHAR)*(wcslen(apwszNames[0])+1));
  940. _JumpIfAllocFailed(*ppwszFriendlyName, error);
  941. wcscpy(*ppwszFriendlyName, apwszNames[0]);
  942. error:
  943. if (NULL != apwszNames)
  944. {
  945. CAFreeCertTypeProperty(hCertType, apwszNames);
  946. }
  947. return(hr);
  948. }
  949. /////////////////////////////////////////////////////////////////////////////
  950. // Email notification support
  951. LPCWSTR CEmailNotify::m_pcwszEventRegKeys[CEmailNotify::m_gcEvents] =
  952. {
  953. wszREGEXITISSUEDKEY,
  954. wszREGEXITPENDINGKEY,
  955. wszREGEXITDENIEDKEY,
  956. wszREGEXITREVOKEDKEY,
  957. wszREGEXITCRLISSUEDKEY,
  958. wszREGEXITSHUTDOWNKEY,
  959. wszREGEXITSTARTUPKEY,
  960. };
  961. /////////////////////////////////////////////////////////////////////////////
  962. CEmailNotify::CEmailNotify()
  963. {
  964. m_hkeySMTP = NULL;
  965. m_dwEventFilter = 0;
  966. m_bstrCAMailAddress = NULL;
  967. m_pICDOConfig = NULL;
  968. m_fReloadCDOConfig = true;
  969. VariantInit(&m_varTemplateRestrictions);
  970. }
  971. /////////////////////////////////////////////////////////////////////////////
  972. CEmailNotify::~CEmailNotify()
  973. {
  974. if (m_pICDOConfig)
  975. {
  976. m_pICDOConfig->Release();
  977. }
  978. VariantClear(&m_varTemplateRestrictions);
  979. if(m_bstrCAMailAddress)
  980. {
  981. SysFreeString(m_bstrCAMailAddress);
  982. }
  983. if(m_hkeySMTP)
  984. {
  985. RegCloseKey(m_hkeySMTP);
  986. }
  987. }
  988. /////////////////////////////////////////////////////////////////////////////
  989. HRESULT
  990. CEmailNotify::Init(
  991. IN HKEY hkeyExit,
  992. IN WCHAR const *pwszDescription)
  993. {
  994. HRESULT hr;
  995. VARIANT varValue;
  996. hr = RegOpenKeyEx(
  997. hkeyExit,
  998. wszREGEXITSMTPKEY,
  999. 0, // dwReserved
  1000. KEY_READ | KEY_QUERY_VALUE,
  1001. &m_hkeySMTP);
  1002. if ((HRESULT) ERROR_FILE_NOT_FOUND == hr || S_OK == hr)
  1003. {
  1004. hr = _CreateSMTPRegSettings(hkeyExit);
  1005. _JumpIfError(hr, error, "CreateSMTPRegSettings");
  1006. }
  1007. _JumpIfError(hr, error, "RegOpenKey(SMTP)");
  1008. // load event filter
  1009. hr = RegGetValue(
  1010. m_hkeySMTP,
  1011. wszREGEXITSMTPEVENTFILTER,
  1012. &varValue);
  1013. if(S_OK==hr)
  1014. {
  1015. m_dwEventFilter = V_I4(&varValue);
  1016. }
  1017. else if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
  1018. {
  1019. hr = S_OK;
  1020. }
  1021. _JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPEVENTFILTER);
  1022. m_dwEventFilter = V_I4(&varValue);
  1023. if(m_dwEventFilter) // no need to load config if no notification enabled
  1024. {
  1025. // load per event type info from each subkey
  1026. hr = _LoadEventInfoFromRegistry();
  1027. _JumpIfError(hr, error, "_LoadEventInfoFromRegistry");
  1028. // load template restrictions
  1029. hr = _LoadTemplateRestrictionsFromRegistry();
  1030. _JumpIfError(hr, error, "CCertExit::_LoadTemplateRestrictionsFromRegistry");
  1031. hr = _InitCDO();
  1032. _JumpIfError(hr, error, "CEmailNotify::InitCDO");
  1033. // send startup notification mail
  1034. hr = Notify(EXITEVENT_STARTUP, 0, pwszDescription);
  1035. _PrintIfError(hr, "Notify(EXITEVENT_STARTUP)");
  1036. }
  1037. hr = S_OK;
  1038. error:
  1039. if (S_OK != hr && m_pICDOConfig)
  1040. {
  1041. m_pICDOConfig->Release();
  1042. m_pICDOConfig = NULL;
  1043. }
  1044. return(hr);
  1045. }
  1046. /////////////////////////////////////////////////////////////////////////////
  1047. HRESULT CEmailNotify::_InitCDO()
  1048. {
  1049. HRESULT hr;
  1050. Fields* pFields = NULL;
  1051. IConfiguration *pICDOConfig = NULL;
  1052. // load SMTP fields
  1053. hr = CoCreateInstance(CDO::CLSID_Configuration,
  1054. NULL,
  1055. CLSCTX_INPROC_SERVER,
  1056. CDO::IID_IConfiguration,
  1057. reinterpret_cast<void**>(&pICDOConfig));
  1058. _JumpIfError(hr, error, "CoCreateInstance CDO_IConfiguration");
  1059. hr = pICDOConfig->get_Fields(&pFields);
  1060. _JumpIfError(hr, error, "CDO::IConfig::get_Fields");
  1061. hr = _LoadSMTPFieldsFromRegistry(pFields);
  1062. _JumpIfError(hr, error, "_LoadFieldsFromRegistry");
  1063. hr = _LoadSMTPFieldsFromLSASecret(pFields);
  1064. // don't bail, optional fields
  1065. _PrintIfError(hr, "_LoadFieldsFromLSASecret");
  1066. hr = pFields->Update();
  1067. _JumpIfError(hr, error, "config");
  1068. m_rwlockCDOConfig.GetExclusive();
  1069. if(m_pICDOConfig)
  1070. {
  1071. m_pICDOConfig->Release();
  1072. }
  1073. m_pICDOConfig = pICDOConfig;
  1074. pICDOConfig = NULL;
  1075. m_fReloadCDOConfig = false;
  1076. m_rwlockCDOConfig.Release();
  1077. error:
  1078. if(pFields)
  1079. {
  1080. pFields->Release();
  1081. }
  1082. if(pICDOConfig)
  1083. {
  1084. pICDOConfig->Release();
  1085. }
  1086. return hr;
  1087. }
  1088. /////////////////////////////////////////////////////////////////////////////
  1089. inline bool CEmailNotify::_IsEventEnabled(DWORD dwEvent)
  1090. {
  1091. return (dwEvent & m_dwEventFilter)?true:false;
  1092. }
  1093. /////////////////////////////////////////////////////////////////////////////
  1094. //
  1095. // Create the following registry structure under exit key
  1096. //
  1097. // SMTP
  1098. // EventFilter DWORD
  1099. // SMTPServer SZ
  1100. // SMTPAuthenticate DWORD
  1101. //
  1102. // Issued
  1103. // BodyFormat SZ
  1104. // BodyArg MULTISZ
  1105. // TitleFormat SZ
  1106. // TitleArg MULTISZ
  1107. //
  1108. // Pending
  1109. // ...same as Issued
  1110. // Denied
  1111. // ...same as Issued
  1112. // Revoked
  1113. // ...same as Issued
  1114. // CLRIssued
  1115. // ...same as Issued
  1116. // Shutdown
  1117. // ...same as Issued
  1118. //
  1119. HRESULT CEmailNotify::_CreateSMTPRegSettings(HKEY hkeyExit)
  1120. {
  1121. HRESULT hr;
  1122. DWORD dwDisp;
  1123. VARIANT varValue;
  1124. HKEY hkeyEvent = NULL;
  1125. typedef struct tagEventFormat
  1126. {
  1127. LPCWSTR pcwszRegKey;
  1128. int nTitleFormatResourceID;
  1129. int nBodyFormatResourceID;
  1130. LPWSTR pcwszBodyArg;
  1131. DWORD cbBodyArg;
  1132. LPWSTR pcwszTitleArg;
  1133. DWORD cbTitleArg;
  1134. } EventFormat;
  1135. WCHAR wszzTitleArg[] =
  1136. wszPROPSANITIZEDCANAME
  1137. L"\0";
  1138. WCHAR wszzBodyArgIssued[] =
  1139. wszPROPCERTIFICATEREQUESTID L"\0"
  1140. wszPROPUPN L"\0"
  1141. wszPROPREQUESTDOT wszPROPREQUESTERNAME L"\0"
  1142. wszPROPCERTIFICATESERIALNUMBER L"\0"
  1143. wszPROPCERTIFICATENOTBEFOREDATE L"\0"
  1144. wszPROPCERTIFICATENOTAFTERDATE L"\0"
  1145. wszPROPDISTINGUISHEDNAME L"\0"
  1146. wszPROPCERTTEMPLATE L"\0"
  1147. wszPROPCERTIFICATEHASH L"\0"
  1148. wszPROPREQUESTDOT wszPROPREQUESTDISPOSITIONMESSAGE L"\0"
  1149. ;
  1150. WCHAR wszzBodyArgPending[] =
  1151. wszPROPREQUESTDOT wszPROPREQUESTREQUESTID L"\0"
  1152. wszPROPUPN L"\0"
  1153. wszPROPREQUESTDOT wszPROPREQUESTERNAME L"\0"
  1154. wszPROPREQUESTDOT wszPROPREQUESTSUBMITTEDWHEN L"\0"
  1155. wszPROPREQUESTDOT wszPROPDISTINGUISHEDNAME L"\0"
  1156. wszPROPCERTTEMPLATE L"\0"
  1157. wszPROPREQUESTDOT wszPROPREQUESTDISPOSITIONMESSAGE L"\0"
  1158. ;
  1159. WCHAR wszzBodyArgDenied[] =
  1160. wszPROPREQUESTDOT wszPROPREQUESTREQUESTID L"\0"
  1161. wszPROPREQUESTDOT wszPROPREQUESTERNAME L"\0"
  1162. wszPROPREQUESTDOT wszPROPREQUESTSUBMITTEDWHEN L"\0"
  1163. wszPROPREQUESTDOT wszPROPDISTINGUISHEDNAME L"\0"
  1164. wszPROPREQUESTDOT wszPROPREQUESTDISPOSITIONMESSAGE L"\0"
  1165. wszPROPREQUESTDOT wszPROPREQUESTSTATUSCODE L"\0"
  1166. ;
  1167. WCHAR wszzBodyArgRevoked[] =
  1168. wszPROPCERTIFICATEREQUESTID L"\0"
  1169. wszPROPREQUESTDOT wszPROPREQUESTREVOKEDWHEN L"\0"
  1170. wszPROPREQUESTDOT wszPROPREQUESTREVOKEDEFFECTIVEWHEN L"\0"
  1171. wszPROPREQUESTDOT wszPROPREQUESTREVOKEDREASON L"\0"
  1172. wszPROPUPN L"\0"
  1173. wszPROPREQUESTDOT wszPROPREQUESTERNAME L"\0"
  1174. wszPROPCERTIFICATESERIALNUMBER L"\0"
  1175. wszPROPCERTIFICATENOTBEFOREDATE L"\0"
  1176. wszPROPCERTIFICATENOTAFTERDATE L"\0"
  1177. wszPROPDISTINGUISHEDNAME L"\0"
  1178. wszPROPCERTTEMPLATE L"\0"
  1179. wszPROPCERTIFICATEHASH L"\0"
  1180. wszPROPREQUESTDOT wszPROPREQUESTSTATUSCODE L"\0"
  1181. ;
  1182. WCHAR wszzBodyArgCRLIssued[] = L"\0";
  1183. WCHAR wszzBodyArgShutdown[] = L"\0";
  1184. WCHAR wszzBodyArgStartup[] = L"\0";
  1185. // order in this list must match the order of events in m_pcwszEventRegKeys
  1186. EventFormat FormatList[] =
  1187. {
  1188. // issued
  1189. {
  1190. wszREGEXITISSUEDKEY,
  1191. IDS_TITLEFORMAT_ISSUED,
  1192. IDS_BODYFORMAT_ISSUED,
  1193. wszzBodyArgIssued,
  1194. sizeof(wszzBodyArgIssued),
  1195. wszzTitleArg,
  1196. sizeof(wszzTitleArg),
  1197. },
  1198. // pending
  1199. {
  1200. wszREGEXITPENDINGKEY,
  1201. IDS_TITLEFORMAT_PENDING,
  1202. IDS_BODYFORMAT_PENDING,
  1203. wszzBodyArgPending,
  1204. sizeof(wszzBodyArgPending),
  1205. wszzTitleArg,
  1206. sizeof(wszzTitleArg),
  1207. },
  1208. // denied
  1209. {
  1210. wszREGEXITDENIEDKEY,
  1211. IDS_TITLEFORMAT_DENIED,
  1212. IDS_BODYFORMAT_DENIED,
  1213. wszzBodyArgDenied,
  1214. sizeof(wszzBodyArgDenied),
  1215. wszzTitleArg,
  1216. sizeof(wszzTitleArg),
  1217. },
  1218. // revoked
  1219. {
  1220. wszREGEXITREVOKEDKEY,
  1221. IDS_TITLEFORMAT_REVOKED,
  1222. IDS_BODYFORMAT_REVOKED,
  1223. wszzBodyArgRevoked,
  1224. sizeof(wszzBodyArgRevoked),
  1225. wszzTitleArg,
  1226. sizeof(wszzTitleArg),
  1227. },
  1228. // CRL issued
  1229. {
  1230. wszREGEXITCRLISSUEDKEY,
  1231. IDS_TITLEFORMAT_CRLISSUED,
  1232. IDS_BODYFORMAT_CRLISSUED,
  1233. wszzBodyArgCRLIssued,
  1234. sizeof(wszzBodyArgCRLIssued),
  1235. wszzTitleArg,
  1236. sizeof(wszzTitleArg),
  1237. },
  1238. // shutdown
  1239. {
  1240. wszREGEXITSHUTDOWNKEY,
  1241. IDS_TITLEFORMAT_SHUTDOWN,
  1242. IDS_BODYFORMAT_SHUTDOWN,
  1243. wszzBodyArgShutdown,
  1244. sizeof(wszzBodyArgShutdown),
  1245. wszzTitleArg,
  1246. sizeof(wszzTitleArg),
  1247. },
  1248. // startup
  1249. {
  1250. wszREGEXITSTARTUPKEY,
  1251. IDS_TITLEFORMAT_STARTUP,
  1252. IDS_BODYFORMAT_STARTUP,
  1253. wszzBodyArgStartup,
  1254. sizeof(wszzBodyArgStartup),
  1255. wszzTitleArg,
  1256. sizeof(wszzTitleArg),
  1257. },
  1258. };
  1259. if(!m_hkeySMTP)
  1260. {
  1261. hr = RegCreateKeyEx(
  1262. hkeyExit,
  1263. wszREGEXITSMTPKEY,
  1264. 0,
  1265. NULL,
  1266. REG_OPTION_NON_VOLATILE,
  1267. KEY_ALL_ACCESS,
  1268. NULL,
  1269. &m_hkeySMTP,
  1270. &dwDisp);
  1271. _JumpIfErrorStr(hr, error, "RegCreateKeyEx", wszREGEXITSMTPKEY);
  1272. if(REG_CREATED_NEW_KEY==dwDisp)
  1273. {
  1274. V_VT(&varValue) = VT_I4;
  1275. V_I4(&varValue) = 0; // all notifications disabled
  1276. hr = RegSetValue(
  1277. m_hkeySMTP,
  1278. wszREGEXITSMTPEVENTFILTER,
  1279. &varValue);
  1280. _JumpIfErrorStr(hr, error, "RegSetValue", wszREGEXITSMTPEVENTFILTER);
  1281. VariantClear(&varValue);
  1282. V_VT(&varValue) = VT_BSTR;
  1283. V_BSTR(&varValue) = SysAllocString(L"");// just create the value, user
  1284. hr = RegSetValue( // needs to set server name
  1285. m_hkeySMTP,
  1286. wszREGEXITSMTPSERVER,
  1287. &varValue);
  1288. _JumpIfErrorStr(hr, error, "RegSetValue", wszREGEXITSMTPSERVER);
  1289. VariantClear(&varValue);
  1290. V_VT(&varValue) = VT_I4;
  1291. V_I4(&varValue) = cdoAnonymous;
  1292. hr = RegSetValue(
  1293. m_hkeySMTP,
  1294. wszREGEXITSMTPAUTHENTICATE,
  1295. &varValue);
  1296. _JumpIfErrorStr(hr, error, "RegSetValue", wszREGEXITSMTPAUTHENTICATE);
  1297. VariantClear(&varValue);
  1298. }
  1299. }
  1300. for(int i=0; i<ARRAYSIZE(FormatList); i++)
  1301. {
  1302. CAutoLPWSTR pwszBodyFormat;
  1303. CAutoLPWSTR pwszTitleFormat;
  1304. // create key
  1305. hr = RegCreateKeyEx(
  1306. m_hkeySMTP,
  1307. FormatList[i].pcwszRegKey,
  1308. 0,
  1309. NULL,
  1310. REG_OPTION_NON_VOLATILE,
  1311. KEY_ALL_ACCESS,
  1312. NULL,
  1313. &hkeyEvent,
  1314. &dwDisp);
  1315. _JumpIfErrorStr(hr, error, "RegCreateKeyEx", wszREGEXITSMTPKEY);
  1316. if(REG_CREATED_NEW_KEY==dwDisp)
  1317. {
  1318. // set body format
  1319. hr = myLoadRCString(
  1320. g_hInstance,
  1321. FormatList[i].nBodyFormatResourceID,
  1322. &pwszBodyFormat);
  1323. _JumpIfError(hr, error, "myLoadRCString body format");
  1324. // Body format is a multi line string, convert it to multisz
  1325. // in place by replacing \n with \0
  1326. // Also if we find consecutive \n's, insert spaces in between
  1327. // so we don't end up with a double \0 which normally marks
  1328. // the end of multisz.
  1329. {
  1330. DWORD cbBodyFormat = sizeof(WCHAR)*(wcslen(pwszBodyFormat)+1);
  1331. CAutoLPWSTR pwszBodyFormatFixed = (LPWSTR)LocalAlloc(
  1332. LMEM_FIXED|LMEM_ZEROINIT,
  1333. cbBodyFormat*2); // worst case scenario buffer has only \n's
  1334. _JumpIfAllocFailed(pwszBodyFormatFixed, error);
  1335. WCHAR *pchSrc, *pchDest;
  1336. for(cbBodyFormat=2, pchSrc=pwszBodyFormat, pchDest=pwszBodyFormatFixed;
  1337. *pchSrc;
  1338. pchSrc++, pchDest++, cbBodyFormat++)
  1339. {
  1340. if(L'\n' == *pchSrc)
  1341. {
  1342. *pchDest = L'\0';
  1343. if(L'\n' == *(pchSrc+1)) // detected \n\n, insert space in between
  1344. {
  1345. *++pchDest = L' ';
  1346. cbBodyFormat++;
  1347. }
  1348. } else
  1349. {
  1350. *pchDest = *pchSrc;
  1351. }
  1352. }
  1353. *pchDest++ = L'\0';
  1354. *pchDest = L'\0';
  1355. cbBodyFormat *= 2;
  1356. hr = RegSetValueEx(
  1357. hkeyEvent,
  1358. wszREGEXITBODYFORMAT,
  1359. 0,
  1360. REG_MULTI_SZ,
  1361. (CONST BYTE*) (LPCWSTR) pwszBodyFormatFixed,
  1362. cbBodyFormat);
  1363. _JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGEXITBODYFORMAT);
  1364. }
  1365. // set body args
  1366. hr = RegSetValueEx(
  1367. hkeyEvent,
  1368. wszREGEXITBODYARG,
  1369. 0,
  1370. REG_MULTI_SZ,
  1371. (CONST BYTE*) FormatList[i].pcwszBodyArg,
  1372. FormatList[i].cbBodyArg);
  1373. _JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGEXITBODYARG);
  1374. // set title format
  1375. hr = myLoadRCString(
  1376. g_hInstance,
  1377. FormatList[i].nTitleFormatResourceID,
  1378. &pwszTitleFormat);
  1379. _JumpIfError(hr, error, "myLoadRCString title format");
  1380. hr = RegSetValueEx(
  1381. hkeyEvent,
  1382. wszREGEXITTITLEFORMAT,
  1383. 0,
  1384. REG_SZ,
  1385. (CONST BYTE*) (LPCWSTR) pwszTitleFormat,
  1386. sizeof(WCHAR)*(wcslen(pwszTitleFormat)+1));
  1387. _JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGEXITTITLEFORMAT);
  1388. // set title args
  1389. hr = RegSetValueEx(
  1390. hkeyEvent,
  1391. wszREGEXITTITLEARG,
  1392. 0,
  1393. REG_MULTI_SZ,
  1394. (CONST BYTE*) FormatList[i].pcwszTitleArg,
  1395. FormatList[i].cbTitleArg);
  1396. _JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGEXITBODYARG);
  1397. RegCloseKey(hkeyEvent);
  1398. hkeyEvent = NULL;
  1399. }
  1400. }
  1401. hr = S_OK;
  1402. error:
  1403. if(hkeyEvent)
  1404. {
  1405. RegCloseKey(hkeyEvent);
  1406. }
  1407. return hr;
  1408. }
  1409. /////////////////////////////////////////////////////////////////////////////
  1410. HRESULT CEmailNotify::_LoadTemplateRestrictionsFromRegistry()
  1411. {
  1412. HRESULT hr;
  1413. hr = RegGetValue(
  1414. m_hkeySMTP,
  1415. wszREGEXITSMTPTEMPLATES,
  1416. &m_varTemplateRestrictions);
  1417. if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
  1418. {
  1419. hr =S_OK;
  1420. }
  1421. _JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPTEMPLATES);
  1422. error:
  1423. return hr;
  1424. }
  1425. /////////////////////////////////////////////////////////////////////////////
  1426. inline bool CEmailNotify::_TemplateRestrictionsEnabled(DWORD dwEvent)
  1427. {
  1428. if((dwEvent & EXITEVENT_CRLISSUED) ||
  1429. (dwEvent & EXITEVENT_SHUTDOWN) ||
  1430. (dwEvent & EXITEVENT_STARTUP) ||
  1431. VT_EMPTY==V_VT(&m_varTemplateRestrictions))
  1432. {
  1433. return false;
  1434. }
  1435. else
  1436. {
  1437. return true;
  1438. }
  1439. }
  1440. /////////////////////////////////////////////////////////////////////////////
  1441. bool CEmailNotify::_IsRestrictedTemplate(BSTR strTemplate)
  1442. {
  1443. // attempt to retrieve the name and OID for this template
  1444. CAutoHCERTTYPE hCertType;
  1445. bool fFoundByName = true;
  1446. CAutoBSTR strAlternateTemplateName;
  1447. if(!strTemplate)
  1448. return true;
  1449. HRESULT hr = CAFindCertTypeByName(
  1450. strTemplate,
  1451. NULL,
  1452. CT_FIND_LOCAL_SYSTEM |
  1453. CT_ENUM_MACHINE_TYPES |
  1454. CT_ENUM_USER_TYPES,
  1455. &hCertType);
  1456. if(HRESULT_FROM_WIN32(ERROR_NOT_FOUND) == hr)
  1457. {
  1458. // try with the OID
  1459. hr = CAFindCertTypeByName(
  1460. strTemplate,
  1461. NULL,
  1462. CT_FIND_LOCAL_SYSTEM |
  1463. CT_ENUM_MACHINE_TYPES |
  1464. CT_ENUM_USER_TYPES |
  1465. CT_FIND_BY_OID,
  1466. &hCertType);
  1467. fFoundByName = false;
  1468. }
  1469. if(S_OK==hr)
  1470. {
  1471. LPWSTR *ppwszProp = NULL;
  1472. hr = CAGetCertTypeProperty(
  1473. hCertType,
  1474. fFoundByName?CERTTYPE_PROP_OID:CERTTYPE_PROP_CN,
  1475. &ppwszProp);
  1476. if(S_OK==hr)
  1477. {
  1478. if(ppwszProp && ppwszProp[0])
  1479. {
  1480. strAlternateTemplateName = SysAllocString(
  1481. ppwszProp[0]);
  1482. }
  1483. CAFreeCertTypeProperty(
  1484. hCertType,
  1485. ppwszProp);
  1486. }
  1487. }
  1488. // Must be an array of BSTRs, otherwise loading from registry
  1489. // should have failed
  1490. CSASSERT((VT_ARRAY|VT_BSTR)==V_VT(&m_varTemplateRestrictions));
  1491. BSTR strTempl;
  1492. SafeArrayEnum<BSTR> saenumTemplates(V_ARRAY(&m_varTemplateRestrictions));
  1493. while(S_OK==saenumTemplates.Next(strTempl))
  1494. {
  1495. if((strTemplate &&
  1496. 0==mylstrcmpiL(strTempl, strTemplate)) ||
  1497. (strAlternateTemplateName &&
  1498. 0==mylstrcmpiL(strTempl, strAlternateTemplateName)))
  1499. {
  1500. // found it, send mail
  1501. return false;
  1502. }
  1503. }
  1504. // template not in the list, don't send mail
  1505. return true;
  1506. }
  1507. /////////////////////////////////////////////////////////////////////////////
  1508. HRESULT CEmailNotify::_LoadSMTPFieldsFromRegistry(Fields* pFields)
  1509. {
  1510. HRESULT hr;
  1511. DWORD dwType;
  1512. BYTE* pbValue = NULL;
  1513. DWORD cbValue;
  1514. VARIANT varValue;
  1515. DWORD dwIndex;
  1516. LPWSTR pwszValName = NULL;
  1517. DWORD cbValName;
  1518. DWORD cValues;
  1519. static LPCWSTR pcwszHTTP = L"http://";
  1520. static size_t cHTTP = wcslen(pcwszHTTP);
  1521. // load cdoSMTPServer
  1522. hr = RegGetValue(
  1523. m_hkeySMTP,
  1524. wszREGEXITSMTPSERVER,
  1525. &varValue);
  1526. _JumpIfErrorStr(hr, error, "error retrieving SMTP field", wszREGEXITSMTPSERVER);
  1527. if(V_VT(&varValue) != VT_BSTR)
  1528. {
  1529. VariantClear(&varValue);
  1530. hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
  1531. _JumpErrorStr(hr, error, "invalid field", wszREGEXITSMTPSERVER);
  1532. }
  1533. hr = _SetField(
  1534. pFields,
  1535. cdoSMTPServer,
  1536. &varValue);
  1537. _JumpIfErrorStr(hr, error, "_SetField", cdoSMTPServer);
  1538. VariantClear(&varValue);
  1539. // authentication method optional
  1540. hr = RegGetValue(
  1541. m_hkeySMTP,
  1542. wszREGEXITSMTPAUTHENTICATE,
  1543. &varValue);
  1544. if(S_OK==hr &&
  1545. V_VT(&varValue) != VT_I4)
  1546. {
  1547. hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
  1548. _JumpErrorStr(hr, error, "invalid CC field", wszREGEXITSMTPAUTHENTICATE);
  1549. }
  1550. if(S_OK == hr)
  1551. {
  1552. hr = _SetField(
  1553. pFields,
  1554. cdoSMTPAuthenticate,
  1555. &varValue);
  1556. _JumpIfErrorStr(hr, error, "_SetField", cdoSMTPAuthenticate);
  1557. VariantClear(&varValue);
  1558. }
  1559. else if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
  1560. {
  1561. hr = S_OK;
  1562. }
  1563. else
  1564. {
  1565. _JumpErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPAUTHENTICATE);
  1566. }
  1567. // set other SMTP defaults
  1568. V_VT(&varValue) = VT_I4;
  1569. V_I4(&varValue) = 25; // well known SMTP port
  1570. hr = _SetField(
  1571. pFields,
  1572. cdoSMTPServerPort,
  1573. &varValue);
  1574. _JumpIfErrorStr(hr, error, "_SetField", cdoSMTPAuthenticate);
  1575. V_VT(&varValue) = VT_I4;
  1576. V_I4(&varValue) = cdoSendUsingPort;
  1577. hr = _SetField(
  1578. pFields,
  1579. cdoSendUsingMethod,
  1580. &varValue);
  1581. _JumpIfErrorStr(hr, error, "_SetField", cdoSendUsingMethod);
  1582. // enumerate and set any other CDO fields (only if value name is full HTTP URL)
  1583. hr = RegQueryInfoKey(
  1584. m_hkeySMTP,
  1585. NULL,
  1586. NULL,
  1587. NULL,
  1588. NULL,
  1589. NULL,
  1590. NULL,
  1591. &cValues,
  1592. &cbValName,
  1593. &cbValue,
  1594. NULL,
  1595. NULL);
  1596. _JumpIfErrorStr(hr, error, "RegQueryInfoKey", wszREGEXITSMTPKEY);
  1597. pwszValName = (LPWSTR) LocalAlloc(LMEM_FIXED, ++cbValName*sizeof(WCHAR));
  1598. _JumpIfAllocFailed(pwszValName, error);
  1599. pbValue = (BYTE *) LocalAlloc(LMEM_FIXED, cbValue);
  1600. _JumpIfAllocFailed(pbValue, error);
  1601. for(dwIndex=0;dwIndex<cValues;dwIndex++)
  1602. {
  1603. DWORD cbValueTemp = cbValue;
  1604. DWORD cbValNameTemp = cbValName;
  1605. hr = RegEnumValue(
  1606. m_hkeySMTP,
  1607. dwIndex,
  1608. pwszValName,
  1609. &cbValNameTemp,
  1610. NULL,
  1611. &dwType,
  1612. pbValue,
  1613. &cbValueTemp);
  1614. if (hr != S_OK)
  1615. {
  1616. hr = myHError(hr);
  1617. _JumpError(hr, error, "RegEnumValue");
  1618. }
  1619. // ignore if not an HTTP URL
  1620. if(_wcsnicmp(pwszValName, pcwszHTTP, cHTTP))
  1621. {
  1622. continue;
  1623. }
  1624. hr = myRegValueToVariant(
  1625. dwType,
  1626. cbValueTemp,
  1627. pbValue,
  1628. &varValue);
  1629. _JumpIfError(hr, error, "myRegValueToVariant");
  1630. hr = _SetField(
  1631. pFields,
  1632. pwszValName,
  1633. &varValue);
  1634. _JumpIfError(hr, error, "_SetField");
  1635. VariantClear(&varValue);
  1636. }
  1637. hr = S_OK;
  1638. error:
  1639. VariantClear(&varValue);
  1640. if(pwszValName)
  1641. {
  1642. LocalFree(pwszValName);
  1643. }
  1644. if(pbValue)
  1645. {
  1646. LocalFree(pbValue);
  1647. }
  1648. return hr;
  1649. }
  1650. /////////////////////////////////////////////////////////////////////////////
  1651. HRESULT CEmailNotify::_SetField(
  1652. Fields* pFields,
  1653. LPCWSTR pcwszFieldSchemaName,
  1654. VARIANT *pvarFieldValue)
  1655. {
  1656. HRESULT hr;
  1657. Field* pfld = NULL;
  1658. VARIANT vtName;
  1659. VariantInit(&vtName);
  1660. V_VT(&vtName) = VT_BSTR;
  1661. V_BSTR(&vtName) = SysAllocString(pcwszFieldSchemaName);
  1662. _JumpIfAllocFailed(V_BSTR(&vtName), error);
  1663. hr = pFields->get_Item(vtName, &pfld);
  1664. _JumpIfErrorStr(hr, error, "CDO::Field::get_Item", pcwszFieldSchemaName);
  1665. hr = pfld->put_Value(*pvarFieldValue);
  1666. _JumpIfErrorStr(hr, error, "CDO::Field::put_Value", pcwszFieldSchemaName);
  1667. error:
  1668. VariantClear(&vtName);
  1669. if(pfld)
  1670. {
  1671. pfld->Release();
  1672. }
  1673. return hr;
  1674. }
  1675. /////////////////////////////////////////////////////////////////////////////
  1676. HRESULT CEmailNotify::_LoadSMTPFieldsFromLSASecret(
  1677. Fields* pFields)
  1678. {
  1679. HRESULT hr;
  1680. VARIANT var; // don't clear
  1681. LPWSTR pwszProfileName = NULL;
  1682. LPWSTR pwszLogonName = NULL;
  1683. LPWSTR pwszPassword = NULL;
  1684. BSTR bstrLogonName = NULL;
  1685. BSTR bstrPassword = NULL;
  1686. hr = myGetMapiInfo(
  1687. NULL,
  1688. &pwszProfileName, // not used
  1689. &pwszLogonName,
  1690. &pwszPassword);
  1691. if(S_OK == hr) // if NTLM is used, username & password aren't needed
  1692. {
  1693. bstrLogonName = SysAllocString(pwszLogonName);
  1694. _JumpIfAllocFailed(bstrLogonName, error);
  1695. bstrPassword = SysAllocString(pwszPassword);
  1696. _JumpIfAllocFailed(bstrPassword, error);
  1697. V_VT(&var) = VT_BSTR;
  1698. V_BSTR(&var) = bstrLogonName;
  1699. hr = _SetField(
  1700. pFields,
  1701. cdoSendUserName,
  1702. &var);
  1703. _JumpIfError(hr, error, "_SetField");
  1704. V_VT(&var) = VT_BSTR;
  1705. V_BSTR(&var) = bstrPassword;
  1706. hr = _SetField(
  1707. pFields,
  1708. cdoSendPassword,
  1709. &var);
  1710. _JumpIfError(hr, error, "_SetField");
  1711. }
  1712. hr = S_OK;
  1713. error:
  1714. if (NULL != pwszProfileName)
  1715. {
  1716. LocalFree(pwszProfileName);
  1717. }
  1718. if (NULL != pwszLogonName)
  1719. {
  1720. LocalFree(pwszLogonName);
  1721. }
  1722. if (NULL != pwszPassword)
  1723. {
  1724. myZeroDataString(pwszPassword); // password data
  1725. LocalFree(pwszPassword);
  1726. }
  1727. if(NULL != bstrLogonName)
  1728. {
  1729. SysFreeString(bstrLogonName);
  1730. }
  1731. if(NULL != bstrPassword)
  1732. {
  1733. myZeroDataString(bstrPassword); // password data
  1734. SysFreeString(bstrPassword);
  1735. }
  1736. return hr;
  1737. }
  1738. /////////////////////////////////////////////////////////////////////////////
  1739. HRESULT CEmailNotify::_LoadEventInfoFromRegistry()
  1740. {
  1741. HRESULT hr;
  1742. CSASSERT(m_hkeySMTP);
  1743. for(int i=0; i<m_gcEvents;i++)
  1744. {
  1745. hr = m_NotifyInfoArray[i].LoadInfoFromRegistry(
  1746. m_hkeySMTP,
  1747. m_pcwszEventRegKeys[i]);
  1748. _JumpIfErrorStr(hr, error,
  1749. "CNotifyInfo::LoadInfoFromRegistry",
  1750. m_pcwszEventRegKeys[i]);
  1751. }
  1752. hr = S_OK;
  1753. error:
  1754. return hr;
  1755. }
  1756. /////////////////////////////////////////////////////////////////////////////
  1757. HRESULT CEmailNotify::_GetCAMailAddress(
  1758. ICertServerExit* pServer,
  1759. BSTR& rbstrAddress)
  1760. {
  1761. HRESULT hr;
  1762. VARIANT varMachineDNSName;
  1763. VARIANT varCAName;
  1764. CAutoLPWSTR pwszMailAddr;
  1765. VariantInit(&varCAName);
  1766. VariantInit(&varMachineDNSName);
  1767. if(!m_bstrCAMailAddress)
  1768. {
  1769. //
  1770. // CA_name@machine_dns_name
  1771. //
  1772. hr = exitGetProperty(
  1773. pServer,
  1774. FALSE, // fRequest
  1775. wszPROPSANITIZEDCANAME,
  1776. PROPTYPE_STRING,
  1777. &varCAName);
  1778. _JumpIfErrorStr(hr, error, "Exit:GetCertificateProperty",
  1779. wszPROPSANITIZEDCANAME);
  1780. hr = exitGetProperty(
  1781. pServer,
  1782. FALSE, // fRequest
  1783. wszPROPMACHINEDNSNAME,
  1784. PROPTYPE_STRING,
  1785. &varMachineDNSName);
  1786. _JumpIfErrorStr(hr, error, "Exit:GetCertificateProperty",
  1787. wszPROPMACHINEDNSNAME);
  1788. pwszMailAddr = (LPWSTR) LocalAlloc(
  1789. LMEM_FIXED,
  1790. sizeof(WCHAR)*(SysStringLen(V_BSTR(&varCAName))+
  1791. SysStringLen(V_BSTR(&varMachineDNSName))
  1792. +2));
  1793. _JumpIfAllocFailed(pwszMailAddr, error);
  1794. wcscpy(pwszMailAddr, V_BSTR(&varCAName));
  1795. wcscat(pwszMailAddr, L"@");
  1796. wcscat(pwszMailAddr, V_BSTR(&varMachineDNSName));
  1797. m_bstrCAMailAddress = SysAllocString(pwszMailAddr);
  1798. _JumpIfAllocFailed(m_bstrCAMailAddress, error);
  1799. }
  1800. rbstrAddress = m_bstrCAMailAddress;
  1801. hr = S_OK;
  1802. error:
  1803. VariantClear(&varCAName);
  1804. VariantClear(&varMachineDNSName);
  1805. return(hr);
  1806. }
  1807. /////////////////////////////////////////////////////////////////////////////
  1808. HRESULT CEmailNotify::_GetEmailFromCertSubject(
  1809. const VARIANT *pVarCert,
  1810. BSTR *pbstrEmail)
  1811. {
  1812. HRESULT hr;
  1813. PCCERT_CONTEXT pcc = NULL;
  1814. CAutoLPWSTR strSubjectEmail;
  1815. pcc = CertCreateCertificateContext(
  1816. X509_ASN_ENCODING,
  1817. (const BYTE *)V_BSTR(pVarCert),
  1818. SysStringByteLen(V_BSTR(pVarCert)));
  1819. if(!pcc)
  1820. {
  1821. hr = myHLastError();
  1822. _JumpError(hr, error, "CertCreateCertificateContext");
  1823. }
  1824. hr = myGetCertSubjectField(
  1825. pcc,
  1826. szOID_RSA_emailAddr,
  1827. &strSubjectEmail);
  1828. _JumpIfError(hr, error, "myGetCertSubjectField");
  1829. *pbstrEmail = SysAllocString(strSubjectEmail);
  1830. _JumpIfAllocFailed(*pbstrEmail, error);
  1831. error:
  1832. if(pcc)
  1833. {
  1834. CertFreeCertificateContext(pcc);
  1835. }
  1836. return hr;
  1837. }
  1838. inline DWORD CEmailNotify::_MapEventToOrd(LONG lEvent)
  1839. {
  1840. switch (lEvent)
  1841. { case EXITEVENT_CERTISSUED: return 0;
  1842. case EXITEVENT_CERTPENDING: return 1;
  1843. case EXITEVENT_CERTDENIED: return 2;
  1844. case EXITEVENT_CERTREVOKED: return 3;
  1845. // not impl case EXITEVENT_CERTRETRIEVEPENDING:
  1846. case EXITEVENT_CRLISSUED: return 4;
  1847. case EXITEVENT_SHUTDOWN: return 5;
  1848. case EXITEVENT_STARTUP: return 6;
  1849. default: return MAXDWORD;
  1850. }
  1851. }
  1852. /////////////////////////////////////////////////////////////////////////////
  1853. HRESULT
  1854. CEmailNotify::Notify(
  1855. IN DWORD lExitEvent,
  1856. IN LONG lContext,
  1857. IN WCHAR const *pwszDescription)
  1858. {
  1859. HRESULT hr;
  1860. ICertServerExit* pServer = NULL;
  1861. IMessage* pMsg = NULL;
  1862. IBodyPart *pBp = NULL;
  1863. CNotifyInfo *pNotifyInfo;
  1864. BSTR bstrCharSet = NULL;
  1865. BSTR bstrSetTo, bstrSetFrom; // no free
  1866. BSTR bstrTo = NULL;
  1867. BSTR bstrTitle = NULL;
  1868. BSTR bstrBody = NULL;
  1869. BSTR bstrTemplate = NULL;
  1870. VARIANT varCert;
  1871. bool fRWLockAcquired = false;
  1872. bool fRetryReloadCDOConfig = false;
  1873. VariantInit(&varCert);
  1874. bstrSetTo = NULL;
  1875. if(_IsEventEnabled(lExitEvent))
  1876. {
  1877. hr = GetServerCallbackInterface(&pServer, lContext);
  1878. _JumpIfError(hr, error, "Exit:GetServerCallbackInterface");
  1879. if(_TemplateRestrictionsEnabled(lExitEvent))
  1880. {
  1881. hr = exitGetStringProperty(
  1882. pServer,
  1883. FALSE,
  1884. FALSE,
  1885. wszPROPCERTIFICATETEMPLATE,
  1886. &bstrTemplate);
  1887. if((S_OK == hr || CERTSRV_E_PROPERTY_EMPTY == hr) &&
  1888. _IsRestrictedTemplate(bstrTemplate))
  1889. {
  1890. // don't send mail for this template
  1891. hr = S_OK;
  1892. goto error;
  1893. }
  1894. _JumpIfErrorStr(hr, error, "exitGetStringProperty",
  1895. wszPROPCERTIFICATETEMPLATE);
  1896. }
  1897. pNotifyInfo = &m_NotifyInfoArray[_MapEventToOrd(lExitEvent)];
  1898. hr = CoCreateInstance(CDO::CLSID_Message,
  1899. NULL,
  1900. CLSCTX_INPROC_SERVER,
  1901. CDO::IID_IMessage,
  1902. reinterpret_cast<void**>(&pMsg));
  1903. _JumpIfError(hr, error, "CoCreateInstance CDO_IConfiguration");
  1904. ///////////////////////////////////////////////////////////////////////////
  1905. // Set recipient. Override with registry To field if found.
  1906. if(VT_BSTR == V_VT(&pNotifyInfo->m_varTo))
  1907. {
  1908. bstrSetTo = V_BSTR(&pNotifyInfo->m_varTo);
  1909. }
  1910. else
  1911. {
  1912. // Recipient not enforced in the registry, try to find one in the cert.
  1913. // For CRL and shutdown events, no cert is available, so To field is
  1914. // mandatory
  1915. if(lExitEvent&EXITEVENT_CRLISSUED ||
  1916. lExitEvent&EXITEVENT_SHUTDOWN ||
  1917. lExitEvent&EXITEVENT_STARTUP)
  1918. {
  1919. hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
  1920. _JumpError(hr, error,
  1921. "Recipient field is mandatory for CRL or SHUTDOWN events");
  1922. }
  1923. // look for recipient's address in email cert property (retrieved from
  1924. // subject alt name)
  1925. hr = exitGetStringProperty(
  1926. pServer,
  1927. FALSE,
  1928. FALSE,
  1929. wszPROPEMAIL,
  1930. &bstrTo);
  1931. // no email in subject alt name, try in subject
  1932. if(CERTSRV_E_PROPERTY_EMPTY == hr)
  1933. {
  1934. hr = exitGetProperty(
  1935. pServer,
  1936. FALSE, // fRequest
  1937. wszPROPRAWCERTIFICATE,
  1938. PROPTYPE_BINARY,
  1939. &varCert);
  1940. if(S_OK == hr)
  1941. {
  1942. hr = _GetEmailFromCertSubject(
  1943. &varCert,
  1944. &bstrTo);
  1945. }
  1946. }
  1947. // no email in subject, send to CC list if any
  1948. if(S_OK != hr && VT_BSTR == V_VT(&pNotifyInfo->m_varCC))
  1949. {
  1950. hr = S_OK;
  1951. bstrSetTo = V_BSTR(&pNotifyInfo->m_varCC);
  1952. }
  1953. _JumpIfError(hr, error, "failed to find a recipient");
  1954. if(bstrTo)
  1955. {
  1956. bstrSetTo = bstrTo;
  1957. }
  1958. }
  1959. hr = pMsg->put_To(bstrSetTo);
  1960. _JumpIfError(hr, error, "put_To");
  1961. ///////////////////////////////////////////////////////////////////////
  1962. // Set sender. If not specified in the registry, build it:
  1963. //
  1964. // CAName@MachineDNSName
  1965. //
  1966. if(VT_BSTR == V_VT(&pNotifyInfo->m_varFrom))
  1967. {
  1968. bstrSetFrom = V_BSTR(&pNotifyInfo->m_varFrom);
  1969. }
  1970. else
  1971. {
  1972. hr = _GetCAMailAddress(pServer, bstrSetFrom);
  1973. _JumpIfError(hr, error, "CEmailNotify::_GetCAMailAddress");
  1974. }
  1975. hr = pMsg->put_From(bstrSetFrom);
  1976. _JumpIfError(hr, error, "put_From");
  1977. ///////////////////////////////////////////////////////////////////////////
  1978. // Set CC list, if set in registry
  1979. if(VT_BSTR==V_VT(&pNotifyInfo->m_varCC))
  1980. {
  1981. hr = pMsg->put_CC(V_BSTR(&pNotifyInfo->m_varCC));
  1982. _JumpIfError(hr, error, "put_CC");
  1983. }
  1984. ///////////////////////////////////////////////////////////////////////////
  1985. // Set message body
  1986. hr = pNotifyInfo->BuildMessageBody(pServer, bstrBody);
  1987. _JumpIfError(hr, error, "CNotifyInfo::BuildMessageBody");
  1988. hr = pMsg->put_TextBody(bstrBody);
  1989. _JumpIfError(hr, error, "put_Body");
  1990. ///////////////////////////////////////////////////////////////////////
  1991. // Set body part to UTF-8 charset
  1992. hr = pMsg->get_TextBodyPart(&pBp);
  1993. _JumpIfError(hr, error, "get_BodyPart");
  1994. bstrCharSet = SysAllocString(L"utf-8");
  1995. if (NULL == bstrCharSet)
  1996. {
  1997. hr = E_OUTOFMEMORY;
  1998. _JumpError(hr, error, "Exit:SysAllocString");
  1999. }
  2000. hr = pBp->put_Charset(bstrCharSet);
  2001. _JumpIfError(hr, error, "put_Charset");
  2002. ///////////////////////////////////////////////////////////////////////////
  2003. // Set message title
  2004. hr = pNotifyInfo->BuildMessageTitle(pServer, bstrTitle);
  2005. _JumpIfError(hr, error, "CNotifyInfo::BuildMessageTitle");
  2006. hr = pMsg->put_Subject(bstrTitle);
  2007. _JumpIfError(hr, error, "put_Subject");
  2008. ///////////////////////////////////////////////////////////////////////////
  2009. // Send SMTP message
  2010. DBGPRINT((DBG_SS_CERTEXIT,
  2011. "---MAIL NOTIFICATION---\nTo: %ws\nFrom: %ws\nCC: %ws\n%ws\n%ws\n---END MAIL NOTIFICATION---\n",
  2012. bstrSetTo,
  2013. bstrSetFrom,
  2014. VT_BSTR==V_VT(&pNotifyInfo->m_varCC)?V_BSTR(&pNotifyInfo->m_varCC):L"",
  2015. bstrTitle,
  2016. bstrBody));
  2017. fRetryReloadCDOConfig = false;
  2018. while(true)
  2019. {
  2020. if(m_fReloadCDOConfig || fRetryReloadCDOConfig)
  2021. {
  2022. hr = _InitCDO();
  2023. _JumpIfError(hr, error, "CEmailNotify::_InitCDO");
  2024. }
  2025. // protect m_pICDOCOnfig from being updated
  2026. m_rwlockCDOConfig.GetShared();
  2027. fRWLockAcquired = true;
  2028. hr = pMsg->putref_Configuration(m_pICDOConfig);
  2029. _JumpIfError(hr, error, "putref_Configuration");
  2030. hr = pMsg->Send();
  2031. fRWLockAcquired = false;
  2032. m_rwlockCDOConfig.Release();
  2033. if(CDO_E_SMTP_SEND_FAILED == hr ||
  2034. CDO_E_CONNECTION_DROPPED == hr ||
  2035. CDO_E_FAILED_TO_CONNECT == hr)
  2036. {
  2037. // if this is the first time and it failed due to server connection
  2038. // problems, try again
  2039. if(!fRetryReloadCDOConfig)
  2040. {
  2041. _PrintError(hr,
  2042. "Failed to send mail, reconnecting to server");
  2043. fRetryReloadCDOConfig = true;
  2044. continue;
  2045. }
  2046. m_fReloadCDOConfig = true;
  2047. }
  2048. _JumpIfError(hr, error, "Send");
  2049. break;
  2050. }
  2051. }
  2052. hr = S_OK;
  2053. error:
  2054. if (S_OK != hr)
  2055. {
  2056. WCHAR awc[cwcDWORDSPRINTF];
  2057. WCHAR *apwsz[2];
  2058. switch (lExitEvent)
  2059. {
  2060. case EXITEVENT_INVALID:
  2061. apwsz[0] = L"EXITEVENT_INVALID";
  2062. break;
  2063. case EXITEVENT_CERTISSUED:
  2064. apwsz[0] = L"EXITEVENT_CERTISSUED";
  2065. break;
  2066. case EXITEVENT_CERTPENDING:
  2067. apwsz[0] = L"EXITEVENT_CERTPENDING";
  2068. break;
  2069. case EXITEVENT_CERTDENIED:
  2070. apwsz[0] = L"EXITEVENT_CERTDENIED";
  2071. break;
  2072. case EXITEVENT_CERTREVOKED:
  2073. apwsz[0] = L"EXITEVENT_CERTREVOKED";
  2074. break;
  2075. case EXITEVENT_CERTRETRIEVEPENDING:
  2076. apwsz[0] = L"EXITEVENT_CERTRETRIEVEPENDING";
  2077. break;
  2078. case EXITEVENT_CRLISSUED:
  2079. apwsz[0] = L"EXITEVENT_CRLISSUED";
  2080. break;
  2081. case EXITEVENT_SHUTDOWN:
  2082. apwsz[0] = L"EXITEVENT_SHUTDOWN";
  2083. break;
  2084. case EXITEVENT_STARTUP:
  2085. apwsz[0] = L"EXITEVENT_STARTUP";
  2086. break;
  2087. default:
  2088. wsprintf(awc, L"0x%x", lExitEvent);
  2089. apwsz[0] = awc;
  2090. break;
  2091. }
  2092. apwsz[1] = bstrSetTo;
  2093. if (NULL == bstrSetTo)
  2094. {
  2095. apwsz[1] = wszREGEXITPROPNOTFOUND;
  2096. }
  2097. ::LogModuleStatus(
  2098. g_hInstance,
  2099. S_OK,
  2100. MSG_UNABLE_TO_MAIL_NOTIFICATION,
  2101. FALSE, // fPolicy
  2102. pwszDescription,
  2103. apwsz,
  2104. NULL);
  2105. }
  2106. if(fRWLockAcquired)
  2107. {
  2108. m_rwlockCDOConfig.Release();
  2109. }
  2110. if(pServer)
  2111. {
  2112. pServer->Release();
  2113. }
  2114. if(pMsg)
  2115. {
  2116. pMsg->Release();
  2117. }
  2118. if (NULL != pBp)
  2119. {
  2120. pBp->Release();
  2121. }
  2122. if (NULL != bstrCharSet)
  2123. {
  2124. SysFreeString(bstrCharSet);
  2125. }
  2126. if (bstrTo)
  2127. {
  2128. SysFreeString(bstrTo);
  2129. }
  2130. if (bstrBody)
  2131. {
  2132. SysFreeString(bstrBody);
  2133. }
  2134. if (bstrTitle)
  2135. {
  2136. SysFreeString(bstrTitle);
  2137. }
  2138. if (bstrTemplate)
  2139. {
  2140. SysFreeString(bstrTemplate);
  2141. }
  2142. VariantClear(&varCert);
  2143. return hr;
  2144. }
  2145. LONG CNotifyInfo::FormattedMessageInfo::m_gPropTypes[] =
  2146. {
  2147. PROPTYPE_LONG,
  2148. PROPTYPE_DATE,
  2149. PROPTYPE_BINARY,
  2150. PROPTYPE_STRING,
  2151. };
  2152. LPCWSTR CNotifyInfo::FormattedMessageInfo::m_gwszArchivedKeyPresent = L"1";
  2153. /////////////////////////////////////////////////////////////////////////////
  2154. CNotifyInfo::CNotifyInfo()
  2155. {
  2156. VariantInit(&m_varFrom);
  2157. VariantInit(&m_varTo);
  2158. VariantInit(&m_varCC);
  2159. }
  2160. /////////////////////////////////////////////////////////////////////////////
  2161. CNotifyInfo::~CNotifyInfo()
  2162. {
  2163. VariantClear(&m_varFrom);
  2164. VariantClear(&m_varTo);
  2165. VariantClear(&m_varCC);
  2166. }
  2167. /////////////////////////////////////////////////////////////////////////////
  2168. HRESULT CNotifyInfo::LoadInfoFromRegistry(
  2169. HKEY hkeySMTP,
  2170. LPCWSTR pcwszSubkey)
  2171. {
  2172. HRESULT hr;
  2173. HKEY hkeyEventInfo = NULL;
  2174. VARIANT varBodyFormatTmp;
  2175. VariantInit(&varBodyFormatTmp);
  2176. hr = RegOpenKeyEx(
  2177. hkeySMTP,
  2178. pcwszSubkey,
  2179. 0, // dwReserved
  2180. KEY_READ | KEY_QUERY_VALUE,
  2181. &hkeyEventInfo);
  2182. _JumpIfErrorStr(hr, error, "RegOpenKey", pcwszSubkey);
  2183. hr = RegGetValue(
  2184. hkeyEventInfo,
  2185. wszREGEXITSMTPFROM,
  2186. &m_varFrom);
  2187. if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
  2188. {
  2189. hr = S_OK;
  2190. }
  2191. _JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPFROM);
  2192. hr = RegGetValue(
  2193. hkeyEventInfo,
  2194. wszREGEXITSMTPTO,
  2195. &m_varTo);
  2196. if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
  2197. {
  2198. hr =S_OK;
  2199. }
  2200. _JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPTO);
  2201. hr = RegGetValue(
  2202. hkeyEventInfo,
  2203. wszREGEXITSMTPCC,
  2204. &m_varCC);
  2205. if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
  2206. {
  2207. hr =S_OK;
  2208. }
  2209. _JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPCC);
  2210. hr = RegGetValue(
  2211. hkeyEventInfo,
  2212. wszREGEXITTITLEFORMAT,
  2213. &m_TitleFormat.m_varFormat);
  2214. if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr ||
  2215. VT_BSTR != V_VT(&m_TitleFormat.m_varFormat))
  2216. {
  2217. hr =S_OK;
  2218. VariantClear(&m_TitleFormat.m_varFormat);
  2219. V_VT(&m_TitleFormat.m_varFormat) = VT_BSTR;
  2220. V_BSTR(&m_TitleFormat.m_varFormat) = SysAllocString(L"");
  2221. _JumpIfAllocFailed(V_BSTR(&m_TitleFormat.m_varFormat), error);
  2222. }
  2223. _JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITTITLEFORMAT);
  2224. hr = RegGetValue(
  2225. hkeyEventInfo,
  2226. wszREGEXITTITLEARG,
  2227. &m_TitleFormat.m_varArgs);
  2228. if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr ||
  2229. (VT_ARRAY|VT_BSTR) != V_VT(&m_TitleFormat.m_varArgs))
  2230. {
  2231. VariantClear(&m_TitleFormat.m_varArgs);
  2232. hr =S_OK;
  2233. }
  2234. _JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITTITLEARG);
  2235. hr = RegGetValue(
  2236. hkeyEventInfo,
  2237. wszREGEXITBODYFORMAT,
  2238. &varBodyFormatTmp);
  2239. if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr ||
  2240. ((VT_ARRAY|VT_BSTR) != V_VT(&varBodyFormatTmp)&&
  2241. (VT_BSTR) != V_VT(&varBodyFormatTmp)))
  2242. {
  2243. hr = S_OK;
  2244. V_VT(&m_BodyFormat.m_varFormat) = VT_BSTR;
  2245. V_BSTR(&m_BodyFormat.m_varFormat) = SysAllocString(L"");
  2246. _JumpIfAllocFailed(V_BSTR(&m_BodyFormat.m_varFormat), error);
  2247. }
  2248. else if(S_OK == hr)
  2249. {
  2250. if((VT_ARRAY|VT_BSTR) == V_VT(&varBodyFormatTmp))
  2251. {
  2252. // code down the road expects this to be a BSTR
  2253. // so we concatenate the strings, separated by new lines (\n)
  2254. hr = _ConvertBSTRArrayToBSTR(
  2255. varBodyFormatTmp,
  2256. m_BodyFormat.m_varFormat);
  2257. }
  2258. else // VT_BSTR
  2259. {
  2260. hr = VariantCopy(
  2261. &m_BodyFormat.m_varFormat,
  2262. &varBodyFormatTmp);
  2263. }
  2264. }
  2265. _JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITBODYFORMAT);
  2266. hr = RegGetValue(
  2267. hkeyEventInfo,
  2268. wszREGEXITBODYARG,
  2269. &m_BodyFormat.m_varArgs);
  2270. if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
  2271. {
  2272. hr =S_OK;
  2273. }
  2274. _JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITBODYARG);
  2275. hr = S_OK;
  2276. error:
  2277. if (hkeyEventInfo)
  2278. {
  2279. RegCloseKey(hkeyEventInfo);
  2280. }
  2281. VariantClear(&varBodyFormatTmp);
  2282. return hr;
  2283. }
  2284. /////////////////////////////////////////////////////////////////////////////
  2285. HRESULT CNotifyInfo::_ConvertBSTRArrayToBSTR(VARIANT& varIn, VARIANT& varOut)
  2286. {
  2287. HRESULT hr;
  2288. SafeArrayEnum<BSTR>
  2289. saenumArgs(V_ARRAY(&varIn));
  2290. BSTR bstrArg; //no free
  2291. DWORD cchBufSize = 1;
  2292. LPWSTR pwszOut = NULL;
  2293. WCHAR *pchCrt;
  2294. for(hr = saenumArgs.Next(bstrArg);
  2295. S_OK==hr;
  2296. hr = saenumArgs.Next(bstrArg))
  2297. {
  2298. cchBufSize += SysStringLen(bstrArg)+wcslen(L"\n");
  2299. }
  2300. pwszOut = (LPWSTR) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
  2301. cchBufSize*sizeof(WCHAR));
  2302. _JumpIfAllocFailed(pwszOut, error);
  2303. saenumArgs.Reset();
  2304. pchCrt = pwszOut;
  2305. for(hr = saenumArgs.Next(bstrArg);
  2306. S_OK==hr;
  2307. hr = saenumArgs.Next(bstrArg))
  2308. {
  2309. wcscat(pchCrt, bstrArg);
  2310. wcscat(pchCrt, L"\n");
  2311. }
  2312. V_VT(&varOut) = VT_BSTR;
  2313. V_BSTR(&varOut) = SysAllocString(pwszOut);
  2314. _JumpIfAllocFailed(V_BSTR(&varOut), error);
  2315. hr = S_OK;
  2316. error:
  2317. LOCAL_FREE(pwszOut);
  2318. return hr;
  2319. }
  2320. /////////////////////////////////////////////////////////////////////////////
  2321. HRESULT CNotifyInfo::BuildMessageTitle(ICertServerExit* pServer, BSTR& rbstrOut)
  2322. {
  2323. return m_TitleFormat.BuildFormattedString(
  2324. pServer,
  2325. rbstrOut);
  2326. }
  2327. /////////////////////////////////////////////////////////////////////////////
  2328. HRESULT CNotifyInfo::BuildMessageBody (ICertServerExit* pServer, BSTR& rbstrOut)
  2329. {
  2330. return m_BodyFormat.BuildFormattedString(
  2331. pServer,
  2332. rbstrOut);
  2333. }
  2334. /////////////////////////////////////////////////////////////////////////////
  2335. HRESULT
  2336. CNotifyInfo::FormattedMessageInfo::_FormatStringFromArgs(
  2337. IN LPWSTR *ppwszArgs,
  2338. OPTIONAL OUT WCHAR *pwszOut,
  2339. IN OUT DWORD *pcwcOut)
  2340. {
  2341. HRESULT hr;
  2342. WCHAR const *pwszFmt;
  2343. DWORD cwcOut;
  2344. DWORD cwcBuf;
  2345. WCHAR const *pwszOutOrg = pwszOut;
  2346. cwcBuf = 0;
  2347. if (NULL != pwszOut)
  2348. {
  2349. cwcBuf = *pcwcOut;
  2350. }
  2351. cwcOut = 0;
  2352. pwszFmt = V_BSTR(&m_varFormat);
  2353. if (NULL != pwszFmt)
  2354. {
  2355. while (L'\0' != *pwszFmt)
  2356. {
  2357. DWORD cwcCopy;
  2358. DWORD cwcSkip;
  2359. WCHAR const *pwszT;
  2360. WCHAR const *pwszArg;
  2361. cwcSkip = 0;
  2362. pwszArg = NULL;
  2363. pwszT = wcschr(pwszFmt, L'%');
  2364. if (NULL != pwszT)
  2365. {
  2366. LONG iArg;
  2367. cwcCopy = SAFE_SUBTRACT_POINTERS(pwszT, pwszFmt);
  2368. pwszT++;
  2369. iArg = _wtoi(pwszT);
  2370. if (0 < iArg && m_nArgs >= iArg)
  2371. {
  2372. pwszArg = ppwszArgs[iArg - 1];
  2373. cwcSkip++;
  2374. while (iswdigit(*pwszT))
  2375. {
  2376. pwszT++;
  2377. cwcSkip++;
  2378. }
  2379. }
  2380. else
  2381. {
  2382. cwcCopy++;
  2383. if (L'%' == *pwszT)
  2384. {
  2385. cwcSkip++;
  2386. }
  2387. else
  2388. {
  2389. _PrintErrorStr(
  2390. E_INVALIDARG,
  2391. "Exit:Bad Arg specifier",
  2392. &pwszT[-1]);
  2393. }
  2394. }
  2395. }
  2396. else
  2397. {
  2398. cwcCopy = wcslen(pwszFmt);
  2399. }
  2400. if (NULL != pwszOut)
  2401. {
  2402. if (cwcCopy >= cwcBuf)
  2403. {
  2404. _PrintErrorStr(
  2405. HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER),
  2406. "Exit:overflow",
  2407. pwszFmt);
  2408. pwszOut = NULL;
  2409. }
  2410. else
  2411. {
  2412. CopyMemory(pwszOut, pwszFmt, cwcCopy * sizeof(WCHAR));
  2413. pwszOut += cwcCopy;
  2414. cwcBuf -= cwcCopy;
  2415. }
  2416. }
  2417. pwszFmt += cwcCopy + cwcSkip;
  2418. cwcOut += cwcCopy;
  2419. if (NULL != pwszArg)
  2420. {
  2421. cwcCopy = wcslen(pwszArg);
  2422. if (NULL != pwszOut)
  2423. {
  2424. if (cwcCopy >= cwcBuf)
  2425. {
  2426. pwszOut = NULL;
  2427. _PrintErrorStr(
  2428. HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER),
  2429. "Exit:overflow",
  2430. pwszFmt);
  2431. }
  2432. else
  2433. {
  2434. CopyMemory(pwszOut, pwszArg, cwcCopy * sizeof(WCHAR));
  2435. pwszOut += cwcCopy;
  2436. cwcBuf -= cwcCopy;
  2437. }
  2438. }
  2439. cwcOut += cwcCopy;
  2440. }
  2441. }
  2442. }
  2443. if (NULL != pwszOut)
  2444. {
  2445. if (1 > cwcBuf)
  2446. {
  2447. pwszOut = NULL;
  2448. _PrintError(
  2449. HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER),
  2450. "Exit:overflow end");
  2451. }
  2452. else
  2453. {
  2454. *pwszOut = L'\0';
  2455. }
  2456. }
  2457. *pcwcOut = cwcOut;
  2458. if (NULL == pwszOut)
  2459. {
  2460. (*pcwcOut)++;
  2461. if (NULL != pwszOutOrg)
  2462. {
  2463. hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  2464. _JumpError(hr, error, "Exit:pwszOut");
  2465. }
  2466. }
  2467. hr = S_OK;
  2468. error:
  2469. return(hr);
  2470. }
  2471. /////////////////////////////////////////////////////////////////////////////
  2472. HRESULT
  2473. CNotifyInfo::FormattedMessageInfo::BuildFormattedString(
  2474. ICertServerExit* pServer,
  2475. BSTR& rbstrOut)
  2476. {
  2477. HRESULT hr;
  2478. LPWSTR *ppwszArgs = NULL;
  2479. DWORD cwcOut;
  2480. hr = BuildArgList(pServer, ppwszArgs);
  2481. _JumpIfError(hr, error, "BuildArgList");
  2482. hr = _FormatStringFromArgs(ppwszArgs, NULL, &cwcOut);
  2483. _JumpIfError(hr, error, "_FormatStringFromArgs");
  2484. if (0 < cwcOut) // count included L'\0' terminator
  2485. {
  2486. cwcOut--;
  2487. }
  2488. rbstrOut = SysAllocStringLen(NULL, cwcOut);
  2489. if (NULL == rbstrOut)
  2490. {
  2491. hr = E_OUTOFMEMORY;
  2492. _JumpError(hr, error, "Exit:LocalAlloc");
  2493. }
  2494. cwcOut++; // buffer must include L'\0' terminator
  2495. hr = _FormatStringFromArgs(ppwszArgs, rbstrOut, &cwcOut);
  2496. _JumpIfError(hr, error, "_FormatStringFromArgs");
  2497. // count no longer includes L'\0' terminator
  2498. CSASSERT(SysStringLen(rbstrOut) == wcslen(rbstrOut));
  2499. CSASSERT(SysStringLen(rbstrOut) == cwcOut);
  2500. error:
  2501. FreeArgList(ppwszArgs);
  2502. if (S_OK != hr)
  2503. {
  2504. if (NULL != rbstrOut)
  2505. {
  2506. SysFreeString(rbstrOut);
  2507. rbstrOut = NULL;
  2508. }
  2509. }
  2510. return(hr);
  2511. }
  2512. /////////////////////////////////////////////////////////////////////////////
  2513. void
  2514. CNotifyInfo::FormattedMessageInfo::FreeArgList(
  2515. LPWSTR*& rppwszArgs)
  2516. {
  2517. if(rppwszArgs)
  2518. {
  2519. for(LONG cArgs = 0;cArgs<m_nArgs;cArgs++)
  2520. {
  2521. if(rppwszArgs[cArgs])
  2522. {
  2523. LocalFree(rppwszArgs[cArgs]);
  2524. rppwszArgs[cArgs] = NULL;
  2525. }
  2526. }
  2527. LocalFree(rppwszArgs);
  2528. rppwszArgs = NULL;
  2529. }
  2530. }
  2531. HRESULT CNotifyInfo::FormattedMessageInfo::InitializeArgInfo(
  2532. ICertServerExit* pServer)
  2533. {
  2534. HRESULT hr;
  2535. SafeArrayEnum<BSTR>
  2536. saenumArgs(V_ARRAY(&m_varArgs));
  2537. BSTR bstrArg; //no free
  2538. LONG cArgs;
  2539. VARIANT varValue;
  2540. GetCertOrRequestProp pGetPropertyFunc;
  2541. EnterCriticalSection(&m_critsectObjInit);
  2542. if(!m_fInitialized)
  2543. {
  2544. if(VT_EMPTY == V_VT(&m_varArgs))
  2545. {
  2546. m_nArgs = 0;
  2547. }
  2548. else
  2549. {
  2550. m_nArgs = saenumArgs.GetCount();
  2551. CSASSERT(!m_pfArgFromRequestTable);
  2552. m_pfArgFromRequestTable = (bool*)
  2553. LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
  2554. sizeof(bool)*m_nArgs);
  2555. _JumpIfAllocFailed(m_pfArgFromRequestTable, error);
  2556. CSASSERT(!m_pArgType);
  2557. m_pArgType = (LONG*)
  2558. LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
  2559. sizeof(LONG)*m_nArgs);
  2560. _JumpIfAllocFailed(m_pArgType, error);
  2561. VariantInit(&varValue);
  2562. for(cArgs=0, hr = saenumArgs.Next(bstrArg);
  2563. S_OK==hr;
  2564. cArgs++, hr = saenumArgs.Next(bstrArg))
  2565. {
  2566. BSTR bstrPropertyName = bstrArg;
  2567. VariantInit(&varValue);
  2568. m_pfArgFromRequestTable[cArgs] =
  2569. (0 == _wcsnicmp(bstrArg,
  2570. wszPROPREQUESTDOT,
  2571. wcslen(wszPROPREQUESTDOT)));
  2572. if(m_pfArgFromRequestTable[cArgs])
  2573. {
  2574. // properties from request table start with request.
  2575. bstrPropertyName += wcslen(wszPROPREQUESTDOT);
  2576. }
  2577. pGetPropertyFunc =
  2578. m_pfArgFromRequestTable[cArgs]?
  2579. (&(ICertServerExit::GetRequestProperty)):
  2580. (&(ICertServerExit::GetCertificateProperty));
  2581. // we don't know the type of property yet, figure it out
  2582. // by trying each type
  2583. for(LONG cType = 0; cType<ARRAYSIZE(m_gPropTypes); cType++)
  2584. {
  2585. hr = (pServer->*pGetPropertyFunc)(
  2586. bstrPropertyName,
  2587. m_gPropTypes[cType],
  2588. &varValue);
  2589. if(S_OK == hr ||
  2590. CERTSRV_E_PROPERTY_EMPTY == hr) // found the type
  2591. {
  2592. m_pArgType[cArgs] = m_gPropTypes[cType];
  2593. DBGPRINT((DBG_SS_CERTEXIT, "Property %s has type %d\n",
  2594. bstrPropertyName, m_gPropTypes[cType]));
  2595. break;
  2596. }
  2597. }
  2598. // if not found, default will be 0 (invalid type)
  2599. VariantClear(&varValue);
  2600. }
  2601. }
  2602. m_fInitialized = true;
  2603. }
  2604. hr = S_OK;
  2605. error:
  2606. if(S_OK != hr)
  2607. {
  2608. LOCAL_FREE(m_pfArgFromRequestTable);
  2609. m_pfArgFromRequestTable = NULL;
  2610. LOCAL_FREE(m_pArgType);
  2611. m_pArgType = NULL;
  2612. }
  2613. LeaveCriticalSection(&m_critsectObjInit);
  2614. return hr;
  2615. }
  2616. /////////////////////////////////////////////////////////////////////////////
  2617. HRESULT
  2618. CNotifyInfo::FormattedMessageInfo::BuildArgList(
  2619. ICertServerExit* pServer,
  2620. LPWSTR*& rppwszArgs)
  2621. {
  2622. HRESULT hr;
  2623. SafeArrayEnum<BSTR>
  2624. saenumArgs(V_ARRAY(&m_varArgs));
  2625. BSTR bstrArg; //no free
  2626. LONG cArgs;
  2627. VARIANT varValue;
  2628. GetCertOrRequestProp pGetPropertyFunc;
  2629. rppwszArgs = NULL;
  2630. // REG_SZ, ie VT_BSTR
  2631. if(VT_BSTR != V_VT(&m_varFormat))
  2632. {
  2633. hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
  2634. _JumpError(hr, error, "invalid message format");
  2635. }
  2636. // REG_MULTISZ, ie VT_ARRAY|VT_BSTR or VT_EMPTY if not found
  2637. if((VT_ARRAY|VT_BSTR) != V_VT(&m_varArgs) &&
  2638. VT_EMPTY != V_VT(&m_varArgs))
  2639. {
  2640. hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
  2641. _JumpError(hr, error, "invalid message arg");
  2642. }
  2643. if(!m_fInitialized)
  2644. {
  2645. hr = InitializeArgInfo(pServer);
  2646. _JumpIfError(hr, error, "FormattedMessageInfo::InitializeArgInfo");
  2647. }
  2648. saenumArgs.Reset();
  2649. if(m_nArgs>0)
  2650. {
  2651. rppwszArgs = (LPWSTR*) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
  2652. m_nArgs*sizeof(LPWSTR));
  2653. _JumpIfAllocFailed(rppwszArgs, error);
  2654. for(cArgs=0, hr = saenumArgs.Next(bstrArg);
  2655. S_OK==hr;
  2656. cArgs++, hr = saenumArgs.Next(bstrArg))
  2657. {
  2658. BSTR bstrPropertyName = bstrArg;
  2659. LONG lType = m_pArgType[cArgs];
  2660. VariantInit(&varValue);
  2661. if(m_pfArgFromRequestTable[cArgs])
  2662. {
  2663. // properties from request table start with request.
  2664. bstrPropertyName += wcslen(wszPROPREQUESTDOT);
  2665. }
  2666. pGetPropertyFunc =
  2667. m_pfArgFromRequestTable[cArgs]?
  2668. (&(ICertServerExit::GetRequestProperty)):
  2669. (&(ICertServerExit::GetCertificateProperty));
  2670. hr = (pServer->*pGetPropertyFunc)(
  2671. bstrPropertyName,
  2672. m_pArgType[cArgs],
  2673. &varValue);
  2674. if(S_OK != hr)
  2675. {
  2676. lType = PROPTYPE_STRING;
  2677. V_VT(&varValue) = VT_BSTR;
  2678. V_BSTR(&varValue) = SysAllocString(wszREGEXITPROPNOTFOUND);
  2679. _JumpIfAllocFailed(V_BSTR(&varValue), error);
  2680. hr = S_OK;
  2681. }
  2682. hr = ConvertToString(
  2683. &varValue,
  2684. lType,
  2685. bstrPropertyName,
  2686. &rppwszArgs[cArgs]);
  2687. if(S_OK != hr)
  2688. {
  2689. rppwszArgs[cArgs] = (LPWSTR) LocalAlloc(LMEM_FIXED,
  2690. sizeof(WCHAR)*(wcslen(wszREGEXITPROPNOTFOUND)+1));
  2691. _JumpIfAllocFailed(rppwszArgs[cArgs], error);
  2692. wcscpy(rppwszArgs[cArgs], wszREGEXITPROPNOTFOUND);
  2693. }
  2694. VariantClear(&varValue);
  2695. }
  2696. }
  2697. hr = S_OK;
  2698. error:
  2699. if(S_OK != hr)
  2700. {
  2701. FreeArgList(rppwszArgs);
  2702. }
  2703. return hr;
  2704. }
  2705. /////////////////////////////////////////////////////////////////////////////
  2706. HRESULT CNotifyInfo::FormattedMessageInfo::ConvertToString(
  2707. VARIANT* pvarValue,
  2708. LONG lType,
  2709. LPCWSTR pcwszPropertyName,
  2710. LPWSTR* ppwszValue)
  2711. {
  2712. HRESULT hr = E_FAIL;
  2713. switch(lType)
  2714. {
  2715. case PROPTYPE_LONG:
  2716. if(0 == _wcsicmp(pcwszPropertyName, wszPROPREQUESTSTATUSCODE))
  2717. {
  2718. *ppwszValue = const_cast<WCHAR*>(myGetErrorMessageText(
  2719. V_I4(pvarValue),
  2720. TRUE));
  2721. if(!*ppwszValue)
  2722. {
  2723. hr = E_OUTOFMEMORY;
  2724. }
  2725. else
  2726. {
  2727. hr = S_OK;
  2728. }
  2729. }
  2730. else
  2731. {
  2732. hr = ConvertToStringI2I4(
  2733. V_I4(pvarValue),
  2734. ppwszValue);
  2735. }
  2736. break;
  2737. case PROPTYPE_DATE:
  2738. hr = ConvertToStringDATE(&V_DATE(pvarValue), TRUE, ppwszValue);
  2739. break;
  2740. case PROPTYPE_BINARY:
  2741. if(0 == _wcsicmp(pcwszPropertyName, wszPROPREQUESTRAWARCHIVEDKEY))
  2742. {
  2743. hr = myDupString(m_gwszArchivedKeyPresent, ppwszValue);
  2744. }
  2745. else
  2746. {
  2747. hr = myCryptBinaryToString(
  2748. (const BYTE*) V_BSTR(pvarValue),
  2749. SysStringByteLen(V_BSTR(pvarValue)),
  2750. CRYPT_STRING_BASE64,
  2751. ppwszValue);
  2752. }
  2753. break;
  2754. case PROPTYPE_STRING:
  2755. if(0 == _wcsicmp(pcwszPropertyName, wszPROPCERTTEMPLATE))
  2756. {
  2757. hr = GetCertTypeFriendlyName(
  2758. V_BSTR(pvarValue),
  2759. ppwszValue);
  2760. } // fall through
  2761. if(S_OK != hr)
  2762. {
  2763. hr = ConvertToStringWSZ(
  2764. V_BSTR(pvarValue),
  2765. ppwszValue);
  2766. }
  2767. break;
  2768. }
  2769. return hr;
  2770. }