/* File: CLDAPNode.cpp Contains: LDAP node management class Copyright: © 2001 by Apple Computer, Inc., all rights reserved. NOT_FOR_OPEN_SOURCE */ #include #include //used for strcpy, etc. #include //used for malloc #include //use for isprint #include //error logging #include #include #include #include "CLDAPNode.h" #include "CLDAPv3Configs.h" #define OCSEPCHARS " '()$" extern CPlugInRef *gConfigTable; //TODO need STL type for config table instead extern uInt32 gConfigTableLen; // -------------------------------------------------------------------------------- // * CLDAPNode () // -------------------------------------------------------------------------------- CLDAPNode::CLDAPNode ( void ) { } // CLDAPNode // -------------------------------------------------------------------------------- // * ~CLDAPNode () // -------------------------------------------------------------------------------- CLDAPNode::~CLDAPNode ( void ) { } // ~CLDAPNode // --------------------------------------------------------------------------- // * SafeOpen // --------------------------------------------------------------------------- sInt32 CLDAPNode::SafeOpen ( char *inNodeName, LDAP **outLDAPHost, uInt32 *outLDAPConfigTableIndex ) { sInt32 siResult = eDSNoErr; sLDAPNodeStruct *pLDAPNodeStruct = nil; uInt32 iTableIndex = 0; sLDAPConfigData *pConfig = nil; int ldapPort = LDAP_PORT; char *aLDAPName = nil; bool bConfigFound = false; LDAPNodeMapI aLDAPNodeMapI; string aNodeName(inNodeName); //if already open then just get host and config index //if not open then bind to get host and search for config index //called from OpenDirNode //allow the inNodeName to have a suffixed ":portNumber" for directed open //inNodeName is NOT consumed here fLDAPNodeOpenMutex.Wait(); aLDAPNodeMapI = fLDAPNodeMap.find(aNodeName); if (aLDAPNodeMapI == fLDAPNodeMap.end()) { pLDAPNodeStruct = (sLDAPNodeStruct *) calloc(1, sizeof(sLDAPNodeStruct)); pLDAPNodeStruct->fRefCount = 1; pLDAPNodeStruct->fLDAPSessionMutex = new DSMutexSemaphore(); // don't care if this was originally in the config file or not // ie. allow non-configured connections if possible // however, they need to use the standard LDAP PORT if no config entry exists // search now for possible LDAP port entry //Cycle through the gConfigTable to get the LDAP port to use for the ldap_open //start at index of one since 0 is generic config for (iTableIndex=1; iTableIndexGetItemData( iTableIndex ); if (pConfig != nil) { if (pConfig->fServerName != nil) { if (::strcmp(pConfig->fServerName,inNodeName) == 0) { ldapPort = pConfig->fServerPort; bConfigFound = true; pLDAPNodeStruct->fLDAPConfigTableIndex = iTableIndex; //exit the for loop if entry found break; } // if name found } // if name not nil }// if config entry not nil } // loop over config table entries if (!bConfigFound) { //here we have not found a configuration but will allow the open //first check if there is a suffixed ':' port number on the inNodeName siResult = ParseLDAPNodeName( inNodeName, &aLDAPName, &ldapPort ); if (siResult == eDSNoErr) { pLDAPNodeStruct->fServerName = aLDAPName; pLDAPNodeStruct->fDirectLDAPPort = ldapPort; pLDAPNodeStruct->fLDAPConfigTableIndex = 0; //TODO need to access the LDAP server for possible mapping configuration that can be added //thus fLDAPConfigTableIndex would then be non-zero } } //add this to the fLDAPNodeMap fLDAPNodeMap[aNodeName] = pLDAPNodeStruct; //fLDAPNodeMap.insert(pair(aNodeName, pLDAPNodeStruct)); fLDAPNodeOpenMutex.Signal(); } else { pLDAPNodeStruct = aLDAPNodeMapI->second; fLDAPNodeOpenMutex.Signal(); pConfig = (sLDAPConfigData *)gConfigTable->GetItemData( pLDAPNodeStruct->fLDAPConfigTableIndex ); ldapPort = pConfig->fServerPort; pLDAPNodeStruct->fRefCount++; } if (siResult == eDSNoErr) { if (pLDAPNodeStruct->fLDAPSessionMutex != nil) { pLDAPNodeStruct->fLDAPSessionMutex->Wait(); } //call to bind here siResult = BindProc( pLDAPNodeStruct ); if (pLDAPNodeStruct->fLDAPSessionMutex != nil) { pLDAPNodeStruct->fLDAPSessionMutex->Signal(); } //set the out parameters now *outLDAPHost = pLDAPNodeStruct->fHost; *outLDAPConfigTableIndex = pLDAPNodeStruct->fLDAPConfigTableIndex; } return(siResult); } // SafeOpen // --------------------------------------------------------------------------- // * AuthOpen // --------------------------------------------------------------------------- sInt32 CLDAPNode::AuthOpen ( char *inNodeName, LDAP *inHost, char *inUserName, void *inAuthCredential, char *inAuthType, LDAP **outLDAPHost, uInt32 *inOutLDAPConfigTableIndex, bool shouldCloseOld ) { sInt32 siResult = eDSNoErr; sLDAPNodeStruct *pLDAPNodeStruct = nil; sLDAPNodeStruct *pLDAPAuthNodeStruct = nil; int ldapPort = LDAP_PORT; char *aLDAPName = nil; LDAPNodeMapI aLDAPNodeMapI; string aNodeName(inNodeName); //there must be a fLDAPNodeMap entry for this ie. SafeOpen was already called since //this would come from a know Node Ref then need to SafeClose the non Auth Open //this is not ref counted NOR reused //inOutLDAPConfigTableIndex could be passed in for a directed open ie. not zero //called from the hierarchy below DoAuthentication //inNodeName is NOT consumed here fLDAPNodeOpenMutex.Wait(); aLDAPNodeMapI = fLDAPNodeMap.find(aNodeName); if (aLDAPNodeMapI != fLDAPNodeMap.end()) { pLDAPNodeStruct = aLDAPNodeMapI->second; pLDAPAuthNodeStruct = (sLDAPNodeStruct *) calloc(1, sizeof(sLDAPNodeStruct)); if (pLDAPNodeStruct->fLDAPConfigTableIndex != 0) { pLDAPAuthNodeStruct->fLDAPConfigTableIndex = pLDAPNodeStruct->fLDAPConfigTableIndex; } else { pLDAPAuthNodeStruct->fServerName = pLDAPNodeStruct->fServerName; pLDAPAuthNodeStruct->fDirectLDAPPort = pLDAPNodeStruct->fDirectLDAPPort; } pLDAPAuthNodeStruct->fUserName = inUserName; pLDAPAuthNodeStruct->fAuthCredential = inAuthCredential; pLDAPAuthNodeStruct->fAuthType = inAuthType; fLDAPNodeOpenMutex.Signal(); //call to bind here siResult = BindProc( pLDAPAuthNodeStruct ); if (siResult == eDSNoErr) { //set the out parameters now *outLDAPHost = pLDAPAuthNodeStruct->fHost; *inOutLDAPConfigTableIndex = pLDAPNodeStruct->fLDAPConfigTableIndex; //using duplicate of SafeClose(inNodeName) code here directly since within mutex if ( shouldCloseOld ) { fLDAPNodeOpenMutex.Wait(); if (inHost == pLDAPNodeStruct->fHost) { pLDAPNodeStruct->fRefCount--; if (pLDAPNodeStruct->fRefCount == 0) { //remove the entry if refcount is zero after cleaning contents CleanLDAPNodeStruct(pLDAPNodeStruct); fLDAPNodeMap.erase(aNodeName); } } fLDAPNodeOpenMutex.Signal(); } } if (pLDAPAuthNodeStruct != nil) { free(pLDAPAuthNodeStruct); pLDAPAuthNodeStruct = nil; } } else if (inHost != nil) //case where a second Auth is made on the Dir Node but //original LDAPNodeMap entry was already SafeClosed { fLDAPNodeOpenMutex.Signal(); pLDAPAuthNodeStruct = (sLDAPNodeStruct *) calloc(1, sizeof(sLDAPNodeStruct)); //here we have no configuration but will allow the open //first check if there is a suffixed ':' port number on the inNodeName siResult = ParseLDAPNodeName( inNodeName, &aLDAPName, &ldapPort ); if (siResult == eDSNoErr) { pLDAPAuthNodeStruct->fServerName = aLDAPName; pLDAPAuthNodeStruct->fDirectLDAPPort = ldapPort; pLDAPAuthNodeStruct->fLDAPConfigTableIndex = *inOutLDAPConfigTableIndex; //if ( *inOutLDAPConfigTableIndex == 0 ) //{ //NO try again to access the LDAP server for possible mapping configuration that can be added //SINCE we assume that this cannot be retrieved since attempt will have already been made in //the original SafeOpen for this directed open //} //call to bind here siResult = BindProc( pLDAPAuthNodeStruct ); if (siResult == eDSNoErr) { //set the out parameter now *outLDAPHost = pLDAPAuthNodeStruct->fHost; if ( shouldCloseOld ) { ldap_unbind( inHost ); inHost = nil; } } else { siResult = eDSAuthFailed; } } if (pLDAPAuthNodeStruct != nil) { if (pLDAPAuthNodeStruct->fServerName != nil) { free(pLDAPAuthNodeStruct->fServerName); //only var owned by this temporary struct here } free(pLDAPAuthNodeStruct); pLDAPAuthNodeStruct = nil; } } else { fLDAPNodeOpenMutex.Signal(); siResult = eDSOpenNodeFailed; } return(siResult); }// AuthOpen // --------------------------------------------------------------------------- // * RebindSession // --------------------------------------------------------------------------- sInt32 CLDAPNode::RebindSession( char *inNodeName, LDAP *inHost, LDAP **outLDAPHost ) { sInt32 siResult = eDSNoErr; sLDAPNodeStruct *pLDAPNodeStruct = nil; LDAPNodeMapI aLDAPNodeMapI; string aNodeName(inNodeName); //must already be open //called from ?? fLDAPNodeOpenMutex.Wait(); aLDAPNodeMapI = fLDAPNodeMap.find(aNodeName); if (aLDAPNodeMapI != fLDAPNodeMap.end()) { pLDAPNodeStruct = aLDAPNodeMapI->second; //check if host already rebound ie. only relevant when in a Continue //since we cannot rebind for this context but a rebind has already taken place if (pLDAPNodeStruct->fHost != inHost) { siResult = eDSOpenNodeFailed; } if (siResult == eDSNoErr) { if (pLDAPNodeStruct->fHost != nil) { ldap_unbind(pLDAPNodeStruct->fHost); pLDAPNodeStruct->fHost = nil; } fLDAPNodeOpenMutex.Signal(); if (pLDAPNodeStruct->fLDAPSessionMutex != nil) { pLDAPNodeStruct->fLDAPSessionMutex->Wait(); } //call to bind here //TODO How many retries do we do? siResult = BindProc( pLDAPNodeStruct ); if (pLDAPNodeStruct->fLDAPSessionMutex != nil) { pLDAPNodeStruct->fLDAPSessionMutex->Signal(); } if (siResult == eDSNoErr) { //set the out parameters now *outLDAPHost = pLDAPNodeStruct->fHost; } } else { fLDAPNodeOpenMutex.Signal(); } } else //no entry in fLDAPNodeMap { fLDAPNodeOpenMutex.Signal(); siResult = eDSOpenNodeFailed; } return(siResult); }// RebindSession // --------------------------------------------------------------------------- // * SimpleAuth // --------------------------------------------------------------------------- sInt32 CLDAPNode::SimpleAuth( char *inNodeName, char *inUserName, void *inAuthCredential ) { sInt32 siResult = eDSNoErr; sLDAPNodeStruct *pLDAPAuthNodeStruct = nil; sLDAPNodeStruct *pLDAPNodeStruct = nil; LDAPNodeMapI aLDAPNodeMapI; string aNodeName(inNodeName); //use bind to do the auth //called from DoClearTextAuth fLDAPNodeOpenMutex.Wait(); aLDAPNodeMapI = fLDAPNodeMap.find(aNodeName); if (aLDAPNodeMapI != fLDAPNodeMap.end()) { pLDAPNodeStruct = aLDAPNodeMapI->second; //build the temporary auth node struct for use with BindProc pLDAPAuthNodeStruct = (sLDAPNodeStruct *) calloc(1, sizeof(sLDAPNodeStruct)); if (pLDAPNodeStruct->fLDAPConfigTableIndex != 0) { pLDAPAuthNodeStruct->fLDAPConfigTableIndex = pLDAPNodeStruct->fLDAPConfigTableIndex; } else { pLDAPAuthNodeStruct->fServerName = pLDAPNodeStruct->fServerName; pLDAPAuthNodeStruct->fDirectLDAPPort = pLDAPNodeStruct->fDirectLDAPPort; } pLDAPAuthNodeStruct->fHost = nil; //new session handle for this directed auth //don't want this bind to disrupt //any other ops on the LDAP server pLDAPAuthNodeStruct->fLDAPSessionMutex = nil; pLDAPAuthNodeStruct->fUserName = inUserName; pLDAPAuthNodeStruct->fAuthCredential = inAuthCredential; //fAuthType nil is default for using a password //now try to bind siResult = BindProc( pLDAPAuthNodeStruct ); if (siResult != eDSNoErr) { siResult = eDSAuthFailed; } //no cleanup of contents other than session handle if (pLDAPAuthNodeStruct->fHost != nil) { ldap_unbind(pLDAPAuthNodeStruct->fHost); } free(pLDAPAuthNodeStruct); fLDAPNodeOpenMutex.Signal(); } else //no entry in fLDAPNodeMap { fLDAPNodeOpenMutex.Signal(); siResult = eDSAuthFailed; } return(siResult); }// SimpleAuth // --------------------------------------------------------------------------- // * RebindAuthSession // --------------------------------------------------------------------------- sInt32 CLDAPNode::RebindAuthSession( char *inNodeName, LDAP *inHost, char *inUserName, void *inAuthCredential, char *inAuthType, uInt32 inLDAPConfigTableIndex, LDAP **outLDAPHost ) { sInt32 siResult = eDSNoErr; sLDAPNodeStruct *pLDAPAuthNodeStruct = nil; int ldapPort = LDAP_PORT; char *aLDAPName = nil; //must already have had a session //called from ?? if (inHost != nil) //want to use this Auth session for the Dir Node context //original LDAPNodeMap entry was already SafeClosed { pLDAPAuthNodeStruct = (sLDAPNodeStruct *) calloc(1, sizeof(sLDAPNodeStruct)); //here we have no configuration but will allow the open //first check if there is a suffixed ':' port number on the inNodeName siResult = ParseLDAPNodeName( inNodeName, &aLDAPName, &ldapPort ); if (siResult == eDSNoErr) { pLDAPAuthNodeStruct->fServerName = aLDAPName; pLDAPAuthNodeStruct->fDirectLDAPPort = ldapPort; pLDAPAuthNodeStruct->fHost = nil; pLDAPAuthNodeStruct->fLDAPConfigTableIndex = inLDAPConfigTableIndex; pLDAPAuthNodeStruct->fUserName = inUserName; pLDAPAuthNodeStruct->fAuthCredential = inAuthCredential; //fAuthType nil is default for using a password ldap_unbind(inHost); //call to bind here //TODO How many retries do we do? siResult = BindProc( pLDAPAuthNodeStruct ); if (siResult == eDSNoErr) { //set the out parameter now *outLDAPHost = pLDAPAuthNodeStruct->fHost; } } if (pLDAPAuthNodeStruct != nil) { if (pLDAPAuthNodeStruct->fServerName != nil) { free(pLDAPAuthNodeStruct->fServerName); //only var owned by this temporary struct here } free(pLDAPAuthNodeStruct); pLDAPAuthNodeStruct = nil; } } else { siResult = eDSOpenNodeFailed; } return(siResult); }// RebindAuthSession // --------------------------------------------------------------------------- // * SafeClose // --------------------------------------------------------------------------- sInt32 CLDAPNode::SafeClose ( char *inNodeName, LDAP *inHost ) { sInt32 siResult = eDSNoErr; sLDAPNodeStruct *pLDAPNodeStruct = nil; LDAPNodeMapI aLDAPNodeMapI; string aNodeName(inNodeName); //decrement ref count and delete if ref count zero //if Auth call was active then the inHost will NOT be nil and we need to unbind it //called from CloseDirNode fLDAPNodeOpenMutex.Wait(); if (inHost != nil) { //this check is for auth call active closes ldap_unbind( inHost ); } else { aLDAPNodeMapI = fLDAPNodeMap.find(aNodeName); if (aLDAPNodeMapI != fLDAPNodeMap.end()) { pLDAPNodeStruct = aLDAPNodeMapI->second; pLDAPNodeStruct->fRefCount--; if (pLDAPNodeStruct->fRefCount == 0) { //remove the entry if refcount is zero after cleaning contents CleanLDAPNodeStruct(pLDAPNodeStruct); fLDAPNodeMap.erase(aNodeName); free(pLDAPNodeStruct); } } } fLDAPNodeOpenMutex.Signal(); return(siResult); }// SafeClose // --------------------------------------------------------------------------- // * GetSchema // --------------------------------------------------------------------------- void CLDAPNode::GetSchema ( sLDAPContextData *inContext ) { sInt32 siResult = eDSNoErr; sLDAPConfigData *pConfig = nil; LDAPMessage *LDAPResult = nil; BerElement *ber; struct berval **bValues; char *pAttr = nil; sObjectClassSchema *aOCSchema = nil; bool bSkipToTag = true; char *lineEntry = nil; char *strtokContext = nil; LDAP *aHost = nil; if ( inContext != nil ) { pConfig = (sLDAPConfigData *)gConfigTable->GetItemData( inContext->fConfigTableIndex ); if (pConfig != nil) { aHost = LockSession(inContext); if ( (aHost != nil) && !(pConfig->bOCBuilt) ) //valid LDAP handle and schema not already built { //at this point we can make the call to the LDAP server to determine the object class schema //then after building the ObjectClassMap we can assign it to the pConfig->fObjectClassSchema //in either case we set the pConfig->bOCBuilt since we made the attempt siResult = GetSchemaMessage( aHost, pConfig->fSearchTimeout, &LDAPResult); if (siResult == eDSNoErr) { //parse the attributes in the LDAPResult - should only be one ie. objectclass 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 each value of the attribute we need to parse and add as an entry to the objectclass schema map for (int i = 0; bValues[i] != NULL; i++ ) { aOCSchema = nil; if (lineEntry != nil) //delimiter chars will be overwritten by NULLs { free(lineEntry); lineEntry = nil; } //here we actually parse the values lineEntry = (char *)calloc(1,bValues[i]->bv_len+1); strcpy(lineEntry, bValues[i]->bv_val); char *aToken = nil; //find the objectclass name 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) { //now use the NAME to create an entry //first check if that NAME is already present - unlikely if (aOCClassMap->find(aToken) == aOCClassMap->end()) { aOCSchema = new(sObjectClassSchema); (*aOCClassMap)[aToken] = aOCSchema; } } } if (aOCSchema == nil) { continue; } if (aToken == nil) { continue; } //here we have the NAME - at least one of them //now check if there are any more NAME values 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); //get the other SUP entries 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); //get the other MUST entries 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); //get the other MAY entries 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; } } } // for each bValues[i] if (lineEntry != nil) //delimiter chars will be overwritten by NULLs { free(lineEntry); lineEntry = nil; } ldap_value_free_len(bValues); pConfig->fObjectClassSchema = aOCClassMap; } // if bValues = ldap_get_values_len ... if (pAttr != nil) { ldap_memfree( pAttr ); } } // for ( loop over ldap_next_attribute ) if (ber != nil) { ber_free( ber, 0 ); } ldap_msgfree( LDAPResult ); } } pConfig->bOCBuilt = true; UnLockSession(inContext); } } } // GetSchema // --------------------------------------------------------------------------- // * ParseLDAPNodeName // --------------------------------------------------------------------------- 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; //parse a string with a name and possibly a suffix of ':' followed by a port number if (inNodeName != nil) { inLength = strlen(inNodeName); portPos = strchr(inNodeName, ':'); //check if ':' found if (portPos != nil) { portPos++; //check if nothing after ':' if (portPos != nil) { ldapPort = strtoul(portPos,NULL,NULL); //if error in conversion set back to default if (ldapPort == 0) { ldapPort = LDAP_PORT; } inLength = inLength - strlen(portPos); } //strip off the suffix ':???' 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); }// ParseLDAPNodeName // --------------------------------------------------------------------------- // * CleanLDAPNodeStruct // --------------------------------------------------------------------------- sInt32 CLDAPNode::CleanLDAPNodeStruct ( sLDAPNodeStruct *inLDAPNodeStruct ) { sInt32 siResult = eDSNoErr; //assumes struct has ownership of all members if (inLDAPNodeStruct != nil) { if (inLDAPNodeStruct->fLDAPSessionMutex != nil) { inLDAPNodeStruct->fLDAPSessionMutex->Wait(); } if (inLDAPNodeStruct->fHost != nil) { ldap_unbind( inLDAPNodeStruct->fHost ); inLDAPNodeStruct->fHost = nil; } if (inLDAPNodeStruct->fLDAPSessionMutex != nil) { inLDAPNodeStruct->fLDAPSessionMutex->Signal(); delete(inLDAPNodeStruct->fLDAPSessionMutex); inLDAPNodeStruct->fLDAPSessionMutex = nil; } if (inLDAPNodeStruct->fServerName != nil) { free( inLDAPNodeStruct->fServerName ); inLDAPNodeStruct->fServerName = nil; } if (inLDAPNodeStruct->fUserName != nil) { free( inLDAPNodeStruct->fUserName ); inLDAPNodeStruct->fUserName = nil; } if (inLDAPNodeStruct->fAuthCredential != nil) { free( inLDAPNodeStruct->fAuthCredential ); inLDAPNodeStruct->fAuthCredential = nil; } if (inLDAPNodeStruct->fAuthType != nil) { free( inLDAPNodeStruct->fAuthType ); inLDAPNodeStruct->fAuthType = nil; } } return(siResult); }// CleanLDAPNodeStruct // --------------------------------------------------------------------------- // * BindProc // --------------------------------------------------------------------------- sInt32 CLDAPNode::BindProc ( sLDAPNodeStruct *inLDAPNodeStruct ) { sInt32 siResult = eDSNoErr; int bindMsgId = 0; int version = -1; sLDAPConfigData *pConfig = nil; char *ldapAcct = nil; char *ldapPasswd = nil; int openTO = 0; LDAP *inLDAPHost = inLDAPNodeStruct->fHost; LDAPMessage *result = nil; int ldapReturnCode = 0; int retryDelayTime = 0; try { if ( inLDAPNodeStruct == nil ) throw( (sInt32)eDSNullParameter ); if (inLDAPNodeStruct->fLDAPSessionMutex != nil) { inLDAPNodeStruct->fLDAPSessionMutex->Wait(); } // Here is the bind to the LDAP server // Note that there may be stored name/password in the config table // ie. always use the config table data if authentication has not explicitly been set // use LDAPAuthNodeMap if inLDAPNodeStruct contains a username //check that we were already here if (inLDAPHost == NULL) { //retrieve the config data //don't need to retrieve for the case of "generic unknown" so don't check index 0 if (( inLDAPNodeStruct->fLDAPConfigTableIndex < gConfigTableLen) && ( inLDAPNodeStruct->fLDAPConfigTableIndex >= 1 )) { pConfig = (sLDAPConfigData *)gConfigTable->GetItemData( inLDAPNodeStruct->fLDAPConfigTableIndex ); if (pConfig != nil) { if ( (pConfig->bSecureUse) && (inLDAPNodeStruct->fUserName == nil) ) { if (pConfig->fServerAccount != nil) { ldapAcct = new char[1+::strlen(pConfig->fServerAccount)]; ::strcpy( ldapAcct, pConfig->fServerAccount ); } if (pConfig->fServerPassword != nil) { ldapPasswd = new char[1+::strlen(pConfig->fServerPassword)]; ::strcpy( ldapPasswd, pConfig->fServerPassword ); } } else { if (inLDAPNodeStruct->fUserName != nil) { ldapAcct = new char[1+::strlen(inLDAPNodeStruct->fUserName)]; ::strcpy( ldapAcct, inLDAPNodeStruct->fUserName ); } if (inLDAPNodeStruct->fAuthCredential != nil) { if (inLDAPNodeStruct->fAuthType != nil) { //auth type of clear text means char * password if (strcmp(inLDAPNodeStruct->fAuthType,kDSStdAuthClearText) == 0) { ldapPasswd = new char[1+::strlen((char*)(inLDAPNodeStruct->fAuthCredential))]; ::strcpy( ldapPasswd, (char*)(inLDAPNodeStruct->fAuthCredential) ); } } else //default is password { ldapPasswd = new char[1+::strlen((char*)(inLDAPNodeStruct->fAuthCredential))]; ::strcpy( ldapPasswd, (char*)(inLDAPNodeStruct->fAuthCredential) ); } } } openTO = pConfig->fOpenCloseTimeout; } } if (inLDAPNodeStruct->fLDAPConfigTableIndex != 0) { if (pConfig != nil) { inLDAPHost = ldap_init( pConfig->fServerName, pConfig->fServerPort ); } } else { inLDAPHost = ldap_init( inLDAPNodeStruct->fServerName, inLDAPNodeStruct->fDirectLDAPPort ); } if ( inLDAPHost == nil ) throw( (sInt32)eDSCannotAccessSession ); if (pConfig != nil) { if ( pConfig->bIsSSL ) { int ldapOptVal = LDAP_OPT_X_TLS_HARD; ldap_set_option(inLDAPHost, LDAP_OPT_X_TLS, &ldapOptVal); } } /* LDAPv3 only */ version = LDAP_VERSION3; ldap_set_option( inLDAPHost, LDAP_OPT_PROTOCOL_VERSION, &version ); //heuristic to prevent many consecutive failures with long timeouts //ie. forcing quick failures after first failure during a window of //the same length as the timeout value fLDAPNodeOpenMutex.Wait(); if ( inLDAPNodeStruct->bHasFailed ) { if ( time( nil ) < inLDAPNodeStruct->fDelayedBindTime ) { fLDAPNodeOpenMutex.Signal(); throw( (sInt32)eDSCannotAccessSession ); } else { inLDAPNodeStruct->bHasFailed = false; //fDelayedBindTime then is unused so no need to reset } } fLDAPNodeOpenMutex.Signal(); //this is our and only our LDAP session for now //need to use our timeout so we don't hang indefinitely bindMsgId = ldap_bind( inLDAPHost, ldapAcct, ldapPasswd, LDAP_AUTH_SIMPLE ); if (openTO == 0) { ldapReturnCode = ldap_result(inLDAPHost, bindMsgId, 0, NULL, &result); } else { struct timeval tv; tv.tv_sec = openTO; tv.tv_usec = 0; ldapReturnCode = ldap_result(inLDAPHost, bindMsgId, 0, &tv, &result); } if ( ldapReturnCode == -1 ) { throw( (sInt32)eDSCannotAccessSession ); } else if ( ldapReturnCode == 0 ) { // timed out, let's forget it ldap_abandon(inLDAPHost, bindMsgId); if ( openTO < 120 ) { retryDelayTime = 120; //case where LDAP user has specified low timeout value so defaulting this } else { retryDelayTime = openTO; } //log this timed out connection if (pConfig != nil) { syslog(LOG_INFO,"DSLDAPv3PlugIn: Timed out in attempt to bind to [%s] LDAP server.", pConfig->fServerName); syslog(LOG_INFO,"DSLDAPv3PlugIn: Disabled future attempts to bind to [%s] LDAP server for next %d seconds.", pConfig->fServerName, retryDelayTime); } else { syslog(LOG_INFO,"DSLDAPv3PlugIn: Timed out in attempt to bind to [%s] LDAP server.", inLDAPNodeStruct->fServerName); syslog(LOG_INFO,"DSLDAPv3PlugIn: Disabled future attempts to bind to [%s] LDAP server for next %d seconds.", inLDAPNodeStruct->fServerName, retryDelayTime); } fLDAPNodeOpenMutex.Wait(); inLDAPNodeStruct->bHasFailed = true; inLDAPNodeStruct->fDelayedBindTime = time( nil ) + retryDelayTime; fLDAPNodeOpenMutex.Signal(); throw( (sInt32)eDSCannotAccessSession ); } else if ( ldap_result2error(inLDAPHost, result, 1) != LDAP_SUCCESS ) { throw( (sInt32)eDSCannotAccessSession ); } fLDAPNodeOpenMutex.Wait(); inLDAPNodeStruct->fHost = inLDAPHost; fLDAPNodeOpenMutex.Signal(); //result is consumed above within ldap_result2error result = nil; } } // try catch ( sInt32 err ) { siResult = err; } if (ldapAcct != nil) { delete (ldapAcct); ldapAcct = nil; } if (ldapPasswd != nil) { delete (ldapPasswd); ldapPasswd = nil; } if (inLDAPNodeStruct->fLDAPSessionMutex != nil) { inLDAPNodeStruct->fLDAPSessionMutex->Signal(); } return (siResult); }// BindProc //------------------------------------------------------------------------------------ // * GetSchemaMessage //------------------------------------------------------------------------------------ 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; struct berval **bValues; char *pAttr = nil; try { //search for the specific LDAP record subschemasubentry at the rootDSE which contains //the "dn" of the subschema record // here is the call to the LDAP server asynchronously which requires // host handle, search base, search scope(LDAP_SCOPE_SUBTREE for all), search filter, // attribute list (NULL for all), return attrs values flag // Note: asynchronous call is made so that a MsgId can be used for future calls // This returns us the message ID which is used to query the server for the results if ( (ldapMsgId = ldap_search( inHost, "", LDAP_SCOPE_BASE, "(objectclass=*)", sattrs, 0) ) == -1 ) { bResultFound = false; } else { bResultFound = true; //retrieve the actual LDAP record data for use internally //useful only from the read-only perspective if (inSearchTO == 0) { ldapReturnCode = ldap_result(inHost, ldapMsgId, 0, NULL, &result); } else { struct timeval tv; tv.tv_sec = inSearchTO; tv.tv_usec = 0; ldapReturnCode = ldap_result(inHost, ldapMsgId, 0, &tv, &result); } } if ( (bResultFound) && ( ldapReturnCode == LDAP_RES_SEARCH_ENTRY ) ) { siResult = eDSNoErr; //get the subschemaDN here //parse the attributes in the result - should only be one ie. subschemasubentry 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) { // should be only one value of the attribute 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 bValues = ldap_get_values_len ... if (pAttr != nil) { ldap_memfree( pAttr ); } } // for ( loop over ldap_next_attribute ) if (ber != nil) { ber_free( ber, 0 ); } ldap_msgfree( result ); result = nil; } // if bResultFound and ldapReturnCode okay 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; } } if (subschemaDN != nil) { //here we call to get the actual subschema record //here is the call to the LDAP server asynchronously which requires // host handle, search base, search scope(LDAP_SCOPE_SUBTREE for all), search filter, // attribute list (NULL for all), return attrs values flag // Note: asynchronous call is made so that a MsgId can be used for future calls // This returns us the message ID which is used to query the server for the results if ( (ldapMsgId = ldap_search( inHost, subschemaDN, LDAP_SCOPE_BASE, "(objectclass=subSchema)", attrs, 0) ) == -1 ) { bResultFound = false; } else { bResultFound = true; //retrieve the actual LDAP record data for use internally //useful only from the read-only perspective //KW when write capability is added, we will need to re-read the result after a write if (inSearchTO == 0) { ldapReturnCode = ldap_result(inHost, ldapMsgId, 0, NULL, &result); } else { struct timeval tv; tv.tv_sec = inSearchTO; tv.tv_usec = 0; ldapReturnCode = ldap_result(inHost, ldapMsgId, 0, &tv, &result); } } free(subschemaDN); subschemaDN = nil; if ( (bResultFound) && ( ldapReturnCode == LDAP_RES_SEARCH_ENTRY ) ) { siResult = eDSNoErr; } // if bResultFound and ldapReturnCode okay 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; } } } } catch ( sInt32 err ) { siResult = err; } if (result != nil) { *outResultMsg = result; } return( siResult ); } // GetSchemaMessage //------------------------------------------------------------------------------------ // * IsTokenNotATag //------------------------------------------------------------------------------------ bool CLDAPNode::IsTokenNotATag ( char *inToken ) { if (inToken == nil) { return true; } //check for first char in inToken as an uppercase letter in the following set //"NDOSAMX" since that will cover the following tags //NAME,DESC,OBSOLETE,SUP,ABSTRACT,STRUCTURAL,AUXILIARY,MUST,MAY,X-ORIGIN 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) //appears that iPlanet uses a non-standard tag ie. post RFC 2252 { 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 ); } // IsTokenNotATag // --------------------------------------------------------------------------- // * Lock Session // --------------------------------------------------------------------------- LDAP* CLDAPNode::LockSession( sLDAPContextData *inContext ) { sLDAPNodeStruct *pLDAPNodeStruct = nil; LDAPNodeMapI aLDAPNodeMapI; if (inContext != nil) { if (inContext->authCallActive) { if (inContext->fLDAPSessionMutex != nil) { inContext->fLDAPSessionMutex->Wait(); } return inContext->fHost; } else { string aNodeName(inContext->fName); fLDAPNodeOpenMutex.Wait(); aLDAPNodeMapI = fLDAPNodeMap.find(aNodeName); if (aLDAPNodeMapI != fLDAPNodeMap.end()) { pLDAPNodeStruct = aLDAPNodeMapI->second; pLDAPNodeStruct->fRefCount++; } fLDAPNodeOpenMutex.Signal(); if (pLDAPNodeStruct != nil) { if (pLDAPNodeStruct->fLDAPSessionMutex != nil) { pLDAPNodeStruct->fLDAPSessionMutex->Wait(); } return pLDAPNodeStruct->fHost; } } } return nil; } // LockSession // --------------------------------------------------------------------------- // * UnLock Session // --------------------------------------------------------------------------- void CLDAPNode::UnLockSession( sLDAPContextData *inContext ) { sLDAPNodeStruct *pLDAPNodeStruct = nil; LDAPNodeMapI aLDAPNodeMapI; if (inContext != nil) { if (inContext->authCallActive) { if (inContext->fLDAPSessionMutex != nil) { inContext->fLDAPSessionMutex->Signal(); } } else { string aNodeName(inContext->fName); fLDAPNodeOpenMutex.Wait(); aLDAPNodeMapI = fLDAPNodeMap.find(aNodeName); if (aLDAPNodeMapI != fLDAPNodeMap.end()) { pLDAPNodeStruct = aLDAPNodeMapI->second; } if (pLDAPNodeStruct != nil) { if (pLDAPNodeStruct->fLDAPSessionMutex != nil) { pLDAPNodeStruct->fLDAPSessionMutex->Signal(); } pLDAPNodeStruct->fRefCount--; if (pLDAPNodeStruct->fRefCount == 0) { //remove the entry if refcount is zero after cleaning contents CleanLDAPNodeStruct(pLDAPNodeStruct); fLDAPNodeMap.erase(aNodeName); } } fLDAPNodeOpenMutex.Signal(); } } } //UnLockSession