"Fossies" - the Fresh Open Source Software Archive

Member "nss-pam-ldapd-0.9.12/nslcd/myldap.c" (15 Nov 2021, 85840 Bytes) of package /linux/privat/nss-pam-ldapd-0.9.12.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "myldap.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.9.11_vs_0.9.12.

    1 /*
    2    myldap.c - simple interface to do LDAP requests
    3    Parts of this file were part of the nss_ldap library (as ldap-nss.c)
    4    which has been forked into the nss-pam-ldapd library.
    5 
    6    Copyright (C) 1997-2006 Luke Howard
    7    Copyright (C) 2006-2007 West Consulting
    8    Copyright (C) 2006-2020 Arthur de Jong
    9 
   10    This library is free software; you can redistribute it and/or
   11    modify it under the terms of the GNU Lesser General Public
   12    License as published by the Free Software Foundation; either
   13    version 2.1 of the License, or (at your option) any later version.
   14 
   15    This library is distributed in the hope that it will be useful,
   16    but WITHOUT ANY WARRANTY; without even the implied warranty of
   17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   18    Lesser General Public License for more details.
   19 
   20    You should have received a copy of the GNU Lesser General Public
   21    License along with this library; if not, write to the Free Software
   22    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   23    02110-1301 USA
   24 */
   25 
   26 /*
   27    This library expects to use an LDAP library to provide the real
   28    functionality and only provides a convenient wrapper.
   29    Some pointers for more information on the LDAP API:
   30      http://tools.ietf.org/id/draft-ietf-ldapext-ldap-c-api-05.txt
   31      http://www.mozilla.org/directory/csdk-docs/function.htm
   32      http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/apis/dirserv1.htm
   33      http://www.openldap.org/software/man.cgi?query=ldap
   34 */
   35 
   36 #include "config.h"
   37 
   38 #include <stdio.h>
   39 #include <stdlib.h>
   40 #include <unistd.h>
   41 #include <string.h>
   42 #include <strings.h>
   43 #include <sys/time.h>
   44 #include <time.h>
   45 #include <sys/types.h>
   46 #include <sys/socket.h>
   47 #include <errno.h>
   48 #include <lber.h>
   49 #include <ldap.h>
   50 #ifdef HAVE_LDAP_SSL_H
   51 #include <ldap_ssl.h>
   52 #endif
   53 #ifdef HAVE_GSSLDAP_H
   54 #include <gssldap.h>
   55 #endif
   56 #ifdef HAVE_GSSSASL_H
   57 #include <gsssasl.h>
   58 #endif
   59 #ifdef HAVE_SASL_SASL_H
   60 #include <sasl/sasl.h>
   61 #endif
   62 #ifdef HAVE_SASL_H
   63 #include <sasl.h>
   64 #endif
   65 #include <ctype.h>
   66 #include <pthread.h>
   67 #include <stdarg.h>
   68 
   69 #include "myldap.h"
   70 #include "common.h"
   71 #include "log.h"
   72 #include "cfg.h"
   73 #include "common/set.h"
   74 #include "compat/ldap_compat.h"
   75 #include "attmap.h"
   76 
   77 /* the maximum number of searches per session */
   78 #define MAX_SEARCHES_IN_SESSION 4
   79 
   80 /* the maximum number of dn's to log to the debug log for each search */
   81 #define MAX_DEBUG_LOG_DNS 10
   82 
   83 /* a fake scope that is used to not perform an actual search but only
   84    simulate the handling of the search (used for authentication) */
   85 #define MYLDAP_SCOPE_BINDONLY 0x1972  /* magic number: should never be a real scope */
   86 
   87 /* This refers to a current LDAP session that contains the connection
   88    information. */
   89 struct ldap_session {
   90   /* the connection */
   91   LDAP *ld;
   92   /* timestamp of last activity */
   93   time_t lastactivity;
   94   /* index into uris: currently connected LDAP uri */
   95   int current_uri;
   96   /* a list of searches registered with this session */
   97   struct myldap_search *searches[MAX_SEARCHES_IN_SESSION];
   98   /* the username to bind with */
   99   char binddn[BUFLEN_DN];
  100   /* the password to bind with if any */
  101   char bindpw[BUFLEN_PASSWORD];
  102   /* the authentication result (NSLCD_PAM_* code) */
  103   int policy_response;
  104   /* the authentication message */
  105   char policy_message[BUFLEN_MESSAGE];
  106 };
  107 
  108 /* A search description set as returned by myldap_search(). */
  109 struct myldap_search {
  110   /* reference to the session */
  111   MYLDAP_SESSION *session;
  112   /* indicator that the search is still valid */
  113   int valid;
  114   /* the parameters describing the search */
  115   const char *base;
  116   int scope;
  117   const char *filter;
  118   char **attrs;
  119   /* a pointer to the current result entry, used for
  120      freeing resource allocated with that entry */
  121   MYLDAP_ENTRY *entry;
  122   /* LDAP message id for the search, -1 indicates absence of an active search */
  123   int msgid;
  124   /* the last result that was returned by ldap_result() */
  125   LDAPMessage *msg;
  126   /* cookie for paged searches */
  127   struct berval *cookie;
  128   /* to indicate that we can retry the search from myldap_get_entry() */
  129   int may_retry_search;
  130   /* the number of results returned so far */
  131   int count;
  132 };
  133 
  134 /* The maximum number of calls to myldap_get_values() that may be
  135    done per returned entry. */
  136 #define MAX_ATTRIBUTES_PER_ENTRY 16
  137 
  138 /* The maximum number of buffers (used for ranged attribute values and
  139    values returned by bervalues_to_values()) that may be stored per entry. */
  140 #define MAX_BUFFERS_PER_ENTRY 8
  141 
  142 /* A single entry from the LDAP database as returned by
  143    myldap_get_entry(). */
  144 struct myldap_entry {
  145   /* reference to the search to be used to get parameters
  146      (e.g. LDAP connection) for other calls */
  147   MYLDAP_SEARCH *search;
  148   /* the DN */
  149   const char *dn;
  150   /* a cached version of the exploded rdn */
  151   char **exploded_rdn;
  152   /* a cache of attribute to value list */
  153   char **attributevalues[MAX_ATTRIBUTES_PER_ENTRY];
  154   /* a reference to buffers so we can free() them later on */
  155   char **buffers[MAX_BUFFERS_PER_ENTRY];
  156 };
  157 
  158 /* Flag to record first search operation */
  159 int first_search = 1;
  160 
  161 static void myldap_err(int pri, LDAP *ld, int rc, const char *format, ...)
  162 {
  163   char message[BUFLEN_MESSAGE];
  164   char *msg_ldap = NULL;
  165   char *msg_diag = NULL;
  166   char *msg_errno = NULL;
  167   va_list ap;
  168   /* make the message */
  169   va_start(ap, format);
  170   vsnprintf(message, sizeof(message), format, ap);
  171   message[sizeof(message) - 1] = '\0';
  172   va_end(ap);
  173   /* get the various error message */
  174   if (rc != LDAP_SUCCESS)
  175   {
  176     msg_ldap = ldap_err2string(rc);
  177     /* get the diagnostic information */
  178 #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
  179     if (ld != NULL)
  180       ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg_diag);
  181 #endif /* LDAP_OPT_DIAGNOSTIC_MESSAGE */
  182   }
  183   if (errno != 0)
  184     msg_errno = strerror(errno);
  185   /* log the message */
  186   log_log(pri, "%s%s%s%s%s%s%s", message,
  187           (msg_ldap == NULL) ? "" : ": ", (msg_ldap == NULL) ? "" : msg_ldap,
  188           (msg_diag == NULL) ? "" : ": ", (msg_diag == NULL) ? "" : msg_diag,
  189           (msg_errno == NULL) ? "" : ": ", (msg_errno == NULL) ? "" : msg_errno);
  190   /* free diagnostic message */
  191   if (msg_diag != NULL)
  192     ldap_memfree(msg_diag);
  193 }
  194 
  195 static MYLDAP_ENTRY *myldap_entry_new(MYLDAP_SEARCH *search)
  196 {
  197   MYLDAP_ENTRY *entry;
  198   int i;
  199   /* Note: as an alternative we could embed the myldap_entry into the
  200      myldap_search struct to save on malloc() and free() calls. */
  201   /* allocate new entry */
  202   entry = (MYLDAP_ENTRY *)malloc(sizeof(struct myldap_entry));
  203   if (entry == NULL)
  204   {
  205     log_log(LOG_CRIT, "myldap_entry_new(): malloc() failed to allocate memory");
  206     exit(EXIT_FAILURE);
  207   }
  208   /* fill in fields */
  209   entry->search = search;
  210   entry->dn = NULL;
  211   entry->exploded_rdn = NULL;
  212   for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
  213     entry->attributevalues[i] = NULL;
  214   for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
  215     entry->buffers[i] = NULL;
  216   /* return the fresh entry */
  217   return entry;
  218 }
  219 
  220 static void myldap_entry_free(MYLDAP_ENTRY *entry)
  221 {
  222   int i;
  223   /* free the DN */
  224   if (entry->dn != NULL)
  225     ldap_memfree((char *)entry->dn);
  226   /* free the exploded RDN */
  227   if (entry->exploded_rdn != NULL)
  228     ldap_value_free(entry->exploded_rdn);
  229   /* free all attribute values */
  230   for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
  231     if (entry->attributevalues[i] != NULL)
  232       ldap_value_free(entry->attributevalues[i]);
  233   /* free all buffers */
  234   for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
  235     if (entry->buffers[i] != NULL)
  236       free(entry->buffers[i]);
  237   /* we don't need the result anymore, ditch it. */
  238   ldap_msgfree(entry->search->msg);
  239   entry->search->msg = NULL;
  240   /* free the actual memory for the struct */
  241   free(entry);
  242 }
  243 
  244 static MYLDAP_SEARCH *myldap_search_new(MYLDAP_SESSION *session,
  245                                         const char *base, int scope,
  246                                         const char *filter,
  247                                         const char **attrs)
  248 {
  249   char *buffer;
  250   MYLDAP_SEARCH *search;
  251   int i;
  252   size_t sz;
  253   /* figure out size for new memory block to allocate
  254      this has the advantage that we can free the whole lot with one call */
  255   sz = sizeof(struct myldap_search);
  256   sz += strlen(base) + 1 + strlen(filter) + 1;
  257   for (i = 0; attrs[i] != NULL; i++)
  258     sz += strlen(attrs[i]) + 1;
  259   sz += (i + 1) * sizeof(char *);
  260   /* allocate new results memory region */
  261   buffer = (char *)malloc(sz);
  262   if (buffer == NULL)
  263   {
  264     log_log(LOG_CRIT, "myldap_search_new(): malloc() failed to allocate memory");
  265     exit(EXIT_FAILURE);
  266   }
  267   /* initialize struct */
  268   search = (MYLDAP_SEARCH *)(void *)(buffer);
  269   buffer += sizeof(struct myldap_search);
  270   /* save pointer to session */
  271   search->session = session;
  272   /* flag as valid search */
  273   search->valid = 1;
  274   /* initialize array of attributes */
  275   search->attrs = (char **)(void *)buffer;
  276   buffer += (i + 1) * sizeof(char *);
  277   /* copy base */
  278   strcpy(buffer, base);
  279   search->base = buffer;
  280   buffer += strlen(base) + 1;
  281   /* just plainly store scope */
  282   search->scope = scope;
  283   /* copy filter */
  284   strcpy(buffer, filter);
  285   search->filter = buffer;
  286   buffer += strlen(filter) + 1;
  287   /* copy attributes themselves */
  288   for (i = 0; attrs[i] != NULL; i++)
  289   {
  290     strcpy(buffer, attrs[i]);
  291     search->attrs[i] = buffer;
  292     buffer += strlen(attrs[i]) + 1;
  293   }
  294   search->attrs[i] = NULL;
  295   /* initialize context */
  296   search->cookie = NULL;
  297   search->msg = NULL;
  298   search->msgid = -1;
  299   search->may_retry_search = 1;
  300   /* clear result entry */
  301   search->entry = NULL;
  302   search->count = 0;
  303   /* return the new search struct */
  304   return search;
  305 }
  306 
  307 static MYLDAP_SESSION *myldap_session_new(void)
  308 {
  309   MYLDAP_SESSION *session;
  310   int i;
  311   /* allocate memory for the session storage */
  312   session = (struct ldap_session *)malloc(sizeof(struct ldap_session));
  313   if (session == NULL)
  314   {
  315     log_log(LOG_CRIT, "myldap_session_new(): malloc() failed to allocate memory");
  316     exit(EXIT_FAILURE);
  317   }
  318   /* initialize the session */
  319   session->ld = NULL;
  320   session->lastactivity = 0;
  321   session->current_uri = 0;
  322   for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
  323     session->searches[i] = NULL;
  324   session->binddn[0] = '\0';
  325   memset(session->bindpw, 0, sizeof(session->bindpw));
  326   session->bindpw[0] = '\0';
  327   session->policy_response = NSLCD_PAM_SUCCESS;
  328   session->policy_message[0] = '\0';
  329   /* return the new session */
  330   return session;
  331 }
  332 
  333 PURE static inline int is_valid_entry(MYLDAP_ENTRY *entry)
  334 {
  335  return (entry != NULL) && (entry->search != NULL) &&
  336         (entry->search->session != NULL) && (entry->search->session->ld != NULL) &&
  337         (entry->search->msg != NULL);
  338 }
  339 
  340 #ifdef HAVE_SASL_INTERACT_T
  341 /* this is registered with ldap_sasl_interactive_bind_s() in do_bind() */
  342 static int do_sasl_interact(LDAP UNUSED(*ld), unsigned UNUSED(flags),
  343                             void *defaults, void *_interact)
  344 {
  345   struct ldap_config *cfg = defaults;
  346   sasl_interact_t *interact = _interact;
  347   while (interact->id != SASL_CB_LIST_END)
  348   {
  349     switch (interact->id)
  350     {
  351       case SASL_CB_GETREALM:
  352         if (cfg->sasl_realm)
  353         {
  354           log_log(LOG_DEBUG, "do_sasl_interact(): returning sasl_realm \"%s\"",
  355                   cfg->sasl_realm);
  356           interact->result = cfg->sasl_realm;
  357           interact->len = strlen(cfg->sasl_realm);
  358         }
  359         else
  360           log_log(LOG_DEBUG, "do_sasl_interact(): were asked for sasl_realm but we don't have any");
  361         break;
  362       case SASL_CB_AUTHNAME:
  363         if (cfg->sasl_authcid)
  364         {
  365           log_log(LOG_DEBUG, "do_sasl_interact(): returning sasl_authcid \"%s\"",
  366                   cfg->sasl_authcid);
  367           interact->result = cfg->sasl_authcid;
  368           interact->len = strlen(cfg->sasl_authcid);
  369         }
  370         else
  371           log_log(LOG_DEBUG, "do_sasl_interact(): were asked for sasl_authcid but we don't have any");
  372         break;
  373       case SASL_CB_USER:
  374         if (cfg->sasl_authzid)
  375         {
  376           log_log(LOG_DEBUG, "do_sasl_interact(): returning sasl_authzid \"%s\"",
  377                   cfg->sasl_authzid);
  378           interact->result = cfg->sasl_authzid;
  379           interact->len = strlen(cfg->sasl_authzid);
  380         }
  381         else
  382           log_log(LOG_DEBUG, "do_sasl_interact(): were asked for sasl_authzid but we don't have any");
  383         break;
  384       case SASL_CB_PASS:
  385         if (cfg->bindpw)
  386         {
  387           log_log(LOG_DEBUG, "do_sasl_interact(): returning bindpw \"***\"");
  388           interact->result = cfg->bindpw;
  389           interact->len = strlen(cfg->bindpw);
  390         }
  391         else
  392           log_log(LOG_DEBUG, "do_sasl_interact(): were asked for bindpw but we don't have any");
  393         break;
  394       default:
  395         /* just ignore */
  396         break;
  397     }
  398     interact++;
  399   }
  400   return LDAP_SUCCESS;
  401 }
  402 #endif /* HAVE_SASL_INTERACT_T */
  403 
  404 #define LDAP_SET_OPTION(ld, option, invalue)                                \
  405   rc = ldap_set_option(ld, option, invalue);                                \
  406   if (rc != LDAP_SUCCESS)                                                   \
  407   {                                                                         \
  408     myldap_err(LOG_ERR, ld, rc, "ldap_set_option(" #option ") failed");     \
  409     return rc;                                                              \
  410   }
  411 
  412 #if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE)
  413 static void print_ppolicy_expiry(MYLDAP_SESSION *session, unsigned int sec)
  414 {
  415   unsigned int days = 0;
  416   unsigned int hours = 0;
  417   unsigned int minutes = 0;
  418   /* return this warning so PAM can present it to the user */
  419   if (strlen(session->policy_message) != 0)
  420     return;
  421   if (sec > 24 * 3600)
  422   {
  423     days = sec / (24 * 3600);
  424     sec -= days * 24 * 3600;
  425   }
  426   if (sec > 3600)
  427   {
  428     hours = sec / 3600;
  429     sec -= (hours * 3600);
  430   }
  431   if (sec > 60)
  432   {
  433     minutes = sec / 60;
  434     sec -= minutes * 60;
  435   }
  436   if (days > 1)
  437     mysnprintf(session->policy_message, sizeof(session->policy_message),
  438                "Password will expire in %u days", days);
  439   else if (days > 0)
  440     mysnprintf(session->policy_message, sizeof(session->policy_message),
  441                "Password will expire in %u hours", hours + 24);
  442   else if (hours > 1)
  443   {
  444     if (minutes > 1)
  445       mysnprintf(session->policy_message, sizeof(session->policy_message),
  446                  "Password will expire in %u hours and %u minutes",
  447                  hours, minutes);
  448     else
  449       mysnprintf(session->policy_message, sizeof(session->policy_message),
  450                  "Password will expire in %u hours", hours);
  451   }
  452   else if (hours > 0)
  453     mysnprintf(session->policy_message, sizeof(session->policy_message),
  454                "Password will expire in %u minutes", minutes + 60);
  455   else if (minutes > 1)
  456   {
  457     if (sec > 1)
  458       mysnprintf(session->policy_message, sizeof(session->policy_message),
  459                  "Password will expire in %u minutes and %u seconds",
  460                  minutes, sec);
  461     else
  462       mysnprintf(session->policy_message, sizeof(session->policy_message),
  463                  "Password will expire in %u minutes", minutes);
  464   }
  465   else
  466     mysnprintf(session->policy_message, sizeof(session->policy_message),
  467                "Password will expire in %u seconds", sec);
  468 }
  469 
  470 static void handle_ppolicy_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPControl **ctrls)
  471 {
  472   int i;
  473   int rc;
  474   /* clear policy response information in session */
  475   session->policy_response = NSLCD_PAM_SUCCESS;
  476   strncpy(session->policy_message, "", sizeof(session->policy_message));
  477   for (i = 0; ctrls[i] != NULL; i++)
  478   {
  479     if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PWEXPIRED) == 0)
  480     {
  481       /* check for expired control: force the user to change their password */
  482       log_log(LOG_DEBUG, "got LDAP_CONTROL_PWEXPIRED (password expired, user should change)");
  483       if (session->policy_response == NSLCD_PAM_SUCCESS)
  484         session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD;
  485     }
  486     else if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PWEXPIRING) == 0)
  487     {
  488       /* check for password expiration warning control: the password is about
  489          to expire (returns the number of seconds remaining until the password
  490          expires) */
  491       char seconds[32];
  492       long int sec;
  493       mysnprintf(seconds, sizeof(seconds), "%.*s", (int)ctrls[i]->ldctl_value.bv_len,
  494                  ctrls[i]->ldctl_value.bv_val);
  495       sec = atol(seconds);
  496       log_log(LOG_DEBUG, "got LDAP_CONTROL_PWEXPIRING (password will expire in %ld seconds)",
  497               sec);
  498       print_ppolicy_expiry(session, (unsigned int)sec);
  499     }
  500     else if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0)
  501     {
  502       /* check for password policy control */
  503       int expire = 0, grace = 0;
  504       LDAPPasswordPolicyError error = -1;
  505       rc = ldap_parse_passwordpolicy_control(ld, ctrls[i], &expire, &grace, &error);
  506       if (rc != LDAP_SUCCESS)
  507         myldap_err(LOG_WARNING, ld, rc, "ldap_parse_passwordpolicy_control() failed (ignored)");
  508       else
  509       {
  510         /* log returned control information */
  511         log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (%s)",
  512                 ldap_passwordpolicy_err2txt(error));
  513         if (expire >= 0)
  514           log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (password will expire in %d seconds)",
  515                   expire);
  516         if (grace >= 0)
  517           log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (%d grace logins left)",
  518                   grace);
  519         /* return this information to PAM */
  520         if ((error == PP_passwordExpired) &&
  521             ((session->policy_response == NSLCD_PAM_SUCCESS) ||
  522              (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD)))
  523         {
  524           /* this means that the password has expired and must be reset */
  525           session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD;
  526           mysnprintf(session->policy_message, sizeof(session->policy_message),
  527                      "%s", ldap_passwordpolicy_err2txt(error));
  528         }
  529         else if ((error == PP_accountLocked) &&
  530                  ((session->policy_response == NSLCD_PAM_SUCCESS) ||
  531                   (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD)))
  532         {
  533           /* this means that the account is locked and the user cannot log
  534              in (the bind probably failed already) */
  535           session->policy_response = NSLCD_PAM_ACCT_EXPIRED;
  536           mysnprintf(session->policy_message, sizeof(session->policy_message),
  537                      "%s", ldap_passwordpolicy_err2txt(error));
  538         }
  539         else if ((error == PP_changeAfterReset) &&
  540                  (session->policy_response == NSLCD_PAM_SUCCESS))
  541         {
  542           /* this indicates that the password must be changed before the
  543              user is allowed to perform any other operation */
  544           session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD;
  545           mysnprintf(session->policy_message, sizeof(session->policy_message),
  546                      "%s", ldap_passwordpolicy_err2txt(error));
  547         }
  548         else if ((error != PP_noError) &&
  549                  ((session->policy_response == NSLCD_PAM_SUCCESS) ||
  550                   (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD)))
  551         {
  552           /* any other error is assumed to mean that the operation failed */
  553           session->policy_response = NSLCD_PAM_PERM_DENIED;
  554           mysnprintf(session->policy_message, sizeof(session->policy_message),
  555                      "%s", ldap_passwordpolicy_err2txt(error));
  556         }
  557         /* both expire and grace should just be warnings to the user */
  558         if ((expire >= 0) && (strlen(session->policy_message) == 0))
  559         {
  560           /* if no other error has happened, this indicates that the password
  561              will soon expire (number of seconds) */
  562           print_ppolicy_expiry(session, (unsigned int)expire);
  563         }
  564         else if ((grace >= 0) && (strlen(session->policy_message) == 0))
  565         {
  566           /* this indicates the number of grace logins that are left before
  567              no further login attempts will be allowed */
  568           mysnprintf(session->policy_message, sizeof(session->policy_message),
  569                      "Password expired, %d grace logins left", grace);
  570         }
  571       }
  572     }
  573     /* ignore any other controls */
  574   }
  575 }
  576 
  577 static int do_ppolicy_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri)
  578 {
  579   int rc, parserc;
  580   struct berval cred;
  581   LDAPControl passwd_policy_req;
  582   LDAPControl *requestctrls[2];
  583   LDAPControl **responsectrls;
  584   int msgid;
  585   struct timeval timeout;
  586   LDAPMessage *result;
  587   /* build policy request if pam_authc_ppolicy is set */
  588   if (nslcd_cfg->pam_authc_ppolicy)
  589   {
  590     passwd_policy_req.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
  591     passwd_policy_req.ldctl_value.bv_val = NULL; /* none */
  592     passwd_policy_req.ldctl_value.bv_len = 0;
  593     passwd_policy_req.ldctl_iscritical = 0; /* not critical */
  594     requestctrls[0] = &passwd_policy_req;
  595   }
  596   else
  597     requestctrls[0] = NULL;
  598   requestctrls[1] = NULL;
  599   /* build password berval */
  600   cred.bv_val = (char *)session->bindpw;
  601   cred.bv_len = strlen(session->bindpw);
  602   /* do a SASL simple bind with the binddn and bindpw */
  603   log_log(LOG_DEBUG, "ldap_sasl_bind(\"%s\",%s) (uri=\"%s\") (ppolicy=%s)",
  604           session->binddn, (session->bindpw[0] != '\0') ? "\"***\"" : "\"\"",
  605           uri, (requestctrls[0] == NULL) ? "no" : "yes");
  606   rc = ldap_sasl_bind(ld, session->binddn, LDAP_SASL_SIMPLE, &cred, requestctrls, NULL, &msgid);
  607   if (rc != LDAP_SUCCESS)
  608     return rc;
  609   if (msgid == -1)
  610   {
  611     myldap_err(LOG_WARNING, ld, rc,"ldap_sasl_bind() failed (msgid=-1, uri=%s)", uri);
  612     return LDAP_OPERATIONS_ERROR;
  613   }
  614   /* get the result from the bind operation */
  615   timeout.tv_sec = nslcd_cfg->bind_timelimit;
  616   timeout.tv_usec = 0;
  617   result = NULL;
  618   rc = ldap_result(ld, msgid, LDAP_MSG_ALL, &timeout, &result);
  619   if (rc == -1) /* some error */
  620   {
  621     if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
  622       rc = LDAP_UNAVAILABLE;
  623     myldap_err(LOG_ERR, ld, rc, "ldap_result() failed");
  624     if (result != NULL)
  625       ldap_msgfree(result);
  626     return LDAP_LOCAL_ERROR;
  627   }
  628   if (rc == 0) /* the timeout expired */
  629   {
  630     log_log(LOG_ERR, "ldap_result() timed out");
  631     if (result != NULL)
  632       ldap_msgfree(result);
  633     return LDAP_TIMEOUT;
  634   }
  635   /* parse the result from the bind operation (frees result, gets controls) */
  636   responsectrls = NULL;
  637   parserc = ldap_parse_result(ld, result, &rc, NULL, NULL, NULL, &responsectrls, 1);
  638   if (parserc != LDAP_SUCCESS)
  639   {
  640     myldap_err(LOG_ERR, ld, parserc, "ldap_parse_result() failed");
  641     if (responsectrls != NULL)
  642       ldap_controls_free(responsectrls);
  643     return parserc;
  644   }
  645   /* handle any returned controls */
  646   if (responsectrls != NULL)
  647   {
  648     if (nslcd_cfg->pam_authc_ppolicy)
  649       handle_ppolicy_controls(session, ld, responsectrls);
  650     ldap_controls_free(responsectrls);
  651   }
  652   /* return the result of the BIND operation */
  653   if (rc != LDAP_SUCCESS)
  654   {
  655     myldap_err(LOG_DEBUG, ld, rc, "ldap_parse_result() result");
  656     return rc;
  657   }
  658   return LDAP_SUCCESS;
  659 }
  660 #endif /* no SASL, so no ppolicy */
  661 
  662 /* This function performs the authentication phase of opening a connection.
  663    The binddn and bindpw parameters may be used to override the authentication
  664    mechanism defined in the configuration.  This returns an LDAP result
  665    code. */
  666 static int do_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri)
  667 {
  668   int rc;
  669 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
  670 #ifndef HAVE_SASL_INTERACT_T
  671   struct berval cred;
  672 #endif /* not HAVE_SASL_INTERACT_T */
  673 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
  674 #ifdef LDAP_OPT_X_TLS
  675   /* check if StartTLS is requested */
  676   if (nslcd_cfg->ssl == SSL_START_TLS)
  677   {
  678     log_log(LOG_DEBUG, "ldap_start_tls_s()");
  679     errno = 0;
  680     rc = ldap_start_tls_s(ld, NULL, NULL);
  681     if (rc != LDAP_SUCCESS)
  682     {
  683       myldap_err(LOG_WARNING, ld, rc, "ldap_start_tls_s() failed (uri=%s)",
  684                  uri);
  685       return rc;
  686     }
  687   }
  688 #endif /* LDAP_OPT_X_TLS */
  689   /* check if the binddn and bindpw are overwritten in the session */
  690   if (session->binddn[0] != '\0')
  691   {
  692 #if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE)
  693     return do_ppolicy_bind(session, ld, uri);
  694 #else /* no SASL, so no ppolicy */
  695     /* do a simple bind */
  696     log_log(LOG_DEBUG, "ldap_simple_bind_s(\"%s\",%s) (uri=\"%s\")",
  697             session->binddn,
  698             (session->bindpw[0] != '\0') ? "\"***\"" : "\"\"",
  699             uri);
  700     return ldap_simple_bind_s(ld, session->binddn, session->bindpw);
  701 #endif
  702   }
  703   /* perform SASL bind if requested and available on platform */
  704 #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
  705   /* TODO: store this information in the session */
  706   if (nslcd_cfg->sasl_mech != NULL)
  707   {
  708     /* do a SASL bind */
  709     if (nslcd_cfg->sasl_secprops != NULL)
  710     {
  711       log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_SASL_SECPROPS,\"%s\")",
  712               nslcd_cfg->sasl_secprops);
  713       LDAP_SET_OPTION(ld, LDAP_OPT_X_SASL_SECPROPS, (void *)nslcd_cfg->sasl_secprops);
  714     }
  715 #ifdef HAVE_SASL_INTERACT_T
  716     if (nslcd_cfg->binddn != NULL)
  717       log_log(LOG_DEBUG, "ldap_sasl_interactive_bind_s(\"%s\",\"%s\") (uri=\"%s\")",
  718               nslcd_cfg->binddn, nslcd_cfg->sasl_mech, uri);
  719     else
  720       log_log(LOG_DEBUG, "ldap_sasl_interactive_bind_s(NULL,\"%s\") (uri=\"%s\")",
  721               nslcd_cfg->sasl_mech, uri);
  722     return ldap_sasl_interactive_bind_s(ld, nslcd_cfg->binddn,
  723                                         nslcd_cfg->sasl_mech, NULL, NULL,
  724                                         LDAP_SASL_QUIET, do_sasl_interact,
  725                                         (void *)nslcd_cfg);
  726 #else /* HAVE_SASL_INTERACT_T */
  727     if (nslcd_cfg->bindpw != NULL)
  728     {
  729       cred.bv_val = nslcd_cfg->bindpw;
  730       cred.bv_len = strlen(nslcd_cfg->bindpw);
  731     }
  732     else
  733     {
  734       cred.bv_val = "";
  735       cred.bv_len = 0;
  736     }
  737     if (nslcd_cfg->binddn != NULL)
  738       log_log(LOG_DEBUG, "ldap_sasl_bind_s(\"%s\",\"%s\",%s) (uri=\"%s\")",
  739               nslcd_cfg->binddn, nslcd_cfg->sasl_mech,
  740               nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
  741     else
  742       log_log(LOG_DEBUG, "ldap_sasl_bind_s(NULL,\"%s\",%s) (uri=\"%s\")",
  743               nslcd_cfg->sasl_mech,
  744               nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
  745     return ldap_sasl_bind_s(ld, nslcd_cfg->binddn,
  746                             nslcd_cfg->sasl_mech, &cred, NULL, NULL, NULL);
  747 #endif /* not HAVE_SASL_INTERACT_T */
  748   }
  749 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
  750   /* do a simple bind */
  751   if (nslcd_cfg->binddn)
  752     log_log(LOG_DEBUG, "ldap_simple_bind_s(\"%s\",%s) (uri=\"%s\")",
  753             nslcd_cfg->binddn, nslcd_cfg->bindpw ? "\"***\"" : "NULL",
  754             uri);
  755   else
  756     log_log(LOG_DEBUG, "ldap_simple_bind_s(NULL,%s) (uri=\"%s\")",
  757             nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
  758   return ldap_simple_bind_s(ld, nslcd_cfg->binddn, nslcd_cfg->bindpw);
  759 }
  760 
  761 #ifdef HAVE_LDAP_SET_REBIND_PROC
  762 /* This function is called by the LDAP library when chasing referrals.
  763    It is configured with the ldap_set_rebind_proc() below. */
  764 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
  765 static int do_rebind(LDAP *ld, LDAP_CONST char *url,
  766                      ber_tag_t UNUSED(request),
  767                      ber_int_t UNUSED(msgid), void *arg)
  768 {
  769   MYLDAP_SESSION *session = (MYLDAP_SESSION *)arg;
  770   log_log(LOG_DEBUG, "rebinding to %s", url);
  771   return do_bind(session, ld, url);
  772 }
  773 #else /* not recent OpenLDAP */
  774 static int do_rebind(LDAP *ld, char **dnp, char **passwdp, int *authmethodp,
  775                      int freeit, void *arg)
  776 {
  777   MYLDAP_SESSION *session = (MYLDAP_SESSION *)arg;
  778   if (freeit)
  779   {
  780     free(*dnp);
  781     memset(*passwdp, 0, strlen(*passwdp));
  782     free(*passwdp);
  783   }
  784   else
  785   {
  786     log_log(LOG_DEBUG, "rebinding");
  787     *dnp = strdup(session->binddn);
  788     *passwdp = strdup(session->bindpw);
  789     *authmethodp = LDAP_AUTH_SIMPLE;
  790     if ((*dnp == NULL) || (*passwdp == NULL))
  791     {
  792       if (*dnp != NULL)
  793         free(*dnp);
  794       log_log(LOG_CRIT, "do_rebind(): strdup() failed to allocate memory");
  795       return LDAP_NO_MEMORY;
  796     }
  797   }
  798   return LDAP_SUCCESS;
  799 }
  800 #endif /* not recent OpenLDAP */
  801 #endif /* HAVE_LDAP_SET_REBIND_PROC */
  802 
  803 /* set a recieve and send timeout on a socket */
  804 static int set_socket_timeout(LDAP *ld, time_t sec, suseconds_t usec)
  805 {
  806   struct timeval tv;
  807   int rc = LDAP_SUCCESS;
  808   int sd;
  809   log_log(LOG_DEBUG, "set_socket_timeout(%lu,%lu)",
  810           (unsigned long)sec, (unsigned long)usec);
  811   /* get the socket */
  812   if ((rc = ldap_get_option(ld, LDAP_OPT_DESC, &sd)) != LDAP_SUCCESS)
  813   {
  814     myldap_err(LOG_ERR, ld, rc, "ldap_get_option(LDAP_OPT_DESC) failed");
  815     return rc;
  816   }
  817   /* ignore invalid (probably closed) file descriptors */
  818   if (sd <= 0)
  819     return LDAP_SUCCESS;
  820   /* set timeouts */
  821   memset(&tv, 0, sizeof(tv));
  822   tv.tv_sec = sec;
  823   tv.tv_usec = usec;
  824   if (setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv)))
  825   {
  826     log_log(LOG_ERR, "setsockopt(%d,SO_RCVTIMEO) failed: %s",
  827             sd, strerror(errno));
  828     rc = LDAP_LOCAL_ERROR;
  829   }
  830   if (setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, (void *)&tv, sizeof(tv)))
  831   {
  832     log_log(LOG_ERR, "setsockopt(%d,SO_RCVTIMEO) failed: %s",
  833             sd, strerror(errno));
  834     rc = LDAP_LOCAL_ERROR;
  835   }
  836   return rc;
  837 }
  838 
  839 #ifdef LDAP_OPT_CONNECT_CB
  840 /* This function is called by the LDAP library once a connection was made to the server. We
  841    set a timeout on the socket here, to catch network timeouts during the ssl
  842    handshake phase. It is configured with LDAP_OPT_CONNECT_CB. */
  843 static int connect_cb(LDAP *ld, Sockbuf UNUSED(*sb),
  844                       LDAPURLDesc UNUSED(*srv), struct sockaddr UNUSED(*addr),
  845                       struct ldap_conncb UNUSED(*ctx))
  846 {
  847   /* set timeout options on socket to avoid hang in some cases (a little
  848      more than the normal timeout so this should only be triggered in cases
  849      where the library behaves incorrectly) */
  850   if (nslcd_cfg->timelimit)
  851     set_socket_timeout(ld, nslcd_cfg->timelimit, 500000);
  852   return LDAP_SUCCESS;
  853 }
  854 
  855 /* We have an empty disconnect callback because LDAP_OPT_CONNECT_CB expects
  856    both functions to be available. */
  857 static void disconnect_cb(LDAP UNUSED(*ld), Sockbuf UNUSED(*sb),
  858                           struct ldap_conncb UNUSED(*ctx))
  859 {
  860 }
  861 #endif /* LDAP_OPT_CONNECT_CB */
  862 
  863 /* This function sets a number of properties on the connection, based
  864    what is configured in the configfile. This function returns an
  865    LDAP status code. */
  866 static int do_set_options(MYLDAP_SESSION *session)
  867 {
  868   int rc;
  869   struct timeval tv;
  870 #ifdef LDAP_OPT_CONNECT_CB
  871   /* make this static because OpenLDAP doesn't make its own copy */
  872   static struct ldap_conncb cb;
  873 #endif /* LDAP_OPT_CONNECT_CB */
  874 #ifdef LDAP_OPT_X_TLS
  875   int i;
  876 #endif /* LDAP_OPT_X_TLS */
  877 #ifdef HAVE_LDAP_SET_REBIND_PROC
  878   /* the rebind function that is called when chasing referrals, see
  879      http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/apis/ldap_set_rebind_proc.htm
  880      http://www.openldap.org/software/man.cgi?query=ldap_set_rebind_proc&manpath=OpenLDAP+2.4-Release */
  881   /* TODO: probably only set this if we should chase referrals */
  882   log_log(LOG_DEBUG, "ldap_set_rebind_proc()");
  883 #ifndef LDAP_SET_REBIND_PROC_RETURNS_VOID /* it returns int */
  884   rc = ldap_set_rebind_proc(session->ld, do_rebind, session);
  885   if (rc != LDAP_SUCCESS)
  886   {
  887     myldap_err(LOG_ERR, session->ld, rc, "ldap_set_rebind_proc() failed");
  888     return rc;
  889   }
  890 #else /* ldap_set_rebind_proc() returns void */
  891   ldap_set_rebind_proc(session->ld, do_rebind, session);
  892 #endif
  893 #endif /* HAVE_LDAP_SET_REBIND_PROC */
  894   /* set the protocol version to use */
  895   log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_PROTOCOL_VERSION,%d)",
  896           nslcd_cfg->ldap_version);
  897   LDAP_SET_OPTION(session->ld, LDAP_OPT_PROTOCOL_VERSION,
  898                   &nslcd_cfg->ldap_version);
  899   /* set some other options */
  900   log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_DEREF,%d)",
  901           nslcd_cfg->deref);
  902   LDAP_SET_OPTION(session->ld, LDAP_OPT_DEREF, &nslcd_cfg->deref);
  903   log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_TIMELIMIT,%d)",
  904           nslcd_cfg->timelimit);
  905   LDAP_SET_OPTION(session->ld, LDAP_OPT_TIMELIMIT, &nslcd_cfg->timelimit);
  906   tv.tv_sec = nslcd_cfg->bind_timelimit;
  907   tv.tv_usec = 0;
  908 #ifdef LDAP_OPT_TIMEOUT
  909   log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_TIMEOUT,%d)",
  910           nslcd_cfg->bind_timelimit);
  911   LDAP_SET_OPTION(session->ld, LDAP_OPT_TIMEOUT, &tv);
  912 #endif /* LDAP_OPT_TIMEOUT */
  913 #ifdef LDAP_OPT_NETWORK_TIMEOUT
  914   log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT,%d)",
  915           nslcd_cfg->bind_timelimit);
  916   LDAP_SET_OPTION(session->ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
  917 #endif /* LDAP_OPT_NETWORK_TIMEOUT */
  918 #ifdef LDAP_X_OPT_CONNECT_TIMEOUT
  919   log_log(LOG_DEBUG, "ldap_set_option(LDAP_X_OPT_CONNECT_TIMEOUT,%d)",
  920           nslcd_cfg->bind_timelimit);
  921   LDAP_SET_OPTION(session->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &tv);
  922 #endif /* LDAP_X_OPT_CONNECT_TIMEOUT */
  923   log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_REFERRALS,%s)",
  924           nslcd_cfg->referrals ? "LDAP_OPT_ON" : "LDAP_OPT_OFF");
  925   LDAP_SET_OPTION(session->ld, LDAP_OPT_REFERRALS,
  926                   nslcd_cfg->referrals ? LDAP_OPT_ON : LDAP_OPT_OFF);
  927   log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_RESTART,LDAP_OPT_ON)");
  928   LDAP_SET_OPTION(session->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
  929 #ifdef LDAP_OPT_CONNECT_CB
  930   /* register a connection callback */
  931   cb.lc_add = connect_cb;
  932   cb.lc_del = disconnect_cb;
  933   cb.lc_arg = NULL;
  934   LDAP_SET_OPTION(session->ld, LDAP_OPT_CONNECT_CB, (void *)&cb);
  935 #endif /* LDAP_OPT_CONNECT_CB */
  936 #ifdef LDAP_OPT_X_TLS
  937   /* if SSL is desired, then enable it */
  938   if ((nslcd_cfg->ssl == SSL_LDAPS) ||
  939       (strncasecmp(nslcd_cfg->uris[session->current_uri].uri, "ldaps://", 8) == 0))
  940   {
  941     /* use tls */
  942     i = LDAP_OPT_X_TLS_HARD;
  943     log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_TLS,LDAP_OPT_X_TLS_HARD)");
  944     LDAP_SET_OPTION(session->ld, LDAP_OPT_X_TLS, &i);
  945   }
  946 #endif /* LDAP_OPT_X_TLS */
  947 #ifdef LDAP_OPT_X_SASL_NOCANON
  948   if (nslcd_cfg->sasl_canonicalize >= 0)
  949   {
  950     log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_SASL_NOCANON,%s)",
  951             nslcd_cfg->sasl_canonicalize ? "LDAP_OPT_OFF" : "LDAP_OPT_ON");
  952     LDAP_SET_OPTION(session->ld, LDAP_OPT_X_SASL_NOCANON,
  953                     nslcd_cfg->sasl_canonicalize ? LDAP_OPT_OFF : LDAP_OPT_ON);
  954   }
  955 #endif /* LDAP_OPT_X_SASL_NOCANON */
  956   /* if nothing above failed, everything should be fine */
  957   return LDAP_SUCCESS;
  958 }
  959 
  960 /* close the connection to the server and invalidate any running searches */
  961 static void do_close(MYLDAP_SESSION *session)
  962 {
  963   int i;
  964   int rc;
  965   time_t sec;
  966   /* if we had reachability problems with the server close the connection */
  967   if (session->ld != NULL)
  968   {
  969     /* set timeout options on socket to avoid hang in some cases
  970        (we set a short timeout because we don't care too much about properly
  971        shutting down the connection) */
  972     if (nslcd_cfg->timelimit)
  973     {
  974       sec = nslcd_cfg->timelimit / 2;
  975       if (!sec)
  976         sec = 1;
  977       set_socket_timeout(session->ld, sec, 0);
  978     }
  979     /* go over the other searches and partially close them */
  980     for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
  981     {
  982       if (session->searches[i] != NULL)
  983       {
  984         /* free any messages (because later ld is no longer valid) */
  985         if (session->searches[i]->msg != NULL)
  986         {
  987           ldap_msgfree(session->searches[i]->msg);
  988           session->searches[i]->msg = NULL;
  989         }
  990         /* abandon the search if there were more results to fetch */
  991         if (session->searches[i]->msgid != -1)
  992         {
  993           log_log(LOG_DEBUG, "ldap_abandon()");
  994           if (ldap_abandon(session->searches[i]->session->ld, session->searches[i]->msgid))
  995           {
  996             if (ldap_get_option(session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
  997               rc = LDAP_OTHER;
  998             myldap_err(LOG_WARNING, session->ld, rc,
  999                        "ldap_abandon() failed to abandon search");
 1000           }
 1001           session->searches[i]->msgid = -1;
 1002         }
 1003         /* flag the search as invalid */
 1004         session->searches[i]->valid = 0;
 1005       }
 1006     }
 1007     /* close the connection to the server */
 1008     log_log(LOG_DEBUG, "ldap_unbind()");
 1009     rc = ldap_unbind(session->ld);
 1010     session->ld = NULL;
 1011     if (rc != LDAP_SUCCESS)
 1012       myldap_err(LOG_WARNING, session->ld, rc, "ldap_unbind() failed");
 1013   }
 1014 }
 1015 
 1016 void myldap_session_check(MYLDAP_SESSION *session)
 1017 {
 1018   int i;
 1019   time_t current_time;
 1020   int sd;
 1021   int rc;
 1022   struct sockaddr sa;
 1023   socklen_t salen = sizeof(sa);
 1024   /* check parameters */
 1025   if (session == NULL)
 1026   {
 1027     log_log(LOG_ERR, "myldap_session_check(): invalid parameter passed");
 1028     errno = EINVAL;
 1029     return;
 1030   }
 1031   if (session->ld != NULL)
 1032   {
 1033     rc = ldap_get_option(session->ld, LDAP_OPT_DESC, &sd);
 1034     if (rc != LDAP_SUCCESS)
 1035     {
 1036       myldap_err(LOG_WARNING, session->ld, rc,
 1037                  "ldap_get_option(LDAP_OPT_DESC) failed (ignored)");
 1038     }
 1039     else
 1040     {
 1041       /* check if the connection was closed by the peer */
 1042       if (getpeername(sd, &sa, &salen) == -1)
 1043       {
 1044         if (errno == ENOTCONN)
 1045         {
 1046           log_log(LOG_DEBUG, "myldap_session_check(): connection reset by peer");
 1047           do_close(session);
 1048           return;
 1049         }
 1050       }
 1051     }
 1052     /* check if we should time out the connection */
 1053     if (nslcd_cfg->idle_timelimit > 0)
 1054     {
 1055       /* if we have any running searches, don't time out */
 1056       for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
 1057         if ((session->searches[i] != NULL) && (session->searches[i]->valid))
 1058           return;
 1059       /* consider timeout (there are no running searches) */
 1060       time(&current_time);
 1061       if ((session->lastactivity + nslcd_cfg->idle_timelimit) < current_time)
 1062       {
 1063         log_log(LOG_DEBUG, "myldap_session_check(): idle_timelimit reached");
 1064         do_close(session);
 1065         /* try to use the first URI from the list again */
 1066         session->current_uri = 0;
 1067       }
 1068     }
 1069   }
 1070 }
 1071 
 1072 /* This opens connection to an LDAP server, sets all connection options
 1073    and binds to the server. This returns an LDAP status code. */
 1074 static int do_open(MYLDAP_SESSION *session)
 1075 {
 1076   int rc;
 1077   /* if the connection is still there (ie. ldap_unbind() wasn't
 1078      called) then we can return the cached connection */
 1079   if (session->ld != NULL)
 1080     return LDAP_SUCCESS;
 1081   /* we should build a new session now */
 1082   session->ld = NULL;
 1083   session->lastactivity = 0;
 1084   /* open the connection */
 1085   log_log(LOG_DEBUG, "ldap_initialize(%s)",
 1086           nslcd_cfg->uris[session->current_uri].uri);
 1087   errno = 0;
 1088   rc = ldap_initialize(&(session->ld), nslcd_cfg->uris[session->current_uri].uri);
 1089   if (rc != LDAP_SUCCESS)
 1090   {
 1091     myldap_err(LOG_WARNING, session->ld, rc, "ldap_initialize(%s) failed",
 1092                nslcd_cfg->uris[session->current_uri].uri);
 1093     if (session->ld != NULL)
 1094       do_close(session);
 1095     return rc;
 1096   }
 1097   else if (session->ld == NULL)
 1098   {
 1099     log_log(LOG_WARNING, "ldap_initialize() returned NULL");
 1100     return LDAP_LOCAL_ERROR;
 1101   }
 1102   /* set the options for the connection */
 1103   rc = do_set_options(session);
 1104   if (rc != LDAP_SUCCESS)
 1105   {
 1106     do_close(session);
 1107     return rc;
 1108   }
 1109   /* bind to the server */
 1110   errno = 0;
 1111   rc = do_bind(session, session->ld, nslcd_cfg->uris[session->current_uri].uri);
 1112   if (rc != LDAP_SUCCESS)
 1113   {
 1114     /* log actual LDAP error code */
 1115     myldap_err((session->binddn[0] == '\0') ? LOG_WARNING : LOG_DEBUG,
 1116                session->ld, rc, "failed to bind to LDAP server %s",
 1117                nslcd_cfg->uris[session->current_uri].uri);
 1118     do_close(session);
 1119     return rc;
 1120   }
 1121   /* update last activity and finish off state */
 1122   time(&(session->lastactivity));
 1123   return LDAP_SUCCESS;
 1124 }
 1125 
 1126 /* Perform a simple bind operation and return the ppolicy results. */
 1127 int myldap_bind(MYLDAP_SESSION *session, const char *dn, const char *password,
 1128                 int *response, const char **message)
 1129 {
 1130   MYLDAP_SEARCH *search;
 1131   static const char *attrs[2];
 1132   int rc;
 1133   /* error out when buffers are too small */
 1134   if (strlen(dn) >= sizeof(session->binddn))
 1135   {
 1136     log_log(LOG_ERR, "myldap_bind(): binddn buffer too small (%lu required)",
 1137             (unsigned long) strlen(dn));
 1138     return LDAP_LOCAL_ERROR;
 1139   }
 1140   if (strlen(password) >= sizeof(session->bindpw))
 1141   {
 1142     log_log(LOG_ERR, "myldap_bind(): bindpw buffer too small (%lu required)",
 1143             (unsigned long) strlen(password));
 1144     return LDAP_LOCAL_ERROR;
 1145   }
 1146   /* copy dn and password into session */
 1147   strncpy(session->binddn, dn, sizeof(session->binddn));
 1148   session->binddn[sizeof(session->binddn) - 1] = '\0';
 1149   strncpy(session->bindpw, password, sizeof(session->bindpw));
 1150   session->bindpw[sizeof(session->bindpw) - 1] = '\0';
 1151   /* construct a fake search to trigger the BIND operation */
 1152   attrs[0] = "dn";
 1153   attrs[1] = NULL;
 1154   search = myldap_search(session, session->binddn, MYLDAP_SCOPE_BINDONLY,
 1155                          "(objectClass=*)", attrs, &rc);
 1156   if (search != NULL)
 1157     myldap_search_close(search);
 1158   /* return ppolicy results */
 1159   if (response != NULL)
 1160     *response = session->policy_response;
 1161   if (message != NULL)
 1162     *message = session->policy_message;
 1163   return rc;
 1164 }
 1165 
 1166 /* perform a search operation, the connection is assumed to be open */
 1167 static int do_try_search(MYLDAP_SEARCH *search)
 1168 {
 1169   int ctrlidx = 0;
 1170   int rc;
 1171   LDAPControl *serverctrls[3];
 1172 #ifdef HAVE_LDAP_CREATE_DEREF_CONTROL
 1173   int i;
 1174   struct LDAPDerefSpec ds[2];
 1175   char *deref_attrs[2];
 1176 #endif /* HAVE_LDAP_CREATE_DEREF_CONTROL */
 1177   int msgid;
 1178   /* if we're using paging, build a page control */
 1179   if ((nslcd_cfg->pagesize > 0) && (search->scope != LDAP_SCOPE_BASE))
 1180   {
 1181     rc = ldap_create_page_control(search->session->ld, nslcd_cfg->pagesize,
 1182                                   search->cookie, 0, &serverctrls[ctrlidx]);
 1183     if (rc == LDAP_SUCCESS)
 1184       ctrlidx++;
 1185     else
 1186     {
 1187       myldap_err(LOG_WARNING, search->session->ld, rc,
 1188                  "ldap_create_page_control() failed");
 1189       serverctrls[ctrlidx] = NULL;
 1190       /* if we were paging, failure building the second control is fatal */
 1191       if (search->cookie != NULL)
 1192         return rc;
 1193     }
 1194   }
 1195 #ifdef HAVE_LDAP_CREATE_DEREF_CONTROL
 1196   /* if doing group searches, add deref control to search request
 1197      (this is currently a bit of a hack and hard-coded for group searches
 1198      which are detected by requesting the attmap_group_member member
 1199      attribute) */
 1200   for (i = 0; search->attrs[i] != NULL; i++)
 1201     if (strcasecmp(search->attrs[i], attmap_group_member) == 0)
 1202     {
 1203       /* attributes from dereff'd entries */
 1204       deref_attrs[0] = (void *)attmap_passwd_uid;
 1205       deref_attrs[1] = NULL;
 1206       /* build deref control */
 1207       ds[0].derefAttr = (void *)attmap_group_member;
 1208       ds[0].attributes = deref_attrs;
 1209       ds[1].derefAttr = NULL;
 1210       ds[1].attributes = NULL;
 1211       rc = ldap_create_deref_control(search->session->ld, ds, 0, &serverctrls[ctrlidx]);
 1212       if (rc == LDAP_SUCCESS)
 1213         ctrlidx++;
 1214       else
 1215       {
 1216         myldap_err(LOG_WARNING, search->session->ld, rc,
 1217                    "ldap_create_deref_control() failed");
 1218         serverctrls[ctrlidx] = NULL;
 1219       }
 1220     }
 1221 #endif /* HAVE_LDAP_CREATE_DEREF_CONTROL */
 1222   /* NULL terminate control list */
 1223   serverctrls[ctrlidx] = NULL;
 1224   /* clear error flag (perhaps control setting failed) */
 1225   if (ctrlidx > 0)
 1226   {
 1227     rc = LDAP_SUCCESS;
 1228     if (ldap_set_option(search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
 1229       log_log(LOG_WARNING, "failed to clear the error flag");
 1230   }
 1231   /* perform the search */
 1232   rc = ldap_search_ext(search->session->ld, search->base, search->scope,
 1233                        search->filter, (char **)(search->attrs),
 1234                        0, serverctrls[0] == NULL ? NULL : serverctrls,
 1235                        NULL, NULL, LDAP_NO_LIMIT, &msgid);
 1236   /* free the controls if we had them */
 1237   for (ctrlidx = 0; serverctrls[ctrlidx] != NULL; ctrlidx++)
 1238     ldap_control_free(serverctrls[ctrlidx]);
 1239   /* handle errors */
 1240   if (rc != LDAP_SUCCESS)
 1241   {
 1242     myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_search_ext() failed");
 1243     return rc;
 1244   }
 1245   /* update the last activity on the connection */
 1246   time(&(search->session->lastactivity));
 1247   /* save msgid */
 1248   search->msgid = msgid;
 1249   /* return the new search */
 1250   return LDAP_SUCCESS;
 1251 }
 1252 
 1253 MYLDAP_SESSION *myldap_create_session(void)
 1254 {
 1255   return myldap_session_new();
 1256 }
 1257 
 1258 void myldap_session_cleanup(MYLDAP_SESSION *session)
 1259 {
 1260   int i;
 1261   /* check parameter */
 1262   if (session == NULL)
 1263   {
 1264     log_log(LOG_ERR, "myldap_session_cleanup(): invalid session passed");
 1265     return;
 1266   }
 1267   /* go over all searches in the session and close them */
 1268   for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
 1269   {
 1270     if (session->searches[i] != NULL)
 1271     {
 1272       myldap_search_close(session->searches[i]);
 1273       session->searches[i] = NULL;
 1274     }
 1275   }
 1276 }
 1277 
 1278 void myldap_session_close(MYLDAP_SESSION *session)
 1279 {
 1280   /* check parameter */
 1281   if (session == NULL)
 1282   {
 1283     log_log(LOG_ERR, "myldap_session_cleanup(): invalid session passed");
 1284     return;
 1285   }
 1286   /* close pending searches */
 1287   myldap_session_cleanup(session);
 1288   /* close any open connections */
 1289   do_close(session);
 1290   /* free allocated memory */
 1291   memset(session->bindpw, 0, sizeof(session->bindpw));
 1292   free(session);
 1293 }
 1294 
 1295 /* mutex for updating the times in the uri */
 1296 pthread_mutex_t uris_mutex = PTHREAD_MUTEX_INITIALIZER;
 1297 
 1298 static int do_retry_search(MYLDAP_SEARCH *search)
 1299 {
 1300   int sleeptime = 0;
 1301   int start_uri;
 1302   time_t endtime;
 1303   time_t nexttry;
 1304   time_t t;
 1305   int rc = LDAP_UNAVAILABLE;
 1306   struct myldap_uri *current_uri;
 1307   int dotry[NSS_LDAP_CONFIG_MAX_URIS];
 1308   int do_invalidate = 0;
 1309   /* clear time stamps */
 1310   for (start_uri = 0; start_uri < NSS_LDAP_CONFIG_MAX_URIS; start_uri++)
 1311     dotry[start_uri] = 1;
 1312   /* keep trying until we time out */
 1313   endtime = time(NULL) + nslcd_cfg->reconnect_retrytime;
 1314   while (1)
 1315   {
 1316     nexttry = endtime;
 1317     /* try each configured URL once */
 1318     pthread_mutex_lock(&uris_mutex);
 1319     start_uri = search->session->current_uri;
 1320     do
 1321     {
 1322       current_uri = &(nslcd_cfg->uris[search->session->current_uri]);
 1323       /* only try this URI if we should */
 1324       if (!dotry[search->session->current_uri])
 1325       { /* skip this URI */ }
 1326       else if ((current_uri->lastfail > (current_uri->firstfail + nslcd_cfg->reconnect_retrytime)) &&
 1327                ((t = time(NULL)) < (current_uri->lastfail + nslcd_cfg->reconnect_retrytime)))
 1328       {
 1329         /* we are in a hard fail state and have retried not long ago */
 1330         log_log(LOG_DEBUG, "not retrying server %s which failed just %d second(s) ago and has been failing for %d seconds",
 1331                 current_uri->uri, (int)(t - current_uri->lastfail),
 1332                 (int)(t - current_uri->firstfail));
 1333         dotry[search->session->current_uri] = 0;
 1334       }
 1335       else
 1336       {
 1337         /* try to start the search */
 1338         pthread_mutex_unlock(&uris_mutex);
 1339         /* ensure that we have an open connection and start a search */
 1340         rc = do_open(search->session);
 1341         /* perform the actual search, unless we were only binding */
 1342         if ((rc == LDAP_SUCCESS) && (search->scope != MYLDAP_SCOPE_BINDONLY))
 1343           rc = do_try_search(search);
 1344         /* if we are authenticating a user and get an error regarding failed
 1345            password we should error out instead of trying all servers */
 1346         if ((search->session->binddn[0] != '\0') && (rc == LDAP_INVALID_CREDENTIALS))
 1347         {
 1348           do_close(search->session);
 1349           return rc;
 1350         }
 1351         if (rc == LDAP_SUCCESS)
 1352         {
 1353           pthread_mutex_lock(&uris_mutex);
 1354           /* check if we are coming back from an error */
 1355           if ((current_uri->lastfail > 0) || (search->session->current_uri != start_uri))
 1356           {
 1357             log_log(LOG_INFO, "connected to LDAP server %s", current_uri->uri);
 1358             do_invalidate = 1;
 1359           }
 1360           if (first_search)
 1361           {
 1362             do_invalidate = 1;
 1363             first_search = 0;
 1364           }
 1365           /* update ok time */
 1366           current_uri->firstfail = 0;
 1367           current_uri->lastfail = 0;
 1368           pthread_mutex_unlock(&uris_mutex);
 1369           /* flag the search as valid */
 1370           search->valid = 1;
 1371           /* signal external invalidation of configured caches */
 1372           if (do_invalidate)
 1373             invalidator_do(LM_NONE);
 1374           return LDAP_SUCCESS;
 1375         }
 1376         /* close the current connection */
 1377         do_close(search->session);
 1378         /* update time of failure and figure out when we should retry */
 1379         pthread_mutex_lock(&uris_mutex);
 1380         t = time(NULL);
 1381         /* update timestamps unless we are doing an authentication search */
 1382         if (search->session->binddn[0] == '\0')
 1383         {
 1384           if (current_uri->firstfail == 0)
 1385             current_uri->firstfail = t;
 1386           current_uri->lastfail = t;
 1387         }
 1388         /* if it is one of these, retrying this URI is not going to help */
 1389         if ((rc == LDAP_INVALID_CREDENTIALS) || (rc == LDAP_INSUFFICIENT_ACCESS) ||
 1390             (rc == LDAP_AUTH_METHOD_NOT_SUPPORTED))
 1391           dotry[search->session->current_uri] = 0;
 1392         /* check when we should try this URI again */
 1393         else if (t <= (current_uri->firstfail + nslcd_cfg->reconnect_retrytime))
 1394         {
 1395           t += nslcd_cfg->reconnect_sleeptime;
 1396           if (t < nexttry)
 1397             nexttry = t;
 1398         }
 1399       }
 1400       /* try the next URI (with wrap-around) */
 1401       search->session->current_uri++;
 1402       if (nslcd_cfg->uris[search->session->current_uri].uri == NULL)
 1403         search->session->current_uri = 0;
 1404     }
 1405     while (search->session->current_uri != start_uri);
 1406     pthread_mutex_unlock(&uris_mutex);
 1407     /* see if it is any use sleeping */
 1408     if (nexttry >= endtime)
 1409     {
 1410       if (search->session->binddn[0] == '\0')
 1411         myldap_err(LOG_ERR, search->session->ld, rc, "no available LDAP server found");
 1412       return rc;
 1413     }
 1414     /* sleep between tries */
 1415     sleeptime = nexttry - time(NULL);
 1416     if (sleeptime > 0)
 1417     {
 1418       log_log(LOG_WARNING, "no available LDAP server found, sleeping %d seconds",
 1419               sleeptime);
 1420       (void)sleep(sleeptime);
 1421     }
 1422   }
 1423 }
 1424 
 1425 /* force quick retries of all failing LDAP servers */
 1426 void myldap_immediate_reconnect(void)
 1427 {
 1428   int i;
 1429   time_t t;
 1430   t = time(NULL) - nslcd_cfg->reconnect_retrytime;
 1431   pthread_mutex_lock(&uris_mutex);
 1432   for (i = 0; i < (NSS_LDAP_CONFIG_MAX_URIS + 1); i++)
 1433   {
 1434     /* only adjust failing connections that are in a hard fail state */
 1435     if ((nslcd_cfg->uris[i].lastfail > t) &&
 1436         (nslcd_cfg->uris[i].lastfail > (nslcd_cfg->uris[i].firstfail + nslcd_cfg->reconnect_retrytime)))
 1437     {
 1438       /* move lastfail back to ensure quick retry */
 1439       log_log(LOG_DEBUG, "moving lastfail of %s %d second(s) back to force retry",
 1440               nslcd_cfg->uris[i].uri, (int)(nslcd_cfg->uris[i].lastfail - t));
 1441       nslcd_cfg->uris[i].lastfail = t;
 1442     }
 1443   }
 1444   pthread_mutex_unlock(&uris_mutex);
 1445 }
 1446 
 1447 MYLDAP_SEARCH *myldap_search(MYLDAP_SESSION *session,
 1448                              const char *base, int scope, const char *filter,
 1449                              const char **attrs, int *rcp)
 1450 {
 1451   MYLDAP_SEARCH *search;
 1452   int i;
 1453   int rc;
 1454   /* check parameters */
 1455   if ((session == NULL) || (base == NULL) || (filter == NULL) || (attrs == NULL))
 1456   {
 1457     log_log(LOG_ERR, "myldap_search(): invalid parameter passed");
 1458     errno = EINVAL;
 1459     if (rcp != NULL)
 1460       *rcp = LDAP_OPERATIONS_ERROR;
 1461     return NULL;
 1462   }
 1463   /* log the call */
 1464   log_log(LOG_DEBUG, "myldap_search(base=\"%s\", filter=\"%s\")",
 1465           base, filter);
 1466   /* check if the idle time for the connection has expired */
 1467   myldap_session_check(session);
 1468   /* allocate a new search entry */
 1469   search = myldap_search_new(session, base, scope, filter, attrs);
 1470   /* find a place in the session where we can register our search */
 1471   for (i = 0; (i < MAX_SEARCHES_IN_SESSION) && (session->searches[i] != NULL); i++)
 1472     /* nothing */ ;
 1473   if (i >= MAX_SEARCHES_IN_SESSION)
 1474   {
 1475     log_log(LOG_ERR, "myldap_search(): too many searches registered with session (max %d)",
 1476             MAX_SEARCHES_IN_SESSION);
 1477     myldap_search_close(search);
 1478     if (rcp != NULL)
 1479       *rcp = LDAP_OPERATIONS_ERROR;
 1480     return NULL;
 1481   }
 1482   /* register search with the session so we can free it later on */
 1483   session->searches[i] = search;
 1484   /* do the search with retries to all configured servers */
 1485   rc = do_retry_search(search);
 1486   if (rc != LDAP_SUCCESS)
 1487   {
 1488     myldap_search_close(search);
 1489     if (rcp != NULL)
 1490       *rcp = rc;
 1491     return NULL;
 1492   }
 1493   if (rcp != NULL)
 1494     *rcp = LDAP_SUCCESS;
 1495   return search;
 1496 }
 1497 
 1498 void myldap_search_close(MYLDAP_SEARCH *search)
 1499 {
 1500   int i;
 1501   if (search == NULL)
 1502     return;
 1503   /* free any messages */
 1504   if (search->msg != NULL)
 1505   {
 1506     ldap_msgfree(search->msg);
 1507     search->msg = NULL;
 1508   }
 1509   /* abandon the search if there were more results to fetch */
 1510   if ((search->session->ld != NULL) && (search->msgid != -1))
 1511   {
 1512     ldap_abandon(search->session->ld, search->msgid);
 1513     search->msgid = -1;
 1514   }
 1515   /* find the reference to this search in the session */
 1516   for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
 1517   {
 1518     if (search->session->searches[i] == search)
 1519       search->session->searches[i] = NULL;
 1520   }
 1521   /* free any search entries */
 1522   if (search->entry != NULL)
 1523     myldap_entry_free(search->entry);
 1524   /* clean up cookie */
 1525   if (search->cookie != NULL)
 1526     ber_bvfree(search->cookie);
 1527   /* free read messages */
 1528   if (search->msg != NULL)
 1529     ldap_msgfree(search->msg);
 1530   /* free the storage we allocated */
 1531   free(search);
 1532 }
 1533 
 1534 MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search, int *rcp)
 1535 {
 1536   int rc;
 1537   int parserc;
 1538   struct timeval tv, *tvp;
 1539   LDAPControl **resultcontrols;
 1540   ber_int_t count;
 1541   /* check parameters */
 1542   if ((search == NULL) || (search->session == NULL) || (search->session->ld == NULL))
 1543   {
 1544     log_log(LOG_ERR, "myldap_get_entry(): invalid search passed");
 1545     errno = EINVAL;
 1546     if (rcp != NULL)
 1547       *rcp = LDAP_OPERATIONS_ERROR;
 1548     return NULL;
 1549   }
 1550   /* check if the connection wasn't closed in another search */
 1551   if (!search->valid)
 1552   {
 1553     log_log(LOG_WARNING, "myldap_get_entry(): connection was closed");
 1554     /* retry the search */
 1555     if (search->may_retry_search)
 1556     {
 1557       log_log(LOG_DEBUG, "myldap_get_entry(): retry search");
 1558       search->may_retry_search = 0;
 1559       if (do_retry_search(search) == LDAP_SUCCESS)
 1560         return myldap_get_entry(search, rcp);
 1561     }
 1562     myldap_search_close(search);
 1563     if (rcp != NULL)
 1564       *rcp = LDAP_SERVER_DOWN;
 1565     return NULL;
 1566   }
 1567   /* set up a timelimit value for operations */
 1568   if (nslcd_cfg->timelimit == LDAP_NO_LIMIT)
 1569     tvp = NULL;
 1570   else
 1571   {
 1572     tv.tv_sec = nslcd_cfg->timelimit;
 1573     tv.tv_usec = 0;
 1574     tvp = &tv;
 1575   }
 1576   /* if we have an existing result entry, free it */
 1577   if (search->entry != NULL)
 1578   {
 1579     myldap_entry_free(search->entry);
 1580     search->entry = NULL;
 1581   }
 1582   /* try to parse results until we have a final error or ok */
 1583   while (1)
 1584   {
 1585     /* free the previous message if there was any */
 1586     if (search->msg != NULL)
 1587     {
 1588       ldap_msgfree(search->msg);
 1589       search->msg = NULL;
 1590     }
 1591     /* get the next result */
 1592     rc = ldap_result(search->session->ld, search->msgid, LDAP_MSG_ONE, tvp,
 1593                      &(search->msg));
 1594     /* handle result */
 1595     switch (rc)
 1596     {
 1597       case LDAP_RES_SEARCH_ENTRY:
 1598         /* we have a normal search entry, update timestamp and return result */
 1599         time(&(search->session->lastactivity));
 1600         search->entry = myldap_entry_new(search);
 1601         if (rcp != NULL)
 1602           *rcp = LDAP_SUCCESS;
 1603         /* log the first couple of dns in the result (but not all, to
 1604            prevent swamping the log) */
 1605         if (search->count < MAX_DEBUG_LOG_DNS)
 1606           log_log(LOG_DEBUG, "ldap_result(): %s", myldap_get_dn(search->entry));
 1607         search->count++;
 1608         search->may_retry_search = 0;
 1609         return search->entry;
 1610       case LDAP_RES_SEARCH_RESULT:
 1611         /* we have a search result, parse it */
 1612         resultcontrols = NULL;
 1613         if (search->cookie != NULL)
 1614         {
 1615           ber_bvfree(search->cookie);
 1616           search->cookie = NULL;
 1617         }
 1618         /* NB: this frees search->msg */
 1619         parserc = ldap_parse_result(search->session->ld, search->msg, &rc,
 1620                                     NULL, NULL, NULL, &resultcontrols, 1);
 1621         search->msg = NULL;
 1622         /* check for errors during parsing */
 1623         if ((parserc != LDAP_SUCCESS) && (parserc != LDAP_MORE_RESULTS_TO_RETURN))
 1624         {
 1625           if (resultcontrols != NULL)
 1626             ldap_controls_free(resultcontrols);
 1627           myldap_err(LOG_ERR, search->session->ld, parserc, "ldap_parse_result() failed");
 1628           myldap_search_close(search);
 1629           if (rcp != NULL)
 1630             *rcp = parserc;
 1631           return NULL;
 1632         }
 1633         /* check for errors in message */
 1634         if ((rc != LDAP_SUCCESS) && (rc != LDAP_MORE_RESULTS_TO_RETURN))
 1635         {
 1636           if (resultcontrols != NULL)
 1637             ldap_controls_free(resultcontrols);
 1638           myldap_err(LOG_ERR, search->session->ld, rc, "ldap_result() failed");
 1639           /* close connection on connection problems */
 1640           if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
 1641             do_close(search->session);
 1642           myldap_search_close(search);
 1643           if (rcp != NULL)
 1644             *rcp = rc;
 1645           return NULL;
 1646         }
 1647         /* handle result controls */
 1648         if (resultcontrols != NULL)
 1649         {
 1650           /* see if there are any more pages to come */
 1651           rc = ldap_parse_page_control(search->session->ld, resultcontrols,
 1652                                        &count, &(search->cookie));
 1653           if (rc != LDAP_SUCCESS)
 1654           {
 1655             if (rc != LDAP_CONTROL_NOT_FOUND)
 1656               myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_parse_page_control() failed");
 1657             /* clear error flag */
 1658             rc = LDAP_SUCCESS;
 1659             if (ldap_set_option(search->session->ld, LDAP_OPT_ERROR_NUMBER,
 1660                                 &rc) != LDAP_SUCCESS)
 1661               log_log(LOG_WARNING, "failed to clear the error flag");
 1662           }
 1663           /* TODO: handle the above return code?? */
 1664           ldap_controls_free(resultcontrols);
 1665         }
 1666         search->msgid = -1;
 1667         /* check if there are more pages to come */
 1668         if ((search->cookie == NULL) || (search->cookie->bv_len == 0))
 1669         {
 1670           if (search->count > MAX_DEBUG_LOG_DNS)
 1671             log_log(LOG_DEBUG, "ldap_result(): ... %d more results",
 1672                     search->count - MAX_DEBUG_LOG_DNS);
 1673           log_log(LOG_DEBUG, "ldap_result(): end of results (%d total)",
 1674                   search->count);
 1675           /* we are at the end of the search, no more results */
 1676           myldap_search_close(search);
 1677           if (rcp != NULL)
 1678             *rcp = LDAP_SUCCESS;
 1679           return NULL;
 1680         }
 1681         /* try the next page */
 1682         rc = do_try_search(search);
 1683         if (rc != LDAP_SUCCESS)
 1684         {
 1685           /* close connection on connection problems */
 1686           if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
 1687             do_close(search->session);
 1688           myldap_search_close(search);
 1689           if (rcp != NULL)
 1690             *rcp = rc;
 1691           return NULL;
 1692         }
 1693         /* we continue with another pass */
 1694         break;
 1695       case LDAP_RES_SEARCH_REFERENCE:
 1696         break; /* just ignore search references */
 1697       default:
 1698         /* we have some error condition, find out which */
 1699         switch (rc)
 1700         {
 1701           case -1:
 1702             /* try to get error code */
 1703             if (ldap_get_option(search->session->ld, LDAP_OPT_ERROR_NUMBER,
 1704                                 &rc) != LDAP_SUCCESS)
 1705               rc = LDAP_UNAVAILABLE;
 1706             myldap_err(LOG_ERR, search->session->ld, rc, "ldap_result() failed");
 1707             break;
 1708           case 0:
 1709             /* the timeout expired */
 1710             log_log(LOG_ERR, "ldap_result() timed out");
 1711             rc = LDAP_TIMELIMIT_EXCEEDED;
 1712             break;
 1713           default:
 1714             /* unknown code */
 1715             log_log(LOG_WARNING, "ldap_result() returned unexpected result type");
 1716             rc = LDAP_PROTOCOL_ERROR;
 1717         }
 1718         /* close connection on some connection problems */
 1719         if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN) ||
 1720             (rc == LDAP_SUCCESS) || (rc == LDAP_TIMELIMIT_EXCEEDED) ||
 1721             (rc == LDAP_OPERATIONS_ERROR) || (rc == LDAP_PROTOCOL_ERROR) ||
 1722             (rc == LDAP_BUSY) || (rc == LDAP_UNWILLING_TO_PERFORM) ||
 1723             (rc == LDAP_TIMEOUT) || (rc == LDAP_CONNECT_ERROR) ||
 1724             (rc == LDAP_NOT_SUPPORTED))
 1725         {
 1726           do_close(search->session);
 1727           /* retry once if no data has been received yet */
 1728           if (search->may_retry_search)
 1729           {
 1730             log_log(LOG_DEBUG, "myldap_get_entry(): retry search");
 1731             search->may_retry_search = 0;
 1732             if (do_retry_search(search) == LDAP_SUCCESS)
 1733               return myldap_get_entry(search, rcp);
 1734           }
 1735         }
 1736         /* close search */
 1737         myldap_search_close(search);
 1738         if (rcp != NULL)
 1739           *rcp = rc;
 1740         return NULL;
 1741     }
 1742   }
 1743 }
 1744 
 1745 /* Get the DN from the entry. This function only returns NULL (and sets
 1746    errno) if an incorrect entry is passed. If the DN value cannot be
 1747    retrieved "unknown" is returned instead. */
 1748 const char *myldap_get_dn(MYLDAP_ENTRY *entry)
 1749 {
 1750   int rc;
 1751   /* check parameters */
 1752   if (!is_valid_entry(entry))
 1753   {
 1754     log_log(LOG_ERR, "myldap_get_dn(): invalid result entry passed");
 1755     errno = EINVAL;
 1756     return "unknown";
 1757   }
 1758   /* if we don't have it yet, retrieve it */
 1759   if ((entry->dn == NULL) && (entry->search->valid))
 1760   {
 1761     entry->dn = ldap_get_dn(entry->search->session->ld, entry->search->msg);
 1762     if (entry->dn == NULL)
 1763     {
 1764       if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER,
 1765                           &rc) != LDAP_SUCCESS)
 1766         rc = LDAP_UNAVAILABLE;
 1767       myldap_err(LOG_WARNING, entry->search->session->ld, rc, "ldap_get_dn() returned NULL");
 1768       /* close connection on connection problems */
 1769       if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
 1770         do_close(entry->search->session);
 1771     }
 1772   }
 1773   /* if we still don't have it, return unknown */
 1774   if (entry->dn == NULL)
 1775     return "unknown";
 1776   /* return it */
 1777   return entry->dn;
 1778 }
 1779 
 1780 char *myldap_cpy_dn(MYLDAP_ENTRY *entry, char *buf, size_t buflen)
 1781 {
 1782   const char *dn;
 1783   /* get the dn */
 1784   dn = myldap_get_dn(entry);
 1785   /* copy into buffer */
 1786   if (strlen(dn) < buflen)
 1787     strcpy(buf, dn);
 1788   else
 1789     buf = NULL;
 1790   return buf;
 1791 }
 1792 
 1793 /* Perform ranged retrieval of attributes.
 1794    http://msdn.microsoft.com/en-us/library/aa367017(vs.85).aspx
 1795    http://www.tkk.fi/cc/docs/kerberos/draft-kashi-incremental-00.txt */
 1796 static char **myldap_get_ranged_values(MYLDAP_ENTRY *entry, const char *attr)
 1797 {
 1798   char **values;
 1799   char *attn;
 1800   const char *attrs[2];
 1801   BerElement *ber;
 1802   int i;
 1803   int startat = 0, nxt = 0;
 1804   char attbuf[80];
 1805   const char *dn = myldap_get_dn(entry);
 1806   MYLDAP_SESSION *session = entry->search->session;
 1807   MYLDAP_SEARCH *search = NULL;
 1808   SET *set = NULL;
 1809   /* build the attribute name to find */
 1810   if (mysnprintf(attbuf, sizeof(attbuf), "%s;range=0-*", attr))
 1811   {
 1812     log_log(LOG_ERR, "myldap_get_ranged_values(): attbuf buffer too small (%lu required)",
 1813             (unsigned long) strlen(attr) + 10);
 1814     return NULL;
 1815   }
 1816   /* keep doing lookups untul we can't get any more results */
 1817   while (1)
 1818   {
 1819     /* go over all attributes to find the ranged attribute */
 1820     ber = NULL;
 1821     attn = ldap_first_attribute(entry->search->session->ld, entry->search->msg, &ber);
 1822     values = NULL;
 1823     while (attn != NULL)
 1824     {
 1825       if (strncasecmp(attn, attbuf, strlen(attbuf) - 1) == 0)
 1826       {
 1827         log_log(LOG_DEBUG, "found ranged results %s", attn);
 1828         nxt = atoi(attn + strlen(attbuf) - 1) + 1;
 1829         values = ldap_get_values(entry->search->session->ld, entry->search->msg, attn);
 1830         ldap_memfree(attn);
 1831         break;
 1832       }
 1833       /* free old attribute name and get next one */
 1834       ldap_memfree(attn);
 1835       attn = ldap_next_attribute(entry->search->session->ld, entry->search->msg, ber);
 1836     }
 1837     ber_free(ber, 0);
 1838     /* see if we found any values */
 1839     if ((values == NULL) || (*values == NULL))
 1840       break;
 1841     /* allocate memory */
 1842     if (set == NULL)
 1843     {
 1844       set = set_new();
 1845       if (set == NULL)
 1846       {
 1847         ldap_value_free(values);
 1848         log_log(LOG_CRIT, "myldap_get_ranged_values(): set_new() failed to allocate memory");
 1849         return NULL;
 1850       }
 1851     }
 1852     /* add to the set */
 1853     for (i = 0; values[i] != NULL; i++)
 1854       set_add(set, values[i]);
 1855     /* free results */
 1856     ldap_value_free(values);
 1857     /* check if we should start a new search */
 1858     if (nxt <= startat)
 1859       break;
 1860     startat = nxt;
 1861     /* build attributes for a new search */
 1862     if (mysnprintf(attbuf, sizeof(attbuf), "%s;range=%d-*", attr, startat))
 1863     {
 1864       log_log(LOG_ERR, "myldap_get_ranged_values(): attbuf buffer too small");
 1865       break;
 1866     }
 1867     attrs[0] = attbuf;
 1868     attrs[1] = NULL;
 1869     /* close the previous search, if any */
 1870     if (search != NULL)
 1871       myldap_search_close(search);
 1872     /* start the new search */
 1873     search = myldap_search(session, dn, LDAP_SCOPE_BASE, "(objectClass=*)", attrs, NULL);
 1874     if (search == NULL)
 1875       break;
 1876     entry = myldap_get_entry(search, NULL);
 1877     if (entry == NULL)
 1878       break;
 1879   }
 1880   /* close any started searches */
 1881   if (search != NULL)
 1882     myldap_search_close(search);
 1883   /* return the contents of the set as a list */
 1884   if (set == NULL)
 1885     return NULL;
 1886   values = (char **)set_tolist(set);
 1887   set_free(set);
 1888   if (values == NULL)
 1889     log_log(LOG_CRIT, "myldap_get_ranged_values(): malloc() failed to allocate memory");
 1890   return values;
 1891 }
 1892 
 1893 /* Simple wrapper around ldap_get_values(). */
 1894 const char **myldap_get_values(MYLDAP_ENTRY *entry, const char *attr)
 1895 {
 1896   char **values;
 1897   int rc;
 1898   int i;
 1899   /* check parameters */
 1900   if (!is_valid_entry(entry))
 1901   {
 1902     log_log(LOG_ERR, "myldap_get_values(): invalid result entry passed");
 1903     errno = EINVAL;
 1904     return NULL;
 1905   }
 1906   else if (attr == NULL)
 1907   {
 1908     log_log(LOG_ERR, "myldap_get_values(): invalid attribute name passed");
 1909     errno = EINVAL;
 1910     return NULL;
 1911   }
 1912   if (!entry->search->valid)
 1913     return NULL; /* search has been stopped */
 1914   /* get from LDAP */
 1915   values = ldap_get_values(entry->search->session->ld, entry->search->msg, attr);
 1916   if (values == NULL)
 1917   {
 1918     if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
 1919       rc = LDAP_UNAVAILABLE;
 1920     /* ignore decoding errors as they are just non-existing attribute values */
 1921     if (rc == LDAP_DECODING_ERROR)
 1922     {
 1923       rc = LDAP_SUCCESS;
 1924       if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
 1925         log_log(LOG_WARNING, "failed to clear the error flag");
 1926     }
 1927     else if (rc == LDAP_SUCCESS)
 1928     {
 1929       /* we have a success code but no values, let's try to get ranged
 1930          values */
 1931       values = myldap_get_ranged_values(entry, attr);
 1932       if (values == NULL)
 1933         return NULL;
 1934       /* store values entry so we can free it later on */
 1935       for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
 1936         if (entry->buffers[i] == NULL)
 1937         {
 1938           entry->buffers[i] = values;
 1939           return (const char **)entry->buffers[i];
 1940         }
 1941       /* we found no room to store the values */
 1942       log_log(LOG_ERR, "ldap_get_values() couldn't store results, increase MAX_BUFFERS_PER_ENTRY");
 1943       free(values);
 1944       return NULL;
 1945     }
 1946     else
 1947       myldap_err(LOG_WARNING, entry->search->session->ld, rc,
 1948                  "ldap_get_values() of attribute \"%s\" on entry \"%s\" returned NULL",
 1949                  attr, myldap_get_dn(entry));
 1950     return NULL;
 1951   }
 1952   /* store values entry so we can free it later on */
 1953   for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
 1954     if (entry->attributevalues[i] == NULL)
 1955     {
 1956       entry->attributevalues[i] = values;
 1957       return (const char **)values;
 1958     }
 1959   /* we found no room to store the entry */
 1960   log_log(LOG_ERR, "ldap_get_values() couldn't store results, increase MAX_ATTRIBUTES_PER_ENTRY");
 1961   ldap_value_free(values);
 1962   return NULL;
 1963 }
 1964 
 1965 /* Convert the bervalues to a simple list of strings that can be freed
 1966    with one call to free(). */
 1967 static const char **bervalues_to_values(struct berval **bvalues)
 1968 {
 1969   int num_values;
 1970   int i;
 1971   size_t sz;
 1972   char *buf;
 1973   char **values;
 1974   /* figure out how much memory to allocate */
 1975   num_values = ldap_count_values_len(bvalues);
 1976   sz = (num_values + 1) * sizeof(char *);
 1977   for (i = 0; i < num_values; i++)
 1978     sz += bvalues[i]->bv_len + 1;
 1979   /* allocate the needed memory */
 1980   values = (char **)malloc(sz);
 1981   if (values == NULL)
 1982   {
 1983     log_log(LOG_CRIT, "bervalues_to_values(): malloc() failed to allocate memory");
 1984     return NULL;
 1985   }
 1986   buf = (char *)values;
 1987   buf += (num_values + 1) * sizeof(char *);
 1988   /* copy from bvalues */
 1989   for (i = 0; i < num_values; i++)
 1990   {
 1991     values[i] = buf;
 1992     memcpy(values[i], bvalues[i]->bv_val, bvalues[i]->bv_len);
 1993     values[i][bvalues[i]->bv_len] = '\0';
 1994     buf += bvalues[i]->bv_len + 1;
 1995   }
 1996   values[i] = NULL;
 1997   return (const char **)values;
 1998 }
 1999 
 2000 /* Simple wrapper around ldap_get_values(). */
 2001 const char **myldap_get_values_len(MYLDAP_ENTRY *entry, const char *attr)
 2002 {
 2003   const char **values;
 2004   struct berval **bvalues;
 2005   int rc;
 2006   int i;
 2007   /* check parameters */
 2008   if (!is_valid_entry(entry))
 2009   {
 2010     log_log(LOG_ERR, "myldap_get_values_len(): invalid result entry passed");
 2011     errno = EINVAL;
 2012     return NULL;
 2013   }
 2014   else if (attr == NULL)
 2015   {
 2016     log_log(LOG_ERR, "myldap_get_values_len(): invalid attribute name passed");
 2017     errno = EINVAL;
 2018     return NULL;
 2019   }
 2020   if (!entry->search->valid)
 2021     return NULL; /* search has been stopped */
 2022   /* get from LDAP */
 2023   bvalues = ldap_get_values_len(entry->search->session->ld, entry->search->msg, attr);
 2024   if (bvalues == NULL)
 2025   {
 2026     if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
 2027       rc = LDAP_UNAVAILABLE;
 2028     /* ignore decoding errors as they are just non-existing attribute values */
 2029     if (rc == LDAP_DECODING_ERROR)
 2030     {
 2031       rc = LDAP_SUCCESS;
 2032       if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
 2033         log_log(LOG_WARNING, "failed to clear the error flag");
 2034       return NULL;
 2035     }
 2036     else if (rc == LDAP_SUCCESS)
 2037     {
 2038       /* we have a success code but no values, let's try to get ranged
 2039          values */
 2040       values = (const char **)myldap_get_ranged_values(entry, attr);
 2041     }
 2042     else
 2043     {
 2044       myldap_err(LOG_WARNING, entry->search->session->ld, rc,
 2045                  "myldap_get_values_len() of attribute \"%s\" on entry \"%s\" returned NULL",
 2046                  attr, myldap_get_dn(entry));
 2047       return NULL;
 2048     }
 2049   }
 2050   else
 2051   {
 2052     values = bervalues_to_values(bvalues);
 2053     ldap_value_free_len(bvalues);
 2054   }
 2055   /* check if we got allocated memory */
 2056   if (values == NULL)
 2057     return NULL;
 2058   /* store values entry so we can free it later on */
 2059   for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
 2060     if (entry->buffers[i] == NULL)
 2061     {
 2062       entry->buffers[i] = (char **)values;
 2063       return values;
 2064     }
 2065   /* we found no room to store the values */
 2066   log_log(LOG_ERR, "myldap_get_values_len() couldn't store results, increase MAX_BUFFERS_PER_ENTRY");
 2067   free(values);
 2068   return NULL;
 2069 }
 2070 
 2071 /* Go over the entries in exploded_rdn and see if any start with
 2072    the requested attribute. Return a reference to the value part of
 2073    the DN (does not modify exploded_rdn). */
 2074 static const char *find_rdn_value(char **exploded_rdn, const char *attr)
 2075 {
 2076   int i, j;
 2077   int l;
 2078   if (exploded_rdn == NULL)
 2079     return NULL;
 2080   /* go over all RDNs */
 2081   l = strlen(attr);
 2082   for (i = 0; exploded_rdn[i] != NULL; i++)
 2083   {
 2084     /* check that RDN starts with attr */
 2085     if (strncasecmp(exploded_rdn[i], attr, l) != 0)
 2086       continue;
 2087     j = l;
 2088     /* skip spaces */
 2089     while (isspace(exploded_rdn[i][j]))
 2090       j++;
 2091     /* ensure that we found an equals sign now */
 2092     if (exploded_rdn[i][j] != '=')
 2093       continue;
 2094     j++;
 2095     /* skip more spaces */
 2096     while (isspace(exploded_rdn[i][j]))
 2097       j++;
 2098     /* ensure that we're not at the end of the string */
 2099     if (exploded_rdn[i][j] == '\0')
 2100       continue;
 2101     /* we found our value */
 2102     return exploded_rdn[i] + j;
 2103   }
 2104   /* fail */
 2105   return NULL;
 2106 }
 2107 
 2108 /* explode the first part of DN into parts
 2109    (e.g. "cn=Test", "uid=test")
 2110    The returned value should be freed with ldap_value_free(). */
 2111 static char **get_exploded_rdn(const char *dn)
 2112 {
 2113   char **exploded_dn;
 2114   char **exploded_rdn;
 2115   /* check if we have a DN */
 2116   if ((dn == NULL) || (strcasecmp(dn, "unknown") == 0))
 2117     return NULL;
 2118   /* explode dn into { "uid=test", "ou=people", ..., NULL } */
 2119   exploded_dn = ldap_explode_dn(dn, 0);
 2120   if ((exploded_dn == NULL) || (exploded_dn[0] == NULL))
 2121   {
 2122     log_log(LOG_WARNING, "ldap_explode_dn(%s) returned NULL: %s",
 2123             dn, strerror(errno));
 2124     return NULL;
 2125   }
 2126   /* explode rdn (first part of exploded_dn),
 2127      e.g. "cn=Test User+uid=testusr" into
 2128      { "cn=Test User", "uid=testusr", NULL } */
 2129   errno = 0;
 2130   exploded_rdn = ldap_explode_rdn(exploded_dn[0], 0);
 2131   if ((exploded_rdn == NULL) || (exploded_rdn[0] == NULL))
 2132   {
 2133     log_log(LOG_WARNING, "ldap_explode_rdn(%s) returned NULL: %s",
 2134             exploded_dn[0], strerror(errno));
 2135     if (exploded_rdn != NULL)
 2136       ldap_value_free(exploded_rdn);
 2137     ldap_value_free(exploded_dn);
 2138     return NULL;
 2139   }
 2140   ldap_value_free(exploded_dn);
 2141   return exploded_rdn;
 2142 }
 2143 
 2144 const char *myldap_get_rdn_value(MYLDAP_ENTRY *entry, const char *attr)
 2145 {
 2146   /* check parameters */
 2147   if (!is_valid_entry(entry))
 2148   {
 2149     log_log(LOG_ERR, "myldap_get_rdn_value(): invalid result entry passed");
 2150     errno = EINVAL;
 2151     return NULL;
 2152   }
 2153   else if (attr == NULL)
 2154   {
 2155     log_log(LOG_ERR, "myldap_get_rdn_value(): invalid attribute name passed");
 2156     errno = EINVAL;
 2157     return NULL;
 2158   }
 2159   /* check if entry contains exploded_rdn */
 2160   if (entry->exploded_rdn == NULL)
 2161   {
 2162     entry->exploded_rdn = get_exploded_rdn(myldap_get_dn(entry));
 2163     if (entry->exploded_rdn == NULL)
 2164       return NULL;
 2165   }
 2166   /* find rnd value */
 2167   return find_rdn_value(entry->exploded_rdn, attr);
 2168 }
 2169 
 2170 const char *myldap_cpy_rdn_value(const char *dn, const char *attr,
 2171                                  char *buf, size_t buflen)
 2172 {
 2173   char **exploded_rdn;
 2174   const char *value;
 2175   /* explode dn into { "cn=Test", "uid=test", NULL } */
 2176   exploded_rdn = get_exploded_rdn(dn);
 2177   if (exploded_rdn == NULL)
 2178     return NULL;
 2179   /* see if we have a match */
 2180   value = find_rdn_value(exploded_rdn, attr);
 2181   /* if we have something store it in the buffer */
 2182   if ((value != NULL) && (strlen(value) < buflen))
 2183     strcpy(buf, value);
 2184   else
 2185     value = NULL;
 2186   /* free allocated stuff */
 2187   ldap_value_free(exploded_rdn);
 2188   /* check if we have something to return */
 2189   return (value != NULL) ? buf : NULL;
 2190 }
 2191 
 2192 int myldap_has_objectclass(MYLDAP_ENTRY *entry, const char *objectclass)
 2193 {
 2194   const char **values;
 2195   int i;
 2196   if ((!is_valid_entry(entry)) || (objectclass == NULL))
 2197   {
 2198     log_log(LOG_ERR, "myldap_has_objectclass(): invalid argument passed");
 2199     errno = EINVAL;
 2200     return 0;
 2201   }
 2202   values = myldap_get_values(entry, "objectClass");
 2203   if (values == NULL)
 2204     return 0;
 2205   for (i = 0; values[i] != NULL; i++)
 2206   {
 2207     if (strcasecmp(values[i], objectclass) == 0)
 2208       return -1;
 2209   }
 2210   return 0;
 2211 }
 2212 
 2213 #ifdef HAVE_LDAP_PARSE_DEREF_CONTROL
 2214 const char ***myldap_get_deref_values(MYLDAP_ENTRY *entry,
 2215                 const char *derefattr, const char *getattr)
 2216 {
 2217   LDAPControl **entryctrls;
 2218   LDAPDerefRes *deref, *d;
 2219   LDAPDerefVal *a;
 2220   int i, pass;
 2221   int rc;
 2222   int found;
 2223   int counts[2];
 2224   size_t sizes[2], size;
 2225   char *buffer = NULL;
 2226   char ***results = NULL;
 2227   rc = ldap_get_entry_controls(entry->search->session->ld, entry->search->msg,
 2228                                 &entryctrls);
 2229   if (rc != LDAP_SUCCESS)
 2230   {
 2231     myldap_err(LOG_WARNING, entry->search->session->ld, rc,
 2232                "ldap_get_entry_controls() failed");
 2233     return NULL;
 2234   }
 2235   if (entryctrls == NULL)
 2236     return NULL;
 2237   /* see if we can find a deref control */
 2238   rc = ldap_parse_deref_control(entry->search->session->ld, entryctrls,
 2239                                 &deref);
 2240   if ((rc != LDAP_SUCCESS) || (deref == NULL))
 2241   {
 2242     if ((rc != LDAP_SUCCESS) && (rc != LDAP_CONTROL_NOT_FOUND))
 2243       myldap_err(LOG_WARNING, entry->search->session->ld, rc,
 2244                  "ldap_parse_deref_control() failed");
 2245     /* clear error flag */
 2246     rc = LDAP_SUCCESS;
 2247     if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER,
 2248                         &rc) != LDAP_SUCCESS)
 2249       log_log(LOG_WARNING, "failed to clear the error flag");
 2250     ldap_controls_free(entryctrls);
 2251     return NULL;
 2252   }
 2253   /* two passes: one to calculate size, one to store data */
 2254   for (pass=0; pass < 2; pass++)
 2255   {
 2256     /* reset counters and size */
 2257     for (i = 0; i < 2; i++)
 2258     {
 2259       counts[i] = 0;
 2260       sizes[i] = 0;
 2261     }
 2262     /* go over all deref'd attributes and find the one we're looking for */
 2263     for (d = deref; d != NULL; d = d->next)
 2264       if ((d->derefAttr != NULL) && (d->derefVal.bv_val != NULL) &&
 2265           (strcasecmp(derefattr, d->derefAttr) == 0))
 2266       {
 2267         /* we should have one d per original attribute value */
 2268         found = 0;
 2269         /* go over deref'd attribute values to find the ones we're looking for */
 2270         for (a = d->attrVals; a != NULL; a = a->next)
 2271           if ((a->type != NULL) && (a->vals != NULL) &&
 2272               (strcasecmp(getattr, a->type) == 0))
 2273             for (i=0; a->vals[i].bv_val != NULL; i++)
 2274             {
 2275               found = 1;
 2276               if (results == NULL)
 2277               {
 2278                 log_log(LOG_DEBUG, "deref %s %s=%s -> %s=%s",
 2279                         myldap_get_dn(entry),  d->derefAttr, d->derefVal.bv_val,
 2280                         a->type, a->vals[i].bv_val);
 2281                 counts[0]++;
 2282                 sizes[0] += strlen(a->vals[i].bv_val) + 1;
 2283               }
 2284               else
 2285               {
 2286                 strcpy(buffer, a->vals[i].bv_val);
 2287                 results[0][counts[0]++] = buffer;
 2288                 buffer += strlen(buffer) + 1;
 2289               }
 2290             }
 2291         if (!found)
 2292         {
 2293           if (results == NULL)
 2294           {
 2295             log_log(LOG_DEBUG, "no %s deref %s %s=%s", getattr,
 2296                     myldap_get_dn(entry),  d->derefAttr, d->derefVal.bv_val);
 2297             counts[1]++;
 2298             sizes[1] += strlen(d->derefVal.bv_val) + 1;
 2299           }
 2300           else
 2301           {
 2302             strcpy(buffer, d->derefVal.bv_val);
 2303             results[1][counts[1]++] = buffer;
 2304             buffer += strlen(buffer) + 1;
 2305           }
 2306         }
 2307       }
 2308     /* allocate memory after first pass */
 2309     if (results == NULL)
 2310     {
 2311       size = sizeof(char **) * 3;
 2312       for (i = 0; i < 2; i++)
 2313         size += sizeof(char *) * (counts[i] + 1);
 2314       for (i = 0; i < 2; i++)
 2315         size += sizeof(char) * sizes[i];
 2316       buffer = (char *)malloc(size);
 2317       if (buffer == NULL)
 2318       {
 2319         log_log(LOG_CRIT, "myldap_get_deref_values(): malloc() failed to allocate memory");
 2320         return NULL;
 2321       }
 2322       /* allocate the list of lists */
 2323       results = (void *)buffer;
 2324       buffer += sizeof(char **) * 3;
 2325       /* allocate the lists */
 2326       for (i = 0; i < 2; i++)
 2327       {
 2328         results[i] = (char **)buffer;
 2329         buffer += sizeof(char *) * (counts[i] + 1);
 2330       }
 2331       results[i] = NULL;
 2332     }
 2333   }
 2334   /* NULL terminate the lists */
 2335   results[0][counts[0]] = NULL;
 2336   results[1][counts[1]] = NULL;
 2337   /* free control data */
 2338   ldap_derefresponse_free(deref);
 2339   ldap_controls_free(entryctrls);
 2340   /* store results so we can free it later on */
 2341   for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
 2342     if (entry->buffers[i] == NULL)
 2343     {
 2344       entry->buffers[i] = (void *)results;
 2345       return (const char ***)results;
 2346     }
 2347   /* we found no room to store the values */
 2348   log_log(LOG_ERR, "myldap_get_deref_values() couldn't store results, "
 2349           "increase MAX_BUFFERS_PER_ENTRY");
 2350   free(results);
 2351   return NULL;
 2352 }
 2353 #else /* not HAVE_LDAP_PARSE_DEREF_CONTROL */
 2354 const char ***myldap_get_deref_values(MYLDAP_ENTRY UNUSED(*entry),
 2355                 const char UNUSED(*derefattr), const char UNUSED(*getattr))
 2356 {
 2357   return NULL;
 2358 }
 2359 #endif /* not HAVE_LDAP_PARSE_DEREF_CONTROL */
 2360 
 2361 int myldap_escape(const char *src, char *buffer, size_t buflen)
 2362 {
 2363   size_t pos = 0;
 2364   /* go over all characters in source string */
 2365   for (; *src != '\0'; src++)
 2366   {
 2367     /* check if char will fit */
 2368     if ((pos + 4) >= buflen)
 2369       return -1;
 2370     /* do escaping for some characters */
 2371     switch (*src)
 2372     {
 2373       case '*':
 2374         strcpy(buffer + pos, "\\2a");
 2375         pos += 3;
 2376         break;
 2377       case '(':
 2378         strcpy(buffer + pos, "\\28");
 2379         pos += 3;
 2380         break;
 2381       case ')':
 2382         strcpy(buffer + pos, "\\29");
 2383         pos += 3;
 2384         break;
 2385       case '\\':
 2386         strcpy(buffer + pos, "\\5c");
 2387         pos += 3;
 2388         break;
 2389       default:
 2390         /* just copy character */
 2391         buffer[pos++] = *src;
 2392         break;
 2393     }
 2394   }
 2395   /* terminate destination string */
 2396   buffer[pos] = '\0';
 2397   return 0;
 2398 }
 2399 
 2400 int myldap_set_debuglevel(int level)
 2401 {
 2402   int i;
 2403   int rc;
 2404   /* turn on debugging */
 2405   if (level > 1)
 2406   {
 2407 #ifdef LBER_OPT_LOG_PRINT_FILE
 2408     log_log(LOG_DEBUG, "ber_set_option(LBER_OPT_LOG_PRINT_FILE)");
 2409     rc = ber_set_option(NULL, LBER_OPT_LOG_PRINT_FILE, stderr);
 2410     if (rc != LDAP_SUCCESS)
 2411     {
 2412       myldap_err(LOG_ERR, NULL, rc, "ber_set_option(LBER_OPT_LOG_PRINT_FILE) failed");
 2413       return rc;
 2414     }
 2415 #endif /* LBER_OPT_LOG_PRINT_FILE */
 2416 #ifdef LBER_OPT_DEBUG_LEVEL
 2417     if (level > 2)
 2418     {
 2419       i = -1;
 2420       log_log(LOG_DEBUG, "ber_set_option(LBER_OPT_DEBUG_LEVEL,-1)");
 2421       rc = ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &i);
 2422       if (rc != LDAP_SUCCESS)
 2423       {
 2424         myldap_err(LOG_ERR, NULL, rc, "ber_set_option(LBER_OPT_DEBUG_LEVEL) failed");
 2425         return rc;
 2426       }
 2427     }
 2428 #endif /* LBER_OPT_DEBUG_LEVEL */
 2429 #ifdef LDAP_OPT_DEBUG_LEVEL
 2430     i = -1;
 2431     log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_DEBUG_LEVEL,-1)");
 2432     rc = ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &i);
 2433     if (rc != LDAP_SUCCESS)
 2434     {
 2435       myldap_err(LOG_ERR, NULL, rc, "ldap_set_option(LDAP_OPT_DEBUG_LEVEL) failed");
 2436       return rc;
 2437     }
 2438 #endif /* LDAP_OPT_DEBUG_LEVEL */
 2439   }
 2440   return LDAP_SUCCESS;
 2441 }
 2442 
 2443 int myldap_passwd(MYLDAP_SESSION *session,
 2444                   const char *userdn, const char *oldpassword,
 2445                   const char *newpasswd)
 2446 {
 2447   int rc;
 2448   struct berval ber_userdn, ber_oldpassword, ber_newpassword, ber_retpassword;
 2449   /* check parameters */
 2450   if ((session == NULL) || (userdn == NULL) || (newpasswd == NULL))
 2451   {
 2452     log_log(LOG_ERR, "myldap_passwd(): invalid parameter passed");
 2453     errno = EINVAL;
 2454     return LDAP_OTHER;
 2455   }
 2456   /* log the call */
 2457   log_log(LOG_DEBUG, "myldap_passwd(userdn=\"%s\",oldpasswd=%s,newpasswd=\"***\")",
 2458           userdn, oldpassword ? "\"***\"" : "NULL");
 2459   /* translate to ber stuff */
 2460   ber_userdn.bv_val = (char *)userdn;
 2461   ber_userdn.bv_len = strlen(userdn);
 2462   ber_newpassword.bv_val = (char *)newpasswd;
 2463   ber_newpassword.bv_len = strlen(newpasswd);
 2464   ber_retpassword.bv_val = NULL;
 2465   ber_retpassword.bv_len = 0;
 2466   /* perform request */
 2467   log_log(LOG_DEBUG, "myldap_passwd(): try ldap_passwd_s() without old password");
 2468   rc = ldap_passwd_s(session->ld, &ber_userdn, NULL, &ber_newpassword,
 2469                      &ber_retpassword, NULL, NULL);
 2470   if (rc != LDAP_SUCCESS)
 2471     myldap_err(LOG_ERR, session->ld, rc, "ldap_passwd_s() without old password failed");
 2472   /* free returned data if needed */
 2473   if (ber_retpassword.bv_val != NULL)
 2474     ldap_memfree(ber_retpassword.bv_val);
 2475   if ((rc != LDAP_SUCCESS) && (oldpassword != NULL))
 2476   {
 2477     /* retry with old password */
 2478     log_log(LOG_DEBUG, "myldap_passwd(): try ldap_passwd_s() with old password");
 2479     ber_oldpassword.bv_val = (char *)oldpassword;
 2480     ber_oldpassword.bv_len = strlen(oldpassword);
 2481     /* perform request */
 2482     rc = ldap_passwd_s(session->ld, &ber_userdn, &ber_oldpassword,
 2483                        &ber_newpassword, &ber_retpassword, NULL, NULL);
 2484     if (rc != LDAP_SUCCESS)
 2485       myldap_err(LOG_ERR, session->ld, rc, "ldap_passwd_s() with old password failed");
 2486     /* free returned data if needed */
 2487     if (ber_retpassword.bv_val != NULL)
 2488       ldap_memfree(ber_retpassword.bv_val);
 2489   }
 2490   return rc;
 2491 }
 2492 
 2493 int myldap_modify(MYLDAP_SESSION *session, const char *dn, LDAPMod * mods[])
 2494 {
 2495   if ((session == NULL) || (dn == NULL))
 2496   {
 2497     log_log(LOG_ERR, "myldap_passwd(): invalid parameter passed");
 2498     errno = EINVAL;
 2499     return LDAP_OTHER;
 2500   }
 2501   return ldap_modify_ext_s(session->ld, dn, mods, NULL, NULL);
 2502 }
 2503 
 2504 int myldap_error_message(MYLDAP_SESSION *session, int rc,
 2505                          char *buffer, size_t buflen)
 2506 {
 2507   char *msg_diag = NULL;
 2508   if ((session == NULL) || (buffer == NULL) || (buflen <= 0))
 2509   {
 2510     log_log(LOG_ERR, "myldap_error_message(): invalid parameter passed");
 2511     errno = EINVAL;
 2512     return LDAP_OTHER;
 2513   }
 2514   /* clear buffer */
 2515   buffer[0] = '\0';
 2516 #ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
 2517   if (session->ld != NULL)
 2518     ldap_get_option(session->ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg_diag);
 2519 #endif /* LDAP_OPT_DIAGNOSTIC_MESSAGE */
 2520   /* return msg_diag or generic error message */
 2521   mysnprintf(buffer, buflen - 1, "%s",
 2522              ((msg_diag != NULL) && (msg_diag[0]!='\0')) ?
 2523              msg_diag : ldap_err2string(rc));
 2524   /* free diagnostic message */
 2525   if (msg_diag != NULL)
 2526     ldap_memfree(msg_diag);
 2527   return LDAP_SUCCESS;
 2528 }