Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2007-12-20 17:58:44 +0100
committerArthur de Jong <arthur@arthurdejong.org>2007-12-20 17:58:44 +0100
commit4672d6dbcb5a69b770faf0832893366e78c67600 (patch)
treeacafaca25fe3443f45b3d1f00a5a017baee7a580
parentd3f57728a029b4a3215304e2fdfd3a1a55b68087 (diff)
refactor myldap code to get rid of most of the old nss status codes, properly handle failures of ldap function calls and improve sourcecode comments
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/nss-ldapd@509 ef36b2f9-881f-0410-afb5-c4e39611909c
-rw-r--r--nslcd/myldap.c603
-rw-r--r--nslcd/myldap.h4
2 files changed, 296 insertions, 311 deletions
diff --git a/nslcd/myldap.c b/nslcd/myldap.c
index 77fab77..0c64b60 100644
--- a/nslcd/myldap.c
+++ b/nslcd/myldap.c
@@ -23,24 +23,28 @@
02110-1301 USA
*/
+/*
+ This library expects to use an LDAP library to provide the real
+ functionality and only provides a convenient wrapper.
+ Some pointers for more information on the LDAP API:
+ http://tools.ietf.org/id/draft-ietf-ldapext-ldap-c-api-05.txt
+ http://www.mozilla.org/directory/csdk-docs/function.htm
+ http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/apis/dirserv1.htm
+ http://www.openldap.org/software/man.cgi?query=ldap
+*/
+
#include "config.h"
-#include <assert.h>
+#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
-#ifdef HAVE_STRINGS_H
#include <strings.h>
-#endif
-#include <stdio.h>
-#include <signal.h>
-#include <fcntl.h>
-#include <time.h>
#include <sys/time.h>
+#include <time.h>
+#include <sys/types.h>
#include <sys/socket.h>
-#include <sys/param.h>
#include <errno.h>
-#include <netinet/in.h>
#include <ldap.h>
#ifdef HAVE_LDAP_SSL_H
#include <ldap_ssl.h>
@@ -74,10 +78,8 @@
/* the maximum number of searches per session */
#define MAX_SEARCHES_IN_SESSION 4
-/*
- * convenient wrapper around pointer into global config list, and a
- * connection to an LDAP server.
- */
+/* This refers to a current LDAP session that contains the connection
+ information. */
struct ldap_session
{
/* the connection */
@@ -103,7 +105,7 @@ struct myldap_search
/* a pointer to the current result entry, used for
freeing resource allocated with that entry */
MYLDAP_ENTRY *entry;
- /* LDAP message id */
+ /* LDAP message id for the search, -1 indicates absense of an active search */
int msgid;
/* the last result that was returned by ldap_result() */
LDAPMessage *msg;
@@ -282,43 +284,7 @@ PURE static inline int is_valid_entry(MYLDAP_ENTRY *entry)
return (entry!=NULL)&&is_valid_search(entry->search)&&(entry->search->msg!=NULL);
}
-static enum nss_status do_map_error(int rc)
-{
- switch (rc)
- {
- case LDAP_SUCCESS:
- case LDAP_SIZELIMIT_EXCEEDED:
- case LDAP_TIMELIMIT_EXCEEDED:
- return NSS_STATUS_SUCCESS;
- break;
- case LDAP_NO_SUCH_ATTRIBUTE:
- case LDAP_UNDEFINED_TYPE:
- case LDAP_INAPPROPRIATE_MATCHING:
- case LDAP_CONSTRAINT_VIOLATION:
- case LDAP_TYPE_OR_VALUE_EXISTS:
- case LDAP_INVALID_SYNTAX:
- case LDAP_NO_SUCH_OBJECT:
- case LDAP_ALIAS_PROBLEM:
- case LDAP_INVALID_DN_SYNTAX:
- case LDAP_IS_LEAF:
- case LDAP_ALIAS_DEREF_PROBLEM:
- case LDAP_FILTER_ERROR:
- return NSS_STATUS_NOTFOUND;
- break;
- case LDAP_SERVER_DOWN:
- case LDAP_TIMEOUT:
- case LDAP_UNAVAILABLE:
- case LDAP_BUSY:
-#ifdef LDAP_CONNECT_ERROR
- case LDAP_CONNECT_ERROR:
-#endif /* LDAP_CONNECT_ERROR */
- case LDAP_LOCAL_ERROR:
- case LDAP_INVALID_CREDENTIALS:
- default:
- return NSS_STATUS_UNAVAIL;
- }
-}
-
+/* this is registered with ldap_sasl_interactive_bind_s() in do_bind() */
static int do_sasl_interact(LDAP UNUSED(*ld),unsigned UNUSED(flags),void *defaults,void *_interact)
{
char *authzid=(char *)defaults;
@@ -347,17 +313,25 @@ static int do_sasl_interact(LDAP UNUSED(*ld),unsigned UNUSED(flags),void *defaul
return LDAP_SUCCESS;
}
-/* this returns an LDAP result code */
+#define LDAP_SET_OPTION(ld,option,invalue) \
+ rc=ldap_set_option(ld,option,invalue); \
+ if (rc!=LDAP_SUCCESS) \
+ { \
+ log_log(LOG_ERR,"ldap_set_option("__STRING(option)") failed: %s",ldap_err2string(rc)); \
+ return rc; \
+ }
+
+/* This function performs the authentication phase of opening a connection.
+ This returns an LDAP result code. */
static int do_bind(MYLDAP_SESSION *session)
{
int rc;
char *binddn,*bindarg;
int usesasl;
- /*
- * If we're running as root, let us bind as a special
- * user, so we can fake shadow passwords.
- */
+ /* If we're running as root, let us bind as a special
+ user, so we can fake shadow passwords. */
/* TODO: store this information in the session */
+ /* FIXME: this is wrong, we should not do this!!!!!!! */
if ((geteuid()==0)&&(nslcd_cfg->ldc_rootbinddn!=NULL))
{
binddn=nslcd_cfg->ldc_rootbinddn;
@@ -385,12 +359,7 @@ static int do_bind(MYLDAP_SESSION *session)
log_log(LOG_DEBUG,"SASL bind as %s",binddn);
if (nslcd_cfg->ldc_sasl_secprops!=NULL)
{
- rc=ldap_set_option(session->ls_conn,LDAP_OPT_X_SASL_SECPROPS,(void *)nslcd_cfg->ldc_sasl_secprops);
- if (rc!=LDAP_SUCCESS)
- {
- log_log(LOG_ERR,"unable to set SASL security properties: %s",ldap_err2string(rc));
- return -1;
- }
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_X_SASL_SECPROPS,(void *)nslcd_cfg->ldc_sasl_secprops);
}
rc=ldap_sasl_interactive_bind_s(session->ls_conn,binddn,"GSSAPI",NULL,NULL,
LDAP_SASL_QUIET,
@@ -399,10 +368,8 @@ static int do_bind(MYLDAP_SESSION *session)
}
}
-/*
- * This function is called by the LDAP library when chasing referrals.
- * It is configured with the ldap_set_rebind_proc() below.
- */
+/* This function is called by the LDAP library when chasing referrals.
+ It is configured with the ldap_set_rebind_proc() below. */
static int do_rebind(LDAP *UNUSED(ld),LDAP_CONST char UNUSED(*url),
ber_tag_t UNUSED(request),
ber_int_t UNUSED(msgid),void *arg)
@@ -410,102 +377,95 @@ static int do_rebind(LDAP *UNUSED(ld),LDAP_CONST char UNUSED(*url),
return do_bind((MYLDAP_SESSION *)arg);
}
-/*
- * Close the global session, sending an unbind.
- * Closes connection to the LDAP server.
- */
-static void do_close(MYLDAP_SESSION *session)
-{
- log_log(LOG_DEBUG,"==> do_close");
- if (session->ls_conn!=NULL)
- ldap_unbind(session->ls_conn);
- session->ls_conn=NULL;
- log_log(LOG_DEBUG,"<== do_close");
-}
-
-static int do_ssl_options(void)
+/* This function sets a number of properties on the connection, based
+ what is configured in the configfile. This function returns an
+ LDAP status code. */
+static int do_set_options(MYLDAP_SESSION *session)
{
- /* TODO: save return value of ldap_set_option() and include it in the error message */
- /* rand file */
- if (nslcd_cfg->ldc_tls_randfile!=NULL)
+ int rc;
+ struct timeval tv;
+ int tls=LDAP_OPT_X_TLS_HARD;
+ /* turn on debugging */
+ if (nslcd_cfg->ldc_debug)
{
- if (ldap_set_option(NULL,LDAP_OPT_X_TLS_RANDOM_FILE,
- nslcd_cfg->ldc_tls_randfile)!=LDAP_SUCCESS)
+ rc=ber_set_option(NULL,LBER_OPT_DEBUG_LEVEL,&nslcd_cfg->ldc_debug);
+ if (rc!=LDAP_SUCCESS)
{
- log_log(LOG_ERR,"setting of LDAP_OPT_X_TLS_RANDOM_FILE failed");
- return LDAP_OPERATIONS_ERROR;
+ log_log(LOG_ERR,"ber_set_option(LBER_OPT_DEBUG_LEVEL) failed: %s",ldap_err2string(rc));
+ return rc;
}
+ LDAP_SET_OPTION(NULL,LDAP_OPT_DEBUG_LEVEL,&nslcd_cfg->ldc_debug);
}
- /* ca cert file */
- if (nslcd_cfg->ldc_tls_cacertfile!=NULL)
+ /* the rebind function that is called when chasing referrals, see
+ http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/apis/ldap_set_rebind_proc.htm
+ http://www.openldap.org/software/man.cgi?query=ldap_set_rebind_proc&manpath=OpenLDAP+2.4-Release */
+ /* TODO: probably only set this if we should chase referrals */
+ rc=ldap_set_rebind_proc(session->ls_conn,do_rebind,session);
+ if (rc!=LDAP_SUCCESS)
{
- if (ldap_set_option(NULL,LDAP_OPT_X_TLS_CACERTFILE,
- nslcd_cfg->ldc_tls_cacertfile)!=LDAP_SUCCESS)
- {
- log_log(LOG_ERR,"setting of LDAP_OPT_X_TLS_CACERTFILE failed");
- return LDAP_OPERATIONS_ERROR;
- }
+ log_log(LOG_ERR,"ldap_set_rebind_proc() failed: %s",ldap_err2string(rc));
+ return rc;
}
- /* ca cert directory */
- if (nslcd_cfg->ldc_tls_cacertdir!=NULL)
+ /* set the protocol version to use */
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_PROTOCOL_VERSION,&nslcd_cfg->ldc_version);
+ /* set some other options */
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_DEREF,&nslcd_cfg->ldc_deref);
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_TIMELIMIT,&nslcd_cfg->ldc_timelimit);
+ tv.tv_sec=nslcd_cfg->ldc_bind_timelimit;
+ tv.tv_usec=0;
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_TIMEOUT,&tv);
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_NETWORK_TIMEOUT,&tv);
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_REFERRALS,nslcd_cfg->ldc_referrals?LDAP_OPT_ON:LDAP_OPT_OFF);
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_RESTART,nslcd_cfg->ldc_restart?LDAP_OPT_ON:LDAP_OPT_OFF);
+ /* if SSL is desired, then enable it */
+ if (nslcd_cfg->ldc_ssl_on==SSL_LDAPS)
{
- if (ldap_set_option(NULL,LDAP_OPT_X_TLS_CACERTDIR,
- nslcd_cfg->ldc_tls_cacertdir)!=LDAP_SUCCESS)
+ /* use tls */
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_X_TLS,&tls);
+ /* rand file */
+ if (nslcd_cfg->ldc_tls_randfile!=NULL)
{
- log_log(LOG_ERR,"setting of LDAP_OPT_X_TLS_CACERTDIR failed");
- return LDAP_OPERATIONS_ERROR;
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_X_TLS_RANDOM_FILE,nslcd_cfg->ldc_tls_randfile);
}
- }
- /* require cert? */
- if (nslcd_cfg->ldc_tls_checkpeer > -1)
- {
- if (ldap_set_option(NULL,LDAP_OPT_X_TLS_REQUIRE_CERT,
- &nslcd_cfg->ldc_tls_checkpeer)!=LDAP_SUCCESS)
+ /* ca cert file */
+ if (nslcd_cfg->ldc_tls_cacertfile!=NULL)
{
- log_log(LOG_ERR,"setting of LDAP_OPT_X_TLS_REQUIRE_CERT failed");
- return LDAP_OPERATIONS_ERROR;
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_X_TLS_CACERTFILE,nslcd_cfg->ldc_tls_cacertfile);
}
- }
- /* set cipher suite, certificate and private key: */
- if (nslcd_cfg->ldc_tls_ciphers != NULL)
- {
- if (ldap_set_option(NULL,LDAP_OPT_X_TLS_CIPHER_SUITE,
- nslcd_cfg->ldc_tls_ciphers)!=LDAP_SUCCESS)
+ /* ca cert directory */
+ if (nslcd_cfg->ldc_tls_cacertdir!=NULL)
{
- log_log(LOG_ERR,"setting of LDAP_OPT_X_TLS_CIPHER_SUITE failed");
- return LDAP_OPERATIONS_ERROR;
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_X_TLS_CACERTDIR,nslcd_cfg->ldc_tls_cacertdir);
}
- }
-
- if (nslcd_cfg->ldc_tls_cert != NULL)
- {
- if (ldap_set_option(NULL,LDAP_OPT_X_TLS_CERTFILE,
- nslcd_cfg->ldc_tls_cert)!=LDAP_SUCCESS)
+ /* require cert? */
+ if (nslcd_cfg->ldc_tls_checkpeer>-1)
{
- log_log(LOG_ERR,"setting of LDAP_OPT_X_TLS_CERTFILE failed");
- return LDAP_OPERATIONS_ERROR;
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_X_TLS_REQUIRE_CERT,&nslcd_cfg->ldc_tls_checkpeer);
}
- }
- if (nslcd_cfg->ldc_tls_key != NULL)
- {
- if (ldap_set_option(NULL,LDAP_OPT_X_TLS_KEYFILE,
- nslcd_cfg->ldc_tls_key)!=LDAP_SUCCESS)
+ /* set cipher suite, certificate and private key */
+ if (nslcd_cfg->ldc_tls_ciphers!=NULL)
+ {
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_X_TLS_CIPHER_SUITE,nslcd_cfg->ldc_tls_ciphers);
+ }
+ /* set certificate */
+ if (nslcd_cfg->ldc_tls_cert!=NULL)
+ {
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_X_TLS_CERTFILE,nslcd_cfg->ldc_tls_cert);
+ }
+ /* set up key */
+ if (nslcd_cfg->ldc_tls_key!=NULL)
{
- log_log(LOG_ERR,"setting of LDAP_OPT_X_TLS_KEYFILE failed");
- return LDAP_OPERATIONS_ERROR;
+ LDAP_SET_OPTION(session->ls_conn,LDAP_OPT_X_TLS_KEYFILE,nslcd_cfg->ldc_tls_key);
}
}
+ /* if nothing above failed, everything should be fine */
return LDAP_SUCCESS;
}
-/*
- * Opens connection to an LDAP server, sets all connection options
- * and binds to the server. This returns a simple (0/-1) status code.
- * TODO: this should return an LDAP error code
- */
+/* This opens connection to an LDAP server, sets all connection options
+ and binds to the server. This returns an LDAP status code. */
static int do_open(MYLDAP_SESSION *session)
{
- struct timeval tv;
int rc;
time_t current_time;
int sd=-1;
@@ -516,15 +476,14 @@ static int do_open(MYLDAP_SESSION *session)
if ((session->ls_timestamp+nslcd_cfg->ldc_idle_timelimit)<current_time)
{
log_log(LOG_DEBUG,"do_open(): idle_timelimit reached");
- do_close(session);
+ ldap_unbind(session->ls_conn);
+ session->ls_conn=NULL;
}
}
- /* if the connection is still there (ie. do_close() wasn't
+ /* if the connection is still there (ie. ldap_unbind() wasn't
called) then we can return the cached connection */
if (session->ls_conn!=NULL)
- {
- return 0;
- }
+ return LDAP_SUCCESS;
/* we should build a new session now */
session->ls_conn=NULL;
session->ls_timestamp=0;
@@ -535,51 +494,25 @@ static int do_open(MYLDAP_SESSION *session)
log_log(LOG_WARNING,"ldap_initialize(%s) failed: %s: %s",
nslcd_cfg->ldc_uris[session->ls_current_uri],
ldap_err2string(rc),strerror(errno));
- return -1;
+ if (session->ls_conn!=NULL)
+ {
+ ldap_unbind(session->ls_conn);
+ session->ls_conn=NULL;
+ }
+ return rc;
}
else if (session->ls_conn==NULL)
{
log_log(LOG_WARNING,"ldap_initialize() returned NULL");
- return -1;
+ return LDAP_LOCAL_ERROR;
}
- /* turn on debugging */
- if (nslcd_cfg->ldc_debug)
- {
- ber_set_option(NULL,LBER_OPT_DEBUG_LEVEL,&nslcd_cfg->ldc_debug);
- ldap_set_option(NULL,LDAP_OPT_DEBUG_LEVEL,&nslcd_cfg->ldc_debug);
- }
- /* the rebind function that is called when chasing referrals, see
- http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/apis/ldap_set_rebind_proc.htm
- http://www.openldap.org/software/man.cgi?query=ldap_set_rebind_proc&manpath=OpenLDAP+2.4-Release */
- /* TODO: probably only set this if we should chase referrals */
- ldap_set_rebind_proc(session->ls_conn,do_rebind,session);
- /* set the protocol version to use */
- ldap_set_option(session->ls_conn,LDAP_OPT_PROTOCOL_VERSION,&nslcd_cfg->ldc_version);
- ldap_set_option(session->ls_conn,LDAP_OPT_DEREF,&nslcd_cfg->ldc_deref);
- ldap_set_option(session->ls_conn,LDAP_OPT_TIMELIMIT,&nslcd_cfg->ldc_timelimit);
- tv.tv_sec=nslcd_cfg->ldc_bind_timelimit;
- tv.tv_usec=0;
- ldap_set_option(session->ls_conn,LDAP_OPT_TIMEOUT,&tv);
- ldap_set_option(session->ls_conn,LDAP_OPT_NETWORK_TIMEOUT,&tv);
- ldap_set_option(session->ls_conn,LDAP_OPT_REFERRALS,nslcd_cfg->ldc_referrals?LDAP_OPT_ON:LDAP_OPT_OFF);
- ldap_set_option(session->ls_conn,LDAP_OPT_RESTART,nslcd_cfg->ldc_restart?LDAP_OPT_ON:LDAP_OPT_OFF);
- /* if SSL is desired, then enable it */
- if (nslcd_cfg->ldc_ssl_on==SSL_LDAPS)
+ /* set the options for the connection */
+ rc=do_set_options(session);
+ if (rc!=LDAP_SUCCESS)
{
- int tls=LDAP_OPT_X_TLS_HARD;
- if (ldap_set_option(session->ls_conn,LDAP_OPT_X_TLS,&tls)!=LDAP_SUCCESS)
- {
- do_close(session);
- log_log(LOG_DEBUG,"<== do_open(TLS setup failed)");
- return -1;
- }
- /* set up SSL context */
- if (do_ssl_options()!=LDAP_SUCCESS)
- {
- do_close(session);
- log_log(LOG_DEBUG,"<== do_open(SSL setup failed)");
- return -1;
- }
+ ldap_unbind(session->ls_conn);
+ session->ls_conn=NULL;
+ return rc;
}
/* bind to the server */
rc=do_bind(session);
@@ -589,34 +522,31 @@ static int do_open(MYLDAP_SESSION *session)
log_log(LOG_WARNING,"failed to bind to LDAP server %s: %s: %s",
nslcd_cfg->ldc_uris[session->ls_current_uri],
ldap_err2string(rc),strerror(errno));
- do_close(session);
- return -1;
+ ldap_unbind(session->ls_conn);
+ session->ls_conn=NULL;
+ return rc;
}
- /* disable keepalive on a LDAP connection socket */
+ /* disable keepalive on the LDAP connection socket */
if (ldap_get_option(session->ls_conn,LDAP_OPT_DESC,&sd)==0)
{
int off=0;
/* ignore errors */
(void)setsockopt(sd,SOL_SOCKET,SO_KEEPALIVE,(void *)&off,sizeof(off));
- (void)fcntl(sd,F_SETFD,FD_CLOEXEC);
}
/* update last activity and finish off state */
time(&(session->ls_timestamp));
log_log(LOG_DEBUG,"do_open(): connected to %s",nslcd_cfg->ldc_uris[session->ls_current_uri]);
- return 0;
+ return LDAP_SUCCESS;
}
-/*
- * Wrapper around ldap_result() to skip over search references
- * and deal transparently with the last entry.
- */
-static enum nss_status do_result(MYLDAP_SEARCH *search)
+/* Wrapper around ldap_result() to skip over search references and deal
+ transparently with the last entry. */
+static int do_result(MYLDAP_SEARCH *search)
{
- int rc=LDAP_UNAVAILABLE;
- enum nss_status stat=NSS_STATUS_TRYAGAIN;
+ int rc=LDAP_RES_SEARCH_REFERENCE;
struct timeval tv,*tvp;
int parserc;
- LDAPControl **resultControls;
+ LDAPControl **resultcontrols;
/* set up a timelimit value for operations */
if (nslcd_cfg->ldc_timelimit==LDAP_NO_LIMIT)
tvp=NULL;
@@ -626,10 +556,10 @@ static enum nss_status do_result(MYLDAP_SEARCH *search)
tv.tv_usec=0;
tvp=&tv;
}
- /* loop until we have something else than a LDAP_RES_SEARCH_REFERENCE */
- do
+ /* loop while we have search references */
+ while (rc==LDAP_RES_SEARCH_REFERENCE)
{
- /* free the previous message */
+ /* free the previous message if there was any */
if (search->msg!=NULL)
{
ldap_msgfree(search->msg);
@@ -637,105 +567,121 @@ static enum nss_status do_result(MYLDAP_SEARCH *search)
}
/* get the next result */
rc=ldap_result(search->session->ls_conn,search->msgid,LDAP_MSG_ONE,tvp,&(search->msg));
- switch (rc)
- {
- case -1:
- case 0:
- if (ldap_get_option(search->session->ls_conn,LDAP_OPT_ERROR_NUMBER,&rc)!=LDAP_SUCCESS)
- rc=LDAP_UNAVAILABLE;
- log_log(LOG_ERR,"could not get LDAP result: %s",ldap_err2string(rc));
- stat=NSS_STATUS_UNAVAIL;
- break;
- case LDAP_RES_SEARCH_ENTRY:
- /* update timestamp on success */
- time(&(search->session->ls_timestamp));
- stat=NSS_STATUS_SUCCESS;
- break;
- case LDAP_RES_SEARCH_RESULT:
- resultControls=NULL;
- if (search->cookie!=NULL)
- ber_bvfree(search->cookie);
+ }
+ /* handle result */
+ switch (rc)
+ {
+ case -1:
+ /* we have an error condition, try to get error code */
+ if (ldap_get_option(search->session->ls_conn,LDAP_OPT_ERROR_NUMBER,&rc)!=LDAP_SUCCESS)
+ rc=LDAP_UNAVAILABLE;
+ log_log(LOG_ERR,"ldap_result(): %s",ldap_err2string(rc));
+ return rc;
+ case 0:
+ /* the timeout expired */
+ log_log(LOG_ERR,"ldap_result() timed out");
+ return LDAP_TIMELIMIT_EXCEEDED;
+ case LDAP_RES_SEARCH_ENTRY:
+ /* we have a normal search entry, update timestamp and we're done */
+ time(&(search->session->ls_timestamp));
+ return LDAP_SUCCESS;
+ case LDAP_RES_SEARCH_RESULT:
+ /* we have a search result, parse it */
+ resultcontrols=NULL;
+ if (search->cookie!=NULL)
+ {
+ ber_bvfree(search->cookie);
search->cookie=NULL;
- /* NB: this frees search->msg */
- parserc=ldap_parse_result(search->session->ls_conn,search->msg,&rc,NULL,
- NULL,NULL,&resultControls,1);
- search->msg=NULL;
- if ((parserc!=LDAP_SUCCESS)&&(parserc!=LDAP_MORE_RESULTS_TO_RETURN))
- {
- stat = NSS_STATUS_UNAVAIL;
- ldap_abandon(search->session->ls_conn,search->msgid);
- log_log(LOG_ERR,"could not get LDAP result: %s",ldap_err2string(rc));
- }
- else if (resultControls!=NULL)
- {
- /* See if there are any more pages to come */
- parserc=ldap_parse_page_control(search->session->ls_conn,
- resultControls,NULL,
- &(search->cookie));
- /* TODO: handle the above return code?? */
- ldap_controls_free(resultControls);
- stat=NSS_STATUS_NOTFOUND;
- }
- else
- stat=NSS_STATUS_NOTFOUND;
- search->msgid=-1;
- break;
- default:
- stat = NSS_STATUS_UNAVAIL;
- break;
- }
+ }
+ /* NB: this frees search->msg */
+ parserc=ldap_parse_result(search->session->ls_conn,search->msg,&rc,NULL,
+ NULL,NULL,&resultcontrols,1);
+ search->msg=NULL;
+ /* check for errors during parsing */
+ if ((parserc!=LDAP_SUCCESS)&&(parserc!=LDAP_MORE_RESULTS_TO_RETURN))
+ {
+ if (resultcontrols!=NULL)
+ ldap_controls_free(resultcontrols);
+ ldap_abandon(search->session->ls_conn,search->msgid);
+ log_log(LOG_ERR,"could not parse LDAP result: %s",ldap_err2string(parserc));
+ return parserc;
+ }
+ /* check for errors in message */
+ if ((rc!=LDAP_SUCCESS)&&(rc!=LDAP_MORE_RESULTS_TO_RETURN))
+ {
+ if (resultcontrols!=NULL)
+ ldap_controls_free(resultcontrols);
+ ldap_abandon(search->session->ls_conn,search->msgid);
+ log_log(LOG_ERR,"could not get LDAP result: %s",ldap_err2string(rc));
+ return rc;
+ }
+ /* handle result controls */
+ if (resultcontrols!=NULL)
+ {
+ /* see if there are any more pages to come */
+ ldap_parse_page_control(search->session->ls_conn,
+ resultcontrols,NULL,
+ &(search->cookie));
+ /* TODO: handle the above return code?? */
+ ldap_controls_free(resultcontrols);
+ }
+ search->msgid=-1;
+ return LDAP_SUCCESS;
+ default:
+ log_log(LOG_WARNING,"ldap_result() returned unexpected result type");
+ return LDAP_LOCAL_ERROR;
}
- while (rc==LDAP_RES_SEARCH_REFERENCE);
- /* TODO: check which statement could actually return LDAP_RES_SEARCH_REFERENCE
- and try to get as much error handling code outside of the loop */
- return stat;
}
-static int do_search(MYLDAP_SEARCH *search,int *msgidp)
+/* TODO: this is only called from do_with_reconnect(), we should probably move it there */
+static enum nss_status do_map_error(int rc)
{
- int rc;
- LDAPControl *serverCtrls[2];
- LDAPControl **pServerCtrls;
- /* ensure that we have an open connection */
- if (do_open(search->session))
- return LDAP_SERVER_DOWN;
- /* if we're using paging, build a page control */
- if (nslcd_cfg->ldc_pagesize>0)
- {
- rc=ldap_create_page_control(search->session->ls_conn,nslcd_cfg->ldc_pagesize,
- NULL,0,&serverCtrls[0]);
- if (rc!=LDAP_SUCCESS)
- return rc;
- serverCtrls[1]=NULL;
- pServerCtrls=serverCtrls;
- }
- else
- pServerCtrls=NULL;
- /* perform the search */
- rc=ldap_search_ext(search->session->ls_conn,search->base,search->scope,
- search->filter,search->attrs,0,pServerCtrls,NULL,
- LDAP_NO_LIMIT,LDAP_NO_LIMIT,msgidp);
- /* free the controls if we had them */
- if (pServerCtrls!=NULL)
+ switch (rc)
{
- ldap_control_free(serverCtrls[0]);
- serverCtrls[0]=NULL;
+ case LDAP_SUCCESS:
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_TIMELIMIT_EXCEEDED:
+ return NSS_STATUS_SUCCESS;
+ break;
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ case LDAP_UNDEFINED_TYPE:
+ case LDAP_INAPPROPRIATE_MATCHING:
+ case LDAP_CONSTRAINT_VIOLATION:
+ case LDAP_TYPE_OR_VALUE_EXISTS:
+ case LDAP_INVALID_SYNTAX:
+ case LDAP_NO_SUCH_OBJECT:
+ case LDAP_ALIAS_PROBLEM:
+ case LDAP_INVALID_DN_SYNTAX:
+ case LDAP_IS_LEAF:
+ case LDAP_ALIAS_DEREF_PROBLEM:
+ case LDAP_FILTER_ERROR:
+ return NSS_STATUS_NOTFOUND;
+ break;
+ case LDAP_SERVER_DOWN:
+ case LDAP_TIMEOUT:
+ case LDAP_UNAVAILABLE:
+ case LDAP_BUSY:
+#ifdef LDAP_CONNECT_ERROR
+ case LDAP_CONNECT_ERROR:
+#endif /* LDAP_CONNECT_ERROR */
+ case LDAP_LOCAL_ERROR:
+ case LDAP_INVALID_CREDENTIALS:
+ default:
+ return NSS_STATUS_UNAVAIL;
}
- return rc;
}
-/*
- * Function to call do_search() with reconnection logic (depending on
- * wheter res or msgid is not NULL).
- */
-static enum nss_status do_with_reconnect(
- MYLDAP_SEARCH *search)
+/* Perform a search with reconnection logic.
+ TODO: this is only called from myldap_search(), probably move the code there */
+static int do_with_reconnect(MYLDAP_SEARCH *search)
{
int rc=LDAP_UNAVAILABLE, tries=0, backoff=0;
int hard=1, start_uri=0, log=0;
enum nss_status stat=NSS_STATUS_UNAVAIL;
int msgid;
int maxtries;
+ LDAPControl *serverCtrls[2];
+ LDAPControl **pServerCtrls;
/* get the maximum number of tries */
maxtries=nslcd_cfg->ldc_reconnect_tries;
/* keep trying until we have success or a hard failure */
@@ -751,11 +697,39 @@ static enum nss_status do_with_reconnect(
log_log(LOG_INFO,"reconnecting to LDAP server (sleeping %d seconds)...",backoff);
(void)sleep(backoff);
}
- /* for each "try", attempt to connect to all configured URIs */
+ /* try each configured URL once */
start_uri=search->session->ls_current_uri;
do
{
- stat=do_map_error(do_search(search,&msgid));
+ /* ensure that we have an open connection */
+ rc=do_open(search->session);
+ if (rc==LDAP_SUCCESS)
+ {
+ /* if we're using paging, build a page control */
+ if (nslcd_cfg->ldc_pagesize>0)
+ {
+ rc=ldap_create_page_control(search->session->ls_conn,nslcd_cfg->ldc_pagesize,
+ NULL,0,&serverCtrls[0]);
+ if (rc!=LDAP_SUCCESS)
+ return rc;
+ serverCtrls[1]=NULL;
+ pServerCtrls=serverCtrls;
+ }
+ else
+ pServerCtrls=NULL;
+ /* perform the search */
+ rc=ldap_search_ext(search->session->ls_conn,
+ search->base,search->scope,search->filter,
+ search->attrs,0,pServerCtrls,NULL,NULL,
+ LDAP_NO_LIMIT,&msgid);
+ /* free the controls if we had them */
+ if (pServerCtrls!=NULL)
+ {
+ ldap_control_free(serverCtrls[0]);
+ serverCtrls[0]=NULL;
+ }
+ }
+ stat=do_map_error(rc);
/* if we got any feedback from the server, don't try any other URIs */
if (stat!=NSS_STATUS_UNAVAIL)
break;
@@ -770,26 +744,29 @@ static enum nss_status do_with_reconnect(
/* TODO: we should probably close in the loop above */
if (stat==NSS_STATUS_UNAVAIL)
{
- do_close(search->session);
+ /* close the connection */
+ if (search->session!=NULL)
+ {
+ ldap_unbind(search->session->ls_conn);
+ search->session->ls_conn=NULL;
+ }
/* If a soft reconnect policy is specified, then do not
- * try to reconnect to the LDAP server if it is down.
- */
+ try to reconnect to the LDAP server if it is down. */
if (nslcd_cfg->ldc_reconnect_pol == LP_RECONNECT_SOFT)
hard = 0;
++tries;
}
}
-
switch (stat)
{
case NSS_STATUS_UNAVAIL:
log_log(LOG_ERR,"could not search LDAP server - %s",ldap_err2string(rc));
- return NSS_STATUS_UNAVAIL;
+ return rc;
case NSS_STATUS_TRYAGAIN:
log_log(LOG_ERR,"could not %s %sconnect to LDAP server - %s",
hard?"hard":"soft", tries?"re":"",
ldap_err2string(rc));
- return NSS_STATUS_UNAVAIL;
+ return rc;
case NSS_STATUS_SUCCESS:
if (log)
{
@@ -805,11 +782,11 @@ static enum nss_status do_with_reconnect(
/* update the last activity on the connection */
time(&(search->session->ls_timestamp));
search->msgid=msgid;
- return NSS_STATUS_SUCCESS;
+ return LDAP_SUCCESS;
case NSS_STATUS_NOTFOUND:
case NSS_STATUS_RETURN:
default:
- return stat;
+ return rc;
}
}
@@ -876,7 +853,7 @@ MYLDAP_SEARCH *myldap_search(
/* allocate a new search entry */
search=myldap_search_new(session,base,scope,filter,attrs);
/* set up a new search */
- if (do_with_reconnect(search)!=NSS_STATUS_SUCCESS)
+ if (do_with_reconnect(search)!=LDAP_SUCCESS)
{
myldap_search_free(search);
return NULL;
@@ -900,9 +877,18 @@ void myldap_search_close(MYLDAP_SEARCH *search)
int i;
if (!is_valid_search(search))
return;
+ /* free any messages */
+ if (search->msg!=NULL)
+ {
+ ldap_msgfree(search->msg);
+ search->msg=NULL;
+ }
/* abandon the search if there were more results to fetch */
- if ((search->msgid>-1)&&(do_result(search)==NSS_STATUS_SUCCESS))
+ if (search->msgid!=-1)
+ {
ldap_abandon(search->session->ls_conn,search->msgid);
+ search->msgid=-1;
+ }
/* find the reference to this search in the session */
for (i=0;i<MAX_SEARCHES_IN_SESSION;i++)
{
@@ -915,13 +901,12 @@ void myldap_search_close(MYLDAP_SEARCH *search)
MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search)
{
- enum nss_status stat=NSS_STATUS_SUCCESS;
int msgid;
int rc;
/* check parameters */
if (!is_valid_search(search))
{
- log_log(LOG_ERR,"myldap_get_entry(): invalid search entry passed");
+ log_log(LOG_ERR,"myldap_get_entry(): invalid search passed");
errno=EINVAL;
return NULL;
}
@@ -936,15 +921,16 @@ MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search)
{
/* get an entry from the LDAP server, the result
is stored in context->ec_res */
- stat=do_result(search);
+ rc=do_result(search);
/* we we have an entry construct a search entry from it */
- if (stat==NSS_STATUS_SUCCESS)
+ if ((rc==LDAP_SUCCESS)&&(search->msg!=NULL))
{
/* we have a normal entry, return it */
search->entry=myldap_entry_new(search);
return search->entry;
}
- else if ( (stat==NSS_STATUS_NOTFOUND) &&
+ else if ( (rc==LDAP_SUCCESS) &&
+ (search->msgid==-1) &&
(search->cookie!=NULL) &&
(search->cookie->bv_len!=0) )
{
@@ -960,16 +946,16 @@ MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search)
/* FIXME: figure out if we need to free something */
return NULL;
}
+ /* set up a new search for the next page */
rc=ldap_search_ext(search->session->ls_conn,
search->base,search->scope,search->filter,
- search->attrs,0,serverctrls,NULL,LDAP_NO_LIMIT,
+ search->attrs,0,serverctrls,NULL,NULL,
LDAP_NO_LIMIT,&msgid);
ldap_control_free(serverctrls[0]);
- if (msgid<0)
+ if (rc!=LDAP_SUCCESS)
{
log_log(LOG_WARNING,"myldap_get_entry(): ldap_search_ext() failed: %s",
ldap_err2string(rc));
- /* FIXME: figure out if we need to free something */
return NULL;
}
search->msgid=msgid;
@@ -977,18 +963,17 @@ MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search)
}
else
{
- log_log(LOG_DEBUG,"myldap_get_entry(): do_result() returned error code");
- /* there was another problem, bail out */
+ /* there was another problem, bail out
+ (do_result() already logged an error if any)
+ most likely there were no more results */
return NULL;
}
}
}
-/*
- * Get the DN from the entry. This function only returns NULL (and sets
- * errno) if an incorrect entry is passed. If the DN value cannot be
- * retreived "unknown" is returned instead.
- */
+/* Get the DN from the entry. This function only returns NULL (and sets
+ errno) if an incorrect entry is passed. If the DN value cannot be
+ retreived "unknown" is returned instead. */
const char *myldap_get_dn(MYLDAP_ENTRY *entry)
{
int rc;
diff --git a/nslcd/myldap.h b/nslcd/myldap.h
index 83b97ba..a34111d 100644
--- a/nslcd/myldap.h
+++ b/nslcd/myldap.h
@@ -23,9 +23,9 @@
#ifndef _MYLDAP_H
#define _MYLDAP_H
-/* for size_t: */
+/* for size_t */
#include <stdlib.h>
-/* for LDAP_SCOPE_*: */
+/* for LDAP_SCOPE_* */
#include <ldap.h>
#include "compat/attrs.h"