#include "Mbrd_Cache.h"
#include "CPlugInList.h"
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <uuid/uuid.h>
#include <DirectoryServiceCore/CLog.h>
#include <membership.h>
extern CPlugInList *gPlugins;
struct _MbrdCache
{
int32_t fRefCount;
int32_t fNumItems;
int32_t fDefaultExpiration;
int32_t fDefaultNegativeExpiration;
int32_t fKernelExpiration;
int32_t fMaximumRefresh;
int32_t fKerberosFallback;
pthread_mutex_t fCacheLock;
UserGroup *fListHead;
UserGroup *fListTail;
struct HashTable fGUIDHash;
struct HashTable fSIDHash;
struct HashTable fUIDHash;
struct HashTable fGIDHash;
struct HashTable fUserNameHash;
struct HashTable fGroupNameHash;
struct HashTable fComputerNameHash;
struct HashTable fComputerGroupNameHash;
struct HashTable fKerberosHash;
struct HashTable fX509Hash;
};
#pragma mark -
#pragma mark Internal routines
void ConvertSIDToString( char* string, ntsid_t* sid )
{
char* current = string;
long long temp = 0;
int i;
for (i = 0; i < 6; i++)
temp = (temp << 8) | sid->sid_authority[i];
sprintf(current,"S-%u-%llu", sid->sid_kind, temp);
for(i=0; i < sid->sid_authcount; i++)
{
current = current + strlen(current);
sprintf(current, "-%u", sid->sid_authorities[i]);
}
}
static bool ItemOutdated( UserGroup* item, int flags )
{
if ( item->fNodeAvailable == false )
return false;
if ( item->fNode != NULL )
{
uint32_t iToken = 0;
char tempNode[512];
strlcpy( tempNode, item->fNode, sizeof(tempNode) );
char *nodeName = strtok( tempNode, "/" );
if ( nodeName != NULL )
iToken = gPlugins->GetValidDataStamp( nodeName );
if ( iToken != item->fToken )
return true;
}
if ( (flags & KAUTH_EXTLOOKUP_REFRESH_MEMBERSHIP) != 0 )
return (item->fMaximumRefresh <= GetElapsedSeconds());
return (item->fExpiration <= GetElapsedSeconds());
}
static void MbrdCache_RemoveFromList( MbrdCache *cache, UserGroup* ug )
{
if ( ug->fLink == NULL )
cache->fListTail = ug->fBackLink;
else
ug->fLink->fBackLink = ug->fBackLink;
if ( ug->fBackLink == NULL )
cache->fListHead = ug->fLink;
else
ug->fBackLink->fLink = ug->fLink;
UserGroup_Release( ug );
__sync_sub_and_fetch( &cache->fNumItems, 1 );
}
static void MbrdCache_AddToHeadOfList( MbrdCache *cache, UserGroup* ug )
{
UserGroup_Retain( ug );
ug->fBackLink = NULL;
if ( cache->fListHead == NULL )
{
cache->fListHead = cache->fListTail = ug;
ug->fLink = NULL;
}
else
{
ug->fLink = cache->fListHead;
cache->fListHead->fBackLink = ug;
cache->fListHead = ug;
}
__sync_add_and_fetch( &cache->fNumItems, 1 );
}
static void MbrdCache_AddToHashes( MbrdCache *cache, UserGroup *ug )
{
uint32_t secs = GetElapsedSeconds();
ug->fMaximumRefresh = secs + cache->fMaximumRefresh;
ug->fExpiration = secs + ((ug->fFlags & kUGFlagNotFound) != 0 ? cache->fDefaultNegativeExpiration : cache->fDefaultExpiration);
if ( (ug->fFlags & kUGFlagHasGUID) != 0 )
HashTable_Add( &cache->fGUIDHash, ug, (ug->fFoundBy & kUGFoundByGUID) != 0 );
if ( (ug->fRecordType & kUGRecordTypeGroup) != 0 ) {
if ( (ug->fFlags & kUGFlagHasID) != 0 )
HashTable_Add( &cache->fGIDHash, ug, (ug->fFoundBy & kUGFoundByID) != 0 );
if ( ug->fName != NULL )
HashTable_Add( &cache->fGroupNameHash, ug, (ug->fFoundBy & kUGFoundByName) != 0 );
}
if ( (ug->fRecordType & kUGRecordTypeUser) != 0 ) {
if ( (ug->fFlags & kUGFlagHasID) != 0 )
HashTable_Add( &cache->fUIDHash, ug, (ug->fFoundBy & kUGFoundByID) != 0 );
if ( ug->fName != NULL )
HashTable_Add( &cache->fUserNameHash, ug, (ug->fFoundBy & kUGFoundByName) != 0 );
if ( ug->fKerberos[0] != NULL )
HashTable_Add( &cache->fKerberosHash, ug, (ug->fFoundBy & kUGFoundByKerberos) != 0 );
if ( ug->fX509DN[0] != NULL )
HashTable_Add( &cache->fX509Hash, ug, (ug->fFoundBy & kUGFoundByX509DN) != 0 );
}
if ( (ug->fRecordType & kUGRecordTypeComputer) != 0 ) {
if ( (ug->fFlags & kUGFlagHasID) != 0 )
HashTable_Add( &cache->fUIDHash, ug, (ug->fFoundBy & kUGFoundByID) != 0 );
if ( ug->fName != NULL )
HashTable_Add( &cache->fComputerNameHash, ug, (ug->fFoundBy & kUGFoundByName) != 0 );
if ( ug->fKerberos[0] != NULL )
HashTable_Add( &cache->fKerberosHash, ug, (ug->fFoundBy & kUGFoundByKerberos) != 0 );
if ( ug->fX509DN[0] != NULL )
HashTable_Add( &cache->fX509Hash, ug, (ug->fFoundBy & kUGFoundByX509DN) != 0 );
}
if ( (ug->fRecordType & kUGRecordTypeComputerGroup) != 0 ) {
if ( ug->fName != NULL )
HashTable_Add( &cache->fComputerGroupNameHash, ug, (ug->fFoundBy & kUGFoundByName) != 0 );
}
if ( (ug->fFlags & kUGFlagHasSID) != 0 )
HashTable_Add( &cache->fSIDHash, ug, (ug->fFoundBy & kUGFoundBySID) != 0 );
}
static void MbrdCache_RemoveFromHashes( MbrdCache *cache, UserGroup *ug )
{
HashTable_Remove( &cache->fGUIDHash, ug );
if ( (ug->fRecordType & kUGRecordTypeGroup) != 0 ) {
if ( (ug->fFlags & kUGFlagHasID) != 0 )
HashTable_Remove( &cache->fGIDHash, ug );
if ( ug->fName != NULL )
HashTable_Remove( &cache->fGroupNameHash, ug );
}
if ( (ug->fRecordType & kUGRecordTypeUser) != 0 ) {
if ( (ug->fFlags & kUGFlagHasID) != 0 )
HashTable_Remove( &cache->fUIDHash, ug );
if ( ug->fName != NULL )
HashTable_Remove( &cache->fUserNameHash, ug );
}
if ( (ug->fRecordType & kUGRecordTypeComputer) != 0 ) {
if ( (ug->fFlags & kUGFlagHasID) != 0 )
HashTable_Remove( &cache->fUIDHash, ug );
if ( ug->fName != NULL )
HashTable_Remove( &cache->fComputerNameHash, ug );
}
if ( (ug->fRecordType & kUGRecordTypeComputerGroup) != 0 ) {
if ( ug->fName != NULL )
HashTable_Remove( &cache->fComputerGroupNameHash, ug );
}
if ( (ug->fFlags & kUGFlagHasSID) != 0 )
HashTable_Remove( &cache->fSIDHash, ug );
}
static void MbrdCache_AddEntry( MbrdCache *cache, UserGroup *ug )
{
int32_t beforeRefCount = ug->fRefCount;
MbrdCache_AddToHashes( cache, ug );
if ( beforeRefCount != ug->fRefCount ) {
MbrdCache_AddToHeadOfList( cache, ug );
}
}
static void MbrdCache_RemoveEntry( MbrdCache *cache, UserGroup* ug )
{
MbrdCache_RemoveFromHashes( cache, ug );
MbrdCache_RemoveFromList( cache, ug );
}
static UserGroup *MbrdCache_UpdateExistingRecord( MbrdCache *cache, UserGroup *existing, UserGroup *source )
{
if ( source == NULL ) {
return existing;
}
if ( existing == NULL ) {
DbgLog( kLogDebug, "mbr_mig - Membership - Adding record %X (%s) because it's new", source, source->fName );
MbrdCache_AddEntry( cache, source );
return source;
}
if ( source == existing ) {
DbgLog( kLogDebug, "mbr_mig - Membership - Not updating record %X (%s) because update is identical record", existing, existing->fName );
return source;
}
if ( (existing->fFlags & kUGFlagNotFound) != 0 && (source->fFlags & kUGFlagNotFound) != 0 ) {
DbgLog( kLogDebug, "mbr_mig - Membership - Not updating record %X (%s) because both entries are negative", existing, existing->fName );
UserGroup_Release( source );
return existing;
}
if ( existing->fNodeAvailable == false && (source->fFlags & kUGFlagNotFound) != 0 ) {
DbgLog( kLogInfo, "mbr_mig - Membership - Not updating record %X (%s) because it's offline", existing, existing->fName );
UserGroup_Release( source );
return existing;
}
if ( existing->fNode != NULL && source->fNode != NULL && strcmp(existing->fNode, source->fNode) == 0 &&
existing->fRecordType == source->fRecordType &&
existing->fName != NULL && source->fName != NULL && strcmp(existing->fName, source->fName) == 0 )
{
char buffer[128] = { 0, };
MbrdCache_RemoveFromHashes( cache, existing ); UserGroup_Merge( existing, source, false );
MbrdCache_AddToHashes( cache, existing );
if ( source->fFoundBy & kUGFoundByNestedGroup ) {
DbgLog( kLogInfo, "mbr_mig - Membership - Refreshing record '%s' (%X) with result of indirect search", source->fName, source,
existing, UserGroup_GetFoundByString(source, buffer, sizeof(buffer)) );
}
else {
DbgLog( kLogInfo, "mbr_mig - Membership - Merged record '%s' (%X) into %X - new authority '%s'", source->fName, source,
existing, UserGroup_GetFoundByString(source, buffer, sizeof(buffer)) );
}
UserGroup_Release( source );
return existing;
}
DbgLog( kLogInfo, "mbr_mig - Membership - Removing record %X (%s) to replace with %X (%s)",
existing, existing->fName ? : "",
source, source->fName ? : "" );
MbrdCache_RemoveEntry( cache, existing ); MbrdCache_AddEntry( cache, source );
UserGroup_Release( existing );
return source;
}
#pragma mark -
#pragma mark Public routines
MbrdCache *MbrdCache_Create( int32_t defaultExpiration, int32_t defaultNegativeExpiration, int32_t kernelExp, int32_t maxRefresh, int32_t kerberosFallback )
{
MbrdCache *cache = (MbrdCache *) calloc( 1, sizeof(MbrdCache) );
assert( cache != NULL );
cache->fDefaultExpiration = defaultExpiration;
cache->fDefaultNegativeExpiration = defaultNegativeExpiration;
cache->fKernelExpiration = kernelExp;
cache->fMaximumRefresh = maxRefresh;
cache->fKerberosFallback = kerberosFallback;
pthread_mutexattr_t attr;
assert( pthread_mutexattr_init(&attr) == 0);
assert( pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) == 0);
assert( pthread_mutex_init(&cache->fCacheLock, &attr) == 0);
pthread_mutexattr_destroy( &attr );
HashTable_Initialize( &cache->fGUIDHash, "Global GUID", cache, eGUIDHash );
HashTable_Initialize( &cache->fSIDHash, "Global SID", cache, eSIDHash );
HashTable_Initialize( &cache->fUIDHash, "Global UID", cache, eIDHash );
HashTable_Initialize( &cache->fGIDHash, "Global GID", cache, eIDHash );
HashTable_Initialize( &cache->fUserNameHash, "User Name", cache, eNameHash );
HashTable_Initialize( &cache->fGroupNameHash, "Group Name", cache, eNameHash );
HashTable_Initialize( &cache->fComputerNameHash, "Computer Name", cache, eNameHash );
HashTable_Initialize( &cache->fComputerGroupNameHash, "ComputerGroup Name", cache, eNameHash );
HashTable_Initialize( &cache->fKerberosHash, "Kerberos", cache, eKerberosHash );
HashTable_Initialize( &cache->fX509Hash, "X509DN", cache, eNameHash );
return cache;
}
void MbrdCache_Release( MbrdCache *cache )
{
if ( dsReleaseObject(cache, &cache->fRefCount, false) == true ) {
pthread_mutex_destroy( &cache->fCacheLock );
HashTable_FreeContents( &cache->fGUIDHash );
HashTable_FreeContents( &cache->fSIDHash );
HashTable_FreeContents( &cache->fUIDHash );
HashTable_FreeContents( &cache->fGIDHash );
HashTable_FreeContents( &cache->fUserNameHash );
HashTable_FreeContents( &cache->fGroupNameHash );
HashTable_FreeContents( &cache->fComputerNameHash );
HashTable_FreeContents( &cache->fComputerGroupNameHash );
HashTable_FreeContents( &cache->fKerberosHash );
HashTable_FreeContents( &cache->fX509Hash );
free( cache );
cache = NULL;
}
}
int32_t MbrdCache_GetDefaultExpiration( MbrdCache *cache )
{
if ( cache == NULL ) return 0;
return cache->fDefaultExpiration;
}
UserGroup* MbrdCache_GetAndRetain( MbrdCache *cache, int recordType, int idType, const void *idValue, int32_t flags )
{
if ( cache == NULL ) return NULL;
UserGroup *cacheResult = NULL;
ntsid_t tempsid;
ntsid_t *sid = (ntsid_t *) idValue;
const char *reqOrigin = ((flags & kKernelRequest) != 0 ? "mbr_syscall" : "mbr_mig");
switch ( idType )
{
case ID_TYPE_UID:
if ( (recordType & kUGRecordTypeUser) != 0 )
cacheResult = HashTable_GetAndRetain( &cache->fUIDHash, idValue );
if ( cacheResult == NULL )
DbgLog( kLogInfo, "%s - Membership - Cache miss - by UID %d", reqOrigin, *((id_t *) idValue) );
break;
case ID_TYPE_GID:
if ( cacheResult == NULL && (recordType & kUGRecordTypeGroup) != 0 )
cacheResult = HashTable_GetAndRetain( &cache->fGIDHash, idValue );
if ( cacheResult == NULL )
DbgLog( kLogInfo, "%s - Membership - Cache miss - by GID %d", reqOrigin, *((id_t *) idValue) );
break;
case ID_TYPE_SID:
if ( sid != NULL )
{
if ( sid->sid_authcount > KAUTH_NTSID_MAX_AUTHORITIES )
return NULL;
memset( &tempsid, 0, sizeof(tempsid) );
memcpy( &tempsid, sid, KAUTH_NTSID_SIZE(sid) );
sid = &tempsid;
cacheResult = HashTable_GetAndRetain( &cache->fSIDHash, sid );
if ( cacheResult == NULL && LoggingEnabled(kLogInfo) ) {
char sidString[256];
ConvertSIDToString( sidString, sid );
DbgLog( kLogInfo, "%s - Membership - Cache miss - search by SID %s for type %X", reqOrigin, sidString, recordType );
}
}
break;
case ID_TYPE_USERNAME:
if ( (recordType & kUGRecordTypeUser) != 0 )
cacheResult = HashTable_GetAndRetain( &cache->fUserNameHash, idValue );
if ( cacheResult == NULL && (recordType & kUGRecordTypeComputer) != 0 )
cacheResult = HashTable_GetAndRetain( &cache->fComputerNameHash, idValue );
if ( cacheResult == NULL )
DbgLog( kLogInfo, "%s - Membership - Cache miss - by Name %s for type %X", reqOrigin, (char *)idValue, recordType );
break;
case ID_TYPE_GROUPNAME:
if ( (recordType & kUGRecordTypeGroup) != 0 )
cacheResult = HashTable_GetAndRetain( &cache->fGroupNameHash, idValue );
if ( cacheResult == NULL && (recordType & kUGRecordTypeComputerGroup) != 0 )
cacheResult = HashTable_GetAndRetain( &cache->fComputerGroupNameHash, idValue );
if ( cacheResult == NULL )
DbgLog( kLogInfo, "%s - Membership - Cache miss - by Name %s for type %X", reqOrigin, (char *)idValue, recordType );
break;
case ID_TYPE_KERBEROS:
cacheResult = HashTable_GetAndRetain( &cache->fKerberosHash, idValue );
break;
case ID_TYPE_X509_DN:
cacheResult = HashTable_GetAndRetain( &cache->fX509Hash, idValue );
break;
case ID_TYPE_GUID:
cacheResult = HashTable_GetAndRetain( &cache->fGUIDHash, idValue );
if ( cacheResult == NULL && LoggingEnabled(kLogInfo) ) {
uuid_string_t guidString;
uuid_unparse_upper( (unsigned char *)idValue, guidString );
DbgLog( kLogInfo, "%s - Membership - Cache miss - search by GUID %s for type %X", reqOrigin, guidString, recordType );
}
break;
default:
DbgLog( kLogError, "%s - Membership - unknown record search requested for ID type %d", reqOrigin, idType );
break;
};
if ( cacheResult != NULL ) {
DbgLog( kLogDebug, "%s - Membership - Cache hit - %s (%X)", reqOrigin, (cacheResult->fName ? : "\"no name\""), cacheResult );
}
return cacheResult;
}
UserGroup *MbrdCache_AddOrUpdate( MbrdCache *cache, UserGroup *entry, uint32_t flags )
{
if ( cache == NULL ) return NULL;
if ( (entry->fFlags & kUGFlagHasID) != 0 && (entry->fFlags & kUGFlagHasGUID) == 0 )
{
uint32_t* temp = (uint32_t *) &entry->fGUID;
if ( (entry->fRecordType & kUGRecordTypeUser) != 0 ) {
temp[0] = htonl( 0xFFFFEEEE );
temp[1] = htonl( 0xDDDDCCCC );
temp[2] = htonl( 0xBBBBAAAA );
temp[3] = htonl( entry->fID );
}
else if ( (entry->fRecordType & kUGRecordTypeComputer) != 0 ) {
temp[0] = htonl( 0xBBBBAAAA );
temp[1] = htonl( 0xDDDDBBBB );
temp[2] = htonl( 0xFFFFCCCC );
temp[3] = htonl( entry->fID );
}
else if ( (entry->fRecordType & kUGRecordTypeGroup) != 0 ) {
temp[0] = htonl( 0xAAAABBBB );
temp[1] = htonl( 0xCCCCDDDD );
temp[2] = htonl( 0xEEEEFFFF );
temp[3] = htonl( entry->fID );
}
else if ( (entry->fRecordType & kUGRecordTypeComputerGroup) != 0 ) {
temp[0] = htonl( 0xEEEEFFFF );
temp[1] = htonl( 0xCCCCDDDD );
temp[2] = htonl( 0xAAAABBBB );
temp[3] = htonl( entry->fID );
}
entry->fFlags |= kUGFlagHasGUID;
}
UserGroup *result = NULL;
int rc = pthread_mutex_lock( &cache->fCacheLock );
assert( rc == 0 );
switch ( entry->fFoundBy )
{
case kUGFoundByNestedGroup: case kUGFoundByGUID:
result = HashTable_GetAndRetain( &cache->fGUIDHash, entry->fGUID );
break;
case kUGFoundByID:
if ( (entry->fRecordType & (kUGRecordTypeUser | kUGRecordTypeComputer)) != 0 ) {
result = HashTable_GetAndRetain( &cache->fUIDHash, &entry->fID );
break;
}
if ( (entry->fRecordType & kUGRecordTypeGroup) != 0 ) {
result = HashTable_GetAndRetain( &cache->fGIDHash, &entry->fID );
}
break;
case kUGFoundByName:
if ( (entry->fRecordType & kUGRecordTypeUser) != 0 ) {
result = HashTable_GetAndRetain( &cache->fUserNameHash, entry->fName );
break;
}
if ( (entry->fRecordType & kUGRecordTypeComputer) != 0 ) {
result = HashTable_GetAndRetain( &cache->fComputerNameHash, entry->fName );
break;
}
if ( (entry->fRecordType & kUGRecordTypeGroup) != 0 ) {
result = HashTable_GetAndRetain( &cache->fGroupNameHash, entry->fName );
break;
}
if ( (entry->fRecordType & kUGRecordTypeComputerGroup) != 0 ) {
result = HashTable_GetAndRetain( &cache->fComputerGroupNameHash, entry->fName );
}
break;
case kUGFoundByX509DN:
result = HashTable_GetAndRetain( &cache->fX509Hash, entry->fX509DN[0] );
break;
case kUGFoundByKerberos:
if ((entry->fFlags & kUGFlagHasKerberos) != 0) {
result = HashTable_GetAndRetain( &cache->fKerberosHash, entry->fKerberos[0] );
}
break;
}
if ( result != NULL && result->fRecordType != entry->fRecordType )
{
result->fNodeAvailable = true;
MbrdCache_RemoveEntry( cache, result );
UserGroup_Release( result );
result = NULL;
}
if ( result != NULL ) {
uint32_t secs = GetElapsedSeconds();
result->fTimestamp = time( NULL );
result->fMaximumRefresh = secs + cache->fMaximumRefresh;
result->fExpiration = secs + ((result->fFlags & kUGFlagNotFound) != 0 ? cache->fDefaultNegativeExpiration : cache->fDefaultExpiration);
result = MbrdCache_UpdateExistingRecord( cache, result, entry );
}
else {
entry->fTimestamp = time( NULL );
MbrdCache_AddEntry( cache, entry );
result = entry;
}
rc = pthread_mutex_unlock( &cache->fCacheLock );
assert( rc == 0 );
return result;
}
void MbrdCache_RefreshHashes( MbrdCache *cache, UserGroup *existing )
{
int rc = pthread_mutex_lock( &cache->fCacheLock );
assert( rc == 0 );
MbrdCache_RemoveFromHashes( cache, existing ); MbrdCache_AddToHashes( cache, existing );
rc = pthread_mutex_unlock( &cache->fCacheLock );
assert( rc == 0 );
}
int MbrdCache_SetNodeAvailability( MbrdCache *cache, const char *nodeName, bool nodeAvailable )
{
int iCount = 0;
if ( cache == NULL ) return 0;
assert( pthread_mutex_lock(&cache->fCacheLock) == 0);
UserGroup* temp = cache->fListHead;
while ( temp != NULL )
{
if ( temp->fNodeAvailable != nodeAvailable && temp->fNode != NULL && strcmp(nodeName, temp->fNode) == 0 ) {
__sync_bool_compare_and_swap( &temp->fNodeAvailable, temp->fNodeAvailable, nodeAvailable );
iCount++;
}
temp = temp->fLink;
}
assert( pthread_mutex_unlock(&cache->fCacheLock) == 0);
return iCount;
}
void MbrdCache_Sweep( MbrdCache *cache )
{
if ( cache == NULL ) return;
assert( pthread_mutex_lock(&cache->fCacheLock) == 0 );
UserGroup* temp = cache->fListHead;
while ( temp != NULL )
{
UserGroup *delItem = temp;
temp = temp->fLink;
if ( ItemOutdated(delItem, 0) == true ) {
MbrdCache_RemoveEntry( cache, delItem );
}
}
assert( pthread_mutex_unlock(&cache->fCacheLock) == 0 );
}
void MbrdCache_NodeChangeOccurred( MbrdCache *cache )
{
if ( cache == NULL ) return;
uint32_t currentTime = GetElapsedSeconds();
assert( pthread_mutex_lock(&cache->fCacheLock) == 0);
UserGroup* temp = cache->fListHead;
while ( temp != NULL ) {
UserGroup *delItem = temp;
temp = temp->fLink;
if ( (delItem->fFlags & kUGFlagNotFound) != 0 ) {
MbrdCache_RemoveEntry( cache, delItem );
}
else {
delItem->fExpiration = currentTime;
}
}
assert( pthread_mutex_unlock(&cache->fCacheLock) == 0 );
}
void MbrdCache_ResetCache( MbrdCache *cache )
{
if ( cache == NULL ) return;
assert( pthread_mutex_lock(&cache->fCacheLock) == 0);
HashTable_Reset( &cache->fGUIDHash );
HashTable_Reset( &cache->fSIDHash );
HashTable_Reset( &cache->fUIDHash );
HashTable_Reset( &cache->fGIDHash );
HashTable_Reset( &cache->fUserNameHash );
HashTable_Reset( &cache->fGroupNameHash );
HashTable_Reset( &cache->fComputerNameHash );
HashTable_Reset( &cache->fComputerGroupNameHash );
HashTable_Reset(&cache->fX509Hash);
HashTable_Reset(&cache->fKerberosHash);
UserGroup* temp = cache->fListHead;
cache->fListHead = NULL;
cache->fListTail = NULL;
assert( pthread_mutex_unlock(&cache->fCacheLock) == 0 );
while (temp != NULL)
{
UserGroup *delItem = temp;
temp = delItem->fLink;
UserGroup_Release( delItem );
}
}
void MbrdCache_DumpState( MbrdCache *cache )
{
if ( cache == NULL ) return;
struct stat sb;
const char *logName = "/Library/Logs/membership_dump.log";
int ii;
for ( ii = 8; ii >= 0; ii-- )
{
char fileName[128];
char newName[128];
snprintf( fileName, sizeof(fileName), "%s.%d", logName, ii );
if ( lstat(fileName, &sb) == 0 )
{
snprintf( newName, sizeof(newName), "%s.%d", logName, ii+1 );
unlink( newName );
rename( fileName, newName );
}
}
if ( lstat(logName, &sb) == 0 )
{
char newName[128];
snprintf( newName, sizeof(newName), "%s.0", logName );
unlink( newName );
rename( logName, newName );
}
int fd = open( logName, O_EXCL | O_CREAT | O_RDWR, 0644 );
if ( fd == -1 )
return;
FILE* dumpFile = fdopen( fd, "w" );
if ( dumpFile == NULL ) {
close( fd );
return;
}
assert( pthread_mutex_lock(&cache->fCacheLock) == 0 );
fprintf( dumpFile, "Global UID count: %ld\n", cache->fUIDHash.fNumEntries );
fprintf( dumpFile, "Global GID count: %ld\n", cache->fGIDHash.fNumEntries );
fprintf( dumpFile, "Global GUID count: %ld\n", cache->fGUIDHash.fNumEntries );
fprintf( dumpFile, "Global SID count: %ld\n", cache->fSIDHash.fNumEntries );
fprintf( dumpFile, "Global User Name count: %ld\n", cache->fUserNameHash.fNumEntries );
fprintf( dumpFile, "Global Group Name count: %ld\n", cache->fGroupNameHash.fNumEntries );
fprintf( dumpFile, "Global Computer Name count: %ld\n", cache->fComputerNameHash.fNumEntries );
fprintf( dumpFile, "Global ComputerGroup Name count: %ld\n", cache->fComputerGroupNameHash.fNumEntries );
fprintf( dumpFile, "Global Kerberos count: %ld\n", cache->fKerberosHash.fNumEntries );
fprintf( dumpFile, "Global X509DN count: %ld\n\n", cache->fX509Hash.fNumEntries );
UserGroup* temp = cache->fListHead;
while (temp != NULL)
{
fprintf( dumpFile, "%p: %s - %s\n", temp, UserGroup_GetRecordTypeString(temp), (temp->fName ? : "(not found)") );
if ( (temp->fFlags & kUGFlagHasID) != 0 ) fprintf( dumpFile, "\tid: %d\n", temp->fID );
if ( (temp->fFlags & kUGFlagHasGUID) != 0 ) {
uuid_string_t guidString;
uuid_unparse_upper( temp->fGUID, guidString );
fprintf( dumpFile, "\tuuid: %s\n", guidString );
}
if ( (temp->fFlags & kUGFlagHasSID) != 0 ) {
char sidString[256];
ConvertSIDToString( sidString, &temp->fSID );
fprintf( dumpFile, "\tsid: %s\n", sidString );
}
for ( ii = 0; ii < kMaxAltIdentities && temp->fKerberos[ii]; ii++ ) {
fprintf( dumpFile, "\tKerberos ID: %s\n", temp->fKerberos[ii] );
}
for ( ii = 0; ii < kMaxAltIdentities && temp->fX509DN[ii] != NULL; ii++ ) {
fprintf( dumpFile, "\tX509 DN: %s\n", temp->fX509DN[ii] );
}
fprintf( dumpFile, "\tref: %d\n", temp->fRefCount);
if ( temp->fNode != NULL ) {
fprintf( dumpFile, "\tnode: '%s'\n\tstate: %s\n", temp->fNode, (temp->fNodeAvailable == true ? "online" : "offline") );
}
char timeBuffer[26];
fprintf( dumpFile, "\ttimestamp: %s", ctime_r(&temp->fTimestamp, timeBuffer) );
fprintf( dumpFile, "\tTTL: %d sec\n", MbrdCache_TTL(cache, temp, 0) );
char buffer[128] = { 0, };
fprintf( dumpFile, "\t%sfound by: %s\n", ((temp->fFlags & kUGFlagNotFound) != 0 ? "not " : ""),
UserGroup_GetFoundByString(temp, buffer, sizeof(buffer)) );
UserGroup **groups = NULL;
int numResults = HashTable_CreateItemArray( &temp->fGUIDMembershipHash, &groups );
if ( numResults > 0 )
{
int i;
fprintf(dumpFile, "\tmember of %d groups: ", numResults);
for (i = 0; i < numResults; i++)
{
if (i != 0) fprintf(dumpFile, ", ");
if ( groups[i]->fNode != NULL ) {
fprintf( dumpFile, "%s(%d - %p - %s - %s)", (groups[i]->fName ? :"Unknown"), groups[i]->fID, groups[i],
groups[i]->fNode ?:"", (groups[i]->fNodeAvailable == true ? "online" : "offline") );
}
else {
fprintf( dumpFile, "not found(%d - %p)", groups[i]->fID, groups[i] );
}
UserGroup_Release( groups[i] );
}
fprintf(dumpFile, "\n");
}
fprintf( dumpFile, "\n" );
if ( groups != NULL ) {
free( groups );
groups = NULL;
}
temp = temp->fLink;
}
assert( pthread_mutex_unlock(&cache->fCacheLock) == 0 );
fclose( dumpFile );
}
int32_t MbrdCache_TTL( MbrdCache *cache, UserGroup *entry, int32_t flags )
{
if ( (flags & kKernelRequest) != 0 ) {
return cache->fKernelExpiration;
}
uint32_t current = GetElapsedSeconds();
if ( entry != NULL && current < entry->fExpiration ) {
return entry->fExpiration - current;
}
return 10;
}
int32_t MbrdCache_KerberosFallback( MbrdCache *cache )
{
return cache->fKerberosFallback;
}