LDAPv3SupportFunctions.cpp [plain text]
#include <syslog.h>
#include <DirectoryServiceCore/CSharedData.h>
#include "LDAPv3SupportFunctions.h"
#include "DirServicesUtilsPriv.h"
#include "CDSPluginUtils.h"
#include "CLDAPConnection.h"
#include "CLDAPNodeConfig.h"
#include "DSLDAPUtils.h"
#include "DSUtils.h"
#define kKerberosPrefsFilePath "/Library/Preferences/edu.mit.Kerberos"
#define kKerberosPrefsRecordTemplate "\
<?xml version=\"1.0\" encoding=\"UTF-8\"?> \
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"> \
<plist version=\"1.0\"> \
<dict> \
<key>KADM_List</key> \
<array> \
<string>%s</string> \
</array> \
<key>KDC_List</key> \
<array> \
<string>%s</string> \
</array> \
</dict> \
</plist>"
static tDirStatus DSCheckForLDAPResult( LDAP *inHost,
int32_t inSearchTimeout,
int inHowMany,
int inLDAPMsgId,
LDAPMessage **outResult )
{
tDirStatus siResult = eDSCannotAccessSession;
if ( inHost != NULL )
{
struct timeval tv = { inSearchTimeout, 0 };
int rc = ldap_result( inHost, inLDAPMsgId, inHowMany, &tv, outResult );
switch ( rc )
{
case LDAP_RES_SEARCH_ENTRY:
siResult = eDSNoErr;
break;
case LDAP_RES_SEARCH_RESULT:
{
int ldapErr = LDAP_SUCCESS;
char* ldapErrMsg = NULL;
rc = ldap_parse_result( inHost, *outResult, &ldapErr, NULL, &ldapErrMsg, NULL, NULL, 0);
if ( rc == LDAP_SUCCESS )
{
if ( ldapErr == LDAP_SUCCESS )
{
siResult = eDSRecordNotFound; }
else
{
siResult = eDSOperationFailed;
DbgLog( kLogNotice, "CLDAPv3::DSCheckForLDAPResult - LDAP server search result error %d: %s",
ldapErr, (ldapErrMsg == NULL || ldapErrMsg[0] == '\0') ? "Unknown error" : ldapErrMsg );
}
}
else
{
siResult = eDSOperationFailed;
DbgLog( kLogPlugin, "CLDAPv3::DSCheckForLDAPResult - ldap_parse_result() failed, return code = %d", rc );
}
DSFree( ldapErrMsg );
break;
}
case 0:
case -1:
siResult = eDSCannotAccessSession;
break;
default:
DbgLog( kLogPlugin, "CLDAPv3::DSCheckForLDAPResult - unexpected result from LDAP (result=%d).", rc );
siResult = eUndefinedError;
break;
}
}
if ( siResult != eDSNoErr && (*outResult) != NULL )
{
ldap_msgfree( (*outResult) );
(*outResult) = NULL;
}
return siResult;
}
tDirStatus DSInitiateOrContinueSearch( sLDAPContextData *inContext,
sLDAPContinueData *inContinue,
char *inSearchBase,
char **inAttrList,
ber_int_t inScope,
char *inQueryFilter,
LDAPMessage **outResult )
{
tDirStatus siResult = eDSCannotAccessSession;
(*outResult) = NULL;
LDAP *aHost = inContext->fLDAPConnection->LockLDAPSession();
if ( aHost != NULL )
{
if ( inContinue->fLDAPMsgId == 0 )
{
int iRecCount = 0;
int numRetries = 2;
if ( inContinue->fLimitRecSearch != 0 )
iRecCount = inContinue->fLimitRecSearch - inContinue->fTotalRecCount;
do
{
int rc = ldap_search_ext( aHost, inSearchBase, inScope, inQueryFilter, inAttrList, 0, NULL, NULL, 0,
iRecCount, &(inContinue->fLDAPMsgId) );
switch ( rc )
{
case LDAP_FILTER_ERROR:
siResult = eParameterError;
break;
case LDAP_SUCCESS:
siResult = eDSNoErr;
break;
case LDAP_UNAVAILABLE:
case LDAP_SERVER_DOWN:
case LDAP_BUSY:
case LDAP_LOCAL_ERROR:
case LDAP_TIMEOUT:
siResult = eDSCannotAccessSession;
break;
default:
siResult = eDSRecordNotFound;
break;
}
if ( siResult == eDSNoErr )
{
inContinue->fRefLD = aHost;
siResult = DSCheckForLDAPResult( aHost, inContext->fLDAPConnection->fNodeConfig->fSearchTimeout, LDAP_MSG_ONE,
inContinue->fLDAPMsgId, outResult );
}
if ( siResult != eDSNoErr && inContinue->fLDAPMsgId > 0 )
{
ldap_abandon_ext( aHost, inContinue->fLDAPMsgId, NULL, NULL );
inContinue->fLDAPMsgId = 0;
inContinue->fRefLD = NULL;
}
if ( siResult == eDSCannotAccessSession )
{
DbgLog( kLogPlugin, "CLDAPv3::DSInitiateOrContinueSearch - search initiate failed trying %d more times", numRetries );
if ( (*outResult) != NULL )
{
ldap_msgfree( *outResult );
(*outResult) = NULL;
}
if ( inContinue->fLDAPMsgId > 0 )
{
ldap_abandon_ext( aHost, inContinue->fLDAPMsgId, NULL, NULL );
inContinue->fLDAPMsgId = 0;
inContinue->fRefLD = NULL;
}
if ( numRetries-- <= 0 )
break;
inContext->fLDAPConnection->UnlockLDAPSession( aHost, true );
aHost = inContext->fLDAPConnection->LockLDAPSession();
}
} while ( siResult == eDSCannotAccessSession && aHost != NULL );
}
else
{
if ( inContinue->fResult != NULL )
{
(*outResult) = inContinue->fResult;
inContinue->fResult = NULL;
siResult = eDSNoErr;
}
else
{
siResult = DSCheckForLDAPResult( aHost, inContext->fLDAPConnection->fNodeConfig->fSearchTimeout, LDAP_MSG_ONE,
inContinue->fLDAPMsgId, outResult );
}
}
if ( siResult == eDSCannotAccessSession || siResult == eDSRecordNotFound )
{
if ( (*outResult) != NULL )
{
ldap_msgfree( *outResult );
(*outResult) = NULL;
}
if ( inContinue->fLDAPMsgId > 0 )
{
if ( aHost == inContinue->fRefLD )
ldap_abandon_ext( aHost, inContinue->fLDAPMsgId, NULL, NULL );
inContinue->fLDAPMsgId = 0;
inContinue->fRefLD = NULL;
}
}
inContext->fLDAPConnection->UnlockLDAPSession( aHost, (siResult == eDSCannotAccessSession) );
}
return siResult;
}
tDirStatus DSRetrieveSynchronous( char *inSearchBase,
char **inAttrs,
sLDAPContextData *inContext,
ber_int_t inScope,
char *inQueryFilter,
LDAPMessage **outResult,
char **outDN )
{
tDirStatus siResult = eDSRecordNotFound;
int numRetries = 2;
(*outResult) = NULL;
LDAP *aHost = inContext->fLDAPConnection->LockLDAPSession();
if ( aHost != NULL )
{
struct timeval tv = { inContext->fLDAPConnection->fNodeConfig->fSearchTimeout, 0 };
do
{
int rc = ldap_search_ext_s( aHost, inSearchBase, inScope, inQueryFilter, inAttrs, false, NULL, NULL, &tv, 0, outResult );
if ( rc == LDAP_SUCCESS && (*outResult) != NULL )
{
if ( ldap_msgtype(*outResult) == LDAP_RES_SEARCH_ENTRY )
{
siResult = eDSNoErr;
if ( outDN != NULL )
(*outDN) = ldap_get_dn( aHost, (*outResult) );
}
else
{
ldap_msgfree( *outResult );
(*outResult) = NULL;
}
}
else if ( IsFatalLDAPError(rc) )
{
siResult = eDSCannotAccessSession;
inContext->fLDAPConnection->UnlockLDAPSession( aHost, true );
aHost = inContext->fLDAPConnection->LockLDAPSession();
}
} while ( siResult == eDSCannotAccessSession && aHost != NULL && numRetries-- > 0 );
inContext->fLDAPConnection->UnlockLDAPSession( aHost, (siResult == eDSCannotAccessSession) );
}
else
{
siResult = eDSCannotAccessSession;
}
return siResult;
}
char *GetDNForRecordName( char *inRecName,
sLDAPContextData *inContext,
const char *inRecordType )
{
char *ldapDN = NULL;
char *pLDAPSearchBase = NULL;
char *queryFilter = NULL;
LDAPMessage *result = NULL;
bool bOCANDGroup = false;
CFArrayRef OCSearchList = NULL;
ber_int_t scope = LDAP_SCOPE_SUBTREE;
tDirStatus searchResult = eDSNoErr;
if ( inRecName == nil ) return NULL;
if ( inContext == nil ) return NULL;
CLDAPNodeConfig *nodeConfig = inContext->fLDAPConnection->fNodeConfig;
if ( nodeConfig == NULL ) return NULL;
pLDAPSearchBase = nodeConfig->MapRecToSearchBase( inRecordType, 1, &bOCANDGroup, &OCSearchList, &scope );
if ( pLDAPSearchBase == nil ) return NULL;
queryFilter = nodeConfig->BuildLDAPQueryFilter( (char *)kDSNAttrRecordName, inRecName, eDSExact, false, inRecordType,
pLDAPSearchBase, bOCANDGroup, OCSearchList );
if ( queryFilter != NULL )
searchResult = DSRetrieveSynchronous( pLDAPSearchBase, NULL, inContext, scope, queryFilter, &result, &ldapDN );
if ( result != NULL )
{
ldap_msgfree( result );
result = NULL;
}
DSCFRelease( OCSearchList );
DSDelete( pLDAPSearchBase );
DSDelete( queryFilter );
return( ldapDN );
}
tDirStatus ldapParseAuthAuthority( const char *inAuthAuthority,
char **outVersion,
char **outAuthTag,
char **outAuthData )
{
char **authDataArray = NULL;
int keepIndex = 0;
tDirStatus status = dsParseAuthAuthorityExtended( inAuthAuthority, outVersion, outAuthTag, &authDataArray );
if ( status != eDSNoErr )
return status;
if( *outAuthTag && strcmp(*outAuthTag, kDSTagAuthAuthorityKerberosv5) == 0 )
keepIndex = 1;
for ( int idx = 0; authDataArray[idx] != NULL; idx++ )
{
if ( idx == keepIndex )
*outAuthData = authDataArray[idx];
else
DSFreeString( authDataArray[idx] );
}
DSFree( authDataArray );
return eDSNoErr;
}
void VerifyKerberosForRealm( const char *inRealmName, const char *inServer )
{
tDirStatus siResult = eDSNoErr;
tDirReference dsRef = 0;
tDataListPtr nodeDL = NULL;
tDirNodeReference dsNodeRef = 0;
tDataNodePtr pRecType = NULL;
tRecordReference recRef = 0;
tDataNodePtr pRecName = NULL;
tDataNodePtr pAttrName = NULL;
tDataList dataList = { 0 };
int error_num = 0;
struct hostent *hostEntryTemp = NULL;
struct hostent *hostEntry = NULL;
char recNameStr[256] = {0,};
char *kprefTemplateStr = NULL;
size_t kprefTemplateStrLen = 0;
if ( inServer == NULL )
{
DbgLog( kLogPlugin, "VerifyKerberosForRealm - inServer == NULL" );
return;
}
hostEntryTemp = getipnodebyname(inServer, AF_INET, AI_DEFAULT, &error_num);
if ( hostEntryTemp == NULL )
{
DbgLog( kLogPlugin, "VerifyKerberosForRealm - getipnodebyname returned NULL for server %s", inServer );
return;
}
hostEntry = getipnodebyaddr( hostEntryTemp->h_addr_list[0], hostEntryTemp->h_length, hostEntryTemp->h_addrtype, &error_num );
freehostent( hostEntryTemp );
hostEntryTemp = NULL;
if ( hostEntry == NULL )
{
DbgLog( kLogPlugin, "VerifyKerberosForRealm - gethostbyaddr returned NULL for server %s", inServer );
return;
}
siResult = dsOpenDirService( &dsRef );
if ( siResult != eDSNoErr )
goto cleanup;
nodeDL = dsBuildFromPathPriv( kstrDefaultLocalNodeName, "/" );
siResult = dsOpenDirNode( dsRef, nodeDL, &dsNodeRef );
dsDataListDeallocatePriv( nodeDL );
free( nodeDL );
if ( siResult != eDSNoErr )
{
DbgLog( kLogPlugin, "VerifyKerberosForRealm - Error %d while opening Directory Node " kstrDefaultLocalNodeName, siResult );
goto cleanup;
}
pRecType = dsDataNodeAllocateString( dsRef, kDSStdRecordTypeConfig );
snprintf( recNameStr, sizeof(recNameStr), "Kerberos:%s", inRealmName );
pRecName = dsDataNodeAllocateString( dsRef, recNameStr );
siResult = dsOpenRecord( dsNodeRef, pRecType, pRecName, &recRef );
switch ( siResult )
{
case eDSNoErr:
goto cleanup;
case eDSRecordNotFound:
break;
default:
DbgLog( kLogPlugin, "VerifyKerberosForRealm - Error %d while retrieving record /Config/%s", siResult, recNameStr );
goto cleanup;
}
siResult = dsCreateRecordAndOpen( dsNodeRef, pRecType, pRecName, &recRef );
if ( siResult != eDSNoErr )
{
DbgLog( kLogPlugin, "VerifyKerberosForRealm - Error %d attempting to create and open record /Config/%s",
siResult, recNameStr );
goto cleanup;
}
pAttrName = dsDataNodeAllocateString( dsRef, kDS1AttrXMLPlist );
kprefTemplateStr = (char *)malloc( (kprefTemplateStrLen = sizeof(kKerberosPrefsRecordTemplate) + strlen(hostEntry->h_name)*2) );
snprintf( kprefTemplateStr, kprefTemplateStrLen, kKerberosPrefsRecordTemplate, hostEntry->h_name, hostEntry->h_name );
dsBuildListFromStringsAlloc( dsRef, &dataList, kprefTemplateStr, NULL );
siResult = dsSetAttributeValues( recRef, pAttrName, &dataList );
if ( siResult != eDSNoErr )
{
DbgLog( kLogPlugin, "VerifyKerberosForRealm - Error %d attempting to set XMLPlist in record /Config/%s",
siResult, recNameStr );
}
dsDataListDeallocatePriv( &dataList );
cleanup:
DSFreeString( kprefTemplateStr );
if ( pAttrName != NULL )
dsDataNodeDeAllocate( dsRef, pAttrName );
if ( pRecName != NULL )
dsDataNodeDeAllocate( dsRef, pRecName );
if ( pRecType != NULL )
dsDataNodeDeAllocate( dsRef, pRecType );
if ( recRef != 0 )
dsCloseRecord( recRef );
if ( dsNodeRef != 0 )
dsCloseDirNode( dsNodeRef );
if ( dsRef != 0 )
dsCloseDirService( dsRef );
if ( hostEntry != NULL )
freehostent( hostEntry );
}