Mbrd_UserGroup.cpp [plain text]
#include "Mbrd_UserGroup.h"
#include "Mbrd_HashTable.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/syslog.h>
#include <unistd.h>
#include <libkern/OSByteOrder.h>
#include <mach/mach_time.h>
#include <pthread.h>
#include <fcntl.h>
#include <DirectoryService/DirServices.h>
#include <DirectoryService/DirServicesConst.h>
#include <DirectoryService/DirServicesUtils.h>
#include <DirectoryServiceCore/DSMutexSemaphore.h>
extern DSMutexSemaphore gMbrGlobalMutex;
tDirReference gMbrdDirRef = 0;
tDirNodeReference gMbrdSearchNode = 0;
#define TYPE_USER 0
#define TYPE_GROUP 1
#define TYPE_BOTH 2
#define TYPE_COMPUTER 3
int gMaxGUIDSInCache = 500;
int gDefaultExpiration = 4*60*60;
int gDefaultNegativeExpiration = 2*60*60;
int gLoginExpiration = 2*60;
uint32_t gScaleNumerator = 0;
uint32_t gScaleDenominator = 0;
UserGroup* gListHead = NULL;
UserGroup* gListTail = NULL;
long gNumItemsInCache = 0;
HashTable* gGUIDHash = NULL;
HashTable* gSIDHash = NULL;
HashTable* gUIDHash = NULL;
HashTable* gGIDHash = NULL;
HashTable* gUserNameHash = NULL;
HashTable* gGroupNameHash = NULL;
HashTable* gComputerNameHash = NULL;
static pthread_key_t gThreadKey;
StatBlock *gStatBlock = NULL;
guid_t gEveryoneGuid;
ntsid_t gEveryoneSID;
guid_t gLocalAccountsGuid;
guid_t gNetAccountsGuid;
void ResetUserGroup(UserGroup* ug);
UserGroup* DoRecordSearch(int recordType, char* attribute, char* value, UserGroup* membershipRoot);
void AddToHeadOfList(UserGroup* ug);
void RemoveFromList(UserGroup* ug);
uid_t GetTempIDForGUID(guid_t* guid);
uid_t GetTempIDForSID(ntsid_t* sid);
bool FindTempID(uid_t id, guid_t** guid, ntsid_t** sid);
void ConvertGUIDToString(guid_t* data, char* string);
void ConvertGUIDFromString(char* string, guid_t* destination);
void ConvertSIDToString(char* string, ntsid_t* sid);
void ConvertSIDFromString(char* string, ntsid_t* sid);
void GenerateItemMembership(UserGroup* item);
UserGroup* GetNewUGStruct(void);
#define kUUIDBlock 1
#define kSmallSIDBlock 2
#define kLargeSIDBlock 3
typedef struct TempUIDCacheBlockBase
{
struct TempUIDCacheBlockBase* fNext;
uid_t fKind;
int fNumIDs;
uid_t fStartID;
} TempUIDCacheBlockBase;
typedef struct TempUIDCacheBlockSmall
{
struct TempUIDCacheBlock* fNext;
uid_t fKind;
int fNumIDs;
uid_t fStartID;
guid_t fGUIDs[1024];
} TempUIDCacheBlockSmall;
typedef struct TempUIDCacheBlockLarge
{
struct TempUIDCacheBlock* fNext;
uid_t fKind;
int fNumIDs;
uid_t fStartID;
ntsid_t fSIDs[1024];
} TempUIDCacheBlockLarge;
TempUIDCacheBlockBase* gUIDCache = NULL;
static tDataListPtr gUserType = NULL;
static tDataListPtr gComputerType = NULL;
static tDataListPtr gGroupType = NULL;
static tDataListPtr gUnknownType = NULL;
static tDataListPtr gAttrsToGet = NULL;
void SetThreadFlags(int flags)
{
pthread_setspecific(gThreadKey, (const void*) flags);
}
uint32_t GetElapsedSeconds(void)
{
uint64_t elapsed = mach_absolute_time();
if (gScaleNumerator == 0)
{
struct mach_timebase_info mti = {0};
mach_timebase_info(&mti);
gScaleNumerator = mti.numer;
gScaleDenominator = mti.denom;
}
long double temp = (long double)(((long double)elapsed *
(long double)gScaleNumerator)/(long double)gScaleDenominator);
uint32_t elapsedSeconds = (uint32_t) (temp/(long double)NSEC_PER_SEC);
return elapsedSeconds;
}
uint64_t GetElapsedMicroSeconds(void)
{
uint64_t elapsed = mach_absolute_time();
if (gScaleNumerator == 0)
{
struct mach_timebase_info mti = {0};
mach_timebase_info(&mti);
gScaleNumerator = mti.numer;
gScaleDenominator = mti.denom;
}
long double temp = (long double)(((long double)elapsed *
(long double)gScaleNumerator)/(long double)gScaleDenominator);
uint64_t elapsedMicroSeconds = (uint64_t) (temp/(long double)NSEC_PER_USEC);
return elapsedMicroSeconds;
}
void AddToAverage(uint32_t* average, uint32_t* numDataPoints, uint32_t newDataPoint)
{
*average = (((*average) * (*numDataPoints)) + newDataPoint) / (*numDataPoints + 1);
*numDataPoints = *numDataPoints + 1;
}
void OpenDirService(void)
{
tDataBufferPtr nodeBuffer = NULL;
tContextData localContext = 0;
tDataListPtr nodeName = NULL;
UInt32 returnCount = 0;
tDirStatus status = eDSNoErr;
dsOpenDirService(&gMbrdDirRef);
nodeBuffer = dsDataBufferAllocate(gMbrdDirRef, 4096);
status = dsFindDirNodes(gMbrdDirRef, nodeBuffer, NULL, eDSAuthenticationSearchNodeName, &returnCount, &localContext);
if (status != eDSNoErr || returnCount == 0)
syslog(LOG_CRIT, "dsFindDirNodes returned %d, count = %d", status, returnCount);
status = dsGetDirNodeName(gMbrdDirRef, nodeBuffer, 1, &nodeName); if (status != eDSNoErr)
syslog(LOG_CRIT, "dsGetDirNodeName returned %d", status);
status = dsOpenDirNode(gMbrdDirRef, nodeName, &gMbrdSearchNode);
if (status != eDSNoErr)
syslog(LOG_CRIT, "dsOpenDirNode returned %d", status);
if (localContext != 0)
dsReleaseContinueData(gMbrdDirRef, localContext);
dsDataBufferDeAllocate(gMbrdDirRef, nodeBuffer);
dsDataListDeallocate(gMbrdDirRef, nodeName);
free(nodeName);
}
void Mbrd_InitializeUserGroup(int numToCache, int defaultExpiration, int defaultNegativeExpiration, int loginExp)
{
gMaxGUIDSInCache = numToCache;
gDefaultExpiration = defaultExpiration;
gDefaultNegativeExpiration = defaultNegativeExpiration;
gLoginExpiration = loginExp;
pthread_key_create(&gThreadKey, NULL);
gListHead = NULL;
gListTail = NULL;
gNumItemsInCache = 0;
gGUIDHash = CreateHash(__offsetof(UserGroup, fGUID), sizeof(guid_t), 0, 0);
gSIDHash = CreateHash(__offsetof(UserGroup, fSID), sizeof(ntsid_t), 1, 0);
gUIDHash = CreateHash(__offsetof(UserGroup, fID), sizeof(uid_t), 0, 0);
gGIDHash = CreateHash(__offsetof(UserGroup, fID), sizeof(uid_t), 0, 0);
gUserNameHash = CreateHash(__offsetof(UserGroup, fName), 0, 1, 0);
gGroupNameHash = CreateHash(__offsetof(UserGroup, fName), 0, 1, 0);
gComputerNameHash = CreateHash(__offsetof(UserGroup, fName), 0, 1, 0);
ConvertGUIDFromString("ABCDEFAB-CDEF-ABCD-EFAB-CDEF0000000C", &gEveryoneGuid);
ConvertSIDFromString("S-1-1-0", &gEveryoneSID);
ConvertGUIDFromString("ABCDEFAB-CDEF-ABCD-EFAB-CDEF0000003D", &gLocalAccountsGuid);
ConvertGUIDFromString("ABCDEFAB-CDEF-ABCD-EFAB-CDEF0000003E", &gNetAccountsGuid);
gStatBlock = (StatBlock*)malloc(sizeof(StatBlock));
memset(gStatBlock, 0, sizeof(StatBlock));
gStatBlock->fTotalUpTime = GetElapsedSeconds();
gUserType = dsBuildListFromStrings(0, kDSStdRecordTypeUsers, NULL);
gComputerType = dsBuildListFromStrings(0, kDSStdRecordTypeComputers, NULL);
gGroupType = dsBuildListFromStrings(0, kDSStdRecordTypeGroups, kDSStdRecordTypeComputerGroups, NULL);
gUnknownType = dsBuildListFromStrings(0, kDSStdRecordTypeUsers, kDSStdRecordTypeComputers, kDSStdRecordTypeGroups, kDSStdRecordTypeComputerGroups, NULL);
gAttrsToGet = dsBuildListFromStrings(0, kDS1AttrUniqueID, kDS1AttrGeneratedUID, kDSNAttrRecordName, kDS1AttrPrimaryGroupID, kDS1AttrTimeToLive, kDS1AttrSMBSID, kDS1AttrENetAddress, kDSNAttrMetaNodeLocation, kDS1AttrCopyTimestamp, NULL);
}
void Mbrd_ResetCache( void )
{
UserGroup* temp = gListHead;
while (temp != NULL)
{
temp->fExpiration = 0;
temp->fLoginExpiration = 0;
temp = temp->fLink;
}
}
void Mbrd_DumpState( void )
{
struct stat sb;
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;
}
UserGroup* temp = gListHead;
fprintf(dumpFile, "\nCache dump:\n\n");
while (temp != NULL)
{
char guidString[37];
char sidString[256];
const char* name = "";
char* type = NULL;
if (temp->fIsUser)
type = strdup("User");
else if (temp->fIsComputer)
type = strdup("Computer");
else
type = strdup("Group");
ConvertGUIDToString(&temp->fGUID, guidString);
if (temp->fSID != NULL)
ConvertSIDToString(sidString, temp->fSID);
else
sidString[0] = 0;
if (temp->fNotFound)
{
if (temp->fName == NULL)
name = "(doesn't exist)";
else
{
fprintf(dumpFile, "0x%X: %s %s (doesn't exist)\n", (unsigned int)temp, type, temp->fName);
temp = temp->fLink;
continue;
}
}
else if (temp->fName != NULL)
name = temp->fName;
fprintf(dumpFile, "0x%X: %s %s id: %d uuid: %s sid: %s (ref = %d)\n", (unsigned int)temp, type, name, temp->fID, guidString, sidString, temp->fRefCount);
if (temp->fGUIDMembershipHash != NULL)
{
int i;
UserGroup* groups[250];
int numResults = GetHashEntries(temp->fGUIDMembershipHash, groups, 250);
fprintf(dumpFile, "\tMembership:");
for (i = 0; i < numResults; i++)
{
if (i != 0) fprintf(dumpFile, ", ");
name = (groups[i]->fName != NULL)?groups[i]->fName:"";
fprintf(dumpFile, "%s(0x%X)", name, (unsigned int)groups[i]);
}
fprintf(dumpFile, "\n");
}
temp = temp->fLink;
free(type);
}
fclose(dumpFile);
}
int ItemOutdated(UserGroup* item)
{
int flags = (int) pthread_getspecific(gThreadKey);
if ((flags & kUseLoginTimeOutMask) != 0)
return (item->fLoginExpiration <= GetElapsedSeconds());
return (item->fExpiration <= GetElapsedSeconds());
}
UserGroup* GetItem(int recordType, char* idType, void* guidData, ntsid_t* sid, int id, char* name)
{
static DSMutexSemaphore searchMutex;
UserGroup* result = NULL;
UserGroup* cacheResult = NULL;
ntsid_t tempsid;
bool bSearchMutexHeld = false;
if (sid != NULL)
{
if (sid->sid_authcount > KAUTH_NTSID_MAX_AUTHORITIES)
return NULL;
memset(&tempsid, 0, sizeof(ntsid_t));
memcpy(&tempsid, sid, KAUTH_NTSID_SIZE(sid));
sid = &tempsid;
}
lookagain:
if (guidData != NULL)
{
cacheResult = HashLookup(gGUIDHash, guidData);
}
else if (sid != NULL)
{
cacheResult = HashLookup(gSIDHash, sid);
}
else if (name != NULL)
{
if (recordType == TYPE_USER)
cacheResult = HashLookup(gUserNameHash, (void*)name);
else if (recordType == TYPE_COMPUTER)
cacheResult = HashLookup(gComputerNameHash, (void*)name);
else
cacheResult = HashLookup(gGroupNameHash, (void*)name);
}
else
{
if (recordType == TYPE_USER)
cacheResult = HashLookup(gUIDHash, (void*)&id);
else
cacheResult = HashLookup(gGIDHash, (void*)&id);
}
if (cacheResult == NULL || ItemOutdated(cacheResult))
{
if ( bSearchMutexHeld == false )
{
gMbrGlobalMutex.SignalLock();
searchMutex.WaitLock();
gMbrGlobalMutex.WaitLock();
bSearchMutexHeld = true;
goto lookagain; }
else
{
gMbrGlobalMutex.SignalLock(); }
uint64_t microsec = GetElapsedMicroSeconds();
gStatBlock->fCacheMisses++;
if (guidData != NULL)
{
char guidString[37];
ConvertGUIDToString((guid_t *)guidData, guidString);
result = DoRecordSearch(recordType, idType, guidString, NULL);
}
else if (sid != NULL)
{
char sidString[256];
ConvertSIDToString(sidString, sid);
result = DoRecordSearch(recordType, idType, sidString, NULL);
}
else if (name != NULL)
{
result = DoRecordSearch(recordType, idType, name, NULL);
}
else
{
char idStr[16];
sprintf(idStr,"%d", id);
result = DoRecordSearch(recordType, idType, idStr, NULL);
}
if ((cacheResult != NULL) && (result != cacheResult) && (cacheResult->fRefCount == 0))
{
ResetUserGroup(cacheResult);
cacheResult = NULL;
}
microsec = GetElapsedMicroSeconds() - microsec;
AddToAverage(&gStatBlock->fAverageuSecPerRecordLookup, &gStatBlock->fTotalRecordLookups, (uint32_t)microsec);
gMbrGlobalMutex.WaitLock(); }
else
{
gStatBlock->fCacheHits++;
result = cacheResult;
}
if ( bSearchMutexHeld )
{
searchMutex.SignalLock();
bSearchMutexHeld = false;
}
if (result == NULL)
{
u_int16_t checkval;
if (cacheResult == NULL)
result = GetNewUGStruct();
else
{
ResetUserGroup(cacheResult);
result = cacheResult;
}
gStatBlock->fNumFailedRecordLookups++;
checkval = result->fCheckVal;
memset(result, 0, sizeof(UserGroup));
result->fCheckVal = checkval + 1;
result->fIsUser = (recordType == TYPE_USER);
result->fIsComputer = (recordType == TYPE_COMPUTER);
AddToHeadOfList(result);
if (guidData != NULL)
{
memcpy(&result->fGUID, guidData, sizeof(guid_t));
result->fID = GetTempIDForGUID((guid_t*)guidData);
AddToHash(gGUIDHash, result);
AddToHash(gGIDHash, result);
}
else if (sid != NULL)
{
result->fSID = (ntsid_t*)malloc(sizeof(ntsid_t));
memcpy(result->fSID, sid, sizeof(ntsid_t));
result->fID = GetTempIDForSID(sid);
long* temp = (long*)&result->fGUID;
temp[0] = htonl(0xAAAABBBB);
temp[1] = htonl(0xCCCCDDDD);
temp[2] = htonl(0xEEEEFFFF);
temp[3] = htonl(result->fID);
AddToHash(gGUIDHash, result);
AddToHash(gSIDHash, result);
AddToHash(gGIDHash, result);
}
else if (name != NULL)
{
result->fName = (char*)malloc(strlen(name) + 1);
strcpy(result->fName, name);
if (recordType == TYPE_USER)
AddToHash(gUserNameHash, result);
else if (recordType == TYPE_COMPUTER)
AddToHash(gComputerNameHash, result);
else
AddToHash(gGroupNameHash, result);
}
else
{
result->fID = id;
if (recordType == TYPE_USER)
AddToHash(gUIDHash, result);
else
{
ntsid_t* tempsidPtr = NULL;
guid_t* tempguid;
FindTempID(id, &tempguid, &tempsidPtr);
if (tempsidPtr != NULL && tempsidPtr->sid_authcount <= KAUTH_NTSID_MAX_AUTHORITIES)
{
result->fSID = (ntsid_t*)malloc(sizeof(ntsid_t));
memset(result->fSID, 0, sizeof(ntsid_t));
memcpy(result->fSID, tempsidPtr, KAUTH_NTSID_SIZE(tempsidPtr));
AddToHash(gSIDHash, result);
}
if (tempguid != NULL)
{
memcpy(&result->fGUID, tempguid, sizeof(guid_t));
}
else
{
long* temp = (long*)&result->fGUID;
temp[0] = htonl(0xAAAABBBB);
temp[1] = htonl(0xCCCCDDDD);
temp[2] = htonl(0xEEEEFFFF);
temp[3] = htonl(result->fID);
}
AddToHash(gGUIDHash, result);
AddToHash(gGIDHash, result);
}
}
result->fNotFound = 1;
result->fExpiration = GetElapsedSeconds() + gDefaultNegativeExpiration;
result->fLoginExpiration = GetElapsedSeconds() + gLoginExpiration;
}
if ((recordType == TYPE_USER) && result->fNotFound)
{
result = NULL;
}
return result;
}
int IsCompatibilityGUID(guid_t* guid, int* isUser, uid_t* id)
{
unsigned long* temp = (unsigned long*)guid;
int result = 0;
if ((temp[0] == htonl(0xFFFFEEEE)) && (temp[1] == htonl(0xDDDDCCCC)) && (temp[2] == htonl(0xBBBBAAAA)))
{
*id = ntohl(temp[3]);
*isUser = 1;
result = 1;
}
else if ((temp[0] == htonl(0xAAAABBBB)) && (temp[1] == htonl(0xCCCCDDDD)) && (temp[2] == htonl(0xEEEEFFFF)))
{
*id = ntohl(temp[3]);
*isUser = 0;
result = 1;
}
return result;
}
UserGroup* GetItemWithGUID(guid_t* guid)
{
int isUser;
uid_t id;
if (IsCompatibilityGUID(guid, &isUser, &id))
{
if (isUser)
return GetUserWithUID(id);
else
return GetGroupWithGID(id);
}
return GetItem(TYPE_BOTH, kDS1AttrGeneratedUID, guid, NULL, 0, NULL);
}
UserGroup* GetItemWithSID(ntsid_t* sid)
{
return GetItem(TYPE_BOTH, kDS1AttrSMBSID, NULL, sid, 0, NULL);
}
UserGroup* GetUserWithUID(int uid)
{
return GetItem(TYPE_USER, kDS1AttrUniqueID, NULL, NULL, uid, NULL);
}
UserGroup* GetGroupWithGID(int gid)
{
return GetItem(TYPE_GROUP, kDS1AttrPrimaryGroupID, NULL, NULL, gid, NULL);
}
UserGroup* GetUserWithName(char* name)
{
return GetItem(TYPE_USER, kDSNAttrRecordName, NULL, NULL, 0, name);
}
UserGroup* GetGroupWithName(char* name)
{
UserGroup* result = GetItem(TYPE_GROUP, kDSNAttrRecordName, NULL, NULL, 0, name);
if (result->fNotFound)
result = NULL;
return result;
}
int IsUserMemberOfGroupByGUID(UserGroup* user, guid_t* groupGUID)
{
UserGroup* result;
if (memcmp(&user->fGUID, groupGUID, sizeof(guid_t)) == 0)
return 1;
if (memcmp(&gEveryoneGuid, groupGUID, sizeof(guid_t)) == 0)
return 1;
if (memcmp(&gNetAccountsGuid, groupGUID, sizeof(guid_t)) == 0)
return ((user->fIsUser || user->fIsComputer) && user->fIsLocalAccount == false);
if (memcmp(&gLocalAccountsGuid, groupGUID, sizeof(guid_t)) == 0)
return ((user->fIsUser || user->fIsComputer) && user->fIsLocalAccount == true);
GenerateItemMembership(user);
result = HashLookup(user->fGUIDMembershipHash, groupGUID);
if ( result == NULL )
{
UserGroup *everyone = GetItemWithGUID( &gEveryoneGuid );
if ( everyone != NULL ) {
GenerateItemMembership( everyone );
result = HashLookup(everyone->fGUIDMembershipHash, groupGUID);
}
}
if ( result == NULL && user->fIsLocalAccount == false )
{
UserGroup *netAccts = GetItemWithGUID( &gNetAccountsGuid );
if ( netAccts != NULL ) {
GenerateItemMembership( netAccts );
result = HashLookup(netAccts->fGUIDMembershipHash, groupGUID);
}
}
if ( result == NULL && user->fIsLocalAccount == true )
{
UserGroup *localAccts = GetItemWithGUID( &gLocalAccountsGuid );
if ( localAccts != NULL ) {
GenerateItemMembership( localAccts );
result = HashLookup(localAccts->fGUIDMembershipHash, groupGUID);
}
}
return (result != NULL);
}
int IsUserMemberOfGroupBySID(UserGroup* user, ntsid_t* groupSID)
{
ntsid_t tempSID;
UserGroup* result;
if (groupSID->sid_authcount > KAUTH_NTSID_MAX_AUTHORITIES)
return 0;
memset(&tempSID, 0, sizeof(ntsid_t));
memcpy(&tempSID, groupSID, KAUTH_NTSID_SIZE(groupSID));
if ((user->fSID != NULL) && memcmp(user->fSID, &tempSID, sizeof(ntsid_t)) == 0)
return 1;
if (memcmp(&gEveryoneSID, &tempSID, sizeof(ntsid_t)) == 0)
return 1;
GenerateItemMembership(user);
result = HashLookup(user->fSIDMembershipHash, &tempSID);
if ( result == NULL )
{
UserGroup *everyone = GetItemWithGUID( &gEveryoneGuid );
if ( everyone != NULL ) {
GenerateItemMembership( everyone );
result = HashLookup(everyone->fSIDMembershipHash, &tempSID);
}
}
return (result != NULL);
}
int IsUserMemberOfGroupByGID(UserGroup* user, int gid)
{
UserGroup* result;
guid_t temp;
GenerateItemMembership(user);
result = HashLookup(user->fGIDMembershipHash, (void*)&gid);
memset(&temp, 0, sizeof(guid_t));
*(uid_t*)&temp = gid;
return (result != NULL);
}
int Get16Groups(UserGroup* user, gid_t* gidArray)
{
UserGroup* groups[16];
int numGroups;
int numResults = 1;
int i;
gidArray[0] = user->fPrimaryGroup;
GenerateItemMembership(user);
numGroups = GetHashEntries(user->fGUIDMembershipHash, groups, 16);
for (i=0; i<numGroups; i++)
{
if (groups[i]->fID != user->fPrimaryGroup)
{
gidArray[numResults++] = groups[i]->fID;
}
if (numResults == 16) break;
}
return numResults;
}
int GetAllGroups(UserGroup* user, gid_t** gidArray)
{
int numResults = 0;
GenerateItemMembership(user);
(*gidArray) = NULL;
HashTable* hash = user->fGUIDMembershipHash;
if ( hash != NULL && hash->fTable != NULL )
{
gid_t *itemArray = NULL;
int iTemp = 1; int i;
for (i = 0; i < hash->fTableSize; i++)
{
if (hash->fTable[i] != NULL)
iTemp++;
}
(*gidArray) = itemArray = (gid_t *) calloc( iTemp, sizeof(gid_t) );
itemArray[numResults++] = user->fPrimaryGroup;
for (i = 0; i < hash->fTableSize; i++)
{
if (hash->fTable[i] != NULL)
{
UserGroup* group = hash->fTable[i];
if (group->fID != user->fPrimaryGroup)
itemArray[numResults++] = group->fID;
}
}
}
return numResults;
}
void ResetUserGroup(UserGroup* ug)
{
int checkval;
ReleaseHash(ug->fGUIDMembershipHash);
ReleaseHash(ug->fSIDMembershipHash);
ReleaseHash(ug->fGIDMembershipHash);
RemoveFromHash(gGUIDHash, ug);
if (ug->fName != NULL)
{
if (ug->fIsUser)
RemoveFromHash(gUserNameHash, ug);
else if (ug->fIsComputer)
RemoveFromHash(gComputerNameHash, ug);
else
RemoveFromHash(gGroupNameHash, ug);
free(ug->fName);
ug->fName = NULL;
}
if (ug->fSID != NULL)
{
RemoveFromHash(gSIDHash, ug);
free(ug->fSID);
ug->fSID = NULL;
}
if (ug->fIsUser)
RemoveFromHash(gUIDHash, ug);
else
RemoveFromHash(gGIDHash, ug);
RemoveFromList(ug);
checkval = ug->fCheckVal;
memset(ug, 0, sizeof(UserGroup));
ug->fCheckVal = checkval;
}
UserGroup* GetNewUGStruct(void)
{
UserGroup* result = NULL;
uint32_t currentTime = GetElapsedSeconds();
while (result == NULL)
{
if (gNumItemsInCache < gMaxGUIDSInCache)
{
result = (UserGroup*)malloc(sizeof(UserGroup));
memset(result, 0, sizeof(UserGroup));
gNumItemsInCache++;
}
if (result == NULL)
{
UserGroup* current = gListTail;
while (current != NULL)
{
if (current->fRefCount == 0 && currentTime > current->fLoginExpiration)
{
break;
}
current = current->fBackLink;
}
if (current != NULL)
{
ResetUserGroup(current);
result = current;
}
else
{
gMaxGUIDSInCache += 10;
}
}
}
return result;
}
void UpdateIDOrNameHash(HashTable* hash, UserGroup* result, void* resultKey)
{
UserGroup* idLookup = HashLookup(hash, resultKey);
if ((idLookup != NULL) && (idLookup != result))
{
RemoveFromHash(hash, idLookup);
}
if ((idLookup == NULL) || (idLookup != result))
{
AddToHash(hash, result);
}
}
UserGroup* FindOrAddUG(UserGroup* templateUG, int hasGUID, int hasID, int foundByID, int foundByName)
{
UserGroup* result = NULL;
if (!hasID)
{
if (templateUG->fName != NULL)
{
free(templateUG->fName);
templateUG->fName = NULL;
}
if (templateUG->fSID != NULL)
{
free(templateUG->fSID);
templateUG->fSID = NULL;
}
return NULL;
}
else
{
if (!hasGUID)
{
long* temp = (long*)&templateUG->fGUID;
if (templateUG->fIsUser)
{
temp[0] = htonl(0xFFFFEEEE);
temp[1] = htonl(0xDDDDCCCC);
temp[2] = htonl(0xBBBBAAAA);
temp[3] = htonl(templateUG->fID);
}
else
{
temp[0] = htonl(0xAAAABBBB);
temp[1] = htonl(0xCCCCDDDD);
temp[2] = htonl(0xEEEEFFFF);
temp[3] = htonl(templateUG->fID);
}
}
}
result = HashLookup(gGUIDHash, &templateUG->fGUID);
if (result == NULL || ItemOutdated(result))
{
int checkval;
if (result == NULL)
result = GetNewUGStruct();
else
{
templateUG->fRefCount = result->fRefCount;
ResetUserGroup(result);
}
checkval = result->fCheckVal;
memcpy(result, templateUG, sizeof(UserGroup));
result->fCheckVal = checkval + 1;
AddToHeadOfList(result);
AddToHash(gGUIDHash, result);
if (result->fSID != NULL)
AddToHash(gSIDHash, result);
}
else
{
if (templateUG->fName != NULL)
{
free(templateUG->fName);
templateUG->fName = NULL;
}
if (templateUG->fSID != NULL)
{
free(templateUG->fSID);
templateUG->fSID = NULL;
}
}
if (foundByID)
{
if (result->fIsUser)
UpdateIDOrNameHash(gUIDHash, result, (void*)&result->fID);
else
UpdateIDOrNameHash(gGIDHash, result, (void*)&result->fID);
}
if (foundByName && (result->fName != 0))
{
if (result->fIsUser)
UpdateIDOrNameHash(gUserNameHash, result, result->fName);
else if (result->fIsComputer)
UpdateIDOrNameHash(gComputerNameHash, result, result->fName);
else
UpdateIDOrNameHash(gGroupNameHash, result, result->fName);
}
return result;
}
void ConvertBytesToHex(char** string, char** data, int numBytes)
{
int i;
for (i=0; i < numBytes; i++)
{
unsigned char hi = ((**data) >> 4) & 0xf;
unsigned char low = (**data) & 0xf;
if (hi < 10)
**string = '0' + hi;
else
**string = 'A' + hi - 10;
(*string)++;
if (low < 10)
**string = '0' + low;
else
**string = 'A' + low - 10;
(*string)++;
(*data)++;
}
}
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]);
}
}
void ConvertSIDFromString(char* string, ntsid_t* sid)
{
char* current = string+2;
int count = 0;
long long temp;
memset(sid, 0, sizeof(ntsid_t));
if (string[0] != 'S' || string[1] != '-') return;
sid->sid_kind = strtoul(current, ¤t, 10);
if (*current == '\0') return;
current++;
temp = strtoll(current, ¤t, 10);
temp = OSSwapHostToBigInt64(temp);
memcpy(sid->sid_authority, ((char*)&temp)+2, 6);
while (*current != '\0')
{
current++;
sid->sid_authorities[count] = strtoul(current, ¤t, 10);
count++;
}
sid->sid_authcount = count;
}
void ConvertGUIDToString(guid_t* data, char* string)
{
char* guid = (char*)data;
char* strPtr = string;
ConvertBytesToHex(&strPtr, &guid, 4);
*strPtr = '-'; strPtr++;
ConvertBytesToHex(&strPtr, &guid, 2);
*strPtr = '-'; strPtr++;
ConvertBytesToHex(&strPtr, &guid, 2);
*strPtr = '-'; strPtr++;
ConvertBytesToHex(&strPtr, &guid, 2);
*strPtr = '-'; strPtr++;
ConvertBytesToHex(&strPtr, &guid, 6);
*strPtr = '\0';
}
void ConvertGUIDFromString(char* string, guid_t* destination)
{
short dataIndex = 0;
int isFirstNibble = 1;
while (*string != '\0' && dataIndex < 16)
{
char nibble;
if (*string >= '0' && *string <= '9')
nibble = *string - '0';
else if (*string >= 'A' && *string <= 'F')
nibble = *string - 'A' + 10;
else if (*string >= 'a' && *string <= 'f')
nibble = *string - 'a' + 10;
else
{
string++;
continue;
}
if (isFirstNibble)
{
destination->g_guid[dataIndex] = nibble << 4;
isFirstNibble = 0;
}
else
{
destination->g_guid[dataIndex] |= nibble;
dataIndex++;
isFirstNibble = 1;
}
string++;
}
}
void GenerateItemMembership(UserGroup* item)
{
char guidString[37];
UserGroup* primary = NULL;
if (item->fGUIDMembershipHash != NULL) return;
item->fRefCount++;
item->fGUIDMembershipHash = CreateHash(__offsetof(UserGroup, fGUID), sizeof(guid_t), 0, 1);
item->fSIDMembershipHash = CreateHash(__offsetof(UserGroup, fSID), sizeof(ntsid_t), 1, 1);
item->fGIDMembershipHash = CreateHash(__offsetof(UserGroup, fID), sizeof(uid_t), 0, 1);
if (item->fIsUser)
{
primary = GetGroupWithGID(item->fPrimaryGroup);
}
if (primary != NULL)
{
AddToHash(item->fGUIDMembershipHash, primary);
if (primary->fSID != NULL)
AddToHash(item->fSIDMembershipHash, primary);
AddToHash(item->fGIDMembershipHash, primary);
}
ConvertGUIDToString(&item->fGUID, guidString);
if ( (item->fIsUser) || (item->fIsComputer) )
{
uint64_t microsec = GetElapsedMicroSeconds();
DoRecordSearch(TYPE_GROUP, kDSNAttrGroupMembership, item->fName, item);
DoRecordSearch(TYPE_GROUP, kDSNAttrGroupMembers, guidString, item);
if (primary != NULL)
{
char primaryguidString[37];
ConvertGUIDToString(&primary->fGUID, primaryguidString);
DoRecordSearch(TYPE_GROUP, kDSNAttrNestedGroups, primaryguidString, item);
}
microsec = GetElapsedMicroSeconds() - microsec;
AddToAverage(&gStatBlock->fAverageuSecPerMembershipSearch, &gStatBlock->fTotalMembershipSearches, (uint32_t)microsec);
}
else
{
DoRecordSearch(TYPE_GROUP, kDSNAttrNestedGroups, guidString, item);
}
item->fRefCount--;
}
UserGroup* DoRecordSearch(int recordType, char* attribute, char* value, UserGroup* membershipRoot)
{
UInt32 recCount = 1;
tContextData localContext = 0;
UInt32 buffSize = 4096;
tDataBufferPtr searchBuffer = dsDataBufferAllocate( gMbrdDirRef, buffSize );
tDataListPtr recType;
UserGroup* result = NULL;
tDirStatus status;
unsigned int recordIndex;
unsigned int attrIndex;
uint64_t microsec = GetElapsedMicroSeconds();
uint64_t totalTime = 0;
int foundByID, foundByName;
if (recordType == TYPE_USER)
recType = gUserType;
else if (recordType == TYPE_COMPUTER)
recType = gComputerType;
else if (recordType == TYPE_GROUP)
recType = gGroupType;
else
recType = gUnknownType;
tDataNodePtr attrType = dsDataNodeAllocateString(gMbrdDirRef, attribute);
tDataNodePtr lookUpPtr = dsDataNodeAllocateString(gMbrdDirRef, value);
do {
do {
recCount = 0;
status = dsDoAttributeValueSearchWithData(gMbrdSearchNode, searchBuffer, recType,
attrType, eDSExact, lookUpPtr, gAttrsToGet, 0,
&recCount, &localContext);
if (status == eDSBufferTooSmall) {
buffSize *= 2;
if ( buffSize > 1024 * 1024 )
break;
dsDataBufferDeAllocate( gMbrdDirRef, searchBuffer );
searchBuffer = dsDataBufferAllocate( gMbrdDirRef, buffSize );
if ( searchBuffer == NULL )
status = eMemoryError;
}
} while (((status == eDSNoErr) && (recCount == 0) && (localContext != 0)) ||
(status == eDSBufferTooSmall));
if ((status == eDSNoErr) && (recCount <= 0))
break;
if ( status == eDSInvalidContinueData )
{
localContext = 0;
continue;
}
else if (status != eDSNoErr)
{
break;
}
for (recordIndex = 1; recordIndex <= recCount; ++recordIndex)
{
tAttributeValueListRef attributeValueListRef = 0;
tAttributeListRef attributeListRef = 0;
tRecordEntryPtr recordEntryPtr = NULL;
tAttributeEntryPtr attributeInfo = NULL;
tAttributeValueEntryPtr attrValue = NULL;
int hasGUID = 0;
int hasID = 0;
status = dsGetRecordEntry(gMbrdSearchNode, searchBuffer, recordIndex, &attributeListRef, &recordEntryPtr);
if (status == eDSNoErr)
{
char* recTypeStr = NULL;
UserGroup templateUG;
memset(&templateUG, 0, sizeof(UserGroup));
templateUG.fExpiration = GetElapsedSeconds() + gDefaultExpiration;
templateUG.fLoginExpiration = GetElapsedSeconds() + gLoginExpiration;
status = dsGetRecordTypeFromEntry( recordEntryPtr, &recTypeStr );
if (status == eDSNoErr && (strcmp(recTypeStr, kDSStdRecordTypeUsers) == 0) )
{
templateUG.fIsUser = 1;
templateUG.fIsComputer = 0;
}
else if (status == eDSNoErr && (strcmp(recTypeStr, kDSStdRecordTypeComputers) == 0) )
{
templateUG.fIsUser = 0;
templateUG.fIsComputer = 1;
}
else if (status == eDSNoErr && ( (strcmp(recTypeStr, kDSStdRecordTypeGroups) == 0) || (strcmp(recTypeStr, kDSStdRecordTypeComputerGroups) == 0) ) )
{
templateUG.fIsUser = 0;
templateUG.fIsComputer = 0;
}
else
{
if (status == eDSNoErr) free(recTypeStr);
continue;
}
free(recTypeStr);
bool bWasSetByCopyTimestamp = false;
for (attrIndex = 1; attrIndex <= recordEntryPtr->fRecordAttributeCount; ++attrIndex)
{
status = dsGetAttributeEntry(gMbrdSearchNode, searchBuffer, attributeListRef,
attrIndex, &attributeValueListRef, &attributeInfo);
if (status == eDSNoErr)
{
status = dsGetAttributeValue(gMbrdSearchNode, searchBuffer, 1, attributeValueListRef, &attrValue);
if (status == eDSNoErr)
{
char* attrName = (char*)attributeInfo->fAttributeSignature.fBufferData;
if (strcmp(attrName, kDSNAttrRecordName) == 0)
{
char* temp = (char*)(attrValue->fAttributeValueData.fBufferData);
templateUG.fName = (char *)malloc(strlen(temp) + 1);
strcpy(templateUG.fName, temp);
}
else if (strcmp(attrName, kDS1AttrGeneratedUID) == 0)
{
char* temp = (char*)(attrValue->fAttributeValueData.fBufferData);
ConvertGUIDFromString(temp, &templateUG.fGUID);
hasGUID = 1;
}
else if (strcmp(attrName, kDS1AttrSMBSID) == 0)
{
char* temp = (char*)(attrValue->fAttributeValueData.fBufferData);
templateUG.fSID = (ntsid_t*)malloc(sizeof(ntsid_t));
ConvertSIDFromString(temp, templateUG.fSID);
}
else if (strcmp(attrName, kDS1AttrUniqueID) == 0)
{
char* temp = (char*)(attrValue->fAttributeValueData.fBufferData);
templateUG.fID = strtol(temp, NULL, 10);
hasID = 1;
}
else if ( (strcmp(attrName, kDS1AttrENetAddress) == 0) && (hasID != 1) ) {
char* ptr = (char*)(attrValue->fAttributeValueData.fBufferData);
ptr = strstr(ptr, ":"); ptr++;
ptr = strstr(ptr, ":"); ptr++;
char *holder = (char *)calloc(1, 11);
holder[0]='0';
holder[1]='x';
int cnt = 2;
while ( (ptr != NULL) && (cnt < 10) )
{
if (ptr[0] != ':')
{
if ( ((ptr+1 == NULL) || (ptr[1] == ':')) && (cnt != 9) && ( (cnt % 2) == 0) )
{
holder[cnt] = '0'; cnt++;
}
holder[cnt] = ptr[0];
cnt++;
}
ptr++;
}
templateUG.fID = strtol(holder, NULL, 16);
free(holder);
hasID = 1;
}
else if (strcmp(attrName, kDS1AttrTimeToLive) == 0)
{
int multiplier = 1;
char* temp = (char*)(attrValue->fAttributeValueData.fBufferData);
char* endPtr;
int num = strtol(temp, &endPtr, 10);
switch (*endPtr)
{
case 's':
multiplier = 1;
break;
case 'm':
multiplier = 60;
break;
case 'h':
multiplier = 60 * 60;
break;
case 'd':
multiplier = 60 * 60 * 24;
break;
}
templateUG.fExpiration = GetElapsedSeconds() + num * multiplier;
}
else if (strcmp(attrName, kDS1AttrPrimaryGroupID) == 0)
{
char* temp = (char*)(attrValue->fAttributeValueData.fBufferData);
int id = strtol(temp, NULL, 10);
if (templateUG.fIsUser)
{
templateUG.fPrimaryGroup = id;
}
else
{
templateUG.fID = id;
hasID = 1;
}
}
else if ( bWasSetByCopyTimestamp == false && strcmp(attrName, kDSNAttrMetaNodeLocation) == 0 )
{
char* temp = (char*)(attrValue->fAttributeValueData.fBufferData);
templateUG.fIsLocalAccount = false;
if ( strcmp(temp, "/Local/Default") == 0 || strcmp(temp, "/BSD/local") == 0 )
templateUG.fIsLocalAccount = true;
}
else if ( strcmp(attrName, kDS1AttrCopyTimestamp) == 0 && attrValue->fAttributeValueData.fBufferLength > 0 )
{
templateUG.fIsLocalAccount = false;
bWasSetByCopyTimestamp = true; }
dsDeallocAttributeValueEntry(gMbrdDirRef, attrValue);
}
dsDeallocAttributeEntry(gMbrdDirRef, attributeInfo);
dsCloseAttributeValueList(attributeValueListRef);
}
}
dsDeallocRecordEntry(gMbrdDirRef, recordEntryPtr);
dsCloseAttributeList(attributeListRef);
foundByID = ((strcmp(attribute, kDS1AttrUniqueID) == 0) || (strcmp(attribute, kDS1AttrPrimaryGroupID) == 0));
foundByName = (strcmp(attribute, kDSNAttrRecordName) == 0);
gMbrGlobalMutex.WaitLock();
result = FindOrAddUG(&templateUG, hasGUID, hasID, foundByID, foundByName);
if (result)
{
if (membershipRoot)
{
AddToHash(membershipRoot->fGUIDMembershipHash, result);
if (result->fSID != NULL)
AddToHash(membershipRoot->fSIDMembershipHash, result);
AddToHash(membershipRoot->fGIDMembershipHash, result);
if (result->fGUIDMembershipHash == NULL)
{
totalTime += GetElapsedMicroSeconds() - microsec;
GenerateItemMembership(result);
microsec = GetElapsedMicroSeconds();
}
MergeHashEntries(membershipRoot->fGUIDMembershipHash, result->fGUIDMembershipHash);
MergeHashEntries(membershipRoot->fSIDMembershipHash, result->fSIDMembershipHash);
MergeHashEntries(membershipRoot->fGIDMembershipHash, result->fGIDMembershipHash);
if (result->fExpiration < membershipRoot->fExpiration)
membershipRoot->fExpiration = result->fExpiration;
}
else
{
gMbrGlobalMutex.SignalLock();
if (localContext != 0)
{
dsReleaseContinueData(gMbrdSearchNode, localContext);
}
localContext = 0;
break; }
}
gMbrGlobalMutex.SignalLock();
}
}
} while ( localContext != 0 );
dsDataBufferDeAllocate(gMbrdDirRef, searchBuffer);
dsDataNodeDeAllocate(gMbrdDirRef, attrType);
dsDataNodeDeAllocate(gMbrdDirRef, lookUpPtr);
totalTime += GetElapsedMicroSeconds() - microsec;
if (strcmp(attribute, kDSNAttrGroupMembers) == 0)
AddToAverage(&gStatBlock->fAverageuSecPerGUIDMemberSearch, &gStatBlock->fTotalGUIDMemberSearches, (uint32_t)totalTime);
else if (strcmp(attribute, kDSNAttrNestedGroups) == 0)
AddToAverage(&gStatBlock->fAverageuSecPerNestedMemberSearch, &gStatBlock->fTotalNestedMemberSearches, (uint32_t)totalTime);
else if (strcmp(attribute, kDSNAttrGroupMembership) == 0)
AddToAverage(&gStatBlock->fAverageuSecPerLegacySearch, &gStatBlock->fTotalLegacySearches, (uint32_t)totalTime);
return result;
}
void RemoveFromList(UserGroup* ug)
{
if (ug->fLink == NULL)
gListTail = ug->fBackLink;
else
ug->fLink->fBackLink = ug->fBackLink;
if (ug->fBackLink == NULL)
gListHead = ug->fLink;
else
ug->fBackLink->fLink = ug->fLink;
}
void AddToHeadOfList(UserGroup* ug)
{
if (gListHead == NULL)
{
gListHead = gListTail = ug;
ug->fLink = NULL;
}
else
{
ug->fLink = gListHead;
gListHead->fBackLink = ug;
gListHead = ug;
}
ug->fBackLink = NULL;
}
void TouchItem(UserGroup* item)
{
RemoveFromList(item);
AddToHeadOfList(item);
}
void* UIDCacheIndexToPointer(TempUIDCacheBlockBase* block, int inIndex)
{
void* result;
if (block->fKind == kUUIDBlock || block->fKind == kSmallSIDBlock)
{
TempUIDCacheBlockSmall* temp = (TempUIDCacheBlockSmall*)block;
result = &temp->fGUIDs[inIndex];
}
else
{
TempUIDCacheBlockLarge* temp = (TempUIDCacheBlockLarge*)block;
result = &temp->fSIDs[inIndex];
}
return result;
}
uid_t GetTempID(void* id, int blockKind, int idSize)
{
TempUIDCacheBlockBase* block = gUIDCache;
TempUIDCacheBlockBase* lasttypeblock = NULL;
TempUIDCacheBlockBase* lastblock = NULL;
while (block != NULL)
{
int i;
if (block->fKind == (uid_t) blockKind)
{
lasttypeblock = block;
for (i = 0; i < block->fNumIDs; i++)
{
if (memcmp(id, UIDCacheIndexToPointer(lasttypeblock, i), idSize) == 0)
return block->fStartID + i;
}
}
lastblock = block;
block = block->fNext;
}
if ((lasttypeblock == NULL) || (lasttypeblock->fNumIDs == 1024))
{
if (idSize == sizeof(guid_t))
{
lasttypeblock = (TempUIDCacheBlockBase*)malloc(sizeof(TempUIDCacheBlockSmall));
memset(lasttypeblock, 0, sizeof(TempUIDCacheBlockSmall));
}
else
{
lasttypeblock = (TempUIDCacheBlockBase*)malloc(sizeof(TempUIDCacheBlockLarge));
memset(lasttypeblock, 0, sizeof(TempUIDCacheBlockLarge));
}
lasttypeblock->fKind = blockKind;
if (lastblock != NULL)
{
lastblock->fNext = lasttypeblock;
lasttypeblock->fStartID = lastblock->fStartID + 1024;
}
else
{
gUIDCache = lasttypeblock;
lasttypeblock->fStartID = 0x82000000;
}
}
memcpy(UIDCacheIndexToPointer(lasttypeblock, lasttypeblock->fNumIDs), id, idSize);
lasttypeblock->fNumIDs++;
return lasttypeblock->fStartID + lasttypeblock->fNumIDs - 1;
}
uid_t GetTempIDForGUID(guid_t* guid)
{
return GetTempID(guid, kUUIDBlock, sizeof(guid_t));
}
uid_t GetTempIDForSID(ntsid_t* sid)
{
if (sid->sid_authcount <= 2)
return GetTempID(sid, kSmallSIDBlock, sizeof(guid_t));
return GetTempID(sid, kLargeSIDBlock, sizeof(ntsid_t));
}
bool FindTempID(uid_t id, guid_t** guid, ntsid_t** sid)
{
TempUIDCacheBlockBase* block = gUIDCache;
*guid = NULL;
*sid = NULL;
if (id < 0x82000000) return false;
while ((block != NULL) && (block->fStartID + 1024 <= id))
block = block->fNext;
if (block == NULL) return false;
if (block->fStartID + block->fNumIDs < id) return false;
if (block->fKind == kUUIDBlock)
*guid = (guid_t*)UIDCacheIndexToPointer(block, id - block->fStartID);
else
*sid = (ntsid_t*)UIDCacheIndexToPointer(block, id - block->fStartID);
return true;
}