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.

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