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.

901 lines
27 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows NT Security
  4. // Copyright (C) Microsoft Corporation, 1997 - 2000
  5. //
  6. // File: xcert.cpp
  7. //
  8. // Contents: CCertChainEngine's Cross Certificate Methods
  9. //
  10. // History: 22-Dec-99 philh Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include <global.hxx>
  14. #include <dbgdef.h>
  15. //+=========================================================================
  16. // Cross Certificate Distribution Point Support Functions
  17. //==========================================================================
  18. //+-------------------------------------------------------------------------
  19. // Get and allocate the cross certificate distribution points Url array
  20. // and info for the specified certificate.
  21. //--------------------------------------------------------------------------
  22. BOOL
  23. WINAPI
  24. XCertGetDistPointsUrl(
  25. IN PCCERT_CONTEXT pCert,
  26. OUT PCRYPT_URL_ARRAY *ppUrlArray,
  27. OUT PCRYPT_URL_INFO *ppUrlInfo
  28. )
  29. {
  30. BOOL fResult;
  31. PCRYPT_URL_ARRAY pUrlArray = NULL;
  32. DWORD cbUrlArray = 0;
  33. PCRYPT_URL_INFO pUrlInfo = NULL;
  34. DWORD cbUrlInfo = 0;
  35. if (!ChainGetObjectUrl(
  36. URL_OID_CROSS_CERT_DIST_POINT,
  37. (LPVOID) pCert,
  38. CRYPT_GET_URL_FROM_PROPERTY | CRYPT_GET_URL_FROM_EXTENSION,
  39. NULL, // pUrlArray
  40. &cbUrlArray,
  41. NULL, // pUrlInfo
  42. &cbUrlInfo,
  43. NULL // pvReserved
  44. ))
  45. goto GetObjectUrlError;
  46. pUrlArray = (PCRYPT_URL_ARRAY) new BYTE [cbUrlArray];
  47. if (NULL == pUrlArray)
  48. goto OutOfMemory;
  49. pUrlInfo = (PCRYPT_URL_INFO) new BYTE [cbUrlInfo];
  50. if (NULL == pUrlInfo)
  51. goto OutOfMemory;
  52. if (!ChainGetObjectUrl(
  53. URL_OID_CROSS_CERT_DIST_POINT,
  54. (LPVOID) pCert,
  55. CRYPT_GET_URL_FROM_PROPERTY | CRYPT_GET_URL_FROM_EXTENSION,
  56. pUrlArray,
  57. &cbUrlArray,
  58. pUrlInfo,
  59. &cbUrlInfo,
  60. NULL // pvReserved
  61. ))
  62. goto GetObjectUrlError;
  63. if (0 == pUrlArray->cUrl || 0 == pUrlInfo->cGroup)
  64. goto NoDistPointUrls;
  65. fResult = TRUE;
  66. CommonReturn:
  67. *ppUrlArray = pUrlArray;
  68. *ppUrlInfo = pUrlInfo;
  69. return fResult;
  70. ErrorReturn:
  71. if (pUrlArray) {
  72. delete (LPBYTE) pUrlArray;
  73. pUrlArray = NULL;
  74. }
  75. if (pUrlInfo) {
  76. delete (LPBYTE) pUrlInfo;
  77. pUrlInfo = NULL;
  78. }
  79. fResult = FALSE;
  80. goto CommonReturn;
  81. TRACE_ERROR(GetObjectUrlError)
  82. SET_ERROR(OutOfMemory, E_OUTOFMEMORY)
  83. SET_ERROR(NoDistPointUrls, CRYPT_E_NOT_FOUND)
  84. }
  85. //+-------------------------------------------------------------------------
  86. // Checks and returns TRUE if all the Urls are contained in the
  87. // distribution point.
  88. //--------------------------------------------------------------------------
  89. BOOL
  90. WINAPI
  91. XCertIsUrlInDistPoint(
  92. IN DWORD cUrl,
  93. IN LPWSTR *ppwszUrl,
  94. IN PXCERT_DP_ENTRY pEntry
  95. )
  96. {
  97. for ( ; 0 < cUrl; cUrl--, ppwszUrl++) {
  98. DWORD cDPUrl = pEntry->cUrl;
  99. LPWSTR *ppwszDPUrl = pEntry->rgpwszUrl;
  100. for ( ; 0 < cDPUrl; cDPUrl--, ppwszDPUrl++) {
  101. if (0 == wcscmp(*ppwszUrl, *ppwszDPUrl))
  102. break;
  103. }
  104. if (0 == cDPUrl)
  105. return FALSE;
  106. }
  107. return TRUE;
  108. }
  109. //+-------------------------------------------------------------------------
  110. // Finds a distribution point link containing all the Urls.
  111. //--------------------------------------------------------------------------
  112. PXCERT_DP_LINK
  113. WINAPI
  114. XCertFindUrlInDistPointLinks(
  115. IN DWORD cUrl,
  116. IN LPWSTR *rgpwszUrl,
  117. IN PXCERT_DP_LINK pLink
  118. )
  119. {
  120. for ( ; pLink; pLink = pLink->pNext) {
  121. if (XCertIsUrlInDistPoint(cUrl, rgpwszUrl, pLink->pCrossCertDPEntry))
  122. return pLink;
  123. }
  124. return NULL;
  125. }
  126. //+-------------------------------------------------------------------------
  127. // Finds a distribution point entry containing all the Urls.
  128. //--------------------------------------------------------------------------
  129. PXCERT_DP_ENTRY
  130. WINAPI
  131. XCertFindUrlInDistPointEntries(
  132. IN DWORD cUrl,
  133. IN LPWSTR *rgpwszUrl,
  134. PXCERT_DP_ENTRY pEntry
  135. )
  136. {
  137. for ( ; pEntry; pEntry = pEntry->pNext) {
  138. if (XCertIsUrlInDistPoint(cUrl, rgpwszUrl, pEntry))
  139. return pEntry;
  140. }
  141. return NULL;
  142. }
  143. //+-------------------------------------------------------------------------
  144. // Inserts the cross certificate distribution entry into the engine's
  145. // list. The list is ordered according to ascending NextSyncTimes.
  146. //--------------------------------------------------------------------------
  147. void
  148. CCertChainEngine::InsertCrossCertDistPointEntry(
  149. IN OUT PXCERT_DP_ENTRY pEntry
  150. )
  151. {
  152. if (NULL == m_pCrossCertDPEntry) {
  153. // First entry to be added to engine's list
  154. pEntry->pNext = NULL;
  155. pEntry->pPrev = NULL;
  156. m_pCrossCertDPEntry = pEntry;
  157. } else {
  158. PXCERT_DP_ENTRY pListEntry = m_pCrossCertDPEntry;
  159. BOOL fLast = FALSE;
  160. // Loop while Entry's NextSyncTime > list's NextSyncTime
  161. while (0 < CompareFileTime(&pEntry->NextSyncTime,
  162. &pListEntry->NextSyncTime)) {
  163. if (NULL == pListEntry->pNext) {
  164. fLast = TRUE;
  165. break;
  166. } else
  167. pListEntry = pListEntry->pNext;
  168. }
  169. if (fLast) {
  170. assert(NULL == pListEntry->pNext);
  171. pEntry->pNext = NULL;
  172. pEntry->pPrev = pListEntry;
  173. pListEntry->pNext = pEntry;
  174. } else {
  175. pEntry->pNext = pListEntry;
  176. pEntry->pPrev = pListEntry->pPrev;
  177. if (pListEntry->pPrev) {
  178. assert(pListEntry->pPrev->pNext == pListEntry);
  179. pListEntry->pPrev->pNext = pEntry;
  180. } else {
  181. assert(m_pCrossCertDPEntry == pListEntry);
  182. m_pCrossCertDPEntry = pEntry;
  183. }
  184. pListEntry->pPrev = pEntry;
  185. }
  186. }
  187. }
  188. //+-------------------------------------------------------------------------
  189. // Removes the cross certificate distribution point from the engine's list.
  190. //--------------------------------------------------------------------------
  191. void
  192. CCertChainEngine::RemoveCrossCertDistPointEntry(
  193. IN OUT PXCERT_DP_ENTRY pEntry
  194. )
  195. {
  196. if (pEntry->pNext)
  197. pEntry->pNext->pPrev = pEntry->pPrev;
  198. if (pEntry->pPrev)
  199. pEntry->pPrev->pNext = pEntry->pNext;
  200. else
  201. m_pCrossCertDPEntry = pEntry->pNext;
  202. }
  203. //+-------------------------------------------------------------------------
  204. // For an online certificate distribution point updates the NextSyncTime
  205. // and repositions accordingly in the engine's list.
  206. //
  207. // NextSyncTime = LastSyncTime + dwSyncDeltaTime.
  208. //--------------------------------------------------------------------------
  209. void
  210. CCertChainEngine::RepositionOnlineCrossCertDistPointEntry(
  211. IN OUT PXCERT_DP_ENTRY pEntry,
  212. IN LPFILETIME pLastSyncTime
  213. )
  214. {
  215. assert(!I_CryptIsZeroFileTime(pLastSyncTime));
  216. pEntry->LastSyncTime = *pLastSyncTime;
  217. pEntry->dwOfflineCnt = 0;
  218. I_CryptIncrementFileTimeBySeconds(
  219. pLastSyncTime,
  220. pEntry->dwSyncDeltaTime,
  221. &pEntry->NextSyncTime
  222. );
  223. RemoveCrossCertDistPointEntry(pEntry);
  224. InsertCrossCertDistPointEntry(pEntry);
  225. }
  226. //+-------------------------------------------------------------------------
  227. // For an offline certificate distribution point, increments the offline
  228. // count, updates the NextSyncTime to be some delta from the current time
  229. // and repositions accordingly in the engine's list.
  230. //
  231. // NextSyncTime = CurrentTime +
  232. // rgChainOfflineUrlDeltaSeconds[dwOfflineCnt - 1]
  233. //--------------------------------------------------------------------------
  234. void
  235. CCertChainEngine::RepositionOfflineCrossCertDistPointEntry(
  236. IN OUT PXCERT_DP_ENTRY pEntry,
  237. IN LPFILETIME pCurrentTime
  238. )
  239. {
  240. pEntry->dwOfflineCnt++;
  241. I_CryptIncrementFileTimeBySeconds(
  242. pCurrentTime,
  243. ChainGetOfflineUrlDeltaSeconds(pEntry->dwOfflineCnt),
  244. &pEntry->NextSyncTime
  245. );
  246. RemoveCrossCertDistPointEntry(pEntry);
  247. InsertCrossCertDistPointEntry(pEntry);
  248. }
  249. //+-------------------------------------------------------------------------
  250. // For a smaller SyncDeltaTime in a certificate distribution point,
  251. // updates the NextSyncTime and repositions accordingly in the engine's list.
  252. //
  253. // Note, if the distribution point is offline, the NextSyncTime isn't
  254. // updated.
  255. //
  256. // NextSyncTime = LastSyncTime + dwSyncDeltaTime.
  257. //--------------------------------------------------------------------------
  258. void
  259. CCertChainEngine::RepositionNewSyncDeltaTimeCrossCertDistPointEntry(
  260. IN OUT PXCERT_DP_ENTRY pEntry,
  261. IN DWORD dwSyncDeltaTime
  262. )
  263. {
  264. if (dwSyncDeltaTime >= pEntry->dwSyncDeltaTime)
  265. return;
  266. pEntry->dwSyncDeltaTime = dwSyncDeltaTime;
  267. if (I_CryptIsZeroFileTime(&pEntry->LastSyncTime) ||
  268. 0 != pEntry->dwOfflineCnt)
  269. return;
  270. RepositionOnlineCrossCertDistPointEntry(pEntry, &pEntry->LastSyncTime);
  271. }
  272. //+-------------------------------------------------------------------------
  273. // Creates the cross certificate distribution point and insert's in the
  274. // engine's list.
  275. //
  276. // The returned entry has a refCnt of 1.
  277. //--------------------------------------------------------------------------
  278. PXCERT_DP_ENTRY
  279. CCertChainEngine::CreateCrossCertDistPointEntry(
  280. IN DWORD dwSyncDeltaTime,
  281. IN DWORD cUrl,
  282. IN LPWSTR *rgpwszUrl
  283. )
  284. {
  285. PXCERT_DP_ENTRY pEntry;
  286. DWORD cbEntry;
  287. LPWSTR *ppwszEntryUrl;
  288. LPWSTR pwszEntryUrl;
  289. DWORD i;
  290. cbEntry = sizeof(XCERT_DP_ENTRY) + cUrl * sizeof(LPWSTR);
  291. for (i = 0; i < cUrl; i++)
  292. cbEntry += (wcslen(rgpwszUrl[i]) + 1) * sizeof(WCHAR);
  293. pEntry = (PXCERT_DP_ENTRY) new BYTE [cbEntry];
  294. if (NULL == pEntry) {
  295. SetLastError((DWORD) E_OUTOFMEMORY);
  296. return NULL;
  297. }
  298. memset(pEntry, 0, sizeof(XCERT_DP_ENTRY));
  299. pEntry->lRefCnt = 1;
  300. pEntry->dwSyncDeltaTime = dwSyncDeltaTime;
  301. pEntry->cUrl = cUrl;
  302. pEntry->rgpwszUrl = ppwszEntryUrl = (LPWSTR *) &pEntry[1];
  303. pwszEntryUrl = (LPWSTR) &ppwszEntryUrl[cUrl];
  304. for (i = 0; i < cUrl; i++) {
  305. ppwszEntryUrl[i] = pwszEntryUrl;
  306. wcscpy(pwszEntryUrl, rgpwszUrl[i]);
  307. pwszEntryUrl += wcslen(rgpwszUrl[i]) + 1;
  308. }
  309. InsertCrossCertDistPointEntry(pEntry);
  310. return pEntry;
  311. }
  312. //+-------------------------------------------------------------------------
  313. // Increments the cross certificate distribution point's reference count.
  314. //--------------------------------------------------------------------------
  315. void
  316. CCertChainEngine::AddRefCrossCertDistPointEntry(
  317. IN OUT PXCERT_DP_ENTRY pEntry
  318. )
  319. {
  320. pEntry->lRefCnt++;
  321. }
  322. //+-------------------------------------------------------------------------
  323. // Decrements the cross certificate distribution point's reference count.
  324. //
  325. // When decremented to 0, removed from the engine's list and freed.
  326. //
  327. // Returns TRUE if decremented to 0 and freed.
  328. //--------------------------------------------------------------------------
  329. BOOL
  330. CCertChainEngine::ReleaseCrossCertDistPointEntry(
  331. IN OUT PXCERT_DP_ENTRY pEntry
  332. )
  333. {
  334. if (0 != --pEntry->lRefCnt)
  335. return FALSE;
  336. RemoveCrossCertDistPointEntry(pEntry);
  337. FreeCrossCertDistPoints(&pEntry->pChildCrossCertDPLink);
  338. if (pEntry->hUrlStore) {
  339. CertRemoveStoreFromCollection(
  340. m_hCrossCertStore,
  341. pEntry->hUrlStore
  342. );
  343. CertCloseStore(pEntry->hUrlStore, 0);
  344. }
  345. delete (LPBYTE) pEntry;
  346. return TRUE;
  347. }
  348. //+-------------------------------------------------------------------------
  349. // Finds and gets the Cross Certificate Distribution Points for the
  350. // specified certificate store.
  351. //
  352. // *ppLinkHead is updated to contain the store's distribution point links.
  353. //--------------------------------------------------------------------------
  354. BOOL
  355. CCertChainEngine::GetCrossCertDistPointsForStore(
  356. IN HCERTSTORE hStore,
  357. IN OUT PXCERT_DP_LINK *ppLinkHead
  358. )
  359. {
  360. BOOL fResult;
  361. PXCERT_DP_LINK pOldLinkHead = *ppLinkHead;
  362. PXCERT_DP_LINK pNewLinkHead = NULL;
  363. PCCERT_CONTEXT pCert = NULL;
  364. PCRYPT_URL_ARRAY pUrlArray = NULL;
  365. PCRYPT_URL_INFO pUrlInfo = NULL;
  366. while (pCert = CertFindCertificateInStore(
  367. hStore,
  368. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  369. 0, // dwFindFlags
  370. CERT_FIND_CROSS_CERT_DIST_POINTS,
  371. NULL, // pvFindPara,
  372. pCert
  373. )) {
  374. DWORD dwSyncDeltaTime;
  375. DWORD cDP;
  376. DWORD *pcUrl;
  377. LPWSTR *ppwszUrl;
  378. if (!XCertGetDistPointsUrl(
  379. pCert,
  380. &pUrlArray,
  381. &pUrlInfo
  382. ))
  383. continue;
  384. dwSyncDeltaTime = pUrlInfo->dwSyncDeltaTime;
  385. if (0 == dwSyncDeltaTime)
  386. dwSyncDeltaTime = XCERT_DEFAULT_SYNC_DELTA_TIME;
  387. else if (XCERT_MIN_SYNC_DELTA_TIME > dwSyncDeltaTime)
  388. dwSyncDeltaTime = XCERT_MIN_SYNC_DELTA_TIME;
  389. cDP = pUrlInfo->cGroup;
  390. pcUrl = pUrlInfo->rgcGroupEntry;
  391. ppwszUrl = pUrlArray->rgwszUrl;
  392. for ( ; 0 < cDP; cDP--, ppwszUrl += *pcUrl++) {
  393. PXCERT_DP_LINK pLink;
  394. PXCERT_DP_ENTRY pEntry;
  395. DWORD cUrl = *pcUrl;
  396. if (0 == cUrl)
  397. continue;
  398. // Do we already have an entry in the new list
  399. if (XCertFindUrlInDistPointLinks(cUrl, ppwszUrl, pNewLinkHead))
  400. continue;
  401. // If the entry existed in the old list, move to the new list
  402. if (pLink = XCertFindUrlInDistPointLinks(
  403. cUrl, ppwszUrl, pOldLinkHead)) {
  404. if (pLink->pNext)
  405. pLink->pNext->pPrev = pLink->pPrev;
  406. if (pLink->pPrev)
  407. pLink->pPrev->pNext = pLink->pNext;
  408. else
  409. pOldLinkHead = pLink->pNext;
  410. RepositionNewSyncDeltaTimeCrossCertDistPointEntry(
  411. pLink->pCrossCertDPEntry, dwSyncDeltaTime);
  412. } else {
  413. // Check if the entry already exists for the engine
  414. if (pEntry = XCertFindUrlInDistPointEntries(
  415. cUrl, ppwszUrl, m_pCrossCertDPEntry)) {
  416. AddRefCrossCertDistPointEntry(pEntry);
  417. RepositionNewSyncDeltaTimeCrossCertDistPointEntry(
  418. pEntry, dwSyncDeltaTime);
  419. } else {
  420. // Create entry and insert at beginning of
  421. // entries list.
  422. if (NULL == (pEntry = CreateCrossCertDistPointEntry(
  423. dwSyncDeltaTime,
  424. cUrl,
  425. ppwszUrl
  426. )))
  427. goto CreateDistPointEntryError;
  428. }
  429. pLink = new XCERT_DP_LINK;
  430. if (NULL == pLink) {
  431. ReleaseCrossCertDistPointEntry(pEntry);
  432. goto CreateDistPointLinkError;
  433. }
  434. pLink->pCrossCertDPEntry = pEntry;
  435. }
  436. if (pNewLinkHead) {
  437. assert(NULL == pNewLinkHead->pPrev);
  438. pNewLinkHead->pPrev = pLink;
  439. }
  440. pLink->pNext = pNewLinkHead;
  441. pLink->pPrev = NULL;
  442. pNewLinkHead = pLink;
  443. }
  444. delete (LPBYTE) pUrlArray;
  445. pUrlArray = NULL;
  446. delete (LPBYTE) pUrlInfo;
  447. pUrlInfo = NULL;
  448. }
  449. assert(NULL == pUrlArray);
  450. assert(NULL == pUrlInfo);
  451. assert(NULL == pCert);
  452. *ppLinkHead = pNewLinkHead;
  453. fResult = TRUE;
  454. CommonReturn:
  455. if (pOldLinkHead) {
  456. DWORD dwErr = GetLastError();
  457. FreeCrossCertDistPoints(&pOldLinkHead);
  458. SetLastError(dwErr);
  459. }
  460. return fResult;
  461. ErrorReturn:
  462. *ppLinkHead = NULL;
  463. if (pUrlArray)
  464. delete (LPBYTE) pUrlArray;
  465. if (pUrlInfo)
  466. delete (LPBYTE) pUrlInfo;
  467. if (pCert)
  468. CertFreeCertificateContext(pCert);
  469. if (pNewLinkHead) {
  470. FreeCrossCertDistPoints(&pNewLinkHead);
  471. assert(NULL == pNewLinkHead);
  472. }
  473. fResult = FALSE;
  474. goto CommonReturn;
  475. TRACE_ERROR(CreateDistPointEntryError)
  476. TRACE_ERROR(CreateDistPointLinkError)
  477. }
  478. //+-------------------------------------------------------------------------
  479. // Removes an orphan'ed entry not in any list of links.
  480. //--------------------------------------------------------------------------
  481. void
  482. CCertChainEngine::RemoveCrossCertDistPointOrphanEntry(
  483. IN PXCERT_DP_ENTRY pOrphanEntry
  484. )
  485. {
  486. PXCERT_DP_ENTRY pEntry;
  487. for (pEntry = m_pCrossCertDPEntry; pEntry; pEntry = pEntry->pNext) {
  488. PXCERT_DP_LINK pLink = pEntry->pChildCrossCertDPLink;
  489. while (pLink) {
  490. if (pLink->pCrossCertDPEntry == pOrphanEntry) {
  491. if (pLink->pNext)
  492. pLink->pNext->pPrev = pLink->pPrev;
  493. if (pLink->pPrev)
  494. pLink->pPrev->pNext = pLink->pNext;
  495. else
  496. pEntry->pChildCrossCertDPLink = pLink->pNext;
  497. delete pLink;
  498. if (ReleaseCrossCertDistPointEntry(pOrphanEntry))
  499. return;
  500. else
  501. break;
  502. }
  503. pLink = pLink->pNext;
  504. }
  505. }
  506. }
  507. //+-------------------------------------------------------------------------
  508. // Returns TRUE if the entry is in this or any child link list
  509. //--------------------------------------------------------------------------
  510. BOOL
  511. WINAPI
  512. XCertIsDistPointInLinkList(
  513. IN PXCERT_DP_ENTRY pOrphanEntry,
  514. IN PXCERT_DP_LINK pLink
  515. )
  516. {
  517. for (; pLink; pLink = pLink->pNext) {
  518. PXCERT_DP_ENTRY pEntry = pLink->pCrossCertDPEntry;
  519. if (pOrphanEntry == pEntry)
  520. return TRUE;
  521. // Note, inhibit recursion by checking an entry's list of links
  522. // only once.
  523. if (!pEntry->fChecked) {
  524. pEntry->fChecked = TRUE;
  525. if (XCertIsDistPointInLinkList(pOrphanEntry,
  526. pEntry->pChildCrossCertDPLink))
  527. return TRUE;
  528. }
  529. }
  530. return FALSE;
  531. }
  532. //+-------------------------------------------------------------------------
  533. // Frees the cross certificate distribution point links.
  534. //--------------------------------------------------------------------------
  535. void
  536. CCertChainEngine::FreeCrossCertDistPoints(
  537. IN OUT PXCERT_DP_LINK *ppLinkHead
  538. )
  539. {
  540. PXCERT_DP_LINK pLink = *ppLinkHead;
  541. *ppLinkHead = NULL;
  542. while (pLink) {
  543. PXCERT_DP_LINK pDelete;
  544. PXCERT_DP_ENTRY pEntry;
  545. pEntry = pLink->pCrossCertDPEntry;
  546. if (ReleaseCrossCertDistPointEntry(pEntry))
  547. ;
  548. else {
  549. // Clear the fChecked flag for all entries
  550. PXCERT_DP_ENTRY pCheckEntry;
  551. for (pCheckEntry = m_pCrossCertDPEntry; pCheckEntry;
  552. pCheckEntry = pCheckEntry->pNext)
  553. pCheckEntry->fChecked = FALSE;
  554. if (!XCertIsDistPointInLinkList(pEntry, m_pCrossCertDPLink))
  555. // An orphaned entry. Not in anyone else's list
  556. RemoveCrossCertDistPointOrphanEntry(pEntry);
  557. }
  558. pDelete = pLink;
  559. pLink = pLink->pNext;
  560. delete pDelete;
  561. }
  562. }
  563. //+-------------------------------------------------------------------------
  564. // Retrieve the cross certificates
  565. //
  566. // Leaves the engine's critical section to do the URL
  567. // fetching. If the engine was touched by another thread,
  568. // it fails with LastError set to ERROR_CAN_NOT_COMPLETE.
  569. //
  570. // If the URL store is changed, increments engine's touch count and flushes
  571. // issuer and end cert object caches.
  572. //
  573. // Assumption: Chain engine is locked once in the calling thread.
  574. //--------------------------------------------------------------------------
  575. BOOL
  576. CCertChainEngine::RetrieveCrossCertUrl(
  577. IN PCCHAINCALLCONTEXT pCallContext,
  578. IN OUT PXCERT_DP_ENTRY pEntry,
  579. IN DWORD dwRetrievalFlags,
  580. IN OUT BOOL *pfTimeValid
  581. )
  582. {
  583. BOOL fResult;
  584. FILETIME CurrentTime;
  585. HCERTSTORE hNewUrlStore = NULL;
  586. FILETIME NewLastSyncTime;
  587. CRYPT_RETRIEVE_AUX_INFO RetrieveAuxInfo;
  588. DWORD i;
  589. memset(&RetrieveAuxInfo, 0, sizeof(RetrieveAuxInfo));
  590. RetrieveAuxInfo.cbSize = sizeof(RetrieveAuxInfo);
  591. RetrieveAuxInfo.pLastSyncTime = &NewLastSyncTime;
  592. pCallContext->CurrentTime(&CurrentTime);
  593. // Loop through Urls and try to retrieve a time valid cross cert URL
  594. for (i = 0; i < pEntry->cUrl; i++) {
  595. NewLastSyncTime = CurrentTime;
  596. LPWSTR pwszUrl = NULL;
  597. DWORD cbUrl;
  598. // Do URL fetching outside of the engine's critical section
  599. // Need to make a copy of the Url string. pEntry
  600. // can be modified by another thread outside of the critical section.
  601. cbUrl = (wcslen(pEntry->rgpwszUrl[i]) + 1) * sizeof(WCHAR);
  602. pwszUrl = (LPWSTR) PkiNonzeroAlloc(cbUrl);
  603. if (NULL == pwszUrl)
  604. goto OutOfMemory;
  605. memcpy(pwszUrl, pEntry->rgpwszUrl[i], cbUrl);
  606. pCallContext->ChainEngine()->UnlockEngine();
  607. fResult = ChainRetrieveObjectByUrlW(
  608. pwszUrl,
  609. CONTEXT_OID_CAPI2_ANY,
  610. dwRetrievalFlags |
  611. CRYPT_RETRIEVE_MULTIPLE_OBJECTS |
  612. CRYPT_STICKY_CACHE_RETRIEVAL,
  613. pCallContext->ChainPara()->dwUrlRetrievalTimeout,
  614. (LPVOID *) &hNewUrlStore,
  615. NULL, // hAsyncRetrieve
  616. NULL, // pCredentials
  617. NULL, // pvVerify
  618. &RetrieveAuxInfo
  619. );
  620. pCallContext->ChainEngine()->LockEngine();
  621. PkiFree(pwszUrl);
  622. if (pCallContext->IsTouchedEngine())
  623. goto TouchedDuringUrlRetrieval;
  624. if (fResult) {
  625. assert(hNewUrlStore);
  626. if (0 > CompareFileTime(&pEntry->LastSyncTime, &NewLastSyncTime)) {
  627. BOOL fStoreChanged = FALSE;
  628. // Move us to the head of the Url list
  629. DWORD j;
  630. LPWSTR pwszUrl = pEntry->rgpwszUrl[i];
  631. for (j = i; 0 < j; j--)
  632. pEntry->rgpwszUrl[j] = pEntry->rgpwszUrl[j - 1];
  633. pEntry->rgpwszUrl[0] = pwszUrl;
  634. if (NULL == pEntry->hUrlStore) {
  635. if (!CertAddStoreToCollection(
  636. m_hCrossCertStore,
  637. hNewUrlStore,
  638. 0,
  639. 0
  640. ))
  641. goto AddStoreToCollectionError;
  642. pEntry->hUrlStore = hNewUrlStore;
  643. hNewUrlStore = NULL;
  644. fStoreChanged = TRUE;
  645. } else {
  646. DWORD dwOutFlags = 0;
  647. if (!I_CertSyncStoreEx(
  648. pEntry->hUrlStore,
  649. hNewUrlStore,
  650. ICERT_SYNC_STORE_INHIBIT_SYNC_PROPERTY_IN_FLAG,
  651. &dwOutFlags,
  652. NULL // pvReserved
  653. ))
  654. goto SyncStoreError;
  655. if (dwOutFlags & ICERT_SYNC_STORE_CHANGED_OUT_FLAG)
  656. fStoreChanged = TRUE;
  657. }
  658. if (fStoreChanged) {
  659. m_pCertObjectCache->FlushObjects( pCallContext );
  660. pCallContext->TouchEngine();
  661. if (!GetCrossCertDistPointsForStore(
  662. pEntry->hUrlStore,
  663. &pEntry->pChildCrossCertDPLink
  664. ))
  665. goto UpdateDistPointError;
  666. }
  667. RepositionOnlineCrossCertDistPointEntry(pEntry,
  668. &NewLastSyncTime);
  669. if (0 < CompareFileTime(&pEntry->NextSyncTime, &CurrentTime)) {
  670. *pfTimeValid = TRUE;
  671. break;
  672. }
  673. }
  674. if (hNewUrlStore) {
  675. CertCloseStore(hNewUrlStore, 0);
  676. hNewUrlStore = NULL;
  677. }
  678. }
  679. }
  680. fResult = TRUE;
  681. CommonReturn:
  682. if (hNewUrlStore)
  683. CertCloseStore(hNewUrlStore, 0);
  684. return fResult;
  685. ErrorReturn:
  686. fResult = FALSE;
  687. goto CommonReturn;
  688. TRACE_ERROR(AddStoreToCollectionError)
  689. TRACE_ERROR(SyncStoreError)
  690. TRACE_ERROR(UpdateDistPointError)
  691. TRACE_ERROR(OutOfMemory)
  692. SET_ERROR(TouchedDuringUrlRetrieval, ERROR_CAN_NOT_COMPLETE)
  693. }
  694. //+-------------------------------------------------------------------------
  695. // Update cross certificate distribution points whose NextSyncTime has
  696. // expired.
  697. //
  698. // Leaves the engine's critical section to do the URL
  699. // fetching. If the engine was touched by another thread,
  700. // it fails with LastError set to ERROR_CAN_NOT_COMPLETE.
  701. //
  702. // If the URL store is changed, increments engine's touch count and flushes
  703. // issuer and end cert object caches.
  704. //
  705. // Assumption: Chain engine is locked once in the calling thread.
  706. //--------------------------------------------------------------------------
  707. BOOL
  708. CCertChainEngine::UpdateCrossCerts(
  709. IN PCCHAINCALLCONTEXT pCallContext
  710. )
  711. {
  712. BOOL fResult;
  713. PXCERT_DP_ENTRY pEntry;
  714. FILETIME CurrentTime;
  715. pEntry = m_pCrossCertDPEntry;
  716. if (NULL == pEntry)
  717. goto SuccessReturn;
  718. m_dwCrossCertDPResyncIndex++;
  719. pCallContext->CurrentTime(&CurrentTime);
  720. while (pEntry &&
  721. 0 >= CompareFileTime(&pEntry->NextSyncTime, &CurrentTime)) {
  722. PXCERT_DP_ENTRY pNextEntry = pEntry->pNext;
  723. if (pEntry->dwResyncIndex < m_dwCrossCertDPResyncIndex) {
  724. BOOL fTimeValid = FALSE;
  725. if (0 == pEntry->dwResyncIndex || pCallContext->IsOnline()) {
  726. RetrieveCrossCertUrl(
  727. pCallContext,
  728. pEntry,
  729. CRYPT_CACHE_ONLY_RETRIEVAL,
  730. &fTimeValid
  731. );
  732. if (pCallContext->IsTouchedEngine())
  733. goto TouchedDuringUrlRetrieval;
  734. if (!fTimeValid && pCallContext->IsOnline()) {
  735. RetrieveCrossCertUrl(
  736. pCallContext,
  737. pEntry,
  738. CRYPT_WIRE_ONLY_RETRIEVAL,
  739. &fTimeValid
  740. );
  741. if (pCallContext->IsTouchedEngine())
  742. goto TouchedDuringUrlRetrieval;
  743. if (!fTimeValid)
  744. RepositionOfflineCrossCertDistPointEntry(pEntry,
  745. &CurrentTime);
  746. }
  747. // Start over at the beginning. May have added some entries.
  748. pNextEntry = m_pCrossCertDPEntry;
  749. }
  750. pEntry->dwResyncIndex = m_dwCrossCertDPResyncIndex;
  751. }
  752. // else
  753. // Skip entries we have already processed.
  754. pEntry = pNextEntry;
  755. }
  756. SuccessReturn:
  757. fResult = TRUE;
  758. CommonReturn:
  759. return fResult;
  760. ErrorReturn:
  761. fResult = FALSE;
  762. goto CommonReturn;
  763. SET_ERROR(TouchedDuringUrlRetrieval, ERROR_CAN_NOT_COMPLETE)
  764. }