blob: b187cb19039c1d0e01d8c7c20c535a1ef29774e3 [file] [log] [blame]
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
//******************************************************************************
// LCOV_EXCL_START -- lets be a little paranoid and not let any in-line funcs
// from the header files slip into the coverage count
#include "authEvents.h"
#include "ldapconfignode.h"
#include "ldapconfigfile.h"
#include <sys/stat.h>
// These defines affect openLDAP header files and must appear before s
// those includes.
#undef HAVE_LDAPSSL_INIT
#undef HAVE_LDAP_INIT
#undef HAVE_LDAP_START_TLS_S
#undef LDAP_SET_REBIND_PROC_ARGS
#define HAVE_LDAP_INITIALIZE 1
#define HAVE_LDAP_SET_OPTION 1
#define LDAP_DEPRECATED 1
#define HAVE_LDAP_PARSE_RESULT 1
#define HAVE_LDAP_CONTROLS_FREE 1
#define HAVE_LDAP_SASL_BIND 1
#define LDAP_SASL_SIMPLE 1
//#define OPENLDAP_DEBUG 1
#include <lber.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
#include <string>
#include <vector>
#include <memory>
#include <sys/time.h>
#include <sys/param.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <ldap.h>
#include <ctime>
#include <netdb.h>
#include "common/evl_sqlog_eventnum.h"
// LCOV_EXCL_STOP
enum LD_Status {
LD_STATUS_OK = 200,
LD_STATUS_RESOURCE_FAILURE = 201
};
enum NodeState {
NODE_NOT_INITIALIZED = 1000,
NODE_ACTIVE = 1001
};
enum LDAP_VERSIONS { LDAP_VERSION_2 = 2, LDAP_VERSION_3 = 3};
#define INSERT_EVENT(authEvents,eventID,eventText) insertAuthEvent(authEvents, eventID,eventText,LL_ERROR)
static size_t numBindRetries = 0;
static size_t numSearchRetries = 0;
class ConfigHostContents
{
public:
ConfigHostContents();
ConfigHostContents(ConfigHostContents &rhs);
ConfigHostContents& operator=( const ConfigHostContents& rhs );
void refresh(LDAPHostConfig &);
LDAPHostConfig *LDAPConfig_;
long lastHostIndex_;
string lastHostName_;
vector<string> excludedHostNames;
};
class ConfigNodeContents
{
public:
ConfigNodeContents(
LDAPConfigNode::LDAPConfigType configType,
LDAPConnectionType connectionType);
LDAPConfigNode::LDAPConfigType configType_;
LDAPConnectionType connectionType_;
LDAP * searchLD_; // OpenLDAP connection for search
LDAP * authLD_; // OpenLDAP connection for auth
NodeState status_;
ConfigHostContents * host_;
};
static void addExcludedHostName(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
const char * hostName);
static LDAuthStatus bindUser(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
const char * username,
const char * password,
bool reconnect,
int & LDAPError);
static int closeConnection(ConfigNodeContents & self);
static inline bool connectToHost(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
const char * hostName,
bool isLoadBalanceHost,
LDAPURLDesc & url);
static LD_Status connectToURL(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
LDAPURLDesc & url);
static void convertUsername(string & username);
static bool getEffectiveHostName(
const char * hostName,
char * effectiveHostName);
static bool getNonExcludedHostName(
const char * hostName,
char * effectiveHostName,
const vector<string> &excludedHosts,
bool isLoadBalanceHost,
bool shouldExcludeBadHosts,
int retryCount,
int retryDelay);
static LD_Status initConnection(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
char * hostName,
bool skipHost = false);
static bool isHostNameExcluded(
const char * hostName,
const vector<string> & excludedHosts);
inline static void logConfigFileError(
std::vector<AuthEvent> & authEvents,
LDAPConfigFileErrorCode fileCode,
int lastLineNumber,
string & lastLine);
static bool readLDAPConfigFile(std::vector<AuthEvent> & authEvents);
static LDSearchStatus searchUser(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
const char * inputName,
string & userDN);
static LDSearchStatus searchUserByDN(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
const string & userDN);
static bool selfCheck(
ConfigNodeContents & self,
bool isInitialized);
inline static bool shouldReadLDAPConfig();
// Should have an array of configurations. Mgr hands out.
static LDAPConfigFile configFile;
static time_t lastRefreshTime = 0;
static LDAPFileContents config;
static ConfigHostContents primaryHost;
static ConfigHostContents secondaryHost;
// The following static pointers hold the connection node for each of the four
// connection/configuration combinations
// There are "search" and "authenticate" connection types and for each type
// there can be a "primary" and "secondary" LDAP server configuration.
static LDAPConfigNode * primarySearchMe = NULL;
static LDAPConfigNode * primaryAuthMe = NULL;
static LDAPConfigNode * secondarySearchMe = NULL;
static LDAPConfigNode * secondaryAuthMe = NULL;
#pragma page "ConfigHostContents::ConfigHostContents"
// *****************************************************************************
// * *
// * Function: ConfigHostContents::ConfigHostContents *
// * *
// * Constructor of a ConfigHostContents object. *
// * *
// *****************************************************************************
ConfigHostContents::ConfigHostContents()
: lastHostIndex_(0),
LDAPConfig_(NULL)
{
}
//**************** End of ConfigHostContents::ConfigHostContents ***************
ConfigHostContents::ConfigHostContents(ConfigHostContents &rhs)
{
lastHostIndex_ = rhs.lastHostIndex_;
LDAPConfig_ = rhs.LDAPConfig_;
lastHostName_ = rhs.lastHostName_;
excludedHostNames = rhs.excludedHostNames;
}
ConfigHostContents& ConfigHostContents::operator=(const ConfigHostContents& rhs)
{
lastHostIndex_ = rhs.lastHostIndex_;
LDAPConfig_ = rhs.LDAPConfig_;
lastHostName_ = rhs.lastHostName_;
excludedHostNames = rhs.excludedHostNames;
}
#pragma page "ConfigHostContents::refresh"
// *****************************************************************************
// * *
// * Function: ConfigHostContents::refresh *
// * *
// * Refreshes the fields of a ConfigHostContents instance. *
// * *
// *****************************************************************************
void ConfigHostContents::refresh(LDAPHostConfig &LDAPHostConfig)
{
LDAPConfig_ = &LDAPHostConfig;
lastHostIndex_ = 0;
lastHostName_.clear();
excludedHostNames.clear();
}
//********************* End of ConfigHostContents::refresh *********************
#pragma page "ConfigNodeContents::ConfigNodeContents"
// *****************************************************************************
// * *
// * Function: ConfigNodeContents::ConfigNodeContents *
// * *
// * constructor of a ConfigNodeContents object. *
// * *
// *****************************************************************************
// * *
// * Parameter: *
// * *
// * <configType> LDAPConfigType In *
// * specifies the configuration (primary or secondary) the node should use.*
// * *
// * <connectionType> LDAPConnectionType In *
// * is the type of connection (auth or search) the node will be used for. *
// * *
// *****************************************************************************
ConfigNodeContents::ConfigNodeContents(
LDAPConfigNode::LDAPConfigType configType,
LDAPConnectionType connectionType)
: configType_(configType),
connectionType_(connectionType),
searchLD_(NULL),
authLD_(NULL),
status_(NODE_NOT_INITIALIZED)
{
}
//**************** End of ConfigNodeContents::ConfigNodeContents ***************
#pragma page "LDAPConfigNode::ClearRetryCounts"
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::ClearRetryCounts *
// * *
// * Resets the retry counts to zero. *
// * *
// *****************************************************************************
void LDAPConfigNode::ClearRetryCounts()
{
numBindRetries = 0;
numSearchRetries = 0;
}
//****************** End of LDAPConfigNode::ClearRetryCounts *******************
#pragma page "LDAPConfigNode::CloseConnection"
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::CloseConnection *
// * *
// * Closes any currently opened connection. No error returned to caller *
// * if close operation fails or if no connections are open. *
// * *
// *****************************************************************************
void LDAPConfigNode::CloseConnection()
{
if (primaryAuthMe != NULL)
closeConnection(primaryAuthMe->self);
if (secondaryAuthMe != NULL)
closeConnection(secondaryAuthMe->self);
if (primarySearchMe != NULL)
closeConnection(primarySearchMe->self);
if (secondarySearchMe != NULL)
closeConnection(secondarySearchMe->self);
}
//****************** End of LDAPConfigNode::CloseConnection ********************
#pragma page "LDAPConfigNode::FreeInstance"
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::FreeInstance *
// * *
// * Destroys the singleton instance of a LDAPConfigNode object. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <configType> LDAPConfigType In *
// * is the configuration type (primary or secondary) of the node to *
// * be freed. *
// * *
// * <connectionType> LDAPConnectionType In *
// * is the connection type (auth or search) of the node to be freed. *
// * *
// *****************************************************************************
// LCOV_EXCL_START -- not called in normal testing
void LDAPConfigNode::FreeInstance(
LDAPConfigType configType,
LDAPConnectionType connectionType)
{
if (configType == PrimaryConfiguration)
{
if (connectionType == AuthenticationConnection)
{
if (primaryAuthMe != NULL)
{
delete primaryAuthMe;
primaryAuthMe = NULL;
}
}
else
{
if (primarySearchMe != NULL)
{
delete primarySearchMe;
primarySearchMe = NULL;
}
}
}
else
if (connectionType == AuthenticationConnection)
{
if (secondaryAuthMe != NULL)
{
delete secondaryAuthMe;
secondaryAuthMe = NULL;
}
}
else
{
if (secondarySearchMe != NULL)
{
delete secondarySearchMe;
secondarySearchMe = NULL;
}
}
}
// LCOV_EXCL_STOP
//******************** End of LDAPConfigNode::FreeInstance *********************
// *****************************************************************************
// * *
// * Function: GetBindRetryCount *
// * *
// * Returns the number of times this instance has retried a bind operation *
// * since the last initialize() call. *
// * *
// *****************************************************************************
// * *
// * Returns: size_t *
// * *
// *****************************************************************************
size_t LDAPConfigNode::GetBindRetryCount()
{
return numBindRetries;
}
//****************** End of LDAPConfigNode::GetBindRetryCount ******************
#pragma page "LDAPConfigNode::GetConfiguration"
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::GetConfiguration *
// * *
// * Gets an LDAP configuration based on the configuration type. Data is *
// * either read from a configuration file (.traf_authentication_config) or *
// * internal values are used. *
// * *
// *****************************************************************************
// * *
// * Returns: bool *
// * *
// * true - LDAP configuration nodes created. *
// * false - Unable to create LDAP configuration nodes. *
// * *
// *****************************************************************************
bool LDAPConfigNode::GetConfiguration(
std::vector<AuthEvent> & authEvents,
LDAPConfigType &configType)
{
// If we have not yet read the config file, attempt to read it. If that
// fails, return false. We could create an internal configurations and hope
// for the best. If so, we would need to report the problem accessing the
// LDAP configuration file.
if (!configFile.isInitialized())
{
if (!readLDAPConfigFile(authEvents))
return false;
// Configuration was successfully read, setup primary and secondary cache
primaryHost.refresh(config.primary);
secondaryHost.refresh(config.secondary);
}
return true;
}
//****************** End of LDAPConfigNode::GetConfiguration *******************
#pragma page "LDAPConfigNode::GetInstance"
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::GetInstance *
// * *
// * Constructs or returns the singleton instance of a LDAPConfigNode *
// * object. There are four subtypes of LDAPConfigNodes, one for each *
// * connection type and one for each configuration. Future work could *
// * merge the two connection types into one instance and manage the *
// * configurations using a map. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <configType> LDAPConfigType In *
// * is the configuration type of the node to be obtained. If configType *
// * is UnknownConfiguration, a configType is chosen based on the setting *
// * in the configuration file data. *
// * *
// * <connectionType> LDAPConnectionType In *
// * is the connection type (auth or search) of the node to be obtained. *
// * *
// *****************************************************************************
// * *
// * Returns: LDAPConfigNode * *
// * *
// * NULL - Unable to obtain the node (not likely) *
// * * - pointer to node matching the config and connection type. *
// * *
// *****************************************************************************
LDAPConfigNode * LDAPConfigNode::GetInstance(
std::vector<AuthEvent> & authEvents,
LDAPConfigType configType,
LDAPConnectionType connectionType)
{
if (!GetConfiguration(authEvents,configType))
return NULL;
// If the config type is not known (not specified, user requests default),
// use the value from the config file defaults section.
if (configType == UnknownConfiguration)
{
if (config.defaultToPrimary)
configType = PrimaryConfiguration;
else
configType = SecondaryConfiguration;
}
//
// There are four types of nodes
//
// 1) Primary Authentication (Bind) Node
// 2) Secondary Authentication (Bind) Node
// 3) Primary Search (Lookup) Node
// 4) Secondary Search (Lookup) Node
//
// We may have already create a node of the type desired. Check the static
// pointers, otherwise, instantiate a new instance.
//
if (connectionType == AuthenticationConnection)
{
if (configType == PrimaryConfiguration)
{
if (primaryAuthMe == NULL)
primaryAuthMe = new LDAPConfigNode(PrimaryConfiguration,AuthenticationConnection);
return primaryAuthMe;
}
else //Secondary Configuration
{
if (secondaryAuthMe == NULL)
secondaryAuthMe = new LDAPConfigNode(SecondaryConfiguration,AuthenticationConnection);
return secondaryAuthMe;
}
}
if (connectionType == SearchConnection)
{
if (configType == PrimaryConfiguration)
{
if (primarySearchMe == NULL)
primarySearchMe = new LDAPConfigNode(PrimaryConfiguration,SearchConnection);
return primarySearchMe;
}
else //Secondary Configuration
{
if (secondarySearchMe == NULL)
secondarySearchMe = new LDAPConfigNode(SecondaryConfiguration,SearchConnection);
return secondarySearchMe;
}
}
// We should not get here.
// LCOV_EXCL_START
return NULL;
// LCOV_EXCL_STOP
}
//******************** End of LDAPConfigNode::GetInstance **********************
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::GetLDAPConnection *
// * *
// * Obtains and initializes a connection node. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <configType> LDAPConfigType In *
// * is the configuration type of the node to be obtained. If configType *
// * is UnknownConfiguration, a configType is chosen based on the setting *
// * in the configuration file data. *
// * *
// * <connectionType> LDAPConnectionType In *
// * is the connection type (auth or search) of the node to be obtained. *
// * *
// * <hostName> char * [Out] *
// * if specified (non-NULL), passes back the name of the host if the *
// * connection is successful. *
// * *
// *****************************************************************************
// * *
// * Returns: LDAPConfigNode * *
// * *
// * NULL - Unable to obtain or initialize the node *
// * * - Valid connection node *
// * *
// *****************************************************************************
LDAPConfigNode *LDAPConfigNode::GetLDAPConnection(
std::vector<AuthEvent> & authEvents,
LDAPConfigType configType,
LDAPConnectionType connectionType,
char * hostName)
{
LDAPConfigNode *node = LDAPConfigNode::GetInstance(authEvents,configType,connectionType);
if (node == NULL || !node->initialize(authEvents,hostName))
{
// if node is not NULL, extract any events and put in authEvents
return NULL;
}
return node;
}
//******************* End of LDAPConfigNode::GetLDAPConnection *****************
// *****************************************************************************
// * *
// * Function: GetSearchRetryCount *
// * *
// * Returns the number of times this instance has retried a search *
// * operation since the last initialize call. *
// * *
// *****************************************************************************
// * *
// * Returns: size_t *
// * *
// *****************************************************************************
size_t LDAPConfigNode::GetSearchRetryCount()
{
return numSearchRetries;
}
//**************** End of LDAPConfigNode::GetSearchRetryCount ******************
#pragma page "LDAPConfigNode::Refresh"
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::Refresh *A
// * *
// * Closes any currently opened connections and rereads configuration file *
// * *
// *****************************************************************************
void LDAPConfigNode::Refresh(std::vector<AuthEvent> &authEvents)
{
CloseConnection();
readLDAPConfigFile(authEvents);
}
//********************* End of LDAPConfigNode::Refresh *************************
#pragma page "LDAPConfigNode::LDAPConfigNode"
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::LDAPConfigNode *
// * *
// * constructor of LDAPConfigNode object. *
// * *
// *****************************************************************************
// * *
// * Parameter: *
// * *
// * <configType> LDAPConfigType In *
// * is the configuration type (primary or secondary) of the node. *
// * *
// * <connectionType> LDAPConnectionType In *
// * is the type of connection (auth or search) the node will be used for. *
// * *
// *****************************************************************************
LDAPConfigNode::LDAPConfigNode(
LDAPConfigType configType,
LDAPConnectionType connectionType)
: self(*new ConfigNodeContents(configType,connectionType))
{
}
//******************* End of LDAPConfigNode::LDAPConfigNode ********************
#pragma page "LDAPConfigNode::~LDAPConfigNode"
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::~LDAPConfigNode *
// * *
// * Destructor of LDAPConfigNode. Not called for database authentications *
// * until the process is stopped, so clearing pointers is well, pointless. *
// * However, Live Feed chose to load this code as a DLL and subsequently *
// * unload and reload. For M9SP1 Live Feed has changed to only load once, *
// * but in case any other application chooses to load/unload/load, we need *
// * to reset the static pointers. *
// * *
// *****************************************************************************
// LCOV_EXCL_START
LDAPConfigNode::~LDAPConfigNode()
{
closeConnection(self);
if (self.configType_ == PrimaryConfiguration)
{
if (self.connectionType_ == AuthenticationConnection)
primaryAuthMe = NULL;
else
primarySearchMe = NULL;
}
else
if (self.connectionType_ == AuthenticationConnection)
secondaryAuthMe = NULL;
else
secondarySearchMe = NULL;
}
// LCOV_EXCL_STOP
//******************* End of LDAPConfigNode::~LDAPConfigNode *******************
// *****************************************************************************
// * *
// * Function: authenticateUser *
// * *
// * Performs a LDAP user authentication by calling LDAP bind APIs. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <username> const char * In *
// * is the external username. Must be defined on LDAP server. *
// * *
// * <password> const char * In *
// * is the password. Cannot be blank. *
// * *
// *****************************************************************************
// * *
// * Returns: LDAuthStatus *
// * *
// * LDAuthSuccessful: Username and password match values on server. *
// * LDAuthRejected: Either username or password does not match *
// * the values on the server. *
// * LDAuthResourceFailure: Problem communicating with LDAP server. Details *
// * in stdout. For non-Live Feed authentications *
// * error details are also in repository. *
// * *
// *****************************************************************************
LDAuthStatus LDAPConfigNode::authenticateUser(
std::vector<AuthEvent> & authEvents,
const char * username,
const char * password)
{
int LDAPError = LDAP_SUCCESS;
LD_Status status = LD_STATUS_OK;
char eventMsg[MAX_EVENT_MSG_SIZE];
LDAuthStatus authStatus = bindUser(authEvents,self,username,password,true,LDAPError);
if (!self.host_->LDAPConfig_->preserveConnection)
closeConnection(self);
// Unless we encountered a resource error, return the results
// of the authentication.
if (authStatus != LDAuthResourceFailure)
return authStatus;
//
// We had a problem communicating with the LDAP server. Retry as many times
// as requested, pausing between attempts as configured.
//
int retry_count = self.host_->LDAPConfig_->retryCount;
while (retry_count--)
{
numBindRetries++;
closeConnection(self);
sleep(self.host_->LDAPConfig_->retryDelay);
status = initConnection(authEvents,self,NULL,true);
if (status == LD_STATUS_OK)
{
authStatus = bindUser(authEvents,self,username,password,true,LDAPError);
if (!self.host_->LDAPConfig_->preserveConnection)
closeConnection(self);
// If the retry was successful, then return the results of the
// authentication. Otherwise, keep at it until we run out of retries.
if (authStatus != LDAuthResourceFailure)
return authStatus;
}
else
{
// Should we call initialize and try to read the config file again ?
// Refresh(authEvents);
}
}
//
// We tried, but the LDAP server(s) did not want to cooperate.
// Log how hard we tried so we don't get blamed.
//
if (self.host_->LDAPConfig_->retryCount)
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Failed to authenticate LDAP user %s after %d retries\n",
username,self.host_->LDAPConfig_->retryCount);
else
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Failed to authenticate LDAP user %s\n",username);
INSERT_EVENT(authEvents,DBS_UNKNOWN_AUTH_STATUS_ERROR,eventMsg);
return LDAuthResourceFailure;
}
//****************** End of LDAPConfigNode::authenticateUser *******************
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::getConfigType *
// * *
// * Returns the configuration type assigned to this node. Useful when *
// * defaulting and the type is chosen within the class. *
// * *
// *****************************************************************************
// * *
// * Returns: LDAPConfigType *
// * *
// *****************************************************************************
LDAPConfigNode::LDAPConfigType LDAPConfigNode::getConfigType() const
{
return self.configType_;
}
//******************* End of LDAPConfigNode::getConfigType *********************
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::initialize *
// * *
// * This member function calls LDAP APIs to initialize a connection. *
// * This signature of initConnection takes param which is the name of a *
// * file that holds the configuration information. That file is read and *
// * this configuration's attributes are set accordingly. The connection *
// * is then "initialzied" using those attributes *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <hostName> char * [Out] *
// * if specified (non-NULL), passes back the name of the host if the *
// * connection is successful. *
// * *
// *****************************************************************************
// * *
// * Returns: *
// * *
// * TRUE: Node initialized and connection is opened. *
// * FALSE: Initialization failed. *
// * *
// *****************************************************************************
bool LDAPConfigNode::initialize(
std::vector<AuthEvent> & authEvents,
char * hostName)
{
// Verify node has been setup correctly before we attempt to initialize the
// connection and setup the rest of the node.
if (!selfCheck(self,false))
{
INSERT_EVENT(authEvents,DBS_UNKNOWN_AUTH_STATUS_ERROR,"Self check failed in initialize");
return false;
}
bool doRead = shouldReadLDAPConfig(); //ACH pass in self and config
if (self.status_ == NODE_ACTIVE)
{
if (!doRead)
return true;
// Preserve connection was enabled, but we are re-reading the LDAP
// config file, which could change connection settings.
closeConnection(self);
}
if (doRead)
{
// If we cannot read the LDAP configuration file (.traf_authentication_config),
// we can't initialize the node.
if (!readLDAPConfigFile(authEvents))
return false;
// If we have refreshed the configuration, refresh all host config values
if (self.configType_ == PrimaryConfiguration)
primaryHost.refresh(config.primary);
else
secondaryHost.refresh(config.secondary);
}
if (self.configType_ == PrimaryConfiguration)
self.host_ = &primaryHost;
else
self.host_ = &secondaryHost;
LD_Status retCode = initConnection(authEvents,self,hostName);
if (retCode == LD_STATUS_OK)
return true;
int retry_count = self.host_->LDAPConfig_->retryCount;
while (retry_count-- > 0)
{
if (self.connectionType_ == AuthenticationConnection)
numBindRetries++;
else
numSearchRetries++;
sleep(self.host_->LDAPConfig_->retryDelay);
retCode = initConnection(authEvents,self,hostName);
if (retCode == LD_STATUS_OK)
return true;
}
char eventMsg[MAX_EVENT_MSG_SIZE];
if (self.host_->LDAPConfig_->retryCount > 0)
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Unable to establish initial LDAP connection after %d retries, error %d\n",
self.host_->LDAPConfig_->retryCount,retCode);
else
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Unable to establish initial LDAP connection, error %d\n",
retCode);
INSERT_EVENT(authEvents,DBS_NO_LDAP_SEARCH_CONNECTION,eventMsg);
return false;
}
//********************* End of LDAPConfigNode::initialize **********************
// *****************************************************************************
// * *
// * Function: LDAPConfigNode::lookupUser *
// * *
// * Searches for a username on the directory server(s) associated with *
// * this node. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <inputName> const char * In *
// * is the external username to lookup. *
// * *
// * <userDN> string & Out *
// * passes back the distingushed name for this user. *
// * *
// *****************************************************************************
// * *
// * Returns: LDSearchStatus *
// * *
// * LDSearchFound: Username was found on the server. *
// * LDSearchNotFound: Username was not found on the server. *
// * LDSearchResourceFailure: Problem communicating with LDAP server. *
// * Details in stdout. For non-Live Feed *
// * authentications, error details are also in *
// * repository. For Live Feed authentications and *
// * SQL username lookups, data is only stdout. *
// * *
// *****************************************************************************
LDSearchStatus LDAPConfigNode::lookupUser(
std::vector<AuthEvent> & authEvents,
const char * inputName,
string & userDN)
{
int rc = 0;
char eventMsg[MAX_EVENT_MSG_SIZE];
LDSearchStatus searchStatus = searchUser(authEvents,self,inputName,userDN);
if (!self.host_->LDAPConfig_->preserveConnection)
closeConnection(self);
// Unless we could not lookup the user due to a resource error, return
// the results of the search.
if (searchStatus != LDSearchResourceFailure)
return searchStatus;
//
// We had a problem communicating with the LDAP server. Retry as many times
// as requested, pausing between attempts as configured.
//
int retry_count = self.host_->LDAPConfig_->retryCount;
while (retry_count-- > 0)
{
numSearchRetries++;
closeConnection(self);
sleep(self.host_->LDAPConfig_->retryDelay);
LD_Status rc = initConnection(authEvents,self,NULL,true);
if (rc == LD_STATUS_OK)
{
searchStatus = searchUser(authEvents,self,inputName,userDN);
if (!self.host_->LDAPConfig_->preserveConnection)
closeConnection(self);
// Again, if we did not get a resource failure, then return the
// results of the search. Otherwise, keep at it until we
// exhaust the number of retries.
if (searchStatus != LDSearchResourceFailure)
return searchStatus;
}
else
{
// Should we call initialize and try to read the config file again ?
}
}
//
// OK, we gave it our best shot, but we could not communicate with the
// configured directory server(s). Log how hard we tried.
//
if (self.host_->LDAPConfig_->retryCount > 0)
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Failed to search for LDAP user %s after %d retries\n",
inputName,self.host_->LDAPConfig_->retryCount);
else
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Failed to search for LDAP user %s\n",inputName);
INSERT_EVENT(authEvents,DBS_NO_LDAP_SEARCH_CONNECTION,eventMsg);
return LDSearchResourceFailure;
}
//********************* End of LDAPConfigNode::lookupUser **********************
// *****************************************************************************
// * *
// * Begin file static functions. *
// * *
// *****************************************************************************
// *****************************************************************************
// * *
// * Function: addExcludedHostName *
// * *
// * This function adds a host name to the list of excluded host names, and *
// * removes old entries if the list is too large. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <self> ConfigNodeContents & In *
// * is a reference to an instance of a ConfigNodeContents object. *
// * *
// * <hostName> const char * In *
// * is name of the host to be excluded. *
// * *
// *****************************************************************************
static void addExcludedHostName(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
const char * hostName)
{
char eventMsg[MAX_EVENT_MSG_SIZE];
// If the size of the excluded host list is being limited, clear out
// older excluded hosts to make room for the newest entry.
if (self.host_->LDAPConfig_->maxExcludeListSize > 0)
while (self.host_->excludedHostNames.size() >= self.host_->LDAPConfig_->maxExcludeListSize)
{
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Exclude list full, LDAP server %s removed from exclude list\n",
self.host_->excludedHostNames[0].c_str());
INSERT_EVENT(authEvents,DBS_UNKNOWN_AUTH_STATUS_ERROR,eventMsg);
self.host_->excludedHostNames.erase(self.host_->excludedHostNames.begin());
}
self.host_->excludedHostNames.push_back(hostName);
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "LDAP server %s added to exclude list\n",hostName);
INSERT_EVENT(authEvents,DBS_UNKNOWN_AUTH_STATUS_ERROR,eventMsg);
}
//************************ End of addExcludedHostName **************************
// *****************************************************************************
// * *
// * Function: bindUser *
// * *
// * Performs a LDAP user authentication by calling LDAP bind. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <self> ConfigNodeContents & In *
// * is a reference to an instance of a ConfigNodeContents object. *
// * *
// * <username> const char * In *
// * is the name of the user to attempt to bind (logon). *
// * *
// * <password> const char * In *
// * is the password of the user being logged on. *
// * *
// * <reconnect> bool In *
// * is true, if the bind operation fails, attempt to reconnect (once). *
// * *
// * <LDAPError> int & Out *
// * passes back the LDAP error associated the the last suboperation *
// * of the bind operation. *
// * *
// * *
// *****************************************************************************
// * *
// * Returns: LDAuthStatus *
// * *
// * LDAuthSuccessful: username and password are good. *
// * LDAuthRejected: password does not match username. *
// * LDAuthResourceFailure: LDAP operation failed unexpectedly. *
// * *
// *****************************************************************************
static LDAuthStatus bindUser(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
const char * username,
const char * password,
bool reconnect,
int & LDAPError)
{
int rc, msgid, err;
struct timeval timeout;
LDAP *ld;
LDAPMessage *result;
char eventMsg[MAX_EVENT_MSG_SIZE];
int parserc;
LDAPControl **psrvctrls = NULL;
struct berval userpw;
char *errorTextString;
bool isInitialized = reconnect;
LDAPError = LDAP_SUCCESS;
while (true)
{
if (!selfCheck(self,isInitialized))
{
INSERT_EVENT(authEvents,DBS_UNKNOWN_AUTH_STATUS_ERROR,"Self check failed in bindUser");
return LDAuthResourceFailure;
}
if (self.connectionType_ == AuthenticationConnection)
ld = self.authLD_;
else
ld = self.searchLD_;
userpw.bv_val = (char *)password;
userpw.bv_len = (userpw.bv_val != 0) ? strlen (userpw.bv_val) : 0;
LDAPError = ldap_sasl_bind(ld,username,LDAP_SASL_SIMPLE,&userpw,psrvctrls,
0,&msgid);
if (LDAPError != LDAP_SUCCESS || msgid == -1)
{
// LCOV_EXCL_START
if (LDAPError == LDAP_SERVER_DOWN && reconnect)
{
// reconnect & retry
closeConnection(self);
LD_Status status = initConnection(authEvents,self,NULL,true);
if (status != LD_STATUS_OK)
{
INSERT_EVENT(authEvents,DBS_UNKNOWN_AUTH_STATUS_ERROR,"LDAP Auth Error in bindUser; unable to connect to server");
return LDAuthResourceFailure;
}
reconnect = false;
continue;
}
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "LDAP Auth Error in bindUser; error code: %ld, ", (long) LDAPError);
errorTextString = ldap_err2string(LDAPError);
strncat(eventMsg, errorTextString, (MAX_EVENT_MSG_SIZE - (strlen(eventMsg)+4)));
strcat(eventMsg,"\n");
INSERT_EVENT(authEvents,DBS_NO_LDAP_AUTH_CONNECTION,eventMsg);
return LDAuthResourceFailure;
// LCOV_EXCL_STOP
}
// Use value set by LDAP_OPT_TIMEOUT, pass NULL as argument
// timeout.tv_sec = 5;
// timeout.tv_usec = 0; // set timeout to 5 sec
rc = ldap_result (ld, msgid, 1, NULL, &result);
if (rc == -1 || rc == 0) // timeout if rc =0, server down if rc = -1
{
// retry if timeout
if (reconnect)
{
// reconnect & retry
closeConnection(self);
LD_Status status = initConnection(authEvents,self,NULL,true);
if (status != LD_STATUS_OK)
{
// LCOV_EXCL_START
INSERT_EVENT(authEvents,DBS_UNKNOWN_AUTH_STATUS_ERROR,"LDAP Auth Error in bindUser; unable to connect to server");
return LDAuthResourceFailure;
// LCOV_EXCL_STOP
}
reconnect = false;
continue;
}
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "LDAP Auth Error in bindUser; error code: %ld, ", (long)err);
errorTextString = ldap_err2string(err);
strncat(eventMsg, errorTextString, (MAX_EVENT_MSG_SIZE - (strlen(eventMsg)+4)));
strcat(eventMsg, "\n");
INSERT_EVENT(authEvents,DBS_NO_LDAP_AUTH_CONNECTION,eventMsg);
LDAPError = err;
return LDAuthResourceFailure;
}
LDAPControl **ctrls;
parserc = ldap_parse_result (ld, result, &rc, 0, 0, 0, &ctrls, 1);
if (parserc != LDAP_SUCCESS)
{
// LCOV_EXCL_START
char *p = ldap_err2string(parserc);
if (p != NULL)
{
strcpy(eventMsg, "LDAP Auth Error in bindUser; Failed to get bind result: ");
strncat(eventMsg, p, (MAX_EVENT_MSG_SIZE - (strlen(eventMsg)+4)) );
strcat(eventMsg, "\n");
}
else
strcpy(eventMsg, "LDAP Auth Error in bindUser; Failed to get bind result.\n");
INSERT_EVENT(authEvents,DBS_UNKNOWN_AUTH_STATUS_ERROR,eventMsg);
LDAPError = parserc;
return LDAuthResourceFailure;
// LCOV_EXCL_STOP
}
// *****************************************************************************
// * *
// * Password policy is not supported. Support would include notify users *
// * that their authentication failed because their password has expired or *
// * warning that is about to expire, or that they are in a grace period and *
// * need to change their password before a certain date or some number of *
// * authentications. The code is still present, but beyond the check is not *
// * executed. If supported is ever added, the structure needs to be *
// * initialized and passed back to callers. *
// * *
// *****************************************************************************
// Error code returned from password policy control
#define PSWPOLICY_PASSWORDEXPIRED 0
#define PSWPOLICY_ACCOUNTLOCKED 1
#define PSWPOLICY_FIRST_AFTER_RESET 2
#define PSWPOLICY_NOERROR 65535
if (ctrls)
{
// LCOV_EXCL_START
class LdapPasswordPolicy {
public:
int error; // error code returned from password policy control
int expire;
int grace;
};
// pre-set password policy (not supported)
//if (pwdPolicy != NULL)
//{
// pwdPolicy->error = PSWPOLICY_NOERROR;
// pwdPolicy->expire = -1;
// pwdPolicy->grace = -1;
//}
LDAPControl *ctrl = ldap_find_control( LDAP_CONTROL_PASSWORDPOLICYRESPONSE, ctrls );
if (ctrl)
{
LdapPasswordPolicy pwdPolicy;
ldap_parse_passwordpolicy_control(ld,ctrl,&pwdPolicy.expire,
&pwdPolicy.grace,
(LDAPPasswordPolicyError *)&pwdPolicy.error );
}
// LCOV_EXCL_STOP
}
switch (rc)
{
case LDAP_SUCCESS:
return LDAuthSuccessful;
break;
case LDAP_NO_SUCH_OBJECT:
case LDAP_INVALID_CREDENTIALS:
return LDAuthRejected;
break;
default:
// LCOV_EXCL_START
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "LDAP Auth Error in bindUser; error code: %ld, ", (long)rc);
errorTextString = ldap_err2string(rc);
strncat(eventMsg, errorTextString, (MAX_EVENT_MSG_SIZE - (strlen(eventMsg)+4)));
strcat(eventMsg, "\n");
INSERT_EVENT(authEvents,DBS_NO_LDAP_AUTH_CONNECTION,eventMsg);
LDAPError = rc;
return LDAuthResourceFailure;
break;
// LCOV_EXCL_STOP
}
} // while (true)
}
//***************************** End of bindUser ********************************
// *****************************************************************************
// * *
// * Function: closeConnection *
// * This function calls LDAP APIs to close a connection. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <self> ConfigNodeContents & In *
// * is a reference to an instance of a ConfigNodeContents object. *
// * *
// *****************************************************************************
// * *
// * Returns: OpenLDAP error code *
// * *
// *****************************************************************************
static int closeConnection(ConfigNodeContents & self)
{
int rc = LDAP_SUCCESS;
if (self.connectionType_ == AuthenticationConnection && self.authLD_ != NULL)
{
rc = ldap_unbind(self.authLD_);
self.authLD_ = NULL;
self.status_ = NODE_NOT_INITIALIZED;
return rc;
}
if (self.connectionType_ == SearchConnection && self.searchLD_ != NULL)
{
rc = ldap_unbind(self.searchLD_);
self.searchLD_ = NULL;
self.status_ = NODE_NOT_INITIALIZED;
return rc;
}
return rc;
}
//*************************** End of closeConnection ***************************
// *****************************************************************************
// * *
// * Function: connectToHost *
// * *
// * This function attempts to connect to an LDAP host. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <self> ConfigNodeContents & In *
// * is a reference to an instance of a ConfigNodeContents object. *
// * *
// * <hostName> const char * In *
// * is a string containing the host name. *
// * *
// * <isLoadBalanceHost> bool In *
// * is true if <hostName> is a load balancing host. *
// * *
// * <url> LDAPURLDesc & In *
// * is a reference to a LDAP URL Description. *
// * *
// * *
// *****************************************************************************
// * *
// * Returns: bool *
// * *
// * true: a "good" host name was found. *
// * false: Could not find a host name not on the exclude list. :( *
// * *
// *****************************************************************************
static inline bool connectToHost(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
const char * hostName,
bool isLoadBalanceHost,
LDAPURLDesc & url)
{
char effectiveHostName[MAX_HOSTNAME_LENGTH + 1];
// If we can't get a good host, don't even bother trying to connect (waste of
// time!), just return false and let the caller deal with it.
if (!getNonExcludedHostName(hostName,effectiveHostName,
self.host_->excludedHostNames,
isLoadBalanceHost,
self.host_->LDAPConfig_->excludeBadHosts,
self.host_->LDAPConfig_->retryCount,
self.host_->LDAPConfig_->retryDelay))
return false;
// We have a good host. Let's try to connect.
url.lud_host = effectiveHostName;
LD_Status status = connectToURL(authEvents,self,url);
if (status == LD_STATUS_OK)
{
self.host_->lastHostName_ = url.lud_host;
return true;
}
// Could not connect to that host. If we are excluding bad hosts, add it
// to the exclude host name list.
if (self.host_->LDAPConfig_->excludeBadHosts)
addExcludedHostName(authEvents,self,url.lud_host);
return false;
}
//**************************** End of connectToHost ****************************
// *****************************************************************************
// * *
// * Function: connectToURL *
// * *
// * This function calls LDAP APIs to initialize a connection. *
// * It will also make an LDAP call to bind with search user so to make *
// * sure later a user search can be performed. *
// * This is called by the outer initConnection member function for each *
// * url configured. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <self> ConfigNodeContents & In *
// * is a reference to an instance of a ConfigNodeContents object. *
// * *
// * <url> LDAPURLDesc & In *
// * is a reference to a LDAP URL Description. *
// * *
// *****************************************************************************
// * *
// * Returns: LD_Status *
// * *
// * LD_STATUS_OK: a connection was made *
// * LD_STATUS_RESOURCE_FAILURE: a connection could not be made *
// * *
// *****************************************************************************
static LD_Status connectToURL(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
LDAPURLDesc & url)
{
int version;
int debug = 0;
int rc;
char eventMsg[MAX_EVENT_MSG_SIZE];
char *errorTextString;
LDAP *ld = NULL;
struct timeval tv;
char *ldapuri = ldap_url_desc2str( &url );
rc = ldap_initialize (&ld, ldapuri);
if (rc != LDAP_SUCCESS)
{
// LCOV_EXCL_START
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "ldap_initialize failed for LDAP server %s. Error: %d, ",url.lud_host, rc);
errorTextString = ldap_err2string(rc);
strncat(eventMsg, errorTextString, (MAX_EVENT_MSG_SIZE - (strlen(eventMsg)+4)));
strcat(eventMsg, "\n");
INSERT_EVENT(authEvents,DBS_NO_LDAP_SEARCH_CONNECTION,eventMsg);
return LD_STATUS_RESOURCE_FAILURE;
// LCOV_EXCL_STOP
}
if (ld == NULL)
{
// LCOV_EXCL_START
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Failed to initialize the connection to LDAP server %s. Error: ld is NULL", url.lud_host);
INSERT_EVENT(authEvents,DBS_NO_LDAP_SEARCH_CONNECTION,eventMsg);
return LD_STATUS_RESOURCE_FAILURE;
// LCOV_EXCL_STOP
}
version = LDAP_VERSION_3;
(void) ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
// set dereference alias entry to LDAP_DEREF_ALWAYS.
// disable this option if LDAP server has alias entries cause performace issues.
int ldapderef = LDAP_DEREF_ALWAYS;
(void) ldap_set_option (ld, LDAP_OPT_DEREF, &ldapderef);
// set password policy controls
LDAPControl *ctrls[2], c;
c.ldctl_oid = (char *) LDAP_CONTROL_PASSWORDPOLICYREQUEST;
c.ldctl_value.bv_val = NULL;
c.ldctl_value.bv_len = 0;
c.ldctl_iscritical = 0;
ctrls[0] = &c;
ctrls[1] = NULL;
ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls );
// Note: set timeout value to -1 means wait until it finishes
// Default is -1 - no limit.
if (self.host_->LDAPConfig_->timeout > 0)
{
tv.tv_sec = self.host_->LDAPConfig_->timeout;
tv.tv_usec = 0;
(void) ldap_set_option (ld, LDAP_OPT_TIMEOUT, &tv);
}
// Note: set timelimit value to 0 means wait until it finishes
// Default is LDAP_NO_LIMIT (0) - no limit.
if (self.host_->LDAPConfig_->timeLimit > 0)
{
tv.tv_sec = self.host_->LDAPConfig_->timeLimit;
tv.tv_usec = 0;
(void) ldap_set_option (ld, LDAP_OPT_TIMELIMIT, &tv);
}
// Note: If not set then LDAP NETWORK_TIMEOUT
// Default is -1 - infinite timeout
if (self.host_->LDAPConfig_->networkTimeout > 0)
{
tv.tv_sec = self.host_->LDAPConfig_->networkTimeout;
tv.tv_usec = 0;
(void) ldap_set_option (ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
}
(void) ldap_set_option (ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
(void) ldap_set_option (ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
// LCOV_EXCL_START
// Regular SSL mode
if (self.host_->LDAPConfig_->SSL_Level == YES_SSL)
{
// set for SSL port, not for TLS port
int tls = LDAP_OPT_X_TLS_HARD;
(void) ldap_set_option (ld, LDAP_OPT_X_TLS, &tls);
}
// LCOV_EXCL_STOP
// Setup certificate file
if (self.host_->LDAPConfig_->SSL_Level == YES_TLS ||
self.host_->LDAPConfig_->SSL_Level == YES_SSL)
{
int demand = LDAP_OPT_X_TLS_DEMAND;
rc = ldap_set_option(ld,LDAP_OPT_X_TLS_REQUIRE_CERT,&demand);
if (rc != LDAP_SUCCESS)
{
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Require TLS certificate failed for LDAP server %s. Error: %d, ", url.lud_host, rc);
errorTextString = ldap_err2string(rc);
strncat(eventMsg, errorTextString, (MAX_EVENT_MSG_SIZE - (strlen(eventMsg)+4)));
strcat(eventMsg, "\n");
INSERT_EVENT(authEvents,DBS_NO_LDAP_SEARCH_CONNECTION,eventMsg);
return LD_STATUS_RESOURCE_FAILURE;
}
rc = ldap_set_option(NULL,LDAP_OPT_X_TLS_CACERTFILE,
config.TLS_CACERTFilename.c_str());
if (rc != LDAP_SUCCESS)
{
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Set TLS certificate file failed for LDAP server %s. Error: %d, ", url.lud_host, rc);
errorTextString = ldap_err2string(rc);
strncat(eventMsg, errorTextString, (MAX_EVENT_MSG_SIZE - (strlen(eventMsg)+4)));
strcat(eventMsg, "\n");
INSERT_EVENT(authEvents,DBS_NO_LDAP_SEARCH_CONNECTION,eventMsg);
return LD_STATUS_RESOURCE_FAILURE;
}
}
// startTLS
if (self.host_->LDAPConfig_->SSL_Level == YES_TLS)
{
rc = ldap_start_tls_s (ld, NULL, NULL);
if (rc != LDAP_SUCCESS)
{
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "StartTLS failed for LDAP server %s. Error: %d, ", url.lud_host, rc);
errorTextString = ldap_err2string(rc);
strncat(eventMsg, errorTextString, (MAX_EVENT_MSG_SIZE - (strlen(eventMsg)+4)));
strcat(eventMsg, "\n");
INSERT_EVENT(authEvents,DBS_NO_LDAP_SEARCH_CONNECTION,eventMsg);
return LD_STATUS_RESOURCE_FAILURE;
}
}
if (debug)
{
// LCOV_EXCL_START
ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug );
ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug );
// LCOV_EXCL_STOP
}
int LDAPError = LDAP_SUCCESS;
LDAuthStatus authStatus;
if (self.connectionType_ == AuthenticationConnection)
{
self.authLD_ = ld;
authStatus = bindUser(authEvents,self,"","",false,LDAPError);
if (authStatus != LDAuthSuccessful)
{
// LCOV_EXCL_START
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Initial bind failed for LDAP server %s. Error: %d, ",
url.lud_host,LDAPError);
errorTextString = ldap_err2string(LDAPError);
strncat(eventMsg, errorTextString, (MAX_EVENT_MSG_SIZE - (strlen(eventMsg)+4)));
strcat(eventMsg, "\n");
INSERT_EVENT(authEvents,DBS_NO_LDAP_AUTH_CONNECTION,eventMsg);
return LD_STATUS_RESOURCE_FAILURE;
// LCOV_EXCL_STOP
}
self.status_ = NODE_ACTIVE;
return LD_STATUS_OK;
}
// Search Connection
self.searchLD_ = ld;
authStatus = bindUser(authEvents,self,
self.host_->LDAPConfig_->searchDN.c_str(),
self.host_->LDAPConfig_->searchPwd.c_str(),
false,
LDAPError);
if (authStatus != LDAuthSuccessful)
{
// LCOV_EXCL_START
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "Initial bind with search user failed for LDAP server %s. Error: %d, ", url.lud_host, LDAPError);
errorTextString = ldap_err2string(LDAPError);
strncat(eventMsg, errorTextString, (MAX_EVENT_MSG_SIZE - (strlen(eventMsg)+4)));
strcat(eventMsg, "\n");
INSERT_EVENT(authEvents,DBS_NO_LDAP_AUTH_CONNECTION,eventMsg);
return LD_STATUS_RESOURCE_FAILURE;
// LCOV_EXCL_STOP
}
self.status_ = NODE_ACTIVE;
return LD_STATUS_OK;
}
//***************************** End of connectToURL ****************************
// *****************************************************************************
// * *
// * Function: convertUsername *
// * *
// * Scans string and for all reserved characters inserts the escape *
// * character (\). *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <username> string & In/Out *
// * is a string containing the username to be converted. *
// * *
// * *
// *****************************************************************************
static void convertUsername(string & username)
{
const char *reserveChar = ",+\"\\<>;#=";
char newName[513]; // Must be twice as big as largest name, plus 1
char *nPtr = newName;
const char *uPtr = username.c_str();
unsigned int uLen = strlen(uPtr);
for (int i = 0; i < uLen; i++)
{
if (strchr(reserveChar, *uPtr) != NULL)
{
// found a reserved character
*nPtr++ = '\\';
}
*nPtr++ = *uPtr++;
}
*nPtr = '\0';
username = newName;
}
//************************* End of convertUsername *****************************
// *****************************************************************************
// * *
// * Function: getEffectiveHostName *
// * *
// * This function resolves a host name and returns the effective host name. *
// * e.g. load balancer host name abc.com *
// * effective host name server1.abc.com *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <hostName> const char * In *
// * is a string containing the host name. *
// * *
// * *
// * <effectiveHostName> char * Out *
// * passes back a string containing the effective host name. *
// * *
// * *
// *****************************************************************************
// * *
// * Returns: bool *
// * *
// * true: the name was resolved *
// * false: error resolving the name, use the original name. *
// * *
// *****************************************************************************
static bool getEffectiveHostName(
const char * hostName,
char * effectiveHostName)
{
struct hostent *hstnm;
hstnm = gethostbyname(hostName);
if (!hstnm)
{
strcpy(effectiveHostName,hostName);
return false;
}
// May need to support IPv6 in the future, this is good enough for now.
hstnm = gethostbyaddr((const void *)*hstnm->h_addr_list,4,AF_INET);
if (!hstnm)
{
strcpy(effectiveHostName,hostName);
return false;
}
strcpy(effectiveHostName,hstnm->h_name);
return true;
}
//********************** End of getEffectiveHostName ***************************
// *****************************************************************************
// * *
// * Function: initConnection *
// * *
// * This function calls LDAP APIs to initialize a connection. *
// * It will also make an LDAP call to bind with search user so to make *
// * sure later a user search can be performed. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <self> ConfigNodeContents & In *
// * is a reference to an instance of a ConfigNodeContents object. *
// * *
// * <hostName> char * [Out] *
// * if specified (non-NULL), passes back the name of the host if the *
// * connection is successful. *
// * *
// * <skipHost> bool [In] *
// * if specified and true, the last connected host is added to the *
// * excluded hosts list and the connection is attempted with the next *
// * host in the list. *
// * *
// *****************************************************************************
// * *
// * Returns: LD_Status *
// * *
// * LD_STATUS_OK: a connection was made *
// * LD_STATUS_RESOURCE_FAILURE: a connection could not be made *
// * *
// *****************************************************************************
static LD_Status initConnection(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
char * hostName,
bool skipHost)
{
// Bailout if the section requested was not present in the config file
if (!self.host_->LDAPConfig_->sectionRead)
return LD_STATUS_RESOURCE_FAILURE;//ACH Need to log details. User registration type not defined in .traf_authentication_config.
LDAPURLDesc url;
memset( &url, 0, sizeof(url));
if (self.host_->LDAPConfig_->SSL_Level == NO_SSL ||
self.host_->LDAPConfig_->SSL_Level == YES_TLS)
url.lud_scheme = (char *) "ldap";
// LCOV_EXCL_START
else
url.lud_scheme = (char *) "ldaps";
// LCOV_EXCL_STOP
url.lud_port = self.host_->LDAPConfig_->portNumber;
url.lud_scope = LDAP_SCOPE_DEFAULT;
vector<string> hostNames;
//ACH why make a copy?
hostNames = self.host_->LDAPConfig_->hostName;
// If the caller has encountered a problem with the current connection,
// mark that host as bad and move on to the next one in the list.
if (skipHost)
{
addExcludedHostName(authEvents,self,self.host_->lastHostName_.c_str());
self.host_->lastHostIndex_ = (self.host_->lastHostIndex_ + 1) % hostNames.size();
}
// Just in case lastHostIndex_ is overwritten, check for large value and reset
if (self.host_->lastHostIndex_ >= hostNames.size())
self.host_->lastHostIndex_ = 0;
// Start from the host that we last made a good connection
int lastHostIndex = self.host_->lastHostIndex_;
for (int hostIndex = lastHostIndex; hostIndex < hostNames.size(); hostIndex++)
if (connectToHost(authEvents,self,(char *)hostNames[hostIndex].c_str(),
self.host_->LDAPConfig_->isLoadBalancer[hostIndex],url))
{
self.host_->lastHostIndex_ = hostIndex;
if (hostName != NULL)
strcpy(hostName,url.lud_host);
return LD_STATUS_OK;
}
// Start from the first Host Name and try the remaining hosts in the list
for (int hostIndex = 0; hostIndex < lastHostIndex; hostIndex++)
if (connectToHost(authEvents,self,(char *)hostNames[hostIndex].c_str(),
self.host_->LDAPConfig_->isLoadBalancer[hostIndex],url))
{
self.host_->lastHostIndex_ = hostIndex;
if (hostName != NULL)
strcpy(hostName,url.lud_host);
return LD_STATUS_OK;
}
return LD_STATUS_RESOURCE_FAILURE;
}
//**************************** End of initConnection ***************************
// *****************************************************************************
// * *
// * Function: isHostNameExcluded *
// * *
// * This function determines if a host name is on the naughty list. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <hostName> const char * In *
// * is a string containing the host name. *
// * *
// * <excludedHosts> const vector<string> & In *
// * is a list of hosts that should not be used. *
// * *
// *****************************************************************************
// * *
// * Returns: bool *
// * *
// * true: host name is on excluded list. :( *
// * false: host name is not on the excluded list. *
// * *
// *****************************************************************************
static bool isHostNameExcluded(
const char * hostName,
const vector<string> & excludedHosts)
{
for (size_t index = 0; index < excludedHosts.size(); index++)
if (excludedHosts[index].compare(hostName) == 0)
return true;
return false;
}
//************************** End of isHostNameExcluded *************************
// *****************************************************************************
// * *
// * Function: logConfigFileError *
// * *
// * This function logs the error encountered while processing the *
// * LDAP connection config file (.traf_authentication_config). *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <fileCode> LDAPConfigFileErrorCode In *
// * is a reference to an instance of a ConfigNodeContents object. *
// * *
// * <lastLineNumber> int In *
// * is the number of the last line that was read. *
// * *
// * <lastLine> string & In *
// * is the text of the last line that was read. *
// * *
// *****************************************************************************
inline static void logConfigFileError(
std::vector<AuthEvent> & authEvents,
LDAPConfigFileErrorCode fileCode,
int lastLineNumber,
string & lastLine)
{
char eventMsg[MAX_EVENT_MSG_SIZE];
switch (fileCode)
{
case LDAPConfigFile_OK:
return;
break;
case LDAPConfigFile_NoFileProvided:
case LDAPConfigFile_FileNotFound:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** .traf_authentication_config file not found\n");
break;
case LDAPConfigFile_BadAttributeName:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Unrecognized attribute in .traf_authentication_config configuration file. Line %d %s",
lastLineNumber,lastLine.c_str());
break;
case LDAPConfigFile_MissingValue:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Missing required value in .traf_authentication_config configuration file. Line %d %s",
lastLineNumber,lastLine.c_str());
break;
case LDAPConfigFile_ValueOutofRange:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Value out of range in .traf_authentication_config configuration file. Line %d %s",
lastLineNumber,lastLine.c_str());
break;
case LDAPConfigFile_CantOpenFile:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Unable to open .traf_authentication_config configuration file");
break;
case LDAPConfigFile_CantReadFile:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Unable to read .traf_authentication_config configuration file");
break;
case LDAPConfigFile_MissingCACERTFilename:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** TLS requested but no TLS CACERTFilename was provided");
break;
case LDAPConfigFile_MissingHostName:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Missing host name in .traf_authentication_config");
break;
case LDAPConfigFile_MissingUniqueIdentifier:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Missing unique identifier in .traf_authentication_config");
break;
case LDAPConfigFile_MissingSection:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Missing directory server configuration in .traf_authentication_config");
break;
case LDAPConfigFile_ParseError:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Internal error parsing .traf_authentication_config");
break;
case LDAPConfigFile_CantOpenLDAPRC:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Unable to open .ldaprc to determine TLS CACERTFilename");
break;
case LDAPConfigFile_MissingLDAPRC:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Missing .ldaprc and TLS_CACERTFilename not provided; cannot determine TLS CACERT filename");
break;
default:
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "****** Error parsing .traf_authentication_config configuration file");
}
INSERT_EVENT(authEvents,DBS_AUTH_CONFIG,eventMsg);
}
//************************** End of logConfigFileError *************************
// *****************************************************************************
// * *
// * Function: getNonExcludedHostName *
// * *
// * This function gets a host name to use for LDAP operations. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <hostName> const char * In *
// * is a string containing the host name. *
// * *
// * <effectiveHostName> char * Out *
// * passes back a string containing the effective host name. *
// * *
// * <excludedHosts> const vector<string> & In *
// * is a list of hosts that should not be used. *
// * *
// * <isLoadBalanceHost> bool In *
// * is true if <hostName> is a load balancing host. *
// * *
// * <shouldExcludeBadHosts> bool In *
// * is true if the <effectiveHostName> should not be in <excludedHost>. *
// * *
// * <retryCount> int In *
// * is the number of times a load balancer should be called to get a *
// * non-excluded host name if <shouldExcludeBadHost> is true. *
// * *
// * <retryDelay> int In *
// * is the time (in seconds) to delay between retry the load balancer. *
// * *
// *****************************************************************************
// * *
// * Returns: bool *
// * *
// * true: a "good" host name was found. *
// * false: Could not find a host name not on the exclude list. :( *
// * *
// *****************************************************************************
static bool getNonExcludedHostName(
const char * hostName,
char * effectiveHostName,
const vector<string> & excludedHosts,
bool isLoadBalanceHost,
bool shouldExcludeBadHosts,
int retryCount,
int retryDelay)
{
// If this is a load balance host (e.g. abc.com, get the effective
// host name. This name will then be used directly and any error messages
// will include the actual host name. If not the load balancer, use the
// host name that was passed in.
if (isLoadBalanceHost)
getEffectiveHostName(hostName,effectiveHostName);
else
strcpy(effectiveHostName,hostName);
if (!shouldExcludeBadHosts)
return true;
// If the effective host name is not on the list of excluded hosts, use it!
if (!isHostNameExcluded(effectiveHostName,excludedHosts))
return true;
// The host is on the excluded list. If this is not a load balancing host
// there is nothing that can be done here. Return false and hope caller's
// retry logic enables a successful connection.
if (!isLoadBalanceHost)
return false;
// Poke the load balancing host until we get a non-excluded name or
// exhaust the retries.
for (size_t i = 0; i < retryCount; i++)
{
sleep(retryDelay);
getEffectiveHostName(hostName,effectiveHostName);
if (isHostNameExcluded(effectiveHostName,excludedHosts))
continue;
return true;
}
return false;
}
//************************ End of getNonExcludedHostName ***********************
// *****************************************************************************
// * *
// * Function: readLDAPConfigFile *
// * *
// * Calls LDAPConfigFile class to read and parse the LDAP configuration *
// * file. If successful, the results are cached and the current time is *
// * recorded. If unsuccessful, an error is logged to stdout (and eventually *
// * the repository.) *
// * *
// *****************************************************************************
// * *
// * Returns: bool *
// * *
// * true: LDAP config file was read and parsed successfully *
// * false: Error while reading/parsing LDAP config file *
// * *
// *****************************************************************************
static bool readLDAPConfigFile(std::vector<AuthEvent> &authEvents)
{
string configFilename;
LDAPFileContents contents;
int lastLineNumber = -1;
string lastLine;
LDAPConfigFileErrorCode fileCode = configFile.read(configFilename,
config,
lastLineNumber,
lastLine);
if (fileCode != LDAPConfigFile_OK)
{
logConfigFileError(authEvents,fileCode,lastLineNumber,lastLine);
return false;
}
// Only update lastRefreshTime if reading and parsing was successful.
lastRefreshTime = time(0);
return true;
}
//************************** End of readLDAPConfigFile *************************
// *****************************************************************************
// * *
// * Function: searchUser *
// * *
// * This function performs a LDAP user search based on the *
// * input username, it also returns userDN, domainName, domainAttr *
// * about the user that was found. *
// * This funtion is called by ld_auth_user to look for the user first, *
// * then based on the information returned to perform user bind. *
// * *
// * The processing steps of this function: *
// * *
// * if UIF exists, parse domainName, userName *
// * *
// * if User Identifier exists *
// * Search by User Identifier, get DN *
// * else *
// * Search by Unique Identifier, get DN *
// * *
// * if domainName entered by user *
// * find the node matches to domainName, do bind with DN *
// * else *
// * if Domain Attribute exists *
// * Find the node matches to domainName in Domain Attribute, *
// * do bind with DN *
// * else *
// * do bind with DN in highest priority node? *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <self> ConfigNodeContents & In *
// * is a reference to a ConfigNodeContents. *
// * *
// * <inputName> const char * In *
// * is the name supplied by the user. *
// * *
// * <userDN> string & Out *
// * passes back the distingushed name for the user. *
// * *
// *****************************************************************************
// * *
// * Returns: LDSearchStatus *
// * *
// *****************************************************************************
static LDSearchStatus searchUser(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
const char * inputName,
string & userDN)
{
LDSearchStatus searchStatus = LDSearchNotFound;
vector<string> uniqueIdentifiers;
string uniqueIdentifier;
string username = inputName;
convertUsername(username);
uniqueIdentifiers = self.host_->LDAPConfig_->uniqueIdentifier;
for (int j = 0; j < uniqueIdentifiers.size(); j++)
{
uniqueIdentifier = uniqueIdentifiers[j];
size_t pos = uniqueIdentifier.find('=', 0); // look for =
uniqueIdentifier.insert(pos + 1,username); // insert username to make the DN
searchStatus = searchUserByDN(authEvents,self,uniqueIdentifier);
if (searchStatus == LDSearchFound )
{
// user found
userDN = uniqueIdentifier;
return LDSearchFound ;
}
if (searchStatus == LDSearchNotFound)// continue to search
continue;
// any other errors, stop the search and return
// LCOV_EXCL_START
return searchStatus;
// LCOV_EXCL_STOP
}
return searchStatus;
}
//****************************** End of searchUser *****************************
// *****************************************************************************
// * *
// * Function: searchUserByDN *
// * *
// * Performs a LDAP user search by calling LDAP search APIs. *
// * The search call uses a DN as the search base. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <authEvents> std::vector<AuthEvent> & Out *
// * detailed event results of request *
// * *
// * <self> ConfigNodeContents & In *
// * is a reference to a ConfigNodeContents. *
// * *
// * <userDN> string & In *
// * is the distingushed name for the user. *
// * *
// *****************************************************************************
// * *
// * Returns: error code *
// * *
// *****************************************************************************
static LDSearchStatus searchUserByDN(
std::vector<AuthEvent> & authEvents,
ConfigNodeContents & self,
const string & userDN)
{
int rc;
LDAP *ld;
LDAPMessage *res = NULL, *entry = NULL;
auto_ptr<LDAPMessage> test(NULL);
char *filter = NULL;
char *attrs[3];
char *attr, **vals;
BerElement *ptr = 0;
char createTimestamp[16];
char eventMsg[MAX_EVENT_MSG_SIZE];
// Use "createTimestamp" as our "unique ID" for the user on the LDAP server.
strcpy(createTimestamp, "createTimestamp");
attrs[0] = createTimestamp;
attrs[1] = NULL;
// *****************************************************************************
// * *
// * When we search for the username on the LDAP server, there are four *
// * possible error returns: *
// * *
// * 1) Username is found (LDAP_SUCCESS) *
// * This is the most common. We continue on with authentication. *
// * *
// * 2) Username is not found (LDAP_NO_SUCH_OBJECT) *
// * Could be a typo or someone trying to gain unauthorized access. *
// * We stop the authentication process. *
// * *
// * 3) Could not communicate with server (LDAP_SERVER_DOWN) *
// * Could be a transient problem, maybe a network issue. *
// * We retry once within this function, then if it fails *
// * again, log the error, and return an internal resource error. *
// * This triggers retry logic. *
// * *
// * 4) Some other error (e.g. LDAP_OTHER) *
// * We receive one of several dozen other possible errors. *
// * We log the error and return an internal resource error. *
// * This triggers retry logic. *
// * *
// *****************************************************************************
int reconnect = 1;
while (true)
{
if (!selfCheck(self,true))
{
INSERT_EVENT(authEvents,DBS_UNKNOWN_AUTH_STATUS_ERROR,"Self check failed in searchUserByDN");
return LDSearchResourceFailure;
}
ld = self.searchLD_;
rc = ldap_search_ext_s(ld,
userDN.c_str(), // use the DN for base
LDAP_SCOPE_BASE, // search only the base
filter, // no filter
attrs, // return attributes
0, // get both attribute and value
NULL, // server control
NULL, // client control
NULL, // timeout
0, // result size limit
&res ); // search result
// The username was found on the LDAP server, break out of loop.
if (rc == LDAP_SUCCESS)
break;
if (res != NULL)
ldap_msgfree(res);
// Username not found on the LDAP server; similar to bad password, we
// do not retry this error.
// Return and report to the user - No soup for you.
if (rc == LDAP_NO_SUCH_OBJECT)
return LDSearchNotFound;
//Retry server down error once.
if (rc == LDAP_SERVER_DOWN && reconnect != 0)
{
// reconnect & retry
closeConnection(self);
LD_Status status = initConnection(authEvents,self,NULL,true);
// If the reconnect failed, report the resource error and return a
// resource failure error so we will retry per configuration settings.
if (status != LD_STATUS_OK)
{
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "LDAP search error. Unable to connect to server\n");
INSERT_EVENT(authEvents,DBS_LDAP_SEARCH_ERROR,eventMsg);
return LDSearchResourceFailure;
}
reconnect--;
continue;
}
// For all other search errors, report an error and return a resource
// failure (LDAP server or network problem) so we will retry per
// configuration settings.
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "LDAP search error. Error code: %d, %s\n",
rc, ldap_err2string(rc));
INSERT_EVENT(authEvents,DBS_LDAP_SEARCH_ERROR,eventMsg);
return LDSearchResourceFailure;
}
// Check the number of entries found. We only need one. If none were
// returned, the user is not defined on the directory server.
int numberFound = ldap_count_entries(ld,res);
if (numberFound <= 0)
{
if (res != NULL)
ldap_msgfree(res);
return LDSearchNotFound;
}
// Get the first entry returned. If there was no entry returned, that means
// the user is not defined on the directory server. We already check for
// number of entries, but let's be extra careful.
entry = ldap_first_entry(ld, res);
if (entry == NULL)
{
//this should not happen
if (res != NULL)
ldap_msgfree(res);
// log error message
return LDSearchNotFound;
}
attr = ldap_first_attribute(ld, entry, &ptr);
if (attr == NULL)
{
//this should not happen
if (res != NULL)
ldap_msgfree(res);
// log error message
snprintf(eventMsg, MAX_EVENT_MSG_SIZE, "LDAP search error. Attribute %s does not exist in the entry %s", attrs[0], userDN.c_str());
INSERT_EVENT(authEvents,DBS_LDAP_SEARCH_ERROR,eventMsg);
return LDSearchResourceFailure;
}
// OpenLDAP allocates memory and then requires the caller to free it using
// specific OpenLDAP APIs.
ldap_memfree(attr);
if (ptr != NULL)
ber_free(ptr, 0);
if (res != NULL)
ldap_msgfree(res);
return LDSearchFound;
}
//*************************** End of searchUserByDN ****************************
// *****************************************************************************
// * *
// * Function: selfCheck *
// * *
// * Determines if this instance is internally consistent. Based on the *
// * node type and the status, dependent values are checked. *
// * *
// *****************************************************************************
// * *
// * Parameters: *
// * *
// * <self> ConfigNodeContents & In *
// * is a reference to a ConfigNodeContents. *
// * *
// * <isInitialized> bool In *
// * if true, instance must be initialized to be consistent. *
// * *
// *****************************************************************************
// * *
// * Returns: *
// * *
// * TRUE: Node is consistent *
// * FALSE: Node is inconsistent *
// * *
// *****************************************************************************
static bool selfCheck(
ConfigNodeContents & self,
bool isInitialized)
{
//
// We only support two configurations. Got to be one or the other.
//
if (self.configType_ != LDAPConfigNode::PrimaryConfiguration &&
self.configType_ != LDAPConfigNode::SecondaryConfiguration)
return false;
//
// We only support two types of connections. Got to be one or the other.
//
if (self.connectionType_ != AuthenticationConnection &&
self.connectionType_ != SearchConnection)
return false;
//
// If the node has not (yet) been initialized, that is a consistent state.
// However, if the caller expects the node to have been initialized, we have problem.
//
if (self.status_ == NODE_NOT_INITIALIZED)
{
if (isInitialized)
return false;
else
return true;
}
//
// There are only two states. Anything else is a problem. This catches cases
// where the class data has been overwritten.
//
if (self.status_ != NODE_ACTIVE)
return false;
//
// We have an active node. For active nodes, the ld for the connection type
// must be setup.
//
if (self.connectionType_ == AuthenticationConnection && self.authLD_ == NULL)
return false;
if (self.connectionType_ == SearchConnection && self.searchLD_ == NULL)
return false;
//
// All data members are consistent with an active state. Check the
// LDAP Config for consistency.
//
return self.host_->LDAPConfig_->selfCheck(isInitialized);
}
//***************************** End of selfCheck *******************************
// *****************************************************************************
// * *
// * Function: shouldReadLDAPConfig *
// * *
// * Determines if the LDAP config file needs to be (re)read or if we can use *
// * the data we have already read. *
// * *
// * *
// *****************************************************************************
// * *
// * Returns: bool *
// * *
// * true: LDAP config file should be read *
// * false: Use existing data *
// * *
// *****************************************************************************
inline static bool shouldReadLDAPConfig()
{
// If we have never successfully read and parsed the LDAP configuration file,
// then we need to read.
if (lastRefreshTime == 0)
return true;
// We have previously read the file. See if we need to refresh.
// Zero indicates never refresh, so don't read.
if (config.refreshTime == 0)
return false;
// If not enough time has elapsed since we last read the LDAP config file
// then we don't need to re-read the file.
if (time(0) - lastRefreshTime < config.refreshTime)
return false;
// Time to freshen up!
return true;
}
//************************ End of shouldReadLDAPConfig *************************