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.

776 lines
17 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. BOOLEAN fLUIDMatch;
  173. ENTER_BIND_CRITSECT() ;
  174. PADS_LDP pEntry = (PADS_LDP) BindCache.List.Flink ;
  175. //
  176. // Loop thru looking for match. A match is defined as:
  177. // servername & LUID matches, and it is NOT invalid.
  178. //
  179. while (pEntry != &BindCache) {
  180. if ((pEntry->Server != NULL) &&
  181. !(pEntry->Flags & LDP_CACHE_INVALID) &&
  182. (_wcsicmp(pEntry->Server, Address) == 0) &&
  183. pEntry->PortNumber == dwPort) {
  184. //
  185. // If either credential (the cached one or the user-supplied
  186. // one) is default, we must test to make sure thread token
  187. // is identical before re-using
  188. //
  189. fLUIDMatch = FALSE;
  190. if (!AreCredentialsExplicit(pEntry->pCredentials) ||
  191. !AreCredentialsExplicit(&Credentials)) {
  192. if ((memcmp(&Luid,&(pEntry->Luid),sizeof(Luid))==0) &&
  193. ((memcmp(&ModifiedId, &(pEntry->ModifiedId), sizeof(Luid)) == 0) ||
  194. (memcmp(&ModifiedId, &ReservedLuid, sizeof(Luid)) == 0))) {
  195. // tokens match
  196. fLUIDMatch = TRUE;
  197. }
  198. }
  199. else {
  200. // both are explicit credentials --> skip the test
  201. fLUIDMatch = TRUE;
  202. }
  203. if (fLUIDMatch) {
  204. if ((*(pEntry->pCredentials) == Credentials) ||
  205. CanCredentialsBeReused(pEntry->pCredentials, &Credentials))
  206. {
  207. ++pEntry->RefCount ;
  208. LEAVE_BIND_CRITSECT() ;
  209. return(pEntry) ;
  210. }
  211. }
  212. }
  213. pEntry = (PADS_LDP)pEntry->List.Flink ;
  214. }
  215. LEAVE_BIND_CRITSECT() ;
  216. //
  217. // Before we leave clean out stale entries
  218. //
  219. //BindCacheCleanTimedOutEntries();
  220. return NULL ;
  221. }
  222. //
  223. // Checks if credentials are explicit (both username and password
  224. // are non-NULL)
  225. //
  226. BOOL
  227. AreCredentialsExplicit(
  228. CCredentials *pCredentials
  229. )
  230. {
  231. PWSTR pszUser = NULL;
  232. PWSTR pszPwd = NULL;
  233. HRESULT hr;
  234. BOOL rc = FALSE;
  235. hr = pCredentials->GetUserName(&pszUser);
  236. BAIL_ON_FAILURE(hr);
  237. hr = pCredentials->GetPassword(&pszPwd);
  238. BAIL_ON_FAILURE(hr);
  239. if (pszPwd && (pszPwd[0] != L'\0') && pszUser && (pszUser[0] != L'\0')) {
  240. rc = TRUE;
  241. }
  242. error:
  243. if (pszUser)
  244. {
  245. FreeADsStr(pszUser);
  246. }
  247. if (pszPwd)
  248. {
  249. SecureZeroMemory(pszPwd, wcslen(pszPwd)*sizeof(WCHAR));
  250. FreeADsStr(pszPwd);
  251. }
  252. return rc;
  253. }
  254. //
  255. // Check if credentials can be reused. They can be reused if username and
  256. // non-ignored auth flags match and incoming credentials thesame password.
  257. // Ignored auth flags are those defined by
  258. // BIND_CACHE_IGNORED_FLAGS as not to be used in considering whether to
  259. // reuse a connection.
  260. //
  261. BOOL
  262. CanCredentialsBeReused(
  263. CCredentials *pCachedCreds,
  264. CCredentials *pIncomingCreds
  265. )
  266. {
  267. PWSTR pszIncomingUser = NULL;
  268. PWSTR pszIncomingPwd = NULL;
  269. PWSTR pszCachedUser = NULL;
  270. PWSTR pszCachedPwd = NULL;
  271. HRESULT hr;
  272. BOOL rc = FALSE;
  273. //
  274. // Test that the non-ignored auth flags match.
  275. // We need to be smart and reuse the credentials based on the flags.
  276. //
  277. if ( (~BIND_CACHE_IGNORED_FLAGS & pCachedCreds->GetAuthFlags()) !=
  278. (~BIND_CACHE_IGNORED_FLAGS & pIncomingCreds->GetAuthFlags()))
  279. {
  280. return FALSE;
  281. }
  282. //
  283. // Get the user names
  284. //
  285. hr = pCachedCreds->GetUserName(&pszCachedUser);
  286. BAIL_ON_FAILURE(hr);
  287. hr = pIncomingCreds->GetUserName(&pszIncomingUser);
  288. BAIL_ON_FAILURE(hr);
  289. //
  290. // Get the password
  291. //
  292. hr = pIncomingCreds->GetPassword(&pszIncomingPwd);
  293. BAIL_ON_FAILURE(hr);
  294. hr = pCachedCreds->GetPassword(&pszCachedPwd);
  295. BAIL_ON_FAILURE(hr);
  296. //
  297. // Only when both username and password match, will we reuse the connection handle
  298. //
  299. if (((pszCachedUser && pszIncomingUser &&
  300. wcscmp(pszCachedUser, pszIncomingUser) == 0) ||
  301. (!pszCachedUser && !pszIncomingUser))
  302. &&
  303. ((pszCachedPwd && pszIncomingPwd &&
  304. wcscmp(pszCachedPwd, pszIncomingPwd) == 0) ||
  305. (!pszCachedPwd && !pszIncomingPwd)))
  306. {
  307. rc = TRUE;
  308. }
  309. error:
  310. if (pszCachedUser)
  311. {
  312. FreeADsStr(pszCachedUser);
  313. }
  314. if (pszIncomingUser)
  315. {
  316. FreeADsStr(pszIncomingUser);
  317. }
  318. if (pszCachedPwd)
  319. {
  320. SecureZeroMemory(pszCachedPwd, wcslen(pszCachedPwd)*sizeof(WCHAR));
  321. FreeADsStr(pszCachedPwd);
  322. }
  323. if (pszIncomingPwd)
  324. {
  325. SecureZeroMemory(pszIncomingPwd, wcslen(pszIncomingPwd)*sizeof(WCHAR));
  326. FreeADsStr(pszIncomingPwd);
  327. }
  328. return rc;
  329. }
  330. //
  331. // Lookup an entry in the cache based on the Ldap Handle
  332. // DOES NOT Increment ref count
  333. //
  334. PADS_LDP
  335. GetCacheEntry(PLDAP pLdap)
  336. {
  337. ENTER_BIND_CRITSECT() ;
  338. PADS_LDP pEntry = (PADS_LDP) BindCache.List.Flink ;
  339. //
  340. // Loop thru looking for match. A match is defined as:
  341. // servername & LUID matches, and it is NOT invalid.
  342. //
  343. while (pEntry != &BindCache) {
  344. if (pEntry->LdapHandle == pLdap) {
  345. LEAVE_BIND_CRITSECT() ;
  346. return(pEntry) ;
  347. }
  348. pEntry = (PADS_LDP)pEntry->List.Flink ;
  349. }
  350. LEAVE_BIND_CRITSECT() ;
  351. return NULL ;
  352. }
  353. //
  354. // Add entry to cache
  355. //
  356. DWORD
  357. BindCacheAdd(
  358. LPWSTR Address,
  359. LUID Luid,
  360. LUID ModifiedId,
  361. CCredentials& Credentials,
  362. DWORD dwPort,
  363. PADS_LDP pCacheEntry)
  364. {
  365. ENTER_BIND_CRITSECT() ;
  366. if (BindCacheCount > MAX_BIND_CACHE_SIZE) {
  367. //
  368. // If exceed limit, just dont put in cache. Since we leave the
  369. // RefCount & the Links unset, the deref will simply note that
  370. // this entry is not in cache and allow it to be freed.
  371. //
  372. // We limit cache so that if someone leaks handles we dont over
  373. // time end up traversing this huge linked list.
  374. //
  375. LEAVE_BIND_CRITSECT() ;
  376. return(NO_ERROR) ;
  377. }
  378. LPWSTR pServer = (LPWSTR) AllocADsMem(
  379. (wcslen(Address)+1)*sizeof(WCHAR)) ;
  380. if (!pServer) {
  381. LEAVE_BIND_CRITSECT() ;
  382. return(GetLastError()) ;
  383. }
  384. CCredentials * pCredentials = new CCredentials(Credentials);
  385. if (!pCredentials) {
  386. FreeADsMem(pServer);
  387. LEAVE_BIND_CRITSECT();
  388. return(GetLastError());
  389. }
  390. //
  391. // setup the data
  392. //
  393. wcscpy(pServer,Address) ;
  394. pCacheEntry->pCredentials = pCredentials;
  395. pCacheEntry->PortNumber = dwPort;
  396. pCacheEntry->Server = pServer ;
  397. pCacheEntry->RefCount = 1 ;
  398. pCacheEntry->Luid = Luid ;
  399. pCacheEntry->ModifiedId = ModifiedId;
  400. //
  401. // insert into list
  402. //
  403. InsertHeadList(&BindCache.List, &pCacheEntry->List) ;
  404. ++BindCacheCount ;
  405. LEAVE_BIND_CRITSECT() ;
  406. return NO_ERROR ;
  407. }
  408. //
  409. // Bump up the reference count of the particular cache entry
  410. // Returns the final ref count or zero if not there.
  411. //
  412. BOOL BindCacheAddRef(ADS_LDP *pCacheEntry)
  413. {
  414. DWORD dwCount = 0;
  415. ENTER_BIND_CRITSECT() ;
  416. if ((pCacheEntry->List.Flink == NULL) &&
  417. (pCacheEntry->List.Blink == NULL) &&
  418. (pCacheEntry->RefCount == NULL)) {
  419. //
  420. // this is one of them entries that has no LUID.
  421. // ie. it never got into the cache.
  422. //
  423. LEAVE_BIND_CRITSECT() ;
  424. return(0) ;
  425. }
  426. ADsAssert(pCacheEntry->List.Flink) ;
  427. ADsAssert(pCacheEntry->RefCount > 0) ;
  428. pCacheEntry->RefCount++ ;
  429. //
  430. // Save info onto stack variable as we are going
  431. // to leave the critsect before we exit.
  432. //
  433. dwCount = pCacheEntry->RefCount;
  434. LEAVE_BIND_CRITSECT() ;
  435. return(dwCount) ;
  436. }
  437. //
  438. // Adds a referral entry of pNewEntry to pPrimaryEntry. Increments the reference
  439. // count of pPrimaryEntry if succesful.
  440. //
  441. BOOL
  442. AddReferralLink(
  443. PADS_LDP pPrimaryEntry,
  444. PADS_LDP pNewEntry
  445. )
  446. {
  447. ENTER_BIND_CRITSECT() ;
  448. if (!pPrimaryEntry) {
  449. goto error;
  450. }
  451. if (!pPrimaryEntry->ReferralEntries) {
  452. pPrimaryEntry->ReferralEntries = (PADS_LDP *) AllocADsMem(
  453. sizeof(PADS_LDP) * MAX_REFERRAL_ENTRIES);
  454. if (!pPrimaryEntry->ReferralEntries) {
  455. goto error;
  456. }
  457. pPrimaryEntry->nReferralEntries = 0;
  458. }
  459. if (pPrimaryEntry->nReferralEntries >= MAX_REFERRAL_ENTRIES) {
  460. //
  461. // We won't remember more than this
  462. //
  463. goto error;
  464. }
  465. pPrimaryEntry->ReferralEntries[pPrimaryEntry->nReferralEntries] = pNewEntry;
  466. if (!BindCacheAddRef(pNewEntry)) {
  467. goto error;
  468. }
  469. pPrimaryEntry->nReferralEntries++;
  470. LEAVE_BIND_CRITSECT() ;
  471. return TRUE;
  472. error:
  473. LEAVE_BIND_CRITSECT();
  474. return FALSE;
  475. }
  476. DWORD BindCacheDeref(ADS_LDP *pCacheEntry)
  477. {
  478. DWORD dwCount = 0;
  479. LIST_ENTRY DeleteReferralList;
  480. PLIST_ENTRY pEntry;
  481. PADS_LDP EntryInfo;
  482. InitializeListHead(&DeleteReferralList);
  483. dwCount = BindCacheDerefHelper(pCacheEntry, &DeleteReferralList);
  484. // Delete the cached entries
  485. //
  486. while (!IsListEmpty (&DeleteReferralList)) {
  487. pEntry = RemoveHeadList (&DeleteReferralList);
  488. EntryInfo = CONTAINING_RECORD (pEntry, ADS_LDP, ReferralList);
  489. LdapUnbind(EntryInfo);
  490. FreeADsMem(EntryInfo);
  491. }
  492. return dwCount;
  493. }
  494. //
  495. // Dereference an entry in the cache. Removes if ref count is zero.
  496. // Returns the final ref count or zero if not there. If zero, caller
  497. // should close the handle.
  498. //
  499. DWORD BindCacheDerefHelper(ADS_LDP *pCacheEntry, LIST_ENTRY * DeleteReferralList)
  500. {
  501. DWORD dwCount=0;
  502. ENTER_BIND_CRITSECT() ;
  503. if ((pCacheEntry->List.Flink == NULL) &&
  504. (pCacheEntry->List.Blink == NULL) &&
  505. (pCacheEntry->RefCount == NULL)) {
  506. //
  507. // this is one of them entries that has no LUID.
  508. // ie. it never got into the cache.
  509. //
  510. LEAVE_BIND_CRITSECT() ;
  511. return(0) ;
  512. }
  513. ADsAssert(pCacheEntry->List.Flink) ;
  514. ADsAssert(pCacheEntry->RefCount > 0) ;
  515. //
  516. // Dereference by one. If result is non zero, just return.
  517. //
  518. --pCacheEntry->RefCount ;
  519. if (pCacheEntry->RefCount) {
  520. //
  521. // Use a stack variable for this value as
  522. // we call return outside the critsect.
  523. //
  524. dwCount = pCacheEntry->RefCount;
  525. LEAVE_BIND_CRITSECT() ;
  526. return(dwCount);
  527. }
  528. //
  529. // Before clearing the entry away verify that
  530. // we do not need to KeepAround this one.
  531. //
  532. if (pCacheEntry->fKeepAround) {
  533. //
  534. // Set the timer on this entry and leave.
  535. //
  536. pCacheEntry->dwLastUsed = GetTickCount();
  537. LEAVE_BIND_CRITSECT() ;
  538. }
  539. else {
  540. //
  541. // Now that this entry is going away, deref all the referred entries.
  542. //
  543. CommonRemoveEntry(pCacheEntry, DeleteReferralList);
  544. LEAVE_BIND_CRITSECT() ;
  545. }
  546. //
  547. // Look for any other entries that need to be cleaned out
  548. //
  549. //BindCacheCleanTimedOutEntries();
  550. return 0 ;
  551. }
  552. VOID
  553. BindCacheInit(
  554. VOID
  555. )
  556. {
  557. InitializeListHead(&BindCache.List) ;
  558. }
  559. VOID
  560. BindCacheCleanup(
  561. VOID
  562. )
  563. {
  564. PADS_LDP pEntry = (PADS_LDP) BindCache.List.Flink ;
  565. while (pEntry != &BindCache) {
  566. PADS_LDP pNext = (PADS_LDP) pEntry->List.Flink;
  567. (void) FreeADsMem(pEntry->Server) ;
  568. pEntry->Server = NULL ;
  569. if (pEntry->ReferralEntries) {
  570. FreeADsMem(pEntry->ReferralEntries);
  571. }
  572. RemoveEntryList(&pEntry->List) ;
  573. pEntry = pNext;
  574. }
  575. }
  576. //
  577. // Mark handle so that we keep it even after the object count
  578. // has hit zero and remove only after a x mins of zero count.
  579. //
  580. HRESULT
  581. LdapcKeepHandleAround(ADS_LDP *ld)
  582. {
  583. ADsAssert(ld);
  584. ld->fKeepAround = TRUE;
  585. return S_OK;
  586. }