/*++ Copyright (c) 1996 Microsoft Corporation This program is released into the public domain for any purpose. Module Name: cache.c Abstract: This module implements a simple user cache. The cached users are kept in an LRU sorted list. If there will be a large number of simultaneous users, then a sorted array would be more appropriate. --*/ #include #include #include // // Constants // // // The maximum number of users we will cache. If there will be a large number // of simultaneous users, bump this value // #define MAX_CACHED_USERS 100 // // The position after which we'll move a cache entry to the front of the list // #define LIST_REORDER_THRESHOLD 6 // // Cached user structure // typedef struct _USER_INFO { LIST_ENTRY ListEntry; // Double linked list entry CHAR achUserName[SF_MAX_USERNAME]; // External username and password CHAR achPassword[SF_MAX_PASSWORD]; CHAR achNTUserName[SF_MAX_USERNAME]; // NT account and password to map user to CHAR achNTUserPassword[SF_MAX_PASSWORD]; } USER_INFO, *PUSER_INFO; // // Globals // // // Circular double linked list of cached users // LIST_ENTRY CacheListHead; // // Critical section protects cache list // CRITICAL_SECTION csCacheLock; // // Indicates whether we are initialized // BOOL fCacheInitialized = FALSE; // // Number of items in the cache // DWORD cCacheItems = 0; BOOL InitializeCache( VOID ) /*++ Routine Description: Initializes the cache module Return Value: TRUE if initialized successfully, FALSE on error --*/ { if ( fCacheInitialized ) return TRUE; InitializeCriticalSection( &csCacheLock ); CacheListHead.Blink = CacheListHead.Flink = &CacheListHead; fCacheInitialized = TRUE; return TRUE; } BOOL LookupUserInCache( CHAR * pszUserName, BOOL * pfFound, CHAR * pszPassword, CHAR * pszNTUser, CHAR * pszNTUserPassword ) /*++ Routine Description: Checks to see if a user is in the cache and returns the user properties if found Arguments: pszUserName - Case insensitive username to find pfFound - Set to TRUE if the specified user was found pszPassword - Receives password for specified if found pszNTUser - Receives the NT Username to map this user to pszNTUserPassword - Receives the NT Password for pszNTUser Note: pszPassword and pszNTUserPassword must be at least SF_MAX_PASSWORD characters. pszNTUser must be at least SF_MAX_USERNAME characters. Return Value: TRUE if no errors occurred. --*/ { LIST_ENTRY * pEntry; USER_INFO * pUser; DWORD cPosition = 0; // // Search the cache for the specified user // EnterCriticalSection( &csCacheLock ); for ( pEntry = CacheListHead.Flink; pEntry != &CacheListHead; pEntry = pEntry->Flink ) { pUser = CONTAINING_RECORD( pEntry, USER_INFO, ListEntry ); if ( !stricmp( pszUserName, pUser->achUserName )) { goto Found; } cPosition++; } LeaveCriticalSection( &csCacheLock ); // // Not Found // *pfFound = FALSE; return TRUE; Found: // // Copy out the user properties // strcpy( pszPassword, pUser->achPassword ); strcpy( pszNTUser, pUser->achNTUserName ); strcpy( pszNTUserPassword, pUser->achNTUserPassword ); // // Move this user entry to the front of the list as we're probably going // to get subsequent requests for this user. Note we only move it // if it's not already near the front // if ( cPosition > LIST_REORDER_THRESHOLD ) { // // Remove from the old position... // pEntry->Blink->Flink = pEntry->Flink; pEntry->Flink->Blink = pEntry->Blink; // // ...and insert it at the beginning of the list // pEntry->Blink = &CacheListHead; pEntry->Flink = CacheListHead.Flink; CacheListHead.Flink->Blink = pEntry; CacheListHead.Flink = pEntry; } LeaveCriticalSection( &csCacheLock ); *pfFound = TRUE; return TRUE; } BOOL AddUserToCache( CHAR * pszUserName, CHAR * pszPassword, CHAR * pszNTUser, CHAR * pszNTUserPassword ) /*++ Routine Description: Adds the specified user to the cache Arguments: pszUserName - Username to add pszPassword - Contains the external password for this user pszNTUser - Contains the NT user name to use for this user pszNTUserPassword - Contains the password for NTUser Return Value: TRUE if no errors occurred. --*/ { LIST_ENTRY * pEntry; USER_INFO * pUser; // // Check our parameters before adding them to the cache // if ( strlen( pszUserName ) > SF_MAX_USERNAME || strlen( pszPassword ) > SF_MAX_PASSWORD || strlen( pszNTUser ) > SF_MAX_USERNAME || strlen( pszNTUserPassword ) > SF_MAX_PASSWORD ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } // // Search the cache for the specified user to make sure there are no // duplicates // EnterCriticalSection( &csCacheLock ); for ( pEntry = CacheListHead.Flink; pEntry != &CacheListHead; pEntry = pEntry->Flink ) { pUser = CONTAINING_RECORD( pEntry, USER_INFO, ListEntry ); if ( !stricmp( pszUserName, pUser->achUserName )) { goto Found; } } // // Allocate a new cache item and put it at the head of the list // pUser = (USER_INFO *) LocalAlloc( LPTR, sizeof( USER_INFO )); if ( !pUser ) { LeaveCriticalSection( &csCacheLock ); SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return FALSE; } pUser->ListEntry.Flink = CacheListHead.Flink; pUser->ListEntry.Blink = &CacheListHead; CacheListHead.Flink = &pUser->ListEntry; Found: // // Set the various fields // strcpy( pUser->achUserName, pszUserName ); strcpy( pUser->achPassword, pszPassword ); strcpy( pUser->achNTUserName, pszNTUser ); strcpy( pUser->achNTUserPassword, pszNTUserPassword ); cCacheItems++; // // If there are too many cached users, remove the least recently // used one now // if ( cCacheItems > MAX_CACHED_USERS ) { pEntry = CacheListHead.Blink; pEntry->Blink->Flink = &CacheListHead; CacheListHead.Blink = pEntry->Blink; LocalFree( CONTAINING_RECORD( pEntry, USER_INFO, ListEntry )); cCacheItems--; } LeaveCriticalSection( &csCacheLock ); return TRUE; } VOID TerminateCache( VOID ) /*++ Routine Description: Terminates the cache module and frees any allocated memory --*/ { LIST_ENTRY * pEntry; LIST_ENTRY * pEntryNext; USER_INFO * pUser; if ( !fCacheInitialized ) return; EnterCriticalSection( &csCacheLock ); // // Free all of the cache entries // for ( pEntry = CacheListHead.Flink; pEntry != &CacheListHead; pEntry = pEntryNext ) { pUser = CONTAINING_RECORD( pEntry, USER_INFO, ListEntry ); pEntryNext = pEntry->Flink; // // Remove this entry from the list and free it // pEntry->Blink->Flink = pEntry->Flink; pEntry->Flink->Blink = pEntry->Blink; LocalFree( pUser ); } cCacheItems = 0; LeaveCriticalSection( &csCacheLock ); DeleteCriticalSection( &csCacheLock ); fCacheInitialized = FALSE; }