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.

711 lines
15 KiB

  1. #include "ldapc.hxx"
  2. #pragma hdrstop
  3. ADS_LDP BindCache ;
  4. DWORD BindCacheCount = 0 ;
  5. CRITICAL_SECTION BindCacheCritSect ;
  6. LUID ReservedLuid = { 0, 0 } ;
  7. //
  8. // Wait for 1000ms * 60 * minutes
  9. //
  10. #define RETIRE_HANDLE (1000 * 60 * 5)
  11. //
  12. // Return the LUID for current credentials. Note we only use LUID. In theory
  13. // it may be better to use SourceName as well as identifier but the former
  14. // doesnt seem to always return the same string.
  15. //
  16. DWORD BindCacheGetLuid(LUID *Luid, LUID *ModifiedId)
  17. {
  18. HANDLE TokenHandle = NULL ;
  19. TOKEN_STATISTICS TokenInformation ;
  20. DWORD ReturnLength ;
  21. //
  22. // Try thread first. If fail, try process.
  23. //
  24. if (!OpenThreadToken(
  25. GetCurrentThread(),
  26. TOKEN_QUERY,
  27. TRUE,
  28. &TokenHandle)) {
  29. if (!OpenProcessToken(
  30. GetCurrentProcess(),
  31. TOKEN_QUERY,
  32. &TokenHandle)) {
  33. return(GetLastError()) ;
  34. }
  35. }
  36. //
  37. // Get the TokenSource info.
  38. //
  39. if (!GetTokenInformation(
  40. TokenHandle,
  41. TokenStatistics,
  42. &TokenInformation,
  43. sizeof(TokenInformation),
  44. &ReturnLength)) {
  45. CloseHandle(TokenHandle) ;
  46. return(GetLastError()) ;
  47. }
  48. *Luid = TokenInformation.AuthenticationId ;
  49. *ModifiedId = TokenInformation.ModifiedId;
  50. CloseHandle(TokenHandle) ;
  51. return(NO_ERROR) ;
  52. }
  53. //
  54. // Initializes a cache entry
  55. //
  56. DWORD BindCacheAllocEntry(ADS_LDP **ppCacheEntry)
  57. {
  58. ADS_LDP *pCacheEntry ;
  59. *ppCacheEntry = NULL ;
  60. if (!(pCacheEntry = (PADS_LDP)AllocADsMem(sizeof(ADS_LDP)))) {
  61. return(GetLastError()) ;
  62. }
  63. pCacheEntry->RefCount = 0;
  64. pCacheEntry->Flags = 0;
  65. pCacheEntry->List.Flink = NULL ;
  66. pCacheEntry->List.Blink = NULL ;
  67. pCacheEntry->ReferralEntries = NULL ;
  68. pCacheEntry->nReferralEntries = 0 ;
  69. pCacheEntry->fKeepAround = FALSE;
  70. *ppCacheEntry = pCacheEntry ;
  71. return NO_ERROR ;
  72. }
  73. //
  74. // Invalidates a cache entry so it will not be used.
  75. //
  76. VOID BindCacheInvalidateEntry(ADS_LDP *pCacheEntry)
  77. {
  78. pCacheEntry->Flags |= LDP_CACHE_INVALID ;
  79. }
  80. //
  81. // !!!WARNING!!! Make sure you hold the bindcache critsect
  82. // when calling this function.
  83. //
  84. VOID CommonRemoveEntry(ADS_LDP *pCacheEntry, LIST_ENTRY *DeleteReferralList)
  85. {
  86. for (DWORD i=0; i < pCacheEntry->nReferralEntries; i++) {
  87. if (BindCacheDerefHelper( pCacheEntry->ReferralEntries[i], DeleteReferralList) == 0 ) {
  88. InsertTailList( DeleteReferralList, &(pCacheEntry->ReferralEntries[i])->ReferralList );
  89. }
  90. }
  91. //
  92. // Cleanup the entry
  93. //
  94. //
  95. --BindCacheCount ;
  96. (void) FreeADsMem(pCacheEntry->Server) ;
  97. pCacheEntry->Server = NULL ;
  98. delete pCacheEntry->pCredentials;
  99. pCacheEntry->pCredentials = NULL;
  100. if (pCacheEntry->ReferralEntries) {
  101. FreeADsMem(pCacheEntry->ReferralEntries);
  102. }
  103. RemoveEntryList(&pCacheEntry->List) ;
  104. return;
  105. }
  106. #if 0
  107. VOID BindCacheCleanTimedOutEntries()
  108. {
  109. DWORD dwCurrTick = 0;
  110. DWORD dwLastTick = 0;
  111. BOOL fRemoved = FALSE;
  112. ADS_LDP *ldRemove = NULL;
  113. ENTER_BIND_CRITSECT();
  114. PADS_LDP pEntry = (PADS_LDP) BindCache.List.Flink ;
  115. //
  116. // Loop thru looking for match. A match is defined as:
  117. // servername & LUID matches, and it is NOT invalid.
  118. //
  119. while (pEntry != &BindCache) {
  120. //
  121. // See if this one is keepAround entry that is old
  122. //
  123. if (pEntry->RefCount == 0) {
  124. ADsAssert(pEntry->fKeepAround);
  125. //
  126. // GetCurrent tick and see if we need to del the entry
  127. //
  128. dwCurrTick = GetTickCount();
  129. dwLastTick = pEntry->dwLastUsed;
  130. if ((dwLastTick == 0)
  131. || ((dwLastTick <= dwCurrTick)
  132. && ((dwCurrTick - dwLastTick) > RETIRE_HANDLE))
  133. || ((dwLastTick > dwCurrTick)
  134. && ((dwCurrTick + (((DWORD)(-1)) - dwLastTick))
  135. > RETIRE_HANDLE)))
  136. {
  137. //
  138. // Entry needs to be removed.
  139. //
  140. CommonRemoveEntry(pEntry);
  141. fRemoved = TRUE;
  142. }
  143. } // refCount == 0
  144. if (fRemoved) {
  145. LdapUnbind(pEntry);
  146. ldRemove = pEntry;
  147. }
  148. pEntry = (PADS_LDP)pEntry->List.Flink ;
  149. if (ldRemove) {
  150. FreeADsMem(ldRemove);
  151. ldRemove = NULL;
  152. }
  153. }
  154. LEAVE_BIND_CRITSECT();
  155. return;
  156. }
  157. #endif
  158. //
  159. // Lookup an entry in the cache. Does not take into account timeouts.
  160. // Increments ref count if found.
  161. //
  162. PADS_LDP
  163. BindCacheLookup(
  164. LPWSTR Address,
  165. LUID Luid,
  166. LUID ModifiedId,
  167. CCredentials& Credentials,
  168. DWORD dwPort
  169. )
  170. {
  171. DWORD i ;
  172. ENTER_BIND_CRITSECT() ;
  173. PADS_LDP pEntry = (PADS_LDP) BindCache.List.Flink ;
  174. //
  175. // Loop thru looking for match. A match is defined as:
  176. // servername & LUID matches, and it is NOT invalid.
  177. //
  178. while (pEntry != &BindCache) {
  179. if ((pEntry->Server != NULL) &&
  180. !(pEntry->Flags & LDP_CACHE_INVALID) &&
  181. (wcscmp(pEntry->Server, Address) == 0) &&
  182. pEntry->PortNumber == dwPort &&
  183. (memcmp(&Luid,&(pEntry->Luid),sizeof(Luid))==0) &&
  184. ((memcmp(&ModifiedId, &(pEntry->ModifiedId), sizeof(Luid)) == 0) ||
  185. (memcmp(&ModifiedId, &ReservedLuid, sizeof(Luid)) == 0))) {
  186. if ((*(pEntry->pCredentials) == Credentials) ||
  187. CanCredentialsBeReused(pEntry->pCredentials, &Credentials))
  188. {
  189. ++pEntry->RefCount ;
  190. LEAVE_BIND_CRITSECT() ;
  191. return(pEntry) ;
  192. }
  193. }
  194. pEntry = (PADS_LDP)pEntry->List.Flink ;
  195. }
  196. LEAVE_BIND_CRITSECT() ;
  197. //
  198. // Before we leave clean out stale entries
  199. //
  200. //BindCacheCleanTimedOutEntries();
  201. return NULL ;
  202. }
  203. //
  204. // Check if credentials can be reused. They can be reused if username and
  205. // non-ignored auth flags match and incoming credentials thesame password.
  206. // Ignored auth flags are those defined by
  207. // BIND_CACHE_IGNORED_FLAGS as not to be used in considering whether to
  208. // reuse a connection.
  209. //
  210. BOOL
  211. CanCredentialsBeReused(
  212. CCredentials *pCachedCreds,
  213. CCredentials *pIncomingCreds
  214. )
  215. {
  216. PWSTR pszIncomingUser = NULL;
  217. PWSTR pszIncomingPwd = NULL;
  218. PWSTR pszCachedUser = NULL;
  219. PWSTR pszCachedPwd = NULL;
  220. HRESULT hr;
  221. BOOL rc = FALSE;
  222. //
  223. // Test that the non-ignored auth flags match.
  224. // We need to be smart and reuse the credentials based on the flags.
  225. //
  226. if ( (~BIND_CACHE_IGNORED_FLAGS & pCachedCreds->GetAuthFlags()) !=
  227. (~BIND_CACHE_IGNORED_FLAGS & pIncomingCreds->GetAuthFlags()))
  228. {
  229. return FALSE;
  230. }
  231. //
  232. // Get the user names
  233. //
  234. hr = pCachedCreds->GetUserName(&pszCachedUser);
  235. BAIL_ON_FAILURE(hr);
  236. hr = pIncomingCreds->GetUserName(&pszIncomingUser);
  237. BAIL_ON_FAILURE(hr);
  238. //
  239. // Get the password
  240. //
  241. hr = pIncomingCreds->GetPassword(&pszIncomingPwd);
  242. BAIL_ON_FAILURE(hr);
  243. hr = pCachedCreds->GetPassword(&pszCachedPwd);
  244. BAIL_ON_FAILURE(hr);
  245. //
  246. // Only when both username and password match, will we reuse the connection handle
  247. //
  248. if (((pszCachedUser && pszIncomingUser &&
  249. wcscmp(pszCachedUser, pszIncomingUser) == 0) ||
  250. (!pszCachedUser && !pszIncomingUser))
  251. &&
  252. ((pszCachedPwd && pszIncomingPwd &&
  253. wcscmp(pszCachedPwd, pszIncomingPwd) == 0) ||
  254. (!pszCachedPwd && !pszIncomingPwd)))
  255. {
  256. rc = TRUE;
  257. }
  258. error:
  259. if (pszCachedUser)
  260. {
  261. FreeADsStr(pszCachedUser);
  262. }
  263. if (pszIncomingUser)
  264. {
  265. FreeADsStr(pszIncomingUser);
  266. }
  267. if (pszCachedPwd)
  268. {
  269. FreeADsStr(pszCachedPwd);
  270. }
  271. if (pszIncomingPwd)
  272. {
  273. FreeADsStr(pszIncomingPwd);
  274. }
  275. return rc;
  276. }
  277. //
  278. // Lookup an entry in the cache based on the Ldap Handle
  279. // DOES NOT Increment ref count
  280. //
  281. PADS_LDP
  282. GetCacheEntry(PLDAP pLdap)
  283. {
  284. ENTER_BIND_CRITSECT() ;
  285. PADS_LDP pEntry = (PADS_LDP) BindCache.List.Flink ;
  286. //
  287. // Loop thru looking for match. A match is defined as:
  288. // servername & LUID matches, and it is NOT invalid.
  289. //
  290. while (pEntry != &BindCache) {
  291. if (pEntry->LdapHandle == pLdap) {
  292. LEAVE_BIND_CRITSECT() ;
  293. return(pEntry) ;
  294. }
  295. pEntry = (PADS_LDP)pEntry->List.Flink ;
  296. }
  297. LEAVE_BIND_CRITSECT() ;
  298. return NULL ;
  299. }
  300. //
  301. // Add entry to cache
  302. //
  303. DWORD
  304. BindCacheAdd(
  305. LPWSTR Address,
  306. LUID Luid,
  307. LUID ModifiedId,
  308. CCredentials& Credentials,
  309. DWORD dwPort,
  310. PADS_LDP pCacheEntry)
  311. {
  312. ENTER_BIND_CRITSECT() ;
  313. if (BindCacheCount > MAX_BIND_CACHE_SIZE) {
  314. //
  315. // If exceed limit, just dont put in cache. Since we leave the
  316. // RefCount & the Links unset, the deref will simply note that
  317. // this entry is not in cache and allow it to be freed.
  318. //
  319. // We limit cache so that if someone leaks handles we dont over
  320. // time end up traversing this huge linked list.
  321. //
  322. LEAVE_BIND_CRITSECT() ;
  323. return(NO_ERROR) ;
  324. }
  325. LPWSTR pServer = (LPWSTR) AllocADsMem(
  326. (wcslen(Address)+1)*sizeof(WCHAR)) ;
  327. if (!pServer) {
  328. LEAVE_BIND_CRITSECT() ;
  329. return(GetLastError()) ;
  330. }
  331. CCredentials * pCredentials = new CCredentials(Credentials);
  332. if (!pCredentials) {
  333. FreeADsMem(pServer);
  334. LEAVE_BIND_CRITSECT();
  335. return(GetLastError());
  336. }
  337. //
  338. // setup the data
  339. //
  340. wcscpy(pServer,Address) ;
  341. pCacheEntry->pCredentials = pCredentials;
  342. pCacheEntry->PortNumber = dwPort;
  343. pCacheEntry->Server = pServer ;
  344. pCacheEntry->RefCount = 1 ;
  345. pCacheEntry->Luid = Luid ;
  346. pCacheEntry->ModifiedId = ModifiedId;
  347. //
  348. // insert into list
  349. //
  350. InsertHeadList(&BindCache.List, &pCacheEntry->List) ;
  351. ++BindCacheCount ;
  352. LEAVE_BIND_CRITSECT() ;
  353. return NO_ERROR ;
  354. }
  355. //
  356. // Bump up the reference count of the particular cache entry
  357. // Returns the final ref count or zero if not there.
  358. //
  359. BOOL BindCacheAddRef(ADS_LDP *pCacheEntry)
  360. {
  361. DWORD dwCount = 0;
  362. ENTER_BIND_CRITSECT() ;
  363. if ((pCacheEntry->List.Flink == NULL) &&
  364. (pCacheEntry->List.Blink == NULL) &&
  365. (pCacheEntry->RefCount == NULL)) {
  366. //
  367. // this is one of them entries that has no LUID.
  368. // ie. it never got into the cache.
  369. //
  370. LEAVE_BIND_CRITSECT() ;
  371. return(0) ;
  372. }
  373. ADsAssert(pCacheEntry->List.Flink) ;
  374. ADsAssert(pCacheEntry->RefCount > 0) ;
  375. pCacheEntry->RefCount++ ;
  376. //
  377. // Save info onto stack variable as we are going
  378. // to leave the critsect before we exit.
  379. //
  380. dwCount = pCacheEntry->RefCount;
  381. LEAVE_BIND_CRITSECT() ;
  382. return(dwCount) ;
  383. }
  384. //
  385. // Adds a referral entry of pNewEntry to pPrimaryEntry. Increments the reference
  386. // count of pPrimaryEntry if succesful.
  387. //
  388. BOOL
  389. AddReferralLink(
  390. PADS_LDP pPrimaryEntry,
  391. PADS_LDP pNewEntry
  392. )
  393. {
  394. ENTER_BIND_CRITSECT() ;
  395. if (!pPrimaryEntry) {
  396. goto error;
  397. }
  398. if (!pPrimaryEntry->ReferralEntries) {
  399. pPrimaryEntry->ReferralEntries = (PADS_LDP *) AllocADsMem(
  400. sizeof(PADS_LDP) * MAX_REFERRAL_ENTRIES);
  401. if (!pPrimaryEntry->ReferralEntries) {
  402. goto error;
  403. }
  404. pPrimaryEntry->nReferralEntries = 0;
  405. }
  406. if (pPrimaryEntry->nReferralEntries >= MAX_REFERRAL_ENTRIES) {
  407. //
  408. // We won't remember more than this
  409. //
  410. goto error;
  411. }
  412. pPrimaryEntry->ReferralEntries[pPrimaryEntry->nReferralEntries] = pNewEntry;
  413. if (!BindCacheAddRef(pNewEntry)) {
  414. goto error;
  415. }
  416. pPrimaryEntry->nReferralEntries++;
  417. LEAVE_BIND_CRITSECT() ;
  418. return TRUE;
  419. error:
  420. LEAVE_BIND_CRITSECT();
  421. return FALSE;
  422. }
  423. DWORD BindCacheDeref(ADS_LDP *pCacheEntry)
  424. {
  425. DWORD dwCount = 0;
  426. LIST_ENTRY DeleteReferralList;
  427. PLIST_ENTRY pEntry;
  428. PADS_LDP EntryInfo;
  429. InitializeListHead(&DeleteReferralList);
  430. dwCount = BindCacheDerefHelper(pCacheEntry, &DeleteReferralList);
  431. // Delete the cached entries
  432. //
  433. while (!IsListEmpty (&DeleteReferralList)) {
  434. pEntry = RemoveHeadList (&DeleteReferralList);
  435. EntryInfo = CONTAINING_RECORD (pEntry, ADS_LDP, ReferralList);
  436. LdapUnbind(EntryInfo);
  437. FreeADsMem(EntryInfo);
  438. }
  439. return dwCount;
  440. }
  441. //
  442. // Dereference an entry in the cache. Removes if ref count is zero.
  443. // Returns the final ref count or zero if not there. If zero, caller
  444. // should close the handle.
  445. //
  446. DWORD BindCacheDerefHelper(ADS_LDP *pCacheEntry, LIST_ENTRY * DeleteReferralList)
  447. {
  448. DWORD dwCount=0;
  449. ENTER_BIND_CRITSECT() ;
  450. if ((pCacheEntry->List.Flink == NULL) &&
  451. (pCacheEntry->List.Blink == NULL) &&
  452. (pCacheEntry->RefCount == NULL)) {
  453. //
  454. // this is one of them entries that has no LUID.
  455. // ie. it never got into the cache.
  456. //
  457. LEAVE_BIND_CRITSECT() ;
  458. return(0) ;
  459. }
  460. ADsAssert(pCacheEntry->List.Flink) ;
  461. ADsAssert(pCacheEntry->RefCount > 0) ;
  462. //
  463. // Dereference by one. If result is non zero, just return.
  464. //
  465. --pCacheEntry->RefCount ;
  466. if (pCacheEntry->RefCount) {
  467. //
  468. // Use a stack variable for this value as
  469. // we call return outside the critsect.
  470. //
  471. dwCount = pCacheEntry->RefCount;
  472. LEAVE_BIND_CRITSECT() ;
  473. return(dwCount);
  474. }
  475. //
  476. // Before clearing the entry away verify that
  477. // we do not need to KeepAround this one.
  478. //
  479. if (pCacheEntry->fKeepAround) {
  480. //
  481. // Set the timer on this entry and leave.
  482. //
  483. pCacheEntry->dwLastUsed = GetTickCount();
  484. LEAVE_BIND_CRITSECT() ;
  485. }
  486. else {
  487. //
  488. // Now that this entry is going away, deref all the referred entries.
  489. //
  490. CommonRemoveEntry(pCacheEntry, DeleteReferralList);
  491. LEAVE_BIND_CRITSECT() ;
  492. }
  493. //
  494. // Look for any other entries that need to be cleaned out
  495. //
  496. //BindCacheCleanTimedOutEntries();
  497. return 0 ;
  498. }
  499. VOID
  500. BindCacheInit(
  501. VOID
  502. )
  503. {
  504. InitializeCriticalSection(&BindCacheCritSect) ;
  505. InitializeListHead(&BindCache.List) ;
  506. }
  507. VOID
  508. BindCacheCleanup(
  509. VOID
  510. )
  511. {
  512. PADS_LDP pEntry = (PADS_LDP) BindCache.List.Flink ;
  513. while (pEntry != &BindCache) {
  514. PADS_LDP pNext = (PADS_LDP) pEntry->List.Flink;
  515. (void) FreeADsMem(pEntry->Server) ;
  516. pEntry->Server = NULL ;
  517. if (pEntry->ReferralEntries) {
  518. FreeADsMem(pEntry->ReferralEntries);
  519. }
  520. RemoveEntryList(&pEntry->List) ;
  521. pEntry = pNext;
  522. }
  523. //
  524. // Delte the critical section initialized in BindCacheInit
  525. //
  526. DeleteCriticalSection(&BindCacheCritSect) ;
  527. }
  528. //
  529. // Mark handle so that we keep it even after the object count
  530. // has hit zero and remove only after a x mins of zero count.
  531. //
  532. HRESULT
  533. LdapcKeepHandleAround(ADS_LDP *ld)
  534. {
  535. ADsAssert(ld);
  536. ld->fKeepAround = TRUE;
  537. return S_OK;
  538. }