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.

621 lines
15 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 1996
  6. //
  7. // File: bndcache.cxx
  8. //
  9. // Contents: Binding cache for Kerberos Package
  10. //
  11. //
  12. // History: 13-August-1996 Created MikeSw
  13. //
  14. //------------------------------------------------------------------------
  15. #include <kerb.hxx>
  16. #define BNDCACHE_ALLOCATE
  17. #include <kerbp.h>
  18. //+-------------------------------------------------------------------------
  19. //
  20. // Function: KerbInitBindingCache
  21. //
  22. // Synopsis: Initializes the binding cache
  23. //
  24. // Effects: allocates a resources
  25. //
  26. // Arguments: none
  27. //
  28. // Requires:
  29. //
  30. // Returns: STATUS_SUCCESS on success, other error codes on failure
  31. //
  32. // Notes:
  33. //
  34. //
  35. //--------------------------------------------------------------------------
  36. NTSTATUS
  37. KerbInitBindingCache(
  38. VOID
  39. )
  40. {
  41. NTSTATUS Status;
  42. Status = KerbInitializeList( &KerbBindingCache );
  43. if (!NT_SUCCESS(Status))
  44. {
  45. goto Cleanup;
  46. }
  47. KerberosBindingCacheInitialized = TRUE;
  48. Cleanup:
  49. if (!NT_SUCCESS(Status))
  50. {
  51. KerbFreeList( &KerbBindingCache );
  52. }
  53. return(Status);
  54. }
  55. //+-------------------------------------------------------------------------
  56. //
  57. // Function: KerbCleanupBindingCache
  58. //
  59. // Synopsis: Frees the binding cache
  60. //
  61. // Effects:
  62. //
  63. // Arguments: none
  64. //
  65. // Requires:
  66. //
  67. // Returns: none
  68. //
  69. // Notes:
  70. //
  71. //
  72. //--------------------------------------------------------------------------
  73. VOID
  74. KerbCleanupBindingCache(
  75. BOOLEAN FreeList
  76. )
  77. {
  78. PKERB_BINDING_CACHE_ENTRY CacheEntry;
  79. if (KerberosBindingCacheInitialized)
  80. {
  81. KerbLockList(&KerbBindingCache);
  82. //
  83. // Go through the list of bindings and dereference them all
  84. //
  85. while (!IsListEmpty(&KerbBindingCache.List))
  86. {
  87. CacheEntry = CONTAINING_RECORD(
  88. KerbBindingCache.List.Flink,
  89. KERB_BINDING_CACHE_ENTRY,
  90. ListEntry.Next
  91. );
  92. DsysAssert( CacheEntry != NULL );
  93. KerbReferenceListEntry(
  94. &KerbBindingCache,
  95. &CacheEntry->ListEntry,
  96. TRUE
  97. );
  98. KerbDereferenceBindingCacheEntry(CacheEntry);
  99. }
  100. //
  101. // If we want to free the list, orphan the lock, and free the list
  102. // otherwise, proceed on w/ the "fresh" cache.
  103. //
  104. if ( FreeList )
  105. {
  106. KerbFreeList(&KerbBindingCache);
  107. }
  108. else
  109. {
  110. KerbUnlockList(&KerbBindingCache);
  111. }
  112. }
  113. }
  114. //+-------------------------------------------------------------------------
  115. //
  116. // Function: KerbDereferenceBindingCacheEntry
  117. //
  118. // Synopsis: Dereferences a binding cache entry
  119. //
  120. // Effects: Dereferences the binding cache entry to make it go away
  121. // when it is no longer being used.
  122. //
  123. // Arguments: decrements reference count and delets cache entry if it goes
  124. // to zero
  125. //
  126. // Requires: BindingCacheEntry - The binding cache entry to dereference.
  127. //
  128. // Returns: none
  129. //
  130. // Notes:
  131. //
  132. //
  133. //--------------------------------------------------------------------------
  134. VOID
  135. KerbDereferenceBindingCacheEntry(
  136. IN PKERB_BINDING_CACHE_ENTRY BindingCacheEntry
  137. )
  138. {
  139. if (KerbDereferenceListEntry(
  140. &BindingCacheEntry->ListEntry,
  141. &KerbBindingCache
  142. ) )
  143. {
  144. KerbFreeBindingCacheEntry(BindingCacheEntry);
  145. }
  146. }
  147. //+-------------------------------------------------------------------------
  148. //
  149. // Function: KerbReferenceBindingCacheEntry
  150. //
  151. // Synopsis: References a binding cache entry
  152. //
  153. // Effects: Increments the reference count on the binding cache entry
  154. //
  155. // Arguments: BindingCacheEntry - binding cache entry to reference
  156. //
  157. // Requires: The binding cache must be locked
  158. //
  159. // Returns: none
  160. //
  161. // Notes:
  162. //
  163. //
  164. //--------------------------------------------------------------------------
  165. VOID
  166. KerbReferenceBindingCacheEntry(
  167. IN PKERB_BINDING_CACHE_ENTRY BindingCacheEntry,
  168. IN BOOLEAN RemoveFromList
  169. )
  170. {
  171. KerbLockList(&KerbBindingCache);
  172. KerbReferenceListEntry(
  173. &KerbBindingCache,
  174. &BindingCacheEntry->ListEntry,
  175. RemoveFromList
  176. );
  177. KerbUnlockList(&KerbBindingCache);
  178. }
  179. //+-------------------------------------------------------------------------
  180. //
  181. // Function: KerbLocateBindingCacheEntry
  182. //
  183. // Synopsis: References a binding cache entry by name
  184. //
  185. // Effects: Increments the reference count on the binding cache entry
  186. //
  187. // Arguments: RealmName - Contains the name of the realm for which to
  188. // obtain a binding handle.
  189. // DesiredFlags - Flags desired for binding, such as PDC required
  190. // RemoveFromList - Remove cache entry from cache when found.
  191. //
  192. // Requires:
  193. //
  194. // Returns: The referenced cache entry or NULL if it was not found.
  195. //
  196. // Notes: If an invalid entry is found it may be dereferenced
  197. //
  198. //
  199. //--------------------------------------------------------------------------
  200. PKERB_BINDING_CACHE_ENTRY
  201. KerbLocateBindingCacheEntry(
  202. IN PUNICODE_STRING RealmName,
  203. IN ULONG DesiredFlags,
  204. IN BOOLEAN RemoveFromList
  205. )
  206. {
  207. PLIST_ENTRY ListEntry;
  208. PKERB_BINDING_CACHE_ENTRY CacheEntry = NULL;
  209. BOOLEAN Found = FALSE;
  210. if (DesiredFlags == 0)
  211. {
  212. DesiredFlags = KERB_NO_DC_FLAGS;
  213. }
  214. KerbLockList(&KerbBindingCache);
  215. //
  216. // Go through the binding cache looking for the correct entry
  217. //
  218. for (ListEntry = KerbBindingCache.List.Flink ;
  219. ListEntry != &KerbBindingCache.List ;
  220. ListEntry = ListEntry->Flink )
  221. {
  222. CacheEntry = CONTAINING_RECORD(ListEntry, KERB_BINDING_CACHE_ENTRY, ListEntry.Next);
  223. DsysAssert( CacheEntry != NULL );
  224. if ( RtlEqualUnicodeString( &CacheEntry->RealmName, RealmName,TRUE ) &&
  225. ((DesiredFlags & CacheEntry->Flags) == DesiredFlags))
  226. {
  227. Found = TRUE;
  228. //
  229. // Check to see if we should stop using this entry
  230. if (!RemoveFromList)
  231. {
  232. TimeStamp CurrentTime, Timeout;
  233. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime );
  234. if ((CacheEntry->DcFlags & DS_CLOSEST_FLAG) == 0)
  235. {
  236. Timeout = KerbGlobalFarKdcTimeout;
  237. }
  238. else
  239. {
  240. Timeout = KerbGlobalNearKdcTimeout;
  241. }
  242. if (KerbGetTime(CacheEntry->DiscoveryTime) + KerbGetTime(Timeout) < KerbGetTime(CurrentTime))
  243. {
  244. //
  245. // This entry has timed out - it is not close by and we
  246. // don't want to use it for too long, or its time to check
  247. // for a close DC again.
  248. //
  249. // Note: This will have the sideeffect of checking for a new PDC
  250. //
  251. D_DebugLog((DEB_TRACE,
  252. "Purging KDC cache entry Realm: %wZ, Addr: %wZ, DcFlags %x\n",
  253. &CacheEntry->RealmName,
  254. &CacheEntry->KdcAddress,
  255. CacheEntry->DcFlags
  256. ));
  257. RemoveFromList = TRUE;
  258. Found = FALSE;
  259. }
  260. else
  261. {
  262. D_DebugLog((DEB_TRACE,
  263. "**Using** KDC cache entry Realm: %wZ, Addr: %wZ, DcFlags %x\n",
  264. &CacheEntry->RealmName,
  265. &CacheEntry->KdcAddress,
  266. CacheEntry->DcFlags
  267. ));
  268. #ifdef CACHE_TRACE
  269. if ((CacheEntry->DcFlags & DS_CLOSEST_FLAG) == DS_CLOSEST_FLAG)
  270. {
  271. DebugLog((DEB_ERROR, "CLOSE DC "));
  272. }
  273. else
  274. {
  275. DebugLog((DEB_ERROR, "FAR DC "));
  276. }
  277. if ((CacheEntry->DcFlags & DS_PDC_FLAG) == DS_PDC_FLAG)
  278. {
  279. DebugLog((DEB_ERROR, "-- ** PDC **\n"));
  280. }
  281. else
  282. {
  283. DebugLog((DEB_ERROR, "-- BDC\n"));
  284. }
  285. #endif
  286. }
  287. }
  288. KerbReferenceBindingCacheEntry(
  289. CacheEntry,
  290. RemoveFromList
  291. );
  292. //
  293. // If we aren't returning this, dereference it now
  294. //
  295. if (!Found)
  296. {
  297. KerbDereferenceBindingCacheEntry( CacheEntry );
  298. }
  299. break;
  300. }
  301. }
  302. if (!Found)
  303. {
  304. CacheEntry = NULL;
  305. }
  306. KerbUnlockList(&KerbBindingCache);
  307. return(CacheEntry);
  308. }
  309. //+-------------------------------------------------------------------------
  310. //
  311. // Function: KerbFreeBindingCacheEntry
  312. //
  313. // Synopsis: Frees memory associated with a binding cache entry
  314. //
  315. // Effects:
  316. //
  317. // Arguments: BindingCacheEntry - The cache entry to free. It must be
  318. // unlinked.
  319. //
  320. // Requires:
  321. //
  322. // Returns: none
  323. //
  324. // Notes:
  325. //
  326. //
  327. //--------------------------------------------------------------------------
  328. VOID
  329. KerbFreeBindingCacheEntry(
  330. IN PKERB_BINDING_CACHE_ENTRY BindingCacheEntry
  331. )
  332. {
  333. KerbFreeString(&BindingCacheEntry->RealmName);
  334. KerbFreeString(&BindingCacheEntry->KdcAddress);
  335. KerbFree(BindingCacheEntry);
  336. }
  337. //+-------------------------------------------------------------------------
  338. //
  339. // Function: KerbInsertBinding
  340. //
  341. // Synopsis: Inserts a binding into the binding cache
  342. //
  343. // Effects: bumps reference count on binding
  344. //
  345. // Arguments: CacheEntry - Cache entry to insert
  346. //
  347. // Requires:
  348. //
  349. // Returns: STATUS_SUCCESS always
  350. //
  351. // Notes:
  352. //
  353. //
  354. //--------------------------------------------------------------------------
  355. NTSTATUS
  356. KerbInsertBinding(
  357. IN PKERB_BINDING_CACHE_ENTRY CacheEntry
  358. )
  359. {
  360. KerbInsertListEntry(
  361. &CacheEntry->ListEntry,
  362. &KerbBindingCache
  363. );
  364. return(STATUS_SUCCESS);
  365. }
  366. //+-------------------------------------------------------------------------
  367. //
  368. // Function: KerbCacheBinding
  369. //
  370. // Synopsis: Caches a binding in the binding cache
  371. //
  372. // Effects: creates a cache entry.
  373. //
  374. // Arguments: RealmName - The realm name of the KDC the binding is to.
  375. // KdcAddress - address of the KDC
  376. // AddressType - Type of address, from DsGetDCName flags
  377. // Flags - These were the desired flags that we asked for
  378. // DcFlags - These are the flags the dc has
  379. // CacheFlags - Special meaning so we don't use the locator bits
  380. // CacheEntry - Receives the new binding cache entry, referenced
  381. //
  382. // Requires:
  383. //
  384. // Returns: STATUS_SUCCESS on success, other error codes on failure
  385. //
  386. // Notes: Locks the binding cache for write access while adding
  387. // the cache entry. Removes a cache entry for the same domain
  388. // before adding this one.
  389. //
  390. //
  391. //--------------------------------------------------------------------------
  392. NTSTATUS
  393. KerbCacheBinding(
  394. IN PUNICODE_STRING RealmName,
  395. IN PUNICODE_STRING KdcAddress,
  396. IN ULONG AddressType,
  397. IN ULONG Flags,
  398. IN ULONG DcFlags,
  399. IN ULONG CacheFlags,
  400. OUT PKERB_BINDING_CACHE_ENTRY * NewCacheEntry
  401. )
  402. {
  403. PKERB_BINDING_CACHE_ENTRY CacheEntry = NULL;
  404. PKERB_BINDING_CACHE_ENTRY OldCacheEntry = NULL;
  405. NTSTATUS Status = STATUS_SUCCESS;
  406. ULONG DesiredFlags = KERB_NO_DC_FLAGS;
  407. D_DebugLog((DEB_TRACE,
  408. "Adding Binding Cache Entry - %wZ : %wZ, DcFlags %x CacheFlags %x\n",
  409. RealmName,
  410. KdcAddress,
  411. DcFlags,
  412. CacheFlags
  413. ));
  414. Flags &= ~DS_FORCE_REDISCOVERY; //not a valid flag
  415. //
  416. // If we requested a PDC, and this is a PDC, then cache it
  417. // as a PDC. Otherwise, we just got lucky, and we'll use
  418. // the PDC naturally.
  419. //
  420. if ((Flags == DS_PDC_REQUIRED) && ((DcFlags & DS_PDC_FLAG) == DS_PDC_FLAG))
  421. {
  422. D_DebugLog((DEB_TRACE, "Caching as PDC\n"));
  423. DesiredFlags = DS_PDC_REQUIRED;
  424. }
  425. else
  426. {
  427. D_DebugLog((DEB_TRACE, "Caching as BDC\n"));
  428. Flags &= ~DS_PDC_REQUIRED; // clear the flag.
  429. DcFlags &= ~DS_PDC_FLAG;
  430. }
  431. *NewCacheEntry = NULL;
  432. CacheEntry = (PKERB_BINDING_CACHE_ENTRY)
  433. KerbAllocate(sizeof(KERB_BINDING_CACHE_ENTRY));
  434. if (CacheEntry == NULL)
  435. {
  436. Status = STATUS_INSUFFICIENT_RESOURCES;
  437. goto Cleanup;
  438. }
  439. KerbInitializeListEntry(
  440. &CacheEntry->ListEntry
  441. );
  442. GetSystemTimeAsFileTime((PFILETIME)
  443. &CacheEntry->DiscoveryTime
  444. );
  445. Status = KerbDuplicateString(
  446. &CacheEntry->RealmName,
  447. RealmName
  448. );
  449. if (!NT_SUCCESS(Status))
  450. {
  451. goto Cleanup;
  452. }
  453. Status = KerbDuplicateString(
  454. &CacheEntry->KdcAddress,
  455. KdcAddress
  456. );
  457. if (!NT_SUCCESS(Status))
  458. {
  459. goto Cleanup;
  460. }
  461. CacheEntry->AddressType = AddressType;
  462. CacheEntry->Flags = ((Flags == 0) ? KERB_NO_DC_FLAGS : Flags);
  463. CacheEntry->DcFlags = DcFlags;
  464. CacheEntry->CacheFlags = CacheFlags;
  465. //
  466. // Before we insert this binding we want to remove any
  467. // previous instances of bindings to the same realm.
  468. //
  469. OldCacheEntry = KerbLocateBindingCacheEntry(
  470. RealmName,
  471. DesiredFlags, // only hammer on PDC entries
  472. TRUE // remove from cache
  473. );
  474. if (OldCacheEntry != NULL)
  475. {
  476. KerbDereferenceBindingCacheEntry( OldCacheEntry );
  477. }
  478. //
  479. // Insert the cache entry into the cache
  480. //
  481. Status = KerbInsertBinding(
  482. CacheEntry
  483. );
  484. if (!NT_SUCCESS(Status))
  485. {
  486. goto Cleanup;
  487. }
  488. *NewCacheEntry = CacheEntry;
  489. Cleanup:
  490. if (!NT_SUCCESS(Status))
  491. {
  492. if (NULL != CacheEntry)
  493. {
  494. KerbFreeBindingCacheEntry(CacheEntry);
  495. }
  496. }
  497. return(Status);
  498. }
  499. //+-------------------------------------------------------------------------
  500. //
  501. // Function: KerbRemoveBindingCacheEntry
  502. //
  503. // Synopsis: removes an entry from the binding cache
  504. //
  505. // Effects:
  506. //
  507. // Arguments: CacheEntry - entry to remove
  508. //
  509. // Requires:
  510. //
  511. // Returns:
  512. //
  513. // Notes:
  514. //
  515. //
  516. //--------------------------------------------------------------------------
  517. VOID
  518. KerbRemoveBindingCacheEntry(
  519. IN PKERB_BINDING_CACHE_ENTRY CacheEntry
  520. )
  521. {
  522. KerbLockList(&KerbBindingCache);
  523. KerbReferenceBindingCacheEntry(
  524. CacheEntry,
  525. TRUE
  526. );
  527. KerbDereferenceBindingCacheEntry(
  528. CacheEntry
  529. );
  530. KerbUnlockList(&KerbBindingCache);
  531. }