#include <stdio.h>
#include <string.h> //used for strcpy, etc.
#include <stdlib.h> //used for malloc
#include <ctype.h> //use for isprint
#include <syslog.h> //error logging
#include <arpa/inet.h> // inet_ntop
#include <netinet/in.h> // struct sockaddr_in
#include <ifaddrs.h>
#include <fcntl.h>
#include <sasl.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <Kerberos/Kerberos.h>
#include <stack>
#include "CLDAPNode.h"
#include "CLDAPv3Plugin.h"
#include "CLog.h"
#include "DNSLookups.h"
#include "DSLDAPUtils.h"
#include "DSUtils.h"
#include "ServerControl.h"
#pragma mark Defines and Macros
#define OCSEPCHARS " '()$"
#define IsOnlyBitSet(a,b) (((a) & (b)) && ((a) ^ (b)) == 0)
#pragma mark -
#pragma mark Globals, TypeDefs and Static Member Variables
extern bool gServerOS;
extern DSMutexSemaphore *gLDAPKerberosMutex;
extern CLDAPv3Configs *gpConfigFromXML;
extern sInt32 gProcessPID;
struct saslDefaults
{
char *authcid;
char *password;
char *authzid;
};
bool CLDAPNode::fCheckThreadActive = false;
bool gBlockLDAPNetworkChange = false;
class CLDAPv3Plugin;
#pragma mark -
#pragma mark Prototypes
bool checkReachability(struct sockaddr *destAddr);
int sasl_interact( LDAP *ld, unsigned flags, void *inDefaults, void *inInteract );
bool getUserTGTIfNecessaryAndStoreInCache( char *inName, char *inPassword );
int doSASLBindAttemptIfPossible( LDAP *inLDAPHost, sLDAPConfigData *pConfig, char *ldapAcct, char *ldapPasswd, char *kerberosID = NULL );
void *checkFailedServers( void *data );
void LogFailedConnection(const char *inTag, const char *inServerName, int inDisabledIntervalDuration);
void SetSockList(int *inSockList, int inSockCount, bool inClose);
#pragma mark -
#pragma mark Struct sLDAPNodeStruct Functions
sLDAPNodeStruct::sLDAPNodeStruct( void )
{
fNodeName = NULL;
fHost = NULL;
fLDAPSessionMutex = new DSMutexSemaphore;
fRefCount = 1; fConnectionActiveCount = 0;
fLDAPServer = NULL;
fDirectServerName = NULL;
fDirectLDAPPort = 0;
bAuthCallActive = false;
bBadSession = false;
fKerberosId = fLDAPUserName = fLDAPAuthType = NULL;
fLDAPCredentials = NULL;
fLDAPCredentialsLen = 0;
fConnectionStatus = kConnectionUnknown;
fDelayedBindTime = 0;
fIdleTOCount = 0;
fIdleTO = 2; fDelayRebindTry = kLDAPDefaultRebindTryTimeoutInSeconds;
}
sLDAPNodeStruct::sLDAPNodeStruct( const sLDAPNodeStruct &inLDAPNodeStruct)
{
fNodeName = (inLDAPNodeStruct.fNodeName ? strdup( inLDAPNodeStruct.fNodeName ) : NULL);
fHost = NULL;
fLDAPSessionMutex = new DSMutexSemaphore;
fRefCount = 1;
fConnectionActiveCount = 0;
fLDAPServer = (inLDAPNodeStruct.fLDAPServer ? strdup(inLDAPNodeStruct.fLDAPServer) : NULL);
fDirectServerName = (inLDAPNodeStruct.fDirectServerName ? strdup(inLDAPNodeStruct.fDirectServerName) : NULL );
bAuthCallActive = false;
bBadSession = false;
fKerberosId = fLDAPUserName = fLDAPAuthType = NULL;
fLDAPCredentials = NULL;
fLDAPCredentialsLen = 0;
fDirectLDAPPort = inLDAPNodeStruct.fDirectLDAPPort;
fConnectionStatus = inLDAPNodeStruct.fConnectionStatus;
fDelayedBindTime = inLDAPNodeStruct.fDelayedBindTime;
fIdleTOCount = 0;
fIdleTO = inLDAPNodeStruct.fIdleTO;
fDelayRebindTry = inLDAPNodeStruct.fDelayRebindTry;
}
sLDAPNodeStruct::~sLDAPNodeStruct( void )
{
if( fHost )
{
ldap_unbind_ext( fHost, NULL, NULL );
}
DSDelete( fNodeName );
DSDelete( fLDAPServer );
DSDelete( fDirectServerName );
DSDelete( fLDAPUserName );
DSDelete( fKerberosId );
DSDelete( fLDAPAuthType );
DSFree( fLDAPCredentials );
DSDelete( fLDAPSessionMutex );
}
void sLDAPNodeStruct::updateCredentials( void *inLDAPCredentials, uInt32 inLDAPCredentialLen, char *inAuthType )
{
DSFree( fLDAPCredentials );
DSDelete( fLDAPAuthType );
fLDAPAuthType = (inAuthType ? strdup(inAuthType) : NULL);
if ( inLDAPCredentialLen )
{
fLDAPCredentials = calloc( inLDAPCredentialLen + 1, 1 );
bcopy( inLDAPCredentials, fLDAPCredentials, inLDAPCredentialLen );
}
}
void sLDAPNodeStruct::SessionMutexWaitWithFunctionName( const char* callingFunction )
{
double startTime, callTime;
#ifdef LOG_LDAPSessionMutex_Attempts
DBGLOG3( kLogPlugin, "T[%X] %s called, waiting for fLDAPSessionMutex [%X]", pthread_self(), callingFunction, fLDAPSessionMutex );
#endif
startTime = dsTimestamp();
fLDAPSessionMutex->Wait();
fLDAPSessionMutexStartTime = dsTimestamp();
callTime = (fLDAPSessionMutexStartTime - startTime)/USEC_PER_SEC;
#ifdef LOG_LDAPSessionMutex_Attempts
DBGLOG4( kLogPlugin, "T[%X] %s, got fLDAPSessionMutex [%X] after %f seconds", pthread_self(), callingFunction, fLDAPSessionMutex, callTime );
#endif
}
void sLDAPNodeStruct::SessionMutexSignalWithFunctionName( const char* callingFunction )
{
double callTime;
callTime = (dsTimestamp() - fLDAPSessionMutexStartTime)/USEC_PER_SEC;
if ( callTime > kMinTimeToLogLockBeingHeld )
{
DBGLOG4( kLogPlugin, "T[%X] %s, held fLDAPSessionMutex [%X] for %f seconds", pthread_self(), callingFunction, fLDAPSessionMutex, callTime );
}
fLDAPSessionMutex->Signal();
}
#pragma mark -
#pragma mark Struct sLDAPContextData Functions
sLDAPContextData::sLDAPContextData( const sLDAPContextData& inContextData )
{
fType = 0;
offset = 0;
index = 1;
fNodeName = ( inContextData.fNodeName ? strdup(inContextData.fNodeName) : NULL );
fOpenRecordType = fOpenRecordName = fOpenRecordDN = NULL;
fUID = inContextData.fUID;
fEffectiveUID = inContextData.fEffectiveUID;
bLDAPv2ReadOnly = inContextData.bLDAPv2ReadOnly;
fAuthUserName = (inContextData.fAuthUserName ? strdup(inContextData.fAuthUserName) : NULL);
fAuthType = (inContextData.fAuthType ? strdup(inContextData.fAuthType) : NULL);
if( inContextData.fAuthCredential != NULL )
{
fAuthCredential = calloc( inContextData.fAuthCredentialLen + 1, 1 );
bcopy( inContextData.fAuthCredential, fAuthCredential, inContextData.fAuthCredentialLen );
fAuthCredentialLen = inContextData.fAuthCredentialLen;
}
else
{
fAuthCredential = NULL;
fAuthCredentialLen = 0;
}
fPWSRef = 0;
fPWSNodeRef = 0;
fPWSUserIDLength = 0;
fPWSUserID = NULL;
fLDAPNodeStruct = inContextData.fLDAPNodeStruct;
fLDAPNodeStruct->ChangeRefCountBy( 1 );
}
sLDAPContextData::sLDAPContextData( void )
{
fNodeName = NULL;
fType = 0;
offset = 0;
index = 1;
bLDAPv2ReadOnly = false;
fOpenRecordType = fOpenRecordName = fOpenRecordDN = NULL;
fAuthUserName = fAuthType = NULL;
fAuthCredential = NULL;
fAuthCredentialLen = 0;
fPWSRef = 0;
fPWSNodeRef = 0;
fPWSUserIDLength = 0;
fPWSUserID = NULL;
fUID = fEffectiveUID = 0xffffffff; fLDAPNodeStruct = NULL;
}
sLDAPContextData::~sLDAPContextData( void )
{
DSDelete( fNodeName );
DSDelete( fOpenRecordType );
DSDelete( fOpenRecordName );
DSDelete( fOpenRecordDN );
DSDelete( fAuthUserName );
DSFree( fAuthCredential );
DSDelete( fAuthType );
if ( fPWSNodeRef != 0 ) {
dsCloseDirNode( fPWSNodeRef );
fPWSNodeRef = 0;
}
if ( fPWSRef != 0 ) {
dsCloseDirService( fPWSRef );
fPWSRef = 0;
}
DSFree( fPWSUserID );
if( fLDAPNodeStruct )
fLDAPNodeStruct->ChangeRefCountBy( -1 );
}
void sLDAPContextData::setCredentials( char *inUserName, void *inCredential, uInt32 inCredentialLen, char *inAuthType )
{
DSDelete( fAuthUserName );
DSFree( fAuthCredential );
DSDelete( fAuthType );
fAuthUserName = (inUserName ? strdup(inUserName) : NULL);
if( inCredential && inCredentialLen )
{
fAuthCredential = calloc( inCredentialLen + 1, 1 ); bcopy( inCredential, fAuthCredential, inCredentialLen );
fAuthCredentialLen = inCredentialLen;
}
else
{
fAuthCredential = NULL;
fAuthCredentialLen = 0;
}
fAuthType = (inAuthType ? strdup(inAuthType) : NULL);
}
#pragma mark -
#pragma mark Support Routines
bool checkReachability(struct sockaddr *destAddr)
{
SCNetworkReachabilityRef target = NULL;
SCNetworkConnectionFlags flags;
bool ok = false;
target = SCNetworkReachabilityCreateWithAddress(NULL, destAddr);
if (target == NULL) {
goto done;
}
if (!SCNetworkReachabilityGetFlags(target, &flags)) {
goto done;
}
if (!(flags & kSCNetworkFlagsReachable)) {
goto done;
}
if (flags & kSCNetworkFlagsConnectionRequired) {
goto done;
}
ok = true;
done :
if (target) CFRelease(target);
return(ok);
}
int sasl_interact( 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 getUserTGTIfNecessaryAndStoreInCache( char *inName, char *inPassword )
{
int siResult = eDSAuthFailed;
krb5_creds my_creds;
krb5_context krbContext = NULL;
krb5_principal principal = NULL;
krb5_principal cachePrinc = NULL;
krb5_ccache krbCache = NULL;
krb5_error_code retval;
krb5_creds mcreds;
bool bNeedNewCred = true;
char *principalString = NULL;
char *pCacheName = NULL;
try
{
if( inName == NULL || inPassword == NULL ) throw( (int) __LINE__ );
retval = krb5_init_context( &krbContext );
if( retval ) throw((int) __LINE__ );
retval = krb5_parse_name( krbContext, inName, &principal );
if( retval ) throw((int) __LINE__ );
retval = krb5_unparse_name( krbContext, principal, &principalString );
if( retval ) throw((int) __LINE__ );
pCacheName = (char *) malloc( strlen(principalString) + 7 + 1 ); strcpy( pCacheName, "MEMORY:" );
strcat( pCacheName, principalString );
retval = krb5_cc_resolve( krbContext, pCacheName, &krbCache);
if( retval ) throw((int) __LINE__ );
retval = krb5_cc_set_default_name( krbContext, pCacheName );
if( retval ) throw((int) __LINE__ );
if( krb5_cc_get_principal(krbContext, krbCache, &cachePrinc) == 0 )
{
if( krb5_principal_compare(krbContext, principal, cachePrinc) )
{
memset( &mcreds, 0, sizeof(mcreds) );
retval = krb5_copy_principal( krbContext, principal, &mcreds.client );
if( retval ) throw((int) __LINE__ );
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 && krb5_cc_retrieve_cred( krbContext, krbCache, KRB5_TC_SUPPORTED_KTYPES, &mcreds, &my_creds) == 0 )
{
krb5_int32 timeret = 0;
krb5_timeofday( krbContext, &timeret );
timeret += 600;
if( timeret > my_creds.times.endtime )
{
krb5_cc_initialize( krbContext, krbCache, principal );
DBGLOG1( kLogPlugin, "CLDAPNode: Existing TGT expires shortly, initializing cache for user %s", principalString );
}
else
{
bNeedNewCred = false;
DBGLOG1( kLogPlugin, "CLDAPNode: Existing TGT for user %s is Valid", principalString );
}
krb5_free_cred_contents( krbContext, &my_creds );
}
krb5_free_cred_contents( krbContext, &mcreds );
}
else
{
DBGLOG1( kLogPlugin, "CLDAPNode: Cache for user %s being initialized", principalString );
retval = krb5_cc_initialize( krbContext, krbCache, principal );
if( retval ) throw((int) __LINE__ );
}
}
else
{
DBGLOG1( kLogPlugin, "CLDAPNode: Uninitialized cache available, initializing for user %s", principalString );
retval = krb5_cc_initialize( krbContext, krbCache, principal );
if( retval ) throw((int) __LINE__ );
}
OM_uint32 minor_status;
gss_krb5_ccache_name( &minor_status, pCacheName, NULL );
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 );
krb5_get_init_creds_opt_set_address_list( &options, NULL );
if( bNeedNewCred == false )
{
krb5_get_init_creds_opt_set_tkt_life( &options, 300 ); DBGLOG1( kLogPlugin, "CLDAPNode: Getting TGT with short ticket life for verification only for user %s", principalString );
}
retval = krb5_get_init_creds_password( krbContext, &my_creds, principal, inPassword, NULL, 0, startTime, NULL, &options );
if( retval )
{
DBGLOG1( kLogPlugin, "CLDAPNode: Error %d getting TGT", retval );
throw((int) __LINE__ );
}
if( bNeedNewCred )
{
DBGLOG1( kLogPlugin, "CLDAPNode: Storing credentials in Kerberos cache for user %s", principalString );
retval = krb5_cc_store_cred( krbContext, krbCache, &my_creds);
}
else
{
DBGLOG1( kLogPlugin, "CLDAPNode: Valid credentials in Kerberos cache for user %s", principalString );
}
krb5_free_cred_contents( krbContext, &my_creds );
siResult = eDSNoErr;
}
catch (int err )
{
DBGLOG2( kLogPlugin, "CLDAPNode: Error getting TGT for user line %d in %s", err, __FILE__ );
siResult = eDSAuthFailed;
}
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;
}
return (siResult == eDSNoErr);
}
int doSASLBindAttemptIfPossible( LDAP *inLDAPHost, sLDAPConfigData *pConfig, char *ldapAcct, char *ldapPasswd, char *kerberosID )
{
int siResult = LDAP_OTHER;
saslDefaults defaults = { 0 };
char **dn = NULL;
uInt32 iReqSecurity = kSecNoSecurity;
uInt32 iFinalSecurity = kSecNoSecurity;
if( pConfig != NULL && pConfig->fSASLmethods != NULL )
{
CFMutableArrayRef cfSupportedMethods = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
CFRange saslMethodRange = CFRangeMake( 0, CFArrayGetCount( pConfig->fSASLmethods ) );
iReqSecurity = pConfig->fSecurityLevel & kSecSecurityMask;
if( DSIsStringEmpty(ldapAcct) == false && DSIsStringEmpty( ldapPasswd ) == false )
{
dn = ldap_explode_dn( ldapAcct, 1 );
if( dn ) {
defaults.authcid = dn[0];
defaults.authzid = (char *)calloc( sizeof(char), strlen(ldapAcct) + 3 + 1 );
strcpy( defaults.authzid, "dn:" );
strcat( defaults.authzid, ldapAcct );
} else {
defaults.authcid = ldapAcct;
defaults.authzid = (char *)calloc( sizeof(char), strlen(ldapAcct) + 2 + 1 );
strcpy( defaults.authzid, "u:" );
strcat( defaults.authzid, ldapAcct );
}
defaults.password = ldapPasswd;
}
else
{
iReqSecurity &= (~kSecDisallowCleartext);
}
if( pConfig->bIsSSL )
{
iFinalSecurity |= (kSecDisallowCleartext | kSecPacketEncryption);
}
if( CFArrayContainsValue(pConfig->fSASLmethods, saslMethodRange, CFSTR("GSSAPI")) && gLDAPKerberosMutex &&
( (iReqSecurity & kSecDisallowCleartext) ||
(iReqSecurity & kSecManInMiddle) ||
(iReqSecurity & kSecPacketSigning) ||
(iReqSecurity & kSecPacketEncryption) ||
(iReqSecurity & kSecSecurityMask) == 0 ) )
{
CFArrayAppendValue( cfSupportedMethods, CFSTR("GSSAPI") );
iFinalSecurity |= (kSecDisallowCleartext | kSecManInMiddle | kSecPacketSigning | kSecPacketEncryption);
}
if( CFArrayContainsValue(pConfig->fSASLmethods, saslMethodRange, CFSTR("CRAM-MD5")) &&
( IsOnlyBitSet(iReqSecurity,kSecDisallowCleartext) ||
(iReqSecurity & kSecSecurityMask) == 0) )
{
CFArrayAppendValue( cfSupportedMethods, CFSTR("CRAM-MD5") );
iFinalSecurity |= kSecDisallowCleartext;
}
if( (iReqSecurity & iFinalSecurity) == iReqSecurity &&
DSIsStringEmpty(ldapAcct) == false && DSIsStringEmpty(ldapPasswd) == false )
{
CFIndex iCount = CFArrayGetCount( cfSupportedMethods );
if( iCount )
{
for( CFIndex ii = 0; ii < iCount && (siResult == LDAP_OTHER || siResult == LDAP_LOCAL_ERROR); ii++ )
{
CFStringRef cfMethod = (CFStringRef) CFArrayGetValueAtIndex( cfSupportedMethods, ii );
uInt32 uiLength = (uInt32) CFStringGetMaximumSizeForEncoding( CFStringGetLength(cfMethod), kCFStringEncodingUTF8 ) + 1;
char *pMethod = (char *) calloc( sizeof(char), uiLength );
CFStringGetCString( cfMethod, pMethod, uiLength, kCFStringEncodingUTF8 );
DBGLOG1( kLogPlugin, "CLDAPNode: Attempting %s Authentication", pMethod );
if( strcmp(pMethod, "GSSAPI") == 0 )
{
saslDefaults gssapiDefaults = { 0 };
char *username = NULL;
if( pConfig->fServerAccount && strcmp(pConfig->fServerAccount, ldapAcct) == 0 && pConfig->fKerberosId )
{
username = pConfig->fKerberosId;
}
else
{
username = (kerberosID ? kerberosID : defaults.authcid);
}
if( (iReqSecurity & kSecPacketEncryption) != 0 )
{
ldap_set_option( inLDAPHost, LDAP_OPT_X_SASL_SECPROPS, (void *) "minssf=56" );
}
else if( (iReqSecurity & kSecPacketSigning) != 0 )
{
ldap_set_option( inLDAPHost, LDAP_OPT_X_SASL_SECPROPS, (void *) "minssf=1" );
}
if ( username != NULL )
{
char *pRealm = strchr( username, '@' );
if ( pRealm != NULL )
{
gpConfigFromXML->VerifyKerberosForRealm( pRealm+1, pConfig->fServerName );
}
}
gLDAPKerberosMutex->Wait();
if( getUserTGTIfNecessaryAndStoreInCache(username, ldapPasswd) )
{
siResult = ldap_sasl_interactive_bind_s( inLDAPHost, NULL, pMethod, NULL, NULL, LDAP_SASL_QUIET, sasl_interact, &gssapiDefaults );
}
else
{
DBGLOG( kLogPlugin, "CLDAPNode: Couldn't get Kerberos credentials for GSSAPI" );
siResult = LDAP_LOCAL_ERROR;
}
if( siResult == LDAP_LOCAL_ERROR || siResult == LDAP_OTHER )
{
ldap_set_option( inLDAPHost, LDAP_OPT_X_SASL_SECPROPS, (void *) "" ); }
gLDAPKerberosMutex->Signal();
}
else
{
siResult = ldap_sasl_interactive_bind_s( inLDAPHost, NULL, pMethod, NULL, NULL, LDAP_SASL_QUIET, sasl_interact, &defaults );
}
if( siResult == LDAP_AUTH_METHOD_NOT_SUPPORTED )
{
DBGLOG1( kLogPlugin, "CLDAPNode: Failed %s Authentication, not supported", pMethod );
siResult = LDAP_OTHER;
}
else if( siResult == LDAP_OTHER || siResult == LDAP_LOCAL_ERROR )
{
DBGLOG2( kLogPlugin, "CLDAPNode: Failed %s Authentication for %s", pMethod, (defaults.authzid ? defaults.authzid : defaults.authcid) );
}
if( siResult == eDSNoErr )
{
DBGLOG2( kLogPlugin, "CLDAPNode: Successful %s Authentication for %s", pMethod, (defaults.authzid ? defaults.authzid : defaults.authcid) );
}
DSFree( pMethod );
}
}
else
{
DBGLOG( kLogPlugin, "CLDAPNode: No SASL methods found for server." );
}
if( siResult == LDAP_OTHER || siResult == LDAP_LOCAL_ERROR )
{
if( pConfig->bIsSSL )
{
iReqSecurity &= (kSecManInMiddle | kSecPacketSigning);
}
iFinalSecurity = (~iFinalSecurity);
}
}
else {
DBGLOG( kLogPlugin, "CLDAPNode: Skipping SASL methods for server." );
}
if( dn != NULL )
{
ldap_value_free( dn );
dn = NULL;
}
DSFree( defaults.authzid );
DSCFRelease( cfSupportedMethods );
}
if( (iReqSecurity & iFinalSecurity) != iReqSecurity &&
DSIsStringEmpty(ldapAcct) == false && DSIsStringEmpty(ldapPasswd) == false )
{
uInt32 iMissing = (~iFinalSecurity) & iReqSecurity;
char pRequirements[128] = { 0 };
if( (iMissing & kSecDisallowCleartext) == kSecDisallowCleartext )
{
strcat( pRequirements, "No ClearText, " );
}
if( (iMissing & kSecManInMiddle) == kSecManInMiddle )
{
strcat( pRequirements, "Man-In-The-Middle, " );
}
if( (iMissing & kSecPacketSigning) == kSecPacketSigning )
{
strcat( pRequirements, "Packet Signing, " );
}
if( (iMissing & kSecPacketEncryption) == kSecPacketEncryption )
{
strcat( pRequirements, "Packet Encryption" );
}
else if( strlen(pRequirements) )
{
pRequirements[strlen(pRequirements)-2] = 0; }
DBGLOG2( kLogPlugin, "CLDAPNode: Required Policies not Supported: %s. LDAP Connection for Node %s denied.", pRequirements, pConfig->fNodeName );
syslog( LOG_ALERT,"DSLDAPv3PlugIn: Required Policies not Supported: %s. LDAP Connection for Node %s denied.", pRequirements, pConfig->fNodeName );
siResult = LDAP_STRONG_AUTH_REQUIRED;
}
return siResult;
}
void *checkFailedServers( void *data )
{
CLDAPNode *ldapNode = (CLDAPNode *)data;
ldapNode->CheckFailed();
CLDAPNode::fCheckThreadActive = false;
return NULL;
}
void LogFailedConnection(const char *inTag, const char *inServerName, int inDisabledIntervalDuration)
{
if ((inTag != nil) && (inServerName != nil))
{
syslog(LOG_ALERT,"%s: During an attempt to bind to [%s] LDAP server.", inTag, inServerName);
syslog(LOG_ALERT,"%s: Disabled future attempts to bind to [%s] LDAP server for next %d seconds.", inTag, inServerName, inDisabledIntervalDuration);
DBGLOG2( kLogPlugin, "CLDAPNode: Disabled future attempts to bind to LDAP server %s for %d seconds", inServerName, inDisabledIntervalDuration );
}
else
{
syslog(LOG_ALERT,"%s: Logging Failed LDAP connection with incomplete data", (inTag ? inTag : "unknown"));
DBGLOG( kLogPlugin, "CLDAPNode: Failed LDAP connection" );
}
}
void SetSockList(int *inSockList, int inSockCount, bool inClose)
{
for (int iCount = 0; iCount < inSockCount; iCount++)
{
if ( (inClose) && (inSockList[iCount] >= 0) )
{
close(inSockList[iCount]);
}
inSockList[iCount] = -1;
}
}
#pragma mark -
#pragma mark CLDAPNode Functions
CLDAPNode::CLDAPNode ( void )
{
fSupportedSASLMethods = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
CFArrayAppendValue( fSupportedSASLMethods, CFSTR("CRAM-MD5") );
CFArrayAppendValue( fSupportedSASLMethods, CFSTR("GSSAPI") );
fInStartupState = ( gProcessPID < 100 );
}
CLDAPNode::~CLDAPNode ( void )
{
CFRelease( fSupportedSASLMethods );
fSupportedSASLMethods = NULL;
}
double gNodeOpenMutexHeldStartTime;
void CLDAPNode::NodeOpenMutexWaitWithFunctionName( const char* callingFunction, bool waitForCheckFailedThreadToComplete )
{
#ifdef LOG_LDAPNodeOpenMutex_Attempts
double startTime, callTime;
startTime = dsTimestamp();
DBGLOG3( kLogPlugin, "T[%X] %s called, waiting for fLDAPNodeOpenMutex [%X]", pthread_self(), callingFunction, &fLDAPNodeOpenMutex );
#endif
if ( waitForCheckFailedThreadToComplete && fCheckThreadActive )
{
DSSemaphore timedWait;
while(fCheckThreadActive)
{
timedWait.Wait( (uInt32)(.5 * kMilliSecsPerSec) );
}
}
fLDAPNodeOpenMutex.Wait();
gNodeOpenMutexHeldStartTime = dsTimestamp();
#ifdef LOG_LDAPNodeOpenMutex_Attempts
callTime = (gNodeOpenMutexHeldStartTime - startTime)/USEC_PER_SEC;
DBGLOG4( kLogPlugin, "T[%X] %s, got fLDAPNodeOpenMutex [%X] after %f seconds", pthread_self(), callingFunction, &fLDAPNodeOpenMutex, callTime );
#endif
}
void CLDAPNode::NodeOpenMutexSignalWithFunctionName( const char* callingFunction )
{
double callTime;
callTime = (dsTimestamp() - gNodeOpenMutexHeldStartTime)/USEC_PER_SEC;
if ( callTime > kMinTimeToLogLockBeingHeld )
{
DBGLOG4( kLogPlugin, "T[%X] %s, held fLDAPNodeOpenMutex [%X] for %f seconds", pthread_self(), callingFunction, &fLDAPNodeOpenMutex, callTime );
}
#ifdef LOG_LDAPNodeOpenMutex_Attempts
else
{
DBGLOG4( kLogPlugin, "T[%X] %s, held fLDAPNodeOpenMutex [%X] for %f seconds", pthread_self(), callingFunction, &fLDAPNodeOpenMutex, callTime );
}
#endif
fLDAPNodeOpenMutex.Signal();
}
sInt32 CLDAPNode::SafeOpen ( char *inNodeName,
sLDAPNodeStruct **outLDAPNodeStruct )
{
sInt32 siResult = eDSNoErr;
sLDAPNodeStruct *pLDAPNodeStruct = nil;
sLDAPConfigData *pConfig = nil;
int ldapPort = LDAP_PORT;
LDAPNodeMapI aLDAPNodeMapI;
uInt32 openTO = kLDAPDefaultOpenCloseTimeoutInSeconds;
uInt32 idleTO = 0;
uInt32 rebindRetry = 0;
string aNodeName(inNodeName);
bool bGetServerMappings = false;
bool bGetSecuritySettings = false;
bool bBuildReplicaList = false;
char *configNodeName = NULL;
CLDAPv3Plugin::WaitForNetworkTransitionToFinish();
NodeOpenMutexWait();
pConfig = gpConfigFromXML->ConfigWithNodeNameLock( inNodeName );
if (pConfig != nil)
{
openTO = pConfig->fOpenCloseTimeout;
idleTO = pConfig->fIdleTimeout;
rebindRetry = pConfig->fDelayRebindTry;
ldapPort = pConfig->fServerPort;
bGetServerMappings = pConfig->bGetServerMappings;
bGetSecuritySettings = pConfig->bGetSecuritySettings;
bBuildReplicaList = pConfig->bBuildReplicaList;
configNodeName = strdup(pConfig->fNodeName);
gpConfigFromXML->ConfigUnlock( pConfig );
}
aLDAPNodeMapI = fLDAPNodeMap.find(aNodeName);
if (aLDAPNodeMapI == fLDAPNodeMap.end())
{
pLDAPNodeStruct = new sLDAPNodeStruct;
if (pConfig != nil)
{
if (configNodeName != nil)
{
pLDAPNodeStruct->fNodeName = configNodeName;
configNodeName = NULL;
pLDAPNodeStruct->fIdleTO = idleTO;
pLDAPNodeStruct->fDelayRebindTry = rebindRetry;
} else
{
pLDAPNodeStruct->fNodeName = strdup( inNodeName );
}
}
if (pConfig == nil)
{
char *aLDAPName = nil;
siResult = ParseLDAPNodeName( inNodeName, &aLDAPName, &ldapPort );
if (siResult == eDSNoErr)
{
pLDAPNodeStruct->fDirectServerName = aLDAPName;
pLDAPNodeStruct->fDirectLDAPPort = ldapPort;
}
}
fLDAPNodeMap[aNodeName] = pLDAPNodeStruct;
}
else
{
pLDAPNodeStruct = aLDAPNodeMapI->second;
pLDAPNodeStruct->ChangeRefCountBy( 1 ); }
if( *outLDAPNodeStruct )
{
(*outLDAPNodeStruct)->ChangeRefCountBy( -1 );
}
*outLDAPNodeStruct = pLDAPNodeStruct;
NodeOpenMutexSignal();
if (siResult == eDSNoErr)
{
if (pLDAPNodeStruct->fConnectionStatus == kConnectionUnknown)
{
EnsureCheckFailedConnectionsThreadIsRunning();
DSSemaphore timedWait;
double waitTime = dsTimestamp() + USEC_PER_SEC*openTO;
while(pLDAPNodeStruct->fConnectionStatus == kConnectionUnknown)
{
timedWait.Wait( (uInt32)(.5 * kMilliSecsPerSec) );
if ( dsTimestamp() > waitTime )
{
break;
}
}
}
CLDAPv3Plugin::WaitForNetworkTransitionToFinish();
if (pLDAPNodeStruct->fConnectionStatus != kConnectionSafe)
{
siResult = eDSCannotAccessSession;
}
if (siResult == eDSNoErr)
{
pLDAPNodeStruct->SessionMutexWait();
CheckSASLMethods( pLDAPNodeStruct );
if( bBuildReplicaList && bGetServerMappings )
{
pConfig = gpConfigFromXML->ConfigWithNodeNameLock( inNodeName );
pConfig->bBuildReplicaList = false;
gpConfigFromXML->ConfigUnlock( pConfig );
}
siResult = BindProc( pLDAPNodeStruct );
if( bBuildReplicaList && bGetServerMappings )
{
pConfig = gpConfigFromXML->ConfigWithNodeNameLock( inNodeName );
pConfig->bBuildReplicaList = true;
gpConfigFromXML->ConfigUnlock( pConfig );
}
pLDAPNodeStruct->fConnectionActiveCount++;
if( pLDAPNodeStruct->fHost )
{
bool bDoRebind = false;
if( bGetServerMappings )
{
RetrieveServerMappingsIfRequired( pLDAPNodeStruct );
bDoRebind = true; }
if( bGetSecuritySettings )
{
pConfig = gpConfigFromXML->ConfigWithNodeNameLock( inNodeName );
if (pConfig != nil)
{
if( gpConfigFromXML->UpdateConfigWithSecuritySettings( pLDAPNodeStruct->fNodeName, pConfig, pLDAPNodeStruct->fHost ) == eDSNoErr )
{
bDoRebind = true;
}
gpConfigFromXML->ConfigUnlock( pConfig );
}
}
if( bDoRebind )
{
ldap_unbind_ext( pLDAPNodeStruct->fHost, NULL, NULL );
pLDAPNodeStruct->fHost = nil;
siResult = BindProc( pLDAPNodeStruct );
}
}
pLDAPNodeStruct->fConnectionActiveCount--;
pLDAPNodeStruct->SessionMutexSignal();
}
}
DSFreeString( configNodeName );
return(siResult);
}
sInt32 CLDAPNode::AuthOpen ( char *inLDAPUserName,
char *inKerberosId,
void *inLDAPCredentials,
uInt32 inLDAPCredentialsLen,
char *inLDAPAuthType,
sLDAPNodeStruct **outLDAPNodeStruct )
{
sInt32 siResult = eDSAuthFailed;
sLDAPNodeStruct *pLDAPAuthNodeStruct = nil;
sLDAPNodeStruct *inLDAPNodeStruct = *outLDAPNodeStruct; char timestamp[128] = { 0, };
string aNodeName(inLDAPNodeStruct->fNodeName ? inLDAPNodeStruct->fNodeName : inLDAPNodeStruct->fDirectServerName );
snprintf( timestamp, sizeof(timestamp), "%lld", mach_absolute_time() );
aNodeName += ":";
aNodeName += inLDAPUserName;
aNodeName += ":";
aNodeName += timestamp;
inLDAPNodeStruct->SessionMutexWait();
pLDAPAuthNodeStruct = new sLDAPNodeStruct( *inLDAPNodeStruct );
inLDAPNodeStruct->SessionMutexSignal();
pLDAPAuthNodeStruct->bAuthCallActive = true;
pLDAPAuthNodeStruct->fLDAPUserName = (inLDAPUserName ? strdup( inLDAPUserName ) : nil);
pLDAPAuthNodeStruct->fLDAPAuthType = (inLDAPAuthType ? strdup( inLDAPAuthType ) : nil);
pLDAPAuthNodeStruct->fKerberosId = (inKerberosId ? strdup( inKerberosId ) : nil);
if( inLDAPCredentials && inLDAPCredentialsLen )
{
pLDAPAuthNodeStruct->fLDAPCredentials = calloc( inLDAPCredentialsLen + 1, 1 );
bcopy( inLDAPCredentials, pLDAPAuthNodeStruct->fLDAPCredentials, inLDAPCredentialsLen );
pLDAPAuthNodeStruct->fLDAPCredentialsLen = inLDAPCredentialsLen;
}
siResult = BindProc( pLDAPAuthNodeStruct, false, false, true );
if( eDSNoErr == siResult )
{
inLDAPNodeStruct->ChangeRefCountBy( -1 );
*outLDAPNodeStruct = pLDAPAuthNodeStruct;
NodeOpenMutexWait();
fLDAPNodeMap[aNodeName] = pLDAPAuthNodeStruct;
NodeOpenMutexSignal();
}
else {
delete pLDAPAuthNodeStruct;
pLDAPAuthNodeStruct = nil;
}
return( siResult );
}
void CLDAPNode::ForcedSafeClose( const char *inNodeName )
{
sLDAPNodeStruct *pLDAPNodeStruct = nil;
LDAPNodeMapI aLDAPNodeMapI;
LDAPNodeMap aLDAPNodeMap;
stack<string> unusedStack;
NodeOpenMutexWait();
for (aLDAPNodeMapI = fLDAPNodeMap.begin(); aLDAPNodeMapI != fLDAPNodeMap.end(); ++aLDAPNodeMapI)
{
pLDAPNodeStruct = aLDAPNodeMapI->second;
pLDAPNodeStruct->SessionMutexWait();
if( (inNodeName || strcmp(inNodeName, pLDAPNodeStruct->fNodeName) == 0) && pLDAPNodeStruct->fRefCount == 0 )
{
if( pLDAPNodeStruct->fHost )
{
ldap_unbind( pLDAPNodeStruct->fHost ); pLDAPNodeStruct->fHost = NULL;
}
unusedStack.push( aLDAPNodeMapI->first );
}
pLDAPNodeStruct->SessionMutexSignal();
}
while( unusedStack.empty() == false )
{
aLDAPNodeMapI = fLDAPNodeMap.find( unusedStack.top() );
if( aLDAPNodeMapI != fLDAPNodeMap.end() )
{
DBGLOG1( kLogPlugin, "CLDAPNode: Force removing Node: %s -- References: 0 -- from table", aLDAPNodeMapI->first.c_str() );
DSDelete( aLDAPNodeMapI->second );
fLDAPNodeMap.erase( unusedStack.top() );
}
unusedStack.pop();
}
NodeOpenMutexSignal();
}
sInt32 CLDAPNode::RebindSession( sLDAPNodeStruct *pLDAPNodeStruct )
{
sInt32 siResult = eDSNoErr;
CLDAPv3Plugin::WaitForNetworkTransitionToFinish();
NodeOpenMutexWait(); NodeOpenMutexSignal();
pLDAPNodeStruct->SessionMutexWait();
if (pLDAPNodeStruct->fHost != nil)
{
ldap_unbind(pLDAPNodeStruct->fHost);
pLDAPNodeStruct->fHost = nil;
}
siResult = BindProc( pLDAPNodeStruct, false, false, pLDAPNodeStruct->bAuthCallActive );
if (siResult == eDSNoErr)
{
if ( pLDAPNodeStruct->fConnectionStatus != kConnectionSafe )
gSrvrCntl->NodeSearchPolicyChanged();
pLDAPNodeStruct->fConnectionStatus = kConnectionSafe;
RetrieveServerMappingsIfRequired( pLDAPNodeStruct );
}
pLDAPNodeStruct->SessionMutexSignal();
return(siResult);
}
sInt32 CLDAPNode::SimpleAuth( sLDAPNodeStruct *inLDAPNodeStruct,
char *inLDAPUserName,
void *inLDAPCredentials,
uInt32 inLDAPCredentialsLen,
char *inKerberosId )
{
sInt32 siResult = eDSNoErr;
inLDAPNodeStruct->SessionMutexWait();
sLDAPNodeStruct *pLDAPAuthNodeStruct = new sLDAPNodeStruct( *inLDAPNodeStruct );
inLDAPNodeStruct->SessionMutexSignal();
pLDAPAuthNodeStruct->bAuthCallActive = true;
pLDAPAuthNodeStruct->fLDAPUserName = (inLDAPUserName ? strdup( inLDAPUserName ) : nil);
pLDAPAuthNodeStruct->fKerberosId = (inKerberosId ? strdup( inKerberosId ) : nil);
if( inLDAPCredentials && inLDAPCredentialsLen )
{
pLDAPAuthNodeStruct->fLDAPCredentials = calloc( inLDAPCredentialsLen + 1, 1 );
bcopy( inLDAPCredentials, pLDAPAuthNodeStruct->fLDAPCredentials, inLDAPCredentialsLen );
pLDAPAuthNodeStruct->fLDAPCredentialsLen = inLDAPCredentialsLen;
}
siResult = BindProc( pLDAPAuthNodeStruct, false, true, false );
if (siResult != eDSNoErr)
{
siResult = eDSAuthFailed;
}
DSDelete( pLDAPAuthNodeStruct );
return(siResult);
}
void CLDAPNode::GetSchema ( sLDAPContextData *inContext )
{
sInt32 siResult = eDSNoErr;
sLDAPConfigData *pConfig = nil;
LDAPMessage *LDAPResult = nil;
BerElement *ber = nil;
struct berval **bValues = nil;
char *pAttr = nil;
sObjectClassSchema *aOCSchema = nil;
bool bSkipToTag = true;
char *lineEntry = nil;
char *strtokContext = nil;
LDAP *aHost = nil;
if ( inContext != nil )
{
aHost = LockSession(inContext);
if ( aHost != nil ) {
pConfig = gpConfigFromXML->ConfigWithNodeNameLock( inContext->fNodeName );
if (pConfig != nil && !(pConfig->bOCBuilt)) {
siResult = GetSchemaMessage( aHost, pConfig->fSearchTimeout, &LDAPResult);
if (siResult == eDSNoErr)
{
for ( pAttr = ldap_first_attribute (aHost, LDAPResult, &ber );
pAttr != NULL; pAttr = ldap_next_attribute(aHost, LDAPResult, ber ) )
{
if (( bValues = ldap_get_values_len (aHost, LDAPResult, pAttr )) != NULL)
{
ObjectClassMap *aOCClassMap = new(ObjectClassMap);
for (int i = 0; bValues[i] != NULL; i++ )
{
aOCSchema = nil;
if (lineEntry != nil) {
DSFree(lineEntry);
}
lineEntry = (char *)calloc(1,bValues[i]->bv_len+1);
strcpy(lineEntry, bValues[i]->bv_val);
char *aToken = nil;
aToken = strtok_r(lineEntry,OCSEPCHARS, &strtokContext);
while ( (aToken != nil) && (strcmp(aToken,"NAME") != 0) )
{
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
}
if (aToken != nil)
{
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken != nil)
{
if (aOCClassMap->find(aToken) == aOCClassMap->end())
{
aOCSchema = new(sObjectClassSchema);
(*aOCClassMap)[aToken] = aOCSchema;
}
}
}
if (aOCSchema == nil)
{
continue;
}
if (aToken == nil)
{
continue;
}
bSkipToTag = true;
while (bSkipToTag)
{
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
break;
}
bSkipToTag = IsTokenNotATag(aToken);
if (bSkipToTag)
{
aOCSchema->fOtherNames.insert(aOCSchema->fOtherNames.begin(),aToken);
}
}
if (aToken == nil)
{
continue;
}
if (strcmp(aToken,"DESC") == 0)
{
bSkipToTag = true;
while (bSkipToTag)
{
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
break;
}
bSkipToTag = IsTokenNotATag(aToken);
}
if (aToken == nil)
{
continue;
}
}
if (strcmp(aToken,"OBSOLETE") == 0)
{
bSkipToTag = true;
while (bSkipToTag)
{
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
break;
}
bSkipToTag = IsTokenNotATag(aToken);
}
if (aToken == nil)
{
continue;
}
}
if (strcmp(aToken,"SUP") == 0)
{
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
continue;
}
aOCSchema->fParentOCs.insert(aOCSchema->fParentOCs.begin(),aToken);
bSkipToTag = true;
while (bSkipToTag)
{
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
break;
}
bSkipToTag = IsTokenNotATag(aToken);
if (bSkipToTag)
{
aOCSchema->fParentOCs.insert(aOCSchema->fParentOCs.begin(),aToken);
}
}
if (aToken == nil)
{
continue;
}
}
if (strcmp(aToken,"ABSTRACT") == 0)
{
aOCSchema->fType = 0;
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
continue;
}
}
if (strcmp(aToken,"STRUCTURAL") == 0)
{
aOCSchema->fType = 1;
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
continue;
}
}
if (strcmp(aToken,"AUXILIARY") == 0)
{
aOCSchema->fType = 2;
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
continue;
}
}
if (strcmp(aToken,"MUST") == 0)
{
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
continue;
}
aOCSchema->fRequiredAttrs.insert(aOCSchema->fRequiredAttrs.begin(),aToken);
bSkipToTag = true;
while (bSkipToTag)
{
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
break;
}
bSkipToTag = IsTokenNotATag(aToken);
if (bSkipToTag)
{
aOCSchema->fRequiredAttrs.insert(aOCSchema->fRequiredAttrs.begin(),aToken);
}
}
if (aToken == nil)
{
continue;
}
}
if (strcmp(aToken,"MAY") == 0)
{
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
continue;
}
aOCSchema->fAllowedAttrs.insert(aOCSchema->fAllowedAttrs.begin(),aToken);
bSkipToTag = true;
while (bSkipToTag)
{
aToken = strtok_r(NULL,OCSEPCHARS, &strtokContext);
if (aToken == nil)
{
break;
}
bSkipToTag = IsTokenNotATag(aToken);
if (bSkipToTag)
{
aOCSchema->fAllowedAttrs.insert(aOCSchema->fAllowedAttrs.begin(),aToken);
}
}
if (aToken == nil)
{
continue;
}
}
}
if (lineEntry != nil) {
DSFree(lineEntry);
}
ldap_value_free_len(bValues);
pConfig->fObjectClassSchema = aOCClassMap;
}
if (pAttr != nil)
{
ldap_memfree( pAttr );
}
}
if (ber != nil)
{
ber_free( ber, 0 );
}
ldap_msgfree( LDAPResult );
pConfig->bOCBuilt = true;
}
}
if( pConfig != nil )
{
gpConfigFromXML->ConfigUnlock( pConfig );
}
UnLockSession(inContext);
}
}
}
sInt32 CLDAPNode::ParseLDAPNodeName( char *inNodeName,
char **outLDAPName,
int *outLDAPPort )
{
sInt32 siResult = eDSNoErr;
char *portPos = nil;
uInt32 inLength = 0;
int ldapPort = LDAP_PORT;
char *ldapName = nil;
if (inNodeName != nil)
{
inLength = strlen(inNodeName);
portPos = strchr(inNodeName, ':');
if (portPos != nil)
{
portPos++;
if (portPos != nil)
{
ldapPort = strtoul(portPos,NULL,0);
if (ldapPort == 0)
{
ldapPort = LDAP_PORT;
}
inLength = inLength - strlen(portPos);
}
ldapName = (char *) calloc(1, inLength);
strncpy(ldapName, inNodeName, inLength-1);
}
else
{
ldapName = (char *) calloc(1, inLength+1);
strncpy(ldapName, inNodeName, inLength);
}
*outLDAPName = ldapName;
*outLDAPPort = ldapPort;
}
else
{
siResult = eDSNullParameter;
}
return(siResult);
}
void CLDAPNode::RereadDefinedReplicas( sLDAPNodeStruct *inLDAPNodeStruct )
{
sLDAPConfigData *pConfig = nil;
if (inLDAPNodeStruct != NULL)
{
pConfig = gpConfigFromXML->ConfigWithNodeNameLock( inLDAPNodeStruct->fNodeName );
if( pConfig != nil && !(pConfig->bLDAPv2ReadOnly) )
pConfig->bBuildReplicaList = true;
if( pConfig != NULL )
gpConfigFromXML->ConfigUnlock( pConfig );
}
}
sInt32 CLDAPNode::BindProc ( sLDAPNodeStruct *inLDAPNodeStruct, bool bForceBind, bool bCheckPasswordOnly, bool bNeedWriteable )
{
sInt32 siResult = eDSNoErr;
int bindMsgId = 0;
int version = -1;
char *ldapPasswd = nil;
sLDAPConfigData *pConfig = nil;
int openTO = kLDAPDefaultOpenCloseTimeoutInSeconds;
LDAPMessage *result = nil;
int ldapReturnCode = 0;
bool bLDAPv2ReadOnly = false;
try
{
if ( inLDAPNodeStruct == nil ) throw( (sInt32)eDSNullParameter );
if ( inLDAPNodeStruct->bBadSession ) throw( (sInt32)eDSCannotAccessSession );
if ( inLDAPNodeStruct->fConnectionStatus == kConnectionUnsafe && bForceBind == false ) throw( (sInt32)eDSCannotAccessSession );
if ( inLDAPNodeStruct->fConnectionStatus == kConnectionUnknown && bForceBind == false && fInStartupState == true )
{
DSSemaphore timedWait;
timedWait.Wait( (uInt32)(1 * kMilliSecsPerSec) );
if ( inLDAPNodeStruct->fConnectionStatus == kConnectionUnknown && fInStartupState == true )
throw( (sInt32)eDSCannotAccessSession );
}
CLDAPv3Plugin::WaitForNetworkTransitionToFinish();
inLDAPNodeStruct->SessionMutexWait();
if (inLDAPNodeStruct->fHost == NULL)
{
pConfig = gpConfigFromXML->ConfigWithNodeNameLock( inLDAPNodeStruct->fNodeName );
if (pConfig != nil)
{
if( !inLDAPNodeStruct->bAuthCallActive )
{
DSDelete( inLDAPNodeStruct->fLDAPUserName );
DSDelete( inLDAPNodeStruct->fKerberosId );
DSDelete( inLDAPNodeStruct->fLDAPAuthType );
DSFree( inLDAPNodeStruct->fLDAPCredentials );
if ( pConfig->bSecureUse )
{
inLDAPNodeStruct->fLDAPUserName = (pConfig->fServerAccount ? strdup(pConfig->fServerAccount) : nil);
inLDAPNodeStruct->fKerberosId = (pConfig->fKerberosId ? strdup(pConfig->fKerberosId) : nil);
inLDAPNodeStruct->fLDAPCredentials = (void *) (pConfig->fServerPassword ? strdup( pConfig->fServerPassword ) : NULL);
inLDAPNodeStruct->fLDAPCredentialsLen = (pConfig->fServerPassword ? strlen( pConfig->fServerPassword ) : 0);
}
}
openTO = pConfig->fOpenCloseTimeout;
}
if (inLDAPNodeStruct->fLDAPCredentials != nil)
{
if ( inLDAPNodeStruct->fLDAPCredentials && (inLDAPNodeStruct->fLDAPAuthType == nil || strcmp(inLDAPNodeStruct->fLDAPAuthType,kDSStdAuthClearText) == 0) )
{
ldapPasswd = strdup( (char *) inLDAPNodeStruct->fLDAPCredentials );
}
}
if( !bForceBind && inLDAPNodeStruct->fConnectionStatus == kConnectionUnknown )
{
EnsureCheckFailedConnectionsThreadIsRunning();
inLDAPNodeStruct->SessionMutexSignal();
DSSemaphore timedWait;
double waitTime = dsTimestamp() + USEC_PER_SEC*openTO;
while(inLDAPNodeStruct->fConnectionStatus == kConnectionUnknown)
{
timedWait.Wait( (uInt32)(.5 * kMilliSecsPerSec) );
if ( dsTimestamp() > waitTime )
{
break;
}
}
CLDAPv3Plugin::WaitForNetworkTransitionToFinish();
inLDAPNodeStruct->SessionMutexWaitWithFunctionName("CLDAPNode::BindProc(2)");
if (pConfig != nil && pConfig->fReplicaHosts)
pConfig->fReplicaHosts->resetLastUsed();
}
if( !bForceBind && inLDAPNodeStruct->fConnectionStatus != kConnectionSafe )
{
throw( (sInt32)eDSCannotAccessSession );
}
if (inLDAPNodeStruct->fNodeName != NULL )
{
inLDAPNodeStruct->fHost = InitLDAPConnection( inLDAPNodeStruct, pConfig, bNeedWriteable ); }
else if( inLDAPNodeStruct->fDirectServerName )
{
inLDAPNodeStruct->fHost = ldap_init( inLDAPNodeStruct->fDirectServerName, inLDAPNodeStruct->fDirectLDAPPort );
inLDAPNodeStruct->setLastLDAPServer( inLDAPNodeStruct->fDirectServerName );
SetNetworkTimeoutsForHost( inLDAPNodeStruct->fHost, kLDAPDefaultNetworkTimeoutInSeconds );
}
if ( inLDAPNodeStruct->fHost == nil )
{
if( !bCheckPasswordOnly )
{
if (bForceBind)
{
inLDAPNodeStruct->fConnectionStatus = kConnectionUnsafe;
DBGLOG( kLogPlugin, "CLDAPNode: BindProc SETTING kConnectionUnsafe 1" );
}
else
{
LogFailedConnection("InitLDAPConnection or ldap_init failure", inLDAPNodeStruct->fLDAPServer, inLDAPNodeStruct->fDelayRebindTry);
inLDAPNodeStruct->fConnectionStatus = kConnectionUnknown;
}
inLDAPNodeStruct->fDelayedBindTime = time( nil ) + inLDAPNodeStruct->fDelayRebindTry;
}
throw( (sInt32)eDSCannotAccessSession );
}
else
{
if (pConfig != nil)
{
if ( pConfig->bIsSSL && !(pConfig->bLDAPv2ReadOnly) )
{
int ldapOptVal = LDAP_OPT_X_TLS_HARD;
ldap_set_option(inLDAPNodeStruct->fHost, LDAP_OPT_X_TLS, &ldapOptVal);
}
if ( pConfig->bLDAPv2ReadOnly ) bLDAPv2ReadOnly = true;
ldap_set_option(inLDAPNodeStruct->fHost, LDAP_OPT_REFERRALS, (pConfig->bReferrals ? LDAP_OPT_ON : LDAP_OPT_OFF) );
}
if (!bLDAPv2ReadOnly)
{
CheckSASLMethods( inLDAPNodeStruct );
version = LDAP_VERSION3;
ldap_set_option( inLDAPNodeStruct->fHost, LDAP_OPT_PROTOCOL_VERSION, &version );
ldapReturnCode = doSASLBindAttemptIfPossible( inLDAPNodeStruct->fHost, pConfig, inLDAPNodeStruct->fLDAPUserName, ldapPasswd, inLDAPNodeStruct->fKerberosId );
}
if( ldapReturnCode != LDAP_LOCAL_ERROR && ldapReturnCode != LDAP_OTHER && !bLDAPv2ReadOnly )
{
if( ldapReturnCode == LDAP_STRONG_AUTH_REQUIRED )
{
if( bCheckPasswordOnly )
{
DBGLOG( kLogPlugin, "CLDAPNode: Node Authentication failed" );
throw( (sInt32) eDSAuthFailed );
}
if( inLDAPNodeStruct->bAuthCallActive == false )
{
inLDAPNodeStruct->fConnectionStatus = kConnectionUnsafe;
DBGLOG( kLogPlugin, "CLDAPNode: BindProc SETTING kConnectionUnsafe 2" );
inLDAPNodeStruct->fDelayedBindTime = time( nil ) + 3600;
syslog(LOG_ALERT,"DSLDAPv3PlugIn: Policy Violation. Disabled future attempts to bind to [%s] for 1 hour.", inLDAPNodeStruct->fLDAPServer );
DBGLOG1( kLogPlugin, "CLDAPNode: Policy Violation. Disabled future attempts to bind to [%s] for 1 hour.", inLDAPNodeStruct->fLDAPServer );
}
throw( (sInt32)eDSCannotAccessSession );
}
else if( ldapReturnCode != LDAP_SUCCESS )
{
if( bCheckPasswordOnly )
{
DBGLOG( kLogPlugin, "CLDAPNode: Node Authentication failed" );
throw( (sInt32) eDSAuthFailed );
}
if( ldapReturnCode == LDAP_INVALID_CREDENTIALS )
{
inLDAPNodeStruct->bBadSession = true;
DBGLOG( kLogPlugin, "CLDAPNode: Failed doing SASL Authentication - bad credentials" );
}
else
{
DBGLOG( kLogPlugin, "CLDAPNode: Failed doing SASL Authentication" );
}
if (bForceBind)
{
inLDAPNodeStruct->fConnectionStatus = kConnectionUnsafe;
DBGLOG( kLogPlugin, "CLDAPNode: BindProc SETTING kConnectionUnsafe 3" );
}
else
inLDAPNodeStruct->fConnectionStatus = kConnectionUnknown;
inLDAPNodeStruct->fDelayedBindTime = time( nil ) + inLDAPNodeStruct->fDelayRebindTry;
throw( (sInt32)eDSCannotAccessSession );
}
}
else if( pConfig == NULL || pConfig->bIsSSL || (pConfig->fSecurityLevel & kSecDisallowCleartext) == 0 ||
(pConfig->fSecurityLevel & kSecDisallowCleartext == kSecDisallowCleartext && (DSIsStringEmpty(inLDAPNodeStruct->fLDAPUserName) || inLDAPNodeStruct->fLDAPCredentialsLen == 0) ) )
{
timeval tv = { 0 };
char *pErrorString = NULL;
if (bLDAPv2ReadOnly)
{
DBGLOG( kLogPlugin, "CLDAPNode: SASL Authentication not attempted with LDAPv2, failing through to bind" );
}
else if ( DSIsStringEmpty(inLDAPNodeStruct->fLDAPUserName) == false && inLDAPNodeStruct->fLDAPCredentialsLen )
{
DBGLOG( kLogPlugin, "CLDAPNode: SASL Authentication didn't work, failing through to bind" );
}
if (inLDAPNodeStruct->fHost != nil)
{
SetNetworkTimeoutsForHost( inLDAPNodeStruct->fHost, kLDAPDefaultNetworkTimeoutInSeconds );
bindMsgId = ldap_simple_bind( inLDAPNodeStruct->fHost, inLDAPNodeStruct->fLDAPUserName, ldapPasswd );
tv.tv_sec = openTO;
ldapReturnCode = ldap_result( inLDAPNodeStruct->fHost, bindMsgId, 0, &tv, &result );
if ( ldapReturnCode == -1 )
{
pErrorString = "Bind failure - Server Down";
}
else if ( ldapReturnCode == 0 )
{
pErrorString = "Bind timeout";
}
else if ( ldap_result2error(inLDAPNodeStruct->fHost, result, 1) != LDAP_SUCCESS )
{
pErrorString = "Bind failure";
}
}
else
{
pErrorString = "Unable to obtain a LDAP handle";
}
if( pErrorString )
{
if( bCheckPasswordOnly )
{
DBGLOG( kLogPlugin, "CLDAPNode: Node Authentication failed" );
}
else
{
if (bForceBind)
{
inLDAPNodeStruct->fConnectionStatus = kConnectionUnsafe;
DBGLOG( kLogPlugin, "CLDAPNode: BindProc SETTING kConnectionUnsafe 4" );
if( DSIsStringEmpty(inLDAPNodeStruct->fLDAPUserName) || inLDAPNodeStruct->fLDAPCredentialsLen == 0 )
{
DBGLOG1( kLogPlugin, "CLDAPNode: Anonymous Bind Unsuccessful - Retry in %d seconds", inLDAPNodeStruct->fDelayRebindTry );
}
else
{
DBGLOG1( kLogPlugin, "CLDAPNode: Cleartext Bind Authentication Unsuccessful - Retry in %d seconds", inLDAPNodeStruct->fDelayRebindTry );
}
}
else
{
LogFailedConnection( pErrorString, inLDAPNodeStruct->fLDAPServer, inLDAPNodeStruct->fDelayRebindTry );
inLDAPNodeStruct->fConnectionStatus = kConnectionUnknown;
}
inLDAPNodeStruct->fDelayedBindTime = time( nil ) + inLDAPNodeStruct->fDelayRebindTry;
}
throw( (sInt32)eDSCannotAccessSession );
}
if( DSIsStringEmpty(inLDAPNodeStruct->fLDAPUserName) || inLDAPNodeStruct->fLDAPCredentialsLen == 0 )
{
DBGLOG( kLogPlugin, "CLDAPNode: Anonymous Bind Successful" );
}
else
{
DBGLOG( kLogPlugin, "CLDAPNode: Cleartext Bind Authentication Successful" );
}
}
else
{
DBGLOG1( kLogPlugin, "CLDAPNode: ClearText binds disallowed by Policy. LDAP Connection to server %s denied.", inLDAPNodeStruct->fLDAPServer );
syslog( LOG_ALERT,"DSLDAPv3PlugIn: ClearText binds disallowed by Policy. LDAP Connection to server %s denied.", inLDAPNodeStruct->fLDAPServer );
if( !bCheckPasswordOnly )
{
inLDAPNodeStruct->fConnectionStatus = kConnectionUnsafe;
DBGLOG( kLogPlugin, "CLDAPNode: BindProc SETTING kConnectionUnsafe 5" );
inLDAPNodeStruct->fDelayedBindTime = time( nil ) + (inLDAPNodeStruct->fDelayRebindTry ? inLDAPNodeStruct->fDelayRebindTry : kLDAPDefaultRebindTryTimeoutInSeconds);
}
throw( (sInt32)eDSCannotAccessSession );
}
if ( inLDAPNodeStruct->fConnectionStatus != kConnectionSafe )
gSrvrCntl->NodeSearchPolicyChanged();
inLDAPNodeStruct->fConnectionStatus = kConnectionSafe;
}
result = nil;
}
}
catch ( sInt32 err )
{
siResult = err;
}
if( siResult != eDSNoErr && inLDAPNodeStruct && inLDAPNodeStruct->fHost )
{
ldap_unbind_ext( inLDAPNodeStruct->fHost, NULL, NULL );
inLDAPNodeStruct->fHost = NULL;
}
DSDelete( ldapPasswd );
if( pConfig )
{
gpConfigFromXML->ConfigUnlock( pConfig );
}
if( inLDAPNodeStruct )
{
inLDAPNodeStruct->SessionMutexSignalWithFunctionName("CLDAPNode::BindProc(2)");
}
return (siResult);
}
char** CLDAPNode::GetNamingContexts( LDAP *inHost, int inSearchTO, uInt32 *outCount )
{
sInt32 siResult = eDSRecordNotFound;
bool bResultFound = false;
int ldapMsgId = 0;
LDAPMessage *result = nil;
int ldapReturnCode = 0;
char *attrs[2] = {"namingContexts",NULL};
BerElement *ber = nil;
struct berval **bValues = nil;
char *pAttr = nil;
char **outMapSearchBases = nil;
*outCount = 0;
ldapReturnCode = ldap_search_ext( inHost,
"",
LDAP_SCOPE_BASE,
"(objectclass=*)",
attrs,
0,
NULL,
NULL,
0, 0,
&ldapMsgId );
if (ldapReturnCode == LDAP_SUCCESS)
{
bResultFound = true;
struct timeval tv;
tv.tv_usec = 0;
if (inSearchTO == 0)
{
tv.tv_sec = kLDAPDefaultOpenCloseTimeoutInSeconds; }
else
{
tv.tv_sec = inSearchTO;
}
SetNetworkTimeoutsForHost( inHost, kLDAPDefaultNetworkTimeoutInSeconds );
ldapReturnCode = ldap_result(inHost, ldapMsgId, 0, &tv, &result);
}
if ( (bResultFound) &&
( ldapReturnCode == LDAP_RES_SEARCH_ENTRY ) )
{
pAttr = ldap_first_attribute (inHost, result, &ber );
if (pAttr != nil)
{
if (( bValues = ldap_get_values_len (inHost, result, pAttr )) != NULL)
{
uInt32 valCount = 0;
for (int ii = 0; bValues[ii] != nil; ii++ )
{
valCount++;
}
outMapSearchBases = (char **) calloc( valCount+1, sizeof(char *));
for (int i = 0; (bValues[i] != nil) && (bValues[i]->bv_val != nil); i++ )
{
outMapSearchBases[i] = strdup(bValues[i]->bv_val);
(*outCount)++;
siResult = eDSNoErr;
}
ldap_value_free_len(bValues);
} ldap_memfree( pAttr );
}
if (ber != nil)
{
ber_free( ber, 0 );
}
ldap_msgfree( result );
result = nil;
} else if (ldapReturnCode == LDAP_TIMEOUT)
{
siResult = eDSServerTimeout;
if ( result != nil )
{
ldap_msgfree( result );
result = nil;
outMapSearchBases = (char **) -1; }
}
else
{
siResult = eDSRecordNotFound;
if ( result != nil )
{
ldap_msgfree( result );
result = nil;
}
}
DSSearchCleanUp(inHost, ldapMsgId);
return( outMapSearchBases );
}
sInt32 CLDAPNode::GetSchemaMessage ( LDAP *inHost, int inSearchTO, LDAPMessage **outResultMsg )
{
sInt32 siResult = eDSNoErr;
bool bResultFound = false;
int ldapMsgId = 0;
LDAPMessage *result = nil;
int ldapReturnCode = 0;
char *sattrs[2] = {"subschemasubentry",NULL};
char *attrs[2] = {"objectclasses",NULL};
char *subschemaDN = nil;
BerElement *ber = nil;
struct berval **bValues = nil;
char *pAttr = nil;
try
{
if ( (ldapMsgId = ldap_search( inHost, "", LDAP_SCOPE_BASE, "(objectclass=*)", sattrs, 0) ) == -1 )
{
bResultFound = false;
}
else
{
bResultFound = true;
struct timeval tv;
tv.tv_usec = 0;
if (inSearchTO == 0)
{
tv.tv_sec = kLDAPDefaultSearchTimeoutInSeconds; }
else
{
tv.tv_sec = inSearchTO;
}
SetNetworkTimeoutsForHost( inHost, kLDAPDefaultNetworkTimeoutInSeconds );
ldapReturnCode = ldap_result(inHost, ldapMsgId, 0, &tv, &result);
}
if ( (bResultFound) &&
( ldapReturnCode == LDAP_RES_SEARCH_ENTRY ) )
{
siResult = eDSNoErr;
for ( pAttr = ldap_first_attribute (inHost, result, &ber );
pAttr != NULL; pAttr = ldap_next_attribute(inHost, result, ber ) )
{
if (( bValues = ldap_get_values_len (inHost, result, pAttr )) != NULL)
{
if ( bValues[0] != NULL )
{
subschemaDN = (char *) calloc(1, bValues[0]->bv_len + 1);
strcpy(subschemaDN,bValues[0]->bv_val);
}
ldap_value_free_len(bValues);
}
if (pAttr != nil)
{
ldap_memfree( pAttr );
}
}
if (ber != nil)
{
ber_free( ber, 0 );
}
ldap_msgfree( result );
result = nil;
} else if (ldapReturnCode == LDAP_TIMEOUT)
{
siResult = eDSServerTimeout;
if ( result != nil )
{
ldap_msgfree( result );
result = nil;
}
}
else
{
siResult = eDSRecordNotFound;
if ( result != nil )
{
ldap_msgfree( result );
result = nil;
}
}
DSSearchCleanUp(inHost, ldapMsgId);
if (subschemaDN != nil)
{
if ( (ldapMsgId = ldap_search( inHost, subschemaDN, LDAP_SCOPE_BASE, "(objectclass=subSchema)", attrs, 0) ) == -1 )
{
bResultFound = false;
}
else
{
bResultFound = true;
struct timeval tv;
tv.tv_usec = 0;
if (inSearchTO == 0)
{
tv.tv_sec = kLDAPDefaultSearchTimeoutInSeconds; }
else
{
tv.tv_sec = inSearchTO;
}
ldapReturnCode = ldap_result(inHost, ldapMsgId, 0, &tv, &result);
}
DSFree( subschemaDN );
if ( (bResultFound) &&
( ldapReturnCode == LDAP_RES_SEARCH_ENTRY ) )
{
siResult = eDSNoErr;
} else if (ldapReturnCode == LDAP_TIMEOUT)
{
siResult = eDSServerTimeout;
if ( result != nil )
{
ldap_msgfree( result );
result = nil;
}
}
else
{
siResult = eDSRecordNotFound;
if ( result != nil )
{
ldap_msgfree( result );
result = nil;
}
}
DSSearchCleanUp(inHost, ldapMsgId);
}
}
catch ( sInt32 err )
{
siResult = err;
}
if (result != nil)
{
*outResultMsg = result;
}
return( siResult );
}
bool CLDAPNode::IsTokenNotATag ( char *inToken )
{
if (inToken == nil)
{
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 );
}
LDAP* CLDAPNode::LockSession( sLDAPContextData *inContext )
{
LDAP *returnValue = nil;
if (inContext != nil)
{
sLDAPNodeStruct *pLDAPNodeStruct = inContext->fLDAPNodeStruct;
pLDAPNodeStruct->SessionMutexWait();
pLDAPNodeStruct->fConnectionActiveCount++;
pLDAPNodeStruct->fIdleTOCount = 0;
if( pLDAPNodeStruct->fHost == NULL )
{
BindProc( pLDAPNodeStruct, false, false, pLDAPNodeStruct->bAuthCallActive );
}
returnValue = pLDAPNodeStruct->fHost;
}
return returnValue;
}
void CLDAPNode::UnLockSession( sLDAPContextData *inContext, bool inHasFailed )
{
if (inContext != nil)
{
sLDAPNodeStruct *pLDAPNodeStruct = inContext->fLDAPNodeStruct;
pLDAPNodeStruct->fConnectionActiveCount--;
if (inHasFailed)
{
LogFailedConnection( "Search connection failure", pLDAPNodeStruct->fLDAPServer, pLDAPNodeStruct->fDelayRebindTry );
pLDAPNodeStruct->fConnectionStatus = kConnectionUnknown;
pLDAPNodeStruct->fDelayedBindTime = time( nil ) + inContext->fLDAPNodeStruct->fDelayRebindTry;
ldap_unbind_ext( pLDAPNodeStruct->fHost, NULL, NULL );
pLDAPNodeStruct->fHost = NULL;
}
pLDAPNodeStruct->SessionMutexSignal();
}
}
void CLDAPNode::CredentialChange( sLDAPNodeStruct *inLDAPNodeStruct, char *inUserDN )
{
LDAPNodeMapI aLDAPNodeMapI;
LDAPNodeMap aLDAPNodeMap;
string nodeString = inLDAPNodeStruct->fNodeName;
int length = 0;
char *pNodeName = NULL;
nodeString += ":";
nodeString += inUserDN;
length = nodeString.length();
pNodeName = (char *)nodeString.c_str();
NodeOpenMutexWait();
for (aLDAPNodeMapI = fLDAPNodeMap.begin(); aLDAPNodeMapI != fLDAPNodeMap.end(); ++aLDAPNodeMapI)
{
if( inLDAPNodeStruct != aLDAPNodeMapI->second &&
strncmp( pNodeName, aLDAPNodeMapI->first.c_str(), length ) == 0 )
{
aLDAPNodeMapI->second->bBadSession = true;
DBGLOG1( kLogPlugin, "CLDAPNode: Invalidated session %s due to credential change", aLDAPNodeMapI->first.c_str() );
}
}
NodeOpenMutexSignal();
}
void CLDAPNode::CheckIdles( void )
{
sLDAPNodeStruct *pLDAPNodeStruct = nil;
LDAPNodeMapI aLDAPNodeMapI;
bool bShouldWeCheck = false;
stack<string> unusedStack;
NodeOpenMutexWait();
for (aLDAPNodeMapI = fLDAPNodeMap.begin(); CLDAPv3Plugin::HandlingNetworkTransition() == false && aLDAPNodeMapI != fLDAPNodeMap.end(); ++aLDAPNodeMapI)
{
pLDAPNodeStruct = aLDAPNodeMapI->second;
if (pLDAPNodeStruct->fConnectionStatus != kConnectionSafe)
{
bShouldWeCheck = true;
}
pLDAPNodeStruct->SessionMutexWait();
if (pLDAPNodeStruct->fConnectionActiveCount == 0 && pLDAPNodeStruct->fHost) {
if (pLDAPNodeStruct->fIdleTOCount >= pLDAPNodeStruct->fIdleTO) {
ldap_unbind( pLDAPNodeStruct->fHost ); pLDAPNodeStruct->fHost = nil;
pLDAPNodeStruct->fIdleTOCount = 0;
DBGLOG2( kLogPlugin, "CLDAPNode: Status Node: %s -- Server: %s -- Idle Disconnected", aLDAPNodeMapI->first.c_str(), pLDAPNodeStruct->fLDAPServer );
}
else
{
pLDAPNodeStruct->fIdleTOCount++;
DBGLOG3( kLogPlugin, "CLDAPNode: Status Node: %s -- Server: %s - Time: %d sec -- Idle", aLDAPNodeMapI->first.c_str(), pLDAPNodeStruct->fLDAPServer, pLDAPNodeStruct->fIdleTOCount * 30 );
}
}
else if( pLDAPNodeStruct->fRefCount == 0 )
{
if( pLDAPNodeStruct->fConnectionStatus == kConnectionSafe && strchr(aLDAPNodeMapI->first.c_str(), ':') == NULL )
{
unusedStack.push( aLDAPNodeMapI->first );
}
else
{
sLDAPConfigData *pConfig = gpConfigFromXML->ConfigWithNodeNameLock( pLDAPNodeStruct->fNodeName );
if( pConfig )
{
gpConfigFromXML->ConfigUnlock( pConfig );
}
else
{
unusedStack.push( aLDAPNodeMapI->first );
}
}
}
else
{
pLDAPNodeStruct->fIdleTOCount = 0;
}
pLDAPNodeStruct->SessionMutexSignal();
}
while( unusedStack.empty() == false )
{
aLDAPNodeMapI = fLDAPNodeMap.find( unusedStack.top() );
if( aLDAPNodeMapI != fLDAPNodeMap.end() )
{
DBGLOG1( kLogPlugin, "CLDAPNode: Status Node: %s -- References: 0 -- removing from table", aLDAPNodeMapI->first.c_str() );
DSDelete( aLDAPNodeMapI->second );
fLDAPNodeMap.erase( unusedStack.top() );
}
unusedStack.pop();
}
NodeOpenMutexSignal();
if (bShouldWeCheck)
{
EnsureCheckFailedConnectionsThreadIsRunning();
}
}
void CLDAPNode::CheckFailed( void )
{
sLDAPNodeStruct *pLDAPNodeStruct = nil;
LDAPNodeMapI aLDAPNodeMapI;
LDAPNodeMap aLDAPNodeMap;
CLDAPv3Plugin::WaitForNetworkTransitionToFinish();
NodeOpenMutexWaitButNotForCheckFailedThread();
for (aLDAPNodeMapI = fLDAPNodeMap.begin(); CLDAPv3Plugin::HandlingNetworkTransition() == false && aLDAPNodeMapI != fLDAPNodeMap.end(); ++aLDAPNodeMapI)
{
pLDAPNodeStruct = aLDAPNodeMapI->second;
pLDAPNodeStruct->SessionMutexWait();
if( pLDAPNodeStruct->fConnectionStatus != kConnectionSafe ) {
if( pLDAPNodeStruct->fHost != nil )
{
ldap_unbind_ext( pLDAPNodeStruct->fHost, NULL, NULL );
pLDAPNodeStruct->fHost = NULL;
}
if( pLDAPNodeStruct->bBadSession == false )
aLDAPNodeMap[aLDAPNodeMapI->first] = pLDAPNodeStruct;
}
pLDAPNodeStruct->SessionMutexSignal();
}
NodeOpenMutexSignal();
CLDAPv3Plugin::WaitForNetworkTransitionToFinish();
for (aLDAPNodeMapI = aLDAPNodeMap.begin(); CLDAPv3Plugin::HandlingNetworkTransition() == false && aLDAPNodeMapI != aLDAPNodeMap.end(); ++aLDAPNodeMapI)
{
pLDAPNodeStruct = aLDAPNodeMapI->second;
if( pLDAPNodeStruct->fConnectionStatus == kConnectionUnknown || time( nil ) > pLDAPNodeStruct->fDelayedBindTime ) {
DBGLOG1( kLogPlugin, "CLDAPNode: Checking failed Node: %s", aLDAPNodeMapI->first.c_str() );
sInt32 bindStatus = BindProc( pLDAPNodeStruct, true );
if ( bindStatus )
DBGLOG2( kLogPlugin, "CLDAPNode: Checking failed Node: %s returned bindStatus: %d", aLDAPNodeMapI->first.c_str(), bindStatus );
}
}
fInStartupState = false;
}
void CLDAPNode::SystemGoingToSleep( void )
{
gBlockLDAPNetworkChange = true;
NodeOpenMutexWait();
gpConfigFromXML->SetAllConfigBuildReplicaFlagTrue();
if( fLDAPNodeMap.size() > 0 )
{
LDAPNodeMapI aLDAPNodeMapI;
for (aLDAPNodeMapI = fLDAPNodeMap.begin(); aLDAPNodeMapI != fLDAPNodeMap.end(); ++aLDAPNodeMapI)
{
sLDAPNodeStruct *pLDAPNodeStruct = aLDAPNodeMapI->second;
pLDAPNodeStruct->fConnectionStatus = kConnectionUnsafe;
DBGLOG( kLogPlugin, "CLDAPNode: SystemGoingToSleep SETTING kConnectionUnsafe 1" );
pLDAPNodeStruct->fDelayedBindTime = 0;
pLDAPNodeStruct->SessionMutexWait();
if ( pLDAPNodeStruct->fHost != nil ) {
ldap_unbind( pLDAPNodeStruct->fHost ); pLDAPNodeStruct->fHost = nil;
}
pLDAPNodeStruct->SessionMutexSignal();
}
}
NodeOpenMutexSignal();
}
void CLDAPNode::SystemWillPowerOn( void )
{
gBlockLDAPNetworkChange = false;
}
void CLDAPNode::NetTransition( void )
{
NodeOpenMutexWaitButNotForCheckFailedThread();
gpConfigFromXML->SetAllConfigBuildReplicaFlagTrue();
if( fLDAPNodeMap.size() > 0 )
{
LDAPNodeMapI aLDAPNodeMapI;
for (aLDAPNodeMapI = fLDAPNodeMap.begin(); aLDAPNodeMapI != fLDAPNodeMap.end(); ++aLDAPNodeMapI)
{
sLDAPNodeStruct *pLDAPNodeStruct = aLDAPNodeMapI->second;
pLDAPNodeStruct->fConnectionStatus = kConnectionUnknown;
pLDAPNodeStruct->fDelayedBindTime = 0;
pLDAPNodeStruct->SessionMutexWait();
if ( pLDAPNodeStruct->fHost != nil )
{
ldap_unbind( pLDAPNodeStruct->fHost ); pLDAPNodeStruct->fHost = nil;
}
pLDAPNodeStruct->SessionMutexSignal();
}
EnsureCheckFailedConnectionsThreadIsRunning();
}
NodeOpenMutexSignal();
}
LDAP* CLDAPNode::InitLDAPConnection( sLDAPNodeStruct *inLDAPNodeStruct, sLDAPConfigData *inConfig, bool bInNeedWriteable )
{
LDAP *outHost = NULL;
sReplicaInfo *inOutList = nil;
sInt32 replicaSearchResult = eDSNoErr;
if (inConfig == NULL)
return NULL;
if (inConfig->bBuildReplicaList)
{
if (inConfig->fReplicaHostnames == NULL)
{
inConfig->fReplicaHostnames = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
}
if( CFArrayGetCount(inConfig->fReplicaHostnames) == 0 )
{
CFStringRef cfServerString = CFStringCreateWithCString( NULL, inConfig->fServerName, kCFStringEncodingUTF8 );
CFArrayAppendValue(inConfig->fReplicaHostnames, cfServerString);
CFRelease( cfServerString );
cfServerString = NULL;
}
BuildReplicaInfoLinkList( &inOutList, inConfig->fReplicaHostnames, (inConfig->fWriteableHostnames ? inConfig->fWriteableHostnames : inConfig->fReplicaHostnames), inConfig->fServerPort );
replicaSearchResult = RetrieveDefinedReplicas(inLDAPNodeStruct, inConfig->fReplicaHostnames, inConfig->fWriteableHostnames, inConfig->fServerPort, &inOutList);
if ( replicaSearchResult == eDSNoErr || replicaSearchResult == eDSNoStdMappingAvailable || inConfig->fReplicaHosts == nil )
{
if( inOutList )
{
DSDelete( inConfig->fReplicaHosts );
inConfig->fReplicaHosts = inOutList;
}
}
if ( inOutList == NULL )
{
inConfig->bBuildReplicaList = true;
if (inConfig->fReplicaHosts != NULL)
{
DSDelete( inConfig->fReplicaHosts );
}
}
else if (inOutList->fAddrInfo == NULL)
{
sReplicaInfo *aPtr = inOutList->fNext;
bool bAtLeastOneAddr = false;
while (aPtr != NULL)
{
if (aPtr->fAddrInfo != NULL)
{
bAtLeastOneAddr = true;
break;
}
aPtr = aPtr->fNext;
}
if (bAtLeastOneAddr)
{
inConfig->bBuildReplicaList = false;
}
else
{
inConfig->bBuildReplicaList = true;
if (inConfig->fReplicaHosts != NULL)
{
if( inConfig->fReplicaHosts == inOutList ) {
inOutList = NULL;
}
DSDelete( inConfig->fReplicaHosts );
}
}
}
else
{
inConfig->bBuildReplicaList = false;
}
gpConfigFromXML->UpdateReplicaList(inConfig->fNodeName, inConfig->fReplicaHostnames, inConfig->fWriteableHostnames);
}
if( inConfig->fReplicaHosts == NULL && inConfig->fReplicaHostnames != NULL )
{
BuildReplicaInfoLinkList( &inOutList, inConfig->fReplicaHostnames, inConfig->fWriteableHostnames, inConfig->fServerPort );
}
if ( (replicaSearchResult != eDSCannotAccessSession) && (inLDAPNodeStruct->fHost == nil) )
{
if (inConfig->fReplicaHosts != nil)
{
outHost = EstablishConnection( inLDAPNodeStruct, inConfig->fReplicaHosts, inConfig->fServerPort, inConfig->fOpenCloseTimeout, bInNeedWriteable );
if( outHost == nil && bInNeedWriteable )
{
if ((strcmp( inConfig->fServerName, "127.0.0.1" ) == 0)
|| (strcmp( inConfig->fServerName, "localhost" ) == 0))
{
if (gServerOS && !LocalServerIsLDAPReplica())
{
outHost = ldap_init( "127.0.0.1", inConfig->fServerPort );
inLDAPNodeStruct->setLastLDAPServer( "127.0.0.1" );
}
}
else
{
outHost = ldap_init( inConfig->fServerName, inConfig->fServerPort );
inLDAPNodeStruct->setLastLDAPServer( inConfig->fServerName );
}
}
}
else {
outHost = ldap_init( inConfig->fServerName, inConfig->fServerPort );
inLDAPNodeStruct->setLastLDAPServer( inConfig->fServerName );
SetNetworkTimeoutsForHost( outHost, kLDAPDefaultNetworkTimeoutInSeconds );
}
}
if (inLDAPNodeStruct->fHost != nil)
{
outHost = inLDAPNodeStruct->fHost;
}
if (inConfig->fReplicaHosts != inOutList) {
DSDelete(inOutList);
}
return(outHost);
}
struct addrinfo* CLDAPNode::ResolveHostName( CFStringRef inServerNameRef, int inPortNumber )
{
struct addrinfo hints;
struct addrinfo *res = nil;
char portString[32] = {0};
char serverName[512] = {0};
if (CFStringGetCString(inServerNameRef, serverName, 512, kCFStringEncodingUTF8))
{
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM;
sprintf( portString, "%d", inPortNumber );
if ( getaddrinfo(serverName, portString, &hints, &res) != 0 )
{
res = nil;
}
}
return(res);
}
LDAP* CLDAPNode::EstablishConnection( sLDAPNodeStruct *inLDAPNodeStruct, sReplicaInfo *inList, int inPort, int inOpenTimeout, bool bInNeedWriteable )
{
const int maxSockets = 512; LDAP *outHost = nil;
sReplicaInfo *lastUsedReplica = nil;
sReplicaInfo *resolvedRepIter = nil;
struct addrinfo *resolvedAddrIter = nil;
int val = 1;
int len = sizeof(val);
struct timeval recvTimeoutVal = { inOpenTimeout, 0 };
struct timeval recheckTimeoutVal = { 1, 0 };
struct timeval quickCheck = { 0, 3000 }; struct timeval pollValue = { 0, 0 };
int sockCount = 0;
fd_set fdset, fdwrite, fdread;
int fcntlFlags = 0;
char *goodHostAddress = NULL;
int *sockList = nil;
sReplicaInfo **replicaPointers = nil;
struct addrinfo **addrinfoPointers = nil;
int sockIter;
bool bReachableAddresses = false;
bool bTrySelect = false;
if (inPort == 0)
{
inPort = 389; }
if( inList == NULL )
return NULL;
sockList = (int *)calloc( maxSockets, sizeof(int) );
replicaPointers = (sReplicaInfo **)calloc( maxSockets, sizeof(sReplicaInfo *) );
addrinfoPointers = (struct addrinfo **)calloc( maxSockets, sizeof(struct addrinfo *) );
SetSockList( sockList, maxSockets, false );
FD_ZERO( &fdset );
for (resolvedRepIter = inList; resolvedRepIter != nil; resolvedRepIter = resolvedRepIter->fNext)
{
if ( !bInNeedWriteable || ( bInNeedWriteable && resolvedRepIter->bWriteable ))
{
for (resolvedAddrIter = resolvedRepIter->fAddrInfo; resolvedAddrIter != nil && sockCount < maxSockets; resolvedAddrIter = resolvedAddrIter->ai_next)
{
if( bReachableAddresses == false )
{
bReachableAddresses = ReachableAddress( resolvedAddrIter );
}
if( resolvedRepIter->bUsedLast == true )
{
resolvedRepIter->bUsedLast = false;
lastUsedReplica = resolvedRepIter;
}
else
{
replicaPointers[sockCount] = resolvedRepIter;
addrinfoPointers[sockCount] = resolvedAddrIter;
sockCount++;
}
}
}
}
if( lastUsedReplica == NULL && bReachableAddresses == false && sockCount > 0 )
{
struct mach_timebase_info timeBaseInfo;
mach_timebase_info( &timeBaseInfo );
int iReachableCount = 0;
uint64_t delay = (((uint64_t)NSEC_PER_SEC * (uint64_t)timeBaseInfo.denom) / (uint64_t)timeBaseInfo.numer);
while( bReachableAddresses == false && iReachableCount < (inOpenTimeout >> 1) )
{
mach_wait_until( mach_absolute_time() + delay );
iReachableCount++;
int iCount;
for( iCount = 0; iCount < sockCount && bReachableAddresses == false; iCount++ )
{
bReachableAddresses = ReachableAddress( addrinfoPointers[iCount] );
}
}
}
if( bReachableAddresses )
{
if (lastUsedReplica != nil)
{
for (resolvedAddrIter = lastUsedReplica->fAddrInfo; resolvedAddrIter != nil && goodHostAddress == nil; resolvedAddrIter = resolvedAddrIter->ai_next)
{
if( ReachableAddress(resolvedAddrIter) )
{
goodHostAddress = LDAPWithBlockingSocket( resolvedAddrIter, inOpenTimeout );
if( goodHostAddress )
{
DBGLOG2( kLogPlugin, "CLDAPNode: EstablishConnection - Previous replica with IP Address = %s responded for %s", goodHostAddress, (bInNeedWriteable ? "write" : "read") );
}
}
}
}
for( sockIter = 0; sockIter < sockCount && goodHostAddress == nil; sockIter++ )
{
struct addrinfo *tmpAddress = addrinfoPointers[sockIter];
if( ReachableAddress(tmpAddress) )
{
if( IsLocalAddress( tmpAddress ) )
{
goodHostAddress = LDAPWithBlockingSocket( tmpAddress, inOpenTimeout );
if( goodHostAddress )
{
lastUsedReplica = replicaPointers[sockIter];
DBGLOG2( kLogPlugin, "CLDAPNode: EstablishConnection - Attempting to use local address = %s for %s", goodHostAddress, (bInNeedWriteable ? "write" : "read") );
} else {
char *tempaddress = ConvertToIPAddress( addrinfoPointers[sockIter] );
if( tempaddress ) {
DBGLOG1( kLogPlugin, "CLDAPNode: EstablishConnection - Failed local address connect to = %s", tempaddress );
DSFree( tempaddress );
}
}
}
else
{
int aSock = socket( tmpAddress->ai_family, tmpAddress->ai_socktype, tmpAddress->ai_protocol );
if( aSock != -1 )
{
setsockopt( aSock, SOL_SOCKET, SO_NOSIGPIPE, &val, len );
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 )
{
sockList[sockIter] = aSock;
if (connect(aSock, tmpAddress->ai_addr, tmpAddress->ai_addrlen) == -1)
{
FD_SET( aSock, &fdset );
bTrySelect = true;
char *tempaddress = ConvertToIPAddress( addrinfoPointers[sockIter] );
if( tempaddress ) {
DBGLOG1( kLogPlugin, "CLDAPNode: EstablishConnection - Attempting Replica connect to = %s", tempaddress );
DSFree( tempaddress );
}
}
else
{
goodHostAddress = ConvertToIPAddress( tmpAddress );
if( goodHostAddress ) {
lastUsedReplica = replicaPointers[sockIter];
DBGLOG1( kLogPlugin, "CLDAPNode: EstablishConnection - Immediate Response to = %s", goodHostAddress );
}
}
}
else
{
close( aSock );
DBGLOG1( kLogPlugin, "CLDAPNode: EstablishConnection - Unable to do non-blocking connect for socket = %d", aSock );
}
}
else
{
close( aSock );
DBGLOG1( kLogPlugin, "CLDAPNode: EstablishConnection - Unable to do get GETFL = %d", aSock );
}
}
}
}
else
{
char *tempaddress = ConvertToIPAddress( addrinfoPointers[sockIter] );
if( tempaddress )
{
DBGLOG1( kLogPlugin, "CLDAPNode: EstablishConnection - Address not reachable %s", tempaddress );
DSFree( tempaddress );
}
}
if( bTrySelect )
{
FD_COPY( &fdset, &fdwrite );
FD_COPY( &fdset, &fdread );
if( select( FD_SETSIZE, NULL, &fdwrite, NULL, &quickCheck ) > 0 )
{
select( FD_SETSIZE, &fdread, NULL, NULL, &pollValue );
int checkIter;
for( checkIter = 0; checkIter <= sockIter; checkIter++ )
{
int aSock = sockList[checkIter];
if( aSock != -1 && FD_ISSET(aSock, &fdwrite) && !FD_ISSET(aSock, &fdread) )
{
goodHostAddress = ConvertToIPAddress( addrinfoPointers[checkIter] );
if( goodHostAddress )
{
lastUsedReplica = replicaPointers[checkIter];
break;
}
}
else if( aSock != -1 && FD_ISSET(aSock, &fdwrite) && FD_ISSET(aSock, &fdread) )
{
FD_CLR( aSock, &fdset );
char *tmpHostAddr = ConvertToIPAddress( addrinfoPointers[checkIter] );
if( tmpHostAddr ) {
DBGLOG1( kLogPlugin, "CLDAPNode: EstablishConnection - Quick Check Bad socket to host %s clearing from poll", tmpHostAddr );
break;
}
}
}
}
}
}
int iterTry = 0;
while( goodHostAddress == NULL && iterTry++ < inOpenTimeout && bTrySelect )
{
FD_COPY( &fdset, &fdwrite ); FD_COPY( &fdset, &fdread );
recheckTimeoutVal.tv_sec = 1;
if( select(FD_SETSIZE, NULL, &fdwrite, NULL, &recheckTimeoutVal) > 0 )
{
int checkIter;
select( FD_SETSIZE, &fdread, NULL, NULL, &pollValue );
for( checkIter = 0; checkIter < sockCount; checkIter++ )
{
int aSock = sockList[checkIter];
if( aSock != -1 && FD_ISSET(aSock, &fdwrite) && !FD_ISSET(aSock, &fdread) )
{
goodHostAddress = ConvertToIPAddress( addrinfoPointers[checkIter] );
if( goodHostAddress ) {
lastUsedReplica = replicaPointers[checkIter];
break;
}
}
else if( aSock != -1 && FD_ISSET(aSock, &fdwrite) && FD_ISSET(aSock, &fdread) )
{
FD_CLR( aSock, &fdset );
char *tmpHostAddr = ConvertToIPAddress( addrinfoPointers[checkIter] );
if( tmpHostAddr ) {
DBGLOG1( kLogPlugin, "CLDAPNode: EstablishConnection - Bad socket to host %s clearing from poll", tmpHostAddr );
break;
}
}
}
}
}
if( goodHostAddress )
{
outHost = ldap_init( goodHostAddress, inPort );
if (outHost != nil)
{
SetNetworkTimeoutsForHost( outHost, kLDAPDefaultNetworkTimeoutInSeconds );
if( lastUsedReplica )
{
lastUsedReplica->bUsedLast = true;
}
DBGLOG2( kLogPlugin, "CLDAPNode: EstablishConnection - Using replica with IP Address = %s for %s", goodHostAddress, (bInNeedWriteable ? "write" : "read") );
inLDAPNodeStruct->setLastLDAPServer( goodHostAddress );
}
else
{
DBGLOG1( kLogPlugin, "CLDAPNode: EstablishConnection - ldap_init failed for %s", goodHostAddress );
inLDAPNodeStruct->setLastLDAPServer( NULL );
}
DSFree( goodHostAddress );
}
else
{
DBGLOG1( kLogPlugin, "CLDAPNode: EstablishConnection - Could not establish connection for %s", (bInNeedWriteable ? "write" : "read") );
inLDAPNodeStruct->setLastLDAPServer( NULL );
}
}
else
{
DBGLOG( kLogPlugin, "CLDAPNode: EstablishConnection - No reachable addresses, possibly no IP addresses" );
inLDAPNodeStruct->setLastLDAPServer( NULL );
}
SetSockList( sockList, sockCount, true );
DSFree( sockList );
DSFree( replicaPointers )
DSFree( addrinfoPointers );
return(outHost);
}
void CLDAPNode::BuildReplicaInfoLinkList( sReplicaInfo **inOutList, CFArrayRef inRepList, CFArrayRef inWriteableList, int inPort )
{
if( inRepList != NULL && inOutList != NULL )
{
CFStringRef replicaStrRef = NULL;
CFIndex numReps = CFArrayGetCount(inRepList);
if ( numReps > 0)
{
sReplicaInfo *tailItem = NULL;
sReplicaInfo *aNewList = NULL;
CFRange rangeOfWrites = CFRangeMake( 0, (inWriteableList ? CFArrayGetCount(inWriteableList) : 0) );
for (CFIndex indexToRep=0; indexToRep < numReps; indexToRep++ )
{
replicaStrRef = (CFStringRef)::CFArrayGetValueAtIndex( inRepList, indexToRep );
struct addrinfo *addrList = ResolveHostName(replicaStrRef, inPort);
sReplicaInfo* newInfo = (sReplicaInfo *)calloc(1, sizeof(sReplicaInfo));
if( tailItem != NULL )
{
tailItem->fNext = newInfo; tailItem = newInfo; }
else
{
aNewList = newInfo;
tailItem = newInfo;
}
newInfo->fAddrInfo = addrList;
newInfo->hostname = CFStringCreateCopy( kCFAllocatorDefault, replicaStrRef );
if( inWriteableList != nil && CFArrayContainsValue(inWriteableList, rangeOfWrites, replicaStrRef) )
{
newInfo->bWriteable = true;
}
}
if (aNewList != NULL) {
DSDelete( *inOutList );
*inOutList = aNewList;
}
}
}
}
void CLDAPNode::MergeArraysRemovingDuplicates( CFMutableArrayRef cfPrimaryArray, CFArrayRef cfArrayToAdd )
{
CFIndex addCount = CFArrayGetCount( cfArrayToAdd );
CFRange cfRange = CFRangeMake( 0, CFArrayGetCount(cfPrimaryArray) );
if( CFArrayContainsValue(cfArrayToAdd, CFRangeMake(0,addCount), CFSTR("127.0.0.1")) ||
CFArrayContainsValue(cfArrayToAdd, CFRangeMake(0,addCount), CFSTR("localhost")) )
{
CFArrayInsertValueAtIndex( cfPrimaryArray, 0, CFSTR("127.0.0.1") );
cfRange.length++; }
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 CLDAPNode::GetReplicaListFromDNS( sLDAPNodeStruct *inLDAPNodeStruct, CFMutableArrayRef inOutRepList )
{
sInt32 siResult = eDSNoStdMappingAvailable;
CFMutableArrayRef serviceRecords = NULL;
CFMutableArrayRef aRepList = NULL; CFStringRef aString = NULL;
if ( (inLDAPNodeStruct != nil) && (inLDAPNodeStruct->fNodeName != nil) )
{
CFStringRef cfServerName = CFStringCreateWithCString( kCFAllocatorDefault, inLDAPNodeStruct->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( inLDAPNodeStruct->fLDAPUserName && inLDAPNodeStruct->fLDAPCredentialsLen && 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 );
DBGLOG1( kLogPlugin, "CLDAPNode: GetReplicaListFromDNS - Checking computer's primary domain %s", domain );
serviceRecords = getDNSServiceRecs( "ldap", domain );
DSFreeString( domain );
}
}
DSCFRelease( cfDNSdomainsDict );
}
DSCFRelease( cfDomainSuffix );
if( serviceRecords == NULL )
{
DBGLOG1( kLogPlugin, "CLDAPNode: GetReplicaListFromDNS - Checking domain %s", inLDAPNodeStruct->fNodeName );
serviceRecords = getDNSServiceRecs( "ldap", inLDAPNodeStruct->fNodeName );
}
if( inLDAPNodeStruct->fLDAPUserName && inLDAPNodeStruct->fLDAPCredentialsLen && namePartCount > 2 && serviceRecords == NULL )
{
char *subDomain = strchr( inLDAPNodeStruct->fNodeName, '.' ) + 1;
if( subDomain )
{
DBGLOG1( kLogPlugin, "CLDAPNode: GetReplicaListFromDNS - Checking domain %s", 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, "CLDAPNode: GetReplicaListFromDNS - Found ldap replica servers in DNS service records." );
MergeArraysRemovingDuplicates( aRepList, inOutRepList );
CFArrayRemoveAllValues( inOutRepList );
CFArrayAppendArray( inOutRepList, aRepList, CFRangeMake(0,CFArrayGetCount(aRepList)) );
siResult = eDSNoErr;
}
else
{
DBGLOG( kLogPlugin, "CLDAPNode: GetReplicaListFromDNS - No ldap replica servers in DNS service records." );
}
CFRelease( aRepList );
aRepList = NULL;
}
else
{
DBGLOG( kLogPlugin, "CLDAPNode: GetReplicaListFromDNS - No ldap replica servers in DNS service records." );
}
return(siResult);
}
sInt32 CLDAPNode::RetrieveDefinedReplicas( sLDAPNodeStruct *inLDAPNodeStruct, CFMutableArrayRef &inOutRepList, CFMutableArrayRef &inOutWriteableList, int inPort, sReplicaInfo **inOutList )
{
LDAP *outHost = nil;
CFMutableArrayRef aRepList = NULL;
CFMutableArrayRef aWriteableList = NULL;
bool bMessageFound = false;
sInt32 foundResult = eDSRecordNotFound;
sLDAPConfigData *pConfig = nil;
int openTimeout = kLDAPDefaultOpenCloseTimeoutInSeconds;
int searchTimeout = 30; int version = -1;
int bindMsgId = 0;
LDAPMessage *result = nil;
int ldapReturnCode = 0;
bool bIsSSL = false;
bool bReferrals = true;
uInt32 aSecurityLevel = 0;
bool bNoConfig = true;
bool bDNSReplicas = false;
bool bLDAPv2ReadOnly = false;
try
{
CLDAPv3Plugin::WaitForNetworkTransitionToFinish();
inLDAPNodeStruct->SessionMutexWait();
inLDAPNodeStruct->fConnectionActiveCount++;
pConfig = gpConfigFromXML->ConfigWithNodeNameLock( inLDAPNodeStruct->fNodeName );
if (pConfig != nil)
{
bNoConfig = false;
if( !inLDAPNodeStruct->bAuthCallActive )
{
DSDelete( inLDAPNodeStruct->fLDAPUserName );
DSDelete( inLDAPNodeStruct->fKerberosId );
DSDelete( inLDAPNodeStruct->fLDAPAuthType );
DSFree( inLDAPNodeStruct->fLDAPCredentials );
if ( pConfig->bSecureUse )
{
inLDAPNodeStruct->fLDAPUserName = (pConfig->fServerAccount ? strdup(pConfig->fServerAccount) : nil);
inLDAPNodeStruct->fKerberosId = (pConfig->fKerberosId ? strdup(pConfig->fKerberosId) : nil);
inLDAPNodeStruct->fLDAPCredentials = (void *) (pConfig->fServerPassword ? strdup( pConfig->fServerPassword ) : NULL);
inLDAPNodeStruct->fLDAPCredentialsLen = (pConfig->fServerPassword ? strlen( pConfig->fServerPassword ) : 0 );
}
}
openTimeout = pConfig->fOpenCloseTimeout;
searchTimeout = pConfig->fSearchTimeout;
bIsSSL = pConfig->bIsSSL;
bLDAPv2ReadOnly = pConfig->bLDAPv2ReadOnly;
bReferrals = pConfig->bReferrals;
bDNSReplicas = pConfig->bDNSReplicas;
aSecurityLevel = pConfig->fSecurityLevel;
gpConfigFromXML->ConfigUnlock( pConfig );
}
inLDAPNodeStruct->fConnectionActiveCount--;
inLDAPNodeStruct->SessionMutexSignal();
outHost = EstablishConnection( inLDAPNodeStruct, *inOutList, inPort, openTimeout, false );
if (outHost != nil)
{
if (bIsSSL && !bLDAPv2ReadOnly)
{
int ldapOptVal = LDAP_OPT_X_TLS_HARD;
ldap_set_option(outHost, LDAP_OPT_X_TLS, &ldapOptVal);
}
ldap_set_option(outHost, LDAP_OPT_REFERRALS, (bReferrals ? LDAP_OPT_ON : LDAP_OPT_OFF) );
if (!bLDAPv2ReadOnly)
{
version = LDAP_VERSION3;
ldap_set_option( outHost, LDAP_OPT_PROTOCOL_VERSION, &version );
}
pConfig = gpConfigFromXML->ConfigWithNodeNameLock( inLDAPNodeStruct->fNodeName );
if ( pConfig != nil)
{
if (!bLDAPv2ReadOnly)
{
CheckSASLMethods( inLDAPNodeStruct );
ldapReturnCode = doSASLBindAttemptIfPossible( outHost, pConfig, inLDAPNodeStruct->fLDAPUserName, (char *)inLDAPNodeStruct->fLDAPCredentials, inLDAPNodeStruct->fKerberosId );
}
gpConfigFromXML->ConfigUnlock( pConfig );
}
if( ldapReturnCode != LDAP_LOCAL_ERROR && ldapReturnCode != LDAP_OTHER && !bLDAPv2ReadOnly )
{
if( ldapReturnCode != LDAP_SUCCESS )
{
DBGLOG( kLogPlugin, "CLDAPNode: Failed doing SASL Authentication in Replica retrieval" );
inLDAPNodeStruct->fConnectionStatus = kConnectionUnknown;
inLDAPNodeStruct->fDelayedBindTime = time( nil ) + inLDAPNodeStruct->fDelayRebindTry;
throw( (sInt32)eDSCannotAccessSession );
}
}
else if( bNoConfig || bIsSSL || (aSecurityLevel & kSecDisallowCleartext) == 0 ||
(aSecurityLevel & kSecDisallowCleartext == kSecDisallowCleartext && (DSIsStringEmpty(inLDAPNodeStruct->fLDAPUserName) || inLDAPNodeStruct->fLDAPCredentialsLen == 0)) )
{
timeval tv = { 0 };
bindMsgId = ldap_simple_bind( outHost, inLDAPNodeStruct->fLDAPUserName, (char *)inLDAPNodeStruct->fLDAPCredentials );
tv.tv_sec = (openTimeout ? openTimeout : kLDAPDefaultOpenCloseTimeoutInSeconds);
ldapReturnCode = ldap_result( outHost, bindMsgId, 0, &tv, &result );
if ( ldapReturnCode == -1 )
{
throw( (sInt32)eDSCannotAccessSession );
}
else if ( ldapReturnCode == 0 )
{
LogFailedConnection("Bind timeout in Replica retrieval", inLDAPNodeStruct->fLDAPServer, inLDAPNodeStruct->fDelayRebindTry);
inLDAPNodeStruct->fConnectionStatus = kConnectionUnknown;
inLDAPNodeStruct->fDelayedBindTime = time( nil ) + inLDAPNodeStruct->fDelayRebindTry;
throw( (sInt32)eDSCannotAccessSession );
}
else if ( ldap_result2error(outHost, result, 1) != LDAP_SUCCESS )
{
LogFailedConnection("Bind failure in Replica retrieval", inLDAPNodeStruct->fLDAPServer, inLDAPNodeStruct->fDelayRebindTry);
inLDAPNodeStruct->fConnectionStatus = kConnectionUnknown;
inLDAPNodeStruct->fDelayedBindTime = time( nil ) + inLDAPNodeStruct->fDelayRebindTry;
inLDAPNodeStruct->fHost = outHost;
throw( (sInt32)eDSCannotAccessSession );
}
} else {
DBGLOG1( kLogPlugin, "CLDAPNode: ClearText binds disallowed by Policy. LDAP Connection to server %s denied", inLDAPNodeStruct->fLDAPServer );
syslog( LOG_ALERT,"DSLDAPv3PlugIn: ClearText binds disallowed by Policy. LDAP Connection to server %s denied.", inLDAPNodeStruct->fLDAPServer );
inLDAPNodeStruct->fConnectionStatus = kConnectionUnsafe;
inLDAPNodeStruct->fDelayedBindTime = time( nil ) + (inLDAPNodeStruct->fDelayRebindTry ? inLDAPNodeStruct->fDelayRebindTry : kLDAPDefaultRebindTryTimeoutInSeconds);
ldap_unbind_ext( outHost, NULL, NULL );
outHost = NULL;
throw( (sInt32)eDSCannotAccessSession );
}
if ( inLDAPNodeStruct->fConnectionStatus != kConnectionSafe )
gSrvrCntl->NodeSearchPolicyChanged();
inLDAPNodeStruct->fConnectionStatus = kConnectionSafe;
aRepList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
aWriteableList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if( inLDAPNodeStruct != NULL && inLDAPNodeStruct->fNodeName != NULL )
{
CFStringRef cfString = CFStringCreateWithCString( kCFAllocatorDefault, inLDAPNodeStruct->fNodeName, kCFStringEncodingUTF8 );
CFArrayAppendValue( aRepList, cfString );
DSCFRelease( cfString );
}
if ( GetReplicaListFromConfigRecord(outHost, searchTimeout, inLDAPNodeStruct, aRepList, aWriteableList) == eDSNoErr )
{
bMessageFound = true;
}
if ( GetReplicaListFromAltServer(outHost, searchTimeout, aRepList) == eDSNoErr )
{
bMessageFound = true;
}
if( bDNSReplicas )
{
if( GetReplicaListFromDNS(inLDAPNodeStruct, aRepList) == eDSNoErr )
bMessageFound = true;
}
else
{
DBGLOG( kLogPlugin, "CLDAPNode: GetReplicaListFromDNS - Skipped - disabled in configuration" );
}
if (bMessageFound)
{
if (CFArrayGetCount(aRepList) > 0)
{
if( CFArrayGetCount(aWriteableList) == 0 )
{
CFArrayAppendArray( aWriteableList, aRepList, CFRangeMake(0,CFArrayGetCount(aRepList)) );
}
if( inOutRepList )
{
CFArrayRemoveAllValues( inOutRepList );
CFArrayAppendArray( inOutRepList, aRepList, CFRangeMake(0,CFArrayGetCount(aRepList)) );
}
else
{
inOutRepList = aRepList;
aRepList = NULL; }
foundResult = eDSNoErr;
}
if (CFArrayGetCount(aWriteableList) > 0)
{
if( inOutWriteableList )
{
CFArrayRemoveAllValues( inOutWriteableList );
CFArrayAppendArray(inOutWriteableList, aWriteableList, CFRangeMake(0,CFArrayGetCount(aWriteableList)));
}
else
{
inOutWriteableList = aWriteableList;
aWriteableList = NULL; }
foundResult = eDSNoErr;
}
}
BuildReplicaInfoLinkList( inOutList, inOutRepList, inOutWriteableList, inPort );
ldap_unbind(outHost);
if( inOutRepList != NULL && CFArrayGetCount( inOutRepList ) )
{
CFStringRef listString = CFStringCreateByCombiningStrings( kCFAllocatorDefault, inOutRepList, CFSTR(", ") );
uInt32 uiLength = (uInt32) CFStringGetMaximumSizeForEncoding( CFStringGetLength(listString), kCFStringEncodingUTF8 ) + 1;
char *logLine = (char *) calloc( sizeof(char), uiLength );
CFStringGetCString( listString, logLine, uiLength, kCFStringEncodingUTF8 );
DBGLOG1( kLogPlugin, "CLDAPNode: Readable Replica List - %s", logLine );
DSFreeString( logLine );
DSCFRelease( listString );
}
if( inOutWriteableList != NULL && CFArrayGetCount( inOutWriteableList ) )
{
CFStringRef listString = CFStringCreateByCombiningStrings( kCFAllocatorDefault, inOutWriteableList, CFSTR(", ") );
uInt32 uiLength = (uInt32) CFStringGetMaximumSizeForEncoding( CFStringGetLength(listString), kCFStringEncodingUTF8 ) + 1;
char *logLine = (char *) calloc( sizeof(char), uiLength );
CFStringGetCString( listString, logLine, uiLength, kCFStringEncodingUTF8 );
DBGLOG1( kLogPlugin, "CLDAPNode: Writeable Replica List - %s", logLine );
DSFreeString( logLine );
DSCFRelease( listString );
}
}
else
{
foundResult = eDSCannotAccessSession;
}
}
catch ( sInt32 err )
{
foundResult = err;
}
DSCFRelease( aRepList );
DSCFRelease( aWriteableList );
return(foundResult);
}
sInt32 CLDAPNode::GetReplicaListFromAltServer( LDAP *inHost, int inSearchTO, CFMutableArrayRef inOutRepList )
{
sInt32 siResult = eDSRecordNotFound;
bool bResultFound = false;
int ldapMsgId = 0;
LDAPMessage *result = nil;
int ldapReturnCode = 0;
char *attrs[2] = {"altserver",NULL};
BerElement *ber = nil;
struct berval **bValues = nil;
char *pAttr = nil;
CFMutableArrayRef aRepList = NULL;
if ( (ldapMsgId = ldap_search( inHost, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, 0) ) == -1 )
{
bResultFound = false;
}
else
{
bResultFound = true;
struct timeval tv;
tv.tv_usec = 0;
if (inSearchTO == 0)
{
tv.tv_sec = kLDAPDefaultSearchTimeoutInSeconds;
}
else
{
tv.tv_sec = inSearchTO;
}
ldapReturnCode = ldap_result(inHost, ldapMsgId, 0, &tv, &result);
}
if ( (bResultFound) &&
( ldapReturnCode == LDAP_RES_SEARCH_ENTRY ) )
{
aRepList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
pAttr = ldap_first_attribute (inHost, result, &ber );
if (pAttr != nil)
{
if (( bValues = ldap_get_values_len (inHost, result, pAttr )) != NULL)
{
for (int i = 0; bValues[i] != NULL; i++ )
{
if ( bValues[i] != NULL )
{
int offset = 0;
char *strPtr = bValues[i]->bv_val;
if (strPtr != NULL && strlen(strPtr) >= 9) {
if (strncmp(strPtr,"ldaps://",8) == 0)
{
offset = 8;
}
else if (strncmp(strPtr,"ldap://",7) == 0)
{
offset = 7;
}
}
char *strEnd = nil;
strEnd = strchr(strPtr+offset,':');
if (strEnd != nil)
{
strEnd[0] = '\0';
}
else
{
strEnd = strchr(strPtr+offset,'/');
if (strEnd != nil)
{
strEnd[0] = '\0';
}
}
CFStringRef aCFString = CFStringCreateWithCString( NULL, strPtr+offset, kCFStringEncodingUTF8 );
CFArrayAppendValue(aRepList, aCFString);
CFRelease(aCFString);
siResult = eDSNoErr;
}
}
ldap_value_free_len(bValues);
} ldap_memfree( pAttr );
}
if (ber != nil)
{
ber_free( ber, 0 );
}
ldap_msgfree( result );
result = nil;
} else if (ldapReturnCode == LDAP_TIMEOUT)
{
siResult = eDSServerTimeout;
if ( result != nil )
{
ldap_msgfree( result );
result = nil;
}
}
else
{
siResult = eDSRecordNotFound;
if ( result != nil )
{
ldap_msgfree( result );
result = nil;
}
}
DSSearchCleanUp(inHost, ldapMsgId);
if ( aRepList != NULL )
{
if( CFArrayGetCount(aRepList) > 0 )
{
DBGLOG( kLogPlugin, "CLDAPNode: GetReplicaListFromAltServer - Found some ldap replica servers in rootDSE altServer." );
MergeArraysRemovingDuplicates( inOutRepList, aRepList );
siResult = eDSNoErr;
}
else
{
DBGLOG( kLogPlugin, "CLDAPNode: GetReplicaListFromAltServer - No ldap replica servers in rootDSE altServer." );
}
CFRelease( aRepList );
aRepList = NULL;
}
else
{
DBGLOG( kLogPlugin, "CLDAPNode: GetReplicaListFromAltServer - No ldap replica servers in rootDSE altServer." );
}
return( siResult );
}
sInt32 CLDAPNode::GetReplicaListFromConfigRecord( LDAP *inHost, int inSearchTO, sLDAPNodeStruct *inLDAPNodeStruct, CFMutableArrayRef outRepList, CFMutableArrayRef outWriteableList )
{
sInt32 siResult = eDSRecordNotFound;
bool bResultFound = false;
int ldapMsgId = 0;
LDAPMessage *result = nil;
int ldapReturnCode = 0;
BerElement *ber = nil;
struct berval **bValues = nil;
char *pAttr = nil;
LDAPControl **serverctrls = nil;
LDAPControl **clientctrls = nil;
char *nativeRecType = nil;
bool bOCANDGroup = false;
CFArrayRef OCSearchList = nil;
ber_int_t scope = LDAP_SCOPE_BASE;
char *queryFilter = nil;
char *repListAttr = nil;
char *writeListAttr = nil;
int whichAttr = 0;
if (inLDAPNodeStruct == nil)
{
return(siResult);
}
nativeRecType = CLDAPv3Plugin::MapRecToLDAPType( kDSStdRecordTypeConfig,
inLDAPNodeStruct->fNodeName,
1,
&bOCANDGroup,
&OCSearchList,
&scope );
if (nativeRecType == nil)
{
DBGLOG( kLogPlugin, "CLDAPNode: GetReplicaListFromConfigRecord - No Config Record mapping to retrieve replica list." );
return(eDSNoStdMappingAvailable);
}
queryFilter = CLDAPv3Plugin::BuildLDAPQueryFilter( kDSNAttrRecordName,
"ldapreplicas",
eDSExact,
inLDAPNodeStruct->fNodeName,
false,
kDSStdRecordTypeConfig,
nativeRecType,
bOCANDGroup,
OCSearchList );
if (OCSearchList != nil)
{
CFRelease(OCSearchList);
OCSearchList = nil;
}
if (queryFilter == nil)
{
DSFree( nativeRecType );
return(siResult);
}
ldapReturnCode = ldap_search_ext( inHost,
nativeRecType,
scope,
queryFilter,
NULL,
0,
serverctrls,
clientctrls,
0, 0,
&ldapMsgId );
if (ldapReturnCode == LDAP_SUCCESS)
{
bResultFound = true;
struct timeval tv;
tv.tv_usec = 0;
if (inSearchTO == 0)
{
tv.tv_sec = kLDAPDefaultSearchTimeoutInSeconds;
}
else
{
tv.tv_sec = inSearchTO;
}
ldapReturnCode = ldap_result(inHost, ldapMsgId, 0, &tv, &result);
}
DSFree( nativeRecType );
DSFree( queryFilter );
if (serverctrls) ldap_controls_free( serverctrls );
if (clientctrls) ldap_controls_free( clientctrls );
if ( (bResultFound) &&
( ldapReturnCode == LDAP_RES_SEARCH_ENTRY ) )
{
siResult = eDSNoErr;
repListAttr = CLDAPv3Plugin::MapAttrToLDAPType( kDSStdRecordTypeConfig,
kDSNAttrLDAPReadReplicas,
inLDAPNodeStruct->fNodeName,
1 );
writeListAttr = CLDAPv3Plugin::MapAttrToLDAPType( kDSStdRecordTypeConfig,
kDSNAttrLDAPWriteReplicas,
inLDAPNodeStruct->fNodeName,
1 );
for ( pAttr = ldap_first_attribute (inHost, result, &ber );
pAttr != NULL; pAttr = ldap_next_attribute(inHost, result, ber ) )
{
whichAttr = 0;
if ( ( repListAttr != nil ) && ( strcmp(pAttr, repListAttr) == 0 ) )
{
whichAttr = 1;
}
if ( ( writeListAttr != nil ) && ( strcmp(pAttr, writeListAttr) == 0 ) )
{
whichAttr = 2;
}
if ( ( whichAttr != 0 ) && (( bValues = ldap_get_values_len (inHost, result, pAttr )) != NULL) )
{
for (int i = 0; bValues[i] != NULL; i++ )
{
if ( bValues[i] != NULL )
{
int offset = 0;
char *strPtr = bValues[i]->bv_val;
if (strPtr != NULL && strlen(strPtr) >= 9) {
if (strncmp(strPtr,"ldaps://",8) == 0)
{
offset = 8;
}
else if (strncmp(strPtr,"ldap://",7) == 0)
{
offset = 7;
}
}
char *strEnd = nil;
strEnd = strchr(strPtr+offset,':');
if (strEnd != nil)
{
strEnd[0] = '\0';
}
else
{
strEnd = strchr(strPtr+offset,'/');
if (strEnd != nil)
{
strEnd[0] = '\0';
}
}
CFStringRef aCFString = CFStringCreateWithCString( NULL, strPtr+offset, kCFStringEncodingUTF8 );
if (whichAttr == 1)
{
if( CFArrayContainsValue(outRepList, CFRangeMake(0,CFArrayGetCount(outRepList)), aCFString) == false )
{
CFArrayAppendValue(outRepList, aCFString);
}
}
else
{
if( CFArrayContainsValue(outWriteableList, CFRangeMake(0,CFArrayGetCount(outWriteableList)), aCFString) == false )
{
CFArrayAppendValue(outWriteableList, aCFString);
}
}
CFRelease(aCFString);
}
}
ldap_value_free_len(bValues);
} ldap_memfree( pAttr );
}
if (ber != nil)
{
ber_free( ber, 0 );
}
ldap_msgfree( result );
result = nil;
} else if (ldapReturnCode == LDAP_TIMEOUT)
{
siResult = eDSServerTimeout;
if ( result != nil )
{
ldap_msgfree( result );
result = nil;
}
}
else
{
siResult = eDSRecordNotFound;
if ( result != nil )
{
ldap_msgfree( result );
result = nil;
}
}
DSSearchCleanUp(inHost, ldapMsgId);
DSFree( repListAttr );
DSFree( writeListAttr );
if( CFArrayGetCount(outRepList) > 0 )
{
DBGLOG( kLogPlugin, "CLDAPNode: GetReplicaListFromConfigRecord - Found Config Record \"ldapreplicas\" that had Replica information." );
}
else
{
DBGLOG( kLogPlugin, "CLDAPNode: GetReplicaListFromConfigRecord - No Config Record \"ldapreplicas\" with Replica information." );
}
return( siResult );
}
void CLDAPNode::RetrieveServerMappingsIfRequired(sLDAPNodeStruct *inLDAPNodeStruct)
{
sLDAPConfigData *pConfig = nil;
if (inLDAPNodeStruct != nil)
{
pConfig = gpConfigFromXML->ConfigWithNodeNameLock( inLDAPNodeStruct->fNodeName );
if ( (inLDAPNodeStruct->fHost != nil) && (pConfig != nil) && (pConfig->bGetServerMappings) )
{
char **aMapSearchBase = nil;
uInt32 numberBases = 0;
if ( (pConfig->fMapSearchBase == nil) || ( strcmp(pConfig->fMapSearchBase,"") == 0 ) )
{
aMapSearchBase = GetNamingContexts( inLDAPNodeStruct->fHost, pConfig->fOpenCloseTimeout, &numberBases );
if( aMapSearchBase == (char **) -1 )
{
aMapSearchBase = nil;
LogFailedConnection("GetNamingContexts failure", inLDAPNodeStruct->fLDAPServer, inLDAPNodeStruct->fDelayRebindTry);
inLDAPNodeStruct->fConnectionStatus = kConnectionUnknown;
inLDAPNodeStruct->fDelayedBindTime = time( nil ) + inLDAPNodeStruct->fDelayRebindTry;
}
}
else
{
numberBases = 1;
aMapSearchBase = (char **)calloc(numberBases+1, sizeof (char *));
aMapSearchBase[0] = strdup(pConfig->fMapSearchBase);
}
for (uInt32 baseIndex = 0; (baseIndex < numberBases) && (aMapSearchBase[baseIndex] != nil); baseIndex++)
{
if ( (gpConfigFromXML->UpdateLDAPConfigWithServerMappings( pConfig->fNodeName, aMapSearchBase[baseIndex], pConfig->fServerPort, pConfig->bIsSSL, pConfig->bLDAPv2ReadOnly, pConfig->bUseAsDefaultLDAP, inLDAPNodeStruct->fHost )) == eDSNoErr )
{
DBGLOG( kLogPlugin, "CLDAPNode: SafeOpen retrieved server mappings." );
pConfig->bGetServerMappings = false;
break;
}
else
{
syslog(LOG_ALERT,"LDAPv3: SafeOpen Can't retrieve server mappings from search base of <%s>.", aMapSearchBase[baseIndex] );
}
}
if (pConfig->bGetServerMappings == true)
{
DBGLOG( kLogPlugin, "CLDAPNode: SafeOpen Cannot retrieve server mappings at this time." );
syslog(LOG_ALERT,"LDAPv3: SafeOpen Cannot retrieve server mappings at this time.");
}
if (aMapSearchBase != nil)
{
for (uInt32 bIndex = 0; bIndex < numberBases; bIndex++)
{
DSFree( aMapSearchBase[bIndex] );
}
free(aMapSearchBase);
aMapSearchBase = nil;
}
}
if( pConfig )
{
gpConfigFromXML->ConfigUnlock( pConfig );
}
}
}
char *CLDAPNode::LDAPWithBlockingSocket( struct addrinfo *addrInfo, int seconds )
{
int aSock;
int val = 1;
int len = sizeof(val);
struct timeval recvTimeoutVal = { seconds, 0 };
char *returnHostAddress = NULL;
aSock = socket( addrInfo->ai_family, addrInfo->ai_socktype, addrInfo->ai_protocol );
if (aSock != -1)
{
setsockopt( aSock, SOL_SOCKET, SO_NOSIGPIPE, &val, len );
setsockopt( aSock, SOL_SOCKET, SO_RCVTIMEO, &recvTimeoutVal, sizeof(recvTimeoutVal) );
if( connect(aSock, addrInfo->ai_addr, addrInfo->ai_addrlen) == 0 )
{
returnHostAddress = ConvertToIPAddress( addrInfo );
}
close(aSock);
}
return returnHostAddress;
}
char *CLDAPNode::ConvertToIPAddress( struct addrinfo *addrInfo )
{
char *returnHostAddress = NULL;
if (addrInfo->ai_family == AF_INET)
{
returnHostAddress = (char *) calloc( 129, 1 );
if( inet_ntop( AF_INET, (const void *)&(((struct sockaddr_in*)(addrInfo->ai_addr))->sin_addr), returnHostAddress, 129 ) == NULL )
{
free( returnHostAddress );
returnHostAddress = NULL;
}
}
else if (addrInfo->ai_family == AF_INET6)
{
returnHostAddress = (char *) calloc( 129, 1 );
if( inet_ntop( AF_INET6, (const void *)&(((struct sockaddr_in6*)(addrInfo->ai_addr))->sin6_addr), returnHostAddress, 129 ) == NULL )
{
free( returnHostAddress );
returnHostAddress = NULL;
}
}
return returnHostAddress;
}
bool CLDAPNode::IsLocalAddress( struct addrinfo *addrInfo )
{
struct ifaddrs *ifa_list = nil, *ifa = nil;
bool bReturn = false;
if( getifaddrs(&ifa_list) != -1 )
{
for( ifa = ifa_list; ifa; ifa = ifa->ifa_next )
{
if( ifa->ifa_addr->sa_family == addrInfo->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 *) addrInfo->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 *)addrInfo->ai_addr;
if( memcmp( &interface->sin6_addr, &check->sin6_addr, sizeof(struct in6_addr) ) == 0 )
{
bReturn = true;
break;
}
}
}
}
freeifaddrs(ifa_list);
}
return bReturn;
}
bool CLDAPNode::ReachableAddress( struct addrinfo *addrInfo )
{
bool bReturn = IsLocalAddress( addrInfo );
if( bReturn == false )
{
bReturn = checkReachability(addrInfo->ai_addr);
}
return bReturn;
}
void CLDAPNode::CheckSASLMethods( sLDAPNodeStruct *inLDAPNodeStruct )
{
sLDAPConfigData *pConfig = nil;
if (inLDAPNodeStruct != nil )
{
pConfig = gpConfigFromXML->ConfigWithNodeNameLock( inLDAPNodeStruct->fNodeName );
if( pConfig != nil && pConfig->fSASLmethods == NULL && !(pConfig->bLDAPv2ReadOnly) )
{
LDAPMessage *result = nil;
int ldapReturnCode = 0;
char *attrs[2] = { "supportedSASLMechanisms",NULL };
BerElement *ber = nil;
struct berval **bValues = nil;
char *pAttr = nil;
struct timeval tv = { 0, 0 };
pConfig->bBuildReplicaList = false;
LDAP *aHost = InitLDAPConnection( inLDAPNodeStruct, pConfig );
pConfig->bBuildReplicaList = true;
DBGLOG( kLogPlugin, "CLDAPNode: Getting SASL Methods" );
if( aHost )
{
if ( pConfig->bIsSSL )
{
int ldapOptVal = LDAP_OPT_X_TLS_HARD;
ldap_set_option( aHost, LDAP_OPT_X_TLS, &ldapOptVal );
}
ldap_set_option(aHost, LDAP_OPT_REFERRALS, (pConfig->bReferrals ? LDAP_OPT_ON : LDAP_OPT_OFF) );
int version = LDAP_VERSION3;
ldap_set_option( aHost, LDAP_OPT_PROTOCOL_VERSION, &version );
int bindMsgId = ldap_simple_bind( aHost, NULL, NULL );
tv.tv_sec = (pConfig->fOpenCloseTimeout ? pConfig->fOpenCloseTimeout : kLDAPDefaultOpenCloseTimeoutInSeconds);
ldapReturnCode = ldap_result( aHost, bindMsgId, 0, &tv, &result );
if( ldapReturnCode != 0 && ldapReturnCode != -1 && ldap_result2error(aHost, result, 1) == LDAP_SUCCESS )
{
tv.tv_sec = (pConfig->fSearchTimeout ? pConfig->fSearchTimeout : kLDAPDefaultOpenCloseTimeoutInSeconds);
pConfig->fSASLmethods = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
ldapReturnCode = ldap_search_ext_s( aHost,
"",
LDAP_SCOPE_BASE,
"(objectclass=*)",
attrs,
false,
NULL,
NULL,
&tv, 0,
&result );
if (ldapReturnCode == LDAP_SUCCESS)
{
pAttr = ldap_first_attribute (aHost, result, &ber );
if (pAttr != nil)
{
if( (bValues = ldap_get_values_len (aHost, result, pAttr)) != NULL )
{
uInt32 ii = 0;
while( bValues[ii] != NULL )
{
CFStringRef value = CFStringCreateWithCString( NULL, bValues[ii]->bv_val, kCFStringEncodingUTF8 );
CFArrayAppendValue( pConfig->fSASLmethods, value );
CFRelease( value );
ii++;
}
ldap_value_free_len( bValues );
} ldap_memfree( pAttr );
}
if (ber != nil)
{
ber_free( ber, 0 );
}
ldap_msgfree( result );
result = nil;
DBGLOG( kLogPlugin, "CLDAPNode: Successful SASL Method retrieval" );
}
}
if ( aHost != inLDAPNodeStruct->fHost )
{
ldap_unbind_ext( aHost, NULL, NULL );
aHost = NULL;
}
}
}
if( pConfig != nil )
{
gpConfigFromXML->ConfigUnlock( pConfig );
}
}
}
bool CLDAPNode::isSASLMethodSupported ( CFStringRef inMethod )
{
CFRange aRange = CFRangeMake( 0, CFArrayGetCount(fSupportedSASLMethods) );
return CFArrayContainsValue( fSupportedSASLMethods, aRange, inMethod );
}
bool CLDAPNode::LocalServerIsLDAPReplica( )
{
bool bResult = false;
char* fileContents = NULL;
try
{
CFile slapdConf("/etc/openldap/slapd.conf");
if ( !slapdConf.is_open() )
throw(-1);
CFile slapdMacOSXConf;
fileContents = (char*)calloc( 1, slapdConf.FileSize() + 1 );
if ( fileContents != NULL )
{
slapdConf.Read( fileContents, slapdConf.FileSize() );
if ((strncmp( fileContents, "updatedn", sizeof("updatedn") - 1 ) == 0)
|| (strstr( fileContents, "\nupdatedn" ) != NULL))
{
bResult = true;
}
free( fileContents );
fileContents = NULL;
}
if ( !bResult )
{
slapdMacOSXConf.open("/etc/openldap/slapd_macosxserver.conf");
fileContents = (char*)calloc( 1, slapdMacOSXConf.FileSize() + 1 );
}
if (fileContents != NULL)
{
slapdMacOSXConf.Read( fileContents, slapdMacOSXConf.FileSize() );
if ((strncmp( fileContents, "updatedn", sizeof("updatedn") - 1 ) == 0)
|| (strstr( fileContents, "\nupdatedn" ) != NULL))
{
bResult = true;
}
free( fileContents );
fileContents = NULL;
}
}
catch ( ... )
{
}
DSFree( fileContents );
return bResult;
}
void CLDAPNode::EnsureCheckFailedConnectionsThreadIsRunning( void )
{
if( fCheckThreadActive == false )
{
fCheckThreadActive = true;
pthread_t checkThread;
pthread_attr_t _DefaultAttrs;
::pthread_attr_init( &_DefaultAttrs );
::pthread_attr_setdetachstate( &_DefaultAttrs, PTHREAD_CREATE_DETACHED);
pthread_create( &checkThread, &_DefaultAttrs, checkFailedServers, (void *)this );
}
}