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
1412 lines
40 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1993.
|
|
//
|
|
// File: prcache.cxx
|
|
//
|
|
// Contents: Principal cache handling code
|
|
//
|
|
// Classes: CPrincipalHandler
|
|
//
|
|
// History: 4-02-93 WadeR Created
|
|
// 04-Nov-93 WadeR Updated to hold CLogonAccounts
|
|
// 08-Nov-93 WadeR Removed altogether in favour of Account.dll caching. Sigh.
|
|
//
|
|
// Notes: This is the principal cache code.
|
|
//
|
|
// When you first call GetASTicket, it passes in a flag indicating a logon is
|
|
// in progress. This flag migrates to the created cache entry, and is passed
|
|
// out in the TicketInfo when you call GetTicketInfo().
|
|
//
|
|
// When you call TGSTicket with a PAC, it checks the flag. If it's set, it
|
|
// calls LogonComplete(success).
|
|
//
|
|
// LogonComplete will clear the flag, and delete the entry from the cache.
|
|
//
|
|
// When the cache is full and another entry is needed the first pass looks for
|
|
// an entry that is not in the middle of a login. If it finds one, then that
|
|
// is released, and used. If it can't find one, it finds the oldest logon
|
|
// pending, and calls LogonComplete(fail) on it, and uses it.
|
|
//
|
|
// If ClearCache() attempts to remove an entry with a non-zero use count
|
|
// (indicating that someone has called GetLogonInfo and not ReleaseLogonInfo),
|
|
// that entry is tagged as invalid and left alone. Once the use count goes
|
|
// down to zero, it will be discarded.
|
|
//
|
|
// BUGBUG: Need some form of notification from the Account code to
|
|
// tell me that an account changed. Also, need to defer
|
|
// re-loading it if it's in use.
|
|
//
|
|
// BUGBUG: Should change Win4Assert to SafeAssert or ASSERT().
|
|
//
|
|
// BUGBUG: Should remove PrintSizes();
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// Get the common, global header files.
|
|
#include <secpch2.hxx>
|
|
#pragma hdrstop
|
|
|
|
// Place any local #includes files here.
|
|
|
|
#include "secdata.hxx"
|
|
#include <princpl.hxx>
|
|
#include <prcache.hxx>
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
// Global data
|
|
//
|
|
|
|
CPrincipalHandler PrincipalCache;
|
|
|
|
const PWCHAR pwcKdcPrincipal = L"\\KDC";
|
|
const PWCHAR pwcPSPrincipal = L"\\PrivSvr";
|
|
|
|
|
|
// Constants to control cache size. The cache will start out being
|
|
// CACHE_INITIALSIZE bytes (rounded down to a multiple of sizeof(CacheEntry)).
|
|
//
|
|
// When the cache needs to grow, it will grow by CACHE_GROW bytes (also
|
|
// rounded down). When it is CACHE_SHRINK_THRESHOLD bytes larger than needed,
|
|
// it will shrink to become the minumum size needed plus CACHE_SHRINK bytes.
|
|
//
|
|
// Caveats:
|
|
//
|
|
// If CACHE_GROW > CACHE_SHRINK_THRESHOLD, every time the cache grows it will
|
|
// shrink again. If CACHE_SHRINK >= CACHE_SHRINK_THRESHOLD, the cache will
|
|
// shrink over and over again, without changing size. Finally, If
|
|
// CACHE_SHRINK_THRESHOLD < CACHE_INITIALSIZE then the cache will shrink on
|
|
// startup (which is not a smart thing to do).
|
|
//
|
|
// I suggest that CACHE_SHRINK_THRESHOLD >= 2 * CACHE_SHRINK
|
|
// CACHE_SHRINK_THRESHOLD >= 2 * CACHE_GROW
|
|
// CACHE_SHRINK_THRESHOLD > CACHE_INITIALSIZE
|
|
// CACHE_SHRINK ~= CACHE_GROW
|
|
//
|
|
// Currently, sizeof( CacheEntry ) == 24 bytes.
|
|
|
|
|
|
#if 0
|
|
#define CACHE_INITIALSIZE 1024
|
|
#define CACHE_GROW 512
|
|
#define CACHE_SHRINK_THRESHOLD 1536
|
|
#define CACHE_SHRINK 512
|
|
#else
|
|
#pragma MEMO( Really tiny cache sizes for testing )
|
|
#define CACHE_INITIALSIZE 50
|
|
#define CACHE_GROW 25
|
|
#define CACHE_SHRINK_THRESHOLD 75
|
|
#define CACHE_SHRINK 25
|
|
#endif
|
|
|
|
//
|
|
// Flags for fCacheFlags.
|
|
//
|
|
// Note that CACHE_DOING_LOGON (flag for fCacheFlags) has the same value as
|
|
// DOING_LOGON (flag for TicketInfo::LogonSteps).
|
|
//
|
|
#define CACHE_DOING_LOGON DOING_LOGON
|
|
#define CACHE_STICKY 0x00010000
|
|
#define CACHE_INVALID 0x00080000
|
|
|
|
|
|
//
|
|
// Helper functinons (only used by this file).
|
|
//
|
|
|
|
|
|
#define PrintSizes() KdcDebug(( DEB_T_CACHE, "Line %d: cCache=%d, cMaxCacheSize=%d\n", \
|
|
__LINE__, _cCache, _cCacheMaxSize ))
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: MapNameDRN
|
|
//
|
|
// Synopsis: Maps a name to a domain relative name
|
|
//
|
|
// Effects: may orphan memory (indicated by an error message)
|
|
//
|
|
// Arguments: [pwzInName] -- Name to map
|
|
//
|
|
// Returns: pointer into string passed in, or new memory.
|
|
//
|
|
// History: 14-Sep-93 WadeR Created
|
|
//
|
|
// Notes:
|
|
//
|
|
// BUGBUG: The client should be fixed to NEVER pass in a bogus name,
|
|
// so this should be able to map in place or return an error.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
static PWCHAR
|
|
MapNameDRN( PWCHAR pwzInName )
|
|
{
|
|
if (*pwzInName == L'\\')
|
|
return(pwzInName);
|
|
|
|
if (wcsnicmp(pwzInName,
|
|
SecData.KdcRealm()->Buffer,
|
|
SecData.KdcRealm()->Length / sizeof(WCHAR) ) == 0 )
|
|
{
|
|
// Starts with this domain.
|
|
return(pwzInName + SecData.KdcRealm()->Length / sizeof(WCHAR) );
|
|
}
|
|
ULONG cch = wcslen( awcDOMAIN ) - 1; // - 1 because don't want the '\'
|
|
if (wcsnicmp( pwzInName, awcDOMAIN, cch ) == 0)
|
|
{
|
|
// Starts with "domain:"
|
|
return(pwzInName + cch);
|
|
}
|
|
|
|
KdcDebug(( DEB_WARN, "MapNameDRN(%ws): Don't understand (prepending '\\').\n",
|
|
pwzInName ));
|
|
KdcDebug(( DEB_WARN, "MapNameDRN: Memory leak.\n" ));
|
|
|
|
PWCHAR pwNew = new WCHAR [wcslen(pwzInName) + 2];
|
|
pwNew[0] = '\\';
|
|
pwNew[1] = '\0';
|
|
wcscat( pwNew, pwzInName );
|
|
|
|
return(pwNew);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Private member functions.
|
|
//
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::ReleaseCacheEntry
|
|
//
|
|
// Synopsis: Removes a cache slot, releasing if needed.
|
|
//
|
|
// Effects: May call CLogonAccount::Release()
|
|
//
|
|
// Arguments: [i] -- Slot to release
|
|
//
|
|
// Requires: Caller must have write access to _Monitor
|
|
//
|
|
// Returns: void
|
|
//
|
|
// History: 04-Nov-93 WadeR Created
|
|
//
|
|
// Notes: This moves the empty slot to the end.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
CPrincipalHandler::ReleaseCacheEntry( ULONG i )
|
|
{
|
|
KdcDebug(( DEB_T_CACHE, "Releasing slot %d.\n", i ));
|
|
|
|
if (_Cache[i].plga != NULL)
|
|
{
|
|
_Cache[i].plga->Release();
|
|
}
|
|
if (_Cache[i].pwzName)
|
|
{
|
|
delete _Cache[i].pwzName;
|
|
}
|
|
|
|
// Move the empty spot to the end of the array.
|
|
|
|
_cCache--;
|
|
Win4Assert( _cCache >= 0 );
|
|
_Cache[i] = _Cache[_cCache];
|
|
|
|
// Zero out the new empty spot
|
|
_Cache[_cCache].plga = 0;
|
|
_Cache[_cCache].pwzName = 0;
|
|
|
|
PrintSizes();
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::Discard
|
|
//
|
|
// Synopsis: Removes a cache entry, completing it's logon if needed.
|
|
//
|
|
// Effects: may call CLogonAccount::LogonComplete()
|
|
//
|
|
// Arguments: [i] -- Slot to discard
|
|
//
|
|
// Requires: Caller must have write access to _Monitor
|
|
//
|
|
// Returns: void
|
|
//
|
|
// History: 04-Nov-93 WadeR Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
CPrincipalHandler::Discard( int i )
|
|
{
|
|
KdcDebug(( DEB_T_CACHE, "Discarding %ws from cache slot %d%s.\n",
|
|
_Cache[i].pwzName, i,
|
|
(_Cache[i].fCacheFlags & CACHE_DOING_LOGON)?
|
|
" (logon interupted)":
|
|
"" ));
|
|
|
|
// If it's a logon, then mark it as failed.
|
|
// ReleaseCacheEntry will remove the entry, and move the
|
|
// empty spot to the end.
|
|
|
|
if (_Cache[i].fCacheFlags & CACHE_DOING_LOGON)
|
|
{
|
|
_Cache[i].plga->LogonComplete( FALSE, (FILETIME*)&tsZero ); // failed logon, no lockout
|
|
_Cache[i].plga->Save(NULL, FALSE);
|
|
}
|
|
ReleaseCacheEntry( i );
|
|
|
|
PrintSizes();
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::GrowCache
|
|
//
|
|
// Synopsis: Grows the principal cache.
|
|
//
|
|
// Effects: Allocates memory
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// Requires: Caller must have a write lock on the cache.
|
|
//
|
|
// Returns: HRESULT (S_OK or E_OUTOFMEMORY)
|
|
//
|
|
// Signals: none
|
|
//
|
|
// Algorithm: Adds GROW_SIZE bytes to the cache, rounded down to
|
|
// sizeof(CacheElement).
|
|
//
|
|
// History: 05-Nov-93 WadeR Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPrincipalHandler::GrowCache()
|
|
{
|
|
SafeAssert( _cCacheMaxSize >= _cCache );
|
|
|
|
ULONG cNewSize = (_cCacheMaxSize * sizeof( CacheEntry ) + CACHE_GROW)
|
|
/ sizeof( CacheEntry );
|
|
|
|
KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::GrowCache() %d -> %d\n",
|
|
_cCacheMaxSize, cNewSize ));
|
|
|
|
PrintSizes();
|
|
|
|
SafeAssert( cNewSize > _cCache );
|
|
|
|
CacheEntry * pNew = new (NullOnFail) CacheEntry [cNewSize];
|
|
if (pNew == NULL)
|
|
{
|
|
KdcDebug(( DEB_ERROR, "Out of memory.\n" ));
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
RtlCopyMemory( (PBYTE) pNew, (PBYTE) _Cache, _cCache * sizeof( CacheEntry ) );
|
|
delete _Cache;
|
|
_Cache = pNew;
|
|
_cCacheMaxSize = cNewSize;
|
|
|
|
PrintSizes();
|
|
return(S_OK);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::ShrinkCache
|
|
//
|
|
// Synopsis: Shrinks the principal cache, if needed.
|
|
//
|
|
// Effects: Allocates memory
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// Requires: Caller must have a write lock on the cache.
|
|
//
|
|
// Returns: HRESULT (S_OK or E_OUTOFMEMORY)
|
|
//
|
|
// Signals: none
|
|
//
|
|
// Algorithm: If the cache is SHRINK_THRESHOLD bytes too large, resizes
|
|
// it to be just SHRINK_SIZE bytes bigger than needed, rounded
|
|
// down to sizeof(CacheElement).
|
|
//
|
|
// History: 05-Nov-93 WadeR Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPrincipalHandler::ShrinkCache()
|
|
{
|
|
KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::ShrinkCache(%d)\n", _cCacheMaxSize ));
|
|
|
|
|
|
PrintSizes();
|
|
SafeAssert( _cCacheMaxSize >= _cCache );
|
|
|
|
if ( (_cCacheMaxSize - _cCache) >
|
|
CACHE_SHRINK_THRESHOLD / sizeof( CacheEntry ) )
|
|
{
|
|
// Need to shrink the cache.
|
|
ULONG cNewSize = (_cCache * sizeof( CacheEntry ) + CACHE_SHRINK)
|
|
/ sizeof( CacheEntry );
|
|
|
|
KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::ShrinkCache() %d -> %d\n",
|
|
_cCacheMaxSize, cNewSize ));
|
|
|
|
SafeAssert( cNewSize > _cCache );
|
|
|
|
CacheEntry * pNew = new (NullOnFail) CacheEntry [cNewSize];
|
|
if (pNew == NULL)
|
|
{
|
|
KdcDebug(( DEB_ERROR, "Out of memory.\n" ));
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
RtlCopyMemory( (PBYTE) pNew, (PBYTE) _Cache, _cCache * sizeof( CacheEntry ) );
|
|
delete _Cache;
|
|
_Cache = pNew;
|
|
_cCacheMaxSize = cNewSize;
|
|
}
|
|
|
|
PrintSizes();
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::PurgeSomething
|
|
//
|
|
// Synopsis: Makes at least one entry in the cache free.
|
|
//
|
|
// Effects: May release some cache entries, may grow the cache.
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// Requires: _Monitor to be acquired for writing.
|
|
//
|
|
// Algorithm: Deletes the oldest cache element.
|
|
//
|
|
// History: 04-Nov-93 WadeR Created
|
|
//
|
|
// Notes: This function guarentees that on return, there is at least
|
|
// one empty cache entry for the caller to use. To do this,
|
|
// the caller must know that no other thread will think it can
|
|
// use that slot. Therefore the caller must have write access
|
|
// to the cache.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPrincipalHandler::PurgeSomething()
|
|
{
|
|
KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::PurgeSomething()\n" ));
|
|
|
|
PrintSizes();
|
|
|
|
ULONG i;
|
|
TimeStamp tsOldest = tsInfinity;
|
|
ULONG iOldest = 0;
|
|
|
|
//
|
|
// First pass, only consider entries that are not part of
|
|
// a logon in progress.
|
|
//
|
|
|
|
for (i=0; i<_cCache; i++)
|
|
{
|
|
if (!(_Cache[i].fCacheFlags &
|
|
(CACHE_DOING_LOGON | CACHE_STICKY)) &&
|
|
(_Cache[i].cUseCount == 0) &&
|
|
(_Cache[i].tsLastUsed < tsOldest))
|
|
{
|
|
iOldest = i;
|
|
tsOldest = _Cache[i].tsLastUsed;
|
|
}
|
|
}
|
|
|
|
if (tsOldest == tsInfinity)
|
|
{
|
|
//
|
|
// Didn't find anything that could be removed.
|
|
//
|
|
|
|
return GrowCache();
|
|
}
|
|
|
|
//
|
|
// We have found the one to boot out.
|
|
//
|
|
// Convert the lock to write before discarding it. Since we like to leave
|
|
// things the way we found them, convert it back when we're done.
|
|
//
|
|
|
|
Discard( iOldest );
|
|
PrintSizes();
|
|
return(S_OK);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::GetCacheEntry
|
|
//
|
|
// Synopsis: Finds something in the cache, adding it if needed and if
|
|
// requested.
|
|
//
|
|
// Effects: May call GetLogonAccount to insert in the cache.
|
|
// May remove something from the cache to make room.
|
|
//
|
|
// Arguments: [pwzName] -- [in] name of principal
|
|
// [piIndex] -- [out] cache index
|
|
// [fCacheFlags] -- [in] Flags to put in created cache entry
|
|
// [fLoad] -- [in] if true, load missing entry.
|
|
//
|
|
// Signals: nothing (unless GetLogonAccount throws something).
|
|
//
|
|
// Requires: Caller must have read access to _Monitor
|
|
//
|
|
// Returns: S_OK if in cache, else result of GetLogonAccount()
|
|
// KDC_E_C_PRINCIPAL_UNKNOWN if it isn't in the cache and
|
|
// fLoad == FALSE
|
|
//
|
|
// History: 04-Nov-93 WadeR Created
|
|
//
|
|
// Notes: This code is not exception-safe. If GetLogonAccount throws
|
|
// an exception, it will leak resources.
|
|
//
|
|
// This routine returns an index into the cache. This index must remain
|
|
// valid, so you can't allow anyone to change the cache while it's in use.
|
|
// Therefore the caller must hold a read lock on the cache, and release it
|
|
// once the caller is done with the index returned.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPrincipalHandler::GetCacheEntry( PWCHAR pwzName,
|
|
PULONG piIndex,
|
|
ULONG fCacheFlags,
|
|
BOOL fLoad )
|
|
{
|
|
KdcDebug(( DEB_TRACE, "Looking for account %ws (DRN)\n", pwzName ));
|
|
|
|
// Scan through the cache entries looking for this name.
|
|
|
|
for (ULONG i=0; i<_cCache; i++ )
|
|
{
|
|
if (!wcsicmp(_Cache[i].pwzName, pwzName ))
|
|
{
|
|
//
|
|
// We found the cache entry we are interested in.
|
|
//
|
|
|
|
*piIndex = i;
|
|
|
|
//
|
|
// Convert the monitor to write access, so we can modify
|
|
// the last access time.
|
|
//
|
|
|
|
_Monitor.ReadToWrite();
|
|
|
|
GetCurrentTimeStamp( &_Cache[i].tsLastUsed );
|
|
|
|
// Return the monitor and leave.
|
|
|
|
_Monitor.WriteToRead();
|
|
return(S_OK);
|
|
}
|
|
}
|
|
|
|
// Didn't find it in the cache.
|
|
|
|
KdcDebug(( DEB_T_CACHE, "Didn't find %ws in the cache.\n", pwzName ));
|
|
|
|
if (!fLoad)
|
|
{
|
|
return(KDC_E_C_PRINCIPAL_UNKNOWN);
|
|
}
|
|
|
|
//
|
|
// Build the information that we are going to write to the
|
|
// Cache now, before upgrading to a write lock on the monitor.
|
|
// This way, we keep an exclusive lock for as little time as
|
|
// possible.
|
|
//
|
|
|
|
//
|
|
// Copy the name, so it stays valid regardless of what the caller
|
|
// does with it.
|
|
//
|
|
|
|
PWCHAR pwzNameCopy = new (NullOnFail) WCHAR [wcslen( pwzName ) + 1];
|
|
if (pwzNameCopy == 0)
|
|
{
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
wcscpy( pwzNameCopy, pwzName );
|
|
|
|
CLogonAccount* plga;
|
|
|
|
//
|
|
// Get the account object
|
|
//
|
|
|
|
HRESULT hr = GetLogonAccount( pwzName,
|
|
TRUE, // Domain namespace
|
|
&plga );
|
|
if (FAILED(hr))
|
|
{
|
|
KdcDebug(( DEB_WARN, "Error finding principal '%ws' (0x%X)\n",
|
|
pwzName, hr ));
|
|
delete pwzNameCopy;
|
|
return(hr);
|
|
}
|
|
|
|
TimeStamp tsNow;
|
|
GetCurrentTimeStamp( &tsNow );
|
|
|
|
_Monitor.ReadToWrite();
|
|
|
|
if (_cCache == _cCacheMaxSize)
|
|
{
|
|
// Cache is full, so must purge something.
|
|
PurgeSomething();
|
|
}
|
|
|
|
//
|
|
// The last entry is free now.
|
|
//
|
|
//
|
|
// BUGBUG: PurgeSomething could run out of memory.
|
|
//
|
|
|
|
SafeAssert( _cCache < _cCacheMaxSize );
|
|
|
|
//
|
|
// Insert the data we constructed before.
|
|
//
|
|
|
|
_Cache[_cCache].pwzName = pwzNameCopy;
|
|
_Cache[_cCache].plga = plga;
|
|
_Cache[_cCache].tsLastUsed = tsNow;
|
|
_Cache[_cCache].fCacheFlags = fCacheFlags;
|
|
|
|
*piIndex = _cCache;
|
|
_cCache++;
|
|
|
|
_Monitor.WriteToRead();
|
|
|
|
// Subtract 1 because we've already incremented the count (inside the
|
|
// monitor).
|
|
|
|
KdcDebug(( DEB_T_CACHE, "Added %ws in slot %d (flags:%x)\n",
|
|
_Cache[_cCache-1].pwzName, _cCache-1,
|
|
_Cache[_cCache-1].fCacheFlags ));
|
|
|
|
PrintSizes();
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Public methods
|
|
//
|
|
//
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::GetTicketInfo
|
|
//
|
|
// Synopsis: Gets the ticket-granting info for a principal
|
|
//
|
|
// Effects: Cache lookup.
|
|
//
|
|
// Arguments: [pwzName] -- [in] Name of principal
|
|
// [pti] -- [out] ticket info
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 04-Nov-93 WadeR Created
|
|
//
|
|
// Notes: This routine is exception-safe. If any of the routines it
|
|
// calls (GetCacheEntry, CLogonAccount::GetTicketInfo) throw,
|
|
// it will pass the exception up and not leak resources.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPrincipalHandler::GetTicketInfo( PWCHAR pwzName, TicketInfo* pti )
|
|
{
|
|
KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::GetTicketInfo(%ws)\n",
|
|
pwzName ));
|
|
|
|
SafeAssert( _fInitialized );
|
|
|
|
ULONG iIndex;
|
|
PWCHAR pwzDRName = MapNameDRN( pwzName );
|
|
|
|
CReadLock lock( _Monitor );
|
|
|
|
RET_IF_ERROR( DEB_WARN, GetCacheEntry(pwzDRName, &iIndex, 0) );
|
|
|
|
RET_IF_ERROR( DEB_TRACE, _Cache[iIndex].plga->GetTicketInfo(
|
|
&(pti->gGuid),
|
|
&(pti->kKey),
|
|
&(pti->fTicketOpts) ));
|
|
|
|
pti->fLogonSteps = _Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON;
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::GetLogonInfo
|
|
//
|
|
// Synopsis: Gets the logon info (and ticket info) for a principal
|
|
//
|
|
// Effects: Cache lookup, increments the use count of returned principal
|
|
//
|
|
// Arguments: [pwzName] -- [in] name of principal
|
|
// [pli] -- [out] logon info
|
|
// [pti] -- [out] ticket info
|
|
// [phHandle]-- [out] hint for Release, below.
|
|
// [fLogon] -- [in] true if it's a logon attempt
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 04-Nov-93 WadeR Created
|
|
//
|
|
// Notes: This probes the memory returned from
|
|
// CLogonAccount::GetLogonInfo().
|
|
//
|
|
// This routine will catch any exceptions thrown by it's callees, and return
|
|
// and error code without leaking resources.
|
|
//
|
|
// The caller MUST call ReleaseLogonInfo with the handle returned from this
|
|
// call when the logon info is no longer needed. The cache will not delete
|
|
// the logon hours or the valid workstations until ReleaseLogonInfo is called.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#define PROBE_R_DWORD( _x_ ) ((void) (*((volatile long *)(_x_))))
|
|
#define PROBE_R_CHAR( _x_ ) ((void) (*((volatile char *)(_x_))))
|
|
|
|
HRESULT
|
|
CPrincipalHandler::GetLogonInfo(PWCHAR pwzName,
|
|
LogonInfo * pli,
|
|
TicketInfo* pti,
|
|
PULONG phHandle,
|
|
BOOL fLogon )
|
|
{
|
|
KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::GetLogonInfo(%ws)\n",
|
|
pwzName ));
|
|
SafeAssert( _fInitialized );
|
|
HRESULT hr;
|
|
TRY
|
|
{
|
|
CReadLock lock (_Monitor);
|
|
|
|
RtlZeroMemory( pli, sizeof( LogonInfo ) );
|
|
RtlZeroMemory( pti, sizeof( TicketInfo ) );
|
|
|
|
PWCHAR pwzDRName = MapNameDRN( pwzName );
|
|
ULONG iIndex;
|
|
|
|
hr = GetCacheEntry( pwzDRName,
|
|
&iIndex,
|
|
fLogon? CACHE_DOING_LOGON : 0);
|
|
if (FAILED(hr))
|
|
{
|
|
KdcDebug(( DEB_TRACE, "GetCacheEntry(%ws)==%x\n",
|
|
pwzDRName, hr ));
|
|
goto Error;
|
|
}
|
|
|
|
*phHandle = iIndex;
|
|
|
|
hr = _Cache[iIndex].plga->GetTicketInfo( &(pti->gGuid),
|
|
&(pti->kKey),
|
|
&(pti->fTicketOpts) );
|
|
if (FAILED(hr))
|
|
{
|
|
KdcDebug(( DEB_TRACE, "GetTicketInfo(%ws)==%x\n",
|
|
pwzDRName, hr ));
|
|
goto Error;
|
|
}
|
|
pti->fLogonSteps = _Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON;
|
|
|
|
hr = _Cache[iIndex].plga->GetLogonInfo( &(pli->pbValidLogonHours),
|
|
&(pli->prpwszValidWorkstations),
|
|
&(pli->fInteractive),
|
|
&(pli->fAttributes),
|
|
&(pli->ftAccountExpiry),
|
|
&(pli->ftPasswordChange),
|
|
&(pli->ftLockoutTime) );
|
|
if (FAILED(hr))
|
|
{
|
|
KdcDebug(( DEB_TRACE, "GetLogonInfo(%ws)==%x\n",
|
|
pwzDRName, hr ));
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Check that the pointers, etc, returned are valid.
|
|
//
|
|
if (pli->pbValidLogonHours)
|
|
{
|
|
PROBE_R_DWORD( pli->pbValidLogonHours );
|
|
PROBE_R_DWORD( pli->pbValidLogonHours->pBlobData );
|
|
PROBE_R_DWORD( pli->pbValidLogonHours->pBlobData +
|
|
pli->pbValidLogonHours->cbSize );
|
|
#if DBG
|
|
if (KDCInfoLevel & DEB_T_CACHE)
|
|
{
|
|
SECURITY_STRING ss;
|
|
ss = FormatBytes(pli->pbValidLogonHours->pBlobData,
|
|
(BYTE) pli->pbValidLogonHours->cbSize );
|
|
KdcDebug(( DEB_T_CACHE, "Valid logon hours (%d): %wZ\n",
|
|
pli->pbValidLogonHours->cbSize, &ss ));
|
|
SRtlFreeString( &ss );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (pli->prpwszValidWorkstations)
|
|
{
|
|
PROBE_R_DWORD( pli->prpwszValidWorkstations );
|
|
KdcDebug(( DEB_T_CACHE, "There are %d valid workstations\n",
|
|
pli->prpwszValidWorkstations->cElems ));
|
|
for (ULONG i=0; i<pli->prpwszValidWorkstations->cElems; i++ )
|
|
{
|
|
(void) wcslen( pli->prpwszValidWorkstations->pElems[i] );
|
|
KdcDebug(( DEB_T_CACHE, "Valid workstation[%d] = %ws\n",
|
|
i, pli->prpwszValidWorkstations->pElems[i] ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Bump my read lock up to a write lock, then increment the count.
|
|
// Note that the lock is released at the end of this scope
|
|
// automatically.
|
|
//
|
|
|
|
_Monitor.ReadToWrite();
|
|
_Cache[iIndex].cUseCount++;
|
|
_Monitor.WriteToRead();
|
|
|
|
hr = S_OK;
|
|
Error:
|
|
// Fall out of the TRY/CATCH block.
|
|
;
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
hr = e.GetErrorCode();
|
|
KdcDebug(( DEB_ERROR, "Exception 0x%X getting and checking logon info for %ws\n",
|
|
hr, pwzName ));
|
|
}
|
|
END_CATCH
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ReleaseLogonInfo
|
|
//
|
|
// Synopsis: Allows the cache entry to be deleted.
|
|
//
|
|
// Effects: Decrements the use count
|
|
//
|
|
// Arguments: [pwzName] -- Name of principal
|
|
// [iHandle] -- handle returned from GetLogonInfo
|
|
//
|
|
// Returns: void
|
|
//
|
|
// History: 04-Nov-93 WadeR Created
|
|
//
|
|
// Notes: iHandle is just a hint. If it has been moved,
|
|
// this will look through the cache for it.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
CPrincipalHandler::ReleaseLogonInfo( PWCHAR pwzName, ULONG iHandle )
|
|
{
|
|
SafeAssert( _fInitialized );
|
|
|
|
_Monitor.GetRead();
|
|
|
|
if ( (iHandle >= _cCache) ||
|
|
wcsicmp( _Cache[iHandle].pwzName, pwzName ) != 0)
|
|
{
|
|
//
|
|
// The cache entry must have been moved.
|
|
//
|
|
// It still has to be in the cache, because it's tagged as in-use.
|
|
// This will be fast, and it must succeed, because it won't have to
|
|
// hit the disk.
|
|
//
|
|
#if DBG
|
|
HRESULT hr =
|
|
#else
|
|
(void)
|
|
#endif
|
|
GetCacheEntry( pwzName, &iHandle, 0, FALSE ); // No flags, don't load
|
|
#if DBG
|
|
Win4Assert( SUCCEEDED( hr ) );
|
|
#endif
|
|
}
|
|
|
|
Win4Assert( _Cache[iHandle].cUseCount > 0 );
|
|
|
|
|
|
_Monitor.ReadToWrite();
|
|
_Cache[iHandle].cUseCount--;
|
|
|
|
// If the CACHE_INVALID bit is set, and the CACHE_DOING_LOGON bit is NOT
|
|
// set, and the use count is zero, then discard it.
|
|
if (((_Cache[iHandle].fCacheFlags & (CACHE_INVALID | CACHE_DOING_LOGON))
|
|
== CACHE_INVALID ) &&
|
|
(_Cache[iHandle].cUseCount == 0) )
|
|
{
|
|
Discard( iHandle );
|
|
}
|
|
|
|
_Monitor.Release();
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::GetPAC
|
|
//
|
|
// Synopsis: Gets the PAC for a principal.
|
|
//
|
|
// Effects: Allocates memory, creates a CPAC
|
|
//
|
|
// Arguments: [pwzName] -- [in] principal to get pac for
|
|
// [ppPAC] -- [out] pac
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 04-Nov-93 WadeR Created
|
|
//
|
|
// Notes:
|
|
//
|
|
// BUGBUG: Need to merge MikeSe's changes, comment better.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPrincipalHandler::GetPAC( PWCHAR pwzName, CPAC ** ppPAC )
|
|
{
|
|
KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::GetPAC(%ws)\n",
|
|
pwzName ));
|
|
SafeAssert( _fInitialized );
|
|
PWCHAR pwzDRName = MapNameDRN( pwzName );
|
|
SECID gSID;
|
|
KerbKey kGarbage;
|
|
ULONG dwGarbage;
|
|
ULONG iIndex;
|
|
|
|
CReadLock lock(_Monitor);
|
|
|
|
RET_IF_ERROR( DEB_WARN, GetCacheEntry(pwzDRName, &iIndex, 0) ); // 0 -> no flags
|
|
RET_IF_ERROR( DEB_WARN, _Cache[iIndex].plga->GetTicketInfo(
|
|
&gSID,
|
|
&kGarbage,
|
|
&dwGarbage ) );
|
|
*ppPAC = new CPAC;
|
|
(*ppPAC)->Init( _Cache[iIndex].plga->GetGroupInfo(),
|
|
gSID,
|
|
pwzName,
|
|
NULL ); // NT Sid.
|
|
|
|
#if DBG
|
|
// Why would anyone want to get thier PAC if they aren't doing
|
|
// a login?
|
|
if ((_Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON) == 0)
|
|
{
|
|
KdcDebug(( DEB_WARN, "Strange, \"%ws\" fetching a PAC outside login.\n",
|
|
pwzName ));
|
|
}
|
|
#endif
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::LogonComplete
|
|
//
|
|
// Synopsis: Calls LogonComplete on the CLogonAccount, saves it, releases it.
|
|
//
|
|
// Effects: releases memory.
|
|
//
|
|
// Arguments: [pwzName] -- [in] Name of principal who's finished logging on.
|
|
// [fGood] -- [in] True if it's a successful logon.
|
|
// [ftLock] -- [in] Account locked out until this time.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 04-Nov-93 WadeR Created
|
|
//
|
|
// Notes: This catches any exceptions, and passes back error codes.
|
|
// This should check the CACHE_INVALID bit and, if set, discard
|
|
// the entry. But, since this always discards the entry it can
|
|
// safely ignore the bit.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPrincipalHandler::LogonComplete( PWCHAR pwzName,
|
|
BOOL fGood,
|
|
FILETIME ftLock )
|
|
{
|
|
SafeAssert( _fInitialized );
|
|
HRESULT hr;
|
|
KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::LogonComplete(%ws)\n",
|
|
pwzName ));
|
|
TRY
|
|
{
|
|
ULONG iIndex;
|
|
PWCHAR pwzDRName = MapNameDRN( pwzName );
|
|
|
|
CReadLock lock(_Monitor);
|
|
|
|
hr = GetCacheEntry(pwzDRName, &iIndex, 0); // no special flags
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_Monitor.ReadToWrite();
|
|
|
|
if ((_Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON) == 0)
|
|
{
|
|
// Oops! We aged this principal out of the cache already.
|
|
// This means that the principal took so long to log on, we
|
|
// decided that it was a failed logon attempt and we've
|
|
// already marked it as such. So we'll just ignore this call
|
|
// to LogonComplete.
|
|
|
|
KdcDebug(( DEB_ERROR, "Someone called LogonComplete when "
|
|
"they weren't logging on!\n" ));
|
|
}
|
|
else
|
|
{
|
|
_Cache[iIndex].plga->LogonComplete( fGood, &ftLock );
|
|
_Cache[iIndex].plga->Save(NULL, FALSE);
|
|
}
|
|
ReleaseCacheEntry( iIndex );
|
|
hr = ShrinkCache();
|
|
}
|
|
else
|
|
{
|
|
KdcDebug(( DEB_WARN, "GetCacheEntry(%ws) failed 0x%x.\n",
|
|
pwzDRName, hr ));
|
|
}
|
|
}
|
|
CATCH( CException, e )
|
|
{
|
|
hr = e.GetErrorCode();
|
|
KdcDebug(( DEB_ERROR, "Exception 0x%X in LogonComplete( %ws )\n",
|
|
hr, pwzName ));
|
|
}
|
|
END_CATCH
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::ClearCache
|
|
//
|
|
// Synopsis: Removes one or more cache entries
|
|
//
|
|
// Effects: May call LogonComplete on some entries.
|
|
//
|
|
// Arguments: [pwzName] -- Name of principal. If null, clears everything.
|
|
// [fForce] -- If true, remove everything, even pending logons.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Derivation:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 05-Nov-93 WadeR Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPrincipalHandler::ClearCache(PWCHAR pwzName, BOOL fForce)
|
|
{
|
|
KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::ClearCache(%ws)\n", pwzName ));
|
|
|
|
SafeAssert( _fInitialized );
|
|
|
|
// Flag to indicate the principal should be reloaded, because it was
|
|
// in the middle of a logon.
|
|
BOOL fReload = FALSE;
|
|
|
|
if (pwzName)
|
|
{
|
|
ULONG iIndex;
|
|
|
|
// Get a read lock so we can call GetCacheEntry
|
|
_Monitor.GetRead();
|
|
|
|
// Get the cache entry, but don't load it if it isn't there.
|
|
if (GetCacheEntry(pwzName, &iIndex, 0, FALSE) == S_OK)
|
|
{
|
|
// It was found in the cache.
|
|
// We are going to change it, either to mark it as invalid or to
|
|
// delete it, so upgrade our lock.
|
|
_Monitor.ReadToWrite();
|
|
|
|
// If fForce is true, or the cache entry is in use, we can discard
|
|
// it. If it's in use, and fForce isn't set, simply mark it as
|
|
// invalid.
|
|
if (fForce || (_Cache[iIndex].cUseCount == 0 ))
|
|
{
|
|
if (_Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON)
|
|
{
|
|
// This principal was in the process of logging on, so
|
|
// reload the account (so LogonComplete can succeed),
|
|
// unless fForce is true (because we don't want it to
|
|
// reappear).
|
|
fReload = !fForce;
|
|
}
|
|
ReleaseCacheEntry( iIndex );
|
|
}
|
|
else
|
|
{
|
|
_Cache[iIndex].fCacheFlags |= CACHE_INVALID;
|
|
}
|
|
|
|
//
|
|
// If we flushed an account that was in the process of logging on,
|
|
// we should re-load it.
|
|
//
|
|
if (fReload)
|
|
{
|
|
ULONG iFoo;
|
|
_Monitor.WriteToRead();
|
|
(void) GetCacheEntry( pwzName, &iFoo, CACHE_DOING_LOGON );
|
|
}
|
|
}
|
|
_Monitor.Release();
|
|
}
|
|
else
|
|
{
|
|
// Some of the cache entries will be in the middle of a logon. We
|
|
// must save the names away, so they can be re-loaded later.
|
|
//
|
|
// Since the cache is fixed size, we know the maximim number of names.
|
|
PWCHAR * apwzNames = new PWCHAR [_cCacheMaxSize];
|
|
ULONG cNamesUsed = 0;
|
|
|
|
// Going to delete everything, so get the lock once up front.
|
|
|
|
_Monitor.GetWrite();
|
|
|
|
//
|
|
// This loop is a little strange in that both ends move. The
|
|
// Discard() method will move the empty slot to the end of the cache,
|
|
// and decrement _cCache. Since it moves a new cache entry to the
|
|
// vacated spot, we don't want to increment the index if we discard
|
|
// something.
|
|
//
|
|
// On the other hand, if we don't discard the entry, we must increment
|
|
// the index to look at the next entry.
|
|
//
|
|
// Every iteration of the loop gets one step closer to finishing,
|
|
// either by raising the index or lowering the max.
|
|
|
|
ULONG iIndex = 0;
|
|
while (iIndex < _cCache)
|
|
{
|
|
if (fForce || (_Cache[iIndex].cUseCount == 0 ))
|
|
{
|
|
if (!fForce && _Cache[iIndex].fCacheFlags & CACHE_DOING_LOGON)
|
|
{
|
|
apwzNames[cNamesUsed++] = _Cache[iIndex].pwzName;
|
|
_Cache[iIndex].pwzName = 0; // so it isn't deleted by Discard.
|
|
}
|
|
// This will decrement _cCache
|
|
ReleaseCacheEntry( iIndex );
|
|
}
|
|
else
|
|
{
|
|
_Cache[iIndex].fCacheFlags |= CACHE_INVALID;
|
|
iIndex++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finished clearing the cache, so re-load the principals that were in
|
|
// the middle of a logon.
|
|
//
|
|
|
|
_Monitor.WriteToRead();
|
|
for (iIndex=0; iIndex < cNamesUsed; iIndex++)
|
|
{
|
|
ULONG iFoo;
|
|
(void) GetCacheEntry( apwzNames[iIndex], &iFoo, CACHE_DOING_LOGON);
|
|
delete apwzNames[iIndex];
|
|
}
|
|
delete apwzNames;
|
|
_Monitor.Release();
|
|
}
|
|
|
|
|
|
if ( !((pwzName == NULL) && fForce) )
|
|
{
|
|
//
|
|
// If we are forcing everything out of the cache, we shouldn't be
|
|
// loading new stuff again. Otherwise we can count on these being
|
|
// useful.
|
|
//
|
|
// Load the frequently used accounts.
|
|
//
|
|
//
|
|
|
|
ULONG iFoo;
|
|
_Monitor.GetRead();
|
|
RET_IF_ERROR( DEB_ERROR, GetCacheEntry( pwcKdcPrincipal, &iFoo, CACHE_STICKY ));
|
|
RET_IF_ERROR( DEB_ERROR, GetCacheEntry( pwcPSPrincipal, &iFoo, CACHE_STICKY ));
|
|
_Monitor.Release();
|
|
|
|
//
|
|
// Load the ticket info for the KDC and PS
|
|
//
|
|
|
|
TicketInfo tiKDC;
|
|
TicketInfo tiPS;
|
|
|
|
RET_IF_ERROR( DEB_ERROR, GetTicketInfo( pwcKdcPrincipal, &tiKDC ) );
|
|
RET_IF_ERROR( DEB_ERROR, GetTicketInfo( pwcPSPrincipal, &tiPS ) );
|
|
|
|
_Monitor.GetWrite();
|
|
_tiKdc = tiKDC;
|
|
_tiPS = tiPS;
|
|
_Monitor.Release();
|
|
}
|
|
|
|
_Monitor.GetWrite();
|
|
HRESULT hr = ShrinkCache();
|
|
_Monitor.Release();
|
|
#if DBG
|
|
if (FAILED(hr))
|
|
{
|
|
KdcDebug(( DEB_ERROR, "ShrinkCache() == 0x%x\n", hr ));
|
|
}
|
|
#endif
|
|
return(hr);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::AgeCache
|
|
//
|
|
// Synopsis: Removes old entries from the cache.
|
|
//
|
|
// Effects: May release lots of cache slots.
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// Returns: S_OK
|
|
//
|
|
// History: 04-Nov-93 WadeR Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPrincipalHandler::AgeCache()
|
|
{
|
|
KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::AgeCache()\n" ));
|
|
SafeAssert( _fInitialized );
|
|
|
|
ULONG i;
|
|
int cRemoved = 0;
|
|
TimeStamp tsCutoff;
|
|
|
|
GetCurrentTimeStamp( &tsCutoff );
|
|
tsCutoff = tsCutoff - _tsMaxAge;
|
|
|
|
CWriteLock lock( _Monitor );
|
|
|
|
for (i=0; i<_cCache; i++)
|
|
{
|
|
if (!(_Cache[i].fCacheFlags & CACHE_STICKY) &&
|
|
(_Cache[i].cUseCount == 0) &&
|
|
(_Cache[i].tsLastUsed < tsCutoff))
|
|
{
|
|
Discard(i);
|
|
cRemoved++;
|
|
}
|
|
}
|
|
|
|
HRESULT hr = ShrinkCache();
|
|
|
|
KdcDebug(( DEB_T_CACHE, "AgeCache() removed %d\n", cRemoved ));
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPrincipalHandler::Init
|
|
//
|
|
// Synopsis: Initializes the principal handler
|
|
//
|
|
// Effects: Calls CoInitialize, loads PS and KDC info.
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// History: 04-Nov-93 WadeR Created
|
|
//
|
|
// Notes:
|
|
//
|
|
// BUGBUG: If this fails, it could leak resources.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPrincipalHandler::Init()
|
|
{
|
|
KdcDebug(( DEB_T_CACHE, "CPrincipalHandler::Init()\n" ));
|
|
|
|
if (_fInitialized)
|
|
{
|
|
Win4Assert( !"CPrincipalHandler::Init() called twice!" );
|
|
return(E_UNEXPECTED);
|
|
}
|
|
RET_IF_ERROR(DEB_ERROR, _sci.Init() );
|
|
|
|
_tsMaxAge = tsZero;
|
|
AddSecondsToTimeStamp( &_tsMaxAge, 60 * 60 ); // BUGBUG: magic numbers.
|
|
|
|
//
|
|
// Initialize the cache
|
|
//
|
|
_cCache = 0;
|
|
_cCacheMaxSize = CACHE_INITIALSIZE / sizeof( CacheEntry );
|
|
_Cache = new (NullOnFail) CacheEntry [ _cCacheMaxSize ];
|
|
if (_Cache == NULL)
|
|
{
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
|
|
_fInitialized = TRUE;
|
|
|
|
// This will load the KDC and PS info.
|
|
RET_IF_ERROR( DEB_ERROR, ClearCache() );
|
|
|
|
//
|
|
// Load the ticket info for the KDC and PS
|
|
//
|
|
|
|
TicketInfo tiKDC;
|
|
TicketInfo tiPS;
|
|
|
|
RET_IF_ERROR( DEB_ERROR, GetTicketInfo( pwcKdcPrincipal, &tiKDC ) );
|
|
RET_IF_ERROR( DEB_ERROR, GetTicketInfo( pwcPSPrincipal, &tiPS ) );
|
|
|
|
_Monitor.GetWrite();
|
|
_tiKdc = tiKDC;
|
|
_tiPS = tiPS;
|
|
_Monitor.Release();
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|