diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2007-12-20 17:58:44 +0100 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2007-12-20 17:58:44 +0100 |
commit | 4672d6dbcb5a69b770faf0832893366e78c67600 (patch) | |
tree | acafaca25fe3443f45b3d1f00a5a017baee7a580 | |
parent | d3f57728a029b4a3215304e2fdfd3a1a55b68087 (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.c | 603 | ||||
-rw-r--r-- | nslcd/myldap.h | 4 |
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" |