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.

1412 lines
40 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1993.
  5. //
  6. // File: prcache.cxx
  7. //
  8. // Contents: Principal cache handling code
  9. //
  10. // Classes: CPrincipalHandler
  11. //
  12. // History: 4-02-93 WadeR Created
  13. // 04-Nov-93 WadeR Updated to hold CLogonAccounts
  14. // 08-Nov-93 WadeR Removed altogether in favour of Account.dll caching. Sigh.
  15. //
  16. // Notes: This is the principal cache code.
  17. //
  18. // When you first call GetASTicket, it passes in a flag indicating a logon is
  19. // in progress. This flag migrates to the created cache entry, and is passed
  20. // out in the TicketInfo when you call GetTicketInfo().
  21. //
  22. // When you call TGSTicket with a PAC, it checks the flag. If it's set, it
  23. // calls LogonComplete(success).
  24. //
  25. // LogonComplete will clear the flag, and delete the entry from the cache.
  26. //
  27. // When the cache is full and another entry is needed the first pass looks for
  28. // an entry that is not in the middle of a login. If it finds one, then that
  29. // is released, and used. If it can't find one, it finds the oldest logon
  30. // pending, and calls LogonComplete(fail) on it, and uses it.
  31. //
  32. // If ClearCache() attempts to remove an entry with a non-zero use count
  33. // (indicating that someone has called GetLogonInfo and not ReleaseLogonInfo),
  34. // that entry is tagged as invalid and left alone. Once the use count goes
  35. // down to zero, it will be discarded.
  36. //
  37. // BUGBUG: Need some form of notification from the Account code to
  38. // tell me that an account changed. Also, need to defer
  39. // re-loading it if it's in use.
  40. //
  41. // BUGBUG: Should change Win4Assert to SafeAssert or ASSERT().
  42. //
  43. // BUGBUG: Should remove PrintSizes();
  44. //
  45. //----------------------------------------------------------------------------
  46. // Get the common, global header files.
  47. #include <secpch2.hxx>
  48. #pragma hdrstop
  49. // Place any local #includes files here.
  50. #include "secdata.hxx"
  51. #include <princpl.hxx>
  52. #include <prcache.hxx>
  53. ///////////////////////////////////////////////////////////////
  54. //
  55. //
  56. // Global data
  57. //
  58. CPrincipalHandler PrincipalCache;
  59. const PWCHAR pwcKdcPrincipal = L"\\KDC";
  60. const PWCHAR pwcPSPrincipal = L"\\PrivSvr";
  61. // Constants to control cache size. The cache will start out being
  62. // CACHE_INITIALSIZE bytes (rounded down to a multiple of sizeof(CacheEntry)).
  63. //
  64. // When the cache needs to grow, it will grow by CACHE_GROW bytes (also
  65. // rounded down). When it is CACHE_SHRINK_THRESHOLD bytes larger than needed,
  66. // it will shrink to become the minumum size needed plus CACHE_SHRINK bytes.
  67. //
  68. // Caveats:
  69. //
  70. // If CACHE_GROW > CACHE_SHRINK_THRESHOLD, every time the cache grows it will
  71. // shrink again. If CACHE_SHRINK >= CACHE_SHRINK_THRESHOLD, the cache will
  72. // shrink over and over again, without changing size. Finally, If
  73. // CACHE_SHRINK_THRESHOLD < CACHE_INITIALSIZE then the cache will shrink on
  74. // startup (which is not a smart thing to do).
  75. //
  76. // I suggest that CACHE_SHRINK_THRESHOLD >= 2 * CACHE_SHRINK
  77. // CACHE_SHRINK_THRESHOLD >= 2 * CACHE_GROW
  78. // CACHE_SHRINK_THRESHOLD > CACHE_INITIALSIZE
  79. // CACHE_SHRINK ~= CACHE_GROW
  80. //
  81. // Currently, sizeof( CacheEntry ) == 24 bytes.
  82. #if 0
  83. #define CACHE_INITIALSIZE 1024
  84. #define CACHE_GROW 512
  85. #define CACHE_SHRINK_THRESHOLD 1536
  86. #define CACHE_SHRINK 512
  87. #else
  88. #pragma MEMO( Really tiny cache sizes for testing )
  89. #define CACHE_INITIALSIZE 50
  90. #define CACHE_GROW 25
  91. #define CACHE_SHRINK_THRESHOLD 75
  92. #define CACHE_SHRINK 25
  93. #endif
  94. //
  95. // Flags for fCacheFlags.
  96. //
  97. // Note that CACHE_DOING_LOGON (flag for fCacheFlags) has the same value as
  98. // DOING_LOGON (flag for TicketInfo::LogonSteps).
  99. //
  100. #define CACHE_DOING_LOGON DOING_LOGON
  101. #define CACHE_STICKY 0x00010000
  102. #define CACHE_INVALID 0x00080000
  103. //
  104. // Helper functinons (only used by this file).
  105. //
  106. #define PrintSizes() KdcDebug(( DEB_T_CACHE, "Line %d: cCache=%d, cMaxCacheSize=%d\n", \
  107. __LINE__, _cCache, _cCacheMaxSize ))
  108. //+---------------------------------------------------------------------------
  109. //
  110. // Function: MapNameDRN
  111. //
  112. // Synopsis: Maps a name to a domain relative name
  113. //
  114. // Effects: may orphan memory (indicated by an error message)
  115. //
  116. // Arguments: [pwzInName] -- Name to map
  117. //
  118. // Returns: pointer into string passed in, or new memory.
  119. //
  120. // History: 14-Sep-93 WadeR Created
  121. //
  122. // Notes:
  123. //
  124. // BUGBUG: The client should be fixed to NEVER pass in a bogus name,
  125. // so this should be able to map in place or return an error.
  126. //
  127. //----------------------------------------------------------------------------
  128. static PWCHAR
  129. MapNameDRN( PWCHAR pwzInName )
  130. {
  131. if (*pwzInName == L'\\')
  132. return(pwzInName);
  133. if (wcsnicmp(pwzInName,
  134. SecData.KdcRealm()->Buffer,
  135. SecData.KdcRealm()->Length / sizeof(WCHAR) ) == 0 )
  136. {
  137. // Starts with this domain.
  138. return(pwzInName + SecData.KdcRealm()->Length / sizeof(WCHAR) );
  139. }
  140. ULONG cch = wcslen( awcDOMAIN ) - 1; // - 1 because don't want the '\'
  141. if (wcsnicmp( pwzInName, awcDOMAIN, cch ) == 0)
  142. {
  143. // Starts with "domain:"
  144. return(pwzInName + cch);
  145. }
  146. KdcDebug(( DEB_WARN, "MapNameDRN(%ws): Don't understand (prepending '\\').\n",
  147. pwzInName ));
  148. KdcDebug(( DEB_WARN, "MapNameDRN: Memory leak.\n" ));
  149. PWCHAR pwNew = new WCHAR [wcslen(pwzInName) + 2];
  150. pwNew[0] = '\\';
  151. pwNew[1] = '\0';
  152. wcscat( pwNew, pwzInName );
  153. return(pwNew);
  154. }
  155. //
  156. // Private member functions.
  157. //
  158. //+---------------------------------------------------------------------------
  159. //
  160. // Member: CPrincipalHandler::ReleaseCacheEntry
  161. //
  162. // Synopsis: Removes a cache slot, releasing if needed.
  163. //
  164. // Effects: May call CLogonAccount::Release()
  165. //
  166. // Arguments: [i] -- Slot to release
  167. //
  168. // Requires: Caller must have write access to _Monitor
  169. //
  170. // Returns: void
  171. //
  172. // History: 04-Nov-93 WadeR Created
  173. //
  174. // Notes: This moves the empty slot to the end.
  175. //
  176. //----------------------------------------------------------------------------
  177. void
  178. CPrincipalHandler::ReleaseCacheEntry( ULONG i )
  179. {
  180. KdcDebug(( DEB_T_CACHE, "Releasing slot %d.\n", i ));
  181. if (_Cache[i].plga != NULL)
  182. {
  183. _Cache[i].plga->Release();
  184. }
  185. if (_Cache[i].pwzName)
  186. {
  187. delete _Cache[i].pwzName;
  188. }
  189. // Move the empty spot to the end of the array.
  190. _cCache--;
  191. Win4Assert( _cCache >= 0 );
  192. _Cache[i] = _Cache[_cCache];
  193. // Zero out the new empty spot
  194. _Cache[_cCache].plga = 0;
  195. _Cache[_cCache].pwzName = 0;
  196. PrintSizes();
  197. }
  198. //+---------------------------------------------------------------------------
  199. //
  200. // Member: CPrincipalHandler::Discard
  201. //
  202. // Synopsis: Removes a cache entry, completing it's logon if needed.
  203. //
  204. // Effects: may call CLogonAccount::LogonComplete()
  205. //
  206. // Arguments: [i] -- Slot to discard
  207. //
  208. // Requires: Caller must have write access to _Monitor
  209. //
  210. // Returns: void
  211. //
  212. // History: 04-Nov-93 WadeR Created
  213. //
  214. // Notes:
  215. //
  216. //----------------------------------------------------------------------------
  217. void
  218. CPrincipalHandler::Discard( int i )
  219. {
  220. KdcDebug(( DEB_T_CACHE, "Discarding %ws from cache slot %d%s.\n",
  221. _Cache[i].pwzName, i,
  222. (_Cache[i].fCacheFlags & CACHE_DOING_LOGON)?
  223. " (logon interupted)":
  224. "" ));
  225. // If it's a logon, then mark it as failed.
  226. // ReleaseCacheEntry will remove the entry, and move the
  227. // empty spot to the end.
  228. if (_Cache[i].fCacheFlags & CACHE_DOING_LOGON)
  229. {
  230. _Cache[i].plga->LogonComplete( FALSE, (FILETIME*)&tsZero ); // failed logon, no lockout
  231. _Cache[i].plga->Save(NULL, FALSE);
  232. }
  233. ReleaseCacheEntry( i );
  234. PrintSizes();
  235. }
  236. //+---------------------------------------------------------------------------
  237. //
  238. // Member: CPrincipalHandler::GrowCache
  239. //
  240. // Synopsis: Grows the principal cache.
  241. //
  242. // Effects: Allocates memory
  243. //
  244. // Arguments: (none)
  245. //
  246. // Requires: Caller must have a write lock on the cache.
  247. //
  248. // Returns: HRESULT (S_OK or E_OUTOFMEMORY)
  249. //
  250. // Signals: none
  251. //
  252. // Algorithm: Adds GROW_SIZE bytes to the cache, rounded down to
  253. // sizeof(CacheElement).
  254. //
  255. // History: 05-Nov-93 WadeR Created
  256. //
  257. // Notes:
  258. //
  259. //----------------------------------------------------------------------------
  260. HRESULT
  261. CPrincipalHandler::GrowCache()
  262. {
  263. SafeAssert( _cCacheMaxSize >= _cCache );
  264. ULONG cNewSize = (_cCacheMaxSize * sizeof( CacheEntry ) + CACHE_GROW)
  265. / sizeof( CacheEntry );
  266. KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::GrowCache() %d -> %d\n",
  267. _cCacheMaxSize, cNewSize ));
  268. PrintSizes();
  269. SafeAssert( cNewSize > _cCache );
  270. CacheEntry * pNew = new (NullOnFail) CacheEntry [cNewSize];
  271. if (pNew == NULL)
  272. {
  273. KdcDebug(( DEB_ERROR, "Out of memory.\n" ));
  274. return(E_OUTOFMEMORY);
  275. }
  276. RtlCopyMemory( (PBYTE) pNew, (PBYTE) _Cache, _cCache * sizeof( CacheEntry ) );
  277. delete _Cache;
  278. _Cache = pNew;
  279. _cCacheMaxSize = cNewSize;
  280. PrintSizes();
  281. return(S_OK);
  282. }
  283. //+---------------------------------------------------------------------------
  284. //
  285. // Member: CPrincipalHandler::ShrinkCache
  286. //
  287. // Synopsis: Shrinks the principal cache, if needed.
  288. //
  289. // Effects: Allocates memory
  290. //
  291. // Arguments: (none)
  292. //
  293. // Requires: Caller must have a write lock on the cache.
  294. //
  295. // Returns: HRESULT (S_OK or E_OUTOFMEMORY)
  296. //
  297. // Signals: none
  298. //
  299. // Algorithm: If the cache is SHRINK_THRESHOLD bytes too large, resizes
  300. // it to be just SHRINK_SIZE bytes bigger than needed, rounded
  301. // down to sizeof(CacheElement).
  302. //
  303. // History: 05-Nov-93 WadeR Created
  304. //
  305. // Notes:
  306. //
  307. //----------------------------------------------------------------------------
  308. HRESULT
  309. CPrincipalHandler::ShrinkCache()
  310. {
  311. KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::ShrinkCache(%d)\n", _cCacheMaxSize ));
  312. PrintSizes();
  313. SafeAssert( _cCacheMaxSize >= _cCache );
  314. if ( (_cCacheMaxSize - _cCache) >
  315. CACHE_SHRINK_THRESHOLD / sizeof( CacheEntry ) )
  316. {
  317. // Need to shrink the cache.
  318. ULONG cNewSize = (_cCache * sizeof( CacheEntry ) + CACHE_SHRINK)
  319. / sizeof( CacheEntry );
  320. KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::ShrinkCache() %d -> %d\n",
  321. _cCacheMaxSize, cNewSize ));
  322. SafeAssert( cNewSize > _cCache );
  323. CacheEntry * pNew = new (NullOnFail) CacheEntry [cNewSize];
  324. if (pNew == NULL)
  325. {
  326. KdcDebug(( DEB_ERROR, "Out of memory.\n" ));
  327. return(E_OUTOFMEMORY);
  328. }
  329. RtlCopyMemory( (PBYTE) pNew, (PBYTE) _Cache, _cCache * sizeof( CacheEntry ) );
  330. delete _Cache;
  331. _Cache = pNew;
  332. _cCacheMaxSize = cNewSize;
  333. }
  334. PrintSizes();
  335. return(S_OK);
  336. }
  337. //+---------------------------------------------------------------------------
  338. //
  339. // Member: CPrincipalHandler::PurgeSomething
  340. //
  341. // Synopsis: Makes at least one entry in the cache free.
  342. //
  343. // Effects: May release some cache entries, may grow the cache.
  344. //
  345. // Arguments: (none)
  346. //
  347. // Requires: _Monitor to be acquired for writing.
  348. //
  349. // Algorithm: Deletes the oldest cache element.
  350. //
  351. // History: 04-Nov-93 WadeR Created
  352. //
  353. // Notes: This function guarentees that on return, there is at least
  354. // one empty cache entry for the caller to use. To do this,
  355. // the caller must know that no other thread will think it can
  356. // use that slot. Therefore the caller must have write access
  357. // to the cache.
  358. //
  359. //----------------------------------------------------------------------------
  360. HRESULT
  361. CPrincipalHandler::PurgeSomething()
  362. {
  363. KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::PurgeSomething()\n" ));
  364. PrintSizes();
  365. ULONG i;
  366. TimeStamp tsOldest = tsInfinity;
  367. ULONG iOldest = 0;
  368. //
  369. // First pass, only consider entries that are not part of
  370. // a logon in progress.
  371. //
  372. for (i=0; i<_cCache; i++)
  373. {
  374. if (!(_Cache[i].fCacheFlags &
  375. (CACHE_DOING_LOGON | CACHE_STICKY)) &&
  376. (_Cache[i].cUseCount == 0) &&
  377. (_Cache[i].tsLastUsed < tsOldest))
  378. {
  379. iOldest = i;
  380. tsOldest = _Cache[i].tsLastUsed;
  381. }
  382. }
  383. if (tsOldest == tsInfinity)
  384. {
  385. //
  386. // Didn't find anything that could be removed.
  387. //
  388. return GrowCache();
  389. }
  390. //
  391. // We have found the one to boot out.
  392. //
  393. // Convert the lock to write before discarding it. Since we like to leave
  394. // things the way we found them, convert it back when we're done.
  395. //
  396. Discard( iOldest );
  397. PrintSizes();
  398. return(S_OK);
  399. }
  400. //+---------------------------------------------------------------------------
  401. //
  402. // Member: CPrincipalHandler::GetCacheEntry
  403. //
  404. // Synopsis: Finds something in the cache, adding it if needed and if
  405. // requested.
  406. //
  407. // Effects: May call GetLogonAccount to insert in the cache.
  408. // May remove something from the cache to make room.
  409. //
  410. // Arguments: [pwzName] -- [in] name of principal
  411. // [piIndex] -- [out] cache index
  412. // [fCacheFlags] -- [in] Flags to put in created cache entry
  413. // [fLoad] -- [in] if true, load missing entry.
  414. //
  415. // Signals: nothing (unless GetLogonAccount throws something).
  416. //
  417. // Requires: Caller must have read access to _Monitor
  418. //
  419. // Returns: S_OK if in cache, else result of GetLogonAccount()
  420. // KDC_E_C_PRINCIPAL_UNKNOWN if it isn't in the cache and
  421. // fLoad == FALSE
  422. //
  423. // History: 04-Nov-93 WadeR Created
  424. //
  425. // Notes: This code is not exception-safe. If GetLogonAccount throws
  426. // an exception, it will leak resources.
  427. //
  428. // This routine returns an index into the cache. This index must remain
  429. // valid, so you can't allow anyone to change the cache while it's in use.
  430. // Therefore the caller must hold a read lock on the cache, and release it
  431. // once the caller is done with the index returned.
  432. //
  433. //----------------------------------------------------------------------------
  434. HRESULT
  435. CPrincipalHandler::GetCacheEntry( PWCHAR pwzName,
  436. PULONG piIndex,
  437. ULONG fCacheFlags,
  438. BOOL fLoad )
  439. {
  440. KdcDebug(( DEB_TRACE, "Looking for account %ws (DRN)\n", pwzName ));
  441. // Scan through the cache entries looking for this name.
  442. for (ULONG i=0; i<_cCache; i++ )
  443. {
  444. if (!wcsicmp(_Cache[i].pwzName, pwzName ))
  445. {
  446. //
  447. // We found the cache entry we are interested in.
  448. //
  449. *piIndex = i;
  450. //
  451. // Convert the monitor to write access, so we can modify
  452. // the last access time.
  453. //
  454. _Monitor.ReadToWrite();
  455. GetCurrentTimeStamp( &_Cache[i].tsLastUsed );
  456. // Return the monitor and leave.
  457. _Monitor.WriteToRead();
  458. return(S_OK);
  459. }
  460. }
  461. // Didn't find it in the cache.
  462. KdcDebug(( DEB_T_CACHE, "Didn't find %ws in the cache.\n", pwzName ));
  463. if (!fLoad)
  464. {
  465. return(KDC_E_C_PRINCIPAL_UNKNOWN);
  466. }
  467. //
  468. // Build the information that we are going to write to the
  469. // Cache now, before upgrading to a write lock on the monitor.
  470. // This way, we keep an exclusive lock for as little time as
  471. // possible.
  472. //
  473. //
  474. // Copy the name, so it stays valid regardless of what the caller
  475. // does with it.
  476. //
  477. PWCHAR pwzNameCopy = new (NullOnFail) WCHAR [wcslen( pwzName ) + 1];
  478. if (pwzNameCopy == 0)
  479. {
  480. return(E_OUTOFMEMORY);
  481. }
  482. wcscpy( pwzNameCopy, pwzName );
  483. CLogonAccount* plga;
  484. //
  485. // Get the account object
  486. //
  487. HRESULT hr = GetLogonAccount( pwzName,
  488. TRUE, // Domain namespace
  489. &plga );
  490. if (FAILED(hr))
  491. {
  492. KdcDebug(( DEB_WARN, "Error finding principal '%ws' (0x%X)\n",
  493. pwzName, hr ));
  494. delete pwzNameCopy;
  495. return(hr);
  496. }
  497. TimeStamp tsNow;
  498. GetCurrentTimeStamp( &tsNow );
  499. _Monitor.ReadToWrite();
  500. if (_cCache == _cCacheMaxSize)
  501. {
  502. // Cache is full, so must purge something.
  503. PurgeSomething();
  504. }
  505. //
  506. // The last entry is free now.
  507. //
  508. //
  509. // BUGBUG: PurgeSomething could run out of memory.
  510. //
  511. SafeAssert( _cCache < _cCacheMaxSize );
  512. //
  513. // Insert the data we constructed before.
  514. //
  515. _Cache[_cCache].pwzName = pwzNameCopy;
  516. _Cache[_cCache].plga = plga;
  517. _Cache[_cCache].tsLastUsed = tsNow;
  518. _Cache[_cCache].fCacheFlags = fCacheFlags;
  519. *piIndex = _cCache;
  520. _cCache++;
  521. _Monitor.WriteToRead();
  522. // Subtract 1 because we've already incremented the count (inside the
  523. // monitor).
  524. KdcDebug(( DEB_T_CACHE, "Added %ws in slot %d (flags:%x)\n",
  525. _Cache[_cCache-1].pwzName, _cCache-1,
  526. _Cache[_cCache-1].fCacheFlags ));
  527. PrintSizes();
  528. return(S_OK);
  529. }
  530. //
  531. //
  532. // Public methods
  533. //
  534. //
  535. //+---------------------------------------------------------------------------
  536. //
  537. // Member: CPrincipalHandler::GetTicketInfo
  538. //
  539. // Synopsis: Gets the ticket-granting info for a principal
  540. //
  541. // Effects: Cache lookup.
  542. //
  543. // Arguments: [pwzName] -- [in] Name of principal
  544. // [pti] -- [out] ticket info
  545. //
  546. // Requires:
  547. //
  548. // Returns:
  549. //
  550. // Signals:
  551. //
  552. // Modifies:
  553. //
  554. // Derivation:
  555. //
  556. // Algorithm:
  557. //
  558. // History: 04-Nov-93 WadeR Created
  559. //
  560. // Notes: This routine is exception-safe. If any of the routines it
  561. // calls (GetCacheEntry, CLogonAccount::GetTicketInfo) throw,
  562. // it will pass the exception up and not leak resources.
  563. //
  564. //----------------------------------------------------------------------------
  565. HRESULT
  566. CPrincipalHandler::GetTicketInfo( PWCHAR pwzName, TicketInfo* pti )
  567. {
  568. KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::GetTicketInfo(%ws)\n",
  569. pwzName ));
  570. SafeAssert( _fInitialized );
  571. ULONG iIndex;
  572. PWCHAR pwzDRName = MapNameDRN( pwzName );
  573. CReadLock lock( _Monitor );
  574. RET_IF_ERROR( DEB_WARN, GetCacheEntry(pwzDRName, &iIndex, 0) );
  575. RET_IF_ERROR( DEB_TRACE, _Cache[iIndex].plga->GetTicketInfo(
  576. &(pti->gGuid),
  577. &(pti->kKey),
  578. &(pti->fTicketOpts) ));
  579. pti->fLogonSteps = _Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON;
  580. return(S_OK);
  581. }
  582. //+---------------------------------------------------------------------------
  583. //
  584. // Member: CPrincipalHandler::GetLogonInfo
  585. //
  586. // Synopsis: Gets the logon info (and ticket info) for a principal
  587. //
  588. // Effects: Cache lookup, increments the use count of returned principal
  589. //
  590. // Arguments: [pwzName] -- [in] name of principal
  591. // [pli] -- [out] logon info
  592. // [pti] -- [out] ticket info
  593. // [phHandle]-- [out] hint for Release, below.
  594. // [fLogon] -- [in] true if it's a logon attempt
  595. //
  596. // Requires:
  597. //
  598. // Returns:
  599. //
  600. // Signals:
  601. //
  602. // Modifies:
  603. //
  604. // Derivation:
  605. //
  606. // Algorithm:
  607. //
  608. // History: 04-Nov-93 WadeR Created
  609. //
  610. // Notes: This probes the memory returned from
  611. // CLogonAccount::GetLogonInfo().
  612. //
  613. // This routine will catch any exceptions thrown by it's callees, and return
  614. // and error code without leaking resources.
  615. //
  616. // The caller MUST call ReleaseLogonInfo with the handle returned from this
  617. // call when the logon info is no longer needed. The cache will not delete
  618. // the logon hours or the valid workstations until ReleaseLogonInfo is called.
  619. //
  620. //----------------------------------------------------------------------------
  621. #define PROBE_R_DWORD( _x_ ) ((void) (*((volatile long *)(_x_))))
  622. #define PROBE_R_CHAR( _x_ ) ((void) (*((volatile char *)(_x_))))
  623. HRESULT
  624. CPrincipalHandler::GetLogonInfo(PWCHAR pwzName,
  625. LogonInfo * pli,
  626. TicketInfo* pti,
  627. PULONG phHandle,
  628. BOOL fLogon )
  629. {
  630. KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::GetLogonInfo(%ws)\n",
  631. pwzName ));
  632. SafeAssert( _fInitialized );
  633. HRESULT hr;
  634. TRY
  635. {
  636. CReadLock lock (_Monitor);
  637. RtlZeroMemory( pli, sizeof( LogonInfo ) );
  638. RtlZeroMemory( pti, sizeof( TicketInfo ) );
  639. PWCHAR pwzDRName = MapNameDRN( pwzName );
  640. ULONG iIndex;
  641. hr = GetCacheEntry( pwzDRName,
  642. &iIndex,
  643. fLogon? CACHE_DOING_LOGON : 0);
  644. if (FAILED(hr))
  645. {
  646. KdcDebug(( DEB_TRACE, "GetCacheEntry(%ws)==%x\n",
  647. pwzDRName, hr ));
  648. goto Error;
  649. }
  650. *phHandle = iIndex;
  651. hr = _Cache[iIndex].plga->GetTicketInfo( &(pti->gGuid),
  652. &(pti->kKey),
  653. &(pti->fTicketOpts) );
  654. if (FAILED(hr))
  655. {
  656. KdcDebug(( DEB_TRACE, "GetTicketInfo(%ws)==%x\n",
  657. pwzDRName, hr ));
  658. goto Error;
  659. }
  660. pti->fLogonSteps = _Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON;
  661. hr = _Cache[iIndex].plga->GetLogonInfo( &(pli->pbValidLogonHours),
  662. &(pli->prpwszValidWorkstations),
  663. &(pli->fInteractive),
  664. &(pli->fAttributes),
  665. &(pli->ftAccountExpiry),
  666. &(pli->ftPasswordChange),
  667. &(pli->ftLockoutTime) );
  668. if (FAILED(hr))
  669. {
  670. KdcDebug(( DEB_TRACE, "GetLogonInfo(%ws)==%x\n",
  671. pwzDRName, hr ));
  672. goto Error;
  673. }
  674. //
  675. // Check that the pointers, etc, returned are valid.
  676. //
  677. if (pli->pbValidLogonHours)
  678. {
  679. PROBE_R_DWORD( pli->pbValidLogonHours );
  680. PROBE_R_DWORD( pli->pbValidLogonHours->pBlobData );
  681. PROBE_R_DWORD( pli->pbValidLogonHours->pBlobData +
  682. pli->pbValidLogonHours->cbSize );
  683. #if DBG
  684. if (KDCInfoLevel & DEB_T_CACHE)
  685. {
  686. SECURITY_STRING ss;
  687. ss = FormatBytes(pli->pbValidLogonHours->pBlobData,
  688. (BYTE) pli->pbValidLogonHours->cbSize );
  689. KdcDebug(( DEB_T_CACHE, "Valid logon hours (%d): %wZ\n",
  690. pli->pbValidLogonHours->cbSize, &ss ));
  691. SRtlFreeString( &ss );
  692. }
  693. #endif
  694. }
  695. if (pli->prpwszValidWorkstations)
  696. {
  697. PROBE_R_DWORD( pli->prpwszValidWorkstations );
  698. KdcDebug(( DEB_T_CACHE, "There are %d valid workstations\n",
  699. pli->prpwszValidWorkstations->cElems ));
  700. for (ULONG i=0; i<pli->prpwszValidWorkstations->cElems; i++ )
  701. {
  702. (void) wcslen( pli->prpwszValidWorkstations->pElems[i] );
  703. KdcDebug(( DEB_T_CACHE, "Valid workstation[%d] = %ws\n",
  704. i, pli->prpwszValidWorkstations->pElems[i] ));
  705. }
  706. }
  707. //
  708. // Bump my read lock up to a write lock, then increment the count.
  709. // Note that the lock is released at the end of this scope
  710. // automatically.
  711. //
  712. _Monitor.ReadToWrite();
  713. _Cache[iIndex].cUseCount++;
  714. _Monitor.WriteToRead();
  715. hr = S_OK;
  716. Error:
  717. // Fall out of the TRY/CATCH block.
  718. ;
  719. }
  720. CATCH( CException, e )
  721. {
  722. hr = e.GetErrorCode();
  723. KdcDebug(( DEB_ERROR, "Exception 0x%X getting and checking logon info for %ws\n",
  724. hr, pwzName ));
  725. }
  726. END_CATCH
  727. return(hr);
  728. }
  729. //+---------------------------------------------------------------------------
  730. //
  731. // Function: ReleaseLogonInfo
  732. //
  733. // Synopsis: Allows the cache entry to be deleted.
  734. //
  735. // Effects: Decrements the use count
  736. //
  737. // Arguments: [pwzName] -- Name of principal
  738. // [iHandle] -- handle returned from GetLogonInfo
  739. //
  740. // Returns: void
  741. //
  742. // History: 04-Nov-93 WadeR Created
  743. //
  744. // Notes: iHandle is just a hint. If it has been moved,
  745. // this will look through the cache for it.
  746. //
  747. //----------------------------------------------------------------------------
  748. void
  749. CPrincipalHandler::ReleaseLogonInfo( PWCHAR pwzName, ULONG iHandle )
  750. {
  751. SafeAssert( _fInitialized );
  752. _Monitor.GetRead();
  753. if ( (iHandle >= _cCache) ||
  754. wcsicmp( _Cache[iHandle].pwzName, pwzName ) != 0)
  755. {
  756. //
  757. // The cache entry must have been moved.
  758. //
  759. // It still has to be in the cache, because it's tagged as in-use.
  760. // This will be fast, and it must succeed, because it won't have to
  761. // hit the disk.
  762. //
  763. #if DBG
  764. HRESULT hr =
  765. #else
  766. (void)
  767. #endif
  768. GetCacheEntry( pwzName, &iHandle, 0, FALSE ); // No flags, don't load
  769. #if DBG
  770. Win4Assert( SUCCEEDED( hr ) );
  771. #endif
  772. }
  773. Win4Assert( _Cache[iHandle].cUseCount > 0 );
  774. _Monitor.ReadToWrite();
  775. _Cache[iHandle].cUseCount--;
  776. // If the CACHE_INVALID bit is set, and the CACHE_DOING_LOGON bit is NOT
  777. // set, and the use count is zero, then discard it.
  778. if (((_Cache[iHandle].fCacheFlags & (CACHE_INVALID | CACHE_DOING_LOGON))
  779. == CACHE_INVALID ) &&
  780. (_Cache[iHandle].cUseCount == 0) )
  781. {
  782. Discard( iHandle );
  783. }
  784. _Monitor.Release();
  785. }
  786. //+---------------------------------------------------------------------------
  787. //
  788. // Member: CPrincipalHandler::GetPAC
  789. //
  790. // Synopsis: Gets the PAC for a principal.
  791. //
  792. // Effects: Allocates memory, creates a CPAC
  793. //
  794. // Arguments: [pwzName] -- [in] principal to get pac for
  795. // [ppPAC] -- [out] pac
  796. //
  797. // Requires:
  798. //
  799. // Returns:
  800. //
  801. // Signals:
  802. //
  803. // Modifies:
  804. //
  805. // Derivation:
  806. //
  807. // Algorithm:
  808. //
  809. // History: 04-Nov-93 WadeR Created
  810. //
  811. // Notes:
  812. //
  813. // BUGBUG: Need to merge MikeSe's changes, comment better.
  814. //
  815. //----------------------------------------------------------------------------
  816. HRESULT
  817. CPrincipalHandler::GetPAC( PWCHAR pwzName, CPAC ** ppPAC )
  818. {
  819. KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::GetPAC(%ws)\n",
  820. pwzName ));
  821. SafeAssert( _fInitialized );
  822. PWCHAR pwzDRName = MapNameDRN( pwzName );
  823. SECID gSID;
  824. KerbKey kGarbage;
  825. ULONG dwGarbage;
  826. ULONG iIndex;
  827. CReadLock lock(_Monitor);
  828. RET_IF_ERROR( DEB_WARN, GetCacheEntry(pwzDRName, &iIndex, 0) ); // 0 -> no flags
  829. RET_IF_ERROR( DEB_WARN, _Cache[iIndex].plga->GetTicketInfo(
  830. &gSID,
  831. &kGarbage,
  832. &dwGarbage ) );
  833. *ppPAC = new CPAC;
  834. (*ppPAC)->Init( _Cache[iIndex].plga->GetGroupInfo(),
  835. gSID,
  836. pwzName,
  837. NULL ); // NT Sid.
  838. #if DBG
  839. // Why would anyone want to get thier PAC if they aren't doing
  840. // a login?
  841. if ((_Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON) == 0)
  842. {
  843. KdcDebug(( DEB_WARN, "Strange, \"%ws\" fetching a PAC outside login.\n",
  844. pwzName ));
  845. }
  846. #endif
  847. return(S_OK);
  848. }
  849. //+---------------------------------------------------------------------------
  850. //
  851. // Member: CPrincipalHandler::LogonComplete
  852. //
  853. // Synopsis: Calls LogonComplete on the CLogonAccount, saves it, releases it.
  854. //
  855. // Effects: releases memory.
  856. //
  857. // Arguments: [pwzName] -- [in] Name of principal who's finished logging on.
  858. // [fGood] -- [in] True if it's a successful logon.
  859. // [ftLock] -- [in] Account locked out until this time.
  860. //
  861. // Requires:
  862. //
  863. // Returns:
  864. //
  865. // Signals:
  866. //
  867. // Modifies:
  868. //
  869. // Derivation:
  870. //
  871. // Algorithm:
  872. //
  873. // History: 04-Nov-93 WadeR Created
  874. //
  875. // Notes: This catches any exceptions, and passes back error codes.
  876. // This should check the CACHE_INVALID bit and, if set, discard
  877. // the entry. But, since this always discards the entry it can
  878. // safely ignore the bit.
  879. //
  880. //----------------------------------------------------------------------------
  881. HRESULT
  882. CPrincipalHandler::LogonComplete( PWCHAR pwzName,
  883. BOOL fGood,
  884. FILETIME ftLock )
  885. {
  886. SafeAssert( _fInitialized );
  887. HRESULT hr;
  888. KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::LogonComplete(%ws)\n",
  889. pwzName ));
  890. TRY
  891. {
  892. ULONG iIndex;
  893. PWCHAR pwzDRName = MapNameDRN( pwzName );
  894. CReadLock lock(_Monitor);
  895. hr = GetCacheEntry(pwzDRName, &iIndex, 0); // no special flags
  896. if (SUCCEEDED(hr))
  897. {
  898. _Monitor.ReadToWrite();
  899. if ((_Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON) == 0)
  900. {
  901. // Oops! We aged this principal out of the cache already.
  902. // This means that the principal took so long to log on, we
  903. // decided that it was a failed logon attempt and we've
  904. // already marked it as such. So we'll just ignore this call
  905. // to LogonComplete.
  906. KdcDebug(( DEB_ERROR, "Someone called LogonComplete when "
  907. "they weren't logging on!\n" ));
  908. }
  909. else
  910. {
  911. _Cache[iIndex].plga->LogonComplete( fGood, &ftLock );
  912. _Cache[iIndex].plga->Save(NULL, FALSE);
  913. }
  914. ReleaseCacheEntry( iIndex );
  915. hr = ShrinkCache();
  916. }
  917. else
  918. {
  919. KdcDebug(( DEB_WARN, "GetCacheEntry(%ws) failed 0x%x.\n",
  920. pwzDRName, hr ));
  921. }
  922. }
  923. CATCH( CException, e )
  924. {
  925. hr = e.GetErrorCode();
  926. KdcDebug(( DEB_ERROR, "Exception 0x%X in LogonComplete( %ws )\n",
  927. hr, pwzName ));
  928. }
  929. END_CATCH
  930. return(hr);
  931. }
  932. //+---------------------------------------------------------------------------
  933. //
  934. // Member: CPrincipalHandler::ClearCache
  935. //
  936. // Synopsis: Removes one or more cache entries
  937. //
  938. // Effects: May call LogonComplete on some entries.
  939. //
  940. // Arguments: [pwzName] -- Name of principal. If null, clears everything.
  941. // [fForce] -- If true, remove everything, even pending logons.
  942. //
  943. // Requires:
  944. //
  945. // Returns:
  946. //
  947. // Signals:
  948. //
  949. // Modifies:
  950. //
  951. // Derivation:
  952. //
  953. // Algorithm:
  954. //
  955. // History: 05-Nov-93 WadeR Created
  956. //
  957. // Notes:
  958. //
  959. //----------------------------------------------------------------------------
  960. HRESULT
  961. CPrincipalHandler::ClearCache(PWCHAR pwzName, BOOL fForce)
  962. {
  963. KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::ClearCache(%ws)\n", pwzName ));
  964. SafeAssert( _fInitialized );
  965. // Flag to indicate the principal should be reloaded, because it was
  966. // in the middle of a logon.
  967. BOOL fReload = FALSE;
  968. if (pwzName)
  969. {
  970. ULONG iIndex;
  971. // Get a read lock so we can call GetCacheEntry
  972. _Monitor.GetRead();
  973. // Get the cache entry, but don't load it if it isn't there.
  974. if (GetCacheEntry(pwzName, &iIndex, 0, FALSE) == S_OK)
  975. {
  976. // It was found in the cache.
  977. // We are going to change it, either to mark it as invalid or to
  978. // delete it, so upgrade our lock.
  979. _Monitor.ReadToWrite();
  980. // If fForce is true, or the cache entry is in use, we can discard
  981. // it. If it's in use, and fForce isn't set, simply mark it as
  982. // invalid.
  983. if (fForce || (_Cache[iIndex].cUseCount == 0 ))
  984. {
  985. if (_Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON)
  986. {
  987. // This principal was in the process of logging on, so
  988. // reload the account (so LogonComplete can succeed),
  989. // unless fForce is true (because we don't want it to
  990. // reappear).
  991. fReload = !fForce;
  992. }
  993. ReleaseCacheEntry( iIndex );
  994. }
  995. else
  996. {
  997. _Cache[iIndex].fCacheFlags |= CACHE_INVALID;
  998. }
  999. //
  1000. // If we flushed an account that was in the process of logging on,
  1001. // we should re-load it.
  1002. //
  1003. if (fReload)
  1004. {
  1005. ULONG iFoo;
  1006. _Monitor.WriteToRead();
  1007. (void) GetCacheEntry( pwzName, &iFoo, CACHE_DOING_LOGON );
  1008. }
  1009. }
  1010. _Monitor.Release();
  1011. }
  1012. else
  1013. {
  1014. // Some of the cache entries will be in the middle of a logon. We
  1015. // must save the names away, so they can be re-loaded later.
  1016. //
  1017. // Since the cache is fixed size, we know the maximim number of names.
  1018. PWCHAR * apwzNames = new PWCHAR [_cCacheMaxSize];
  1019. ULONG cNamesUsed = 0;
  1020. // Going to delete everything, so get the lock once up front.
  1021. _Monitor.GetWrite();
  1022. //
  1023. // This loop is a little strange in that both ends move. The
  1024. // Discard() method will move the empty slot to the end of the cache,
  1025. // and decrement _cCache. Since it moves a new cache entry to the
  1026. // vacated spot, we don't want to increment the index if we discard
  1027. // something.
  1028. //
  1029. // On the other hand, if we don't discard the entry, we must increment
  1030. // the index to look at the next entry.
  1031. //
  1032. // Every iteration of the loop gets one step closer to finishing,
  1033. // either by raising the index or lowering the max.
  1034. ULONG iIndex = 0;
  1035. while (iIndex < _cCache)
  1036. {
  1037. if (fForce || (_Cache[iIndex].cUseCount == 0 ))
  1038. {
  1039. if (!fForce && _Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON)
  1040. {
  1041. apwzNames[cNamesUsed++] = _Cache[iIndex].pwzName;
  1042. _Cache[iIndex].pwzName = 0; // so it isn't deleted by Discard.
  1043. }
  1044. // This will decrement _cCache
  1045. ReleaseCacheEntry( iIndex );
  1046. }
  1047. else
  1048. {
  1049. _Cache[iIndex].fCacheFlags |= CACHE_INVALID;
  1050. iIndex++;
  1051. }
  1052. }
  1053. //
  1054. // Finished clearing the cache, so re-load the principals that were in
  1055. // the middle of a logon.
  1056. //
  1057. _Monitor.WriteToRead();
  1058. for (iIndex=0; iIndex < cNamesUsed; iIndex++)
  1059. {
  1060. ULONG iFoo;
  1061. (void) GetCacheEntry( apwzNames[iIndex], &iFoo, CACHE_DOING_LOGON);
  1062. delete apwzNames[iIndex];
  1063. }
  1064. delete apwzNames;
  1065. _Monitor.Release();
  1066. }
  1067. if ( !((pwzName == NULL) && fForce) )
  1068. {
  1069. //
  1070. // If we are forcing everything out of the cache, we shouldn't be
  1071. // loading new stuff again. Otherwise we can count on these being
  1072. // useful.
  1073. //
  1074. // Load the frequently used accounts.
  1075. //
  1076. //
  1077. ULONG iFoo;
  1078. _Monitor.GetRead();
  1079. RET_IF_ERROR( DEB_ERROR, GetCacheEntry( pwcKdcPrincipal, &iFoo, CACHE_STICKY ));
  1080. RET_IF_ERROR( DEB_ERROR, GetCacheEntry( pwcPSPrincipal, &iFoo, CACHE_STICKY ));
  1081. _Monitor.Release();
  1082. //
  1083. // Load the ticket info for the KDC and PS
  1084. //
  1085. TicketInfo tiKDC;
  1086. TicketInfo tiPS;
  1087. RET_IF_ERROR( DEB_ERROR, GetTicketInfo( pwcKdcPrincipal, &tiKDC ) );
  1088. RET_IF_ERROR( DEB_ERROR, GetTicketInfo( pwcPSPrincipal, &tiPS ) );
  1089. _Monitor.GetWrite();
  1090. _tiKdc = tiKDC;
  1091. _tiPS = tiPS;
  1092. _Monitor.Release();
  1093. }
  1094. _Monitor.GetWrite();
  1095. HRESULT hr = ShrinkCache();
  1096. _Monitor.Release();
  1097. #if DBG
  1098. if (FAILED(hr))
  1099. {
  1100. KdcDebug(( DEB_ERROR, "ShrinkCache() == 0x%x\n", hr ));
  1101. }
  1102. #endif
  1103. return(hr);
  1104. }
  1105. //+---------------------------------------------------------------------------
  1106. //
  1107. // Member: CPrincipalHandler::AgeCache
  1108. //
  1109. // Synopsis: Removes old entries from the cache.
  1110. //
  1111. // Effects: May release lots of cache slots.
  1112. //
  1113. // Arguments: (none)
  1114. //
  1115. // Returns: S_OK
  1116. //
  1117. // History: 04-Nov-93 WadeR Created
  1118. //
  1119. // Notes:
  1120. //
  1121. //----------------------------------------------------------------------------
  1122. HRESULT
  1123. CPrincipalHandler::AgeCache()
  1124. {
  1125. KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::AgeCache()\n" ));
  1126. SafeAssert( _fInitialized );
  1127. ULONG i;
  1128. int cRemoved = 0;
  1129. TimeStamp tsCutoff;
  1130. GetCurrentTimeStamp( &tsCutoff );
  1131. tsCutoff = tsCutoff - _tsMaxAge;
  1132. CWriteLock lock( _Monitor );
  1133. for (i=0; i<_cCache; i++)
  1134. {
  1135. if (!(_Cache[i].fCacheFlags & CACHE_STICKY) &&
  1136. (_Cache[i].cUseCount == 0) &&
  1137. (_Cache[i].tsLastUsed < tsCutoff))
  1138. {
  1139. Discard(i);
  1140. cRemoved++;
  1141. }
  1142. }
  1143. HRESULT hr = ShrinkCache();
  1144. KdcDebug(( DEB_T_CACHE, "AgeCache() removed %d\n", cRemoved ));
  1145. return(hr);
  1146. }
  1147. //+---------------------------------------------------------------------------
  1148. //
  1149. // Member: CPrincipalHandler::Init
  1150. //
  1151. // Synopsis: Initializes the principal handler
  1152. //
  1153. // Effects: Calls CoInitialize, loads PS and KDC info.
  1154. //
  1155. // Arguments: (none)
  1156. //
  1157. // Requires:
  1158. //
  1159. // Returns: HRESULT
  1160. //
  1161. // History: 04-Nov-93 WadeR Created
  1162. //
  1163. // Notes:
  1164. //
  1165. // BUGBUG: If this fails, it could leak resources.
  1166. //
  1167. //----------------------------------------------------------------------------
  1168. HRESULT
  1169. CPrincipalHandler::Init()
  1170. {
  1171. KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::Init()\n" ));
  1172. if (_fInitialized)
  1173. {
  1174. Win4Assert( !"CPrincipalHandler::Init() called twice!" );
  1175. return(E_UNEXPECTED);
  1176. }
  1177. RET_IF_ERROR(DEB_ERROR, _sci.Init() );
  1178. _tsMaxAge = tsZero;
  1179. AddSecondsToTimeStamp( &_tsMaxAge, 60 * 60 ); // BUGBUG: magic numbers.
  1180. //
  1181. // Initialize the cache
  1182. //
  1183. _cCache = 0;
  1184. _cCacheMaxSize = CACHE_INITIALSIZE / sizeof( CacheEntry );
  1185. _Cache = new (NullOnFail) CacheEntry [ _cCacheMaxSize ];
  1186. if (_Cache == NULL)
  1187. {
  1188. return(E_OUTOFMEMORY);
  1189. }
  1190. _fInitialized = TRUE;
  1191. // This will load the KDC and PS info.
  1192. RET_IF_ERROR( DEB_ERROR, ClearCache() );
  1193. //
  1194. // Load the ticket info for the KDC and PS
  1195. //
  1196. TicketInfo tiKDC;
  1197. TicketInfo tiPS;
  1198. RET_IF_ERROR( DEB_ERROR, GetTicketInfo( pwcKdcPrincipal, &tiKDC ) );
  1199. RET_IF_ERROR( DEB_ERROR, GetTicketInfo( pwcPSPrincipal, &tiPS ) );
  1200. _Monitor.GetWrite();
  1201. _tiKdc = tiKDC;
  1202. _tiPS = tiPS;
  1203. _Monitor.Release();
  1204. return(S_OK);
  1205. }