CLDAPReplicaInfo.cpp [plain text]
#include "CLDAPReplicaInfo.h"
#include "CLDAPDefines.h"
#include <DirectoryServiceCore/CLog.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ldap_private.h>
#pragma mark Defines
#define kReplicaFlagSSL (1<<0)
#define kReplicaFlagLDAPv2Only (1<<1)
#define kLDAPServicePrincipalType "ldap"
#pragma mark -
#pragma mark Callbacks
void CLDAPReplicaInfo::ReachabilityCallback( SCNetworkReachabilityRef inTarget, SCNetworkConnectionFlags inFlags, void *inInfo )
{
CLDAPReplicaInfo *replica = (CLDAPReplicaInfo *) inInfo;
replica->ReachabilityNotification( inFlags );
}
#pragma mark -
#pragma mark Class Definition
CLDAPReplicaInfo::CLDAPReplicaInfo( const char *inReplicaIP, int inPort, bool inSSL, bool inLDAPv2, bool inSupportsWrites )
{
fIPAddress = strdup( inReplicaIP );
fAddrInfo = NULL;
fbSupportsWrites = inSupportsWrites;
fFlags = ((inSSL == true ? kReplicaFlagSSL : 0) | (inLDAPv2 == true ? kReplicaFlagLDAPv2Only : 0));
fReachabilityRef = NULL;
fPort = inPort;
fSASLMethods = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
fNamingContexts = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
fServerFQDN = NULL;
fServicePrincipal = NULL;
fSupportedSecurity = kSecNoSecurity;
fReachable = false;
fVerified = false;
fUnchecked = true;
fLDAPURI = NULL;
struct addrinfo hints = { 0 };
char portString[16];
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICHOST;
snprintf( portString, sizeof(portString), "%d", fPort );
if ( getaddrinfo(fIPAddress, portString, &hints, &fAddrInfo) == 0 )
{
fReachabilityRef = SCNetworkReachabilityCreateWithAddress( kCFAllocatorDefault, fAddrInfo->ai_addr );
if ( fReachabilityRef != NULL )
{
SCNetworkConnectionFlags flags = 0;
SCNetworkReachabilityContext context = { 0, this, NULL, NULL, NULL };
SCNetworkReachabilitySetCallback( fReachabilityRef, ReachabilityCallback, &context );
SCNetworkReachabilityScheduleWithRunLoop( fReachabilityRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode );
if ( SCNetworkReachabilityGetFlags(fReachabilityRef, &flags) )
{
if ( (flags & kSCNetworkFlagsReachable) != 0 && (flags & kSCNetworkFlagsConnectionRequired) == 0 )
fReachable = true;
}
}
}
}
CLDAPReplicaInfo::CLDAPReplicaInfo( const char *inLDAPI )
{
fIPAddress = strdup( "ldapi" );
fAddrInfo = NULL;
fbSupportsWrites = true;
fFlags = 0;
fReachabilityRef = NULL;
fPort = LDAP_PORT;
fSASLMethods = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
fNamingContexts = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
fServerFQDN = NULL;
fServicePrincipal = NULL;
fSupportedSecurity = kSecNoSecurity;
fReachable = true;
fVerified = false;
fUnchecked = true;
fLDAPURI = strdup( inLDAPI );
}
CLDAPReplicaInfo::~CLDAPReplicaInfo( void )
{
if ( fAddrInfo != NULL ) {
freeaddrinfo( fAddrInfo );
fAddrInfo = NULL;
}
DSFree( fLDAPURI );
DSFree( fIPAddress );
if ( fReachabilityRef != NULL ) {
SCNetworkReachabilityUnscheduleFromRunLoop( fReachabilityRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode );
DSCFRelease( fReachabilityRef );
}
DSCFRelease( fSASLMethods );
DSCFRelease( fNamingContexts );
DSFree( fServerFQDN );
DSFree( fServicePrincipal );
}
bool CLDAPReplicaInfo::SupportsSASLMethod( CFStringRef inSASLMethod )
{
bool bReturnValue = false;
if ( inSASLMethod == NULL )
return true;
if ( fReachable == true && fVerified == true )
{
fMutex.WaitLock();
bReturnValue = CFArrayContainsValue( fSASLMethods, CFRangeMake(0, CFArrayGetCount(fSASLMethods)), inSASLMethod );
fMutex.SignalLock();
}
return bReturnValue;
}
CFArrayRef CLDAPReplicaInfo::CopySASLMethods( void )
{
CFArrayRef cfReturnValue = NULL;
if ( fReachable == true && fVerified == true )
{
fMutex.WaitLock();
if ( CFArrayGetCount(fSASLMethods) != 0 )
cfReturnValue = CFArrayCreateCopy( kCFAllocatorDefault, fSASLMethods );
fMutex.SignalLock();
}
return cfReturnValue;
}
CFArrayRef CLDAPReplicaInfo::CopyNamingContexts( void )
{
CFArrayRef cfReturnValue = NULL;
if ( fReachable == true && fVerified == true )
{
fMutex.WaitLock();
if ( CFArrayGetCount(fNamingContexts) != 0 )
cfReturnValue = CFArrayCreateCopy( kCFAllocatorDefault, fNamingContexts );
fMutex.SignalLock();
}
return cfReturnValue;
}
char *CLDAPReplicaInfo::CopyServicePrincipal( void )
{
char *returnValue = NULL;
fMutex.WaitLock();
if ( fServicePrincipal != NULL )
{
returnValue = strdup( fServicePrincipal );
}
else
{
char *host = NULL;
char hostname[256] = { 0 };
if ( fServerFQDN != NULL )
{
host = fServerFQDN;
}
else if ( strcmp(fIPAddress, "127.0.0.1") == 0 )
{
if ( gethostname(hostname, sizeof(hostname)) == 0 )
host = hostname;
}
else
{
struct in_addr ipAddr = {0};
struct hostent *hostEnt = NULL;
int err_num = 0;
if ( inet_pton(AF_INET, fIPAddress, &ipAddr) == 1 )
{
hostEnt = getipnodebyaddr( &ipAddr, sizeof(ipAddr), AF_INET, &err_num );
if ( hostEnt != NULL )
{
strlcpy( hostname, hostEnt->h_name, sizeof(hostname) );
host = hostname;
freehostent( hostEnt );
}
}
}
if ( host != NULL )
{
int length = (sizeof(kLDAPServicePrincipalType)-1) + 1 + strlen(host) + 1;
returnValue = (char *) calloc( length, sizeof(char) );
snprintf( returnValue, length, "%s/%s", kLDAPServicePrincipalType, host );
}
}
fMutex.SignalLock();
return returnValue;
}
bool CLDAPReplicaInfo::ShouldAttemptCheck( void )
{
return ( fReachable == true && (fUnchecked == true || fVerified == true) );
}
void CLDAPReplicaInfo::ReachabilityNotification( SCNetworkConnectionFlags inFlags )
{
if ( (inFlags & kSCNetworkFlagsReachable) == 0 || (inFlags & kSCNetworkFlagsConnectionRequired) != 0 )
{
OSAtomicCompareAndSwap32Barrier( true, false, &fReachable );
}
else
{
OSAtomicCompareAndSwap32Barrier( false, true, &fReachable );
OSAtomicCompareAndSwap32Barrier( false, true, &fUnchecked );
}
DbgLog( kLogPlugin, "CLDAPReplicaInfo::ReachabilityNotification - Replica %s is %s", fIPAddress, (fReachable ? "reachable" : "not reachable") );
}
LDAP *CLDAPReplicaInfo::CreateLDAP( void )
{
if ( fReachable == true && fVerified == true )
return CreateLDAPInternal();
return NULL;
}
void CLDAPReplicaInfo::ConnectionFailed( void )
{
OSAtomicCompareAndSwap32Barrier( true, false, &fVerified );
OSAtomicCompareAndSwap32Barrier( true, false, &fUnchecked );
}
LDAP *CLDAPReplicaInfo::CreateLDAPInternal( void )
{
LDAP *pTempLD = NULL;
if ( fLDAPURI != NULL )
{
ldap_initialize( &pTempLD, fLDAPURI );
}
else
{
if ( (fFlags & kReplicaFlagLDAPv2Only) == 0 && (fFlags & kReplicaFlagSSL) != 0 )
{
struct sockaddr_in sa = { 0 };
sa.sin_len = sizeof(sa);
sa.sin_family = PF_INET;
sa.sin_addr.s_addr = inet_addr( fIPAddress );
char hostName[NI_MAXHOST];
if ( getnameinfo( (struct sockaddr*)&sa, sa.sin_len, hostName, sizeof(hostName), NULL, 0, NI_NAMEREQD ) == 0 )
{
pTempLD = ldap_init( hostName, fPort );
}
}
else
{
pTempLD = ldap_init( fIPAddress, fPort );
}
if ( pTempLD != NULL )
{
int flag = 1;
if ( (fFlags & kReplicaFlagLDAPv2Only) == 0 )
{
int ldapOptVal = LDAP_VERSION3;
ldap_set_option( pTempLD, LDAP_OPT_PROTOCOL_VERSION, &ldapOptVal );
if ( (fFlags & kReplicaFlagSSL) != 0 )
{
ldapOptVal = LDAP_OPT_X_TLS_HARD;
ldap_set_option(pTempLD, LDAP_OPT_X_TLS, &ldapOptVal);
}
}
ldap_set_option( pTempLD, LDAP_OPT_NOADDRERR, &flag );
}
}
return pTempLD;
}
LDAP *CLDAPReplicaInfo::VerifiedServerConnection( int inTimeoutSeconds, bool inForceCheck, void *inCallback, void *inParam )
{
LDAP *pReturnHost = NULL;
fMutex.WaitLock();
if ( fReachable == true && (fUnchecked == true || inForceCheck == true) )
{
CFArrayRemoveAllValues( fSASLMethods );
CFArrayRemoveAllValues( fNamingContexts );
DSFree( fServerFQDN );
DSFree( fServicePrincipal );
pReturnHost = CreateLDAPInternal();
if ( pReturnHost != NULL )
{
struct timeval timeout = { inTimeoutSeconds, 0 };
LDAPMessage *result = NULL;
char *attrs[] = { "supportedSASLMechanisms", "namingContexts", "dnsHostName", "krbName", NULL };
if ( fLDAPURI == NULL )
{
ldap_set_option( pReturnHost, LDAP_OPT_TIMEOUT, &timeout );
ldap_set_option( pReturnHost, LDAP_OPT_NETWORK_TIMEOUT, &timeout );
if ( inCallback != NULL )
{
ldap_set_option( pReturnHost, LDAP_OPT_NOTIFYDESC_PROC, inCallback );
ldap_set_option( pReturnHost, LDAP_OPT_NOTIFYDESC_PARAMS, inParam );
}
}
int ldapReturnCode = ldap_search_ext_s( pReturnHost, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, FALSE, NULL, NULL,
&timeout, 0, &result );
if ( ldapReturnCode == LDAP_SUCCESS )
{
struct berval **bValues;
bValues = ldap_get_values_len( pReturnHost, result, "supportedSASLMechanisms" );
if ( bValues != NULL )
{
uint32_t ii = 0;
while ( bValues[ii] != NULL )
{
CFStringRef cfValue = CFStringCreateWithCString( kCFAllocatorDefault, bValues[ii]->bv_val, kCFStringEncodingUTF8 );
CFArrayAppendValue( fSASLMethods, cfValue );
CFRelease( cfValue );
ii++;
}
ldap_value_free_len( bValues );
bValues = NULL;
}
bValues = ldap_get_values_len( pReturnHost, result, "namingContexts" );
if ( bValues != NULL )
{
uint32_t ii = 0;
while ( bValues[ii] != NULL )
{
CFStringRef cfValue = CFStringCreateWithCString( kCFAllocatorDefault, bValues[ii]->bv_val, kCFStringEncodingUTF8 );
CFArrayAppendValue( fNamingContexts, cfValue );
CFRelease( cfValue );
ii++;
}
ldap_value_free_len( bValues );
bValues = NULL;
}
bValues = ldap_get_values_len( pReturnHost, result, "dnsHostName" );
if ( bValues != NULL )
{
if ( bValues[0] != NULL )
{
char *pTemp = bValues[0]->bv_val;
if ( DSIsStringEmpty(pTemp) == false )
{
int bFlag = 1;
fServerFQDN = strdup( pTemp );
DbgLog( kLogPlugin, "CLDAPReplicaInfo::VerifiedServerConnection - setting FQDN for server as %s from rootDSE",
fServerFQDN );
ldap_set_option( pReturnHost, LDAP_OPT_SASL_FQDN, fServerFQDN );
ldap_set_option( pReturnHost, LDAP_OPT_NOREVERSE_LOOKUP, &bFlag );
}
}
ldap_value_free_len( bValues );
bValues = NULL;
}
bValues = ldap_get_values_len( pReturnHost, result, "krbName" );
if ( bValues != NULL )
{
if ( bValues[0] != NULL )
{
char *pTemp = bValues[0]->bv_val;
if ( DSIsStringEmpty(pTemp) == false )
{
fServicePrincipal = strdup( pTemp );
DbgLog( kLogPlugin, "CLDAPReplicaInfo::VerifiedServerConnection - service principal for server is %s",
fServicePrincipal );
}
}
ldap_value_free_len( bValues );
bValues = NULL;
}
ldap_msgfree( result );
result = NULL;
DbgLog( kLogPlugin, "CLDAPReplicaInfo::VerifiedServerConnection - Verified server connectivity - %s", fIPAddress );
OSAtomicCompareAndSwap32Barrier( false, true, &fVerified );
}
else
{
DbgLog( kLogPlugin, "CLDAPReplicaInfo::VerifiedServerConnection - Unable to verify server connectivity %s", fIPAddress );
OSAtomicCompareAndSwap32Barrier( true, false, &fVerified );
ldap_unbind_ext_s( pReturnHost, NULL, NULL );
pReturnHost = NULL;
}
}
OSAtomicCompareAndSwap32Barrier( true, false, &fUnchecked );
}
int32_t iSecurityLevel = kSecNoSecurity;
if ( (fFlags & kReplicaFlagSSL) != 0 )
iSecurityLevel |= (kSecDisallowCleartext | kSecPacketEncryption);
CFRange stRange = CFRangeMake( 0, CFArrayGetCount(fSASLMethods) );
if ( CFArrayContainsValue(fSASLMethods, stRange, CFSTR("CRAM-MD5")) )
iSecurityLevel |= kSecDisallowCleartext;
if ( CFArrayContainsValue(fSASLMethods, stRange, CFSTR("GSSAPI")) )
iSecurityLevel |= (kSecDisallowCleartext | kSecPacketEncryption | kSecManInMiddle | kSecPacketSigning);
fSupportedSecurity = iSecurityLevel;
OSMemoryBarrier();
fMutex.SignalLock();
return pReturnHost;
}