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.

1807 lines
41 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1995.
  5. //
  6. // File: cache.c
  7. //
  8. // Contents:
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 09-23-97 jbanes LSA integration stuff.
  15. // 07-31-98 jbanes Made thread-safe.
  16. //
  17. //----------------------------------------------------------------------------
  18. #include "spbase.h"
  19. #include <limits.h>
  20. #include <mapper.h>
  21. #include <sslcache.h>
  22. SCHANNEL_CACHE SchannelCache =
  23. {
  24. NULL, // SessionCache
  25. SP_CACHE_CLIENT_LIFESPAN, // dwClientLifespan
  26. SP_CACHE_SERVER_LIFESPAN, // dwServerLifespan
  27. SP_CACHE_CLEANUP_INTERVAL, // dwCleanupInterval
  28. SP_MAXIMUM_CACHE_ELEMENTS, // dwCacheSize
  29. SP_MAXIMUM_CACHE_ELEMENTS, // dwMaximumEntries
  30. 0 // dwUsedEntries
  31. };
  32. RTL_CRITICAL_SECTION g_CacheCleanupLock;
  33. BOOL g_CacheCleanupCritSectInitialized = FALSE;
  34. LIST_ENTRY g_CacheCleanupList;
  35. DWORD g_CacheCleanupCount = 0;
  36. HANDLE g_CacheCleanupEvent = NULL;
  37. HANDLE g_CacheCleanupWaitObject = NULL;
  38. BOOL g_fMultipleProcessClientCache = FALSE;
  39. BOOL g_fCacheInitialized = FALSE;
  40. // Perf counter values.
  41. DWORD g_cClientHandshakes = 0;
  42. DWORD g_cServerHandshakes = 0;
  43. DWORD g_cClientReconnects = 0;
  44. DWORD g_cServerReconnects = 0;
  45. BOOL
  46. SPCacheDelete(
  47. PSessCacheItem pItem);
  48. BOOL
  49. CacheExpireElements(
  50. BOOL fCleanupOnly,
  51. BOOL fBackground);
  52. VOID
  53. CacheCleanupHandler(
  54. PVOID pVoid,
  55. BOOLEAN fTimeout);
  56. SP_STATUS
  57. SPInitSessionCache(VOID)
  58. {
  59. DWORD i;
  60. NTSTATUS Status = STATUS_SUCCESS;
  61. SP_BEGIN("SPInitSessionCache");
  62. //
  63. // Allocate memory for cache, and initialize synchronization resource.
  64. //
  65. InitializeListHead(&SchannelCache.EntryList);
  66. RtlInitializeResource(&SchannelCache.Lock);
  67. SchannelCache.LockInitialized = TRUE;
  68. SchannelCache.SessionCache = (PLIST_ENTRY)SPExternalAlloc(SchannelCache.dwCacheSize * sizeof(LIST_ENTRY));
  69. if(SchannelCache.SessionCache == NULL)
  70. {
  71. Status = SP_LOG_RESULT(STATUS_NO_MEMORY);
  72. goto cleanup;
  73. }
  74. for(i = 0; i < SchannelCache.dwCacheSize; i++)
  75. {
  76. InitializeListHead(&SchannelCache.SessionCache[i]);
  77. }
  78. DebugLog((DEB_TRACE, "Space reserved at 0x%x for %d cache entries.\n",
  79. SchannelCache.SessionCache,
  80. SchannelCache.dwCacheSize));
  81. //
  82. // Initialize cache cleanup objects.
  83. //
  84. InitializeListHead(&g_CacheCleanupList);
  85. Status = RtlInitializeCriticalSection(&g_CacheCleanupLock);
  86. if(!NT_SUCCESS(Status))
  87. {
  88. goto cleanup;
  89. }
  90. g_CacheCleanupCritSectInitialized = TRUE;
  91. g_CacheCleanupEvent = CreateEvent(NULL,
  92. FALSE,
  93. FALSE,
  94. NULL);
  95. if(NULL == g_CacheCleanupEvent)
  96. {
  97. Status = GetLastError();
  98. goto cleanup;
  99. }
  100. if(!RegisterWaitForSingleObject(&g_CacheCleanupWaitObject,
  101. g_CacheCleanupEvent,
  102. CacheCleanupHandler,
  103. NULL,
  104. SchannelCache.dwCleanupInterval,
  105. WT_EXECUTEDEFAULT))
  106. {
  107. Status = GetLastError();
  108. goto cleanup;
  109. }
  110. g_fCacheInitialized = TRUE;
  111. Status = STATUS_SUCCESS;
  112. cleanup:
  113. if(!NT_SUCCESS(Status))
  114. {
  115. SPShutdownSessionCache();
  116. }
  117. SP_RETURN(Status);
  118. }
  119. SP_STATUS
  120. SPShutdownSessionCache(VOID)
  121. {
  122. PSessCacheItem pItem;
  123. PLIST_ENTRY pList;
  124. DWORD i;
  125. SP_BEGIN("SPShutdownSessionCache");
  126. if(SchannelCache.LockInitialized)
  127. {
  128. RtlAcquireResourceExclusive(&SchannelCache.Lock, TRUE);
  129. }
  130. g_fCacheInitialized = FALSE;
  131. if(SchannelCache.SessionCache != NULL)
  132. {
  133. // Blindly kill all cache items.
  134. // No contexts should be running at
  135. // this time.
  136. pList = SchannelCache.EntryList.Flink;
  137. while(pList != &SchannelCache.EntryList)
  138. {
  139. pItem = CONTAINING_RECORD(pList, SessCacheItem, EntryList.Flink);
  140. pList = pList->Flink;
  141. SPCacheDelete(pItem);
  142. }
  143. SPExternalFree(SchannelCache.SessionCache);
  144. }
  145. if(g_CacheCleanupCritSectInitialized)
  146. {
  147. RtlDeleteCriticalSection(&g_CacheCleanupLock);
  148. g_CacheCleanupCritSectInitialized = FALSE;
  149. }
  150. if(g_CacheCleanupWaitObject)
  151. {
  152. UnregisterWaitEx(g_CacheCleanupWaitObject, INVALID_HANDLE_VALUE);
  153. g_CacheCleanupWaitObject = NULL;
  154. }
  155. if(g_CacheCleanupEvent)
  156. {
  157. CloseHandle(g_CacheCleanupEvent);
  158. g_CacheCleanupEvent = NULL;
  159. }
  160. if(SchannelCache.LockInitialized)
  161. {
  162. RtlDeleteResource(&SchannelCache.Lock);
  163. SchannelCache.LockInitialized = FALSE;
  164. }
  165. SP_RETURN(PCT_ERR_OK);
  166. }
  167. LONG
  168. SPCacheReference(
  169. PSessCacheItem pItem)
  170. {
  171. LONG cRet;
  172. if(pItem == NULL)
  173. {
  174. return -1;
  175. }
  176. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  177. cRet = InterlockedIncrement(&pItem->cRef);
  178. return cRet;
  179. }
  180. LONG
  181. SPCacheDereference(PSessCacheItem pItem)
  182. {
  183. long cRet;
  184. if(pItem == NULL)
  185. {
  186. return -1;
  187. }
  188. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  189. cRet = InterlockedDecrement(&pItem->cRef);
  190. ASSERT(cRet > 0);
  191. return cRet;
  192. }
  193. BOOL
  194. SPCacheDelete(
  195. PSessCacheItem pItem)
  196. {
  197. long cRet;
  198. DebugLog((DEB_TRACE, "Delete cache item:0x%x\n", pItem));
  199. if(pItem == NULL)
  200. {
  201. return FALSE;
  202. }
  203. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  204. pItem->pActiveServerCred = NULL;
  205. pItem->pServerCred = NULL;
  206. if(pItem->hMasterKey)
  207. {
  208. if(!CryptDestroyKey(pItem->hMasterKey))
  209. {
  210. SP_LOG_RESULT(GetLastError());
  211. }
  212. pItem->hMasterKey = 0;
  213. }
  214. if(pItem->pRemoteCert)
  215. {
  216. CertFreeCertificateContext(pItem->pRemoteCert);
  217. pItem->pRemoteCert = NULL;
  218. }
  219. if(pItem->pRemotePublic)
  220. {
  221. SPExternalFree(pItem->pRemotePublic);
  222. pItem->pRemotePublic = NULL;
  223. }
  224. if(pItem->phMapper)
  225. {
  226. if(pItem->hLocator)
  227. {
  228. SslCloseLocator(pItem->phMapper, pItem->hLocator);
  229. pItem->hLocator = 0;
  230. }
  231. SslDereferenceMapper(pItem->phMapper);
  232. }
  233. pItem->phMapper = NULL;
  234. if(pItem->pbServerCertificate)
  235. {
  236. SPExternalFree(pItem->pbServerCertificate);
  237. pItem->pbServerCertificate = NULL;
  238. pItem->cbServerCertificate = 0;
  239. }
  240. if(pItem->szCacheID)
  241. {
  242. SPExternalFree(pItem->szCacheID);
  243. pItem->szCacheID = NULL;
  244. }
  245. if(pItem->pClientCred)
  246. {
  247. SPDeleteCred(pItem->pClientCred);
  248. pItem->pClientCred = NULL;
  249. }
  250. if(pItem->pClientCert)
  251. {
  252. CertFreeCertificateContext(pItem->pClientCert);
  253. pItem->pClientCert = NULL;
  254. }
  255. if(pItem->pClonedItem)
  256. {
  257. SPCacheDereference(pItem->pClonedItem);
  258. pItem->pClonedItem = NULL;
  259. }
  260. if(pItem->pbAppData)
  261. {
  262. SPExternalFree(pItem->pbAppData);
  263. pItem->pbAppData = NULL;
  264. }
  265. SPExternalFree(pItem);
  266. return TRUE;
  267. }
  268. void
  269. SPCachePurgeCredential(
  270. PSPCredentialGroup pCred)
  271. {
  272. PSessCacheItem pItem;
  273. PLIST_ENTRY pList;
  274. DWORD i;
  275. //
  276. // Only server credentials are bound to the cache, so return if this is
  277. // a client credential.
  278. //
  279. if(pCred->grbitProtocol & SP_PROT_CLIENTS)
  280. {
  281. return;
  282. }
  283. //
  284. // Search through the cache entries looking for entries that are
  285. // bound to the specified server credential.
  286. //
  287. RtlAcquireResourceShared(&SchannelCache.Lock, TRUE);
  288. pList = SchannelCache.EntryList.Flink;
  289. while(pList != &SchannelCache.EntryList)
  290. {
  291. pItem = CONTAINING_RECORD(pList, SessCacheItem, EntryList.Flink);
  292. pList = pList->Flink;
  293. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  294. // Is this a server cache entry?
  295. if((pItem->fProtocol & SP_PROT_SERVERS) == 0)
  296. {
  297. continue;
  298. }
  299. // Does this item match the current credentials?
  300. if(!IsSameThumbprint(&pCred->CredThumbprint, &pItem->CredThumbprint))
  301. {
  302. continue;
  303. }
  304. // Mark this entry as non-resumable. This will cause the entry to
  305. // be deleted automatically by the cleanup routines.
  306. pItem->ZombieJuju = FALSE;
  307. pItem->DeferredJuju = FALSE;
  308. }
  309. RtlReleaseResource(&SchannelCache.Lock);
  310. //
  311. // Delete all unused non-resumable cache entries.
  312. //
  313. CacheExpireElements(FALSE, FALSE);
  314. }
  315. void
  316. SPCachePurgeProcessId(
  317. ULONG ProcessId)
  318. {
  319. PSessCacheItem pItem;
  320. PLIST_ENTRY pList;
  321. DWORD i;
  322. //
  323. // Search through the cache entries looking for entries that are
  324. // bound to the specified process.
  325. //
  326. RtlAcquireResourceShared(&SchannelCache.Lock, TRUE);
  327. pList = SchannelCache.EntryList.Flink;
  328. while(pList != &SchannelCache.EntryList)
  329. {
  330. pItem = CONTAINING_RECORD(pList, SessCacheItem, EntryList.Flink);
  331. pList = pList->Flink;
  332. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  333. // Does this item match the specified process?
  334. if(pItem->ProcessID != ProcessId)
  335. {
  336. continue;
  337. }
  338. // Mark the entry as ownerless.
  339. pItem->ProcessID = 0;
  340. // Mark this entry as non-resumable. This will cause the entry to
  341. // be deleted automatically by the cleanup routines.
  342. pItem->ZombieJuju = FALSE;
  343. pItem->DeferredJuju = FALSE;
  344. }
  345. RtlReleaseResource(&SchannelCache.Lock);
  346. //
  347. // Delete all unused non-resumable cache entries.
  348. //
  349. CacheExpireElements(FALSE, FALSE);
  350. }
  351. BOOL
  352. IsSameTargetName(
  353. LPWSTR Name1,
  354. LPWSTR Name2)
  355. {
  356. if(Name1 == Name2)
  357. {
  358. return TRUE;
  359. }
  360. if(Name1 == NULL || Name2 == NULL || wcscmp(Name1, Name2) != 0)
  361. {
  362. return FALSE;
  363. }
  364. return TRUE;
  365. }
  366. BOOL
  367. DoesAppAllowCipher(
  368. PSPCredentialGroup pCredGroup,
  369. PSessCacheItem pItem)
  370. {
  371. PKeyExchangeInfo pExchInfo;
  372. if(pCredGroup == NULL)
  373. {
  374. return FALSE;
  375. }
  376. //
  377. // Is protocol supported?
  378. //
  379. if((pItem->fProtocol & pCredGroup->grbitEnabledProtocols) == 0)
  380. {
  381. return FALSE;
  382. }
  383. //
  384. // Is cipher supported?
  385. //
  386. if(pItem->dwStrength < pCredGroup->dwMinStrength)
  387. {
  388. return FALSE;
  389. }
  390. if(pItem->dwStrength > pCredGroup->dwMaxStrength)
  391. {
  392. return FALSE;
  393. }
  394. if(!IsAlgAllowed(pCredGroup, pItem->aiCipher))
  395. {
  396. return FALSE;
  397. }
  398. //
  399. // Is hash supported?
  400. //
  401. if(!IsAlgAllowed(pCredGroup, pItem->aiHash))
  402. {
  403. return FALSE;
  404. }
  405. //
  406. // Is exchange alg supported?
  407. //
  408. if(pItem->SessExchSpec != SP_EXCH_UNKNOWN)
  409. {
  410. pExchInfo = GetKeyExchangeInfo(pItem->SessExchSpec);
  411. if(pExchInfo == NULL)
  412. {
  413. return FALSE;
  414. }
  415. if((pExchInfo->fProtocol & pItem->fProtocol) == 0)
  416. {
  417. return FALSE;
  418. }
  419. if(!IsAlgAllowed(pCredGroup, pExchInfo->aiExch))
  420. {
  421. return FALSE;
  422. }
  423. }
  424. return TRUE;
  425. }
  426. BOOL SPCacheRetrieveBySession(
  427. struct _SPContext * pContext,
  428. PBYTE pbSessionID,
  429. DWORD cbSessionID,
  430. PSessCacheItem *ppRetItem)
  431. {
  432. DWORD index;
  433. DWORD timeNow;
  434. ULONG ProcessID;
  435. PSessCacheItem pItem;
  436. PLIST_ENTRY pList;
  437. BOOL fFound = FALSE;
  438. DebugLog((DEB_TRACE, "SPCacheRetrieveBySession (%x) called\n", pContext));
  439. if(ppRetItem == NULL)
  440. {
  441. return FALSE;
  442. }
  443. //
  444. // Compute the cache index.
  445. //
  446. if(cbSessionID < sizeof(DWORD))
  447. {
  448. DebugLog((DEB_TRACE, " FAILED\n"));
  449. return FALSE;
  450. }
  451. CopyMemory((PBYTE)&index, pbSessionID, sizeof(DWORD));
  452. if(index >= SchannelCache.dwCacheSize)
  453. {
  454. DebugLog((DEB_TRACE, " FAILED\n"));
  455. return FALSE;
  456. }
  457. //
  458. // Retrieve the current time and application process id.
  459. //
  460. timeNow = GetTickCount();
  461. SslGetClientProcess(&ProcessID);
  462. //
  463. // Lock the cache for read.
  464. //
  465. RtlAcquireResourceShared(&SchannelCache.Lock, TRUE);
  466. //
  467. // Search through the cache entries at the computed index.
  468. //
  469. pList = SchannelCache.SessionCache[index].Flink;
  470. while(pList != &SchannelCache.SessionCache[index])
  471. {
  472. pItem = CONTAINING_RECORD(pList, SessCacheItem, IndexEntryList.Flink);
  473. pList = pList->Flink ;
  474. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  475. // Is this entry resumable?
  476. if(!pItem->ZombieJuju)
  477. {
  478. continue;
  479. }
  480. // Has this item expired?
  481. if(HasTimeElapsed(pItem->CreationTime, timeNow, pItem->Lifespan))
  482. {
  483. continue;
  484. }
  485. // Does the session id match?
  486. if(cbSessionID != pItem->cbSessionID)
  487. {
  488. continue;
  489. }
  490. if(memcmp(pbSessionID, pItem->SessionID, cbSessionID) != 0)
  491. {
  492. continue;
  493. }
  494. // Is this item for the protocol we're using.
  495. if(0 == (pContext->dwProtocol & pItem->fProtocol))
  496. {
  497. continue;
  498. }
  499. // Does this item belong to our client process?
  500. if(pItem->ProcessID != ProcessID)
  501. {
  502. continue;
  503. }
  504. // Does this item match the current server credentials?
  505. //
  506. // We don't allow different server credentials to share cache
  507. // entries, because if the credential that was used during
  508. // the original full handshake is deleted, then the cache
  509. // entry is unusable. Some server applications (I won't name names)
  510. // create a new credential for each connection, and we have to
  511. // guard against this.
  512. //
  513. // Note that this restriction may result in an extra full
  514. // handshake when IE accesses an IIS site enabled for certificate
  515. // mapping, mostly because IE's behavior is broken.
  516. if(!IsSameThumbprint(&pContext->pCredGroup->CredThumbprint,
  517. &pItem->CredThumbprint))
  518. {
  519. continue;
  520. }
  521. // Make sure that the application supports the cipher suite
  522. // used by this cache entry. This becomes important now that
  523. // we're allowing different server credentials to share
  524. // cache entries.
  525. if(!DoesAppAllowCipher(pContext->pCredGroup, pItem))
  526. {
  527. continue;
  528. }
  529. //
  530. // Found item in cache!!
  531. //
  532. fFound = TRUE;
  533. SPCacheReference(pItem);
  534. // Are we replacing something?
  535. // Then dereference the thing we are replacing.
  536. if(*ppRetItem)
  537. {
  538. SPCacheDereference(*ppRetItem);
  539. }
  540. // Return item referenced.
  541. *ppRetItem = pItem;
  542. break;
  543. }
  544. RtlReleaseResource(&SchannelCache.Lock);
  545. if(fFound)
  546. {
  547. DebugLog((DEB_TRACE, " FOUND IT(%u)\n", index));
  548. InterlockedIncrement(&g_cServerReconnects);
  549. }
  550. else
  551. {
  552. DebugLog((DEB_TRACE, " FAILED\n"));
  553. }
  554. return fFound;
  555. }
  556. DWORD
  557. ComputeClientCacheIndex(
  558. LPWSTR pszTargetName)
  559. {
  560. DWORD index;
  561. MD5_CTX Md5Hash;
  562. DWORD cbTargetName;
  563. if(pszTargetName == NULL)
  564. {
  565. index = 0;
  566. }
  567. else
  568. {
  569. cbTargetName = wcslen(pszTargetName) * sizeof(WCHAR);
  570. MD5Init(&Md5Hash);
  571. MD5Update(&Md5Hash,
  572. (PBYTE)pszTargetName,
  573. cbTargetName);
  574. MD5Final(&Md5Hash);
  575. CopyMemory((PBYTE)&index,
  576. Md5Hash.digest,
  577. sizeof(DWORD));
  578. index %= SchannelCache.dwCacheSize;
  579. }
  580. return index;
  581. }
  582. BOOL
  583. SPCacheRetrieveByName(
  584. LPWSTR pszTargetName,
  585. PSPCredentialGroup pCredGroup,
  586. PSessCacheItem *ppRetItem)
  587. {
  588. DWORD index;
  589. PSessCacheItem pItem;
  590. PSessCacheItem pFoundEntry = NULL;
  591. DWORD timeNow;
  592. LUID LogonId;
  593. PLIST_ENTRY pList;
  594. PSPCredential pCurrentCred = NULL;
  595. DebugLog((DEB_TRACE, "SPCacheRetrieveByName (%ls) called\n", pszTargetName));
  596. if(ppRetItem == NULL)
  597. {
  598. return FALSE;
  599. }
  600. //
  601. // Retrieve the current time and user logon id.
  602. //
  603. timeNow = GetTickCount();
  604. SslGetClientLogonId(&LogonId);
  605. //
  606. // Compute the cache index.
  607. //
  608. index = ComputeClientCacheIndex(pszTargetName);
  609. //
  610. // Lock the cache for read.
  611. //
  612. RtlAcquireResourceShared(&SchannelCache.Lock, TRUE);
  613. //
  614. // Search through the cache entries at the computed index.
  615. //
  616. pList = SchannelCache.SessionCache[index].Flink;
  617. while(pList != &SchannelCache.SessionCache[index])
  618. {
  619. pItem = CONTAINING_RECORD(pList, SessCacheItem, IndexEntryList.Flink);
  620. pList = pList->Flink ;
  621. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  622. // Is this entry resumable?
  623. if(!pItem->ZombieJuju)
  624. {
  625. continue;
  626. }
  627. // Is this item for the protocol we're using?
  628. if(0 == (pCredGroup->grbitEnabledProtocols & pItem->fProtocol))
  629. {
  630. continue;
  631. }
  632. // Has this item expired?
  633. if(HasTimeElapsed(pItem->CreationTime, timeNow, pItem->Lifespan))
  634. {
  635. continue;
  636. }
  637. // Don't allow reconnects when Skipjack is used.
  638. if(pItem->aiCipher == CALG_SKIPJACK)
  639. {
  640. continue;
  641. }
  642. // Does this item belong to our client?
  643. if(!RtlEqualLuid(&pItem->LogonId, &LogonId))
  644. {
  645. continue;
  646. }
  647. // Does this item match our current credentials?
  648. if(g_fMultipleProcessClientCache)
  649. {
  650. // If this cache entry has a client certificate associated with it
  651. // and the passed in client credentials contain one or more certificates,
  652. // then we need to make sure that they overlap.
  653. if(IsValidThumbprint(&pItem->CertThumbprint) && pCredGroup->pCredList != NULL)
  654. {
  655. if(!DoesCredThumbprintMatch(pCredGroup, &pItem->CertThumbprint))
  656. {
  657. continue;
  658. }
  659. }
  660. }
  661. else
  662. {
  663. // Make sure the thumbprint of the credential group matches the
  664. // thumbprint of the cache entry.
  665. if(!IsSameThumbprint(&pCredGroup->CredThumbprint,
  666. &pItem->CredThumbprint))
  667. {
  668. continue;
  669. }
  670. }
  671. if(!IsSameTargetName(pItem->szCacheID, pszTargetName))
  672. {
  673. continue;
  674. }
  675. // Make sure that the application supports the cipher suite
  676. // used by this cache entry. This becomes important in the
  677. // multi-process client cache scenario, since different client
  678. // applications may be running with different settings.
  679. if(!DoesAppAllowCipher(pCredGroup, pItem))
  680. {
  681. continue;
  682. }
  683. //
  684. // Found item in cache!!
  685. //
  686. if(pFoundEntry == NULL)
  687. {
  688. // This is the first matching entry found.
  689. SPCacheReference(pItem);
  690. // Remember the current entry.
  691. pFoundEntry = pItem;
  692. }
  693. else
  694. {
  695. if(pItem->CreationTime > pFoundEntry->CreationTime)
  696. {
  697. // We found a newer entry.
  698. SPCacheReference(pItem);
  699. // Disable searching on the previous item.
  700. pFoundEntry->ZombieJuju = FALSE;
  701. // Release the previous item.
  702. SPCacheDereference(pFoundEntry);
  703. // Remember the current entry.
  704. pFoundEntry = pItem;
  705. }
  706. else
  707. {
  708. // This item is older than the previously found entry.
  709. // Disable searching on the current entry.
  710. pItem->ZombieJuju = FALSE;
  711. }
  712. }
  713. }
  714. RtlReleaseResource(&SchannelCache.Lock);
  715. if(pFoundEntry)
  716. {
  717. // Found item in cache!!
  718. // Are we replacing something?
  719. // Then dereference the thing we are replacing.
  720. if(*ppRetItem)
  721. {
  722. SPCacheDereference(*ppRetItem);
  723. }
  724. // Return item referenced.
  725. *ppRetItem = pFoundEntry;
  726. DebugLog((DEB_TRACE, " FOUND IT(%u)\n", index));
  727. InterlockedIncrement(&g_cClientReconnects);
  728. }
  729. else
  730. {
  731. DebugLog((DEB_TRACE, " FAILED\n"));
  732. }
  733. return (pFoundEntry != NULL);
  734. }
  735. BOOL
  736. IsApplicationCertificateMapper(
  737. PHMAPPER phMapper)
  738. {
  739. if(phMapper == NULL)
  740. {
  741. return FALSE;
  742. }
  743. if(phMapper->m_dwFlags & SCH_FLAG_SYSTEM_MAPPER)
  744. {
  745. return FALSE;
  746. }
  747. return TRUE;
  748. }
  749. //+---------------------------------------------------------------------------
  750. //
  751. // Function: CacheExpireElements
  752. //
  753. // Synopsis: Traverse the session cache and remove all expired entries.
  754. // If the cache is oversized, then expire some entries
  755. // early.
  756. //
  757. // Arguments: [fCleanupOnly] -- If this is set, then attempt to delete
  758. // cache entries previously expired. Don't
  759. // traverse the cache.
  760. //
  761. // History: 01-02-2000 jbanes Created.
  762. //
  763. // Notes: This routine should be called only once every five or ten
  764. // minutes.
  765. //
  766. // The tricky bit is how to handle the case where the cache
  767. // entry belongs to IIS, and has an IIS certificate mapper
  768. // "locator" attached to it. In this case, we cannot destroy
  769. // the cache element unless the client process is IIS, because
  770. // we need to callback to IIS in order to destroy the locator.
  771. // In this case, we remove the element from the cache, and
  772. // leave it laying around in a global "cache cleanup" list.
  773. // If this list gets too large, then this routine should be
  774. // called frequently, with the "fCleanupOnly" parameter set
  775. // to TRUE.
  776. //
  777. //----------------------------------------------------------------------------
  778. BOOL
  779. CacheExpireElements(
  780. BOOL fCleanupOnly,
  781. BOOL fBackground)
  782. {
  783. static ULONG RefCount = 0;
  784. ULONG LocalRefCount;
  785. DWORD timeNow;
  786. ULONG ProcessID;
  787. PSessCacheItem pItem;
  788. PLIST_ENTRY pList;
  789. DWORD CleanupCount;
  790. ULONG Count;
  791. //
  792. // If another thread is currently expiring elements, then try again
  793. // later.
  794. //
  795. LocalRefCount = InterlockedIncrement(&RefCount);
  796. if(fBackground && LocalRefCount > 1)
  797. {
  798. InterlockedDecrement(&RefCount);
  799. return FALSE;
  800. }
  801. RtlEnterCriticalSection(&g_CacheCleanupLock);
  802. //
  803. // Retrieve the current time and application process id.
  804. //
  805. timeNow = GetTickCount();
  806. SslGetClientProcess(&ProcessID);
  807. //
  808. // Search through the cache entries looking for expired entries.
  809. //
  810. if(!fCleanupOnly)
  811. {
  812. RtlAcquireResourceExclusive(&SchannelCache.Lock, TRUE);
  813. pList = SchannelCache.EntryList.Flink;
  814. while(pList != &SchannelCache.EntryList)
  815. {
  816. pItem = CONTAINING_RECORD(pList, SessCacheItem, EntryList.Flink);
  817. pList = pList->Flink;
  818. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  819. // Is the cache entry currently being used?
  820. if(pItem->cRef > 1)
  821. {
  822. continue;
  823. }
  824. // Mark all expired cache entries as non-resumable.
  825. if(HasTimeElapsed(pItem->CreationTime, timeNow, pItem->Lifespan))
  826. {
  827. pItem->ZombieJuju = FALSE;
  828. pItem->DeferredJuju = FALSE;
  829. }
  830. // If the cache has gotten too large, then expire elements early. The
  831. // cache elements are sorted by creation time, so the oldest
  832. // entries will be expired first.
  833. if(SchannelCache.dwUsedEntries > SchannelCache.dwMaximumEntries)
  834. {
  835. pItem->ZombieJuju = FALSE;
  836. pItem->DeferredJuju = FALSE;
  837. }
  838. // Don't remove entries that are still valid.
  839. if(pItem->ZombieJuju == TRUE || pItem->DeferredJuju)
  840. {
  841. continue;
  842. }
  843. //
  844. // Remove this entry from the cache, and add it to the list of
  845. // entries to be destroyed.
  846. //
  847. RemoveEntryList(&pItem->IndexEntryList);
  848. RemoveEntryList(&pItem->EntryList);
  849. SchannelCache.dwUsedEntries--;
  850. InsertTailList(&g_CacheCleanupList, &pItem->EntryList);
  851. }
  852. RtlReleaseResource(&SchannelCache.Lock);
  853. }
  854. //
  855. // Kill the expired zombies.
  856. //
  857. CleanupCount = 0;
  858. pList = g_CacheCleanupList.Flink;
  859. while(pList != &g_CacheCleanupList)
  860. {
  861. pItem = CONTAINING_RECORD(pList, SessCacheItem, EntryList.Flink);
  862. pList = pList->Flink;
  863. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  864. // Make sure that we only destroy server entries that belong to the
  865. // current application process. This is necessary because of the
  866. // IIS certificate mapper.
  867. if(pItem->ProcessID != 0 &&
  868. pItem->ProcessID != ProcessID)
  869. {
  870. if(IsApplicationCertificateMapper(pItem->phMapper))
  871. {
  872. CleanupCount++;
  873. continue;
  874. }
  875. }
  876. // Remove entry from cleanup list.
  877. RemoveEntryList(&pItem->EntryList);
  878. // Destroy cache entry.
  879. SPCacheDelete(pItem);
  880. }
  881. g_CacheCleanupCount = CleanupCount;
  882. RtlLeaveCriticalSection(&g_CacheCleanupLock);
  883. InterlockedDecrement(&RefCount);
  884. return TRUE;
  885. }
  886. VOID
  887. CacheCleanupHandler(
  888. PVOID pVoid,
  889. BOOLEAN fTimeout)
  890. {
  891. if(SchannelCache.dwUsedEntries > 0)
  892. {
  893. if(fTimeout)
  894. {
  895. DebugLog((DEB_WARN, "Initiate periodic cache cleanup.\n"));
  896. }
  897. CacheExpireElements(FALSE, TRUE);
  898. ResetEvent(g_CacheCleanupEvent);
  899. }
  900. }
  901. /* allocate a new cache item to be used
  902. * by a context. Initialize it with the
  903. * pszTarget if the target exists.
  904. * Auto-Generate a SessionID
  905. */
  906. BOOL
  907. SPCacheRetrieveNew(
  908. BOOL fServer,
  909. LPWSTR pszTargetName,
  910. PSessCacheItem * ppRetItem)
  911. {
  912. DWORD index;
  913. DWORD timeNow;
  914. ULONG ProcessID;
  915. LUID LogonId;
  916. PSessCacheItem pItem;
  917. BYTE rgbSessionId[SP_MAX_SESSION_ID];
  918. DebugLog((DEB_TRACE, "SPCacheRetrieveNew called\n"));
  919. //
  920. // Trigger cache cleanup if too many cache entries already exist.
  921. //
  922. if(SchannelCache.dwUsedEntries > (SchannelCache.dwMaximumEntries * 21) / 20)
  923. {
  924. DebugLog((DEB_WARN, "Cache size (%d) exceeded threshold (%d), trigger cache cleanup.\n",
  925. SchannelCache.dwUsedEntries,
  926. SchannelCache.dwMaximumEntries));
  927. SetEvent(g_CacheCleanupEvent);
  928. }
  929. //
  930. // Perform cache garbage collection when the list of entries to be
  931. // deleted grows too large.
  932. //
  933. if(fServer && g_CacheCleanupCount > 50)
  934. {
  935. DebugLog((DEB_WARN, "Attempt background cleanup of deleted zombies.\n"));
  936. CacheExpireElements(TRUE, TRUE);
  937. }
  938. //
  939. // Retrieve the current time and user logon id.
  940. //
  941. timeNow = GetTickCount();
  942. SslGetClientProcess(&ProcessID);
  943. SslGetClientLogonId(&LogonId);
  944. //
  945. // Compute the session id and the cache index.
  946. //
  947. if(fServer)
  948. {
  949. GenerateRandomBits(rgbSessionId, sizeof(rgbSessionId));
  950. index = *(DWORD *)rgbSessionId % SchannelCache.dwCacheSize;
  951. *(DWORD *)rgbSessionId = index;
  952. }
  953. else
  954. {
  955. ZeroMemory(rgbSessionId, sizeof(rgbSessionId));
  956. index = ComputeClientCacheIndex(pszTargetName);
  957. }
  958. //
  959. // Allocate a new cache entry.
  960. //
  961. pItem = SPExternalAlloc(sizeof(SessCacheItem));
  962. if(pItem == NULL)
  963. {
  964. SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  965. return FALSE;
  966. }
  967. //
  968. // Fill in the cache internal fields.
  969. //
  970. pItem->Magic = SP_CACHE_MAGIC;
  971. pItem->cRef = 1;
  972. pItem->CreationTime = timeNow;
  973. if(fServer)
  974. {
  975. pItem->Lifespan = SchannelCache.dwServerLifespan;
  976. }
  977. else
  978. {
  979. pItem->Lifespan = SchannelCache.dwClientLifespan;
  980. }
  981. pItem->ProcessID = ProcessID;
  982. pItem->LogonId = LogonId;
  983. #ifdef LOCK_MASTER_KEYS
  984. pItem->csMasterKey = g_rgcsMasterKey + (index % SP_MASTER_KEY_CS_COUNT);
  985. #endif
  986. if(pszTargetName)
  987. {
  988. pItem->szCacheID = SPExternalAlloc((wcslen(pszTargetName) + 1) * sizeof(WCHAR));
  989. if(pItem->szCacheID == NULL)
  990. {
  991. SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  992. SPExternalFree(pItem);
  993. return FALSE;
  994. }
  995. wcscpy(pItem->szCacheID, pszTargetName);
  996. }
  997. else
  998. {
  999. pItem->szCacheID = NULL;
  1000. }
  1001. memcpy(pItem->SessionID, rgbSessionId, sizeof(rgbSessionId));
  1002. //
  1003. // Give the caller a reference.
  1004. //
  1005. SPCacheReference(pItem);
  1006. *ppRetItem = pItem;
  1007. //
  1008. // Add the new entry to the cache.
  1009. //
  1010. RtlAcquireResourceExclusive(&SchannelCache.Lock, TRUE);
  1011. InsertTailList(&SchannelCache.SessionCache[index], &pItem->IndexEntryList);
  1012. InsertTailList(&SchannelCache.EntryList, &pItem->EntryList);
  1013. SchannelCache.dwUsedEntries++;
  1014. RtlReleaseResource(&SchannelCache.Lock);
  1015. return TRUE;
  1016. }
  1017. BOOL
  1018. SPCacheAdd(
  1019. PSPContext pContext)
  1020. {
  1021. PSessCacheItem pItem;
  1022. PSPCredentialGroup pCred;
  1023. DWORD dwLifespan;
  1024. DWORD timeNow;
  1025. timeNow = GetTickCount();
  1026. pItem = pContext->RipeZombie;
  1027. if(!pItem) return FALSE;
  1028. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  1029. pCred = pContext->pCredGroup;
  1030. if(!pCred) return FALSE;
  1031. if(pItem->fProtocol & SP_PROT_CLIENTS)
  1032. {
  1033. dwLifespan = min(pCred->dwSessionLifespan, SchannelCache.dwClientLifespan);
  1034. }
  1035. else
  1036. {
  1037. dwLifespan = min(pCred->dwSessionLifespan, SchannelCache.dwServerLifespan);
  1038. }
  1039. // Remember which client certificate we used.
  1040. if(pItem->fProtocol & SP_PROT_CLIENTS)
  1041. {
  1042. pItem->CredThumbprint = pContext->pCredGroup->CredThumbprint;
  1043. if(pContext->pActiveClientCred)
  1044. {
  1045. pItem->CertThumbprint = pContext->pActiveClientCred->CertThumbprint;
  1046. pItem->pClientCert = CertDuplicateCertificateContext(pContext->pActiveClientCred->pCert);
  1047. if(pItem->pClientCert == NULL)
  1048. {
  1049. SP_LOG_RESULT(GetLastError());
  1050. }
  1051. }
  1052. }
  1053. // Are we supposed to defer reconnects for this connection?
  1054. if(pItem->pServerCred != NULL)
  1055. {
  1056. if(pItem->pServerCred->dwFlags & CRED_FLAG_DISABLE_RECONNECTS)
  1057. {
  1058. pItem->DeferredJuju = TRUE;
  1059. }
  1060. }
  1061. // Allow cache ownership of this item
  1062. pItem->dwFlags |= SP_CACHE_FLAG_READONLY;
  1063. if(!pItem->DeferredJuju)
  1064. {
  1065. pItem->ZombieJuju = TRUE;
  1066. }
  1067. // if we are a cloned item, abort the old
  1068. // item, and then dereference it.
  1069. if(pItem->pClonedItem)
  1070. {
  1071. pItem->pClonedItem->ZombieJuju = FALSE;
  1072. SPCacheDereference(pItem->pClonedItem);
  1073. pItem->pClonedItem = NULL;
  1074. }
  1075. pItem->Lifespan = dwLifespan;
  1076. return TRUE;
  1077. }
  1078. /* Allocate a new cache item, and copy
  1079. * over relevant information from old item,
  1080. * and dereference old item. This is a helper
  1081. * for REDO
  1082. */
  1083. BOOL
  1084. SPCacheClone(PSessCacheItem *ppItem)
  1085. {
  1086. PSessCacheItem pNewItem;
  1087. PSessCacheItem pOldItem;
  1088. if(ppItem == NULL || *ppItem == NULL)
  1089. {
  1090. return FALSE;
  1091. }
  1092. pOldItem = *ppItem;
  1093. ASSERT(pOldItem->Magic == SP_CACHE_MAGIC);
  1094. ASSERT(!(pOldItem->fProtocol & SP_PROT_CLIENTS) || !(pOldItem->fProtocol & SP_PROT_SERVERS));
  1095. // Get a fresh cache item.
  1096. pNewItem = NULL;
  1097. if(!SPCacheRetrieveNew((pOldItem->fProtocol & SP_PROT_CLIENTS) == 0,
  1098. pOldItem->szCacheID,
  1099. &pNewItem))
  1100. {
  1101. return FALSE;
  1102. }
  1103. // Copy the master CSP prov handle.
  1104. pNewItem->hMasterProv = pOldItem->hMasterProv;
  1105. // Copy over old relevant data
  1106. pNewItem->fProtocol = pOldItem->fProtocol;
  1107. pNewItem->dwCF = pOldItem->dwCF;
  1108. pNewItem->phMapper = pOldItem->phMapper;
  1109. pNewItem->pServerCred = pOldItem->pServerCred;
  1110. pNewItem->pActiveServerCred = pOldItem->pActiveServerCred;
  1111. if(pOldItem->dwFlags & SP_CACHE_FLAG_MASTER_EPHEM)
  1112. {
  1113. pNewItem->dwFlags |= SP_CACHE_FLAG_MASTER_EPHEM;
  1114. }
  1115. pNewItem->CredThumbprint = pOldItem->CredThumbprint,
  1116. // This item will be dereferenced, and
  1117. // Aborted when the new item is completed.
  1118. pNewItem->pClonedItem = pOldItem;
  1119. *ppItem = pNewItem;
  1120. return TRUE;
  1121. }
  1122. NTSTATUS
  1123. SetCacheAppData(
  1124. PSessCacheItem pItem,
  1125. PBYTE pbAppData,
  1126. DWORD cbAppData)
  1127. {
  1128. RtlAcquireResourceExclusive(&SchannelCache.Lock, TRUE);
  1129. if(pItem->pbAppData)
  1130. {
  1131. SPExternalFree(pItem->pbAppData);
  1132. }
  1133. pItem->pbAppData = pbAppData;
  1134. pItem->cbAppData = cbAppData;
  1135. RtlReleaseResource(&SchannelCache.Lock);
  1136. return STATUS_SUCCESS;
  1137. }
  1138. NTSTATUS
  1139. GetCacheAppData(
  1140. PSessCacheItem pItem,
  1141. PBYTE *ppbAppData,
  1142. DWORD *pcbAppData)
  1143. {
  1144. if(pItem->pbAppData == NULL)
  1145. {
  1146. *ppbAppData = NULL;
  1147. *pcbAppData = 0;
  1148. return STATUS_SUCCESS;
  1149. }
  1150. RtlAcquireResourceShared(&SchannelCache.Lock, TRUE);
  1151. *pcbAppData = pItem->cbAppData;
  1152. *ppbAppData = SPExternalAlloc(pItem->cbAppData);
  1153. if(*ppbAppData == NULL)
  1154. {
  1155. RtlReleaseResource(&SchannelCache.Lock);
  1156. return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  1157. }
  1158. memcpy(*ppbAppData, pItem->pbAppData, pItem->cbAppData);
  1159. RtlReleaseResource(&SchannelCache.Lock);
  1160. return STATUS_SUCCESS;
  1161. }
  1162. BOOL
  1163. IsEntryToBeProcessed(
  1164. PSessCacheItem pItem,
  1165. PLUID LogonID,
  1166. ULONG ProcessID,
  1167. LPWSTR pszTargetName,
  1168. DWORD dwFlags)
  1169. {
  1170. //
  1171. // Validate client entries.
  1172. //
  1173. if(pItem->fProtocol & SP_PROT_CLIENTS)
  1174. {
  1175. if((dwFlags & SSL_PURGE_CLIENT_ENTRIES) == 0 &&
  1176. (dwFlags & SSL_PURGE_CLIENT_ALL_ENTRIES) == 0)
  1177. {
  1178. return FALSE;
  1179. }
  1180. if((dwFlags & SSL_PURGE_CLIENT_ALL_ENTRIES) == 0)
  1181. {
  1182. if(!RtlEqualLuid(&pItem->LogonId, LogonID))
  1183. {
  1184. return FALSE;
  1185. }
  1186. }
  1187. if(pszTargetName != NULL)
  1188. {
  1189. if(pItem->szCacheID == NULL ||
  1190. wcscmp(pItem->szCacheID, pszTargetName) != 0)
  1191. {
  1192. return FALSE;
  1193. }
  1194. }
  1195. return TRUE;
  1196. }
  1197. //
  1198. // Validate server entries.
  1199. //
  1200. if(pItem->fProtocol & SP_PROT_SERVERS)
  1201. {
  1202. if((dwFlags & SSL_PURGE_SERVER_ENTRIES) == 0 &&
  1203. (dwFlags & SSL_PURGE_SERVER_ALL_ENTRIES) == 0)
  1204. {
  1205. return FALSE;
  1206. }
  1207. if(ProcessID != pItem->ProcessID)
  1208. {
  1209. if((dwFlags & SSL_PURGE_SERVER_ALL_ENTRIES) == 0)
  1210. {
  1211. return FALSE;
  1212. }
  1213. }
  1214. }
  1215. return TRUE;
  1216. }
  1217. NTSTATUS
  1218. SPCachePurgeEntries(
  1219. LUID *LogonID,
  1220. ULONG ProcessID,
  1221. LPWSTR pszTargetName,
  1222. DWORD dwFlags)
  1223. {
  1224. PSessCacheItem pItem;
  1225. PLIST_ENTRY pList;
  1226. LIST_ENTRY DeleteList;
  1227. DebugLog((DEB_TRACE, "Purge cache entries\n"));
  1228. //
  1229. // Initialize the list of deleted entries.
  1230. //
  1231. InitializeListHead(&DeleteList);
  1232. //
  1233. // Enumerate through the cache entries.
  1234. //
  1235. RtlAcquireResourceExclusive(&SchannelCache.Lock, TRUE);
  1236. pList = SchannelCache.EntryList.Flink;
  1237. while(pList != &SchannelCache.EntryList)
  1238. {
  1239. pItem = CONTAINING_RECORD(pList, SessCacheItem, EntryList.Flink);
  1240. pList = pList->Flink;
  1241. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  1242. if(!IsEntryToBeProcessed(pItem,
  1243. LogonID,
  1244. ProcessID,
  1245. pszTargetName,
  1246. dwFlags))
  1247. {
  1248. continue;
  1249. }
  1250. if(pItem->cRef > 1)
  1251. {
  1252. // This entry is currently being used, so don't delete.
  1253. // Mark it as non-resumable, though.
  1254. pItem->ZombieJuju = FALSE;
  1255. pItem->DeferredJuju = FALSE;
  1256. continue;
  1257. }
  1258. if(pItem->ProcessID != 0 &&
  1259. pItem->ProcessID != ProcessID)
  1260. {
  1261. if(IsApplicationCertificateMapper(pItem->phMapper))
  1262. {
  1263. // This entry has a mapper structure that doesn't belong
  1264. // to the calling process, so don't delete. Mark it as
  1265. // non-resumable, though.
  1266. pItem->ZombieJuju = FALSE;
  1267. pItem->DeferredJuju = FALSE;
  1268. continue;
  1269. }
  1270. }
  1271. //
  1272. // Remove this entry from the cache, and add it to the list of
  1273. // entries to be destroyed.
  1274. //
  1275. RemoveEntryList(&pItem->IndexEntryList);
  1276. RemoveEntryList(&pItem->EntryList);
  1277. SchannelCache.dwUsedEntries--;
  1278. InsertTailList(&DeleteList, &pItem->EntryList);
  1279. }
  1280. RtlReleaseResource(&SchannelCache.Lock);
  1281. //
  1282. // Kill the purged zombies.
  1283. //
  1284. pList = DeleteList.Flink;
  1285. while(pList != &DeleteList)
  1286. {
  1287. pItem = CONTAINING_RECORD(pList, SessCacheItem, EntryList.Flink);
  1288. pList = pList->Flink;
  1289. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  1290. SPCacheDelete(pItem);
  1291. }
  1292. return STATUS_SUCCESS;
  1293. }
  1294. NTSTATUS
  1295. SPCacheGetInfo(
  1296. LUID * LogonID,
  1297. LPWSTR pszTargetName,
  1298. DWORD dwFlags,
  1299. PSSL_SESSION_CACHE_INFO_RESPONSE pCacheInfo)
  1300. {
  1301. PSessCacheItem pItem;
  1302. PLIST_ENTRY pList;
  1303. DWORD timeNow;
  1304. ULONG ProcessID;
  1305. pCacheInfo->CacheSize = SchannelCache.dwMaximumEntries;
  1306. pCacheInfo->Entries = 0;
  1307. pCacheInfo->ActiveEntries = 0;
  1308. pCacheInfo->Zombies = 0;
  1309. pCacheInfo->ExpiredZombies = 0;
  1310. pCacheInfo->AbortedZombies = 0;
  1311. pCacheInfo->DeletedZombies = g_CacheCleanupCount;
  1312. timeNow = GetTickCount();
  1313. SslGetClientProcess(&ProcessID);
  1314. RtlAcquireResourceExclusive(&SchannelCache.Lock, TRUE);
  1315. pList = SchannelCache.EntryList.Flink;
  1316. while(pList != &SchannelCache.EntryList)
  1317. {
  1318. pItem = CONTAINING_RECORD(pList, SessCacheItem, EntryList.Flink);
  1319. pList = pList->Flink;
  1320. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  1321. if(pItem->fProtocol & SP_PROT_CLIENTS)
  1322. {
  1323. if((dwFlags & SSL_RETRIEVE_CLIENT_ENTRIES) == 0)
  1324. {
  1325. continue;
  1326. }
  1327. }
  1328. else
  1329. {
  1330. if((dwFlags & SSL_RETRIEVE_SERVER_ENTRIES) == 0)
  1331. {
  1332. continue;
  1333. }
  1334. }
  1335. pCacheInfo->Entries++;
  1336. if(pItem->cRef == 1)
  1337. {
  1338. pCacheInfo->Zombies++;
  1339. if(HasTimeElapsed(pItem->CreationTime,
  1340. timeNow,
  1341. pItem->Lifespan))
  1342. {
  1343. pCacheInfo->ExpiredZombies++;
  1344. }
  1345. if(pItem->ZombieJuju == FALSE)
  1346. {
  1347. pCacheInfo->AbortedZombies++;
  1348. }
  1349. }
  1350. else
  1351. {
  1352. pCacheInfo->ActiveEntries++;
  1353. }
  1354. }
  1355. RtlReleaseResource(&SchannelCache.Lock);
  1356. return STATUS_SUCCESS;
  1357. }
  1358. NTSTATUS
  1359. SPCacheGetPerfmonInfo(
  1360. DWORD dwFlags,
  1361. PSSL_PERFMON_INFO_RESPONSE pPerfmonInfo)
  1362. {
  1363. PSessCacheItem pItem;
  1364. PLIST_ENTRY pList;
  1365. //
  1366. // Compute performance numbers.
  1367. //
  1368. pPerfmonInfo->ClientHandshakesPerSecond = g_cClientHandshakes;
  1369. pPerfmonInfo->ServerHandshakesPerSecond = g_cServerHandshakes;
  1370. pPerfmonInfo->ClientReconnectsPerSecond = g_cClientReconnects;
  1371. pPerfmonInfo->ServerReconnectsPerSecond = g_cServerReconnects;
  1372. //
  1373. // Compute cache info.
  1374. //
  1375. pPerfmonInfo->ClientCacheEntries = 0;
  1376. pPerfmonInfo->ServerCacheEntries = 0;
  1377. pPerfmonInfo->ClientActiveEntries = 0;
  1378. pPerfmonInfo->ServerActiveEntries = 0;
  1379. RtlAcquireResourceShared(&SchannelCache.Lock, TRUE);
  1380. pList = SchannelCache.EntryList.Flink;
  1381. while(pList != &SchannelCache.EntryList)
  1382. {
  1383. pItem = CONTAINING_RECORD(pList, SessCacheItem, EntryList.Flink);
  1384. pList = pList->Flink;
  1385. ASSERT(pItem->Magic == SP_CACHE_MAGIC);
  1386. if(pItem->fProtocol & SP_PROT_CLIENTS)
  1387. {
  1388. pPerfmonInfo->ClientCacheEntries++;
  1389. if(pItem->cRef > 1)
  1390. {
  1391. pPerfmonInfo->ClientActiveEntries++;
  1392. }
  1393. }
  1394. else
  1395. {
  1396. pPerfmonInfo->ServerCacheEntries++;
  1397. if(pItem->cRef > 1)
  1398. {
  1399. pPerfmonInfo->ServerActiveEntries++;
  1400. }
  1401. }
  1402. }
  1403. RtlReleaseResource(&SchannelCache.Lock);
  1404. return STATUS_SUCCESS;
  1405. }