CLDAPConnectionManager.cpp [plain text]
#include "CLDAPConnectionManager.h"
#include "CLDAPConnection.h"
#include "CLDAPNodeConfig.h"
#include "CLDAPv3Plugin.h"
#include <DirectoryService/DirectoryService.h>
#include <DirectoryServiceCore/CLog.h>
#include <Kerberos/krb5.h>
#include <dispatch/dispatch.h>
#include <stack>
using namespace std;
#pragma mark -
#pragma mark Globals, TypeDefs and Static Member Variables
extern uint32_t gSystemGoingToSleep;
int32_t CLDAPConnectionManager::fCheckThreadActive = false;
double CLDAPConnectionManager::fCheckFailedLastRun = 0.0;
DSEventSemaphore CLDAPConnectionManager::fCheckFailedEvent;
#pragma mark -
#pragma mark Struct sLDAPContinueData Functions
sLDAPContinueData::sLDAPContinueData( void )
{
fLDAPMsgId = 0;
fNodeRef = 0;
fLDAPConnection = NULL;
fResult = NULL;
fRefLD = NULL;
fRecNameIndex = 0;
fRecTypeIndex = 0;
fTotalRecCount = 0;
fLimitRecSearch = 0;
fAuthHndl = NULL;
fAuthHandlerProc = NULL;
fAuthAuthorityData = NULL;
fPassPlugContinueData = 0;
}
sLDAPContinueData::~sLDAPContinueData( void )
{
if ( fResult != nil )
{
ldap_msgfree( fResult );
fResult = nil;
}
if ( fLDAPMsgId > 0 )
{
if ( fLDAPConnection != nil )
{
LDAP *aHost = fLDAPConnection->LockLDAPSession();
if ( aHost != NULL )
{
if ( aHost == fRefLD )
{
ldap_abandon_ext( aHost, fLDAPMsgId, NULL, NULL );
}
fLDAPConnection->UnlockLDAPSession( aHost, false );
}
}
fLDAPMsgId = 0;
fRefLD = NULL;
}
DSRelease( fLDAPConnection );
DSFreeString( fAuthAuthorityData );
}
#pragma mark -
#pragma mark Struct sLDAPContextData Functions
sLDAPContextData::sLDAPContextData( const sLDAPContextData& inContextData )
{
fType = 0;
offset = 0;
index = 1;
fOpenRecordType = fOpenRecordName = fOpenRecordDN = NULL;
fUID = inContextData.fUID;
fEffectiveUID = inContextData.fEffectiveUID;
fPWSRef = 0;
fPWSNodeRef = 0;
fPWSUserIDLength = 0;
fPWSUserID = NULL;
fAccruedTimeout.tv_sec = 0;
fAccruedTimeout.tv_usec = 0;
fLDAPConnection = inContextData.fLDAPConnection->Retain();
}
sLDAPContextData::sLDAPContextData( CLDAPConnection *inConnection )
{
fType = 0;
offset = 0;
index = 1;
fOpenRecordType = fOpenRecordName = fOpenRecordDN = NULL;
fPWSRef = 0;
fPWSNodeRef = 0;
fPWSUserIDLength = 0;
fPWSUserID = NULL;
fUID = fEffectiveUID = 0xffffffff; fAccruedTimeout.tv_sec = 0;
fAccruedTimeout.tv_usec = 0;
if ( inConnection != NULL )
fLDAPConnection = inConnection->Retain();
else
fLDAPConnection = NULL;
}
sLDAPContextData::~sLDAPContextData( void )
{
DSDelete( fOpenRecordType );
DSDelete( fOpenRecordName );
DSDelete( fOpenRecordDN );
if ( fPWSNodeRef != 0 ) {
dsCloseDirNode( fPWSNodeRef );
fPWSNodeRef = 0;
}
if ( fPWSRef != 0 ) {
dsCloseDirService( fPWSRef );
fPWSRef = 0;
}
DSFree( fPWSUserID );
DSRelease( fLDAPConnection );
}
#pragma mark -
#pragma mark Class Definition
CLDAPConnectionManager::CLDAPConnectionManager( CLDAPv3Configs *inConfigObject ) :
fLDAPConnectionMapMutex( "CLDAPConnectionManager::fLDAPConnectionMapMutex" )
{
CFTypeRef methods[] = { CFSTR("GSSAPI"), CFSTR("CRAM-MD5") };
fConfigObject = inConfigObject;
fSupportedSASLMethods = CFArrayCreate( kCFAllocatorDefault, methods, sizeof(methods) / sizeof(CFTypeRef), &kCFTypeArrayCallBacks );
}
CLDAPConnectionManager::~CLDAPConnectionManager( void )
{
fLDAPConnectionMapMutex.WaitLock();
LDAPConnectionMapI aLDAPConnectionMapI;
for ( aLDAPConnectionMapI = fLDAPConnectionMap.begin(); aLDAPConnectionMapI != fLDAPConnectionMap.end(); ++aLDAPConnectionMapI )
aLDAPConnectionMapI->second->Release();
fLDAPConnectionMap.clear();
LDAPAuthConnectionList cleanupList( fLDAPAuthConnectionList );
fLDAPAuthConnectionList.clear();
fLDAPConnectionMapMutex.SignalLock();
for ( LDAPAuthConnectionListI cleanupListI = cleanupList.begin(); cleanupListI != cleanupList.end(); ++cleanupListI )
{
(*cleanupListI)->Release();
}
DSCFRelease( fSupportedSASLMethods );
}
bool CLDAPConnectionManager::IsSASLMethodSupported( CFStringRef inMethod )
{
return CFArrayContainsValue( fSupportedSASLMethods, CFRangeMake(0, CFArrayGetCount(fSupportedSASLMethods)), inMethod );
}
CLDAPConnection *CLDAPConnectionManager::GetConnection( const char *inNodeName )
{
CLDAPConnection *pConnection = NULL;
fLDAPConnectionMapMutex.WaitLock();
LDAPConnectionMapI aLDAPConnectionMapI = fLDAPConnectionMap.find( inNodeName );
if ( aLDAPConnectionMapI != fLDAPConnectionMap.end() )
{
int32_t connectionStatus = aLDAPConnectionMapI->second->ConnectionStatus();
if ( connectionStatus == kConnectionSafe )
{
pConnection = aLDAPConnectionMapI->second->Retain();
if ( pConnection->fNodeConfig != NULL )
{
if ( pConnection->fNodeConfig->fEnableUse == false )
{
DSRelease( pConnection );
}
else if ( pConnection->fNodeConfig->fConfigDeleted == true )
{
DSRelease( pConnection );
fLDAPConnectionMap.erase( aLDAPConnectionMapI );
aLDAPConnectionMapI = fLDAPConnectionMap.end();
}
}
}
else if ( connectionStatus == kConnectionUnknown )
{
pConnection = aLDAPConnectionMapI->second->Retain();
LDAP *pTemp = pConnection->LockLDAPSession();
if ( pTemp != NULL )
pConnection->UnlockLDAPSession( pTemp, false );
else
DSRelease( pConnection );
}
}
if ( aLDAPConnectionMapI == fLDAPConnectionMap.end() )
{
pConnection = fConfigObject->CreateConnectionForNode( inNodeName );
if ( pConnection != NULL )
{
if ( ldap_is_ldapi_url(inNodeName) == false )
fLDAPConnectionMap[inNodeName] = pConnection->Retain();
LDAP *pTemp = pConnection->LockLDAPSession();
if ( pTemp != NULL )
pConnection->UnlockLDAPSession( pTemp, false );
else
DSRelease( pConnection );
}
}
fLDAPConnectionMapMutex.SignalLock();
return pConnection;
}
tDirStatus CLDAPConnectionManager::AuthConnection( CLDAPConnection **inConnection, const char *inLDAPUsername, const char *inRecordType,
const char *inKerberosID, const char *inPassword )
{
tDirStatus dsStatus = eDSAuthMasterUnreachable;
bool isLDAPI = false;
if ( (*inConnection) != NULL )
{
CLDAPConnection *pConnection = (*inConnection)->CreateCopy();
if ( pConnection != NULL )
{
pConnection->fWriteable = true;
char *ipStr = pConnection->CopyReplicaIPAddress();
if (ipStr != NULL ) {
isLDAPI = (strcmp(ipStr, "ldapi") == 0);
free( ipStr );
}
if ( isLDAPI )
{
dsStatus = eDSNoErr;
}
else
{
LDAP *pTempLD = pConnection->LockLDAPSession();
if ( pTempLD != NULL )
{
pConnection->UnlockLDAPSession( pTempLD, false );
dsStatus = pConnection->Authenticate( inLDAPUsername, inRecordType, inKerberosID, inPassword );
if ( dsStatus == eDSNoErr )
{
(*inConnection)->Release();
(*inConnection) = pConnection;
fLDAPConnectionMapMutex.WaitLock();
fLDAPAuthConnectionList.push_back( pConnection->Retain() );
fLDAPConnectionMapMutex.SignalLock();
pConnection = NULL;
}
}
}
DSRelease( pConnection );
}
}
return dsStatus;
}
tDirStatus CLDAPConnectionManager::AuthConnectionKerberos( CLDAPConnection **inConnection, const char *inUsername, const char *inRecordType,
krb5_creds *inCredsPtr, const char *inKerberosID )
{
tDirStatus dsStatus = eDSAuthMasterUnreachable;
if ( (*inConnection) != NULL )
{
CLDAPConnection *pConnection = (*inConnection)->CreateCopy();
if ( pConnection != NULL )
{
pConnection->fWriteable = true;
LDAP *pTempLD = pConnection->LockLDAPSession();
if ( pTempLD != NULL )
{
pConnection->UnlockLDAPSession( pTempLD, false );
if ( inCredsPtr != NULL )
{
dsStatus = pConnection->AuthenticateKerberos( inUsername, inRecordType, inCredsPtr, inKerberosID );
if ( dsStatus == eDSNoErr )
{
(*inConnection)->Release();
(*inConnection) = pConnection;
fLDAPConnectionMapMutex.WaitLock();
fLDAPAuthConnectionList.push_back( pConnection->Retain() );
fLDAPConnectionMapMutex.SignalLock();
pConnection = NULL;
}
}
else
{
dsStatus = eDSAuthParameterError;
}
}
DSRelease( pConnection );
}
}
return dsStatus;
}
tDirStatus CLDAPConnectionManager::VerifyCredentials( CLDAPConnection *inConnection, const char *inLDAPUsername, const char *inRecordType,
const char *inKerberosID, const char *inPassword )
{
tDirStatus siResult = eDSAuthFailed;
CLDAPConnection *pConnection = NULL;
if ( inConnection != NULL )
{
pConnection = inConnection->CreateCopy();
if ( pConnection != NULL )
{
siResult = pConnection->Authenticate( inLDAPUsername, inRecordType, inKerberosID, inPassword );
DSRelease( pConnection );
}
}
return siResult;
}
void CLDAPConnectionManager::NodeDeleted( const char *inNodeName )
{
CLDAPConnection *pConnection = NULL;
fLDAPConnectionMapMutex.WaitLock();
LDAPConnectionMapI aLDAPConnectionMapI = fLDAPConnectionMap.find( inNodeName );
if ( aLDAPConnectionMapI != fLDAPConnectionMap.end() )
{
pConnection = aLDAPConnectionMapI->second;
fLDAPConnectionMap.erase( aLDAPConnectionMapI );
}
fLDAPConnectionMapMutex.SignalLock();
DSRelease( pConnection );
}
void CLDAPConnectionManager::PeriodicTask( void )
{
if ( gSystemGoingToSleep )
return;
bool bShouldCheckThread = false;
LDAPConnectionMapI aLDAPConnectionMapI;
LDAPAuthConnectionList cleanupList;
CLDAPv3Plugin::WaitForNetworkTransitionToFinish();
fLDAPConnectionMapMutex.WaitLock();
for ( aLDAPConnectionMapI = fLDAPConnectionMap.begin(); aLDAPConnectionMapI != fLDAPConnectionMap.end(); )
{
CLDAPConnection *pConnection = aLDAPConnectionMapI->second;
if ( pConnection->fNodeConfig->fConfigDeleted == true ||
(pConnection->RetainCount() == 1 && pConnection->ConnectionStatus() == kConnectionSafe) )
{
DbgLog( kLogPlugin, "CLDAPConnectionManager::PeriodicTask - Status Node: %s -- References: 0 -- removing from table",
aLDAPConnectionMapI->first.c_str() );
cleanupList.push_back( pConnection );
fLDAPConnectionMap.erase( aLDAPConnectionMapI++ );
continue;
}
else if ( pConnection->ConnectionStatus() != kConnectionSafe )
{
bShouldCheckThread = true;
}
pConnection->PeriodicTask();
aLDAPConnectionMapI++;
}
LDAPAuthConnectionListI aLDAPAuthConnectionListI;
for ( aLDAPAuthConnectionListI = fLDAPAuthConnectionList.begin(); aLDAPAuthConnectionListI != fLDAPAuthConnectionList.end(); )
{
CLDAPConnection *pConnection = (*aLDAPAuthConnectionListI);
if ( pConnection->fNodeConfig->fConfigDeleted == true ||
(pConnection->RetainCount() == 1 && pConnection->ConnectionStatus() == kConnectionSafe) )
{
DbgLog( kLogPlugin, "CLDAPConnectionManager::PeriodicTask - Status Node: %s:%s -- References: 0 -- removing from table",
pConnection->fNodeConfig->fNodeName, pConnection->fLDAPUsername );
cleanupList.push_back( pConnection );
aLDAPAuthConnectionListI++;
fLDAPAuthConnectionList.remove( pConnection );
continue;
}
else if ( pConnection->ConnectionStatus() != kConnectionSafe )
{
bShouldCheckThread = true;
}
pConnection->PeriodicTask();
aLDAPAuthConnectionListI++;
}
fLDAPConnectionMapMutex.SignalLock();
for ( LDAPAuthConnectionListI cleanupListI = cleanupList.begin(); cleanupListI != cleanupList.end(); cleanupListI++ )
{
(*cleanupListI)->Release();
}
if ( bShouldCheckThread )
{
LaunchCheckFailedThread( true );
}
}
void CLDAPConnectionManager::NetworkTransition( void )
{
if ( gSystemGoingToSleep )
return;
fLDAPConnectionMapMutex.WaitLock();
LDAPConnectionMapI aLDAPConnectionMapI;
for ( aLDAPConnectionMapI = fLDAPConnectionMap.begin(); aLDAPConnectionMapI != fLDAPConnectionMap.end(); ++aLDAPConnectionMapI )
aLDAPConnectionMapI->second->NetworkTransition();
LDAPAuthConnectionListI aLDAPAuthConnectionListI;
for ( aLDAPAuthConnectionListI = fLDAPAuthConnectionList.begin(); aLDAPAuthConnectionListI != fLDAPAuthConnectionList.end();
aLDAPAuthConnectionListI++ )
{
(*aLDAPAuthConnectionListI)->NetworkTransition();
}
LaunchCheckFailedThread( true );
fLDAPConnectionMapMutex.SignalLock();
}
void CLDAPConnectionManager::SystemGoingToSleep( void )
{
OSAtomicTestAndSetBarrier( 0, &gSystemGoingToSleep );
fLDAPConnectionMapMutex.WaitLock();
LDAPConnectionMapI aLDAPConnectionMapI;
for ( aLDAPConnectionMapI = fLDAPConnectionMap.begin(); aLDAPConnectionMapI != fLDAPConnectionMap.end(); ++aLDAPConnectionMapI )
{
aLDAPConnectionMapI->second->SetConnectionStatus( kConnectionUnsafe );
aLDAPConnectionMapI->second->CloseConnectionIfPossible();
}
LDAPAuthConnectionListI aLDAPAuthConnectionListI;
for ( aLDAPAuthConnectionListI = fLDAPAuthConnectionList.begin(); aLDAPAuthConnectionListI != fLDAPAuthConnectionList.end();
++aLDAPAuthConnectionListI )
{
(*aLDAPAuthConnectionListI)->SetConnectionStatus( kConnectionUnsafe );
(*aLDAPAuthConnectionListI)->CloseConnectionIfPossible();
}
fLDAPConnectionMapMutex.SignalLock();
}
void CLDAPConnectionManager::SystemWillPowerOn( void )
{
DbgLog( kLogPlugin, "CLDAPConnectionManager::SystemWillPowerOn - clearing sleep flag" );
OSAtomicTestAndClearBarrier( 0, &gSystemGoingToSleep );
}
void CLDAPConnectionManager::CheckFailed( void )
{
if ( gSystemGoingToSleep )
return;
LDAPConnectionMap aCheckConnections;
LDAPAuthConnectionList cleanupList;
CLDAPv3Plugin::WaitForNetworkTransitionToFinish();
fLDAPConnectionMapMutex.WaitLock();
LDAPConnectionMapI aLDAPConnectionMapI;
for ( aLDAPConnectionMapI = fLDAPConnectionMap.begin(); aLDAPConnectionMapI != fLDAPConnectionMap.end(); )
{
CLDAPConnection *pConnection = aLDAPConnectionMapI->second;
if ( pConnection->fNodeConfig->fConfigDeleted == true ||
(pConnection->RetainCount() == 1 && pConnection->ConnectionStatus() == kConnectionSafe) )
{
DbgLog( kLogPlugin, "CLDAPConnectionManager::CheckFailed - Status Node: %s -- References: 0 -- removing from table",
aLDAPConnectionMapI->first.c_str() );
cleanupList.push_back( pConnection );
fLDAPConnectionMap.erase( aLDAPConnectionMapI++ );
continue;
}
else if ( pConnection->ConnectionStatus() != kConnectionSafe )
{
aCheckConnections[aLDAPConnectionMapI->first] = pConnection->Retain();
}
aLDAPConnectionMapI++;
}
LDAPAuthConnectionListI aLDAPAuthConnectionListI;
for ( aLDAPAuthConnectionListI = fLDAPAuthConnectionList.begin(); aLDAPAuthConnectionListI != fLDAPAuthConnectionList.end(); )
{
CLDAPConnection *pConnection = (*aLDAPAuthConnectionListI);
if ( pConnection->fNodeConfig->fConfigDeleted == true ||
(pConnection->RetainCount() == 1 && pConnection->ConnectionStatus() == kConnectionSafe) )
{
DbgLog( kLogPlugin, "CLDAPConnectionManager::CheckFailed - Status Node: %s:%s -- References: 0 -- removing from table",
pConnection->fNodeConfig->fNodeName, pConnection->fLDAPUsername );
cleanupList.push_back( pConnection );
aLDAPAuthConnectionListI++;
fLDAPAuthConnectionList.remove( pConnection );
}
else if ( pConnection->ConnectionStatus() != kConnectionSafe )
{
aCheckConnections[string(pConnection->fNodeConfig->fNodeName)+string(":")+string(pConnection->fLDAPUsername)] = pConnection->Retain();
}
aLDAPAuthConnectionListI++;
}
fLDAPConnectionMapMutex.SignalLock();
for ( LDAPAuthConnectionListI cleanupListI = cleanupList.begin(); cleanupListI != cleanupList.end(); cleanupListI++ )
{
(*cleanupListI)->Release();
}
CLDAPv3Plugin::WaitForNetworkTransitionToFinish();
if ( aCheckConnections.empty() == false )
{
DbgLog( kLogPlugin, "CLDAPConnectionManager::CheckFailed - checking %d node connections", aCheckConnections.size() );
for ( aLDAPConnectionMapI = aCheckConnections.begin(); aLDAPConnectionMapI != aCheckConnections.end(); ++aLDAPConnectionMapI )
{
CLDAPConnection *pConnection = aLDAPConnectionMapI->second;
pConnection->CheckFailed();
DSRelease( pConnection );
}
}
fCheckFailedEvent.PostEvent();
}
void CLDAPConnectionManager::LaunchCheckFailedThread( bool bForceCheck )
{
if ( (bForceCheck == true || (CFAbsoluteTimeGetCurrent() - fCheckFailedLastRun) > 30.0) &&
__sync_bool_compare_and_swap(&fCheckThreadActive, false, true) == true )
{
fCheckFailedEvent.ResetEvent();
dispatch_async( dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT),
^(void) {
CheckFailed();
fCheckFailedLastRun = CFAbsoluteTimeGetCurrent(); fCheckThreadActive = false;
} );
}
}