CLDAPNodeConfig.cpp [plain text]
#include "CLDAPNodeConfig.h"
#include "BaseDirectoryPlugin.h"
#include "CLDAPPluginPrefs.h"
#include "CDSPluginUtils.h"
#include "CCachePlugin.h"
#include "CLDAPv3Configs.h"
#include "CLDAPConnection.h"
#include "DNSLookups.h"
#include <DirectoryServiceCore/CLog.h>
#include <Kerberos/krb5.h>
#include <Kerberos/gssapi.h>
#include <Kerberos/gssapi_krb5.h>
#include <PasswordServer/AuthFile.h>
#include <netinet/in.h> // struct sockaddr_in
#include <ifaddrs.h>
#include <fcntl.h>
#include <ldap_private.h>
#include <sasl/sasl.h>
#include <syslog.h>
extern CCachePlugin *gCacheNode;
extern DSMutexSemaphore *gKerberosMutex;
#define OCSEPCHARS " '()$"
#define IsOnlyBitSet(a,b) (((a) & (b)) && ((a) ^ (b)) == 0)
struct saslDefaults
{
const char *authcid;
const char *password;
char *authzid;
};
#pragma mark -
#pragma mark Class Definition
CLDAPNodeConfig::CLDAPNodeConfig( CLDAPv3Configs *inConfig, const char *inNodeName, CFStringRef inUUID )
: fMutex("CLDAPNodeConfig::fMutex"), fMappingsLock("CLDAPNodeConfig:fMappingsLock")
{
InitializeVariables();
fNodeName = strdup( inNodeName );
fConfigUUID = (CFStringRef) CFRetain( inUUID );
fConfigObject = inConfig;
char *cStr = NULL;
const char *uuidStr = BaseDirectoryPlugin::GetCStringFromCFString( fConfigUUID, &cStr );
DbgLog( kLogPlugin, "CLDAPNodeConfig::CLDAPNodeConfig - New configured node %s - %s created", fNodeName, uuidStr );
DSFree( cStr );
}
CLDAPNodeConfig::CLDAPNodeConfig( CLDAPv3Configs *inConfig, const char *inLDAPURL, bool inDHCPLDAPServer )
{
LDAPURLDesc *ludpp = NULL;
InitializeVariables();
CFUUIDRef cfUUID = CFUUIDCreate( kCFAllocatorDefault );
fConfigUUID = CFUUIDCreateString( kCFAllocatorDefault, cfUUID );
DSCFRelease( cfUUID );
fServerMappings = true; fDHCPLDAPServer = inDHCPLDAPServer;
fNodeIsLDAPURL = true;
fConfigObject = inConfig;
fAvailable = true;
if ( ldap_is_ldapi_url(inLDAPURL) )
{
fNodeName = strdup( inLDAPURL );
fGetReplicas = false;
fReplicaList.push_back( new CLDAPReplicaInfo(fNodeName) );
}
else if ( ldap_url_parse(inLDAPURL, &ludpp) == LDAP_SUCCESS )
{
fNodeName = (inDHCPLDAPServer ? strdup(ludpp->lud_host) : strdup(inLDAPURL));
fServerName = strdup( ludpp->lud_host );
fServerPort = ludpp->lud_port;
fMapSearchBase = (ludpp->lud_dn != NULL ? strdup(ludpp->lud_dn) : NULL);
fIsSSL = (ludpp->lud_port == LDAPS_PORT);
ldap_free_urldesc( ludpp );
ludpp = NULL;
fReadReplicas = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
fWriteReplicas = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
}
char *cStr = NULL;
const char *uuidStr = BaseDirectoryPlugin::GetCStringFromCFString( fConfigUUID, &cStr );
DbgLog( kLogPlugin, "CLDAPNodeConfig::CLDAPNodeConfig - New %s node %s - %s created", (inDHCPLDAPServer ? "DHCP" : "URL"),
fNodeName, uuidStr );
DSFree( cStr );
}
CLDAPNodeConfig::~CLDAPNodeConfig( void )
{
fMutex.WaitLock();
char *cStr = NULL;
const char *uuidStr = BaseDirectoryPlugin::GetCStringFromCFString( fConfigUUID, &cStr );
DbgLog( kLogPlugin, "CLDAPNodeConfig::~CLDAPNodeConfig - Node %s - %s - Deleted no longer in use", fNodeName, uuidStr );
DSFree( cStr );
DSCFRelease( fConfigUUID );
DSFree( fNodeName );
for ( ListOfReplicasI iter = fReplicaList.begin(); iter != fReplicaList.end(); iter++ )
(*iter)->Release();
fReplicaList.clear();
DSFree( fServerAccount );
DSFree( fServerKerberosID );
DSFree( fServerPassword );
DSFree( fMapSearchBase );
DSFree( fServerName );
DSFree( fConfigUIName );
dispatch_cancel( fDynamicRefreshTimer );
dispatch_release( fDynamicRefreshTimer );
DSCFRelease( fNormalizedMappings );
DSCFRelease( fRecordTypeMapArray );
DSCFRelease( fAttrTypeMapArray );
DSCFRelease( fReadReplicas );
DSCFRelease( fWriteReplicas );
DSCFRelease( fDeniedSASLMethods );
if ( fObjectClassSchema != NULL )
{
ObjectClassMapCI iter = fObjectClassSchema->begin();
while ( iter != fObjectClassSchema->end() )
{
iter->second->fParentOCs.clear();
iter->second->fOtherNames.clear();
iter->second->fRequiredAttrs.clear();
iter->second->fAllowedAttrs.clear();
delete iter->second;
iter++;
}
fObjectClassSchema->clear();
DSDelete( fObjectClassSchema );
}
if ( fReachabilityRef != NULL )
{
SCNetworkReachabilityUnscheduleFromRunLoop( fReachabilityRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode );
DSCFRelease( fReachabilityRef );
}
fMutex.SignalLock();
}
#pragma mark -
#pragma mark Establish an LDAP connection
LDAP *CLDAPNodeConfig::EstablishConnection( CLDAPReplicaInfo **inOutReplicaInfo, bool inWriteable, const char *inLDAPUsername,
const char *inKerberosID, const char *inPassword, void *inCallback, void *inParam,
tDirStatus *outStatus )
{
LDAP *pTempLD = NULL;
(*outStatus) = (inWriteable ? eDSAuthMasterUnreachable : eDSCannotAccessSession);
if ( ldap_is_ldapi_url(fNodeName) )
{
(*outStatus) = eDSAuthMethodNotSupported;
}
else
{
pTempLD = InternalEstablishConnection( inOutReplicaInfo, inWriteable, inCallback, inParam );
if ( pTempLD != NULL )
{
(*outStatus) = AuthenticateUsingCredentials( pTempLD, (*inOutReplicaInfo), inLDAPUsername, inKerberosID, inPassword );
if ( (*outStatus) != eDSNoErr )
{
ldap_unbind_ext_s( pTempLD, NULL, NULL );
pTempLD = NULL;
}
}
}
return pTempLD;
}
LDAP *CLDAPNodeConfig::EstablishConnection( CLDAPReplicaInfo **inOutReplicaInfo, bool inWriteable, const char *inKerberosCache, void *inCallback,
void *inParam, tDirStatus *outStatus )
{
LDAP *pTempLD = NULL;
(*outStatus) = (inWriteable ? eDSAuthMasterUnreachable : eDSCannotAccessSession);
if ( ldap_is_ldapi_url(fNodeName) )
{
(*outStatus) = eDSAuthMethodNotSupported;
}
else
{
pTempLD = InternalEstablishConnection( inOutReplicaInfo, inWriteable, inCallback, inParam );
if ( pTempLD != NULL )
{
(*outStatus) = AuthenticateUsingKerberos( pTempLD, (*inOutReplicaInfo), inKerberosCache );
if ( (*outStatus) != eDSNoErr )
{
ldap_unbind_ext_s( pTempLD, NULL, NULL );
pTempLD = NULL;
}
}
}
return pTempLD;
}
LDAP *CLDAPNodeConfig::EstablishConnection( CLDAPReplicaInfo **inOutReplicaInfo, bool inWriteable, void *inCallback, void *inParam,
tDirStatus *outStatus )
{
LDAP *pTempLD = NULL;
(*outStatus) = (inWriteable ? eDSAuthMasterUnreachable : eDSCannotAccessSession);
fMutex.WaitLock();
bool bSecureUse = fSecureUse;
fMutex.SignalLock();
if ( bSecureUse == true )
{
fMutex.WaitLock();
char *pServerAccount = (fServerAccount != NULL ? strdup(fServerAccount) : NULL);
char *pKerberosID = (fServerKerberosID != NULL ? strdup(fServerKerberosID) : NULL);
char *pPassword = (fServerPassword != NULL ? strdup(fServerPassword) : NULL);
fMutex.SignalLock();
pTempLD = EstablishConnection( inOutReplicaInfo, inWriteable, pServerAccount, pKerberosID, pPassword, inCallback,
inParam, outStatus );
DSFree( pServerAccount );
DSFree( pKerberosID );
DSFreePassword( pPassword );
}
else
{
if ( ldap_is_ldapi_url(fNodeName) )
{
fMutex.WaitLock();
CLDAPReplicaInfo *replica = *(fReplicaList.begin());
(*outStatus) = eDSCannotAccessSession;
pTempLD = replica->VerifiedServerConnection( 10, true, NULL, NULL );
if ( pTempLD != NULL )
{
if ( RetrieveServerMappings(pTempLD, replica) == true )
{
(*outStatus) = eDSNoErr;
(*inOutReplicaInfo) = replica->Retain();
}
if ( (*outStatus) != eDSNoErr )
{
ldap_unbind_ext_s( pTempLD, NULL, NULL );
pTempLD = NULL;
}
}
fMutex.SignalLock();
}
else
{
pTempLD = InternalEstablishConnection( inOutReplicaInfo, inWriteable, inCallback, inParam );
}
}
if ( pTempLD != NULL )
(*outStatus) = eDSNoErr;
return pTempLD;
}
bool CLDAPNodeConfig::UpdateDynamicData( LDAP *inLD, CLDAPReplicaInfo *inReplica )
{
bool bUpdated = false;
if ( inLD == NULL || inReplica == NULL )
return false;
fMutex.WaitLock();
if ( fGetServerMappings == true )
RetrieveServerMappings( inLD, inReplica );
if ( fGetSecuritySettings == true && RetrieveServerSecuritySettings(inLD, inReplica) == true )
bUpdated = true;
if ( fGetReplicas == true )
{
CFMutableArrayRef readList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
CFMutableArrayRef writeList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
if ( RetrieveServerReplicaList(inLD, readList, writeList) == true )
bUpdated = true;
if ( GetReplicaListFromAltServer(inLD, readList) == eDSNoErr )
bUpdated = true;
if ( fDNSReplicas == true )
{
if( GetReplicaListFromDNS(readList) == eDSNoErr )
bUpdated = true;
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetReplicaListFromDNS - Node %s - Skipped - disabled in configuration", fNodeName );
}
if ( CFArrayGetCount(readList) > 0 )
{
DSCFRelease( fReadReplicas );
fReadReplicas = readList;
readList = NULL;
DSCFRelease( fWriteReplicas );
fWriteReplicas = writeList;
writeList = NULL;
if ( fServerName != NULL )
{
CFStringRef cfPreferred = CFStringCreateWithCString( kCFAllocatorDefault, fServerName, kCFStringEncodingUTF8 );
CFArrayInsertValueAtIndex( fReadReplicas, 0, cfPreferred );
DSCFRelease( cfPreferred );
}
for ( ListOfReplicasI iter = fReplicaList.begin(); iter != fReplicaList.end(); iter++ )
(*iter)->Release();
fReplicaList.clear();
DbgLog( kLogPlugin, "CLDAPNodeConfig::UpdateDynamicData - Node %s - emptying current replica list in favor of new list",
fNodeName );
BuildReplicaList();
LaunchKerberosAutoConfigTool();
}
DSCFRelease( readList );
DSCFRelease( writeList );
}
fMutex.SignalLock();
return bUpdated;
}
void CLDAPNodeConfig::ReinitializeReplicaList( void )
{
if ( fServerName != NULL )
{
fMutex.WaitLock();
DbgLog( kLogPlugin, "CLDAPNodeConfig::ReinitializeReplicaList - Node %s - emptying current replica list", fNodeName );
for ( ListOfReplicasI iter = fReplicaList.begin(); iter != fReplicaList.end(); iter++ )
(*iter)->Release();
fReplicaList.clear();
fGetReplicas = true;
CFArrayRemoveAllValues( fReadReplicas );
CFArrayRemoveAllValues( fWriteReplicas );
CFStringRef cfServerName = CFStringCreateWithCString( kCFAllocatorDefault, fServerName, kCFStringEncodingUTF8 );
CFArrayAppendValue( fReadReplicas, cfServerName );
DSCFRelease( cfServerName );
BuildReplicaList();
fMutex.SignalLock();
}
}
#pragma mark -
#pragma mark Attribute and Record type mapping
char *CLDAPNodeConfig::MapRecToSearchBase( const char *inRecType, int inIndex, bool *outOCGroup, CFArrayRef *outOCListCFArray, ber_int_t *outScope )
{
char *outResult = NULL;
UInt32 uiNativeLen = sizeof(kDSNativeRecordTypePrefix) - 1;
UInt32 uiStdLen = sizeof(kDSStdRecordTypePrefix) - 1;
if ( inRecType != NULL && inIndex > 0 )
{
uint32_t uiStrLen = strlen( inRecType );
if ( uiStrLen >= uiNativeLen && strncmp(inRecType, kDSNativeRecordTypePrefix, uiNativeLen) == 0 )
{
if ( uiStrLen > uiNativeLen && inIndex == 1 )
{
outResult = strdup( inRecType + uiNativeLen );
DbgLog( kLogPlugin, "CLDAPNodeConfig::MapRecToSearchBase - Warning native record type <%s> is being used", outResult );
}
}
else if ( uiStrLen >= uiStdLen && strncmp(inRecType, kDSStdRecordTypePrefix, uiStdLen) == 0 )
{
outResult = ExtractRecMap( inRecType, inIndex, outOCGroup, outOCListCFArray, outScope );
}
else if ( inIndex == 1 )
{
outResult = strdup( inRecType );
DbgLog( kLogPlugin, "CLDAPNodeConfig::MapRecToSearchBase - Warning native record type with no provided prefix <%s> is being used",
outResult );
}
}
return( outResult );
}
char *CLDAPNodeConfig::MapAttrToLDAPType( const char *inRecType, const char *inAttrType, int inIndex, bool bSkipLiteralMappings )
{
char *outResult = NULL;
UInt32 uiStrLen = 0;
UInt32 uiNativeLen = sizeof(kDSNativeAttrTypePrefix) - 1;
UInt32 uiStdLen = sizeof(kDSStdAttrTypePrefix) - 1;
if ( inAttrType != NULL && inIndex > 0 )
{
uiStrLen = strlen( inAttrType );
if ( strncmp(inAttrType, kDSNativeAttrTypePrefix, uiNativeLen) == 0 )
{
if ( uiStrLen > uiNativeLen && inIndex == 1 )
{
outResult = strdup( inAttrType + uiNativeLen );
DbgLog( kLogPlugin, "CLDAPNodeConfig::MapAttrToLDAPType - Warning Native attribute type <%s> is being used", outResult );
}
}
else if ( strncmp(inAttrType, kDSStdAttrTypePrefix, uiStdLen) == 0 )
{
if ( bSkipLiteralMappings == false )
{
outResult = ExtractAttrMap( inRecType, inAttrType, inIndex );
}
else
{
while( 1 )
{
outResult = ExtractAttrMap( inRecType, inAttrType, inIndex );
if ( outResult == NULL )
break;
if ( outResult[0] == '#' )
{
DSFree( outResult );
}
else
{
break;
}
inIndex++;
}
}
}
else
{
if ( inIndex == 1 )
{
outResult = strdup( inAttrType );
DbgLog( kLogPlugin, "CLDAPNodeConfig::MapAttrToLDAPType - Warning Native attribute type with no provided prefix <%s> is being used",
outResult );
}
}
}
return( outResult );
}
char **CLDAPNodeConfig::MapAttrToLDAPTypeArray( const char *inRecType, const char *inAttrType )
{
char **outResult = NULL;
UInt32 uiStrLen = 0;
UInt32 uiNativeLen = sizeof(kDSNativeAttrTypePrefix) - 1;
UInt32 uiStdLen = sizeof(kDSStdAttrTypePrefix) - 1;
int countNative = 0;
if ( inAttrType != NULL )
{
uiStrLen = strlen( inAttrType );
if ( strncmp(inAttrType, kDSNativeAttrTypePrefix, uiNativeLen) == 0 )
{
if ( uiStrLen > uiNativeLen )
{
uiStrLen = uiStrLen - uiNativeLen;
outResult = (char **) calloc( 2, sizeof( char * ) );
(*outResult) = strdup( inAttrType + uiNativeLen );
DbgLog( kLogPlugin, "CLDAPNodeConfig::MapAttrToLDAPTypeArray - Warning Native attribute type <%s> is being used", *outResult );
}
}
else if ( strncmp(inAttrType, kDSStdAttrTypePrefix, uiStdLen) == 0 )
{
fMutex.WaitLock();
countNative = AttrMapsCount( inRecType, inAttrType );
if ( countNative > 0 )
{
int aIndex = 1;
int usedIndex = 0;
outResult = (char **) calloc( countNative+1, sizeof(char *) );
while ( 1 )
{
char *singleMap = ExtractAttrMap( inRecType, inAttrType, aIndex );
if ( singleMap == NULL )
break;
if ( singleMap[0] != '#' )
{
outResult[usedIndex] = singleMap; usedIndex++;
singleMap = NULL;
}
else
{
break;
}
DSFree( singleMap );
aIndex++;
}
}
fMutex.SignalLock();
}
else
{
outResult = (char **) calloc( 2, sizeof(char *) );
(*outResult) = strdup( inAttrType );
DbgLog( kLogPlugin, "CLDAPNodeConfig::MapAttrToLDAPTypeArray - Warning Native attribute type with no provided prefix <%s> is being used",
*outResult );
}
}
return outResult;
}
char *CLDAPNodeConfig::ExtractRecMap( const char *inRecType, int inIndex, bool *outOCGroup, CFArrayRef *outOCListCFArray,
ber_int_t* outScope )
{
char *outResult = NULL;
fMappingsLock.WaitLock();
if ( (fNormalizedMappings != NULL) && (inRecType != NULL) )
{
CFStringRef cfRecTypeRef = CFStringCreateWithCStringNoCopy( kCFAllocatorDefault, inRecType, kCFStringEncodingUTF8, kCFAllocatorNull );
CFDictionaryRef cfRecordMap = NULL;
if ( cfRecTypeRef != NULL )
cfRecordMap = (CFDictionaryRef) CFDictionaryGetValue( fNormalizedMappings, cfRecTypeRef );
if ( cfRecordMap != NULL )
{
CFArrayRef cfNativeArray = (CFArrayRef) CFDictionaryGetValue( cfRecordMap, CFSTR(kXMLNativeMapArrayKey) );
if ( inIndex <= CFArrayGetCount(cfNativeArray) )
{
CFDictionaryRef cfCurrentMap = (CFDictionaryRef) CFArrayGetValueAtIndex( cfNativeArray, inIndex-1 );
CFStringRef searchBase = (CFStringRef) CFDictionaryGetValue( cfCurrentMap, CFSTR(kXMLSearchBase) );
if ( searchBase != NULL )
{
UInt32 uiLength = (UInt32) CFStringGetMaximumSizeForEncoding( CFStringGetLength(searchBase), kCFStringEncodingUTF8 ) + 1;
outResult = (char *) calloc( sizeof(char), uiLength );
CFStringGetCString( searchBase, outResult, uiLength, kCFStringEncodingUTF8 );
}
if ( outOCListCFArray != NULL && outOCGroup != NULL )
{
*outOCGroup = 0;
CFArrayRef objectClasses = (CFArrayRef)CFDictionaryGetValue( cfCurrentMap, CFSTR(kXMLObjectClasses) );
if ( objectClasses != NULL )
{
CFStringRef groupOCString = (CFStringRef) CFDictionaryGetValue( cfCurrentMap, CFSTR(kXMLGroupObjectClasses) );
if ( groupOCString != NULL && CFStringCompare( groupOCString, CFSTR("AND"), 0 ) == kCFCompareEqualTo )
*outOCGroup = 1;
*outOCListCFArray = CFArrayCreateCopy(kCFAllocatorDefault, objectClasses);
}
}
if ( outScope != NULL )
{
CFBooleanRef cfBoolRef = (CFBooleanRef) CFDictionaryGetValue( cfCurrentMap, CFSTR(kXMLOneLevelSearchScope) );
if ( cfBoolRef != NULL && CFBooleanGetValue(cfBoolRef) )
*outScope = LDAP_SCOPE_ONELEVEL;
else
*outScope = LDAP_SCOPE_SUBTREE;
}
} }
DSCFRelease( cfRecTypeRef );
}
fMappingsLock.SignalLock();
return( outResult );
}
char *CLDAPNodeConfig::ExtractAttrMap( const char *inRecType, const char *inAttrType, int inIndex )
{
char *outResult = NULL;
fMappingsLock.WaitLock();
if ( (fNormalizedMappings != NULL) && (inRecType != NULL) && (inAttrType != NULL) && (inIndex >= 1) )
{
CFDictionaryRef cfRecordMap = NULL;
CFStringRef cfRecTypeRef = CFStringCreateWithCString(kCFAllocatorDefault, inRecType, kCFStringEncodingUTF8);
if ( cfRecTypeRef != NULL )
{
cfRecordMap = (CFDictionaryRef) CFDictionaryGetValue( fNormalizedMappings, cfRecTypeRef );
if ( cfRecordMap != NULL )
{
CFDictionaryRef cfAttrMapDictRef = (CFDictionaryRef) CFDictionaryGetValue( cfRecordMap, CFSTR(kXMLAttrTypeMapDictKey) );
if ( cfAttrMapDictRef != NULL )
{
CFArrayRef cfMapArray = NULL;
CFStringRef cfAttrTypeRef = CFStringCreateWithCString( kCFAllocatorDefault, inAttrType, kCFStringEncodingUTF8 );
if ( cfAttrTypeRef != NULL )
{
cfMapArray = (CFArrayRef) CFDictionaryGetValue( cfAttrMapDictRef, cfAttrTypeRef );
if ( cfMapArray != NULL && inIndex <= CFArrayGetCount(cfMapArray) )
{
CFStringRef nativeMapString = (CFStringRef) CFArrayGetValueAtIndex( cfMapArray, inIndex - 1 );
UInt32 uiLength = (UInt32) CFStringGetMaximumSizeForEncoding( CFStringGetLength(nativeMapString), kCFStringEncodingUTF8 ) + 1;
outResult = (char *) calloc( sizeof(char), uiLength );
CFStringGetCString( nativeMapString, outResult, uiLength, kCFStringEncodingUTF8 );
}
DSCFRelease(cfAttrTypeRef);
}
}
}
DSCFRelease(cfRecTypeRef);
}
}
fMappingsLock.SignalLock();
return( outResult );
}
char *CLDAPNodeConfig::ExtractStdAttrName( char *inRecType, int &inputIndex )
{
char *outResult = NULL;
fMappingsLock.WaitLock();
if ( (fNormalizedMappings != NULL) && (inRecType != NULL) && (inputIndex >= 1) )
{
CFDictionaryRef cfRecordMap = NULL;
CFStringRef cfRecTypeRef = CFStringCreateWithCString(kCFAllocatorDefault, inRecType, kCFStringEncodingUTF8);
if ( cfRecTypeRef != NULL )
{
cfRecordMap = (CFDictionaryRef) CFDictionaryGetValue( fNormalizedMappings, cfRecTypeRef );
if ( cfRecordMap != NULL )
{
CFDictionaryRef cfAttrMapDict = (CFDictionaryRef) CFDictionaryGetValue( cfRecordMap, CFSTR( kXMLAttrTypeMapDictKey ) );
if (cfAttrMapDict != NULL)
{
CFIndex iTotalEntries = CFDictionaryGetCount( cfAttrMapDict );
if ( inputIndex <= iTotalEntries )
{
CFStringRef *keys = (CFStringRef *) calloc( iTotalEntries, sizeof(CFStringRef) );
if ( keys != NULL )
{
CFDictionaryGetKeysAndValues( cfAttrMapDict, (const void **)keys, NULL );
UInt32 uiLength = (UInt32) CFStringGetMaximumSizeForEncoding( CFStringGetLength(keys[inputIndex - 1]), kCFStringEncodingUTF8 ) + 1;
outResult = (char *) calloc( sizeof(char), uiLength );
CFStringGetCString( keys[inputIndex - 1], outResult, uiLength, kCFStringEncodingUTF8 );
DSFree( keys );
}
}
}
}
DSCFRelease(cfRecTypeRef);
}
}
fMappingsLock.SignalLock();
return( outResult );
}
int CLDAPNodeConfig::AttrMapsCount( const char *inRecType, const char *inAttrType )
{
int outCount = 0;
fMappingsLock.WaitLock();
if ( (fNormalizedMappings != NULL) && (inRecType != NULL) && (inAttrType != NULL) )
{
CFStringRef cfRecTypeRef = CFStringCreateWithCString(kCFAllocatorDefault, inRecType, kCFStringEncodingUTF8);
CFDictionaryRef cfRecordMap = NULL;
if ( cfRecTypeRef != NULL )
cfRecordMap = (CFDictionaryRef) CFDictionaryGetValue( fNormalizedMappings, cfRecTypeRef );
if ( cfRecordMap != NULL )
{
CFDictionaryRef cfAttrMapDictRef = (CFDictionaryRef) CFDictionaryGetValue( cfRecordMap, CFSTR(kXMLAttrTypeMapDictKey) );
if ( cfAttrMapDictRef != NULL )
{
CFArrayRef cfMapArray = NULL;
CFStringRef cfAttrTypeRef = CFStringCreateWithCString(kCFAllocatorDefault, inAttrType, kCFStringEncodingUTF8);
if ( cfAttrTypeRef != NULL )
{
cfMapArray = (CFArrayRef) CFDictionaryGetValue( cfAttrMapDictRef, cfAttrTypeRef );
if ( cfMapArray != NULL )
{
outCount = CFArrayGetCount( cfMapArray );
}
DSCFRelease( cfAttrTypeRef );
}
}
}
DSCFRelease( cfRecTypeRef );
}
fMappingsLock.SignalLock();
return( outCount );
}
CFDictionaryRef CLDAPNodeConfig::CopyNormalizedMappings( void )
{
CFDictionaryRef cfReturnValue = NULL;
fMappingsLock.WaitLock();
if ( fNormalizedMappings != NULL )
cfReturnValue = CFDictionaryCreateCopy( kCFAllocatorDefault, fNormalizedMappings );
fMappingsLock.SignalLock();
return cfReturnValue;
}
void CLDAPNodeConfig::GetReqAttrListForObjectList( CLDAPConnection *inLDAPConnection, listOfStrings &inObjectClassList,
listOfStrings &outReqAttrsList )
{
if ( fObjectClassSchema == NULL ) {
LDAP *aHost = inLDAPConnection->LockLDAPSession();
if ( aHost != NULL ) {
RetrieveServerSchema( aHost );
inLDAPConnection->UnlockLDAPSession( aHost, false );
}
}
fMutex.WaitLock();
if ( fObjectClassSchema != NULL )
{
for (listOfStringsCI iter = inObjectClassList.begin(); iter != inObjectClassList.end(); ++iter)
{
if ( fObjectClassSchema->count(*iter) != 0 )
{
ObjectClassMap::iterator mapIter = fObjectClassSchema->find(*iter);
for (AttrSetCI parentIter = mapIter->second->fParentOCs.begin(); parentIter != mapIter->second->fParentOCs.end(); ++parentIter)
{
bool addObjectClassName = true;
for (listOfStringsCI dupIter = inObjectClassList.begin(); dupIter != inObjectClassList.end(); ++dupIter)
{
if (*dupIter == *parentIter) {
addObjectClassName = false;
break;
}
}
if (addObjectClassName)
{
inObjectClassList.push_back(*parentIter);
}
}
}
}
for (listOfStringsCI iter = inObjectClassList.begin(); iter != inObjectClassList.end(); ++iter)
{
if (fObjectClassSchema->count(*iter) != 0)
{
ObjectClassMap::iterator mapIter = fObjectClassSchema->find(*iter);
for (AttrSetCI reqIter = mapIter->second->fRequiredAttrs.begin(); reqIter != mapIter->second->fRequiredAttrs.end(); ++reqIter)
{
if ( (*reqIter != "objectClass") && (*reqIter != "nTSecurityDescriptor") && (*reqIter != "objectCategory") && (*reqIter != "instanceType") ) {
bool addReqAttr = true;
for (listOfStringsCI dupIter = outReqAttrsList.begin(); dupIter != outReqAttrsList.end(); ++dupIter)
{
if (*dupIter == *reqIter) {
addReqAttr = false;
break;
}
}
if (addReqAttr)
{
outReqAttrsList.push_back(*reqIter);
}
}
if (*reqIter == "nTSecurityDescriptor") {
string nameString("sAMAccountName");
outReqAttrsList.push_back(nameString);
}
}
}
}
}
fMutex.SignalLock();
}
#pragma mark -
#pragma mark Filter routines
char *CLDAPNodeConfig::BuildLDAPQueryFilter( char *inConstAttrType, const char *inConstAttrName, tDirPatternMatch patternMatch,
bool useWellKnownRecType, const char *inRecType, char *inNativeRecType, bool inbOCANDGroup,
CFArrayRef inOCSearchList )
{
char *queryFilter = NULL;
UInt32 matchType = eDSExact;
char *nativeAttrType = NULL;
UInt32 recNameLen = 0;
int numAttributes = 1;
CFMutableStringRef cfStringRef = NULL;
CFMutableStringRef cfQueryStringRef = NULL;
char *escapedName = NULL;
UInt32 escapedIndex = 0;
UInt32 originalIndex = 0;
bool bOnceThru = false;
UInt32 offset = 3;
UInt32 callocLength = 0;
bool objClassAdded = false;
bool bGetAllDueToLiteralMapping = false;
int aOCSearchListCount = 0;
cfQueryStringRef = CFStringCreateMutable( kCFAllocatorDefault, 0 );
if ( (inRecType != NULL) && (inNativeRecType != NULL) )
{
if ( strncmp(inRecType, kDSStdRecordTypePrefix, sizeof(kDSStdRecordTypePrefix)-1) == 0 )
{
if (inOCSearchList != NULL)
{
aOCSearchListCount = CFArrayGetCount(inOCSearchList);
for ( int iOCIndex = 0; iOCIndex < aOCSearchListCount; iOCIndex++ )
{
CFStringRef ocString;
ocString = (CFStringRef)::CFArrayGetValueAtIndex( inOCSearchList, iOCIndex );
if (ocString != NULL)
{
if (!objClassAdded)
{
objClassAdded = true;
if (inbOCANDGroup)
{
CFStringAppendCString(cfQueryStringRef,"(&", kCFStringEncodingUTF8);
}
else
{
CFStringAppendCString(cfQueryStringRef,"(&(|", kCFStringEncodingUTF8);
}
}
CFStringAppendCString(cfQueryStringRef, "(objectclass=", kCFStringEncodingUTF8);
CFStringAppend(cfQueryStringRef, ocString);
CFStringAppendCString(cfQueryStringRef, ")", kCFStringEncodingUTF8);
}
} if (CFStringGetLength(cfQueryStringRef) != 0)
{
if (!inbOCANDGroup)
{
CFStringAppendCString(cfQueryStringRef,")", kCFStringEncodingUTF8);
}
}
}
} }
if ( (CFStringGetLength(cfQueryStringRef) == 0) && (inConstAttrName == NULL) )
{
CFStringAppendCString(cfQueryStringRef,"(&(objectclass=*)", kCFStringEncodingUTF8);
objClassAdded = true;
}
if ( (patternMatch == eDSCompoundExpression) ||
(patternMatch == eDSiCompoundExpression) )
{
CFStringRef cfTempString = ParseCompoundExpression( inConstAttrName, inRecType );
if (cfTempString != NULL)
{
CFStringAppend(cfQueryStringRef, cfTempString);
DSCFRelease(cfTempString);
}
}
else
{
if (inConstAttrName != NULL)
{
recNameLen = strlen(inConstAttrName);
escapedName = (char *)::calloc(1, 3 * recNameLen + 1);
while (originalIndex < recNameLen)
{
switch (inConstAttrName[originalIndex])
{
case '*':
escapedName[escapedIndex] = '\\';
++escapedIndex;
escapedName[escapedIndex] = '2';
++escapedIndex;
escapedName[escapedIndex] = 'a';
++escapedIndex;
break;
case '(':
escapedName[escapedIndex] = '\\';
++escapedIndex;
escapedName[escapedIndex] = '2';
++escapedIndex;
escapedName[escapedIndex] = '8';
++escapedIndex;
break;
case ')':
escapedName[escapedIndex] = '\\';
++escapedIndex;
escapedName[escapedIndex] = '2';
++escapedIndex;
escapedName[escapedIndex] = '9';
++escapedIndex;
break;
case '\\':
escapedName[escapedIndex] = '\\';
++escapedIndex;
escapedName[escapedIndex] = '5';
++escapedIndex;
escapedName[escapedIndex] = 'c';
++escapedIndex;
break;
default:
escapedName[escapedIndex] = inConstAttrName[originalIndex];
++escapedIndex;
break;
}
++originalIndex;
}
cfStringRef = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("(|"));
numAttributes = 1;
nativeAttrType = MapAttrToLDAPType( inRecType, inConstAttrType, numAttributes, false );
if (nativeAttrType == NULL) {
nativeAttrType = strdup("cn");
}
while ( nativeAttrType != NULL )
{
if (nativeAttrType[0] == '#') {
if (strlen(nativeAttrType) > 1)
{
if (DoesThisMatch(escapedName, nativeAttrType+1, patternMatch))
{
if (CFStringGetLength(cfQueryStringRef) == 0)
{
CFStringAppendCString(cfQueryStringRef,"(&(objectclass=*)", kCFStringEncodingUTF8);
objClassAdded = true;
}
bGetAllDueToLiteralMapping = true;
free(nativeAttrType);
nativeAttrType = NULL;
continue;
}
}
}
else
{
if (bOnceThru)
{
if (useWellKnownRecType)
{
bOnceThru = false;
break;
}
offset = 0;
}
matchType = (UInt32) (patternMatch);
switch (matchType)
{
case eDSStartsWith:
case eDSiStartsWith:
CFStringAppendCString(cfStringRef,"(", kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef, nativeAttrType, kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef,"=", kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef, escapedName, kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef,"*)", kCFStringEncodingUTF8);
bOnceThru = true;
break;
case eDSEndsWith:
case eDSiEndsWith:
CFStringAppendCString(cfStringRef,"(", kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef, nativeAttrType, kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef,"=*", kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef, escapedName, kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef,")", kCFStringEncodingUTF8);
bOnceThru = true;
break;
case eDSContains:
case eDSiContains:
CFStringAppendCString(cfStringRef,"(", kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef, nativeAttrType, kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef,"=*", kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef, escapedName, kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef,"*)", kCFStringEncodingUTF8);
bOnceThru = true;
break;
case eDSWildCardPattern:
case eDSiWildCardPattern:
CFStringAppendCString(cfStringRef,"(", kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef, nativeAttrType, kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef,"=", kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef, inConstAttrName, kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef,")", kCFStringEncodingUTF8);
bOnceThru = true;
break;
case eDSRegularExpression:
case eDSiRegularExpression:
CFStringAppendCString(cfStringRef, inConstAttrName, kCFStringEncodingUTF8);
bOnceThru = true;
break;
case eDSExact:
case eDSiExact:
default:
CFStringAppendCString(cfStringRef,"(", kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef, nativeAttrType, kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef,"=", kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef, escapedName, kCFStringEncodingUTF8);
CFStringAppendCString(cfStringRef,")", kCFStringEncodingUTF8);
bOnceThru = true;
break;
} }
if (nativeAttrType != NULL)
{
free(nativeAttrType);
nativeAttrType = NULL;
}
numAttributes++;
nativeAttrType = MapAttrToLDAPType( inRecType, inConstAttrType, numAttributes, false );
}
if (!bGetAllDueToLiteralMapping)
{
if (cfStringRef != NULL)
{
if (offset == 3)
{
CFRange aRangeToDelete;
aRangeToDelete.location = 1;
aRangeToDelete.length = 2;
CFStringDelete(cfStringRef, aRangeToDelete);
}
else
{
CFStringAppendCString(cfStringRef,")", kCFStringEncodingUTF8);
}
CFStringAppend(cfQueryStringRef, cfStringRef);
}
}
if (cfStringRef != NULL)
{
CFRelease(cfStringRef);
cfStringRef = NULL;
}
if (escapedName != NULL)
{
free(escapedName);
escapedName = NULL;
}
} }
if (objClassAdded)
{
CFStringAppendCString(cfQueryStringRef,")", kCFStringEncodingUTF8);
}
if (CFStringGetLength(cfQueryStringRef) != 0)
{
callocLength = (UInt32) CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfQueryStringRef), kCFStringEncodingUTF8) + 1;
queryFilter = (char *) calloc(1, callocLength);
CFStringGetCString( cfQueryStringRef, queryFilter, callocLength, kCFStringEncodingUTF8 );
}
if (cfQueryStringRef != NULL)
{
CFRelease(cfQueryStringRef);
cfQueryStringRef = NULL;
}
return (queryFilter);
}
CFStringRef CLDAPNodeConfig::ParseCompoundExpression( const char *inConstAttrName, const char *inRecType )
{
if ( inConstAttrName == NULL || inRecType == NULL )
return NULL;
const int kMatchOr = 1;
const int kMatchAnd = 2;
const int kMatchNot = 3;
char *workingString = strdup( inConstAttrName );
int iIndex = 0;
int iDepth = 0;
int iExpression = 0; int iMatchType[128]; char *pExpression[2];
char *pWorkingBuffer = NULL;
bool bExpand = false;
bool bBadSearch = true;
char currChar;
int iFilterLen = strlen( workingString );
pExpression[0] = (char *) malloc( iFilterLen );
pExpression[1] = (char *) malloc( iFilterLen );
CFMutableStringRef outExpression = CFStringCreateMutable( kCFAllocatorDefault, 0 );
while ( (currChar = workingString[iIndex]) != '\0' )
{
bool bAddCharToOut = true;
switch (currChar)
{
case '(':
if ( (++iDepth) > 127 ) goto done;
iExpression = 0;
pWorkingBuffer = pExpression[0];
break;
case ')':
if ( (--iDepth) < 0 ) goto done;
if ( pWorkingBuffer != NULL )
{
(*pWorkingBuffer) = '\0'; bExpand = true;
}
break;
case '&':
iMatchType[iDepth] = kMatchAnd;
break;
case '|':
iMatchType[iDepth] = kMatchOr;
break;
case '!':
iMatchType[iDepth] = kMatchNot;
break;
case '\\': if ( pWorkingBuffer == NULL ) goto done;
(*pWorkingBuffer) = currChar;
pWorkingBuffer++;
currChar = workingString[++iIndex];
if ( currChar == '\0' ) goto done;
(*pWorkingBuffer) = currChar;
pWorkingBuffer++;
bAddCharToOut = false;
break;
case '=':
if ( pWorkingBuffer == NULL ) goto done;
(*pWorkingBuffer) = '\0'; pWorkingBuffer = pExpression[1];
iExpression = 1; bAddCharToOut = false;
break;
default:
if ( pWorkingBuffer == NULL ) goto done;
(*pWorkingBuffer) = currChar;
pWorkingBuffer++;
bAddCharToOut = false;
break;
}
if ( bExpand )
{
char **attrNames = NULL;
if ( iExpression != 1 ) goto done;
if ( strncasecmp( pExpression[0], kDSStdAttrTypePrefix, sizeof(kDSStdAttrTypePrefix)-1 ) == 0 )
{
attrNames = MapAttrToLDAPTypeArray( inRecType, pExpression[0] );
}
else if ( strncasecmp( pExpression[0], kDSNativeAttrTypePrefix, sizeof(kDSNativeAttrTypePrefix)-1 ) == 0 )
{
attrNames = (char **) calloc( 2, sizeof(char *) );
attrNames[0] = strdup( pExpression[0] + (sizeof(kDSNativeAttrTypePrefix)-1) );
}
else {
attrNames = (char **) calloc( 2, sizeof(char *) );
attrNames[0] = strdup( pExpression[0] );
}
if ( attrNames != NULL && attrNames[0] != NULL )
{
int iCount = 0;
for ( char **tempNames = attrNames; (*tempNames) != NULL; iCount++, tempNames++ );
if ( iCount > 1 )
{
CFStringAppend( outExpression, CFSTR("|") );
for ( int ii = 0; ii < iCount; ii ++ )
{
CFStringAppendFormat( outExpression, 0, CFSTR("(%s="), attrNames[ii] );
CFStringAppendCString( outExpression, pExpression[1], kCFStringEncodingUTF8 );
CFStringAppend( outExpression, CFSTR(")") );
}
}
else
{
CFStringAppendFormat( outExpression, 0, CFSTR("%s="), attrNames[0] );
CFStringAppendCString( outExpression, pExpression[1], kCFStringEncodingUTF8 );
}
}
else if ( iMatchType[iDepth] == kMatchAnd )
{
goto done;
}
else
{
CFStringDelete( outExpression, CFRangeMake(CFStringGetLength(outExpression)-1, 1) );
bAddCharToOut = false;
}
DSFreeStringList( attrNames );
pWorkingBuffer = NULL;
bExpand = false;
iExpression = 0;
}
if ( bAddCharToOut )
{
char cBuffer[2] = { currChar, 0 };
CFStringAppendCString( outExpression, cBuffer, kCFStringEncodingUTF8 );
}
iIndex++;
}
if ( iDepth == 0 )
bBadSearch = false;
done:
if ( bBadSearch )
DSCFRelease( outExpression );
DSFree( workingString );
DSFree( pExpression[0] );
DSFree( pExpression[1] );
return( outExpression );
}
#pragma mark -
char *CLDAPNodeConfig::CopyUIName( void )
{
char *returnValue = NULL;
fMutex.WaitLock();
if ( fConfigUIName != NULL )
returnValue = strdup( fConfigUIName );
fMutex.SignalLock();
return returnValue;
}
char *CLDAPNodeConfig::CopyMapSearchBase( void )
{
char *returnValue = NULL;
fMutex.WaitLock();
if ( fMapSearchBase != NULL )
returnValue = strdup( fMapSearchBase );
fMutex.SignalLock();
return returnValue;
}
bool CLDAPNodeConfig::CopyCredentials( char **outUsername, char **outKerberosID, char **outPassword )
{
fMutex.WaitLock();
if ( fSecureUse )
{
if ( fServerAccount != NULL )
(*outUsername) = strdup( fServerAccount );
if ( fServerKerberosID != NULL )
(*outKerberosID) = strdup( fServerKerberosID );
if ( fServerPassword != NULL )
(*outPassword) = strdup( fServerPassword );
}
fMutex.SignalLock();
return fSecureUse;
}
void CLDAPNodeConfig::NetworkTransition( void )
{
OSAtomicCompareAndSwap32Barrier( false, true, &fGetReplicas );
fMutex.WaitLock();
fLastFailedCheck = 0.0;
fMutex.SignalLock();
}
bool CLDAPNodeConfig::CheckIfFailed( void )
{
bool bNameResolves = true;
bool bReturnValue = true;
fMutex.WaitLock();
if ( fReachabilityRef != NULL && fAvailable == false && fServerName != NULL )
{
struct addrinfo *addrList = NULL;
if ( getaddrinfo(fServerName, NULL, NULL, &addrList) == 0 )
{
OSAtomicCompareAndSwap32Barrier( false, true, &fAvailable );
freeaddrinfo( addrList );
}
else
{
OSAtomicCompareAndSwap32Barrier( true, false, &fAvailable );
DbgLog( kLogPlugin, "CLDAPNodeConfig::CheckIfFailed - name %s - does not resolve assuming no connectivity", fServerName );
bNameResolves = false;
}
}
fMutex.SignalLock();
if ( bNameResolves == true )
{
bool bForceCheck = false;
CLDAPReplicaInfo *tempReplica = NULL;
fMutex.WaitLock();
CFAbsoluteTime timeCheck = CFAbsoluteTimeGetCurrent();
if ( timeCheck - fLastFailedCheck > fDelayRebindTry )
{
fLastFailedCheck = timeCheck;
bForceCheck = true;
}
fMutex.SignalLock();
LDAP *pTempLD = FindSuitableReplica( &tempReplica, bForceCheck, false, NULL, NULL );
if ( pTempLD != NULL )
{
DSRelease( tempReplica );
ldap_unbind_ext_s( pTempLD, NULL, NULL );
pTempLD = NULL;
bReturnValue = false;
}
}
return bReturnValue;
}
#pragma mark -
#pragma mark Callbacks
void CLDAPNodeConfig::ReachabilityCallback( SCNetworkReachabilityRef inTarget, SCNetworkConnectionFlags inFlags, void *inInfo )
{
((CLDAPNodeConfig *) inInfo)->ReachabilityNotification( inFlags );
}
#pragma mark -
#pragma mark Private Routines
void CLDAPNodeConfig::ReachabilityNotification( SCNetworkConnectionFlags inFlags )
{
if ( (inFlags & kSCNetworkFlagsReachable) == 0 || (inFlags & kSCNetworkFlagsConnectionRequired) != 0 )
OSAtomicCompareAndSwap32Barrier( true, false, &fAvailable );
else
OSAtomicCompareAndSwap32Barrier( false, true, &fAvailable );
DbgLog( kLogPlugin, "CLDAPNodeConfig::ReachabilityNotification - Node: %s - %s", fNodeName,
(fAvailable ? "resolves - enabled" : "does not resolve - disabled") );
}
void CLDAPNodeConfig::SetLDAPOptions( LDAP *inLDAP )
{
int version = LDAP_VERSION3;
ldap_set_option( inLDAP, LDAP_OPT_PROTOCOL_VERSION, &version );
if ( fIsSSL )
{
int ldapOptVal = LDAP_OPT_X_TLS_HARD;
ldap_set_option( inLDAP, LDAP_OPT_X_TLS, &ldapOptVal );
}
ldap_set_option( inLDAP, LDAP_OPT_REFERRALS, (fReferrals ? LDAP_OPT_ON : LDAP_OPT_OFF) );
if ( (fSecurityLevel & kSecPacketEncryption) != 0 )
ldap_set_option( inLDAP, LDAP_OPT_X_SASL_SECPROPS, (void *) "minssf=56" );
else if ( (fSecurityLevel & kSecPacketSigning) != 0 )
ldap_set_option( inLDAP, LDAP_OPT_X_SASL_SECPROPS, (void *) "minssf=1" );
}
void CLDAPNodeConfig::InitializeVariables( void )
{
fConfigUUID = NULL;
fNodeName = NULL;
fDHCPLDAPServer = false;
fNodeIsLDAPURL = false;
fIsSSL = false;
fIdleMaxCount = 2;
fSearchTimeout = kLDAPDefaultSearchTimeoutInSeconds;
fOpenCloseTimeout = kLDAPDefaultOpenCloseTimeoutInSeconds;
fDelayRebindTry = kLDAPDefaultRebindTryTimeoutInSeconds;
fAvailable = false;
fSecureUse = false;
fSecurityLevel = kSecNoSecurity;
fConfigDeleted = false;
fConfigObject = NULL;
fServerAccount = NULL;
fServerKerberosID = NULL;
fServerPassword = NULL;
fMapSearchBase = NULL;
fServerName = NULL;
fConfigUIName = NULL;
fGetServerMappings = true;
fGetReplicas = true;
fGetSecuritySettings = true;
fDynamicRefreshTimer = dispatch_source_timer_create( DISPATCH_TIMER_INTERVAL,
(24ull * 3600ull * NSEC_PER_SEC),
60ull * NSEC_PER_SEC,
NULL,
dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT),
^(dispatch_source_t ds) {
if ( dispatch_source_get_error(ds, NULL) == 0 ) {
fGetReplicas = true;
fGetSecuritySettings = true;
if ( fServerMappings == true )
fGetServerMappings = true;
DbgLog( kLogPlugin, "CLDAPNodeConfig::RefreshDynamicData - Node: %s - all dynamically discovered data will be refreshed", fNodeName );
}
} );
fNormalizedMappings = NULL;
fRecordTypeMapArray = NULL;
fAttrTypeMapArray = NULL;
fReadReplicas = NULL;
fWriteReplicas = NULL;
fDeniedSASLMethods = NULL;
fObjectClassSchema = NULL;
fReachabilityRef = NULL;
fServerPort = LDAP_PORT;
fReferrals = false;
fEnableUse = true;
fDNSReplicas = false;
fServerMappings = false;
fLocalSecurityLevel = kSecNoSecurity;
fLastFailedCheck = 0.0;
}
void CLDAPNodeConfig::ClearSockList( int *inSockList, int inSockCount, bool inClose )
{
for (int iCount = 0; iCount < inSockCount; iCount++)
{
if ( inClose == true && inSockList[iCount] >= 0 )
close( inSockList[iCount] );
inSockList[iCount] = -1;
}
}
bool CLDAPNodeConfig::IsLocalAddress( struct addrinfo *addrInfo )
{
struct ifaddrs *ifa_list = NULL, *ifa = NULL;
bool bReturn = false;
if ( getifaddrs(&ifa_list) == 0 )
{
for( ifa = ifa_list; ifa; ifa = ifa->ifa_next )
{
for ( struct addrinfo *tempAddr = addrInfo; tempAddr != NULL; tempAddr = tempAddr->ai_next )
{
if ( ifa->ifa_addr->sa_family == tempAddr->ai_addr->sa_family )
{
if ( ifa->ifa_addr->sa_family == AF_INET )
{
struct sockaddr_in *interface = (struct sockaddr_in *) ifa->ifa_addr;
struct sockaddr_in *check = (struct sockaddr_in *) tempAddr->ai_addr;
if ( interface->sin_addr.s_addr == check->sin_addr.s_addr )
{
bReturn = true;
break;
}
}
if ( ifa->ifa_addr->sa_family == AF_INET6 )
{
struct sockaddr_in6 *interface = (struct sockaddr_in6 *)ifa->ifa_addr;
struct sockaddr_in6 *check = (struct sockaddr_in6 *)tempAddr->ai_addr;
if ( memcmp(&interface->sin6_addr, &check->sin6_addr, sizeof(struct in6_addr)) == 0 )
{
bReturn = true;
break;
}
}
}
}
}
freeifaddrs(ifa_list);
}
return bReturn;
}
LDAP *CLDAPNodeConfig::CheckWithSelect( fd_set &inSet, struct timeval *inCheckTime, int inCount, int *inSockList,
CLDAPReplicaInfo **inReplicas, CLDAPReplicaInfo **outSelectedReplica,
void *inCallback, void *inParam )
{
LDAP *pReturnLD = NULL;
fd_set fdwrite;
fd_set fdread;
FD_COPY( &inSet, &fdwrite );
FD_COPY( &inSet, &fdread );
if ( select( FD_SETSIZE, NULL, &fdwrite, NULL, inCheckTime ) > 0 )
{
struct timeval pollValue = { 0, 0 };
select( FD_SETSIZE, &fdread, NULL, NULL, &pollValue );
for ( int checkIter = 0; checkIter <= inCount; checkIter++ )
{
int aSockTemp = inSockList[checkIter];
if ( aSockTemp == -1 ) continue;
if ( FD_ISSET(aSockTemp, &fdwrite) != 0 && FD_ISSET(aSockTemp, &fdread) == 0 )
{
pReturnLD = inReplicas[checkIter]->VerifiedServerConnection( 5, true, inCallback, inParam );
if ( pReturnLD != NULL )
{
if ( (inReplicas[checkIter]->fSupportedSecurity & fSecurityLevel) == fSecurityLevel )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::CheckWithSelect - good socket to host %s from poll and verified LDAP",
inReplicas[checkIter]->fIPAddress );
(*outSelectedReplica) = inReplicas[checkIter]->Retain();
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::CheckWithSelect - replica %s doesn't meet security requirements",
inReplicas[checkIter]->fIPAddress );
ldap_unbind_ext_s( pReturnLD, NULL, NULL );
pReturnLD = NULL;
}
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::CheckWithSelect - good socket to host %s but failed check, clearing from poll",
inReplicas[checkIter]->fIPAddress );
}
FD_CLR( aSockTemp, &inSet );
break;
}
else if ( FD_ISSET(aSockTemp, &fdwrite) != 0 && FD_ISSET(aSockTemp, &fdread) != 0 )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::CheckWithSelect - Quick Check Bad socket to host %s clearing from poll",
inReplicas[checkIter]->fIPAddress );
inReplicas[checkIter]->ConnectionFailed();
FD_CLR( aSockTemp, &inSet );
break;
}
}
}
return pReturnLD;
}
LDAP *CLDAPNodeConfig::FindSuitableReplica( CLDAPReplicaInfo **inOutReplicaInfo, bool inForceCheck, bool inWriteable, void *inCallback, void *inParam )
{
LDAP *pReturnLD = NULL;
int maxSockets = 64; struct timeval recvTimeoutVal = { fOpenCloseTimeout, 0 };
int sockCount = 0;
int fcntlFlags = 0;
int sockList[64];
CLDAPReplicaInfo *replicaPointers[64];
int sockIter;
bool bTrySelect = false;
fd_set fdset;
if ( fAvailable == false && inForceCheck == false ) return NULL;
if ( ldap_is_ldapi_url(fNodeName) )
{
if ( (*inOutReplicaInfo) == NULL )
{
ListOfReplicasI replicaIter = fReplicaList.begin();
if ( replicaIter != fReplicaList.end() )
(*inOutReplicaInfo) = *replicaIter;
}
if ( (*inOutReplicaInfo) != NULL )
return (*inOutReplicaInfo)->VerifiedServerConnection( 10, inForceCheck, inCallback, inParam );
return NULL;
}
if ( maxSockets > 64 ) maxSockets = 64;
bzero( replicaPointers, sizeof(replicaPointers) );
ClearSockList( sockList, maxSockets, false );
FD_ZERO( &fdset );
fMutex.WaitLock();
if ( (*inOutReplicaInfo) == NULL && fServerName != NULL && fReplicaList.size() == 0 )
{
if ( CFArrayGetCount(fReadReplicas) == 0 )
{
CFStringRef cfServerName = CFStringCreateWithCString( kCFAllocatorDefault, fServerName, kCFStringEncodingUTF8 );
CFArrayAppendValue( fReadReplicas, cfServerName );
DSCFRelease( cfServerName );
}
BuildReplicaList();
}
for ( ListOfReplicasI replicaIter = fReplicaList.begin(); replicaIter != fReplicaList.end(); replicaIter++ )
{
CLDAPReplicaInfo *replica = (*replicaIter);
if ( inWriteable == true && replica->fbSupportsWrites == false ) continue;
if ( IsLocalAddress(replica->fAddrInfo) )
{
pReturnLD = replica->VerifiedServerConnection( 5, true, inCallback, inParam );
if ( pReturnLD != NULL )
{
if ( (replica->fSupportedSecurity & fSecurityLevel) == fSecurityLevel )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::FindSuitableReplica - Node %s - Using local address = %s for %s", fNodeName,
replica->fIPAddress, (inWriteable ? "write" : "read") );
(*inOutReplicaInfo) = replica->Retain();
break;
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::FindSuitableReplica - Node %s - Replica <%s> doesn't meet security requirements",
fNodeName, replica->fIPAddress );
ldap_unbind_ext_s( pReturnLD, NULL, NULL );
pReturnLD = NULL;
}
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::FindSuitableReplica - Node %s - Local address failed to connect = %s for %s",
fNodeName, replica->fIPAddress, (inWriteable ? "write" : "read") );
}
}
else if ( replica->fReachable && (inForceCheck == true || inWriteable == true || replica->ShouldAttemptCheck()) )
{
replicaPointers[sockCount] = replica->Retain();
sockCount++;
}
}
fMutex.SignalLock();
if ( pReturnLD == NULL && sockCount > 0 )
{
for ( sockIter = 0; sockIter < sockCount && pReturnLD == NULL; sockIter++ )
{
CLDAPReplicaInfo *replica = replicaPointers[sockIter];
struct addrinfo *tmpAddress = replica->fAddrInfo;
int aSock = socket( tmpAddress->ai_family, tmpAddress->ai_socktype, tmpAddress->ai_protocol );
if ( aSock != -1 )
{
int val = 1;
setsockopt( aSock, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val) );
setsockopt( aSock, SOL_SOCKET, SO_RCVTIMEO, &recvTimeoutVal, sizeof(recvTimeoutVal) );
fcntlFlags = fcntl( aSock, F_GETFL, 0 );
if ( fcntlFlags != -1 )
{
if ( fcntl(aSock, F_SETFL, fcntlFlags | O_NONBLOCK) != -1 )
{
if ( connect(aSock, tmpAddress->ai_addr, tmpAddress->ai_addrlen) == -1 )
{
sockList[sockIter] = aSock;
FD_SET( aSock, &fdset );
bTrySelect = true;
DbgLog( kLogPlugin, "CLDAPNodeConfig::FindSuitableReplica - Node %s - Attempting Replica connect to %s for %s",
fNodeName, replica->fIPAddress, (inWriteable ? "write" : "read") );
}
else
{
close( aSock );
pReturnLD = replica->VerifiedServerConnection( 5, true, inCallback, inParam );
if ( pReturnLD != NULL )
{
if ( (replica->fSupportedSecurity & fSecurityLevel) == fSecurityLevel )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::FindSuitableReplica - Node %s - Immediate response by %s for %s",
fNodeName, replica->fIPAddress, (inWriteable ? "write" : "read") );
(*inOutReplicaInfo) = replica->Retain();
break;
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::FindSuitableReplica - Node %s - Replica <%s> doesn't meet security requirements for %s",
fNodeName, replica->fIPAddress, (inWriteable ? "write" : "read") );
ldap_unbind_ext_s( pReturnLD, NULL, NULL );
pReturnLD = NULL;
}
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::FindSuitableReplica - Node %s - Immediate response by %s for %s - but failed check",
fNodeName, replica->fIPAddress, (inWriteable ? "write" : "read") );
}
}
}
else
{
close( aSock );
DbgLog( kLogPlugin, "CLDAPNodeConfig::FindSuitableReplica - Node %s - Unable to do non-blocking connect for socket = %d",
fNodeName, aSock );
}
}
else
{
close( aSock );
DbgLog( kLogPlugin, "CLDAPNodeConfig::FindSuitableReplica - Node %s - Unable to do get GETFL = %d", fNodeName, aSock );
}
}
if ( pReturnLD == NULL && bTrySelect == true )
{
struct timeval quickCheck = { 0, 5000 };
pReturnLD = CheckWithSelect( fdset, &quickCheck, sockCount, sockList, replicaPointers, inOutReplicaInfo, inCallback, inParam );
}
}
int iterTry = 0;
while ( pReturnLD == NULL && bTrySelect == true && iterTry++ < fOpenCloseTimeout )
{
struct timeval recheckTimeoutVal = { 1, 0 };
pReturnLD = CheckWithSelect( fdset, &recheckTimeoutVal, sockCount, sockList, replicaPointers, inOutReplicaInfo, inCallback, inParam );
}
}
for ( int ii = 0; ii < sockCount; ii++ )
DSRelease( replicaPointers[ii] );
ClearSockList( sockList, sockCount, true );
if ( pReturnLD == NULL )
{
if ( inWriteable == false )
OSAtomicCompareAndSwap32Barrier( true, false, &fAvailable );
for ( int ii = 0; ii < sockCount; ii++ )
{
if ( replicaPointers[ii] != NULL )
replicaPointers[ii]->ConnectionFailed();
}
DbgLog( kLogPlugin, "CLDAPNodeConfig::FindSuitableReplica - Node %s - Could not establish connection for %s", fNodeName,
(inWriteable ? "write" : "read") );
}
else
{
OSAtomicCompareAndSwap32Barrier( false, true, &fAvailable );
DbgLog( kLogPlugin, "CLDAPNodeConfig::FindSuitableReplica - Node %s - Established connection to %s for %s",
fNodeName, (*inOutReplicaInfo)->fIPAddress, (inWriteable ? "write" : "read") );
}
return pReturnLD;
}
LDAP *CLDAPNodeConfig::InternalEstablishConnection( CLDAPReplicaInfo **inOutReplicaInfo, bool inWriteable, void *inCallback, void *inParam )
{
LDAP *pReturnLD = NULL;
fMutex.WaitLock();
if ( (*inOutReplicaInfo) == NULL && fServerName != NULL && fReplicaList.size() == 0 )
{
if ( CFArrayGetCount(fReadReplicas) == 0 )
{
CFStringRef cfServerName = CFStringCreateWithCString( kCFAllocatorDefault, fServerName, kCFStringEncodingUTF8 );
CFArrayAppendValue( fReadReplicas, cfServerName );
DSCFRelease( cfServerName );
}
BuildReplicaList();
}
DbgLog( kLogPlugin, "CLDAPNodeConfig::InternalEstablishConnection - Node %s - Connection requested for %s", fNodeName,
(inWriteable ? "write" : "read") );
if ( (*inOutReplicaInfo) != NULL )
{
if ( inWriteable == false || (*inOutReplicaInfo)->fbSupportsWrites == true )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::InternalEstablishConnection - Node %s - attempting previous replica with IP Address = %s for %s",
fNodeName, (*inOutReplicaInfo)->fIPAddress, (inWriteable ? "write" : "read") );
pReturnLD = (*inOutReplicaInfo)->VerifiedServerConnection( 5, true, inCallback, inParam );
if ( pReturnLD != NULL && ((*inOutReplicaInfo)->fSupportedSecurity & fSecurityLevel) != fSecurityLevel )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::InternalEstablishConnection - Node %s - Previous replica <%s> doesn't meet security requirements for %s",
fNodeName, (*inOutReplicaInfo)->fIPAddress, (inWriteable ? "write" : "read") );
ldap_unbind_ext_s( pReturnLD, NULL, NULL );
pReturnLD = NULL;
}
}
if ( pReturnLD != NULL )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::InternalEstablishConnection - Node %s - Previous replica with IP Address = %s responded for %s",
fNodeName, (*inOutReplicaInfo)->fIPAddress, (inWriteable ? "write" : "read") );
}
else
{
DSRelease( *inOutReplicaInfo );
}
}
searchAgain:
if ( pReturnLD == NULL )
pReturnLD = FindSuitableReplica( inOutReplicaInfo, false, inWriteable, inCallback, inParam );
if ( pReturnLD != NULL && (fGetServerMappings == true || fGetSecuritySettings == true || fGetReplicas == true) )
{
bool bDoAgain = false;
SetLDAPOptions( pReturnLD );
char *pServerAccount = NULL;
char *pServerKerberosID = NULL;
char *pServerPassword = NULL;
bool bDynamicDataNeedsUpdate = false;
if ( CopyCredentials(&pServerAccount, &pServerKerberosID, &pServerPassword) == false )
{
bDynamicDataNeedsUpdate = true;
}
else
{
fMutex.SignalLock();
if ( AuthenticateUsingCredentials(pReturnLD, (*inOutReplicaInfo), pServerAccount, pServerKerberosID, pServerPassword) == eDSNoErr )
{
bDynamicDataNeedsUpdate = true;
}
fMutex.WaitLock();
}
if ( bDynamicDataNeedsUpdate )
{
bDoAgain = UpdateDynamicData( pReturnLD, (*inOutReplicaInfo) );
}
DSFree( pServerAccount );
DSFree( pServerKerberosID );
DSFreePassword( pServerPassword );
if ( bDoAgain )
{
DSRelease( *inOutReplicaInfo )
ldap_unbind_ext_s( pReturnLD, NULL, NULL );
pReturnLD = NULL;
DbgLog( kLogPlugin, "CLDAPNodeConfig::InternalEstablishConnection - Node %s - dynamic data was changed closing connecting and searching again", fNodeName );
goto searchAgain;
}
}
fMutex.SignalLock();
return pReturnLD;
}
tDirStatus CLDAPNodeConfig::AuthenticateUsingCredentials( LDAP *inLDAP, CLDAPReplicaInfo *inReplica, const char *inLDAPUsername,
const char *inKerberosID, const char *inPassword )
{
tDirStatus siResult = eDSAuthFailed;
if ( DSIsStringEmpty(inLDAPUsername) == true && DSIsStringEmpty(inPassword) == true )
return eDSNoErr;
const char *usePassword = DSIsStringEmpty(inPassword) ? kEmptyPasswordAltStr : inPassword;
CFMutableArrayRef cfSupportedMethods = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
char **dn = ldap_explode_dn( inLDAPUsername, 1 );
saslDefaults defaults = { 0 };
if ( dn ) {
defaults.authcid = dn[0];
defaults.authzid = (char *) calloc( sizeof(char), strlen(inLDAPUsername) + 3 + 1 );
strcpy( defaults.authzid, "dn:" );
strcat( defaults.authzid, inLDAPUsername );
} else {
defaults.authcid = inLDAPUsername;
defaults.authzid = (char *) calloc( sizeof(char), strlen(inLDAPUsername) + 2 + 1 );
strcpy( defaults.authzid, "u:" );
strcat( defaults.authzid, inLDAPUsername );
}
defaults.password = usePassword;
fMutex.WaitLock();
CFRange range = CFRangeMake( 0, CFArrayGetCount(fDeniedSASLMethods) );
if ( CFArrayContainsValue(fDeniedSASLMethods, range, CFSTR("GSSAPI")) == false &&
inReplica->SupportsSASLMethod(CFSTR("GSSAPI")) )
{
CFArrayAppendValue( cfSupportedMethods, CFSTR("GSSAPI") );
}
if ( (IsOnlyBitSet(fSecurityLevel, kSecDisallowCleartext) || (fSecurityLevel & kSecSecurityMask) == 0) )
{
if ( CFArrayContainsValue(fDeniedSASLMethods, range, CFSTR("DIGEST-MD5")) == false &&
inReplica->SupportsSASLMethod(CFSTR("DIGEST-MD5")) )
{
CFArrayAppendValue( cfSupportedMethods, CFSTR("DIGEST-MD5") );
}
if ( CFArrayContainsValue(fDeniedSASLMethods, range, CFSTR("CRAM-MD5")) == false &&
inReplica->SupportsSASLMethod(CFSTR("CRAM-MD5")) )
{
CFArrayAppendValue( cfSupportedMethods, CFSTR("CRAM-MD5") );
}
}
fMutex.SignalLock();
CFIndex iCount = CFArrayGetCount( cfSupportedMethods );
for ( CFIndex ii = 0; ii < iCount && siResult != eDSNoErr; ii++ )
{
CFStringRef cfMethod = (CFStringRef) CFArrayGetValueAtIndex( cfSupportedMethods, ii );
char *cStr = NULL;
const char *pMethod = BaseDirectoryPlugin::GetCStringFromCFString( cfMethod, &cStr );
DbgLog( kLogPlugin, "CLDAPNodeConfig::AuthenticateUsingCredentials - Attempting %s Authentication", pMethod );
if ( strcmp(pMethod, "GSSAPI") == 0 )
{
char *cacheName = NULL;
const char *username = defaults.authcid;
if ( DSIsStringEmpty(inKerberosID) == false )
{
char *pRealm = strchr( inKerberosID, '@' );
if ( pRealm != NULL && pRealm != inKerberosID && pRealm[1] != '\0' )
username = inKerberosID;
}
if ( username != NULL ) {
GetUserTGTIfNecessaryAndStore( username, usePassword, &cacheName );
}
if ( cacheName != NULL )
siResult = AuthenticateUsingKerberos( inLDAP, inReplica, cacheName );
else
DbgLog( kLogPlugin, "CLDAPNodeConfig::AuthenticateUsingCredentials - Couldn't get Kerberos credentials for GSSAPI <%s>", username );
DSFree( cacheName );
}
else
{
int ldapError = ldap_sasl_interactive_bind_s( inLDAP, NULL, pMethod, NULL, NULL, LDAP_SASL_QUIET, SASLInteract, &defaults );
if ( ldapError == LDAP_SUCCESS )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::AuthenticateUsingCredentials - Successful %s Authentication for %s", pMethod,
(defaults.authzid ? defaults.authzid : defaults.authcid) );
siResult = eDSNoErr;
}
else if ( ldapError == LDAP_AUTH_METHOD_NOT_SUPPORTED )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::AuthenticateUsingCredentials - Failed %s Authentication, not supported", pMethod );
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::AuthenticateUsingCredentials - Failed %s Authentication for %s error %d", pMethod,
(defaults.authzid ? defaults.authzid : defaults.authcid), ldapError );
}
}
DSFree( cStr );
}
if ( siResult == eDSAuthFailed &&
(fSecurityLevel & (kSecPacketSigning | kSecManInMiddle)) == 0 &&
( fIsSSL == true || (fSecurityLevel & kSecDisallowCleartext) == 0 ) )
{
struct berval cred = { 0, (char *)"" };
int bindMsgId = 0;
DbgLog( kLogPlugin, "CLDAPNodeConfig::AuthenticateUsingCredentials - Attempting an LDAP bind auth check" );
if ( inPassword != NULL )
{
cred.bv_val = (char *) usePassword;
cred.bv_len = strlen( usePassword );
}
int ldapError = ldap_sasl_bind( inLDAP, inLDAPUsername, LDAP_SASL_SIMPLE, &cred, NULL, NULL, &bindMsgId );
if ( ldapError == LDAP_SUCCESS )
{
struct timeval timeout = { fOpenCloseTimeout, 0 };
LDAPMessage *result = NULL;
ldapError = ldap_result( inLDAP, bindMsgId, 0, &timeout, &result );
switch ( ldapError )
{
case -1:
inReplica->ConnectionFailed();
case 0:
siResult = eDSAuthMasterUnreachable;
break;
default:
if ( ldap_result2error(inLDAP, result, 0) == LDAP_SUCCESS )
siResult = eDSNoErr;
break;
}
if ( result != NULL )
{
ldap_msgfree( result );
result = NULL;
}
}
DbgLog( kLogPlugin, "CLDAPNodeConfig::AuthenticateUsingCredentials - Cleartext authentication for %s %s", inLDAPUsername,
(siResult == eDSNoErr ? "succceeded" : "failed") );
}
if ( dn != NULL )
{
ldap_value_free( dn );
dn = NULL;
}
DSFree( defaults.authzid );
DSCFRelease( cfSupportedMethods );
return siResult;
}
tDirStatus CLDAPNodeConfig::AuthenticateUsingKerberos( LDAP *inLDAP, CLDAPReplicaInfo *inReplica, const char *inKerberosCache )
{
tDirStatus siResult = eDSAuthFailed;
saslDefaults gssapiDefaults = { 0 };
krb5_context krbContext = NULL;
krb5_ccache krbCache = NULL;
krb5_error_code retval = 0;
krb5_principal principal = NULL;
char *principalString = NULL;
OM_uint32 minor_status = 0;
if ( inLDAP == NULL || inReplica == NULL || inKerberosCache == NULL )
return eParameterError;
if ( inReplica->SupportsSASLMethod(CFSTR("GSSAPI")) == false )
return eDSAuthMethodNotSupported;
gKerberosMutex->WaitLock();
do
{
retval = krb5_init_context( &krbContext );
if ( retval ) break;
retval = krb5_cc_resolve( krbContext, inKerberosCache, &krbCache);
if ( retval ) break;
retval = krb5_cc_get_principal( krbContext, krbCache, &principal );
if ( retval ) break;
retval = krb5_unparse_name( krbContext, principal, &principalString );
if ( retval ) break;
gss_krb5_ccache_name( &minor_status, inKerberosCache, NULL );
int ldapError = ldap_sasl_interactive_bind_s( inLDAP, NULL, "GSSAPI", NULL, NULL, LDAP_SASL_QUIET, SASLInteract, &gssapiDefaults );
if ( ldapError == LDAP_SUCCESS )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::AuthenticateUsingKerberos - Successful GSSAPI Authentication for %s", principalString );
siResult = eDSNoErr;
}
else if ( ldapError == LDAP_AUTH_METHOD_NOT_SUPPORTED )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::AuthenticateUsingKerberos - Failed GSSAPI Authentication, not supported" );
siResult = eDSAuthMethodNotSupported;
}
else if ( ldapError == LDAP_OTHER || ldapError == LDAP_LOCAL_ERROR )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::AuthenticateUsingKerberos - Failed GSSAPI Authentication for %s", principalString );
}
} while ( 0 );
if ( principalString != NULL )
{
krb5_free_unparsed_name( krbContext, principalString );
principalString = NULL;
}
if ( principal != NULL )
{
krb5_free_principal( krbContext, principal );
principal = NULL;
}
if ( krbCache != NULL )
{
if ( siResult == eDSAuthFailed )
{
krb5_cc_destroy( krbContext, krbCache );
krbCache = NULL;
}
else
{
krb5_cc_close( krbContext, krbCache );
krbCache = NULL;
}
}
if ( krbContext != NULL )
{
krb5_free_context( krbContext );
krbContext = NULL;
}
gKerberosMutex->SignalLock();
return siResult;
}
#pragma mark -
#pragma mark Configuration Related
void CLDAPNodeConfig::DeleteConfiguration( void )
{
if ( OSAtomicCompareAndSwap32Barrier(false, true, &fConfigDeleted) == true )
{
char nodeName[256];
strlcpy( nodeName, "/LDAPv3/", sizeof(nodeName) );
strlcat( nodeName, fNodeName, sizeof(nodeName) );
}
}
bool CLDAPNodeConfig::UpdateConfiguraton( CFDictionaryRef inServerConfig, bool inFromServer )
{
CFStringRef cfStringRef = NULL;
SInt32 prevEnableUse = fEnableUse;
cfStringRef = (CFStringRef) CFDictionaryGetValue( inServerConfig, CFSTR(kXMLLDAPVersionKey) );
if ( cfStringRef != NULL && CFGetTypeID(cfStringRef) == CFStringGetTypeID() &&
CFStringCompare(cfStringRef, CFSTR(kDSLDAPPrefs_CurrentVersion), 0) != kCFCompareEqualTo )
{
char *cStr = NULL;
const char *theVersion = BaseDirectoryPlugin::GetCStringFromCFString( cfStringRef, &cStr );
DbgLog( kLogPlugin, "CLDAPNodeConfig::UpdateConfiguraton - server mappings version too old <%s> is not <%s>", theVersion,
kDSLDAPPrefs_CurrentVersion );
DSFree( cStr );
return false;
}
GetSInt32FromDictionary( inServerConfig, CFSTR(kXMLOpenCloseTimeoutSecsKey), &fOpenCloseTimeout, kLDAPDefaultOpenCloseTimeoutInSeconds );
int32_t idleMinutes = 0;
if ( GetSInt32FromDictionary(inServerConfig, CFSTR(kXMLIdleTimeoutMinsKey), &idleMinutes, 1) == true )
{
fIdleMaxCount = (idleMinutes * 2);
OSMemoryBarrier();
}
GetSInt32FromDictionary( inServerConfig, CFSTR(kXMLDelayedRebindTrySecsKey), &fDelayRebindTry, kLDAPDefaultRebindTryTimeoutInSeconds );
GetSInt32FromDictionary( inServerConfig, CFSTR(kXMLSearchTimeoutSecsKey), &fSearchTimeout, kLDAPDefaultSearchTimeoutInSeconds );
GetSInt32FromDictionary( inServerConfig, CFSTR(kXMLUseDNSReplicasFlagKey), &fDNSReplicas, false );
GetSInt32FromDictionary( inServerConfig, CFSTR(kXMLReferralFlagKey), &fReferrals, false );
GetSInt32FromDictionary( inServerConfig, CFSTR(kXMLEnableUseFlagKey), &fEnableUse, true );
fMappingsLock.WaitLock();
DSCFRelease( fAttrTypeMapArray );
CFArrayRef cfAttrArrayRef = (CFArrayRef) CFDictionaryGetValue( inServerConfig, CFSTR(kXMLAttrTypeMapArrayKey) );
if ( cfAttrArrayRef != NULL && CFGetTypeID(cfAttrArrayRef) == CFArrayGetTypeID() && CFArrayGetCount(cfAttrArrayRef) > 0 )
fAttrTypeMapArray = (CFArrayRef) CFRetain( cfAttrArrayRef );
DSCFRelease( fRecordTypeMapArray );
CFArrayRef cfRecordArrayRef = (CFArrayRef) CFDictionaryGetValue( inServerConfig, CFSTR(kXMLRecordTypeMapArrayKey) );
if ( cfRecordArrayRef != NULL && CFGetTypeID(cfRecordArrayRef) == CFArrayGetTypeID() && CFArrayGetCount(cfRecordArrayRef) > 0 )
fRecordTypeMapArray = (CFArrayRef) CFRetain( cfRecordArrayRef );
DSCFRelease( fNormalizedMappings );
fNormalizedMappings = CreateNormalizedRecordAttrMap( cfRecordArrayRef, cfAttrArrayRef );
fMappingsLock.SignalLock();
fMutex.WaitLock();
GetCStringFromDictionary( inServerConfig, CFSTR(kXMLMapSearchBase), &fMapSearchBase );
if ( inFromServer == false )
{
GetSInt32FromDictionary( inServerConfig, CFSTR(kXMLSecureUseFlagKey), &fSecureUse, false );
GetCStringFromDictionary( inServerConfig, CFSTR(kXMLServerAccountKey), &fServerAccount );
GetCStringFromDictionary( inServerConfig, CFSTR(kXMLServerPasswordKey), &fServerPassword );
GetCStringFromDictionary( inServerConfig, CFSTR(kXMLKerberosId), &fServerKerberosID );
GetCStringFromDictionary( inServerConfig, CFSTR(kXMLServerKey), &fServerName );
GetCStringFromDictionary( inServerConfig, CFSTR(kXMLUserDefinedNameKey), &fConfigUIName );
GetSInt32FromDictionary( inServerConfig, CFSTR(kXMLPortNumberKey), &fServerPort, LDAP_PORT );
GetSInt32FromDictionary( inServerConfig, CFSTR(kXMLIsSSLFlagKey), &fIsSSL, false );
if ( fReachabilityRef != NULL )
{
SCNetworkReachabilityUnscheduleFromRunLoop( fReachabilityRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode );
DSCFRelease( fReachabilityRef );
}
if ( fServerName != NULL )
{
char buffer[16];
uint32_t tempFamily = AF_UNSPEC;
if ( inet_pton(AF_INET6, fServerName, buffer) == 1 )
tempFamily = AF_INET6;
if ( tempFamily == AF_UNSPEC && inet_pton(AF_INET, fServerName, buffer) == 1 )
tempFamily = AF_INET;
if ( tempFamily == AF_UNSPEC )
{
fReachabilityRef = SCNetworkReachabilityCreateWithName( kCFAllocatorDefault, fServerName );
if ( fReachabilityRef != NULL )
{
SCNetworkReachabilityContext context = { 0, this, NULL, NULL, NULL };
dispatch_async( dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT),
^(void) {
SCNetworkConnectionFlags ::flags = 0;
if ( SCNetworkReachabilityGetFlags(fReachabilityRef, &flags) )
ReachabilityNotification( flags );
} );
SCNetworkReachabilitySetCallback( fReachabilityRef, ReachabilityCallback, &context );
SCNetworkReachabilityScheduleWithRunLoop( fReachabilityRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode );
}
}
else
{
OSAtomicCompareAndSwap32Barrier( false, true, &fAvailable );
}
}
if ( fServerAccount == NULL && fServerPassword == NULL )
OSAtomicCompareAndSwap32Barrier( true, false, &fSecureUse );
CFDictionaryRef cfLocalPolicy = (CFDictionaryRef) CFDictionaryGetValue( inServerConfig, CFSTR(kXMLLocalSecurityKey) );
if ( cfLocalPolicy != NULL && CFGetTypeID(cfLocalPolicy) == CFDictionaryGetTypeID() )
{
fLocalSecurityLevel = CalculateSecurityPolicy( cfLocalPolicy );
fSecurityLevel |= fLocalSecurityLevel; OSMemoryBarrier();
}
GetSInt32FromDictionary( inServerConfig, CFSTR(kXMLServerMappingsFlagKey), &fServerMappings, false );
if ( fServerMappings == false )
OSAtomicCompareAndSwap32Barrier( true, false, &fGetServerMappings );
DSCFRelease( fReadReplicas );
DSCFRelease( fWriteReplicas );
DSCFRelease( fDeniedSASLMethods );
fReadReplicas = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
fWriteReplicas = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
fDeniedSASLMethods = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
CFStringRef cfServerName = CFStringCreateWithCString( kCFAllocatorDefault, fServerName, kCFStringEncodingUTF8 );
CFArrayAppendValue( fReadReplicas, cfServerName ); DSCFRelease( cfServerName );
CFArrayRef replicas = (CFArrayRef) CFDictionaryGetValue( inServerConfig, CFSTR(kXMLReplicaHostnameListArrayKey) );
if ( replicas != NULL )
CFArrayAppendArray( fReadReplicas, replicas, CFRangeMake(0, CFArrayGetCount(replicas)) );
replicas = (CFArrayRef) CFDictionaryGetValue( inServerConfig, CFSTR(kXMLWriteableHostnameListArrayKey) );
if ( replicas != NULL )
CFArrayAppendArray( fWriteReplicas, replicas, CFRangeMake(0, CFArrayGetCount(replicas)) );
else
CFArrayAppendArray( fWriteReplicas, fReadReplicas, CFRangeMake(0, CFArrayGetCount(fReadReplicas)) );
CFArrayRef denied = (CFArrayRef) CFDictionaryGetValue( inServerConfig, CFSTR(kXMLDeniedSASLMethods) );
if ( denied != NULL && CFArrayGetTypeID() == CFGetTypeID(denied) )
CFArrayAppendArray( fDeniedSASLMethods, denied, CFRangeMake(0, CFArrayGetCount(denied)) );
}
fMutex.SignalLock();
if ( gCacheNode != NULL && prevEnableUse != fEnableUse )
gCacheNode->EmptyCacheEntryType( CACHE_ENTRY_TYPE_ALL );
return true;
}
CFDictionaryRef CLDAPNodeConfig::GetConfiguration( void )
{
CFMutableDictionaryRef curConfigDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
CFStringRef cfStringRef;
CFNumberRef cfNumberRef;
fMutex.WaitLock();
cfNumberRef = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type, &fServerPort );
if ( cfNumberRef != NULL )
{
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLPortNumberKey), cfNumberRef );
CFRelease( cfNumberRef );
}
if ( fConfigUIName != NULL )
{
cfStringRef = CFStringCreateWithCString( kCFAllocatorDefault, fConfigUIName, kCFStringEncodingUTF8 );
if ( cfStringRef != NULL )
{
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLUserDefinedNameKey), cfStringRef );
CFRelease( cfStringRef );
}
}
if ( fServerName != NULL )
{
cfStringRef = CFStringCreateWithCString( kCFAllocatorDefault, fServerName, kCFStringEncodingUTF8 );
if ( cfStringRef != NULL )
{
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLServerKey), cfStringRef );
CFRelease( cfStringRef );
}
}
cfNumberRef = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type, &fOpenCloseTimeout );
if ( cfNumberRef != NULL )
{
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLOpenCloseTimeoutSecsKey), cfNumberRef );
CFRelease( cfNumberRef );
}
cfNumberRef = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type, &fSearchTimeout );
if ( cfNumberRef != NULL )
{
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLSearchTimeoutSecsKey), cfNumberRef );
CFRelease( cfNumberRef );
}
if ( fReadReplicas != NULL)
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLReplicaHostnameListArrayKey), fReadReplicas );
if ( fWriteReplicas != NULL)
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLWriteableHostnameListArrayKey), fWriteReplicas );
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLIsSSLFlagKey), fIsSSL ? kCFBooleanTrue : kCFBooleanFalse );
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLMakeDefLDAPFlagKey), fDHCPLDAPServer ? kCFBooleanTrue : kCFBooleanFalse );
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLEnableUseFlagKey), fEnableUse ? kCFBooleanTrue : kCFBooleanFalse );
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLServerMappingsFlagKey), fServerMappings ? kCFBooleanTrue : kCFBooleanFalse );
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLReferralFlagKey), fReferrals ? kCFBooleanTrue : kCFBooleanFalse );
if ( fDeniedSASLMethods != NULL )
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLDeniedSASLMethods), fDeniedSASLMethods );
fMutex.SignalLock();
fMappingsLock.WaitLock();
if ( fRecordTypeMapArray != NULL )
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLRecordTypeMapArrayKey), fRecordTypeMapArray );
if ( fAttrTypeMapArray != NULL )
CFDictionaryAddValue( curConfigDict, CFSTR(kXMLAttrTypeMapArrayKey), fAttrTypeMapArray );
fMappingsLock.SignalLock();
return curConfigDict;
}
bool CLDAPNodeConfig::RetrieveServerMappings( LDAP *inLDAP, CLDAPReplicaInfo *inReplica )
{
CFArrayRef cfSearchBases = NULL;
bool bReturn = false;
if ( DSIsStringEmpty(fMapSearchBase) )
{
cfSearchBases = inReplica->CopyNamingContexts();
if ( cfSearchBases == NULL )
{
inReplica->ConnectionFailed();
return false;
}
}
else
{
CFStringRef cfMapSearchBase = CFStringCreateWithCString( kCFAllocatorDefault, fMapSearchBase, kCFStringEncodingUTF8 );
if ( cfMapSearchBase == NULL )
return false;
cfSearchBases = CFArrayCreate( kCFAllocatorDefault, (CFTypeRef *)&cfMapSearchBase, 1, &kCFTypeArrayCallBacks );
DSCFRelease( cfMapSearchBase );
}
CFIndex iCount = CFArrayGetCount( cfSearchBases );
char *cStr = NULL;
CFDataRef ourMappings = NULL;
for ( CFIndex ii = 0; ii < iCount && ourMappings == NULL; ii++ )
{
CFStringRef cfSearchBase = (CFStringRef) CFArrayGetValueAtIndex( cfSearchBases, ii );
const char *searchBase = BaseDirectoryPlugin::GetCStringFromCFString( cfSearchBase, &cStr );
struct timeval timeout = { kLDAPDefaultOpenCloseTimeoutInSeconds, 0 }; const char *attrs[] = { "description", NULL };
LDAPMessage *result = NULL;
int returnCode = ldap_search_ext_s( inLDAP, searchBase, LDAP_SCOPE_SUBTREE, "(&(objectclass=organizationalUnit)(ou=macosxodconfig))",
(char **)attrs, FALSE, NULL, NULL, &timeout, 0, &result );
if ( returnCode == LDAP_SUCCESS )
{
struct berval **bValues = ldap_get_values_len( inLDAP, result, "description" );
if ( bValues != NULL )
{
if ( bValues[0] != NULL )
{
ourMappings = CFDataCreate( kCFAllocatorDefault, (UInt8 *)(bValues[0]->bv_val), bValues[0]->bv_len );
DbgLog( kLogPlugin, "CLDAPNodeConfig::RetrieveServerMappings - Node %s - retrieved server mappings from %s - searchbase <%s>.",
fNodeName, inReplica->fIPAddress, searchBase );
}
ldap_value_free_len( bValues );
}
}
else if ( returnCode == LDAP_TIMEOUT || returnCode == LDAP_TIMELIMIT_EXCEEDED )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::RetrieveServerMappings - Node %s - timed out retrieving server mappings from %s.",
fNodeName, inReplica->fIPAddress );
}
if ( result != NULL )
{
ldap_msgfree( result );
result = NULL;
}
DSFree( cStr );
}
if ( ourMappings != NULL )
{
CFDictionaryRef serverConfigDict = (CFDictionaryRef) CFPropertyListCreateFromXMLData( kCFAllocatorDefault,
ourMappings,
kCFPropertyListImmutable,
NULL );
if ( serverConfigDict != NULL &&
CFGetTypeID(serverConfigDict) == CFDictionaryGetTypeID() )
{
UpdateConfiguraton( serverConfigDict, true );
if ( fConfigObject != NULL )
{
fMappingsLock.WaitLock();
fConfigObject->UpdateServerMappingsForUUID( fNodeName, fConfigUUID, fAttrTypeMapArray, fRecordTypeMapArray );
fMappingsLock.SignalLock();
}
bReturn = true;
}
DSCFRelease( serverConfigDict );
}
DSCFRelease( ourMappings );
DSFree( cStr );
DSCFRelease( cfSearchBases );
OSAtomicCompareAndSwap32Barrier( true, false, &fGetServerMappings );
return bReturn;
}
bool CLDAPNodeConfig::RetrieveServerReplicaList( LDAP *inLDAP, CFMutableArrayRef outRepList, CFMutableArrayRef outWriteableList )
{
struct berval **bValues = NULL;
char *nativeRecType = NULL;
bool bOCANDGroup = false;
CFArrayRef OCSearchList = NULL;
ber_int_t scope = LDAP_SCOPE_BASE;
char *queryFilter = NULL;
char *repListAttr = NULL;
char *writeListAttr = NULL;
nativeRecType = MapRecToSearchBase( kDSStdRecordTypeConfig, 1, &bOCANDGroup, &OCSearchList, &scope );
if ( nativeRecType == NULL )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::RetrieveServerReplicaList - No Config Record mapping to retrieve replica list." );
OSAtomicCompareAndSwap32Barrier( true, false, &fGetReplicas );
return false;
}
queryFilter = BuildLDAPQueryFilter( (char *)kDSNAttrRecordName, (char *)"ldapreplicas", eDSExact, false, (char *)kDSStdRecordTypeConfig, nativeRecType,
bOCANDGroup, OCSearchList );
if ( queryFilter != NULL )
{
LDAPMessage *result = NULL;
struct timeval timeout = { fOpenCloseTimeout, 0 };
int ldapReturnCode = ldap_search_ext_s( inLDAP, nativeRecType, scope, queryFilter, NULL, 0, NULL, NULL, &timeout, 0, &result );
if ( ldapReturnCode == LDAP_SUCCESS )
{
repListAttr = MapAttrToLDAPType( kDSStdRecordTypeConfig, kDSNAttrLDAPReadReplicas, 1 );
if ( repListAttr != NULL && (bValues = ldap_get_values_len(inLDAP, result, repListAttr)) != NULL )
{
for ( int ii = 0; bValues[ii] != NULL; ii++ )
{
LDAPURLDesc *ludpp = NULL;
if ( ldap_url_parse(bValues[ii]->bv_val, &ludpp) == LDAP_SUCCESS )
{
CFStringRef cfServer = CFStringCreateWithCString( kCFAllocatorDefault, ludpp->lud_host, kCFStringEncodingUTF8 );
if ( cfServer != NULL )
{
if ( CFArrayContainsValue(outRepList, CFRangeMake(0,CFArrayGetCount(outRepList)), cfServer ) == false)
CFArrayAppendValue( outRepList, cfServer );
DSCFRelease( cfServer );
}
ldap_free_urldesc( ludpp );
ludpp = NULL;
}
}
ldap_value_free_len(bValues);
}
writeListAttr = MapAttrToLDAPType( kDSStdRecordTypeConfig, kDSNAttrLDAPWriteReplicas, 1 );
if ( writeListAttr != NULL && (bValues = ldap_get_values_len(inLDAP, result, writeListAttr)) != NULL )
{
for ( int ii = 0; bValues[ii] != NULL; ii++ )
{
LDAPURLDesc *ludpp = NULL;
if ( ldap_url_parse(bValues[ii]->bv_val, &ludpp) == LDAP_SUCCESS )
{
CFStringRef cfServer = CFStringCreateWithCString( kCFAllocatorDefault, ludpp->lud_host, kCFStringEncodingUTF8 );
if ( cfServer != NULL )
{
if ( CFArrayContainsValue(outWriteableList, CFRangeMake(0,CFArrayGetCount(outWriteableList)), cfServer ) == false)
CFArrayAppendValue( outWriteableList, cfServer );
DSCFRelease( cfServer );
}
ldap_free_urldesc( ludpp );
ludpp = NULL;
}
}
ldap_value_free_len(bValues);
}
}
if ( result != NULL )
{
ldap_msgfree( result );
result = NULL;
}
}
DSCFRelease( OCSearchList );
DSFree( nativeRecType );
DSFree( queryFilter );
DSFree( repListAttr );
DSFree( writeListAttr );
if ( CFArrayGetCount(outRepList) > 0 )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::RetrieveServerReplicaList - Node %s - Found Config Record \"ldapreplicas\" that had Replica information.",
fNodeName );
if ( fConfigObject != NULL )
fConfigObject->UpdateReplicaListForUUID( fNodeName, fConfigUUID, outRepList, outWriteableList );
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::RetrieveServerReplicaList - Node %s - No Config Record \"ldapreplicas\" with Replica information.",
fNodeName );
}
OSAtomicCompareAndSwap32Barrier( true, false, &fGetReplicas );
return (CFArrayGetCount(outRepList) > 0);
}
bool CLDAPNodeConfig::RetrieveServerSecuritySettings( LDAP *inLDAP, CLDAPReplicaInfo *inReplica )
{
LDAPMessage *pLDAPResult = NULL;
timeval stTimeout = { fOpenCloseTimeout, 0 };
bool bChangedPolicy = false;
CFDictionaryRef cfConfiguredSecPolicy = NULL;
char *pAttribute = ExtractAttrMap( kDSStdRecordTypeConfig, kDS1AttrXMLPlist, 1 );
if ( pAttribute != NULL )
{
int ldapReturnCode = ldap_search_ext_s( inLDAP, fMapSearchBase, LDAP_SCOPE_SUBTREE, "(cn=macosxodpolicy)", NULL, false,
NULL, NULL, &stTimeout, 0, &pLDAPResult );
if ( ldapReturnCode == LDAP_SUCCESS )
{
berval **pBValues = NULL;
if ( (pBValues = ldap_get_values_len(inLDAP, pLDAPResult, pAttribute)) != NULL )
{
CFDataRef cfXMLData = CFDataCreate( kCFAllocatorDefault, (UInt8 *)(pBValues[0]->bv_val), pBValues[0]->bv_len );
if ( cfXMLData != NULL )
{
CFMutableDictionaryRef cfTempDict;
cfTempDict = (CFMutableDictionaryRef) CFPropertyListCreateFromXMLData( kCFAllocatorDefault, cfXMLData,
kCFPropertyListMutableContainersAndLeaves, NULL );
if ( cfTempDict )
{
CFDictionaryRef cfConfigPolicy = (CFDictionaryRef) CFDictionaryGetValue( cfTempDict, CFSTR(kXMLConfiguredSecurityKey) );
int32_t iSecurityLevel = fLocalSecurityLevel | CalculateSecurityPolicy( cfConfigPolicy );
if ( fSecurityLevel != iSecurityLevel )
{
fSecurityLevel = iSecurityLevel;
OSMemoryBarrier();
bChangedPolicy = true;
}
cfConfiguredSecPolicy = CFDictionaryCreateCopy( kCFAllocatorDefault, cfConfigPolicy );
DSCFRelease( cfTempDict );
}
DSCFRelease( cfXMLData ); }
ldap_value_free_len( pBValues );
pBValues = NULL;
}
}
if ( pLDAPResult )
{
ldap_msgfree( pLDAPResult );
pLDAPResult = NULL;
}
DSFree( pAttribute );
}
CFArrayRef cfSASLMethods = inReplica->CopySASLMethods();
if ( cfSASLMethods != NULL && CFArrayGetCount(cfSASLMethods) > 0 )
{
CFMutableDictionaryRef cfSupportedSecLevel = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
if ( fIsSSL )
{
CFDictionarySetValue( cfSupportedSecLevel, CFSTR(kXMLSecurityNoClearTextAuths), kCFBooleanTrue );
CFDictionarySetValue( cfSupportedSecLevel, CFSTR(kXMLSecurityPacketEncryption), kCFBooleanTrue );
}
CFRange stRange = CFRangeMake( 0, CFArrayGetCount(cfSASLMethods) );
if ( CFArrayContainsValue( cfSASLMethods, stRange, CFSTR("CRAM-MD5")) )
CFDictionarySetValue( cfSupportedSecLevel, CFSTR(kXMLSecurityNoClearTextAuths), kCFBooleanTrue );
if ( CFArrayContainsValue( cfSASLMethods, stRange, CFSTR("GSSAPI")) )
{
CFDictionarySetValue( cfSupportedSecLevel, CFSTR(kXMLSecurityNoClearTextAuths), kCFBooleanTrue );
CFDictionarySetValue( cfSupportedSecLevel, CFSTR(kXMLSecurityManInTheMiddle), kCFBooleanTrue );
CFDictionarySetValue( cfSupportedSecLevel, CFSTR(kXMLSecurityPacketSigning), kCFBooleanTrue );
CFDictionarySetValue( cfSupportedSecLevel, CFSTR(kXMLSecurityPacketEncryption), kCFBooleanTrue );
}
if ( fConfigObject != NULL )
fConfigObject->UpdateSecurityPolicyForUUID( fNodeName, fConfigUUID, cfConfiguredSecPolicy, cfSupportedSecLevel );
DSCFRelease( cfSupportedSecLevel );
DSCFRelease( cfConfiguredSecPolicy );
}
DSCFRelease( cfSASLMethods );
OSAtomicCompareAndSwap32Barrier( true, false, &fGetSecuritySettings );
return bChangedPolicy;
}
void CLDAPNodeConfig::RetrieveServerSchema( LDAP *inLDAP )
{
sObjectClassSchema *aOCSchema = NULL;
bool bSkipToTag = true;
char *lineEntry = NULL;
char *strtokContext = NULL;
LDAPMessage *result = NULL;
int ldapReturnCode = 0;
const char *sattrs[] = { "subschemasubentry", NULL };
const char *attrs[] = { "objectclasses", NULL };
char *subschemaDN = NULL;
struct berval **bValues = NULL;
struct timeval timeout = { fSearchTimeout, 0 };
fMutex.WaitLock();
if ( fObjectClassSchema != NULL ) {
fMutex.SignalLock();
return;
}
ldapReturnCode = ldap_search_ext_s( inLDAP, "", LDAP_SCOPE_BASE, "(objectclass=*)", (char**)sattrs, 0, NULL, NULL, &timeout, 0, &result );
if ( ldapReturnCode == LDAP_SUCCESS )
{
bValues = ldap_get_values_len( inLDAP, result, "subschemasubentry" );
if ( bValues != NULL )
{
if ( bValues[0] != NULL )
subschemaDN = strdup( bValues[0]->bv_val );
ldap_value_free_len( bValues );
}
}
if ( result != NULL )
{
ldap_msgfree( result );
result = NULL;
}
if ( subschemaDN != NULL )
{
ldapReturnCode = ldap_search_ext_s( inLDAP, subschemaDN, LDAP_SCOPE_BASE, "(objectclass=subSchema)", (char**)attrs, 0, NULL, NULL,
&timeout, 0, &result );
if ( ldapReturnCode == LDAP_SUCCESS )
{
bValues = ldap_get_values_len( inLDAP, result, "objectclasses" );
if ( bValues != NULL )
{
ObjectClassMap *aOCClassMap = new ObjectClassMap;
for ( int i = 0; bValues[i] != NULL; i++ )
{
aOCSchema = NULL;
DSFree( lineEntry );
lineEntry = strdup( bValues[i]->bv_val );
char * aToken = strtok_r( lineEntry, OCSEPCHARS, &strtokContext );
while ( aToken != NULL && strcmp(aToken,"NAME") != 0 )
{
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
}
if ( aToken != NULL )
{
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if (aToken != NULL)
{
if ( aOCClassMap->find( aToken ) == aOCClassMap->end() )
{
aOCSchema = new sObjectClassSchema;
(*aOCClassMap)[aToken] = aOCSchema;
}
}
}
if (aOCSchema == NULL || aToken == NULL)
continue;
bSkipToTag = true;
while ( bSkipToTag )
{
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext);
if ( aToken == NULL )
break;
bSkipToTag = IsTokenNotATag( aToken );
if ( bSkipToTag )
aOCSchema->fOtherNames.insert( aOCSchema->fOtherNames.begin(), aToken );
}
if ( aToken == NULL )
continue;
if ( strcmp(aToken, "DESC") == 0 )
{
bSkipToTag = true;
while ( bSkipToTag )
{
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if ( aToken == NULL )
break;
bSkipToTag = IsTokenNotATag( aToken );
}
if ( aToken == NULL )
continue;
}
if (strcmp(aToken, "OBSOLETE") == 0)
{
bSkipToTag = true;
while ( bSkipToTag )
{
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if ( aToken == NULL )
break;
bSkipToTag = IsTokenNotATag( aToken );
}
if ( aToken == NULL )
continue;
}
if (strcmp(aToken, "SUP") == 0)
{
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if ( aToken == NULL )
continue;
aOCSchema->fParentOCs.insert( aOCSchema->fParentOCs.begin(), aToken );
bSkipToTag = true;
while ( bSkipToTag )
{
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if ( aToken == NULL )
break;
bSkipToTag = IsTokenNotATag( aToken );
if ( bSkipToTag )
aOCSchema->fParentOCs.insert(aOCSchema->fParentOCs.begin(),aToken);
}
if ( aToken == NULL )
continue;
}
if ( strcmp(aToken, "ABSTRACT") == 0 )
{
aOCSchema->fType = 0;
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if ( aToken == NULL )
continue;
}
if ( strcmp(aToken, "STRUCTURAL") == 0 )
{
aOCSchema->fType = 1;
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if ( aToken == NULL )
continue;
}
if ( strcmp(aToken, "AUXILIARY") == 0 )
{
aOCSchema->fType = 2;
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if ( aToken == NULL )
continue;
}
if ( strcmp(aToken, "MUST") == 0 )
{
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if ( aToken == NULL )
continue;
aOCSchema->fRequiredAttrs.insert( aOCSchema->fRequiredAttrs.begin(), aToken );
bSkipToTag = true;
while ( bSkipToTag )
{
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if ( aToken == NULL )
break;
bSkipToTag = IsTokenNotATag( aToken );
if ( bSkipToTag )
aOCSchema->fRequiredAttrs.insert( aOCSchema->fRequiredAttrs.begin(), aToken );
}
if ( aToken == NULL )
continue;
}
if ( strcmp(aToken, "MAY") == 0 )
{
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if ( aToken == NULL )
continue;
aOCSchema->fAllowedAttrs.insert( aOCSchema->fAllowedAttrs.begin(), aToken );
bSkipToTag = true;
while ( bSkipToTag )
{
aToken = strtok_r( NULL, OCSEPCHARS, &strtokContext );
if ( aToken == NULL )
break;
bSkipToTag = IsTokenNotATag( aToken );
if ( bSkipToTag )
aOCSchema->fAllowedAttrs.insert( aOCSchema->fAllowedAttrs.begin(), aToken );
}
if ( aToken == NULL )
continue;
}
}
fObjectClassSchema = aOCClassMap;
ldap_value_free_len( bValues );
bValues = NULL;
DSFree( lineEntry );
}
}
else
{
DbgLog( kLogError, "Failed to retrieve LDAP server schema - LDAP error %d", ldapReturnCode );
}
if ( result != NULL )
{
ldap_msgfree( result );
result = NULL;
}
}
fMutex.SignalLock();
DSFree( subschemaDN );
}
#pragma mark -
#pragma mark Support Routines
bool CLDAPNodeConfig::IsTokenNotATag( char *inToken )
{
if ( inToken == NULL )
return true;
switch( *inToken )
{
case 'N':
case 'D':
case 'O':
case 'S':
case 'A':
case 'M':
case 'X':
if (strcmp(inToken,"DESC") == 0)
return false;
if (strcmp(inToken,"SUP") == 0)
return false;
if (strlen(inToken) > 7)
{
if (strcmp(inToken,"OBSOLETE") == 0)
return false;
if (strcmp(inToken,"ABSTRACT") == 0)
return false;
if (strcmp(inToken,"STRUCTURAL") == 0)
return false;
if (strcmp(inToken,"AUXILIARY") == 0)
return false;
if (strcmp(inToken,"X-ORIGIN") == 0) return false;
}
if (strcmp(inToken,"MUST") == 0)
return false;
if (strcmp(inToken,"MAY") == 0)
return false;
if (strcmp(inToken,"NAME") == 0)
return false;
break;
default:
break;
}
return true;
}
bool CLDAPNodeConfig::GetSInt32FromDictionary( CFDictionaryRef inDictionary, CFStringRef inKey, int32_t *outValue, int32_t defaultValue )
{
CFTypeRef cfValue = CFDictionaryGetValue( inDictionary, inKey );
if ( cfValue != NULL )
{
if ( CFGetTypeID(cfValue) == CFBooleanGetTypeID() )
{
(*outValue) = CFBooleanGetValue( (CFBooleanRef) cfValue );
OSMemoryBarrier();
return true;
}
else if ( CFGetTypeID(cfValue) == CFNumberGetTypeID() &&
CFNumberGetValue((CFNumberRef) cfValue, kCFNumberSInt32Type, outValue) == true )
{
OSMemoryBarrier();
return true;
}
}
(*outValue) = defaultValue;
OSMemoryBarrier();
return false;
}
bool CLDAPNodeConfig::GetCStringFromDictionary( CFDictionaryRef inDictionary, CFStringRef inKey, char **outValue )
{
bool bReturn = false;
CFStringRef cfValue = (CFStringRef) CFDictionaryGetValue( inDictionary, inKey );
if ( cfValue != NULL )
{
if ( CFGetTypeID(cfValue) == CFStringGetTypeID() )
{
char *cStr = NULL;
const char *tempValue = BaseDirectoryPlugin::GetCStringFromCFString( cfValue, &cStr );
if ( tempValue != NULL )
{
DSFree( (*outValue) );
(*outValue) = strdup( tempValue );
bReturn = true;
}
DSFree( cStr );
}
else if ( CFGetTypeID(cfValue) == CFDataGetTypeID() )
{
DSFree( (*outValue) );
CFIndex tempLen = CFDataGetLength( (CFDataRef) cfValue );
(*outValue) = (char *) calloc( tempLen + 1, sizeof(char) );
CFDataGetBytes( (CFDataRef) cfValue, CFRangeMake(0, tempLen), (UInt8*) (*outValue) );
}
}
return bReturn;
}
CFDictionaryRef CLDAPNodeConfig::CreateNormalizedAttributeMap( CFArrayRef inAttrMapArray, CFDictionaryRef inGlobalAttrMap )
{
CFMutableDictionaryRef newAttrMapDict = NULL;
CFIndex iTotal = 0;
CFIndex iGlobalMapTotal = 0;
if ( inGlobalAttrMap != NULL && (iGlobalMapTotal = CFDictionaryGetCount(inGlobalAttrMap)) > 0 )
{
newAttrMapDict = CFDictionaryCreateMutableCopy( kCFAllocatorDefault, 0, inGlobalAttrMap );
}
if ( inAttrMapArray != NULL && CFGetTypeID(inAttrMapArray) == CFArrayGetTypeID() && (iTotal = CFArrayGetCount(inAttrMapArray)) > 0 )
{
if ( newAttrMapDict == NULL )
{
newAttrMapDict = CFDictionaryCreateMutable( kCFAllocatorDefault, iTotal, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
}
for ( CFIndex iMapIndex = 0; iMapIndex < iTotal; iMapIndex++ )
{
CFDictionaryRef attrMapDict = (CFDictionaryRef) CFArrayGetValueAtIndex( inAttrMapArray, iMapIndex );
if ( attrMapDict != NULL && CFGetTypeID(attrMapDict) == CFDictionaryGetTypeID() )
{
CFStringRef cfStdName = (CFStringRef) CFDictionaryGetValue( attrMapDict, CFSTR( kXMLStdNameKey ) );
if ( cfStdName == NULL || CFGetTypeID( cfStdName ) != CFStringGetTypeID() )
continue;
CFArrayRef cfNativeArray = (CFArrayRef) CFDictionaryGetValue( attrMapDict, CFSTR( kXMLNativeMapArrayKey ) );
if ( cfNativeArray == NULL || CFGetTypeID(cfNativeArray) != CFArrayGetTypeID() )
continue;
CFIndex iNativeMapCount = CFArrayGetCount( cfNativeArray );
if ( iNativeMapCount == 0 )
continue;
CFMutableArrayRef cfNewNativeMap = CFArrayCreateMutable( kCFAllocatorDefault, iNativeMapCount, &kCFTypeArrayCallBacks );
for( CFIndex iNativeIndex = 0; iNativeIndex < iNativeMapCount; iNativeIndex++ )
{
CFStringRef cfStringRef = (CFStringRef) CFArrayGetValueAtIndex( cfNativeArray, iNativeIndex );
if ( cfStringRef != NULL && CFGetTypeID(cfStringRef) == CFStringGetTypeID() )
{
CFArrayAppendValue( cfNewNativeMap, cfStringRef );
}
}
if ( CFArrayGetCount(cfNewNativeMap) != 0 )
{
CFDictionarySetValue( newAttrMapDict, cfStdName, cfNewNativeMap );
}
DSCFRelease( cfNewNativeMap );
}
}
}
if ( newAttrMapDict != NULL && CFDictionaryGetCount(newAttrMapDict) == 0 )
DSCFRelease( newAttrMapDict );
return newAttrMapDict;
}
CFDictionaryRef CLDAPNodeConfig::CreateNormalizedRecordAttrMap( CFArrayRef inRecMapArray, CFArrayRef inGlobalAttrMapArray )
{
CFMutableDictionaryRef outRecMapDict = NULL;
CFIndex iTotal = 0;
if ( inRecMapArray != NULL && CFGetTypeID(inRecMapArray) == CFArrayGetTypeID() && (iTotal = CFArrayGetCount(inRecMapArray)) > 0 )
{
outRecMapDict = CFDictionaryCreateMutable( kCFAllocatorDefault, iTotal, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
CFDictionaryRef cfGlobalAttrMap = CreateNormalizedAttributeMap( inGlobalAttrMapArray, NULL );
for( CFIndex iMapIndex = 0; iMapIndex < iTotal; iMapIndex++ )
{
CFDictionaryRef recMapDict = (CFDictionaryRef) CFArrayGetValueAtIndex( inRecMapArray, iMapIndex );
if ( recMapDict != NULL )
{
CFStringRef cfStdName = (CFStringRef) CFDictionaryGetValue( recMapDict, CFSTR( kXMLStdNameKey ) );
if ( cfStdName == NULL || CFGetTypeID( cfStdName ) != CFStringGetTypeID() )
continue;
CFArrayRef cfNativeArray = (CFArrayRef) CFDictionaryGetValue( recMapDict, CFSTR( kXMLNativeMapArrayKey ) );
if ( cfNativeArray == NULL || CFGetTypeID(cfNativeArray) != CFArrayGetTypeID() )
continue;
CFIndex iNativeMapCount = CFArrayGetCount( cfNativeArray );
if ( iNativeMapCount == 0 )
continue;
CFMutableArrayRef cfNewNativeArray = CFArrayCreateMutable( kCFAllocatorDefault, iNativeMapCount, &kCFTypeArrayCallBacks );
for( CFIndex iNativeIndex = 0; iNativeIndex < iNativeMapCount; iNativeIndex++ )
{
CFMutableDictionaryRef cfDictRef = (CFMutableDictionaryRef) CFArrayGetValueAtIndex( cfNativeArray, iNativeIndex );
CFMutableDictionaryRef cfValidNativeDict = NULL;
if ( CFGetTypeID(cfDictRef) == CFDictionaryGetTypeID() )
{
CFArrayRef cfObjectClasses = (CFArrayRef) CFDictionaryGetValue( cfDictRef, CFSTR(kXMLObjectClasses) );
if ( cfObjectClasses != NULL && CFGetTypeID(cfObjectClasses) != CFArrayGetTypeID() )
continue;
CFStringRef cfSearchBase = (CFStringRef) CFDictionaryGetValue( cfDictRef, CFSTR(kXMLSearchBase) );
if ( cfSearchBase != NULL && CFGetTypeID(cfSearchBase) != CFStringGetTypeID() )
cfSearchBase = NULL;
CFBooleanRef cfSearchScope = (CFBooleanRef) CFDictionaryGetValue( cfDictRef, CFSTR(kXMLOneLevelSearchScope) );
if ( cfSearchScope != NULL && CFGetTypeID(cfSearchScope) != CFBooleanGetTypeID() )
cfSearchScope = NULL;
CFStringRef cfGroupClasses = (CFStringRef) CFDictionaryGetValue( cfDictRef, CFSTR(kXMLGroupObjectClasses) );
if ( cfGroupClasses != NULL && CFGetTypeID(cfGroupClasses) != CFStringGetTypeID() )
cfGroupClasses = NULL;
CFIndex iObjClassCount = cfObjectClasses ? CFArrayGetCount( cfObjectClasses ) : 0;
for( CFIndex iObjClassIndex = 0; iObjClassIndex < iObjClassCount; iObjClassIndex++ )
{
if ( CFGetTypeID(CFArrayGetValueAtIndex(cfObjectClasses, iObjClassIndex)) != CFStringGetTypeID() )
{
cfObjectClasses = NULL;
break;
}
}
if ( cfObjectClasses != NULL )
{
cfValidNativeDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
if ( cfSearchBase != NULL )
CFDictionarySetValue( cfValidNativeDict, CFSTR(kXMLSearchBase), cfSearchBase );
if ( cfSearchScope != NULL )
CFDictionarySetValue( cfValidNativeDict, CFSTR(kXMLOneLevelSearchScope), cfSearchScope );
if ( cfGroupClasses != NULL )
CFDictionarySetValue( cfValidNativeDict, CFSTR(kXMLGroupObjectClasses), cfGroupClasses );
CFDictionarySetValue( cfValidNativeDict, CFSTR(kXMLObjectClasses), cfObjectClasses );
}
}
else if ( CFGetTypeID(cfDictRef) == CFStringGetTypeID() )
{
CFArrayRef cfObjectClass = CFArrayCreate( kCFAllocatorDefault, (const void **)&cfDictRef, 1, &kCFTypeArrayCallBacks );
cfValidNativeDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
CFDictionarySetValue( cfValidNativeDict, CFSTR(kXMLObjectClasses), cfObjectClass );
DSCFRelease( cfObjectClass );
}
if ( cfValidNativeDict != NULL )
{
CFArrayAppendValue( cfNewNativeArray, cfValidNativeDict );
DSCFRelease( cfValidNativeDict );
}
}
if ( CFArrayGetCount( cfNewNativeArray ) > 0 )
{
CFMutableDictionaryRef cfNewMap = CFDictionaryCreateMutable( kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
CFArrayRef cfArray = (CFArrayRef) CFDictionaryGetValue( recMapDict, CFSTR(kXMLAttrTypeMapArrayKey) );
if ( cfArray != NULL )
{
CFDictionaryRef cfAttribMap = CreateNormalizedAttributeMap( cfArray, cfGlobalAttrMap );
if ( cfAttribMap != NULL )
{
CFDictionarySetValue( cfNewMap, CFSTR(kXMLAttrTypeMapDictKey), cfAttribMap );
DSCFRelease( cfAttribMap );
}
}
else if ( cfGlobalAttrMap )
{
CFDictionarySetValue( cfNewMap, CFSTR(kXMLAttrTypeMapDictKey), cfGlobalAttrMap );
}
CFDictionarySetValue( cfNewMap, CFSTR(kXMLNativeMapArrayKey), cfNewNativeArray );
CFDictionarySetValue( outRecMapDict, cfStdName, cfNewMap );
DSCFRelease( cfNewMap );
}
DSCFRelease( cfNewNativeArray );
}
}
DSCFRelease( cfGlobalAttrMap );
}
if ( outRecMapDict != NULL && CFDictionaryGetCount(outRecMapDict) == 0 )
DSCFRelease( outRecMapDict );
return outRecMapDict;
}
int32_t CLDAPNodeConfig::CalculateSecurityPolicy( CFDictionaryRef inConfiguration )
{
CFBooleanRef cfBool;
int32_t iSecurityLevel = kSecNoSecurity;
if ( inConfiguration != NULL && CFGetTypeID(inConfiguration) == CFDictionaryGetTypeID() )
{
if ( (cfBool = (CFBooleanRef) CFDictionaryGetValue(inConfiguration, CFSTR(kXMLSecurityNoClearTextAuths))) && CFBooleanGetValue(cfBool) )
iSecurityLevel |= kSecDisallowCleartext;
if ( (cfBool = (CFBooleanRef) CFDictionaryGetValue(inConfiguration, CFSTR(kXMLSecurityManInTheMiddle))) && CFBooleanGetValue(cfBool) )
iSecurityLevel |= kSecManInMiddle;
if ( (cfBool = (CFBooleanRef) CFDictionaryGetValue(inConfiguration, CFSTR(kXMLSecurityPacketSigning))) && CFBooleanGetValue(cfBool) )
iSecurityLevel |= kSecPacketSigning;
if ( (cfBool = (CFBooleanRef) CFDictionaryGetValue(inConfiguration, CFSTR(kXMLSecurityPacketEncryption))) && CFBooleanGetValue(cfBool) )
iSecurityLevel |= kSecPacketEncryption;
}
return iSecurityLevel;
}
int CLDAPNodeConfig::SASLInteract( LDAP *ld, unsigned flags, void *inDefaults, void *inInteract )
{
sasl_interact_t *interact = (sasl_interact_t *)inInteract;
saslDefaults *defaults = (saslDefaults *) inDefaults;
if ( ld == NULL ) return LDAP_PARAM_ERROR;
while ( interact->id != SASL_CB_LIST_END )
{
const char *dflt = interact->defresult;
switch( interact->id )
{
case SASL_CB_AUTHNAME:
if ( defaults ) dflt = defaults->authcid;
break;
case SASL_CB_PASS:
if ( defaults ) dflt = defaults->password;
break;
case SASL_CB_USER:
if ( defaults ) dflt = defaults->authzid;
break;
}
if ( dflt && !(*dflt) )
dflt = NULL;
if ( dflt || interact->id == SASL_CB_USER )
{
interact->result = ((dflt && *dflt) ? dflt : "");
interact->len = strlen( (char *)interact->result );
} else {
return LDAP_OTHER;
}
interact++;
}
return LDAP_SUCCESS;
}
bool CLDAPNodeConfig::GetUserTGTIfNecessaryAndStore( const char *inName, const char *inPassword, char **outCacheName )
{
if ( inName == NULL || inPassword == NULL )
return false;
int siResult = eDSAuthFailed;
krb5_context krbContext = NULL;
krb5_principal principal = NULL;
krb5_principal cachePrinc = NULL;
krb5_ccache krbCache = NULL;
krb5_error_code retval = 0;
krb5_creds mcreds;
krb5_creds my_creds;
bool bNeedNewCred = true;
char *principalString = NULL;
char *pCacheName = NULL;
gKerberosMutex->WaitLock();
do
{
retval = krb5_init_context( &krbContext );
if ( retval != 0 ) break;
retval = krb5_parse_name( krbContext, inName, &principal );
if ( retval != 0 ) break;
retval = krb5_unparse_name( krbContext, principal, &principalString );
if ( retval != 0 ) break;
pCacheName = (char *) malloc( sizeof("MEMORY:") + strlen(principalString) + 1 );
strcpy( pCacheName, "MEMORY:" );
strcat( pCacheName, principalString );
retval = krb5_cc_resolve( krbContext, pCacheName, &krbCache);
if ( retval != 0 ) break;
retval = krb5_cc_set_default_name( krbContext, pCacheName );
if ( retval != 0 ) break;
retval = krb5_cc_get_principal(krbContext, krbCache, &cachePrinc);
if ( retval == 0 )
{
if ( krb5_principal_compare(krbContext, principal, cachePrinc) )
{
memset( &mcreds, 0, sizeof(mcreds) );
retval = krb5_copy_principal( krbContext, principal, &mcreds.client );
if ( retval != 0 ) break;
retval = krb5_build_principal_ext( krbContext, &mcreds.server, principal->realm.length, principal->realm.data,
KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, principal->realm.length, principal->realm.data, 0 );
if ( retval != 0 ) break;
retval = krb5_cc_retrieve_cred( krbContext, krbCache, KRB5_TC_SUPPORTED_KTYPES, &mcreds, &my_creds );
if ( retval == 0 )
{
krb5_int32 timeret = 0;
krb5_timeofday( krbContext, &timeret );
timeret += 600;
if ( timeret > my_creds.times.endtime )
{
krb5_cc_initialize( krbContext, krbCache, principal );
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetUserTGTIfNecessaryAndStore - Existing TGT expires shortly, initializing cache for user %s", principalString );
}
else
{
bNeedNewCred = false;
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetUserTGTIfNecessaryAndStore - Existing TGT for user %s is Valid", principalString );
}
krb5_free_cred_contents( krbContext, &my_creds );
}
krb5_free_cred_contents( krbContext, &mcreds );
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetUserTGTIfNecessaryAndStore - Cache for user %s being initialized", principalString );
retval = krb5_cc_initialize( krbContext, krbCache, principal );
if ( retval != 0 ) break;
}
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetUserTGTIfNecessaryAndStore - Uninitialized cache available, initializing for user %s", principalString );
retval = krb5_cc_initialize( krbContext, krbCache, principal );
if ( retval != 0 ) break;
}
krb5_int32 startTime = 0;
krb5_get_init_creds_opt options;
memset( &my_creds, 0, sizeof(my_creds) );
krb5_get_init_creds_opt_init( &options );
krb5_get_init_creds_opt_set_forwardable( &options, 1 );
krb5_get_init_creds_opt_set_proxiable( &options, 1 );
if ( bNeedNewCred == false )
{
krb5_get_init_creds_opt_set_tkt_life( &options, 300 ); DbgLog( kLogPlugin, "CLDAPNodeConfig::GetUserTGTIfNecessaryAndStore - Getting TGT with short ticket life for verification only for user %s", principalString );
}
retval = krb5_get_init_creds_password( krbContext, &my_creds, principal, (char *)inPassword, NULL, 0, startTime, NULL, &options );
if ( retval != 0 )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetUserTGTIfNecessaryAndStore - Error %d getting TGT", retval );
break;
}
if ( bNeedNewCred )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetUserTGTIfNecessaryAndStore - Storing credentials in Kerberos cache for user %s", principalString );
retval = krb5_cc_store_cred( krbContext, krbCache, &my_creds);
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetUserTGTIfNecessaryAndStore - Valid credentials in Kerberos cache for user %s", principalString );
}
krb5_free_cred_contents( krbContext, &my_creds );
if ( retval == 0 )
siResult = eDSNoErr;
} while ( 0 );
if ( outCacheName != NULL )
(*outCacheName) = pCacheName;
else
DSDelete( pCacheName );
if ( principalString )
{
krb5_free_unparsed_name( krbContext, principalString );
principalString = NULL;
}
if ( principal )
{
krb5_free_principal( krbContext, principal );
principal = NULL;
}
if ( cachePrinc )
{
krb5_free_principal( krbContext, cachePrinc );
cachePrinc = NULL;
}
if ( krbCache )
{
if ( siResult == eDSAuthFailed )
{
krb5_cc_destroy( krbContext, krbCache );
krbCache = NULL;
}
else
{
krb5_cc_close( krbContext, krbCache );
krbCache = NULL;
}
}
if ( krbContext )
{
krb5_free_context( krbContext );
krbContext = NULL;
}
gKerberosMutex->SignalLock();
return (siResult == eDSNoErr);
}
void CLDAPNodeConfig::BuildReplicaList( void )
{
CFMutableArrayRef tempList = CFArrayCreateMutableCopy( kCFAllocatorDefault, 0, fReadReplicas );
CFRange rangeOfWrites = CFRangeMake( 0, CFArrayGetCount(fWriteReplicas) );
char portString[32];
char serverName[512];
struct addrinfo hints;
CFIndex numReps;
CFArrayAppendArray( tempList, fWriteReplicas, rangeOfWrites );
numReps = CFArrayGetCount( tempList );
bzero( &hints, sizeof(hints) );
hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM;
sprintf( portString, "%d", fServerPort );
for ( CFIndex ii = 0; ii < numReps; ii++ )
{
struct addrinfo *addrList = NULL;
CFStringRef replicaStrRef = (CFStringRef) CFArrayGetValueAtIndex( tempList, ii );
if ( CFStringGetCString(replicaStrRef, serverName, sizeof(serverName), kCFStringEncodingUTF8) == true)
{
if ( getaddrinfo(serverName, portString, &hints, &addrList) == 0 )
{
struct addrinfo *addrTemp = addrList;
bool bWriteable = CFArrayContainsValue( fWriteReplicas, rangeOfWrites, replicaStrRef );
char ipAddress[129];
while ( addrTemp != NULL )
{
CLDAPReplicaInfo *replicaInfo = NULL;
if ( addrTemp->ai_family == AF_INET &&
inet_ntop(AF_INET, (const void *)&(((struct sockaddr_in*)(addrTemp->ai_addr))->sin_addr),
ipAddress, sizeof(ipAddress)) == NULL )
{
addrTemp = addrTemp->ai_next;
continue;
}
else if ( addrTemp->ai_family == AF_INET6 &&
inet_ntop( AF_INET6, (const void *)&(((struct sockaddr_in6*)(addrTemp->ai_addr))->sin6_addr),
ipAddress, sizeof(ipAddress) ) == NULL )
{
addrTemp = addrTemp->ai_next;
continue;
}
CFStringRef cfTempString = CFStringCreateWithCStringNoCopy( kCFAllocatorDefault, ipAddress, kCFStringEncodingUTF8,
kCFAllocatorNull );
bool bIPWriteable = (bWriteable ? bWriteable : CFArrayContainsValue(fWriteReplicas, rangeOfWrites, cfTempString));
DSCFRelease( cfTempString );
for ( ListOfReplicasI iter = fReplicaList.begin(); iter != fReplicaList.end(); iter++ )
{
replicaInfo = (*iter);
if ( strcmp(ipAddress, replicaInfo->fIPAddress) == 0 )
{
if ( replicaInfo->fbSupportsWrites != bIPWriteable )
{
replicaInfo->fbSupportsWrites = bIPWriteable;
DbgLog( kLogPlugin, "CLDAPNodeConfig::BuildReplicaList - Node %s - changed replica %s to %s", fNodeName, ipAddress,
(bIPWriteable ? "read/write" : "read") );
}
break;
}
replicaInfo = NULL;
}
if ( replicaInfo == NULL )
{
replicaInfo = new CLDAPReplicaInfo( ipAddress, fServerPort, fIsSSL, bIPWriteable );
if ( replicaInfo != NULL )
{
fReplicaList.push_back( replicaInfo );
DbgLog( kLogPlugin, "CLDAPNodeConfig::BuildReplicaList - Node %s - new replica %s for %s", fNodeName, ipAddress,
(bIPWriteable ? "read/write" : "read") );
}
}
addrTemp = addrTemp->ai_next;
}
freeaddrinfo( addrList );
}
}
}
DSCFRelease( tempList );
}
void CLDAPNodeConfig::MergeArraysRemovingDuplicates( CFMutableArrayRef cfPrimaryArray, CFArrayRef cfArrayToAdd )
{
CFIndex addCount = CFArrayGetCount( cfArrayToAdd );
CFRange cfRange = CFRangeMake( 0, CFArrayGetCount(cfPrimaryArray) );
for( CFIndex ii = 0; ii < addCount; ii++ )
{
CFStringRef cfString = (CFStringRef) CFArrayGetValueAtIndex( cfArrayToAdd, ii );
if( CFArrayContainsValue(cfPrimaryArray, cfRange, cfString) == false )
{
CFArrayAppendValue( cfPrimaryArray, cfString );
cfRange.length++; }
}
}
SInt32 CLDAPNodeConfig::GetReplicaListFromDNS( CFMutableArrayRef inOutRepList )
{
SInt32 siResult = eDSNoStdMappingAvailable;
CFMutableArrayRef serviceRecords = NULL;
CFMutableArrayRef aRepList = NULL; CFStringRef aString = NULL;
CFStringRef cfServerName = CFStringCreateWithCString( kCFAllocatorDefault, fNodeName, kCFStringEncodingUTF8 );
CFArrayRef cfComponents = CFStringCreateArrayBySeparatingStrings( kCFAllocatorDefault, cfServerName, CFSTR(".") );
bool bIsDNSname = true;
int namePartCount = 1; CFStringRef cfDomainSuffix = NULL;
if( cfComponents != NULL )
{
namePartCount = CFArrayGetCount( cfComponents );
if( namePartCount == 4 && CFStringGetIntValue((CFStringRef) CFArrayGetValueAtIndex(cfComponents, 3)) != 0 )
{
bIsDNSname = false;
}
if( bIsDNSname && namePartCount > 2 )
{
cfDomainSuffix = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR("%@.%@"), CFArrayGetValueAtIndex(cfComponents, namePartCount-2), CFArrayGetValueAtIndex(cfComponents, namePartCount-1) );
}
}
DSCFRelease( cfServerName );
DSCFRelease( cfComponents );
if( bIsDNSname )
{
if( fSecureUse == true && namePartCount > 2 && cfDomainSuffix != NULL )
{
CFStringRef cfKey = SCDynamicStoreKeyCreateNetworkGlobalEntity( kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetDNS );
SCDynamicStoreRef cfStore = SCDynamicStoreCreate( kCFAllocatorDefault, CFSTR("DirectoryService"), NULL, NULL );
CFDictionaryRef cfDNSdomainsDict = (CFDictionaryRef) SCDynamicStoreCopyValue( cfStore, cfKey );
DSCFRelease( cfKey );
DSCFRelease( cfStore );
CFArrayRef cfSearchArray = (CFArrayRef) CFDictionaryGetValue( cfDNSdomainsDict, kSCPropNetDNSSearchDomains );
if( cfSearchArray != NULL && CFArrayGetCount( cfSearchArray ) )
{
CFStringRef cfFirstDomain = (CFStringRef) CFArrayGetValueAtIndex( cfSearchArray, 0 );
if( CFStringHasSuffix( cfFirstDomain, cfDomainSuffix ) )
{
UInt32 uiLength = (UInt32) CFStringGetMaximumSizeForEncoding( CFStringGetLength(cfFirstDomain), kCFStringEncodingUTF8 ) + 1;
char *domain = (char *) calloc( sizeof(char), uiLength );
CFStringGetCString( cfFirstDomain, domain, uiLength, kCFStringEncodingUTF8 );
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetReplicaListFromDNS - Node %s - Checking computer's primary domain %s", fNodeName, domain );
serviceRecords = getDNSServiceRecs( "ldap", domain );
DSFreeString( domain );
}
}
DSCFRelease( cfDNSdomainsDict );
}
DSCFRelease( cfDomainSuffix );
if( serviceRecords == NULL )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetReplicaListFromDNS - Node %s - Checking domain %s", fNodeName );
serviceRecords = getDNSServiceRecs( "ldap", fNodeName );
}
if( fSecureUse == true && namePartCount > 2 && serviceRecords == NULL )
{
char *subDomain = strchr( fNodeName, '.' ) + 1;
if( subDomain )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetReplicaListFromDNS - Node %s - Checking domain %s", fNodeName, subDomain );
serviceRecords = getDNSServiceRecs( "ldap", subDomain );
}
}
}
if ( serviceRecords != NULL )
{
aRepList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
CFIndex totalCount = CFArrayGetCount(serviceRecords);
for (CFIndex indexToCnt=0; indexToCnt < totalCount; indexToCnt++ )
{
aString = (CFStringRef)CFDictionaryGetValue( (CFDictionaryRef)::CFArrayGetValueAtIndex( serviceRecords, indexToCnt ), CFSTR("Host"));
if ( aString != NULL );
{
CFArrayAppendValue(aRepList, aString);
}
}
CFRelease(serviceRecords);
}
if ( aRepList != NULL )
{
if( CFArrayGetCount(aRepList) > 0 )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetReplicaListFromDNS - Node %s - Found ldap replica servers in DNS service records.", fNodeName );
MergeArraysRemovingDuplicates( aRepList, inOutRepList );
CFArrayRemoveAllValues( inOutRepList );
CFArrayAppendArray( inOutRepList, aRepList, CFRangeMake(0,CFArrayGetCount(aRepList)) );
siResult = eDSNoErr;
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetReplicaListFromDNS - Node %s - No ldap replica servers in DNS service records.", fNodeName );
}
CFRelease( aRepList );
aRepList = NULL;
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetReplicaListFromDNS - Node %s - No ldap replica servers in DNS service records.", fNodeName );
}
return(siResult);
}
SInt32 CLDAPNodeConfig::GetReplicaListFromAltServer( LDAP *inHost, CFMutableArrayRef inOutRepList )
{
SInt32 siResult = eDSRecordNotFound;
LDAPMessage *result = NULL;
const char *attrs[2] = { "altserver", NULL };
CFMutableArrayRef aRepList = NULL;
struct timeval timeout = { fOpenCloseTimeout, 0 };
int ldapReturnCode = ldap_search_ext_s( inHost, "", LDAP_SCOPE_BASE, "(objectclass=*)", (char **) attrs, 0, NULL, NULL, &timeout, 0, &result );
if ( ldapReturnCode == LDAP_SUCCESS )
{
aRepList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
struct berval **bValues = ldap_get_values_len (inHost, result, "altserver" );
if ( bValues != NULL )
{
for (int i = 0; bValues[i] != NULL; i++ )
{
if ( bValues[i] != NULL )
{
LDAPURLDesc *ludpp = NULL;
if ( ldap_url_parse( bValues[i]->bv_val, &ludpp) == LDAP_SUCCESS )
{
if ( ludpp->lud_port == LDAPS_PORT )
{
if ( fIsSSL == true )
{
CFStringRef cfString = CFStringCreateWithCString( kCFAllocatorDefault, ludpp->lud_host, kCFStringEncodingUTF8 );
if ( cfString != NULL )
{
CFArrayAppendValue( aRepList, cfString );
DSCFRelease( cfString );
}
}
}
ldap_free_urldesc( ludpp );
ludpp = NULL;
}
siResult = eDSNoErr;
}
}
ldap_value_free_len(bValues);
} }
if ( result != NULL )
{
ldap_msgfree( result );
result = NULL;
}
if ( aRepList != NULL )
{
if( CFArrayGetCount(aRepList) > 0 )
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetReplicaListFromAltServer - Node %s - Found some ldap replica servers in rootDSE altServer.",
fNodeName );
MergeArraysRemovingDuplicates( inOutRepList, aRepList );
siResult = eDSNoErr;
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetReplicaListFromAltServer - Node %s - No ldap replica servers in rootDSE altServer.", fNodeName );
}
}
else
{
DbgLog( kLogPlugin, "CLDAPNodeConfig::GetReplicaListFromAltServer - Node %s - No ldap replica servers in rootDSE altServer.", fNodeName );
}
DSCFRelease( aRepList );
return siResult;
}