Mbrd_MembershipResolver.cpp [plain text]
#include "Mbrd_MembershipResolver.h"
#include "Mbrd_Cache.h"
#include <sys/syslog.h>
#include <libkern/OSByteOrder.h>
#include <mach/mach_error.h>
#include <string.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h> // for fcntl() and O_* flags
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <uuid/uuid.h>
#include <DirectoryServiceCore/CLog.h>
#include <DirectoryServiceCore/DSMutexSemaphore.h>
#include <DirectoryService/DirectoryService.h>
#include <DirectoryService/DirServicesConstPriv.h>
#include <assert.h>
#include <membership.h>
#include <dispatch/dispatch.h>
#include <gssapi/gssapi.h>
#include "CInternalDispatch.h"
#include "CPlugInList.h"
#include "CCachePlugin.h"
#include <map>
#include <string>
#include <membershipPriv.h>
#define kMaxItemsInCacheStr "MaxItemsInCache"
#define kDefaultExpirationStr "DefaultExpirationInSecs"
#define kDefaultMaximumRefreshInSecsStr "DefaultMaximumRefreshInSecs"
#define kDefaultNegativeExpirationStr "DefaultFailureExpirationInSecs"
#define kDefaultKernelExpirationInSecsStr "DefaultKernelExpirationInSecs"
#define kKerberosFallbackToRecordName "KerberosFallbackToRecordName"
#define kKernelResolverTimeout "KernelResolverTimeout"
#define kUUIDBlock 1
#define kSmallSIDBlock 2
#define kLargeSIDBlock 3
#define WELL_KNOWN_RID_BASE 1000
#define COMPATIBLITY_SID_PREFIX "S-1-5-21-987654321-987654321-987654321"
#define COMPATIBLITY_SID_PREFIX_SIZE (sizeof(COMPATIBLITY_SID_PREFIX)-1)
typedef struct TempUIDCacheBlockBase
{
struct TempUIDCacheBlockBase* fNext;
int fKind;
int fNumIDs;
uid_t fStartID;
} TempUIDCacheBlockBase;
typedef struct TempUIDCacheBlockSmall
{
struct TempUIDCacheBlock* fNext;
int fKind;
int fNumIDs;
uid_t fStartID;
uuid_t fGUIDs[1024];
} TempUIDCacheBlockSmall;
typedef struct TempUIDCacheBlockLarge
{
struct TempUIDCacheBlock* fNext;
int fKind;
int fNumIDs;
uid_t fStartID;
ntsid_t fSIDs[1024];
} TempUIDCacheBlockLarge;
extern bool gCacheFlushDisabled;
extern dsBool gDSLocalOnlyMode;
extern dsBool gDSInstallDaemonMode;
extern dsBool gServerOS;
extern CPlugInList *gPlugins;
int gKernelTimeout = 60;
static MbrdCache *gMbrdCache = NULL;
static tDirReference gMbrdDirRef = 0;
static tDirNodeReference gMbrdSearchNode = 0;
static tDataListPtr gUserType = NULL;
static tDataListPtr gAllGroupTypes = NULL;
static tDataListPtr gUnknownType = NULL;
static tDataListPtr gAttrsToGet = NULL;
static uuid_t gEveryoneUUID;
static ntsid_t gEveryoneSID;
static uuid_t gLocalAccountsUUID;
static uuid_t gNetAccountsUUID;
static uuid_t gRootUserUUID;
static DSMutexSemaphore gMbrdGlobalMutex( "::gMbrdGlobalMutex" );
static StatBlock gStatBlock;
static TempUIDCacheBlockBase *gUIDCache = NULL;
static map<string, string> sidMap;
static pthread_mutex_t sidMapLock = PTHREAD_MUTEX_INITIALIZER;
static dispatch_queue_t gLookupQueue = NULL;
static pthread_key_t gMembershipThreadKey = NULL;
extern CCachePlugin *gCacheNode;
extern uint32_t gNumberOfCores;
extern sCacheValidation* ParsePasswdEntry( tDirReference inDirRef, tDirNodeReference inNodeRef, kvbuf_t *inBuffer, tDataBufferPtr inDataBuffer,
tRecordEntryPtr inRecEntry, tAttributeListRef inAttrListRef, void *additionalInfo, CCache *inCache,
const char **inKeys );
extern sCacheValidation* ParseGroupEntry( tDirReference inDirRef, tDirNodeReference inNodeRef, kvbuf_t *inBuffer, tDataBufferPtr inDataBuffer,
tRecordEntryPtr inRecEntry, tAttributeListRef inAttrListRef, void *additionalInfo, CCache *inCache,
const char **inKeys );
#pragma mark -
#pragma mark Internal Routines
static void Mbrd_AddToAverage( uint64_t* average, uint64_t* numDataPoints, uint64_t newDataPoint )
{
gMbrdGlobalMutex.WaitLock();
*average = (((*average) * (*numDataPoints)) + newDataPoint) / (*numDataPoints + 1);
__sync_add_and_fetch( numDataPoints, 1 );
gMbrdGlobalMutex.SignalLock();
}
static int IsCompatibilityGUID( uuid_t guid, int* isUser, uid_t* uid )
{
uint32_t * temp = (uint32_t *)guid;
int result = 0;
if ((temp[0] == htonl(0xFFFFEEEE)) && (temp[1] == htonl(0xDDDDCCCC)) && (temp[2] == htonl(0xBBBBAAAA)))
{
*uid = ntohl(temp[3]);
*isUser = 1;
result = 1;
}
else if ((temp[0] == htonl(0xAAAABBBB)) && (temp[1] == htonl(0xCCCCDDDD)) && (temp[2] == htonl(0xEEEEFFFF)))
{
*uid = ntohl(temp[3]);
*isUser = 0;
result = 1;
}
return result;
}
static 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;
}
static TempUIDCacheBlockBase *Mbrd_EntryForID( void *id, int blockKind, int idSize, TempUIDCacheBlockBase **lastblock, uid_t *tempID )
{
gMbrdGlobalMutex.WaitLock();
TempUIDCacheBlockBase* block = gUIDCache;
TempUIDCacheBlockBase* returnValue = NULL;
while ( returnValue == NULL && block != NULL )
{
int i;
if ( block->fKind == blockKind )
{
for ( i = 0; i < block->fNumIDs; i++ )
{
if ( memcmp(id, UIDCacheIndexToPointer(block, i), idSize) == 0 ) {
(*tempID) = block->fStartID + i;
returnValue = block;
break;
}
}
}
(*lastblock) = block;
block = block->fNext;
}
gMbrdGlobalMutex.SignalLock();
return returnValue;
}
static uid_t Mbrd_CreateTempID( void *id, int blockKind, int idSize )
{
TempUIDCacheBlockBase *lastblock = NULL;
uid_t tempID = 0;
TempUIDCacheBlockBase *lasttypeblock = Mbrd_EntryForID( id, blockKind, idSize, &lastblock, &tempID );
if ( lasttypeblock != NULL ) {
return tempID;
}
gMbrdGlobalMutex.WaitLock();
if ((lasttypeblock == NULL) || (lasttypeblock->fNumIDs == 1024))
{
if (idSize == sizeof(uuid_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++;
tempID = lasttypeblock->fStartID + lasttypeblock->fNumIDs - 1;
gMbrdGlobalMutex.SignalLock();
return tempID;
}
static uid_t Mbrd_CreateTempIDForGUID( uuid_t guid )
{
return Mbrd_CreateTempID( guid, kUUIDBlock, sizeof(guid) );
}
static uid_t Mbrd_CreateTempIDForSID( ntsid_t* sid)
{
if (sid->sid_authcount <= 2)
return Mbrd_CreateTempID( sid, kSmallSIDBlock, sizeof(*sid) );
return Mbrd_CreateTempID( sid, kLargeSIDBlock, sizeof(*sid) );
}
static bool Mbrd_ConvertSIDFromString(const char* sidString, ntsid_t* sid)
{
char* current = NULL;
int count = 0;
long long temp;
memset(sid, 0, sizeof(ntsid_t));
if (sidString[0] != 'S' || sidString[1] != '-') return false;
sid->sid_kind = strtoul(sidString+2, ¤t, 10);
if (*current == '\0') return false;
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;
return true;
}
static UserGroup *Mbrd_AddNegative( tDataListPtr recType, int idType, const char *value, uint32_t flags )
{
__sync_add_and_fetch( &gStatBlock.fNumFailedRecordLookups, 1 );
UserGroup *result = UserGroup_Create();
char *endPtr = NULL;
id_t theId;
if ( recType == gAllGroupTypes ) {
result->fRecordType = kUGRecordTypeGroup | kUGRecordTypeComputerGroup;
}
else if ( recType == gUserType ) {
result->fRecordType = kUGRecordTypeUser | kUGRecordTypeComputer;
}
else {
result->fRecordType = kUGRecordTypeUnknown;
}
switch ( idType )
{
case ID_TYPE_UID:
case ID_TYPE_GID:
theId = strtol( value, &endPtr, 10 );
if ( endPtr == NULL || endPtr[0] == '\0' ) {
result->fID = theId;
result->fFlags = kUGFlagHasID | kUGFlagNotFound;
result->fFoundBy = kUGFoundByID;
}
else {
UserGroup_Release( result );
result = NULL;
}
break;
case ID_TYPE_SID:
if ( Mbrd_ConvertSIDFromString(value, &result->fSID) ) {
result->fFlags = kUGFlagHasSID | kUGFlagNotFound;
result->fFoundBy = kUGFoundBySID;
if ( (flags & kKernelRequest) != 0 ) {
result->fID = Mbrd_CreateTempIDForSID( &result->fSID );
result->fFlags |= kUGFlagHasID;
result->fFoundBy |= kUGFoundByID;
}
}
break;
case ID_TYPE_USERNAME:
case ID_TYPE_GROUPNAME:
result->fName = strdup( value );
result->fFlags = kUGFlagNotFound;
result->fFoundBy = kUGFoundByName;
break;
case ID_TYPE_X509_DN:
result->fX509DN[0] = strdup( value );
result->fFlags = kUGFlagNotFound;
result->fFoundBy = kUGFoundByX509DN;
break;
case ID_TYPE_KERBEROS:
result->fKerberos[0] = strdup( value );
result->fFlags = kUGFlagNotFound;
result->fFoundBy = kUGFoundByKerberos;
break;
case ID_TYPE_GUID:
if ( uuid_parse(value, result->fGUID) == 0 ) {
result->fFlags = kUGFlagHasGUID | kUGFlagNotFound;
result->fFoundBy = kUGFoundByGUID;
if ( (flags & kKernelRequest) != 0 ) {
result->fID = Mbrd_CreateTempIDForGUID( result->fGUID );
result->fFlags |= kUGFlagHasID;
result->fFoundBy |= kUGFoundByID;
}
}
else {
UserGroup_Release( result );
result = NULL;
}
break;
default:
UserGroup_Release( result );
result = NULL;
break;
}
if ( result != NULL ) {
return MbrdCache_AddOrUpdate( gMbrdCache, result, 0 );
}
return NULL;
}
static void ParseConfigEntry( tDirNodeReference nodeRef, tDataBufferPtr searchBuffer, UInt32 count )
{
tAttributeValueListRef attributeValueListRef = 0;
tAttributeListRef attributeListRef = 0;
tRecordEntryPtr recordEntryPtr = NULL;
tAttributeEntryPtr attributeInfo = NULL;
tAttributeValueEntryPtr attrValue = NULL;
for ( UInt32 recIndex = 1; recIndex <= count; recIndex++ )
{
tDirStatus status = dsGetRecordEntry( nodeRef, searchBuffer, recIndex, &attributeListRef, &recordEntryPtr );
if ( status == eDSNoErr )
{
char sidStr[MBR_MAX_SID_STRING_SIZE] = { 0 };
char *nodeStr = NULL;
for ( UInt32 attrIndex = 1; attrIndex <= recordEntryPtr->fRecordAttributeCount; attrIndex++ )
{
status = dsGetAttributeEntry( nodeRef, searchBuffer, attributeListRef, attrIndex, &attributeValueListRef, &attributeInfo);
if ( status == eDSNoErr )
{
status = dsGetAttributeValue( nodeRef, searchBuffer, 1, attributeValueListRef, &attrValue );
if ( status == eDSNoErr )
{
char *attribute = attributeInfo->fAttributeSignature.fBufferData;
if ( strcmp(attribute, kDS1AttrXMLPlist) == 0 ) {
CFDataRef data = CFDataCreateWithBytesNoCopy( kCFAllocatorDefault, (UInt8 *) attrValue->fAttributeValueData.fBufferData,
attrValue->fAttributeValueData.fBufferLength, kCFAllocatorNull );
CFPropertyListRef dict = CFPropertyListCreateFromXMLData( kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL );
if ( CFGetTypeID(dict) == CFDictionaryGetTypeID() ) {
CFStringRef sid = (CFStringRef) CFDictionaryGetValue( (CFDictionaryRef) dict, CFSTR("SID") );
if ( sid != NULL ) {
CFStringGetCString( sid, sidStr, sizeof(sidStr), kCFStringEncodingUTF8 );
}
}
DSCFRelease( data );
DSCFRelease( dict );
}
else if ( strcmp(attribute, kDSNAttrMetaNodeLocation) == 0 ) {
nodeStr = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData, attrValue->fAttributeValueData.fBufferLength );
}
else if ( strcmp(attribute, kDS1AttrSMBSID) == 0 ) {
if ( attrValue->fAttributeValueData.fBufferLength < MBR_MAX_SID_STRING_SIZE ) {
bcopy( attrValue->fAttributeValueData.fBufferData, sidStr, attrValue->fAttributeValueData.fBufferLength );
sidStr[attrValue->fAttributeValueData.fBufferLength] = '\0';
}
}
dsDeallocAttributeValueEntry( nodeRef, attrValue );
}
dsCloseAttributeValueList( attributeValueListRef );
dsDeallocAttributeEntry( nodeRef, attributeInfo );
}
}
if ( sidStr[0] != '\0' && nodeStr != NULL ) {
sidMap[nodeStr] = sidStr;
DbgLog( kLogInfo, "Mbrd_CacheSIDSFromNode - Node: %s, SID Prefix: %s", nodeStr, sidStr );
}
DSFree( nodeStr );
dsDeallocRecordEntry( nodeRef, recordEntryPtr );
dsCloseAttributeList( attributeListRef );
}
}
}
static void Mbrd_CacheSIDSFromNode( const char *inNodeName, const char *inRecordType, const char *inRecordName, const char *inAttrType )
{
UInt32 buffSize = 4096;
tDataBufferPtr searchBuffer = dsDataBufferAllocatePriv( buffSize );
tDataListPtr nodeName = dsBuildFromPathPriv( inNodeName, "/" );
tDataListPtr nameList = dsBuildListFromStringsPriv( inRecordName, NULL );
tDataListPtr recTypeList = dsBuildListFromStringsPriv( inRecordType, NULL );
tDataListPtr attrTypeList = dsBuildListFromStringsPriv( inAttrType, kDSNAttrMetaNodeLocation, NULL );
UInt32 recCount = 0;
tDirNodeReference nodeRef = 0;
tContextData localContext = 0;
Mbrd_SetMembershipThread( true );
tDirStatus status = dsOpenDirNode( gMbrdDirRef, nodeName, &nodeRef );
if ( status == eDSNoErr )
{
do {
do {
status = dsGetRecordList( nodeRef, searchBuffer, nameList, eDSiExact, recTypeList, attrTypeList, 0,
&recCount, &localContext );
if ( status == eDSNoErr ) {
ParseConfigEntry( nodeRef, searchBuffer, recCount );
}
else if ( status == eDSBufferTooSmall ) {
buffSize *= 2;
if ( buffSize > 1024 * 1024 )
break;
dsDataBufferDeAllocate( gMbrdDirRef, searchBuffer );
searchBuffer = dsDataBufferAllocate( gMbrdDirRef, buffSize );
if ( searchBuffer == NULL ) {
goto done;
}
}
} while (((status == eDSNoErr) && (recCount == 0) && (localContext != 0)) ||
(status == eDSBufferTooSmall));
} while ( localContext != 0 );
done:
if ( localContext != 0 ) {
dsReleaseContinueData( gMbrdDirRef, localContext );
localContext = 0;
}
dsCloseDirNode( nodeRef );
}
dsDataBufferDeallocatePriv( searchBuffer );
dsDataListDeallocatePriv( nodeName );
free( nodeName );
dsDataListDeallocatePriv( nameList );
free( nameList );
dsDataListDeallocatePriv( recTypeList );
free( recTypeList );
dsDataListDeallocatePriv( attrTypeList );
free( attrTypeList );
Mbrd_SetMembershipThread( false );
}
static const char *Mbrd_GetNodenameOrSIDFromCache( const char *inNodeName, const char *inPrefix )
{
map<string, string>::iterator iter;
const char *result = NULL;
pthread_mutex_lock( &sidMapLock );
if ( sidMap.empty() ) {
Mbrd_CacheSIDSFromNode( "/Local/Default", kDSStdRecordTypeComputers, "localhost", kDS1AttrSMBSID );
if ( sidMap.find("/Local/Default") == sidMap.end() ) {
uuid_t hostuuid;
struct timespec timeout = {0};
if ( gethostuuid(hostuuid, &timeout) == 0 ) {
ntsid_t hostsid = { 0 };
char tempResult[MBR_MAX_SID_STRING_SIZE];
hostsid.sid_kind = 1;
hostsid.sid_authcount = 4;
hostsid.sid_authority[5] = 5;
hostsid.sid_authorities[0] = 21;
hostsid.sid_authorities[1] = ((uint32_t)hostuuid[4] << 24) |
((uint32_t)hostuuid[5] << 16) |
((uint32_t)hostuuid[6] << 8) |
(uint32_t)hostuuid[7];
hostsid.sid_authorities[2] = ((uint32_t)hostuuid[8] << 24) |
((uint32_t)hostuuid[9] << 16) |
((uint32_t)hostuuid[10] << 8) |
(uint32_t)hostuuid[11];
hostsid.sid_authorities[3] = ((uint32_t)hostuuid[12] << 24) |
((uint32_t)hostuuid[13] << 16) |
((uint32_t)hostuuid[14] << 8) |
(uint32_t)hostuuid[15];
ConvertSIDToString( tempResult, &hostsid );
sidMap["/Local/Default"] = tempResult;
}
}
Mbrd_CacheSIDSFromNode( "/Search", kDSStdRecordTypeConfig, "CIFSServer", kDS1AttrXMLPlist );
}
if ( inPrefix != NULL )
{
if ( strcmp(inPrefix, COMPATIBLITY_SID_PREFIX) != 0 )
{
for ( iter = sidMap.begin(); iter != sidMap.end(); iter++ )
{
if ( iter->second == inPrefix ) {
result = iter->first.c_str();
break;
}
}
}
else {
result = "/Search";
}
}
else if ( inNodeName != NULL )
{
iter = sidMap.find( inNodeName );
if ( iter != sidMap.end() ) {
result = iter->second.c_str();
}
else if ( strcmp(inNodeName, "/Local/Default") != 0 ) {
Mbrd_CacheSIDSFromNode( inNodeName, kDSStdRecordTypeConfig, "CIFSServer", kDS1AttrXMLPlist );
iter = sidMap.find( inNodeName );
if ( iter != sidMap.end() ) {
result = iter->second.c_str();
}
if ( result == NULL ) {
sidMap[inNodeName] = COMPATIBLITY_SID_PREFIX;
result = COMPATIBLITY_SID_PREFIX;
}
}
}
pthread_mutex_unlock( &sidMapLock );
return result;
}
UserGroup **Mbrd_FindItemsAndRetain( tDirNodeReference dirNode, tDataListPtr recType, int idType, const char *value, uint32_t flags, UInt32 *recCount )
{
UInt32 count;
tContextData localContext = 0;
UInt32 buffSize = 4096;
tDataBufferPtr searchBuffer = dsDataBufferAllocate( gMbrdDirRef, buffSize );
UserGroup* result = NULL;
UserGroup** results = NULL;
tDirStatus status;
unsigned int recordIndex;
unsigned int attrIndex;
uint64_t microsec = GetElapsedMicroSeconds();
uint64_t totalTime = 0;
UInt32 totalCnt = 0;
uint32_t foundBy = 0;
uint64_t *statTime = &gStatBlock.fAverageuSecPerRecordLookup;
uint64_t *statCount = &gStatBlock.fTotalRecordLookups;
const char *attribute = NULL;
tDirPatternMatch match = eDSExact;
switch ( idType )
{
case ID_TYPE_UID:
attribute = kDS1AttrUniqueID;
foundBy = kUGFoundByID;
break;
case ID_TYPE_GID:
attribute = kDS1AttrPrimaryGroupID;
foundBy = kUGFoundByID;
break;
case ID_TYPE_SID:
attribute = kDS1AttrSMBSID;
foundBy = kUGFoundBySID;
break;
case ID_TYPE_RID:
attribute = kDS1AttrSMBRID;
foundBy = kUGFoundBySID;
break;
case ID_TYPE_GROUPSID:
attribute = kDS1AttrSMBPrimaryGroupSID;
foundBy = kUGFoundBySID;
break;
case ID_TYPE_GROUPRID:
attribute = kDS1AttrSMBGroupRID;
foundBy = kUGFoundBySID;
break;
case ID_TYPE_USERNAME:
case ID_TYPE_GROUPNAME:
attribute = kDSNAttrRecordName;
foundBy = kUGFoundByName;
break;
case ID_TYPE_X509_DN:
attribute = kDSNAttrAltSecurityIdentities;
foundBy = kUGFoundByX509DN;
break;
case ID_TYPE_KERBEROS:
attribute = kDSNAttrAltSecurityIdentities;
foundBy = kUGFoundByKerberos;
break;
case ID_TYPE_GUID:
attribute = kDS1AttrGeneratedUID;
foundBy = kUGFoundByGUID;
match = eDSiExact;
break;
case ID_TYPE_GROUPMEMBERS:
attribute = kDSNAttrGroupMembers;
foundBy = kUGFoundByNestedGroup;
statTime = &gStatBlock.fAverageuSecPerGUIDMemberSearch;
statCount = &gStatBlock.fTotalGUIDMemberSearches;
break;
case ID_TYPE_GROUPMEMBERSHIP:
attribute = kDSNAttrGroupMembership;
foundBy = kUGFoundByNestedGroup;
statTime = &gStatBlock.fAverageuSecPerLegacySearch;
statCount = &gStatBlock.fTotalLegacySearches;
break;
case ID_TYPE_NESTEDGROUPS:
attribute = kDSNAttrNestedGroups;
foundBy = kUGFoundByNestedGroup;
statTime = &gStatBlock.fAverageuSecPerNestedMemberSearch;
statCount = &gStatBlock.fTotalNestedMemberSearches;
break;
}
tDataNodePtr attrType = dsDataNodeAllocateString( gMbrdDirRef, attribute );
tDataNodePtr lookUpPtr = dsDataNodeAllocateString( gMbrdDirRef, value );
Mbrd_SetMembershipThread( true );
do {
do {
count = (*recCount);
status = dsDoAttributeValueSearchWithData(dirNode, searchBuffer, recType,
attrType, match, lookUpPtr, gAttrsToGet, 0,
&count, &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 || status == eDSBadContextData )
{
localContext = 0;
continue;
}
else if (status != eDSNoErr)
{
break;
}
if ( count > 0 ) {
if ( results != NULL ) {
results = (UserGroup **) reallocf( results, (totalCnt + count) * sizeof(UserGroup *) );
}
else {
results = (UserGroup **) malloc( count * sizeof(UserGroup *) );
}
}
for (recordIndex = 1; recordIndex <= count; ++recordIndex)
{
tAttributeValueListRef attributeValueListRef = 0;
tAttributeListRef attributeListRef = 0;
tRecordEntryPtr recordEntryPtr = NULL;
tAttributeEntryPtr attributeInfo = NULL;
tAttributeValueEntryPtr attrValue = NULL;
status = dsGetRecordEntry(dirNode, searchBuffer, recordIndex, &attributeListRef, &recordEntryPtr);
if (status == eDSNoErr)
{
char *recTypeStr = NULL;
bool serviceAccount = false;
status = dsGetRecordTypeFromEntry( recordEntryPtr, &recTypeStr );
if ( status == eDSNoErr )
{
result = UserGroup_Create();
if ( strcmp(recTypeStr, kDSStdRecordTypeUsers) == 0 ) {
const char *keys[] = { "pw_name", "pw_uid", "pw_gecos", NULL }; sCacheValidation *valid;
result->fRecordType = kUGRecordTypeUser;
if ( gCacheNode != NULL ) {
valid = ParsePasswdEntry( gMbrdDirRef, dirNode, NULL, searchBuffer, recordEntryPtr, attributeListRef, NULL,
gCacheNode->fLibinfoCache, keys );
DSRelease( valid );
}
}
else if ( strcmp(recTypeStr, kDSStdRecordTypeComputers) == 0 ) {
result->fRecordType = kUGRecordTypeComputer;
}
else if ( strcmp(recTypeStr, kDSStdRecordTypeComputerGroups) == 0 ) {
result->fRecordType = kUGRecordTypeComputerGroup;
}
else {
#if defined(PRE_CACHE_GROUPS)
const char *keys[] = { "gr_name", "gr_gid", "gr_uuid", NULL };
sCacheValidation *valid;
#endif
result->fRecordType = kUGRecordTypeGroup;
#if defined(PRE_CACHE_GROUPS)
if ( gCacheNode != NULL ) {
valid = ParseGroupEntry( gMbrdDirRef, dirNode, NULL, searchBuffer, recordEntryPtr, attributeListRef,
NULL, gCacheNode->fLibinfoCache, keys );
DSRelease( valid );
}
#endif
}
result->fFoundBy = foundBy;
}
else
{
free( recTypeStr );
continue;
}
free(recTypeStr);
char *smbRID = NULL;
char *smbGroupRID = NULL;
char *smbPrimaryGroupSID = NULL;
char *origHome = NULL;
bool bWasSetByCopyTimestamp = false;
for (attrIndex = 1; attrIndex <= recordEntryPtr->fRecordAttributeCount; ++attrIndex)
{
status = dsGetAttributeEntry(dirNode, searchBuffer, attributeListRef,
attrIndex, &attributeValueListRef, &attributeInfo);
if (status == eDSNoErr)
{
status = dsGetAttributeValue(dirNode, searchBuffer, 1, attributeValueListRef, &attrValue);
if (status == eDSNoErr)
{
char* attrName = attributeInfo->fAttributeSignature.fBufferData;
if (strcmp(attrName, kDSNAttrRecordName) == 0)
{
result->fName = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData,
attrValue->fAttributeValueData.fBufferLength );
if (result->fName != NULL) {
result->fFlags |= kUGFlagHasName;
}
}
else if (strcmp(attrName, kDSNAttrAltSecurityIdentities) == 0 )
{
int iKerb = 0;
int iX509 = 0;
UInt32 index = 1;
do
{
if ( attrValue->fAttributeValueData.fBufferLength > sizeof("Kerberos:") &&
strncasecmp(attrValue->fAttributeValueData.fBufferData, "Kerberos:", sizeof("Kerberos:")-1) == 0 )
{
if ( iKerb < kMaxAltIdentities ) {
result->fKerberos[iKerb] = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData+(sizeof("Kerberos:")-1),
attrValue->fAttributeValueData.fBufferLength-(sizeof("Kerberos:")-1) );
iKerb++;
result->fFlags |= kUGFlagHasKerberos;
}
else {
DbgLog( kLogError, "Mbrd_FindItemsAndRetain - more than '%d' Kerberos identities '%s' for user is not supported",
kMaxAltIdentities, attrValue->fAttributeValueData.fBufferData );
}
}
else if ( attrValue->fAttributeValueData.fBufferLength > sizeof("X509:") &&
strncasecmp(attrValue->fAttributeValueData.fBufferData, "X509:", sizeof("X509:")-1) == 0 )
{
if ( iX509 < kMaxAltIdentities ) {
result->fX509DN[iX509] = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData+(sizeof("X509:")-1),
attrValue->fAttributeValueData.fBufferLength-(sizeof("X509:")-1) );
iX509++;
result->fFlags |= kUGFlagHasX509DN;
}
else {
DbgLog( kLogError, "Mbrd_FindItemsAndRetain - more than '%d' X509 identities '%s' for user is not supported",
kMaxAltIdentities, attrValue->fAttributeValueData.fBufferData );
}
}
if ( ++index <= attributeInfo->fAttributeValueCount ) {
if ( attrValue != NULL ) {
dsDeallocAttributeValueEntry( gMbrdDirRef, attrValue );
attrValue = NULL;
}
status = dsGetAttributeValue( dirNode, searchBuffer, index, attributeValueListRef, &attrValue );
continue;
}
break;
} while ( 1 );
}
else if (strcmp(attrName, kDS1AttrGeneratedUID) == 0)
{
if ( attrValue->fAttributeValueData.fBufferLength >= sizeof(uuid_t) ) {
uuid_parse( attrValue->fAttributeValueData.fBufferData, result->fGUID );
result->fFlags |= kUGFlagHasGUID;
}
}
else if (strcmp(attrName, kDS1AttrSMBRID) == 0)
{
smbRID = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData,
attrValue->fAttributeValueData.fBufferLength );
}
else if (strcmp(attrName, kDS1AttrSMBGroupRID) == 0)
{
smbGroupRID = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData,
attrValue->fAttributeValueData.fBufferLength );
}
else if (strcmp(attrName, kDS1AttrSMBSID) == 0)
{
char *temp = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData,
attrValue->fAttributeValueData.fBufferLength );
if ( Mbrd_ConvertSIDFromString(temp, &result->fSID) ) {
result->fFlags |= kUGFlagHasSID;
}
DSFree( temp );
}
else if (strcmp(attrName, kDS1AttrUniqueID) == 0)
{
char *temp = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData,
attrValue->fAttributeValueData.fBufferLength );
char *endPtr = NULL;
int num = strtol(temp, &endPtr, 10);
if ( endPtr == NULL || (*endPtr) == '\0' ) {
result->fID = num;
result->fFlags |= kUGFlagHasID;
}
DSFree( temp );
}
else if (strcmp(attrName, kDS1AttrTimeToLive) == 0)
{
int multiplier = 1;
char* endPtr = NULL;
char *temp = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData,
attrValue->fAttributeValueData.fBufferLength );
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;
}
DSFree( temp );
result->fExpiration = GetElapsedSeconds() + num * multiplier;
}
else if (strcmp(attrName, kDS1AttrPrimaryGroupID) == 0)
{
char *temp = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData,
attrValue->fAttributeValueData.fBufferLength );
char* endPtr = NULL;
int num = strtol( temp, &endPtr, 10 );
if ( endPtr == NULL || endPtr[0] == '\0' )
{
if ( (result->fRecordType & (kUGRecordTypeUser | kUGRecordTypeComputer)) != 0 ) {
result->fPrimaryGroup = num;
}
else {
result->fID = num;
result->fFlags |= kUGFlagHasID;
}
}
DSFree( temp );
}
else if ( strcmp(attrName, kDSNAttrMetaNodeLocation) == 0 )
{
char *temp = result->fNode = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData,
attrValue->fAttributeValueData.fBufferLength );
if ( bWasSetByCopyTimestamp == false && (strcmp(temp, "/Local/Default") == 0 || strcmp(temp, "/BSD/local") == 0) )
result->fFlags |= kUGFlagLocalAccount;
char *nodeName = strtok( attrValue->fAttributeValueData.fBufferData, "/" );
if ( nodeName != NULL ) {
result->fToken = gPlugins->GetValidDataStamp( nodeName );
}
result->fNodeAvailable = true;
temp = NULL; }
else if ( strcmp(attrName, kDS1AttrCopyTimestamp) == 0 && attrValue->fAttributeValueData.fBufferLength > 0 )
{
result->fFlags &= ~kUGFlagLocalAccount;
bWasSetByCopyTimestamp = true; }
else if ( strcmp(attrName, kDS1AttrSMBPrimaryGroupSID) == 0 ) {
smbPrimaryGroupSID = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData,
attrValue->fAttributeValueData.fBufferLength );
}
else if ( strcmp(attrName, kDS1AttrOriginalNodeName) == 0 ) {
origHome = dsCStrFromCharacters( attrValue->fAttributeValueData.fBufferData,
attrValue->fAttributeValueData.fBufferLength );
}
else if ( strcmp(attrName, kDSNAttrKeywords) == 0 ) {
UInt32 index = 1;
do {
if ( strcmp(attrValue->fAttributeValueData.fBufferData, "com.apple.ServiceAccount") == 0 ) {
serviceAccount = true;
break;
}
if ( ++index <= attributeInfo->fAttributeValueCount ) {
if ( attrValue != NULL ) {
dsDeallocAttributeValueEntry( gMbrdDirRef, attrValue );
attrValue = NULL;
}
status = dsGetAttributeValue( dirNode, searchBuffer, index, attributeValueListRef, &attrValue );
continue;
}
break;
} while (1);
}
if ( attrValue != NULL ) {
dsDeallocAttributeValueEntry(gMbrdDirRef, attrValue);
}
}
dsDeallocAttributeEntry(gMbrdDirRef, attributeInfo);
dsCloseAttributeValueList(attributeValueListRef);
}
}
dsDeallocRecordEntry(gMbrdDirRef, recordEntryPtr);
dsCloseAttributeList(attributeListRef);
if ( (result->fFlags & kUGFlagHasSID) == 0 ) {
const char *sidPrefix = Mbrd_GetNodenameOrSIDFromCache( (origHome ? : result->fNode), NULL );
if ( sidPrefix != NULL ) {
void (^calcSID)(const char *) = ^(const char *sidAttr) {
if ( sidAttr == NULL ) return;
char sidStr[MBR_MAX_SID_STRING_SIZE];
strlcpy( sidStr, sidPrefix, sizeof(sidStr) );
strlcat( sidStr, "-", sizeof(sidStr) );
strlcat( sidStr, sidAttr, sizeof(sidStr) );
if ( Mbrd_ConvertSIDFromString(sidStr, &result->fSID) ) {
result->fFlags |= kUGFlagHasSID;
}
};
if ( (result->fRecordType & (kUGRecordTypeUser | kUGRecordTypeComputer)) != 0 ) {
calcSID( smbRID );
}
else if ( (result->fRecordType & kUGRecordTypeGroup) != 0 ) {
if ( smbPrimaryGroupSID != NULL ) {
if ( Mbrd_ConvertSIDFromString(smbPrimaryGroupSID, &result->fSID) ) {
result->fFlags |= kUGFlagHasSID;
}
} else {
calcSID( smbGroupRID );
if ( (result->fFlags & kUGFlagHasSID) == 0 ) {
calcSID( smbRID );
}
}
}
if ( (result->fFlags & kUGFlagHasSID) == 0 ) {
char ridStr[16];
uint32_t rid = ((result->fID << 1) + WELL_KNOWN_RID_BASE) |
((result->fRecordType & (kUGRecordTypeUser | kUGRecordTypeComputer)) != 0 ? 0 : 1);
snprintf( ridStr, sizeof(ridStr), "%u", rid );
calcSID( ridStr );
}
}
}
DSFree( smbRID );
DSFree( smbGroupRID );
DSFree( smbPrimaryGroupSID );
DSFree( origHome );
if ( serviceAccount == true ) {
UserGroup_Release( result );
result = NULL;
continue;
}
if ( (flags & kKernelRequest) != 0 &&
result->fRecordType != kUGRecordTypeComputerGroup &&
(result->fFlags & kUGFlagHasID) == 0 &&
(result->fFlags & kUGFlagHasGUID) != 0 )
{
result->fID = Mbrd_CreateTempIDForGUID( result->fGUID );
result->fFlags |= kUGFlagHasID;
result->fFoundBy |= kUGFoundByID; }
UserGroup *temp = MbrdCache_AddOrUpdate( gMbrdCache, result, flags );
if ( temp != NULL ) {
results[totalCnt++] = temp;
temp = NULL;
}
}
}
} while ( localContext != 0 || status == eDSInvalidContinueData || status == eDSBadContextData );
if ( localContext != 0 ) {
dsReleaseContinueData( gMbrdDirRef, localContext );
localContext = 0;
}
Mbrd_SetMembershipThread( false );
dsDataBufferDeAllocate( gMbrdDirRef, searchBuffer );
dsDataNodeDeAllocate( gMbrdDirRef, attrType );
dsDataNodeDeAllocate( gMbrdDirRef, lookUpPtr );
totalTime += GetElapsedMicroSeconds() - microsec;
Mbrd_AddToAverage( statTime, statCount, totalTime );
switch(idType) {
case ID_TYPE_GROUPMEMBERS:
case ID_TYPE_GROUPMEMBERSHIP:
case ID_TYPE_NESTEDGROUPS:
Mbrd_AddToAverage(&gStatBlock.fAverageuSecPerMembershipSearch, &gStatBlock.fTotalMembershipSearches, totalTime);
break;
}
(*recCount) = totalCnt;
return results;
}
static UserGroup *Mbrd_FindItemAndRetain( tDirNodeReference dirNode, tDataListPtr recType, int idType, const char *value, uint32_t flags )
{
UInt32 count = 1;
UserGroup **items = Mbrd_FindItemsAndRetain( dirNode, recType, idType, value, flags, &count );
UserGroup *result = NULL;
if ( count > 0 ) {
result = items[0];
items[0] = NULL;
for ( UInt32 ii = 1; ii < count; ii++ ) {
UserGroup_Release( items[ii] );
items[ii] = NULL;
}
free( items );
items = NULL;
}
if ( result == NULL && (flags & kNoNegativeEntry) == 0 ) {
result = Mbrd_AddNegative( recType, idType, value, flags );
}
return result;
}
static UserGroup *__Mbrd_FindItemWithIdentifierAndRetain( UserGroup *origItem, int idType, const char *identifier, int32_t flags )
{
UserGroup *item = NULL;
switch ( idType )
{
case ID_TYPE_UID:
item = Mbrd_FindItemAndRetain( gMbrdSearchNode, gUserType, idType, identifier, flags );
break;
case ID_TYPE_USERNAME:
case ID_TYPE_X509_DN:
case ID_TYPE_KERBEROS:
item = Mbrd_FindItemAndRetain( gMbrdSearchNode, gUserType, idType, identifier, flags );
break;
case ID_TYPE_GID:
item = Mbrd_FindItemAndRetain( gMbrdSearchNode, gAllGroupTypes, idType, identifier, flags );
break;
case ID_TYPE_GROUPNAME:
item = Mbrd_FindItemAndRetain( gMbrdSearchNode, gAllGroupTypes, idType, identifier, flags );
break;
case ID_TYPE_SID:
if ( strncmp(identifier, COMPATIBLITY_SID_PREFIX, COMPATIBLITY_SID_PREFIX_SIZE) != 0 ) {
item = Mbrd_FindItemAndRetain( gMbrdSearchNode, gUnknownType, idType, identifier, flags | kNoNegativeEntry );
if ( item == NULL ) {
item = Mbrd_FindItemAndRetain( gMbrdSearchNode, gUnknownType, ID_TYPE_GROUPSID, identifier, flags | kNoNegativeEntry );
}
}
if ( item == NULL && origItem == NULL ) {
ntsid_t expectedSID;
char *rid;
Mbrd_ConvertSIDFromString( identifier, &expectedSID );
rid = strrchr( identifier, '-' );
if ( rid != NULL ) {
(*rid) = '\0';
rid++;
const char *nodeName = Mbrd_GetNodenameOrSIDFromCache( NULL, identifier );
if ( nodeName != NULL ) {
tDirNodeReference dirNode = 0;
tDataListPtr dirNodeName = dsBuildFromPathPriv( nodeName, "/" );
if ( dsOpenDirNode(gMbrdDirRef, dirNodeName, &dirNode) == eDSNoErr ) {
item = Mbrd_FindItemAndRetain( dirNode, gUnknownType, ID_TYPE_RID, rid, flags | kNoNegativeEntry );
if ( item == NULL ) {
item = Mbrd_FindItemAndRetain( dirNode, gUnknownType, ID_TYPE_GROUPRID, rid, flags | kNoNegativeEntry );
}
uint32_t ridInt = strtoul( rid, NULL, 10 );
if ( item == NULL && ridInt >= WELL_KNOWN_RID_BASE ) {
char ridStr[16];
uint32_t uid = (ridInt - 1000) >> 1;
snprintf( ridStr, sizeof(ridStr), "%u", uid );
if ( (ridInt & 1) == 0 ) {
item = Mbrd_FindItemAndRetain( dirNode, gUserType, ID_TYPE_UID, ridStr, flags );
}
else {
item = Mbrd_FindItemAndRetain( dirNode, gAllGroupTypes, ID_TYPE_GID, ridStr, flags );
}
}
if ( item != NULL ) {
if ( memcmp(&expectedSID, &item->fSID, sizeof(ntsid_t)) == 0 ) {
item->fFoundBy |= kUGFoundBySID; }
else {
UserGroup_Release( item );
item = NULL;
}
}
dsCloseDirNode( dirNode );
dirNode = 0;
}
dsDataListDeallocatePriv( dirNodeName );
free( dirNodeName );
dirNodeName = NULL;
}
*(--rid) = '-';
}
}
if ( item == NULL && origItem == NULL ) {
item = Mbrd_AddNegative( gUnknownType, idType, identifier, flags );
}
break;
case ID_TYPE_GUID:
item = Mbrd_FindItemAndRetain( gMbrdSearchNode, gUnknownType, ID_TYPE_GUID, identifier, flags );
break;
}
return item;
}
static UserGroup* __Mbrd_GetItemWithIdentifierAndRetain( MbrdCache *cache, int idType, const void *identifier, int32_t flags )
{
if ( cache == NULL || identifier == NULL ) return NULL;
const char *reqOrigin = ((flags & kKernelRequest) != 0 ? "mbr_syscall" : "mbr_mig");
UserGroup *item = NULL;
int32_t foundBy = 0;
id_t theID;
int isUser;
guid_t *guid = (guid_t *) identifier;
const char *stringVal = (char *) identifier;
tDataListPtr recType = NULL;
char *(^copyIdentifierAsString)(int, const void *) = ^(int theType, const void *theIdentifier) {
char *returnValue = NULL;
switch ( theType )
{
case ID_TYPE_UID:
case ID_TYPE_GID:
asprintf( &returnValue, "%d", *((id_t *) theIdentifier) );
break;
case ID_TYPE_USERNAME:
case ID_TYPE_GROUPNAME:
case ID_TYPE_X509_DN:
case ID_TYPE_KERBEROS:
returnValue = strdup( (char *) theIdentifier );
break;
case ID_TYPE_SID:
returnValue = (char *) calloc( MBR_MAX_SID_STRING_SIZE, sizeof(char) );
ConvertSIDToString( returnValue, (ntsid_t *) theIdentifier );
break;
case ID_TYPE_GUID:
returnValue = (char *) calloc( 1, sizeof(uuid_string_t) );
uuid_unparse_upper( (unsigned char *) theIdentifier, returnValue );
break;
}
return returnValue;
};
switch ( idType )
{
case ID_TYPE_UID:
case ID_TYPE_USERNAME:
item = MbrdCache_GetAndRetain( cache, kUGRecordTypeUser | kUGRecordTypeComputer, idType, identifier, flags );
recType = gUserType;
break;
case ID_TYPE_X509_DN:
item = MbrdCache_GetAndRetain( cache, kUGRecordTypeUser, idType, stringVal + (sizeof("X509:")-1), flags );
break;
case ID_TYPE_KERBEROS:
item = MbrdCache_GetAndRetain( cache, kUGRecordTypeUser, idType, stringVal + (sizeof("Kerberos:")-1), flags );
recType = gUserType;
break;
case ID_TYPE_GID:
case ID_TYPE_GROUPNAME:
item = MbrdCache_GetAndRetain( cache, kUGRecordTypeGroup | kUGRecordTypeComputerGroup, idType, identifier, flags );
recType = gAllGroupTypes;
break;
case ID_TYPE_SID:
item = MbrdCache_GetAndRetain( cache, kUGRecordTypeUnknown, idType, identifier, flags );
recType = gUnknownType;
break;
case ID_TYPE_GUID:
if ( IsCompatibilityGUID((unsigned char *) guid, &isUser, &theID) == true ) {
if ( isUser ) {
item = MbrdCache_GetAndRetain( cache, kUGRecordTypeUser | kUGRecordTypeComputer, ID_TYPE_UID, &theID, flags );
idType = ID_TYPE_UID;
}
else {
item = MbrdCache_GetAndRetain( cache, kUGRecordTypeGroup | kUGRecordTypeComputerGroup, ID_TYPE_GID, &theID, flags );
idType = ID_TYPE_GID;
}
identifier = &theID;
DbgLog( kLogInfo, "%s - Membership - Compatibility GUID detected, switched to UID/GID", reqOrigin );
}
else {
item = MbrdCache_GetAndRetain( cache, kUGRecordTypeUnknown, idType, guid, flags );
}
recType = gUnknownType;
break;
default:
recType = gUnknownType;
break;
}
if ( item != NULL )
{
bool bAuthoritative = true;
bool bSearchAgain = false;
switch ( idType )
{
case ID_TYPE_UID:
case ID_TYPE_GID:
foundBy = kUGFoundByID;
bAuthoritative = ((item->fFoundBy & kUGFoundByID) == 0);
bSearchAgain = ((__sync_fetch_and_or(&item->fFoundBy, kUGFoundByIDSched) & kUGFoundByIDSched) == 0);
if ( bAuthoritative )
DbgLog( kLogInfo, "%s - Membership - Cache entry was not found by ID (non-authoritative)", reqOrigin );
break;
case ID_TYPE_USERNAME:
case ID_TYPE_GROUPNAME:
foundBy = kUGFoundByName;
bAuthoritative = ((item->fFoundBy & kUGFoundByName) == 0);
bSearchAgain = ((__sync_fetch_and_or(&item->fFoundBy, kUGFoundByNameSched) & kUGFoundByNameSched) == 0);
if ( bAuthoritative )
DbgLog( kLogInfo, "%s - Membership - Cache entry was not found by Name (non-authoritative)", reqOrigin );
break;
case ID_TYPE_X509_DN:
foundBy = kUGFoundByX509DN;
bAuthoritative = ((item->fFoundBy & kUGFoundByX509DN) == 0);
bSearchAgain = ((__sync_fetch_and_or(&item->fFoundBy, kUGFoundByX509DNSched) & kUGFoundByX509DNSched) == 0);
if ( bAuthoritative )
DbgLog( kLogInfo, "%s - Membership - Cache entry was not found by X509DN (non-authoritative)", reqOrigin );
break;
case ID_TYPE_KERBEROS:
foundBy = kUGFoundByKerberos;
bAuthoritative = ((item->fFoundBy & kUGFoundByKerberos) == 0);
bSearchAgain = ((__sync_fetch_and_or(&item->fFoundBy, kUGFoundByKerberosSched) & kUGFoundByKerberosSched) == 0);
if ( bAuthoritative )
DbgLog( kLogInfo, "%s - Membership - Cache entry was not found by KerberosID (non-authoritative)", reqOrigin );
break;
case ID_TYPE_SID:
foundBy = kUGFoundBySID;
bAuthoritative = ((item->fFoundBy & kUGFoundBySID) == 0);
bSearchAgain = ((__sync_fetch_and_or(&item->fFoundBy, kUGFoundBySIDSched) & kUGFoundBySIDSched) == 0);
if ( bAuthoritative )
DbgLog( kLogInfo, "%s - Membership - Cache entry was not found by SID (non-authoritative)", reqOrigin );
break;
case ID_TYPE_GUID:
foundBy = kUGFoundByGUID;
bAuthoritative = ((item->fFoundBy & kUGFoundByGUID) == 0);
bSearchAgain = ((__sync_fetch_and_or(&item->fFoundBy, kUGFoundByGUIDSched) & kUGFoundByGUIDSched) == 0);
if ( bAuthoritative )
DbgLog( kLogInfo, "%s - Membership - Cache entry was not found by GUID (non-authoritative)", reqOrigin );
break;
}
if ( bAuthoritative == false ) {
if ( item != NULL && (item->fFlags & kUGFlagNotFound) == 0 ) {
flags |= kNoNegativeEntry;
}
}
if ( (item->fFlags & kUGFlagIsBuiltin) == 0 ) {
bool bUsable = true;
switch ( idType )
{
case ID_TYPE_GID:
if ( (item->fFlags & kUGFlagReservedID) != 0 ) {
bUsable = false;
}
break;
case ID_TYPE_SID:
if ( (item->fFlags & kUGFlagReservedSID) != 0 ) {
bUsable = false;
}
break;
case ID_TYPE_GROUPNAME:
if ( (item->fFlags & kUGFlagReservedName) != 0 ) {
bUsable = false;
}
break;
}
if ( bUsable == false ) {
DbgLog( kLogInfo, "%s - Membership - Not using cached entry '%s' cause it is a reserved ID/Name/SID but was not local",
reqOrigin, (item->fName ? item->fName : "") );
UserGroup_Release( item );
item = NULL;
bSearchAgain = false;
}
}
if ( bSearchAgain == true ) {
static dispatch_queue_t trampoline;
static dispatch_once_t once;
dispatch_once(&once, ^(void) {
trampoline = dispatch_queue_create("itemRefreshTrampoline", NULL);
dispatch_queue_set_width(trampoline, 32); });
UserGroup *origItem = UserGroup_Retain( item );
char *phID = copyIdentifierAsString( idType, identifier );
dispatch_async(trampoline, ^(void) {
dispatch_sync(origItem->fRefreshQueue, ^(void) {
CInternalDispatch::AddCapability();
UserGroup *tempItem = __Mbrd_FindItemWithIdentifierAndRetain( origItem, idType, phID, flags );
if ( tempItem != NULL ) {
UserGroup_Release( tempItem );
}
});
UserGroup_Release( origItem );
free( phID );
});
}
}
if ( item != NULL ) {
__sync_add_and_fetch( &gStatBlock.fCacheHits, 1 );
}
else {
char *phID = copyIdentifierAsString( idType, identifier );
__sync_add_and_fetch( &gStatBlock.fCacheMisses, 1 );
item = __Mbrd_FindItemWithIdentifierAndRetain( NULL, idType, phID, flags & ~kNoNegativeEntry );
DSFree( phID );
}
if ( item != NULL && (item->fFlags & kUGFlagNotFound) != 0 ) {
if ( (flags & kKernelRequest) == 0 ) {
DbgLog( kLogInfo, "%s - Membership - Cache entry is a negative entry (discarding result)", reqOrigin );
UserGroup_Release( item );
item = NULL;
}
else {
int rc = pthread_mutex_lock( &item->fMutex );
assert( rc == 0 );
if ( (item->fFlags & kUGFlagHasID) == 0 ) {
if ( (item->fFlags & kUGFlagHasGUID) != 0 ) {
item->fID = Mbrd_CreateTempIDForGUID( item->fGUID );
}
else if ( (item->fFlags & kUGFlagHasSID) != 0 ) {
item->fID = Mbrd_CreateTempIDForSID( &item->fSID );
}
item->fFlags |= kUGFlagHasID;
MbrdCache_RefreshHashes( gMbrdCache, item );
}
rc = pthread_mutex_unlock( &item->fMutex );
assert( rc == 0 );
}
}
return item;
}
static UserGroup* Mbrd_GetItemWithIdentifierAndRetain( MbrdCache *cache, int idType, const void *identifier, int32_t flags )
{
__block UserGroup *item = NULL;
dispatch_sync( gLookupQueue,
^(void) {
CInternalDispatch::AddCapability();
item = __Mbrd_GetItemWithIdentifierAndRetain( cache, idType, identifier, flags );
} );
return item;
}
#if 1
static void _addToHashes(UserGroup *item, UserGroup *groupItem, uint32_t flags, dispatch_group_t group);
static bool
_Mbrd_ItemMembership_NeedsRefresh(UserGroup *item, uint32_t flags, bool *outAsyncRefresh)
{
__block bool bAsyncRefresh = ((flags & KAUTH_EXTLOOKUP_REFRESH_MEMBERSHIP) != 0 ? false : true); __block bool bIssueRefresh = false;
dispatch_sync(item->fQueue, ^(void) {
uint32_t currentTime = GetElapsedSeconds();
if ((item->fFlags & kUGFlagValidMembership) == 0
|| ((flags & KAUTH_EXTLOOKUP_REFRESH_MEMBERSHIP) != 0 && item->fMaximumRefresh <= currentTime)) {
if ( __sync_bool_compare_and_swap(&item->fRefreshActive, false, true) == true ) {
if ((item->fFlags & kUGFlagValidMembership) == 0) {
DbgLog(kLogInfo,
"Resolve Groups - '%s' (%s) - generating group memberships",
item->fName ? : "", item->fNode ? : "");
} else {
DbgLog(kLogInfo,
"Resolve Groups - '%s' (%s) force membership refresh requested - %s",
item->fName ? : "", item->fNode ? : "", bAsyncRefresh ? "(Async requested)" : "");
}
bIssueRefresh = true;
bAsyncRefresh = false;
} else if ((item->fFlags & kUGFlagValidMembership) == 0) {
bAsyncRefresh = false;
}
} else if (item->fExpiration <= currentTime) {
if ( __sync_bool_compare_and_swap(&item->fRefreshActive, false, true) == true ) {
DbgLog(kLogInfo,
"Resolve Groups - '%s' (%s) outdated - will refresh entry and group memberships asynchronously",
item->fName ? : "", item->fNode ? : "");
bIssueRefresh = true;
bAsyncRefresh = true;
} else {
DbgLog(kLogDebug,
"Resolve Groups - '%s' outdated but refresh already active",
item->fName ?: "");
}
} else {
DbgLog(kLogDebug,
"Resolve Groups - '%s' fresh %u > %u",
item->fName ?: "", item->fExpiration, currentTime);
}
});
(*outAsyncRefresh) = bAsyncRefresh;
return bIssueRefresh;
}
static void
_Mbrd_ResolveNestedGroups(UserGroup *item, uint32_t flags)
{
uuid_string_t guidString;
char thread_name[256];
char queue_name[256];
UserGroup **items;
UInt32 count = 0;
if (item == NULL || (item->fFlags & kUGFlagNotFound) != 0) {
return;
}
snprintf(thread_name, sizeof(thread_name), "ResolveNestedGroups for '%s'", item->fName);
pthread_setname_np(thread_name);
snprintf(queue_name, sizeof(queue_name), "ResolveNestedGroups.%s", item->fName);
uuid_unparse_upper(item->fGUID, guidString);
items = Mbrd_FindItemsAndRetain(gMbrdSearchNode, gAllGroupTypes, ID_TYPE_NESTEDGROUPS, guidString, flags, &count);
if (items != NULL) {
DbgLog(kLogInfo, "Resolve Groups - adding %d nested groups to membership for '%s'",
count, item->fName ? : "");
for (UInt32 ii = 0; ii < count; ii++) {
UserGroup_AddToHashes(item, items[ii]);
UserGroup_Release(items[ii]);
items[ii] = NULL;
}
DSFree(items);
}
__sync_fetch_and_or(&item->fFlags, kUGFlagValidMembership);
DbgLog(kLogInfo, "Resolve Groups - Finished resolving for %s '%s' - total %d",
((item->fRecordType & (kUGRecordTypeGroup | kUGRecordTypeComputerGroup)) ? "group" : "user"),
item->fName ? : "", item->fGUIDMembershipHash.fNumEntries);
pthread_setname_np("");
}
static void
_addNestedToHashes(UserGroup *item, UserGroup *groupItem, uint32_t flags, dispatch_queue_t queue, dispatch_group_t group)
{
__block UserGroup **groups = NULL;
__block int numResults;
dispatch_sync(groupItem->fQueue, ^(void) {
numResults = HashTable_CreateItemArray(&groupItem->fGIDMembershipHash, &groups);
});
for (int ii = 0; ii < numResults; ii++) {
_addToHashes(item, groups[ii], flags, group);
UserGroup_Release(groups[ii]);
groups[ii] = NULL;
}
DSFree(groups);
}
static void
_addToHashes(UserGroup *item, UserGroup *groupItem, uint32_t flags, dispatch_group_t group)
{
static dispatch_queue_t nested_queue;
static dispatch_once_t once;
dispatch_once(&once, ^(void) {
nested_queue = dispatch_queue_create("ResolveNestedGroups", NULL);
dispatch_queue_set_width(nested_queue, 32); });
if (UserGroup_AddToHashes(item, groupItem) == true) {
bool bAsyncRefresh;
bool bRefresh;
bRefresh = _Mbrd_ItemMembership_NeedsRefresh(groupItem, flags, &bAsyncRefresh);
if (bRefresh == false || bAsyncRefresh == true) {
DbgLog(kLogInfo, "Resolve Groups - adding cached nested memberships from '%s' to '%s'",
groupItem->fName ? : "", item->fName ? : "");
_addNestedToHashes(item, groupItem, flags, nested_queue, group);
__sync_add_and_fetch(&gStatBlock.fCacheHits, 1);
}
if (bRefresh == true) {
if (bAsyncRefresh == true) {
DbgLog(kLogInfo, "Resolve Groups - issuing async refresh for nested group memberships '%s' for '%s'",
groupItem->fName ? : "", item->fName ? : "");
UserGroup_Retain(groupItem);
dispatch_async(nested_queue, ^(void) {
_Mbrd_ResolveNestedGroups(groupItem, flags);
UserGroup_Release(groupItem);
});
} else {
__sync_add_and_fetch(&gStatBlock.fCacheMisses, 1);
DbgLog(kLogInfo, "Resolve Groups - issuing nested group memberships '%s' for '%s'",
groupItem->fName ? : "", item->fName ? : "");
UserGroup_Retain(groupItem);
dispatch_group_async(group, nested_queue, ^(void) {
_Mbrd_ResolveNestedGroups(groupItem, flags);
_addNestedToHashes(item, groupItem, flags, nested_queue, group);
UserGroup_Release(groupItem);
});
}
}
}
}
static void
_Mbrd_GenerateItemMembership(UserGroup *item, uint32_t flags, bool bAsyncRefresh)
{
UserGroup *refreshed = NULL;
uuid_string_t guidString;
char thread_name[256];
int isUser;
uid_t uid;
snprintf(thread_name, sizeof(thread_name), "GenerateItemMembership for '%s' type '%d'", item->fName, item->fRecordType);
pthread_setname_np(thread_name);
uuid_unparse_upper(item->fGUID, guidString);
if ((item->fRecordType & (kUGRecordTypeUser | kUGRecordTypeComputer)) != 0) {
if ((item->fFlags & kUGFoundByGUID) != 0 && IsCompatibilityGUID(item->fGUID, &isUser, &uid) == false) {
uuid_string_t uuidStr;
uuid_unparse_upper(item->fGUID, uuidStr);
refreshed = Mbrd_FindItemAndRetain(gMbrdSearchNode, gUserType, ID_TYPE_GUID, uuidStr, flags);
} else if ((item->fFlags & kUGFoundByID) != 0) {
char uidStr[16];
snprintf(uidStr, sizeof(uidStr), "%d", item->fID);
refreshed = Mbrd_FindItemAndRetain(gMbrdSearchNode, gUserType, ID_TYPE_UID, uidStr, flags);
} else if ((item->fFlags & kUGFoundByName) != 0) {
refreshed = Mbrd_FindItemAndRetain(gMbrdSearchNode, gUserType, ID_TYPE_USERNAME, item->fName, flags);
}
}
if (refreshed != NULL) {
dispatch_group_t group = dispatch_group_create();
UserGroup *workingCopy = UserGroup_Create();
UserGroup **items;
UInt32 count;
UserGroup_Merge(workingCopy, refreshed, false);
if ((item->fRecordType & kUGRecordTypeUser) != 0) {
UserGroup *tempItem = Mbrd_GetItemWithIdentifierAndRetain(gMbrdCache, ID_TYPE_GID, &item->fPrimaryGroup, flags);
if (tempItem != NULL) {
DbgLog(kLogInfo, "Resolve Groups - adding primary group '%d' to membership for '%s'",
tempItem->fID, item->fName ? : "");
_addToHashes(item, tempItem, flags, group);
UserGroup_Release(tempItem);
tempItem = NULL;
}
count = 0;
items = Mbrd_FindItemsAndRetain(gMbrdSearchNode, gAllGroupTypes, ID_TYPE_GROUPMEMBERSHIP, item->fName, flags, &count);
if (items != NULL) {
DbgLog(kLogInfo, "Resolve Groups - adding %d direct name memberships via GUID to membership for '%s'",
count, (item->fName ? : ""));
for (UInt32 ii = 0; ii < count; ii++) {
_addToHashes(item, items[ii], flags, group);
UserGroup_Release(items[ii]);
items[ii] = NULL;
}
DSFree(items);
}
}
UserGroup *tempItem = Mbrd_GetItemWithIdentifierAndRetain(gMbrdCache, ID_TYPE_GUID, gEveryoneUUID, flags);
if (tempItem != NULL) { DbgLog(kLogInfo, "Resolve Groups - adding 'everyone' group %d to membership for '%s'",
tempItem->fID, item->fName ? : "");
_addToHashes(item, tempItem, flags, group);
UserGroup_Release(tempItem);
tempItem = NULL;
}
if ((item->fFlags & kUGFlagLocalAccount) != 0) {
tempItem = Mbrd_GetItemWithIdentifierAndRetain(gMbrdCache, ID_TYPE_GUID, gLocalAccountsUUID, flags);
if (tempItem != NULL) { DbgLog(kLogInfo, "Resolve Groups - adding 'localaccounts' group %d to membership for '%s'",
tempItem->fID, (item->fName ? : ""));
_addToHashes(item, tempItem, flags, group);
UserGroup_Release(tempItem);
tempItem = NULL;
}
} else {
tempItem = Mbrd_GetItemWithIdentifierAndRetain(gMbrdCache, ID_TYPE_GUID, gNetAccountsUUID, flags);
if (tempItem != NULL) { DbgLog(kLogInfo, "Resolve Groups - adding 'netaccounts' group %d to membership for '%s'",
tempItem->fID, (item->fName ? : ""));
_addToHashes(item, tempItem, flags, group);
UserGroup_Release(tempItem);
tempItem = NULL;
}
}
count = 0;
items = Mbrd_FindItemsAndRetain(gMbrdSearchNode, gAllGroupTypes, ID_TYPE_GROUPMEMBERS, guidString, flags, &count);
if (items != NULL) {
DbgLog(kLogInfo, "Resolve Groups - adding %d direct UUID memberships to membership for '%s'",
count, (item->fName ? : ""));
for (UInt32 ii = 0; ii < count; ii++) {
_addToHashes(item, items[ii], flags, group);
UserGroup_Release(items[ii]);
items[ii] = NULL;
}
DSFree(items);
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
UserGroup_Merge(refreshed, workingCopy, true);
if (refreshed != item) {
UserGroup_Merge(item, workingCopy, true);
DbgLog(kLogNotice, "Resolve Groups - '%s' (%s) - inflight membership generation encountered a new entry - syncing groups to original request",
item->fName ? : "", item->fNode ? : "");
}
__sync_fetch_and_or(&item->fFlags, kUGFlagValidMembership);
UserGroup_Release(workingCopy);
UserGroup_Release(refreshed);
}
item->fRefreshActive = false;
}
static void
Mbrd_GenerateItemMembership(UserGroup *item, uint32_t flags)
{
static dispatch_semaphore_t semaphore;
static dispatch_queue_t trampoline;
static dispatch_once_t once;
bool bAsyncRefresh;
bool bIssueRefresh;
dispatch_once(&once, ^(void) {
semaphore = dispatch_semaphore_create(4); trampoline = dispatch_queue_create("membershipRefreshTrampoline", NULL);
});
if (item == NULL) {
return;
}
bIssueRefresh = _Mbrd_ItemMembership_NeedsRefresh(item, flags, &bAsyncRefresh);
if (bIssueRefresh == true) {
item = UserGroup_Retain(item);
dispatch_group_async(item->fRefreshGroup, trampoline, ^(void) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(item->fRefreshGroup, item->fRefreshQueue, ^(void) {
_Mbrd_GenerateItemMembership(item, flags, bAsyncRefresh);
dispatch_semaphore_signal(semaphore);
UserGroup_Release(item);
});
});
}
if (bIssueRefresh == true || bAsyncRefresh == false) {
DbgLog(kLogInfo, "Resolve Groups - '%s' (%s) - waiting for an inflight membership generation to complete",
item->fName ? : "", item->fNode ? : "");
dispatch_group_wait(item->fRefreshGroup, DISPATCH_TIME_FOREVER);
dispatch_sync(item->fQueue, ^(void) {
DbgLog(kLogDebug, "Resolve Groups - '%s' (%s) - finished waiting for an inflight membership generation to complete",
item->fName ? : "", item->fNode ? : "");
});
}
}
#else
static void Mbrd_ResolveGroupsForItem( UserGroup *item, uint32_t flags, UserGroup *membershipRoot = NULL )
{
UserGroup **items;
uuid_string_t guidString;
UInt32 count = 0;
if ( item == NULL || (item->fFlags & kUGFlagNotFound) != 0 )
return;
const char *reqOrigin = ((flags & kKernelRequest) != 0 ? "mbr_syscall" : "mbr_mig");
uuid_unparse_upper( item->fGUID, guidString );
dispatch_queue_t dispatchQueue = dispatch_queue_create("com.apple.DirectoryService.refresh", NULL);
dispatch_group_t dispatchGroup = dispatch_group_create();
void (^addToHashesAndRelease)(UserGroup *, UserGroup *, UserGroup *) = ^(UserGroup *userItem, UserGroup *memberItem, UserGroup *groupItem) {
if ( groupItem != NULL ) {
UserGroup_AddToHashes( groupItem, memberItem );
}
if ( UserGroup_AddToHashes(userItem, memberItem) == true ) {
if ( (memberItem->fFlags & kUGFlagNotFound) == 0 ) {
UserGroup_Retain( memberItem );
dispatch_group_async( dispatchGroup,
dispatchQueue,
^(void) {
DbgLog( kLogInfo, "%s - Membership - Resolve Groups - check nested group '%s' to membership for '%s'",
reqOrigin, memberItem->fName ?: "", userItem->fName ?: "" );
CInternalDispatch::AddCapability();
Mbrd_ResolveGroupsForItem( memberItem, flags, userItem );
UserGroup_Release( memberItem );
} );
}
}
else {
DbgLog( kLogInfo, "%s - Membership - Resolve Groups - group '%s' already in membership for '%s', skipping nested check",
reqOrigin, memberItem->fName ?: "", userItem->fName ?: "" );
}
UserGroup_Release( memberItem ); };
if ( (item->fRecordType & (kUGRecordTypeUser | kUGRecordTypeComputer)) != 0 )
{
if ( (item->fRecordType & kUGRecordTypeUser) != 0 )
{
UserGroup *tempItem = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_GID, &item->fPrimaryGroup, flags );
if ( tempItem != NULL )
{
DbgLog( kLogInfo, "%s - Membership - Resolve Groups - adding primary group '%d' to membership for '%s'",
reqOrigin, tempItem->fID, item->fName ?: "" );
addToHashesAndRelease( item, tempItem, NULL );
tempItem = NULL;
}
count = 0;
items = Mbrd_FindItemsAndRetain( gMbrdSearchNode, gAllGroupTypes, ID_TYPE_GROUPMEMBERSHIP, item->fName, flags, &count );
if ( items != NULL )
{
DbgLog( kLogInfo, "%s - Membership - Resolve Groups - adding %d direct name memberships via GUID to membership for '%s'",
reqOrigin, count, (item->fName ? :"") );
for ( UInt32 ii = 0; ii < count; ii++ )
{
addToHashesAndRelease( item, items[ii], NULL );
items[ii] = NULL;
}
free( items );
items = NULL;
}
}
UserGroup *tempItem = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_GUID, gEveryoneUUID, flags );
if ( tempItem != NULL ) {
DbgLog( kLogInfo, "%s - Membership - Resolve Groups - adding 'everyone' group %d to membership for '%s'",
reqOrigin, tempItem->fID, item->fName ?: "" );
addToHashesAndRelease( item, tempItem, NULL );
tempItem = NULL;
}
if ( (item->fFlags & kUGFlagLocalAccount) != 0 )
{
tempItem = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_GUID, gLocalAccountsUUID, flags );
if ( tempItem != NULL ) {
DbgLog( kLogInfo, "%s - Membership - Resolve Groups - adding 'localaccounts' group %d to membership for '%s'",
reqOrigin, tempItem->fID, (item->fName ? :"") );
addToHashesAndRelease( item, tempItem, NULL );
tempItem = NULL;
}
}
else
{
tempItem = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_GUID, gNetAccountsUUID, flags );
if ( tempItem != NULL ) {
DbgLog( kLogInfo, "%s - Membership - Resolve Groups - adding 'netaccounts' group %d to membership for '%s'",
reqOrigin, tempItem->fID, (item->fName ? :"") );
addToHashesAndRelease( item, tempItem, NULL );
tempItem = NULL;
}
}
count = 0;
items = Mbrd_FindItemsAndRetain( gMbrdSearchNode, gAllGroupTypes, ID_TYPE_GROUPMEMBERS, guidString, flags, &count );
if ( items != NULL )
{
DbgLog( kLogInfo, "%s - Membership - Resolve Groups - adding %d direct UUID memberships to membership for '%s'",
reqOrigin, count, (item->fName ? :"") );
for ( UInt32 ii = 0; ii < count; ii++ )
{
addToHashesAndRelease( item, items[ii], NULL );
items[ii] = NULL;
}
free( items );
items = NULL;
}
}
else if ( membershipRoot != NULL )
{
items = Mbrd_FindItemsAndRetain( gMbrdSearchNode, gAllGroupTypes, ID_TYPE_NESTEDGROUPS, guidString, flags, &count );
if ( items != NULL )
{
DbgLog( kLogInfo, "%s - Membership - Resolve Groups - adding %d nested groups to membership for '%s'",
reqOrigin, count, membershipRoot->fName ?: "" );
for ( UInt32 ii = 0; ii < count; ii++ )
{
addToHashesAndRelease( membershipRoot, items[ii], item );
items[ii] = NULL;
}
free( items );
items = NULL;
}
}
dispatch_group_wait( dispatchGroup, UINT64_MAX );
dispatch_release( dispatchGroup );
dispatch_release(dispatchQueue);
DbgLog( kLogInfo, "%s - Membership - Finished resolving groups for %s '%s' - total %d", reqOrigin,
((item->fRecordType & (kUGRecordTypeGroup | kUGRecordTypeComputerGroup)) ? "group" : "user"),
item->fName ?: "", item->fGUIDMembershipHash.fNumEntries );
if ( (item->fRecordType & (kUGRecordTypeUser | kUGRecordTypeComputer)) != 0 ) {
__sync_or_and_fetch( &item->fFlags, kUGFlagValidMembership );
}
}
static void Mbrd_GenerateItemMembership( UserGroup *item, uint32_t flags, bool inAsyncRefresh = false )
{
static dispatch_semaphore_t semaphore;
static dispatch_queue_t trampoline;
static dispatch_once_t once;
if ( item == NULL ) return;
dispatch_once(&once, ^(void) {
semaphore = dispatch_semaphore_create(4); trampoline = dispatch_queue_create("com.apple.DirectoryService.refreshTrampoline", NULL);
});
__block bool bIssueRefresh = false;
__block bool bAsyncRefresh = ((flags & kKernelRequest) != 0 ? true : inAsyncRefresh); const char *reqOrigin = ((flags & kKernelRequest) != 0 ? "mbr_syscall" : "mbr_mig");
dispatch_group_t group = dispatch_group_create();
dispatch_sync( item->fQueue,
^(void) {
uint32_t currentTime = GetElapsedSeconds();
if ( (item->fFlags & kUGFlagValidMembership) == 0 ||
((flags & KAUTH_EXTLOOKUP_REFRESH_MEMBERSHIP) != 0 && item->fMaximumRefresh <= currentTime) )
{
if ( __sync_bool_compare_and_swap(&item->fRefreshActive, false, true) == true ) {
if ( (item->fFlags & kUGFlagValidMembership) == 0 ) {
DbgLog( kLogInfo, "%s - Membership - '%s' (%s) - generating group memberships",
reqOrigin, item->fName ? : "", item->fNode ? : "" );
}
else {
DbgLog( kLogInfo, "%s - Membership - '%s' (%s) force refresh requested - entry and group memberships %s",
reqOrigin, item->fName ? : "", item->fNode ? : "", bAsyncRefresh ? "(async requested but ignored)" : "" );
}
bIssueRefresh = true;
}
bAsyncRefresh = false;
}
else if ( item->fExpiration <= currentTime )
{
if ( __sync_bool_compare_and_swap(&item->fRefreshActive, false, true) == true ) {
DbgLog(kLogInfo, "%s - Membership - '%s' (%s) outdated - will refresh entry and group memberships%s",
reqOrigin, item->fName ? : "", item->fNode ? : "", (bAsyncRefresh ? " asynchronously" : ""));
bIssueRefresh = true;
}
}
} );
if ( bIssueRefresh == true ) {
dispatch_block_t refresh_block;
refresh_block = ^(void) {
CInternalDispatch::AddCapability();
UserGroup *refreshed = NULL;
int isUser;
uid_t uid;
if ( (item->fFlags & kUGFoundByGUID) != 0 && IsCompatibilityGUID(item->fGUID, &isUser, &uid) == false ) {
uuid_string_t uuidStr;
uuid_unparse_upper( item->fGUID, uuidStr );
refreshed = Mbrd_FindItemAndRetain( gMbrdSearchNode, gUserType, ID_TYPE_GUID, uuidStr, flags );
}
else if ( (item->fFlags & kUGFoundByID) != 0 ) {
char uidStr[16];
snprintf( uidStr, sizeof(uidStr), "%d", item->fID );
refreshed = Mbrd_FindItemAndRetain( gMbrdSearchNode, gUserType, ID_TYPE_UID, uidStr, flags );
}
else if ( (item->fFlags & kUGFoundByName) != 0 ) {
refreshed = Mbrd_FindItemAndRetain( gMbrdSearchNode, gUserType, ID_TYPE_USERNAME, item->fName, flags );
}
if ( refreshed != NULL )
{
UserGroup *workingCopy = UserGroup_Create();
UserGroup_Merge( workingCopy, refreshed, false );
Mbrd_ResolveGroupsForItem( workingCopy, flags );
UserGroup_Merge( refreshed, workingCopy, true );
if ( refreshed != item ) {
UserGroup_Merge( item, workingCopy, true );
item->fRefreshActive = false;
DbgLog( kLogNotice, "%s - Membership - '%s' (%s) - inflight membership generation encountered a new entry - syncing groups to original request",
reqOrigin, item->fName ? : "", item->fNode ? : "" );
}
refreshed->fRefreshActive = false;
UserGroup_Release( workingCopy );
UserGroup_Release( refreshed );
}
UserGroup_Release( item );
dispatch_semaphore_signal(semaphore);
};
UserGroup_Retain( item );
dispatch_group_async(group, trampoline, ^(void) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, item->fRefreshQueue, refresh_block);
});
}
if (bIssueRefresh == true && bAsyncRefresh == false) {
DbgLog( kLogInfo, "%s - Membership - '%s' (%s) - waiting for an inflight membership generation to complete",
reqOrigin, item->fName ? : "", item->fNode ? : "" );
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_sync(item->fRefreshQueue, ^(void) {
DbgLog(kLogDebug, "%s - Membership - '%s' (%s) - finished waiting for an inflight membership generation to complete",
reqOrigin, item->fName ? : "", item->fNode ? : "");
});
}
dispatch_release(group);
}
#endif
int IsUserMemberOfGroupByGUID( UserGroup* user, uuid_t groupGUID, uint32_t flags )
{
bool bAsyncRefresh = false;
if ( (flags & KAUTH_EXTLOOKUP_REFRESH_MEMBERSHIP) != 0 && uuid_is_null(groupGUID) != 0 ) {
if ( (user->fFlags & kUGFlagValidMembership) == 0 ) {
return 0;
}
bAsyncRefresh = true;
}
if ( uuid_compare(gEveryoneUUID, groupGUID) == 0 )
return 1;
if ( user == NULL )
return 0;
if ( uuid_compare(gNetAccountsUUID, groupGUID) == 0 )
return (((user->fRecordType & kUGRecordTypeUser) != 0 || (user->fRecordType & kUGRecordTypeComputer) != 0) &&
(user->fFlags & kUGFlagLocalAccount) == 0);
if ( uuid_compare(gLocalAccountsUUID, groupGUID) == 0 )
return (((user->fRecordType & kUGRecordTypeUser) != 0 || (user->fRecordType & kUGRecordTypeComputer) != 0) &&
(user->fFlags & kUGFlagLocalAccount) != 0);
Mbrd_GenerateItemMembership(user, flags);
UserGroup *result = HashTable_GetAndRetain( &user->fGUIDMembershipHash, groupGUID );
UserGroup_Release( result );
return (result != NULL);
}
int IsUserMemberOfGroupBySID( UserGroup* user, ntsid_t* groupSID, uint32_t flags )
{
ntsid_t tempSID;
UserGroup* result;
if ( user == NULL || groupSID->sid_authcount > KAUTH_NTSID_MAX_AUTHORITIES )
return 0;
memset(&tempSID, 0, sizeof(ntsid_t));
memcpy(&tempSID, groupSID, KAUTH_NTSID_SIZE(groupSID));
if (memcmp(&user->fSID, &tempSID, sizeof(ntsid_t)) == 0)
return 1;
if (memcmp(&gEveryoneSID, &tempSID, sizeof(ntsid_t)) == 0)
return 1;
Mbrd_GenerateItemMembership( user, flags );
result = HashTable_GetAndRetain( &user->fSIDMembershipHash, &tempSID );
UserGroup_Release( result );
return (result != NULL);
}
static int IsUserMemberOfGroupByGID( UserGroup *user, gid_t gid, uint32_t flags )
{
if ( user == NULL ) return false;
Mbrd_GenerateItemMembership( user, flags );
UserGroup *result = HashTable_GetAndRetain( &user->fGIDMembershipHash, (void *)&gid );
UserGroup_Release( result );
return (result != NULL);
}
#pragma mark -
#pragma mark Public routines
void Mbrd_SwapRequest(struct kauth_identity_extlookup* request)
{
int i;
request->el_seqno = OSSwapInt32(request->el_seqno);
request->el_result = OSSwapInt32(request->el_result);
request->el_flags = OSSwapInt32(request->el_flags);
request->el_uid = OSSwapInt32(request->el_uid);
request->el_uguid_valid = OSSwapInt32(request->el_uguid_valid);
request->el_usid_valid = OSSwapInt32(request->el_usid_valid);
request->el_gid = OSSwapInt32(request->el_gid);
request->el_gguid_valid = OSSwapInt32(request->el_gguid_valid);
request->el_member_valid = OSSwapInt32(request->el_member_valid);
for (i = 0; i < KAUTH_NTSID_MAX_AUTHORITIES; i++)
{
request->el_usid.sid_authorities[i] = OSSwapInt32(request->el_usid.sid_authorities[i]);
request->el_gsid.sid_authorities[i] = OSSwapInt32(request->el_gsid.sid_authorities[i]);
}
}
void Mbrd_InitializeGlobals( void )
{
const char* path = "/etc/memberd.conf";
char buffer[1024];
int fd;
off_t len;
int rewriteConfig = 0;
struct stat sb;
int defaultExpiration = kDefaultExpirationClient;
int defaultNegExpiration = kDefaultNegativeExpirationClient;
int kernelExpiration = kDefaultKernelExpiration;
int maximumRefresh = kDefaultMaximumRefresh;
int kerberosFallback = 0;
if ( gServerOS == true )
{
defaultExpiration = kDefaultExpirationServer;
defaultNegExpiration = kDefaultNegativeExpirationServer;
}
if ( !gDSInstallDaemonMode && !gDSLocalOnlyMode )
{
int result = stat(path, &sb);
if ((result != 0) || (sb.st_size > 1023))
rewriteConfig = 1;
else
{
fd = open(path, O_RDONLY, 0);
if (fd < 0) goto initialize;
len = read(fd, buffer, sb.st_size);
close(fd);
if (strncmp(buffer, "#1.3", 4) != 0)
rewriteConfig = 1;
}
if (rewriteConfig)
{
fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0755);
if (fd < 0) goto initialize;
snprintf(buffer, sizeof(buffer), "#1.3\n%s %d\n%s %d\n%s %d\n%s %d\n%s %d\n",
kDefaultExpirationStr, defaultExpiration,
kDefaultNegativeExpirationStr, defaultNegExpiration,
kDefaultKernelExpirationInSecsStr, kernelExpiration,
kDefaultMaximumRefreshInSecsStr, maximumRefresh,
kKerberosFallbackToRecordName, 1);
len = write(fd, buffer, strlen(buffer));
close(fd);
}
else
{
char* temp;
off_t i;
if (len != sb.st_size) goto initialize;
buffer[len] = '\0';
for (i = 0; i < len; i++)
if (buffer[i] == '\n') buffer[i] = '\0';
i = 0;
while (i < len)
{
temp = buffer + i;
if (strncmp(temp, kDefaultExpirationStr, sizeof(kDefaultExpirationStr) - 1) == 0)
{
temp += sizeof(kDefaultExpirationStr) - 1;
defaultExpiration = strtol(temp, &temp, 10);
if (defaultExpiration < 30)
defaultExpiration = 30;
else if (defaultExpiration > 24 * 60 * 60)
defaultExpiration = 24 * 60 * 60;
}
else if (strncmp(temp, kDefaultNegativeExpirationStr, sizeof(kDefaultNegativeExpirationStr) - 1) == 0)
{
temp += sizeof(kDefaultNegativeExpirationStr) - 1;
defaultNegExpiration = strtol(temp, &temp, 10);
if (defaultNegExpiration < 30)
defaultNegExpiration = 30;
else if (defaultNegExpiration > 24 * 60 * 60)
defaultNegExpiration = 24 * 60 * 60;
}
else if (strncmp(temp, kDefaultKernelExpirationInSecsStr, sizeof(kDefaultKernelExpirationInSecsStr) - 1) == 0)
{
temp += sizeof(kDefaultKernelExpirationInSecsStr) - 1;
kernelExpiration = strtol(temp, &temp, 10);
if (kernelExpiration < 30)
kernelExpiration = 30;
else if (kernelExpiration > 1 * 60 * 60)
kernelExpiration = 1 * 60 * 60;
}
else if (strncmp(temp, kDefaultMaximumRefreshInSecsStr, sizeof(kDefaultMaximumRefreshInSecsStr) - 1) == 0)
{
temp += sizeof(kDefaultMaximumRefreshInSecsStr) - 1;
maximumRefresh = strtol(temp, &temp, 10);
if (maximumRefresh < 2 * 60)
maximumRefresh = 2 * 60;
else if (maximumRefresh > 1 * 60 * 60)
maximumRefresh = 1 * 60 * 60;
}
else if (strncmp(temp, kKerberosFallbackToRecordName, sizeof(kKerberosFallbackToRecordName) - 1) == 0 )
{
temp += sizeof(kKerberosFallbackToRecordName) - 1;
kerberosFallback = strtol(temp, &temp, 10);
}
else if (strncmp(temp, kKernelResolverTimeout, sizeof(kKernelResolverTimeout) - 1) == 0 )
{
temp += sizeof(kKernelResolverTimeout) - 1;
gKernelTimeout = strtol(temp, &temp, 10);
if (gKernelTimeout == 0) {
gKernelTimeout = 60; }
}
i += strlen(temp) + 1;
}
}
}
initialize:
gMbrdCache = MbrdCache_Create( defaultExpiration, defaultNegExpiration, kernelExpiration, maximumRefresh, kerberosFallback );
assert( gMbrdCache != NULL );
gLookupQueue = dispatch_queue_create( "Membership lookup queue", NULL );
pthread_key_create( &gMembershipThreadKey, NULL );
uuid_parse( "ABCDEFAB-CDEF-ABCD-EFAB-CDEF0000000C", gEveryoneUUID );
Mbrd_ConvertSIDFromString("S-1-1-0", &gEveryoneSID);
uuid_parse( "ABCDEFAB-CDEF-ABCD-EFAB-CDEF0000003D", gLocalAccountsUUID );
uuid_parse( "ABCDEFAB-CDEF-ABCD-EFAB-CDEF0000003E", gNetAccountsUUID );
uuid_parse( "FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000", gRootUserUUID );
bzero( &gStatBlock, sizeof(gStatBlock) );
gStatBlock.fTotalUpTime = GetElapsedSeconds();
}
void Mbrd_Initialize( void )
{
tDataBufferPtr nodeBuffer = NULL;
tContextData localContext = 0;
tDataListPtr nodeName = NULL;
UInt32 returnCount = 0;
tDirStatus status = dsOpenDirService( &gMbrdDirRef );
assert( status == eDSNoErr );
nodeBuffer = dsDataBufferAllocate( gMbrdDirRef, 4096 );
assert( nodeBuffer != NULL );
status = dsFindDirNodes( gMbrdDirRef, nodeBuffer, NULL, eDSAuthenticationSearchNodeName, &returnCount, &localContext );
assert( status == eDSNoErr && returnCount != 0 );
status = dsGetDirNodeName( gMbrdDirRef, nodeBuffer, 1, &nodeName ); assert( status == eDSNoErr );
status = dsOpenDirNode( gMbrdDirRef, nodeName, &gMbrdSearchNode );
assert( status == eDSNoErr );
if ( localContext != 0 )
dsReleaseContinueData( gMbrdDirRef, localContext );
dsDataBufferDeAllocate( gMbrdDirRef, nodeBuffer );
dsDataListDeallocate( gMbrdDirRef, nodeName );
free( nodeName );
gUserType = dsBuildListFromStringsPriv( kDSStdRecordTypeUsers, kDSStdRecordTypeComputers, NULL );
gAllGroupTypes = dsBuildListFromStringsPriv( kDSStdRecordTypeGroups, kDSStdRecordTypeComputerGroups, NULL );
gUnknownType = dsBuildListFromStringsPriv( kDSStdRecordTypeUsers, kDSStdRecordTypeComputers, kDSStdRecordTypeGroups,
kDSStdRecordTypeComputerGroups, NULL );
gAttrsToGet = dsBuildListFromStringsPriv( kDSNAttrMetaNodeLocation, kDSNAttrRecordName, kDS1AttrPassword, kDS1AttrUniqueID,
kDS1AttrGeneratedUID, kDS1AttrPrimaryGroupID, kDS1AttrNFSHomeDirectory, kDS1AttrUserShell,
kDS1AttrDistinguishedName, kDS1AttrTimeToLive, kDS1AttrSMBSID,
#if defined(PRE_CACHE_GROUPS)
kDSNAttrGroupMembership,
#endif
kDS1AttrENetAddress, kDS1AttrCopyTimestamp, kDSNAttrAltSecurityIdentities, kDS1AttrSMBRID,
kDS1AttrSMBGroupRID, kDS1AttrSMBPrimaryGroupSID, kDS1AttrOriginalNodeName, kDSNAttrKeywords, NULL );
}
void Mbrd_ProcessLookup(struct kauth_identity_extlookup* request)
{
uint32_t flags = request->el_flags;
UserGroup* user = NULL;
UserGroup* group = NULL;
int isMember = -1;
uint64_t microsec = GetElapsedMicroSeconds();
const char *reqOrigin = ((flags & kKernelRequest) != 0 ? "mbr_syscall" : "mbr_mig");
if ( 0 == (flags & ~(KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_VALID_GGUID | KAUTH_EXTLOOKUP_WANT_UID | KAUTH_EXTLOOKUP_WANT_GID)) &&
(flags & (KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_WANT_UID)) == (KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_WANT_UID) &&
uuid_compare(gRootUserUUID, request->el_uguid.g_guid) == 0 )
{
request->el_flags |= KAUTH_EXTLOOKUP_VALID_UID;
request->el_uid = 0;
request->el_result = KAUTH_EXTLOOKUP_SUCCESS;
DbgLog( kLogPlugin, "%s - Dispatch - Lookup - UUID FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000 default answer 0 (root)", reqOrigin );
return;
}
else if ( flags == (flags & (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_WANT_UGUID)) && request->el_uid == 0 )
{
request->el_flags |= KAUTH_EXTLOOKUP_VALID_UGUID;
uuid_copy( request->el_uguid.g_guid, gRootUserUUID );
request->el_uguid_valid = MbrdCache_GetDefaultExpiration( gMbrdCache );
request->el_result = KAUTH_EXTLOOKUP_SUCCESS;
DbgLog( kLogPlugin, "%s - Dispatch - Lookup - UID 0 default answer FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000 (root) - TTL %u",
reqOrigin, request->el_uguid_valid );
return;
}
if ( (flags & KAUTH_EXTLOOKUP_WANT_MEMBERSHIP) != 0 &&
((flags & KAUTH_EXTLOOKUP_VALID_UGUID) != 0 || (flags & KAUTH_EXTLOOKUP_VALID_UID) != 0 || (flags & KAUTH_EXTLOOKUP_VALID_USID) != 0) &&
(((flags & KAUTH_EXTLOOKUP_VALID_GID) != 0 && request->el_gid == 12) ||
((flags & KAUTH_EXTLOOKUP_VALID_GGUID) != 0 && uuid_compare(gEveryoneUUID, request->el_gguid.g_guid) == 0) ||
((flags & KAUTH_EXTLOOKUP_VALID_GSID) != 0 && memcmp(&gEveryoneSID, &request->el_gsid, sizeof(gEveryoneSID)) == 0)) )
{
request->el_member_valid = MbrdCache_GetDefaultExpiration( gMbrdCache ); request->el_flags |= KAUTH_EXTLOOKUP_VALID_MEMBERSHIP | KAUTH_EXTLOOKUP_ISMEMBER;
DbgLog( kLogPlugin, "%s - Dispatch - Membership - is user member of group everyone is always true - TTL %u",
reqOrigin, request->el_member_valid );
return;
}
if ( (flags & KAUTH_EXTLOOKUP_VALID_UGUID) != 0 )
{
user = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_GUID, request->el_uguid.g_guid, flags );
if ( LoggingEnabled(kLogPlugin) )
{
uuid_string_t guidString;
uuid_unparse( request->el_uguid.g_guid, guidString );
DbgLog( kLogPlugin, "%s - Dispatch - Lookup - user/computer GUID %s - %s %s", reqOrigin, guidString,
(user != NULL && (user->fFlags & kUGFlagNotFound) == 0 ? "succeeded" : "was not found"),
(user != NULL && user->fName != NULL ? user->fName : "") );
}
}
else if ( (flags & KAUTH_EXTLOOKUP_VALID_USID) != 0 )
{
user = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_SID, &request->el_usid, flags );
if ( LoggingEnabled(kLogPlugin) )
{
char sidString[256] = { 0, };
ConvertSIDToString( sidString, &request->el_usid );
DbgLog( kLogPlugin, "%s - Dispatch - Lookup - user/computer SID %s - %s %s", reqOrigin, sidString,
(user != NULL && (user->fFlags & kUGFlagNotFound) == 0 ? "succeeded" : "was not found"),
(user != NULL ? user->fName : "") );
}
}
else if ( (flags & KAUTH_EXTLOOKUP_VALID_UID) != 0 )
{
user = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_UID, &request->el_uid, flags );
DbgLog( kLogPlugin, "%s - Dispatch - Lookup - user/computer ID %d - %s %s", reqOrigin, request->el_uid,
(user != NULL && (user->fFlags & kUGFlagNotFound) == 0 ? "succeeded" : "was not found"),
(user != NULL ? user->fName : "") );
}
if ( user != NULL && (user->fRecordType & (kUGRecordTypeGroup | kUGRecordTypeComputerGroup)) != 0 )
{
if ( (user->fFlags & kUGFlagNotFound) == 0 ) {
DbgLog( kLogPlugin, "%s - Dispatch - Lookup - result rejected was not a user/computer", reqOrigin );
}
if ( (flags & kKernelRequest) == 0 || (user->fFlags & kUGFlagNotFound) == 0 ) {
UserGroup_Release( user );
user = NULL;
}
}
if ( (flags & KAUTH_EXTLOOKUP_WANT_MEMBERSHIP) != 0 && user != NULL && (user->fFlags & kUGFlagNotFound) == 0 )
{
request->el_member_valid = MbrdCache_TTL( gMbrdCache, user, flags );
if ( (flags & KAUTH_EXTLOOKUP_VALID_GGUID) != 0 )
{
isMember = IsUserMemberOfGroupByGUID( user, request->el_gguid.g_guid, flags );
if ( user != NULL && user->fName != NULL && LoggingEnabled(kLogPlugin) && uuid_is_null(request->el_gguid.g_guid) == 0 ) {
uuid_string_t guidString;
uuid_unparse( request->el_gguid.g_guid, guidString );
DbgLog( kLogPlugin, "%s - Dispatch - Membership - is %s %s member of group GUID %s = %s - TTL %u", reqOrigin,
((user->fRecordType & kUGRecordTypeUser) != 0 ? "user" : "computer"), user->fName, guidString, (isMember == 1 ? "true" : "false"),
request->el_member_valid );
}
}
else if ( (flags & KAUTH_EXTLOOKUP_VALID_GSID) != 0 )
{
isMember = IsUserMemberOfGroupBySID( user, &request->el_gsid, flags );
if ( user != NULL && user->fName != NULL && LoggingEnabled(kLogPlugin) )
{
char sidString[256] = { 0, };
ConvertSIDToString( sidString, &request->el_gsid );
DbgLog( kLogPlugin, "%s - Dispatch - Membership - is %s %s member of group SID %s = %s - TTL %u", reqOrigin,
((user->fRecordType & kUGRecordTypeUser) != 0 ? "user" : "computer"), user->fName, sidString, (isMember == 1 ? "true" : "false"),
request->el_member_valid );
}
}
else if ( (flags & KAUTH_EXTLOOKUP_VALID_GID) != 0 )
{
isMember = IsUserMemberOfGroupByGID( user, request->el_gid, flags );
if ( user != NULL && user->fName != NULL && LoggingEnabled(kLogPlugin) )
{
DbgLog( kLogPlugin, "%s - Dispatch - Membership - is %s %s member of group GID %d = %s - TTL %u", reqOrigin,
((user->fRecordType & kUGRecordTypeUser) != 0 ? "user" : "computer"), user->fName, request->el_gid, (isMember == 1 ? "true" : "false"),
request->el_member_valid );
}
}
request->el_flags |= KAUTH_EXTLOOKUP_VALID_MEMBERSHIP;
if (isMember == 1) {
request->el_flags |= KAUTH_EXTLOOKUP_ISMEMBER;
}
}
if ( (flags & KAUTH_EXTLOOKUP_WANT_UID) != 0 && user != NULL )
{
request->el_flags |= KAUTH_EXTLOOKUP_VALID_UID;
request->el_uid = user->fID;
DbgLog( kLogPlugin, "%s - Dispatch - WantUID - found %d - %s", reqOrigin, user->fID, (user->fName ?: "") );
}
if ( (flags & KAUTH_EXTLOOKUP_WANT_UGUID) != 0 )
{
request->el_uguid_valid = MbrdCache_TTL( gMbrdCache, user, flags );
if ( user != NULL ) {
request->el_flags |= KAUTH_EXTLOOKUP_VALID_UGUID;
uuid_copy( request->el_uguid.g_guid, user->fGUID );
if ( LoggingEnabled(kLogPlugin) )
{
uuid_string_t guidString;
uuid_unparse( user->fGUID, guidString );
DbgLog( kLogPlugin, "%s - Dispatch - WantGUID - found %s - %s - TTL %u",
reqOrigin, guidString, (user->fName ?: ""), request->el_uguid_valid );
}
}
}
if ( (flags & KAUTH_EXTLOOKUP_WANT_USID) != 0 )
{
request->el_usid_valid = MbrdCache_TTL( gMbrdCache, user, flags );
if ( user != NULL && (user->fFlags & kUGFlagHasSID) != 0 ) {
request->el_flags |= KAUTH_EXTLOOKUP_VALID_USID;
memcpy(&request->el_usid, &user->fSID, sizeof(ntsid_t));
if ( LoggingEnabled(kLogPlugin) )
{
char sidString[256] = { 0, };
ConvertSIDToString( sidString, &user->fSID );
DbgLog( kLogPlugin, "%s - Dispatch - WantSID - found %s - %s - TTL %u",
reqOrigin, sidString, (user->fName ?: ""), request->el_usid_valid );
}
}
}
UserGroup_Release( user );
user = NULL;
if ( (flags & (KAUTH_EXTLOOKUP_WANT_GID | KAUTH_EXTLOOKUP_WANT_GGUID | KAUTH_EXTLOOKUP_WANT_GSID)) != 0 )
{
if (flags & KAUTH_EXTLOOKUP_VALID_GGUID)
{
group = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_GUID, request->el_gguid.g_guid, flags );
if ( LoggingEnabled(kLogPlugin) )
{
uuid_string_t guidString;
uuid_unparse( request->el_gguid.g_guid, guidString );
DbgLog( kLogPlugin, "%s - Dispatch - Lookup - group/computergroup GUID %s - %s %s", reqOrigin, guidString,
(group != NULL && (group->fFlags & kUGFlagNotFound) == 0 ? "succeeded" : "was not found"),
(group != NULL && group->fName != NULL ? group->fName : "") );
}
}
else if (flags & KAUTH_EXTLOOKUP_VALID_GSID)
{
group = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_SID, &request->el_gsid, flags );
if ( LoggingEnabled(kLogPlugin) )
{
char sidString[256] = { 0, };
ConvertSIDToString( sidString, &request->el_gsid );
DbgLog( kLogPlugin, "%s - Dispatch - Lookup - group/computergroup SID %s - %s %s", reqOrigin, sidString,
(group != NULL && (group->fFlags & kUGFlagNotFound) == 0 ? "succeeded" : "was not found"),
(group != NULL && group->fName != NULL ? group->fName : "") );
}
}
else if (flags & KAUTH_EXTLOOKUP_VALID_GID)
{
group = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_GID, &request->el_gid, flags );
DbgLog( kLogPlugin, "%s - Dispatch - Lookup - group/computergroup GID %d - %s %s", reqOrigin, request->el_gid,
(group != NULL && (group->fFlags & kUGFlagNotFound) == 0 ? "succeeded" : "was not found"),
(group != NULL && group->fName != NULL ? group->fName : "") );
}
}
if ( group != NULL && (group->fRecordType & (kUGRecordTypeUser | kUGRecordTypeComputer)) != 0 )
{
if ( (group->fFlags & kUGFlagNotFound) == 0 ) {
DbgLog( kLogPlugin, "%s - Dispatch - Lookup - result rejected was not a group", reqOrigin );
}
UserGroup_Release( group );
group = NULL;
}
if ( (flags & KAUTH_EXTLOOKUP_WANT_GID) != 0 && group != NULL)
{
request->el_flags |= KAUTH_EXTLOOKUP_VALID_GID;
request->el_gid = group->fID;
if ( group->fName != NULL ) {
DbgLog( kLogPlugin, "%s - Dispatch - WantGID - found %d - %s", reqOrigin, group->fID, group->fName );
}
else {
DbgLog( kLogPlugin, "%s - Dispatch - WantGID - found %d", reqOrigin, group->fID );
}
}
if ( (flags & KAUTH_EXTLOOKUP_WANT_GGUID) != 0 )
{
request->el_gguid_valid = MbrdCache_TTL( gMbrdCache, group, flags );
if ( group != NULL ) {
uuid_copy( request->el_gguid.g_guid, group->fGUID );
request->el_flags |= KAUTH_EXTLOOKUP_VALID_GGUID;
if ( group->fName != NULL && LoggingEnabled(kLogPlugin) )
{
uuid_string_t guidString;
uuid_unparse( group->fGUID, guidString );
DbgLog( kLogPlugin, "%s - Dispatch - WantGGUID - found %s - %s - TTL %u",
reqOrigin, guidString, group->fName, request->el_gguid_valid );
}
}
}
if ( (flags & KAUTH_EXTLOOKUP_WANT_GSID) != 0 )
{
request->el_gsid_valid = MbrdCache_TTL( gMbrdCache, group, flags );
if ( group != NULL && (group->fFlags & kUGFlagHasSID) != 0 ) {
request->el_flags |= KAUTH_EXTLOOKUP_VALID_GSID;
memcpy(&request->el_gsid, &group->fSID, sizeof(ntsid_t));
if ( group->fName != NULL && LoggingEnabled(kLogPlugin) )
{
char sidString[256] = { 0, };
ConvertSIDToString( sidString, &group->fSID );
DbgLog( kLogPlugin, "%s - Dispatch - WantGSID - found %s - %s - TTL %u",
reqOrigin, sidString, group->fName, request->el_gsid_valid );
}
}
}
UserGroup_Release( group );
group = NULL;
microsec = GetElapsedMicroSeconds() - microsec;
Mbrd_AddToAverage(&gStatBlock.fAverageuSecPerCall, &gStatBlock.fTotalCallsHandled, microsec);
request->el_result = KAUTH_EXTLOOKUP_SUCCESS;
}
void Mbrd_SweepCache( void )
{
dispatch_async( gLookupQueue,
^(void) {
MbrdCache_Sweep( gMbrdCache );
} );
}
int Mbrd_SetNodeAvailability( const char *nodeName, bool nodeAvailable )
{
return MbrdCache_SetNodeAvailability( gMbrdCache, nodeName, nodeAvailable );
}
int Mbrd_ProcessGetGroups(uint32_t uid, uint32_t* numGroups, GIDArray gids)
{
uint64_t microsec = GetElapsedMicroSeconds();
int result = KERN_SUCCESS;
UserGroup* user = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_UID, &uid, 0 );
*numGroups = 0;
if (user != NULL)
{
Mbrd_GenerateItemMembership( user, 0 );
*numGroups = UserGroup_Get16Groups( user, gids );
UserGroup_Release( user );
user = NULL;
}
else
{
result = KERN_FAILURE;
syslog(LOG_ERR, "GetGroups couldn't find uid %d", uid);
}
microsec = GetElapsedMicroSeconds() - microsec;
Mbrd_AddToAverage(&gStatBlock.fAverageuSecPerCall, &gStatBlock.fTotalCallsHandled, microsec);
return result;
}
int Mbrd_ProcessGetAllGroups(uint32_t uid, uint32_t *numGroups, GIDList *gids )
{
uint64_t microsec = GetElapsedMicroSeconds();
int result = KERN_SUCCESS;
UserGroup* user = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, ID_TYPE_UID, &uid, 0 );
*numGroups = 0;
if (user != NULL)
{
Mbrd_GenerateItemMembership( user, 0 );
*numGroups = UserGroup_GetGroups( user, gids );
UserGroup_Release( user );
user = NULL;
}
else
{
result = KERN_FAILURE;
syslog(LOG_ERR, "GetGroups couldn't find uid %d", uid);
}
microsec = GetElapsedMicroSeconds() - microsec;
Mbrd_AddToAverage(&gStatBlock.fAverageuSecPerCall, &gStatBlock.fTotalCallsHandled, microsec);
return result;
}
static int
parse_external_name( const void *data, size_t len, gss_buffer_desc *oid, gss_buffer_desc *name )
{
oid->length = 0;
oid->value = NULL;
name->length = 0;
name->value = NULL;
if (len < 4)
return 1;
const unsigned char *p = (const unsigned char *) data;
if (memcmp(p, "\x04\x01", 2) != 0)
return 1;
len -= 2;
p += 2;
size_t l = (p[0] << 8) | p[1];
len -= 2;
p += 2;
if (len < l)
return 1;
oid->length = l;
oid->value = (void *) p;
len -= l;
p += l;
if (len < 4)
return 1;
l = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
len -= 4;
p += 4;
if (len != l)
return 1;
name->value = (void *) p;
name->length = l;
return 0;
}
int Mbrd_ProcessMapIdentifier(int idType, const void *identifier, ssize_t identifierSize, guid_t *guid)
{
uint64_t microsec = GetElapsedMicroSeconds();
int result = KERN_FAILURE;
UserGroup* item = NULL;
const void *searchID = NULL;
const char *tempSearchID = NULL;
int nextIDType = idType;
char *allocedString = NULL;
gss_buffer_desc gssName;
if ( idType == ID_TYPE_GSS_EXPORT_NAME ) {
static gss_buffer_desc kerberosOID = { 11, (void *) "\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
gss_buffer_desc oid;
if ( parse_external_name(identifier, identifierSize, &oid, &gssName) == 0 )
{
if ( oid.length == kerberosOID.length && memcmp(oid.value, kerberosOID.value, oid.length) == 0 ) {
identifier = gssName.value;
identifierSize = gssName.length;
nextIDType = ID_TYPE_KERBEROS;
}
}
}
again:
switch ( nextIDType )
{
case ID_TYPE_UID:
if ( sizeof(uid_t) == identifierSize ) {
if ( *((uid_t *) identifier) == 0 ) {
DbgLog( kLogPlugin, "mbr_mig - Dispatch - Map name using default UUID for UID 0 - FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000" );
uuid_copy( guid->g_guid, gRootUserUUID );
result = KERN_SUCCESS;
}
else {
searchID = identifier;
}
}
break;
case ID_TYPE_GID:
if ( sizeof(gid_t) == identifierSize ) {
searchID = identifier;
}
break;
case ID_TYPE_SID:
if ( sizeof(ntsid_t) == identifierSize ) {
searchID = identifier;
}
break;
case ID_TYPE_USERNAME:
tempSearchID = (const char *) identifier;
if ( identifierSize > 0 ) {
if ( tempSearchID[identifierSize] != '\0' ) {
size_t len = identifierSize + 1;
allocedString = (char *) malloc( len );
strlcpy( allocedString, (const char *) identifier, len ); tempSearchID = allocedString;
}
}
if ( DSIsStringEmpty(tempSearchID) == false ) {
if ( strcmp(tempSearchID, "root") == 0 ) {
DbgLog( kLogPlugin, "mbr_mig - Dispatch - Map name using default UUID for root - FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000" );
uuid_copy( guid->g_guid, gRootUserUUID );
}
searchID = tempSearchID;
}
break;
case ID_TYPE_GROUPNAME:
searchID = identifier;
break;
case ID_TYPE_X509_DN:
if ( identifierSize > 0 ) {
size_t len = sizeof("X509:") + identifierSize;
searchID = allocedString = (char *) malloc( len );
strlcpy( allocedString, "X509:", len );
strlcat( allocedString, (const char *) identifier, len );
searchID = allocedString;
}
break;
case ID_TYPE_KERBEROS:
if ( identifierSize > 0 ) {
size_t len = sizeof("Kerberos:") + identifierSize;
searchID = allocedString = (char *) malloc( len );
strlcpy( allocedString, "Kerberos:", len );
strlcat( allocedString, (const char *) identifier, len );
}
break;
}
if ( searchID != NULL ) {
item = Mbrd_GetItemWithIdentifierAndRetain( gMbrdCache, nextIDType, searchID, 0 );
if ( item != NULL ) {
if ( (item->fFlags & kUGFlagNotFound) == 0 ) {
uuid_copy( guid->g_guid, item->fGUID );
result = KERN_SUCCESS;
}
UserGroup_Release( item );
item = NULL;
}
DSFree( allocedString );
if ( result != KERN_SUCCESS ) {
if ( nextIDType == ID_TYPE_KERBEROS && MbrdCache_KerberosFallback(gMbrdCache) != 0 ) {
DbgLog( kLogInfo, "mbr_mig - Membership - Kerberos fallback enabled, looking via record name next" );
nextIDType = ID_TYPE_USERNAME;
goto again;
}
}
}
microsec = GetElapsedMicroSeconds() - microsec;
Mbrd_AddToAverage(&gStatBlock.fAverageuSecPerCall, &gStatBlock.fTotalCallsHandled, microsec);
return result;
}
void dsNodeStateChangeOccurred(void)
{
dispatch_async( gLookupQueue,
^(void) {
MbrdCache_NodeChangeOccurred( gMbrdCache );
pthread_mutex_lock( &sidMapLock );
sidMap.clear();
pthread_mutex_unlock( &sidMapLock );
DbgLog( kLogNotice, "Membership - dsNodeStateChangeOccurred - flagging all entries as expired" );
} );
}
void dsFlushMembershipCache(void)
{
if (gCacheFlushDisabled == true) {
DbgLog(kLogNotice, "mbr_mig - dsFlushMembershipCache - skipped disabled externally");
return;
}
dispatch_async( gLookupQueue,
^(void) {
MbrdCache_ResetCache( gMbrdCache );
pthread_mutex_lock( &sidMapLock );
sidMap.clear();
pthread_mutex_unlock( &sidMapLock );
DbgLog( kLogNotice, "mbr_mig - dsFlushMembershipCache - force cache flush (internally initiated)" );
} );
}
void Mbrd_ProcessResetCache(void)
{
dispatch_async( gLookupQueue,
^(void) {
MbrdCache_ResetCache( gMbrdCache );
pthread_mutex_lock( &sidMapLock );
sidMap.clear();
pthread_mutex_unlock( &sidMapLock );
DbgLog( kLogNotice, "mbr_mig - external flush cache requested" );
} );
}
void Mbrd_ProcessGetStats(StatBlock *stats)
{
gMbrdGlobalMutex.WaitLock();
memcpy( stats, &gStatBlock, sizeof(StatBlock) );
gMbrdGlobalMutex.SignalLock();
stats->fTotalUpTime = GetElapsedSeconds() - stats->fTotalUpTime;
DbgLog( kLogDebug, "mbr_mig - Membership - Get stats" );
}
void Mbrd_ProcessResetStats(void)
{
gMbrdGlobalMutex.WaitLock();
memset( &gStatBlock, 0, sizeof(StatBlock) );
gStatBlock.fTotalUpTime = GetElapsedSeconds();
gMbrdGlobalMutex.SignalLock();
DbgLog( kLogDebug, "mbr_mig - Membership - Reset stats" );
}
void Mbrd_ProcessDumpState(void)
{
MbrdCache_DumpState( gMbrdCache );
DbgLog( kLogDebug, "mbr_mig - Membership - Dump State" );
}
void Mbrd_SetMembershipThread( bool bActive )
{
long current = (long) pthread_getspecific( gMembershipThreadKey );
pthread_setspecific( gMembershipThreadKey, (void *) (bActive == true ? ++current : --current) );
}
bool Mbrd_IsMembershipThread( void )
{
return (pthread_getspecific(gMembershipThreadKey) != NULL);
}
bool dsIsUserMemberOfGroup( const char *inUsername, const char *inGroupName )
{
bool returnVal = false;
guid_t user_uuid;
guid_t group_uuid;
kauth_identity_extlookup request = { 0 };
if ( Mbrd_ProcessMapIdentifier(ID_TYPE_USERNAME, inUsername, -1, &user_uuid) != KERN_SUCCESS ) {
DbgLog( kLogNotice, "dsIsUserMemberOfGroup - Unable to map user %s to UUID", inUsername );
return false;
}
if ( Mbrd_ProcessMapIdentifier(ID_TYPE_GROUPNAME, inGroupName, -1, &group_uuid) != KERN_SUCCESS ) {
DbgLog( kLogNotice, "dsIsUserMemberOfGroup - Unable to map group %s to UUID", inGroupName );
return false;
}
request.el_flags = KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_VALID_GGUID | KAUTH_EXTLOOKUP_WANT_MEMBERSHIP;
memcpy( &request.el_uguid, &user_uuid, sizeof(guid_t) );
memcpy( &request.el_gguid, &group_uuid, sizeof(guid_t) );
Mbrd_ProcessLookup( &request );
if ( (request.el_flags & KAUTH_EXTLOOKUP_VALID_MEMBERSHIP) != 0 && (request.el_flags & KAUTH_EXTLOOKUP_ISMEMBER) != 0 ) {
returnVal = true;
}
return returnVal;
}