Blame nslcd/myldap.c

Packit 6bd9ab
/*
Packit 6bd9ab
   myldap.c - simple interface to do LDAP requests
Packit 6bd9ab
   Parts of this file were part of the nss_ldap library (as ldap-nss.c)
Packit 6bd9ab
   which has been forked into the nss-pam-ldapd library.
Packit 6bd9ab
Packit 6bd9ab
   Copyright (C) 1997-2006 Luke Howard
Packit 6bd9ab
   Copyright (C) 2006-2007 West Consulting
Packit 6bd9ab
   Copyright (C) 2006-2017 Arthur de Jong
Packit 6bd9ab
Packit 6bd9ab
   This library is free software; you can redistribute it and/or
Packit 6bd9ab
   modify it under the terms of the GNU Lesser General Public
Packit 6bd9ab
   License as published by the Free Software Foundation; either
Packit 6bd9ab
   version 2.1 of the License, or (at your option) any later version.
Packit 6bd9ab
Packit 6bd9ab
   This library is distributed in the hope that it will be useful,
Packit 6bd9ab
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6bd9ab
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6bd9ab
   Lesser General Public License for more details.
Packit 6bd9ab
Packit 6bd9ab
   You should have received a copy of the GNU Lesser General Public
Packit 6bd9ab
   License along with this library; if not, write to the Free Software
Packit 6bd9ab
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit 6bd9ab
   02110-1301 USA
Packit 6bd9ab
*/
Packit 6bd9ab
Packit 6bd9ab
/*
Packit 6bd9ab
   This library expects to use an LDAP library to provide the real
Packit 6bd9ab
   functionality and only provides a convenient wrapper.
Packit 6bd9ab
   Some pointers for more information on the LDAP API:
Packit 6bd9ab
     http://tools.ietf.org/id/draft-ietf-ldapext-ldap-c-api-05.txt
Packit 6bd9ab
     http://www.mozilla.org/directory/csdk-docs/function.htm
Packit 6bd9ab
     http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/apis/dirserv1.htm
Packit 6bd9ab
     http://www.openldap.org/software/man.cgi?query=ldap
Packit 6bd9ab
*/
Packit 6bd9ab
Packit 6bd9ab
#include "config.h"
Packit 6bd9ab
Packit 6bd9ab
#include <stdio.h>
Packit 6bd9ab
#include <stdlib.h>
Packit 6bd9ab
#include <unistd.h>
Packit 6bd9ab
#include <string.h>
Packit 6bd9ab
#include <strings.h>
Packit 6bd9ab
#include <sys/time.h>
Packit 6bd9ab
#include <time.h>
Packit 6bd9ab
#include <sys/types.h>
Packit 6bd9ab
#include <sys/socket.h>
Packit 6bd9ab
#include <errno.h>
Packit 6bd9ab
#include <lber.h>
Packit 6bd9ab
#include <ldap.h>
Packit 6bd9ab
#ifdef HAVE_LDAP_SSL_H
Packit 6bd9ab
#include <ldap_ssl.h>
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef HAVE_GSSLDAP_H
Packit 6bd9ab
#include <gssldap.h>
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef HAVE_GSSSASL_H
Packit 6bd9ab
#include <gsssasl.h>
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef HAVE_SASL_SASL_H
Packit 6bd9ab
#include <sasl/sasl.h>
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef HAVE_SASL_H
Packit 6bd9ab
#include <sasl.h>
Packit 6bd9ab
#endif
Packit 6bd9ab
#include <ctype.h>
Packit 6bd9ab
#include <pthread.h>
Packit 6bd9ab
#include <stdarg.h>
Packit 6bd9ab
Packit 6bd9ab
#include "myldap.h"
Packit 6bd9ab
#include "common.h"
Packit 6bd9ab
#include "log.h"
Packit 6bd9ab
#include "cfg.h"
Packit 6bd9ab
#include "common/set.h"
Packit 6bd9ab
#include "compat/ldap_compat.h"
Packit 6bd9ab
#include "attmap.h"
Packit 6bd9ab
Packit 6bd9ab
/* the maximum number of searches per session */
Packit 6bd9ab
#define MAX_SEARCHES_IN_SESSION 4
Packit 6bd9ab
Packit 6bd9ab
/* the maximum number of dn's to log to the debug log for each search */
Packit 6bd9ab
#define MAX_DEBUG_LOG_DNS 10
Packit 6bd9ab
Packit 6bd9ab
/* a fake scope that is used to not perform an actual search but only
Packit 6bd9ab
   simulate the handling of the search (used for authentication) */
Packit 6bd9ab
#define MYLDAP_SCOPE_BINDONLY 0x1972  /* magic number: should never be a real scope */
Packit 6bd9ab
Packit 6bd9ab
/* This refers to a current LDAP session that contains the connection
Packit 6bd9ab
   information. */
Packit 6bd9ab
struct ldap_session {
Packit 6bd9ab
  /* the connection */
Packit 6bd9ab
  LDAP *ld;
Packit 6bd9ab
  /* timestamp of last activity */
Packit 6bd9ab
  time_t lastactivity;
Packit 6bd9ab
  /* index into uris: currently connected LDAP uri */
Packit 6bd9ab
  int current_uri;
Packit 6bd9ab
  /* a list of searches registered with this session */
Packit 6bd9ab
  struct myldap_search *searches[MAX_SEARCHES_IN_SESSION];
Packit 6bd9ab
  /* the username to bind with */
Packit 6bd9ab
  char binddn[BUFLEN_DN];
Packit 6bd9ab
  /* the password to bind with if any */
Packit 6bd9ab
  char bindpw[BUFLEN_PASSWORD];
Packit 6bd9ab
  /* the authentication result (NSLCD_PAM_* code) */
Packit 6bd9ab
  int policy_response;
Packit 6bd9ab
  /* the authentication message */
Packit 6bd9ab
  char policy_message[BUFLEN_MESSAGE];
Packit 6bd9ab
};
Packit 6bd9ab
Packit 6bd9ab
/* A search description set as returned by myldap_search(). */
Packit 6bd9ab
struct myldap_search {
Packit 6bd9ab
  /* reference to the session */
Packit 6bd9ab
  MYLDAP_SESSION *session;
Packit 6bd9ab
  /* indicator that the search is still valid */
Packit 6bd9ab
  int valid;
Packit 6bd9ab
  /* the parameters descibing the search */
Packit 6bd9ab
  const char *base;
Packit 6bd9ab
  int scope;
Packit 6bd9ab
  const char *filter;
Packit 6bd9ab
  char **attrs;
Packit 6bd9ab
  /* a pointer to the current result entry, used for
Packit 6bd9ab
     freeing resource allocated with that entry */
Packit 6bd9ab
  MYLDAP_ENTRY *entry;
Packit 6bd9ab
  /* LDAP message id for the search, -1 indicates absense of an active search */
Packit 6bd9ab
  int msgid;
Packit 6bd9ab
  /* the last result that was returned by ldap_result() */
Packit 6bd9ab
  LDAPMessage *msg;
Packit 6bd9ab
  /* cookie for paged searches */
Packit 6bd9ab
  struct berval *cookie;
Packit 6bd9ab
  /* to indicate that we can retry the search from myldap_get_entry() */
Packit 6bd9ab
  int may_retry_search;
Packit 6bd9ab
  /* the number of resutls returned so far */
Packit 6bd9ab
  int count;
Packit 6bd9ab
};
Packit 6bd9ab
Packit 6bd9ab
/* The maximum number of calls to myldap_get_values() that may be
Packit 6bd9ab
   done per returned entry. */
Packit 6bd9ab
#define MAX_ATTRIBUTES_PER_ENTRY 16
Packit 6bd9ab
Packit 6bd9ab
/* The maximum number of buffers (used for ranged attribute values and
Packit 6bd9ab
   values returned by bervalues_to_values()) that may be stored per entry. */
Packit 6bd9ab
#define MAX_BUFFERS_PER_ENTRY 8
Packit 6bd9ab
Packit 6bd9ab
/* A single entry from the LDAP database as returned by
Packit 6bd9ab
   myldap_get_entry(). */
Packit 6bd9ab
struct myldap_entry {
Packit 6bd9ab
  /* reference to the search to be used to get parameters
Packit 6bd9ab
     (e.g. LDAP connection) for other calls */
Packit 6bd9ab
  MYLDAP_SEARCH *search;
Packit 6bd9ab
  /* the DN */
Packit 6bd9ab
  const char *dn;
Packit 6bd9ab
  /* a cached version of the exploded rdn */
Packit 6bd9ab
  char **exploded_rdn;
Packit 6bd9ab
  /* a cache of attribute to value list */
Packit 6bd9ab
  char **attributevalues[MAX_ATTRIBUTES_PER_ENTRY];
Packit 6bd9ab
  /* a reference to buffers so we can free() them later on */
Packit 6bd9ab
  char **buffers[MAX_BUFFERS_PER_ENTRY];
Packit 6bd9ab
};
Packit 6bd9ab
Packit 6bd9ab
/* Flag to record first search operation */
Packit 6bd9ab
int first_search = 1;
Packit 6bd9ab
Packit 6bd9ab
static void myldap_err(int pri, LDAP *ld, int rc, const char *format, ...)
Packit 6bd9ab
{
Packit 6bd9ab
  char message[BUFLEN_MESSAGE];
Packit 6bd9ab
  char *msg_ldap = NULL;
Packit 6bd9ab
  char *msg_diag = NULL;
Packit 6bd9ab
  char *msg_errno = NULL;
Packit 6bd9ab
  va_list ap;
Packit 6bd9ab
  /* make the message */
Packit 6bd9ab
  va_start(ap, format);
Packit 6bd9ab
  vsnprintf(message, sizeof(message), format, ap);
Packit 6bd9ab
  message[sizeof(message) - 1] = '\0';
Packit 6bd9ab
  va_end(ap);
Packit 6bd9ab
  /* get the various error message */
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    msg_ldap = ldap_err2string(rc);
Packit 6bd9ab
    /* get the diagnostic information */
Packit 6bd9ab
#ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
Packit 6bd9ab
    if (ld != NULL)
Packit 6bd9ab
      ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg_diag);
Packit 6bd9ab
#endif /* LDAP_OPT_DIAGNOSTIC_MESSAGE */
Packit 6bd9ab
  }
Packit 6bd9ab
  if (errno != 0)
Packit 6bd9ab
    msg_errno = strerror(errno);
Packit 6bd9ab
  /* log the message */
Packit 6bd9ab
  log_log(pri, "%s%s%s%s%s%s%s", message,
Packit 6bd9ab
          (msg_ldap == NULL) ? "" : ": ", (msg_ldap == NULL) ? "" : msg_ldap,
Packit 6bd9ab
          (msg_diag == NULL) ? "" : ": ", (msg_diag == NULL) ? "" : msg_diag,
Packit 6bd9ab
          (msg_errno == NULL) ? "" : ": ", (msg_errno == NULL) ? "" : msg_errno);
Packit 6bd9ab
  /* free diagnostic message */
Packit 6bd9ab
  if (msg_diag != NULL)
Packit 6bd9ab
    ldap_memfree(msg_diag);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static MYLDAP_ENTRY *myldap_entry_new(MYLDAP_SEARCH *search)
Packit 6bd9ab
{
Packit 6bd9ab
  MYLDAP_ENTRY *entry;
Packit 6bd9ab
  int i;
Packit 6bd9ab
  /* Note: as an alternative we could embed the myldap_entry into the
Packit 6bd9ab
     myldap_search struct to save on malloc() and free() calls. */
Packit 6bd9ab
  /* allocate new entry */
Packit 6bd9ab
  entry = (MYLDAP_ENTRY *)malloc(sizeof(struct myldap_entry));
Packit 6bd9ab
  if (entry == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_CRIT, "myldap_entry_new(): malloc() failed to allocate memory");
Packit 6bd9ab
    exit(EXIT_FAILURE);
Packit 6bd9ab
  }
Packit 6bd9ab
  /* fill in fields */
Packit 6bd9ab
  entry->search = search;
Packit 6bd9ab
  entry->dn = NULL;
Packit 6bd9ab
  entry->exploded_rdn = NULL;
Packit 6bd9ab
  for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
Packit 6bd9ab
    entry->attributevalues[i] = NULL;
Packit 6bd9ab
  for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
Packit 6bd9ab
    entry->buffers[i] = NULL;
Packit 6bd9ab
  /* return the fresh entry */
Packit 6bd9ab
  return entry;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static void myldap_entry_free(MYLDAP_ENTRY *entry)
Packit 6bd9ab
{
Packit 6bd9ab
  int i;
Packit 6bd9ab
  /* free the DN */
Packit 6bd9ab
  if (entry->dn != NULL)
Packit 6bd9ab
    ldap_memfree((char *)entry->dn);
Packit 6bd9ab
  /* free the exploded RDN */
Packit 6bd9ab
  if (entry->exploded_rdn != NULL)
Packit 6bd9ab
    ldap_value_free(entry->exploded_rdn);
Packit 6bd9ab
  /* free all attribute values */
Packit 6bd9ab
  for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
Packit 6bd9ab
    if (entry->attributevalues[i] != NULL)
Packit 6bd9ab
      ldap_value_free(entry->attributevalues[i]);
Packit 6bd9ab
  /* free all buffers */
Packit 6bd9ab
  for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
Packit 6bd9ab
    if (entry->buffers[i] != NULL)
Packit 6bd9ab
      free(entry->buffers[i]);
Packit 6bd9ab
  /* we don't need the result anymore, ditch it. */
Packit 6bd9ab
  ldap_msgfree(entry->search->msg);
Packit 6bd9ab
  entry->search->msg = NULL;
Packit 6bd9ab
  /* free the actual memory for the struct */
Packit 6bd9ab
  free(entry);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static MYLDAP_SEARCH *myldap_search_new(MYLDAP_SESSION *session,
Packit 6bd9ab
                                        const char *base, int scope,
Packit 6bd9ab
                                        const char *filter,
Packit 6bd9ab
                                        const char **attrs)
Packit 6bd9ab
{
Packit 6bd9ab
  char *buffer;
Packit 6bd9ab
  MYLDAP_SEARCH *search;
Packit 6bd9ab
  int i;
Packit 6bd9ab
  size_t sz;
Packit 6bd9ab
  /* figure out size for new memory block to allocate
Packit 6bd9ab
     this has the advantage that we can free the whole lot with one call */
Packit 6bd9ab
  sz = sizeof(struct myldap_search);
Packit 6bd9ab
  sz += strlen(base) + 1 + strlen(filter) + 1;
Packit 6bd9ab
  for (i = 0; attrs[i] != NULL; i++)
Packit 6bd9ab
    sz += strlen(attrs[i]) + 1;
Packit 6bd9ab
  sz += (i + 1) * sizeof(char *);
Packit 6bd9ab
  /* allocate new results memory region */
Packit 6bd9ab
  buffer = (char *)malloc(sz);
Packit 6bd9ab
  if (buffer == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_CRIT, "myldap_search_new(): malloc() failed to allocate memory");
Packit 6bd9ab
    exit(EXIT_FAILURE);
Packit 6bd9ab
  }
Packit 6bd9ab
  /* initialize struct */
Packit 6bd9ab
  search = (MYLDAP_SEARCH *)(void *)(buffer);
Packit 6bd9ab
  buffer += sizeof(struct myldap_search);
Packit 6bd9ab
  /* save pointer to session */
Packit 6bd9ab
  search->session = session;
Packit 6bd9ab
  /* flag as valid search */
Packit 6bd9ab
  search->valid = 1;
Packit 6bd9ab
  /* initialize array of attributes */
Packit 6bd9ab
  search->attrs = (char **)(void *)buffer;
Packit 6bd9ab
  buffer += (i + 1) * sizeof(char *);
Packit 6bd9ab
  /* copy base */
Packit 6bd9ab
  strcpy(buffer, base);
Packit 6bd9ab
  search->base = buffer;
Packit 6bd9ab
  buffer += strlen(base) + 1;
Packit 6bd9ab
  /* just plainly store scope */
Packit 6bd9ab
  search->scope = scope;
Packit 6bd9ab
  /* copy filter */
Packit 6bd9ab
  strcpy(buffer, filter);
Packit 6bd9ab
  search->filter = buffer;
Packit 6bd9ab
  buffer += strlen(filter) + 1;
Packit 6bd9ab
  /* copy attributes themselves */
Packit 6bd9ab
  for (i = 0; attrs[i] != NULL; i++)
Packit 6bd9ab
  {
Packit 6bd9ab
    strcpy(buffer, attrs[i]);
Packit 6bd9ab
    search->attrs[i] = buffer;
Packit 6bd9ab
    buffer += strlen(attrs[i]) + 1;
Packit 6bd9ab
  }
Packit 6bd9ab
  search->attrs[i] = NULL;
Packit 6bd9ab
  /* initialize context */
Packit 6bd9ab
  search->cookie = NULL;
Packit 6bd9ab
  search->msg = NULL;
Packit 6bd9ab
  search->msgid = -1;
Packit 6bd9ab
  search->may_retry_search = 1;
Packit 6bd9ab
  /* clear result entry */
Packit 6bd9ab
  search->entry = NULL;
Packit 6bd9ab
  search->count = 0;
Packit 6bd9ab
  /* return the new search struct */
Packit 6bd9ab
  return search;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static MYLDAP_SESSION *myldap_session_new(void)
Packit 6bd9ab
{
Packit 6bd9ab
  MYLDAP_SESSION *session;
Packit 6bd9ab
  int i;
Packit 6bd9ab
  /* allocate memory for the session storage */
Packit 6bd9ab
  session = (struct ldap_session *)malloc(sizeof(struct ldap_session));
Packit 6bd9ab
  if (session == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_CRIT, "myldap_session_new(): malloc() failed to allocate memory");
Packit 6bd9ab
    exit(EXIT_FAILURE);
Packit 6bd9ab
  }
Packit 6bd9ab
  /* initialize the session */
Packit 6bd9ab
  session->ld = NULL;
Packit 6bd9ab
  session->lastactivity = 0;
Packit 6bd9ab
  session->current_uri = 0;
Packit 6bd9ab
  for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
Packit 6bd9ab
    session->searches[i] = NULL;
Packit 6bd9ab
  session->binddn[0] = '\0';
Packit 6bd9ab
  memset(session->bindpw, 0, sizeof(session->bindpw));
Packit 6bd9ab
  session->bindpw[0] = '\0';
Packit 6bd9ab
  session->policy_response = NSLCD_PAM_SUCCESS;
Packit 6bd9ab
  session->policy_message[0] = '\0';
Packit 6bd9ab
  /* return the new session */
Packit 6bd9ab
  return session;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
PURE static inline int is_valid_entry(MYLDAP_ENTRY *entry)
Packit 6bd9ab
{
Packit 6bd9ab
 return (entry != NULL) && (entry->search != NULL) &&
Packit 6bd9ab
        (entry->search->session != NULL) && (entry->search->session->ld != NULL) &&
Packit 6bd9ab
        (entry->search->msg != NULL);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
#ifdef HAVE_SASL_INTERACT_T
Packit 6bd9ab
/* this is registered with ldap_sasl_interactive_bind_s() in do_bind() */
Packit 6bd9ab
static int do_sasl_interact(LDAP UNUSED(*ld), unsigned UNUSED(flags),
Packit 6bd9ab
                            void *defaults, void *_interact)
Packit 6bd9ab
{
Packit 6bd9ab
  struct ldap_config *cfg = defaults;
Packit 6bd9ab
  sasl_interact_t *interact = _interact;
Packit 6bd9ab
  while (interact->id != SASL_CB_LIST_END)
Packit 6bd9ab
  {
Packit 6bd9ab
    switch (interact->id)
Packit 6bd9ab
    {
Packit 6bd9ab
      case SASL_CB_GETREALM:
Packit 6bd9ab
        if (cfg->sasl_realm)
Packit 6bd9ab
        {
Packit 6bd9ab
          log_log(LOG_DEBUG, "do_sasl_interact(): returning sasl_realm \"%s\"",
Packit 6bd9ab
                  cfg->sasl_realm);
Packit 6bd9ab
          interact->result = cfg->sasl_realm;
Packit 6bd9ab
          interact->len = strlen(cfg->sasl_realm);
Packit 6bd9ab
        }
Packit 6bd9ab
        else
Packit 6bd9ab
          log_log(LOG_DEBUG, "do_sasl_interact(): were asked for sasl_realm but we don't have any");
Packit 6bd9ab
        break;
Packit 6bd9ab
      case SASL_CB_AUTHNAME:
Packit 6bd9ab
        if (cfg->sasl_authcid)
Packit 6bd9ab
        {
Packit 6bd9ab
          log_log(LOG_DEBUG, "do_sasl_interact(): returning sasl_authcid \"%s\"",
Packit 6bd9ab
                  cfg->sasl_authcid);
Packit 6bd9ab
          interact->result = cfg->sasl_authcid;
Packit 6bd9ab
          interact->len = strlen(cfg->sasl_authcid);
Packit 6bd9ab
        }
Packit 6bd9ab
        else
Packit 6bd9ab
          log_log(LOG_DEBUG, "do_sasl_interact(): were asked for sasl_authcid but we don't have any");
Packit 6bd9ab
        break;
Packit 6bd9ab
      case SASL_CB_USER:
Packit 6bd9ab
        if (cfg->sasl_authzid)
Packit 6bd9ab
        {
Packit 6bd9ab
          log_log(LOG_DEBUG, "do_sasl_interact(): returning sasl_authzid \"%s\"",
Packit 6bd9ab
                  cfg->sasl_authzid);
Packit 6bd9ab
          interact->result = cfg->sasl_authzid;
Packit 6bd9ab
          interact->len = strlen(cfg->sasl_authzid);
Packit 6bd9ab
        }
Packit 6bd9ab
        else
Packit 6bd9ab
          log_log(LOG_DEBUG, "do_sasl_interact(): were asked for sasl_authzid but we don't have any");
Packit 6bd9ab
        break;
Packit 6bd9ab
      case SASL_CB_PASS:
Packit 6bd9ab
        if (cfg->bindpw)
Packit 6bd9ab
        {
Packit 6bd9ab
          log_log(LOG_DEBUG, "do_sasl_interact(): returning bindpw \"***\"");
Packit 6bd9ab
          interact->result = cfg->bindpw;
Packit 6bd9ab
          interact->len = strlen(cfg->bindpw);
Packit 6bd9ab
        }
Packit 6bd9ab
        else
Packit 6bd9ab
          log_log(LOG_DEBUG, "do_sasl_interact(): were asked for bindpw but we don't have any");
Packit 6bd9ab
        break;
Packit 6bd9ab
      default:
Packit 6bd9ab
        /* just ignore */
Packit 6bd9ab
        break;
Packit 6bd9ab
    }
Packit 6bd9ab
    interact++;
Packit 6bd9ab
  }
Packit 6bd9ab
  return LDAP_SUCCESS;
Packit 6bd9ab
}
Packit 6bd9ab
#endif /* HAVE_SASL_INTERACT_T */
Packit 6bd9ab
Packit 6bd9ab
#define LDAP_SET_OPTION(ld, option, invalue)                                \
Packit 6bd9ab
  rc = ldap_set_option(ld, option, invalue);                                \
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)                                                   \
Packit 6bd9ab
  {                                                                         \
Packit 6bd9ab
    myldap_err(LOG_ERR, ld, rc, "ldap_set_option(" #option ") failed");     \
Packit 6bd9ab
    return rc;                                                              \
Packit 6bd9ab
  }
Packit 6bd9ab
Packit 6bd9ab
#if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE)
Packit 6bd9ab
static void print_ppolicy_expiry(MYLDAP_SESSION *session, unsigned int sec)
Packit 6bd9ab
{
Packit 6bd9ab
  unsigned int days = 0;
Packit 6bd9ab
  unsigned int hours = 0;
Packit 6bd9ab
  unsigned int minutes = 0;
Packit 6bd9ab
  /* return this warning so PAM can present it to the user */
Packit 6bd9ab
  if (strlen(session->policy_message) != 0)
Packit 6bd9ab
    return;
Packit 6bd9ab
  if (sec > 24 * 3600)
Packit 6bd9ab
  {
Packit 6bd9ab
    days = sec / (24 * 3600);
Packit 6bd9ab
    sec -= days * 24 * 3600;
Packit 6bd9ab
  }
Packit 6bd9ab
  if (sec > 3600)
Packit 6bd9ab
  {
Packit 6bd9ab
    hours = sec / 3600;
Packit 6bd9ab
    sec -= (hours * 3600);
Packit 6bd9ab
  }
Packit 6bd9ab
  if (sec > 60)
Packit 6bd9ab
  {
Packit 6bd9ab
    minutes = sec / 60;
Packit 6bd9ab
    sec -= minutes * 60;
Packit 6bd9ab
  }
Packit 6bd9ab
  if (days > 1)
Packit 6bd9ab
    mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
               "Password will expires in %u days", days);
Packit 6bd9ab
  else if (days > 0)
Packit 6bd9ab
    mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
               "Password will expires in %u hours", hours + 24);
Packit 6bd9ab
  else if (hours > 1)
Packit 6bd9ab
  {
Packit 6bd9ab
    if (minutes > 1)
Packit 6bd9ab
      mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
                 "Password will expires in %u hours and %u minutes",
Packit 6bd9ab
                 hours, minutes);
Packit 6bd9ab
    else
Packit 6bd9ab
      mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
                 "Password will expires in %u hours", hours);
Packit 6bd9ab
  }
Packit 6bd9ab
  else if (hours > 0)
Packit 6bd9ab
    mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
               "Password will expires in %u minutes", minutes + 60);
Packit 6bd9ab
  else if (minutes > 1)
Packit 6bd9ab
  {
Packit 6bd9ab
    if (sec > 1)
Packit 6bd9ab
      mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
                 "Password will expires in %u minutes and %u seconds",
Packit 6bd9ab
                 minutes, sec);
Packit 6bd9ab
    else
Packit 6bd9ab
      mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
                 "Password will expires in %u minutes", minutes);
Packit 6bd9ab
  }
Packit 6bd9ab
  else
Packit 6bd9ab
    mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
               "Password will expires in %u seconds", sec);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static void handle_ppolicy_controls(MYLDAP_SESSION *session, LDAP *ld, LDAPControl **ctrls)
Packit 6bd9ab
{
Packit 6bd9ab
  int i;
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  /* clear policy response information in session */
Packit 6bd9ab
  session->policy_response = NSLCD_PAM_SUCCESS;
Packit 6bd9ab
  strncpy(session->policy_message, "", sizeof(session->policy_message));
Packit 6bd9ab
  for (i = 0; ctrls[i] != NULL; i++)
Packit 6bd9ab
  {
Packit 6bd9ab
    if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PWEXPIRED) == 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      /* check for expired control: force the user to change their password */
Packit 6bd9ab
      log_log(LOG_DEBUG, "got LDAP_CONTROL_PWEXPIRED (password expired, user should change)");
Packit 6bd9ab
      if (session->policy_response == NSLCD_PAM_SUCCESS)
Packit 6bd9ab
        session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD;
Packit 6bd9ab
    }
Packit 6bd9ab
    else if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PWEXPIRING) == 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      /* check for password expiration warning control: the password is about
Packit 6bd9ab
         to expire (returns the number of seconds remaining until the password
Packit 6bd9ab
         expires) */
Packit 6bd9ab
      char seconds[32];
Packit 6bd9ab
      long int sec;
Packit 6bd9ab
      mysnprintf(seconds, sizeof(seconds), "%.*s", (int)ctrls[i]->ldctl_value.bv_len,
Packit 6bd9ab
                 ctrls[i]->ldctl_value.bv_val);
Packit 6bd9ab
      sec = atol(seconds);
Packit 6bd9ab
      log_log(LOG_DEBUG, "got LDAP_CONTROL_PWEXPIRING (password will expire in %ld seconds)",
Packit 6bd9ab
              sec);
Packit 6bd9ab
      print_ppolicy_expiry(session, (unsigned int)sec);
Packit 6bd9ab
    }
Packit 6bd9ab
    else if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      /* check for password policy control */
Packit 6bd9ab
      int expire = 0, grace = 0;
Packit 6bd9ab
      LDAPPasswordPolicyError error = -1;
Packit 6bd9ab
      rc = ldap_parse_passwordpolicy_control(ld, ctrls[i], &expire, &grace, &error);
Packit 6bd9ab
      if (rc != LDAP_SUCCESS)
Packit 6bd9ab
        myldap_err(LOG_WARNING, ld, rc, "ldap_parse_passwordpolicy_control() failed (ignored)");
Packit 6bd9ab
      else
Packit 6bd9ab
      {
Packit 6bd9ab
        /* log returned control information */
Packit 6bd9ab
        log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (%s)",
Packit 6bd9ab
                ldap_passwordpolicy_err2txt(error));
Packit 6bd9ab
        if (expire >= 0)
Packit 6bd9ab
          log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (password will expire in %d seconds)",
Packit 6bd9ab
                  expire);
Packit 6bd9ab
        if (grace >= 0)
Packit 6bd9ab
          log_log(LOG_DEBUG, "got LDAP_CONTROL_PASSWORDPOLICYRESPONSE (%d grace logins left)",
Packit 6bd9ab
                  grace);
Packit 6bd9ab
        /* return this information to PAM */
Packit 6bd9ab
        if ((error == PP_passwordExpired) &&
Packit 6bd9ab
            ((session->policy_response == NSLCD_PAM_SUCCESS) ||
Packit 6bd9ab
             (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD)))
Packit 6bd9ab
        {
Packit 6bd9ab
          /* this means that the password has expired and must be reset */
Packit 6bd9ab
          session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD;
Packit 6bd9ab
          mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
                     "%s", ldap_passwordpolicy_err2txt(error));
Packit 6bd9ab
        }
Packit 6bd9ab
        else if ((error == PP_accountLocked) &&
Packit 6bd9ab
                 ((session->policy_response == NSLCD_PAM_SUCCESS) ||
Packit 6bd9ab
                  (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD)))
Packit 6bd9ab
        {
Packit 6bd9ab
          /* this means that the account is locked and the user cannot log
Packit 6bd9ab
             in (the bind probably failed already) */
Packit 6bd9ab
          session->policy_response = NSLCD_PAM_ACCT_EXPIRED;
Packit 6bd9ab
          mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
                     "%s", ldap_passwordpolicy_err2txt(error));
Packit 6bd9ab
        }
Packit 6bd9ab
        else if ((error == PP_changeAfterReset) &&
Packit 6bd9ab
                 (session->policy_response == NSLCD_PAM_SUCCESS))
Packit 6bd9ab
        {
Packit 6bd9ab
          /* this indicates that the password must be changed before the
Packit 6bd9ab
             user is allowed to perform any other operation */
Packit 6bd9ab
          session->policy_response = NSLCD_PAM_NEW_AUTHTOK_REQD;
Packit 6bd9ab
          mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
                     "%s", ldap_passwordpolicy_err2txt(error));
Packit 6bd9ab
        }
Packit 6bd9ab
        else if ((error != PP_noError) &&
Packit 6bd9ab
                 ((session->policy_response == NSLCD_PAM_SUCCESS) ||
Packit 6bd9ab
                  (session->policy_response == NSLCD_PAM_NEW_AUTHTOK_REQD)))
Packit 6bd9ab
        {
Packit 6bd9ab
          /* any other error is assumed to mean that the operation failed */
Packit 6bd9ab
          session->policy_response = NSLCD_PAM_PERM_DENIED;
Packit 6bd9ab
          mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
                     "%s", ldap_passwordpolicy_err2txt(error));
Packit 6bd9ab
        }
Packit 6bd9ab
        /* both expire and grace should just be warnings to the user */
Packit 6bd9ab
        if ((expire >= 0) && (strlen(session->policy_message) == 0))
Packit 6bd9ab
        {
Packit 6bd9ab
          /* if no other error has happened, this indicates that the password
Packit 6bd9ab
             will soon expire (number of seconds) */
Packit 6bd9ab
          print_ppolicy_expiry(session, (unsigned int)expire);
Packit 6bd9ab
        }
Packit 6bd9ab
        else if ((grace >= 0) && (strlen(session->policy_message) == 0))
Packit 6bd9ab
        {
Packit 6bd9ab
          /* this indicates the number of grace logins that are left before
Packit 6bd9ab
             no further login attempts will be allowed */
Packit 6bd9ab
          mysnprintf(session->policy_message, sizeof(session->policy_message),
Packit 6bd9ab
                     "Password expired, %d grace logins left", grace);
Packit 6bd9ab
        }
Packit 6bd9ab
      }
Packit 6bd9ab
    }
Packit 6bd9ab
    /* ignore any other controls */
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static int do_ppolicy_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri)
Packit 6bd9ab
{
Packit 6bd9ab
  int rc, parserc;
Packit 6bd9ab
  struct berval cred;
Packit 6bd9ab
  LDAPControl passwd_policy_req;
Packit 6bd9ab
  LDAPControl *requestctrls[2];
Packit 6bd9ab
  LDAPControl **responsectrls;
Packit 6bd9ab
  int msgid;
Packit 6bd9ab
  struct timeval timeout;
Packit 6bd9ab
  LDAPMessage *result;
Packit 6bd9ab
  /* build policy request if pam_authc_ppolicy is set */
Packit 6bd9ab
  if (nslcd_cfg->pam_authc_ppolicy)
Packit 6bd9ab
  {
Packit 6bd9ab
    passwd_policy_req.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
Packit 6bd9ab
    passwd_policy_req.ldctl_value.bv_val = NULL; /* none */
Packit 6bd9ab
    passwd_policy_req.ldctl_value.bv_len = 0;
Packit 6bd9ab
    passwd_policy_req.ldctl_iscritical = 0; /* not critical */
Packit 6bd9ab
    requestctrls[0] = &passwd_policy_req;
Packit 6bd9ab
  }
Packit 6bd9ab
  else
Packit 6bd9ab
    requestctrls[0] = NULL;
Packit 6bd9ab
  requestctrls[1] = NULL;
Packit 6bd9ab
  /* build password berval */
Packit 6bd9ab
  cred.bv_val = (char *)session->bindpw;
Packit 6bd9ab
  cred.bv_len = strlen(session->bindpw);
Packit 6bd9ab
  /* do a SASL simple bind with the binddn and bindpw */
Packit 6bd9ab
  log_log(LOG_DEBUG, "ldap_sasl_bind(\"%s\",%s) (uri=\"%s\") (ppolicy=%s)",
Packit 6bd9ab
          session->binddn, (session->bindpw[0] != '\0') ? "\"***\"" : "\"\"",
Packit 6bd9ab
          uri, (requestctrls[0] == NULL) ? "no" : "yes");
Packit 6bd9ab
  rc = ldap_sasl_bind(ld, session->binddn, LDAP_SASL_SIMPLE, &cred, requestctrls, NULL, &msgid);
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
    return rc;
Packit 6bd9ab
  if (msgid == -1)
Packit 6bd9ab
  {
Packit 6bd9ab
    myldap_err(LOG_WARNING, ld, rc,"ldap_sasl_bind() failed (msgid=-1, uri=%s)", uri);
Packit 6bd9ab
    return LDAP_OPERATIONS_ERROR;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* get the result from the bind operation */
Packit 6bd9ab
  timeout.tv_sec = nslcd_cfg->bind_timelimit;
Packit 6bd9ab
  timeout.tv_usec = 0;
Packit 6bd9ab
  result = NULL;
Packit 6bd9ab
  rc = ldap_result(ld, msgid, LDAP_MSG_ALL, &timeout, &result);
Packit 6bd9ab
  if (rc == -1) /* some error */
Packit 6bd9ab
  {
Packit 6bd9ab
    if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
Packit 6bd9ab
      rc = LDAP_UNAVAILABLE;
Packit 6bd9ab
    myldap_err(LOG_ERR, ld, rc, "ldap_result() failed");
Packit 6bd9ab
    if (result != NULL)
Packit 6bd9ab
      ldap_msgfree(result);
Packit 6bd9ab
    return LDAP_LOCAL_ERROR;
Packit 6bd9ab
  }
Packit 6bd9ab
  if (rc == 0) /* the timeout expired */
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "ldap_result() timed out");
Packit 6bd9ab
    if (result != NULL)
Packit 6bd9ab
      ldap_msgfree(result);
Packit 6bd9ab
    return LDAP_TIMEOUT;
Packit 6bd9ab
  }
Packit 6bd9ab
  responsectrls = NULL;
Packit 6bd9ab
  /* ignore any response controls unless we're interested in ppolicy */
Packit 6bd9ab
  if (nslcd_cfg->pam_authc_ppolicy)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* parse the result from the bind operation (frees result, gets controls) */
Packit 6bd9ab
    parserc = ldap_parse_result(ld, result, &rc, NULL, NULL, NULL, &responsectrls, 1);
Packit 6bd9ab
    if (parserc != LDAP_SUCCESS)
Packit 6bd9ab
    {
Packit 6bd9ab
      myldap_err(LOG_ERR, ld, parserc, "ldap_parse_result() failed");
Packit 6bd9ab
      if (responsectrls != NULL)
Packit 6bd9ab
        ldap_controls_free(responsectrls);
Packit 6bd9ab
      return parserc;
Packit 6bd9ab
    }
Packit 6bd9ab
    /* handle any returned controls */
Packit 6bd9ab
    if (responsectrls != NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      handle_ppolicy_controls(session, ld, responsectrls);
Packit 6bd9ab
      ldap_controls_free(responsectrls);
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  /* return the result of the BIND operation */
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    myldap_err(LOG_DEBUG, ld, rc, "ldap_parse_result() result");
Packit 6bd9ab
    return rc;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* check the returned controls */
Packit 6bd9ab
  return LDAP_SUCCESS;
Packit 6bd9ab
}
Packit 6bd9ab
#endif /* no SASL, so no ppolicy */
Packit 6bd9ab
Packit 6bd9ab
/* This function performs the authentication phase of opening a connection.
Packit 6bd9ab
   The binddn and bindpw parameters may be used to override the authentication
Packit 6bd9ab
   mechanism defined in the configuration.  This returns an LDAP result
Packit 6bd9ab
   code. */
Packit 6bd9ab
static int do_bind(MYLDAP_SESSION *session, LDAP *ld, const char *uri)
Packit 6bd9ab
{
Packit 6bd9ab
  int rc;
Packit 6bd9ab
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
Packit 6bd9ab
#ifndef HAVE_SASL_INTERACT_T
Packit 6bd9ab
  struct berval cred;
Packit 6bd9ab
#endif /* not HAVE_SASL_INTERACT_T */
Packit 6bd9ab
#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
Packit 6bd9ab
#ifdef LDAP_OPT_X_TLS
Packit 6bd9ab
  /* check if StartTLS is requested */
Packit 6bd9ab
  if (nslcd_cfg->ssl == SSL_START_TLS)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_DEBUG, "ldap_start_tls_s()");
Packit 6bd9ab
    errno = 0;
Packit 6bd9ab
    rc = ldap_start_tls_s(ld, NULL, NULL);
Packit 6bd9ab
    if (rc != LDAP_SUCCESS)
Packit 6bd9ab
    {
Packit 6bd9ab
      myldap_err(LOG_WARNING, ld, rc, "ldap_start_tls_s() failed (uri=%s)",
Packit 6bd9ab
                 uri);
Packit 6bd9ab
      return rc;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
#endif /* LDAP_OPT_X_TLS */
Packit 6bd9ab
  /* check if the binddn and bindpw are overwritten in the session */
Packit 6bd9ab
  if (session->binddn[0] != '\0')
Packit 6bd9ab
  {
Packit 6bd9ab
#if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE)
Packit 6bd9ab
    return do_ppolicy_bind(session, ld, uri);
Packit 6bd9ab
#else /* no SASL, so no ppolicy */
Packit 6bd9ab
    /* do a simple bind */
Packit 6bd9ab
    log_log(LOG_DEBUG, "ldap_simple_bind_s(\"%s\",%s) (uri=\"%s\")",
Packit 6bd9ab
            session->binddn,
Packit 6bd9ab
            (session->bindpw[0] != '\0') ? "\"***\"" : "\"\"",
Packit 6bd9ab
            uri);
Packit 6bd9ab
    return ldap_simple_bind_s(ld, session->binddn, session->bindpw);
Packit 6bd9ab
#endif
Packit 6bd9ab
  }
Packit 6bd9ab
  /* perform SASL bind if requested and available on platform */
Packit 6bd9ab
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
Packit 6bd9ab
  /* TODO: store this information in the session */
Packit 6bd9ab
  if (nslcd_cfg->sasl_mech != NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* do a SASL bind */
Packit 6bd9ab
    if (nslcd_cfg->sasl_secprops != NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_SASL_SECPROPS,\"%s\")",
Packit 6bd9ab
              nslcd_cfg->sasl_secprops);
Packit 6bd9ab
      LDAP_SET_OPTION(ld, LDAP_OPT_X_SASL_SECPROPS, (void *)nslcd_cfg->sasl_secprops);
Packit 6bd9ab
    }
Packit 6bd9ab
#ifdef HAVE_SASL_INTERACT_T
Packit 6bd9ab
    if (nslcd_cfg->binddn != NULL)
Packit 6bd9ab
      log_log(LOG_DEBUG, "ldap_sasl_interactive_bind_s(\"%s\",\"%s\") (uri=\"%s\")",
Packit 6bd9ab
              nslcd_cfg->binddn, nslcd_cfg->sasl_mech, uri);
Packit 6bd9ab
    else
Packit 6bd9ab
      log_log(LOG_DEBUG, "ldap_sasl_interactive_bind_s(NULL,\"%s\") (uri=\"%s\")",
Packit 6bd9ab
              nslcd_cfg->sasl_mech, uri);
Packit 6bd9ab
    return ldap_sasl_interactive_bind_s(ld, nslcd_cfg->binddn,
Packit 6bd9ab
                                        nslcd_cfg->sasl_mech, NULL, NULL,
Packit 6bd9ab
                                        LDAP_SASL_QUIET, do_sasl_interact,
Packit 6bd9ab
                                        (void *)nslcd_cfg);
Packit 6bd9ab
#else /* HAVE_SASL_INTERACT_T */
Packit 6bd9ab
    if (nslcd_cfg->bindpw != NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      cred.bv_val = nslcd_cfg->bindpw;
Packit 6bd9ab
      cred.bv_len = strlen(nslcd_cfg->bindpw);
Packit 6bd9ab
    }
Packit 6bd9ab
    else
Packit 6bd9ab
    {
Packit 6bd9ab
      cred.bv_val = "";
Packit 6bd9ab
      cred.bv_len = 0;
Packit 6bd9ab
    }
Packit 6bd9ab
    if (nslcd_cfg->binddn != NULL)
Packit 6bd9ab
      log_log(LOG_DEBUG, "ldap_sasl_bind_s(\"%s\",\"%s\",%s) (uri=\"%s\")",
Packit 6bd9ab
              nslcd_cfg->binddn, nslcd_cfg->sasl_mech,
Packit 6bd9ab
              nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
Packit 6bd9ab
    else
Packit 6bd9ab
      log_log(LOG_DEBUG, "ldap_sasl_bind_s(NULL,\"%s\",%s) (uri=\"%s\")",
Packit 6bd9ab
              nslcd_cfg->sasl_mech,
Packit 6bd9ab
              nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
Packit 6bd9ab
    return ldap_sasl_bind_s(ld, nslcd_cfg->binddn,
Packit 6bd9ab
                            nslcd_cfg->sasl_mech, &cred, NULL, NULL, NULL);
Packit 6bd9ab
#endif /* not HAVE_SASL_INTERACT_T */
Packit 6bd9ab
  }
Packit 6bd9ab
#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
Packit 6bd9ab
  /* do a simple bind */
Packit 6bd9ab
  if (nslcd_cfg->binddn)
Packit 6bd9ab
    log_log(LOG_DEBUG, "ldap_simple_bind_s(\"%s\",%s) (uri=\"%s\")",
Packit 6bd9ab
            nslcd_cfg->binddn, nslcd_cfg->bindpw ? "\"***\"" : "NULL",
Packit 6bd9ab
            uri);
Packit 6bd9ab
  else
Packit 6bd9ab
    log_log(LOG_DEBUG, "ldap_simple_bind_s(NULL,%s) (uri=\"%s\")",
Packit 6bd9ab
            nslcd_cfg->bindpw ? "\"***\"" : "NULL", uri);
Packit 6bd9ab
  return ldap_simple_bind_s(ld, nslcd_cfg->binddn, nslcd_cfg->bindpw);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
#ifdef HAVE_LDAP_SET_REBIND_PROC
Packit 6bd9ab
/* This function is called by the LDAP library when chasing referrals.
Packit 6bd9ab
   It is configured with the ldap_set_rebind_proc() below. */
Packit 6bd9ab
#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
Packit 6bd9ab
static int do_rebind(LDAP *ld, LDAP_CONST char *url,
Packit 6bd9ab
                     ber_tag_t UNUSED(request),
Packit 6bd9ab
                     ber_int_t UNUSED(msgid), void *arg)
Packit 6bd9ab
{
Packit 6bd9ab
  MYLDAP_SESSION *session = (MYLDAP_SESSION *)arg;
Packit 6bd9ab
  log_log(LOG_DEBUG, "rebinding to %s", url);
Packit 6bd9ab
  return do_bind(session, ld, url);
Packit 6bd9ab
}
Packit 6bd9ab
#else /* not recent OpenLDAP */
Packit 6bd9ab
static int do_rebind(LDAP *ld, char **dnp, char **passwdp, int *authmethodp,
Packit 6bd9ab
                     int freeit, void *arg)
Packit 6bd9ab
{
Packit 6bd9ab
  MYLDAP_SESSION *session = (MYLDAP_SESSION *)arg;
Packit 6bd9ab
  if (freeit)
Packit 6bd9ab
  {
Packit 6bd9ab
    free(*dnp);
Packit 6bd9ab
    memset(*passwdp, 0, strlen(*passwdp));
Packit 6bd9ab
    free(*passwdp);
Packit 6bd9ab
  }
Packit 6bd9ab
  else
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_DEBUG, "rebinding");
Packit 6bd9ab
    *dnp = strdup(session->binddn);
Packit 6bd9ab
    *passwdp = strdup(session->bindpw);
Packit 6bd9ab
    *authmethodp = LDAP_AUTH_SIMPLE;
Packit 6bd9ab
    if ((*dnp == NULL) || (*passwdp == NULL))
Packit 6bd9ab
    {
Packit 6bd9ab
      if (*dnp != NULL)
Packit 6bd9ab
        free(*dnp);
Packit 6bd9ab
      log_log(LOG_CRIT, "do_rebind(): strdup() failed to allocate memory");
Packit 6bd9ab
      return LDAP_NO_MEMORY;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  return LDAP_SUCCESS;
Packit 6bd9ab
}
Packit 6bd9ab
#endif /* not recent OpenLDAP */
Packit 6bd9ab
#endif /* HAVE_LDAP_SET_REBIND_PROC */
Packit 6bd9ab
Packit 6bd9ab
/* set a recieve and send timeout on a socket */
Packit 6bd9ab
static int set_socket_timeout(LDAP *ld, time_t sec, suseconds_t usec)
Packit 6bd9ab
{
Packit 6bd9ab
  struct timeval tv;
Packit 6bd9ab
  int rc = LDAP_SUCCESS;
Packit 6bd9ab
  int sd;
Packit 6bd9ab
  log_log(LOG_DEBUG, "set_socket_timeout(%lu,%lu)",
Packit 6bd9ab
          (unsigned long)sec, (unsigned long)usec);
Packit 6bd9ab
  /* get the socket */
Packit 6bd9ab
  if ((rc = ldap_get_option(ld, LDAP_OPT_DESC, &sd)) != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    myldap_err(LOG_ERR, ld, rc, "ldap_get_option(LDAP_OPT_DESC) failed");
Packit 6bd9ab
    return rc;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* ignore invalid (probably closed) file descriptors */
Packit 6bd9ab
  if (sd <= 0)
Packit 6bd9ab
    return LDAP_SUCCESS;
Packit 6bd9ab
  /* set timeouts */
Packit 6bd9ab
  memset(&tv, 0, sizeof(tv));
Packit 6bd9ab
  tv.tv_sec = sec;
Packit 6bd9ab
  tv.tv_usec = usec;
Packit 6bd9ab
  if (setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv)))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "setsockopt(%d,SO_RCVTIMEO) failed: %s",
Packit 6bd9ab
            sd, strerror(errno));
Packit 6bd9ab
    rc = LDAP_LOCAL_ERROR;
Packit 6bd9ab
  }
Packit 6bd9ab
  if (setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, (void *)&tv, sizeof(tv)))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "setsockopt(%d,SO_RCVTIMEO) failed: %s",
Packit 6bd9ab
            sd, strerror(errno));
Packit 6bd9ab
    rc = LDAP_LOCAL_ERROR;
Packit 6bd9ab
  }
Packit 6bd9ab
  return rc;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
#ifdef LDAP_OPT_CONNECT_CB
Packit 6bd9ab
/* This function is called by the LDAP library once a connection was made to the server. We
Packit 6bd9ab
   set a timeout on the socket here, to catch network timeouts during the ssl
Packit 6bd9ab
   handshake phase. It is configured with LDAP_OPT_CONNECT_CB. */
Packit 6bd9ab
static int connect_cb(LDAP *ld, Sockbuf UNUSED(*sb),
Packit 6bd9ab
                      LDAPURLDesc UNUSED(*srv), struct sockaddr UNUSED(*addr),
Packit 6bd9ab
                      struct ldap_conncb UNUSED(*ctx))
Packit 6bd9ab
{
Packit 6bd9ab
  /* set timeout options on socket to avoid hang in some cases (a little
Packit 6bd9ab
     more than the normal timeout so this should only be triggered in cases
Packit 6bd9ab
     where the library behaves incorrectly) */
Packit 6bd9ab
  if (nslcd_cfg->timelimit)
Packit 6bd9ab
    set_socket_timeout(ld, nslcd_cfg->timelimit, 500000);
Packit 6bd9ab
  return LDAP_SUCCESS;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* We have an empty disconnect callback because LDAP_OPT_CONNECT_CB expects
Packit 6bd9ab
   both functions to be available. */
Packit 6bd9ab
static void disconnect_cb(LDAP UNUSED(*ld), Sockbuf UNUSED(*sb),
Packit 6bd9ab
                          struct ldap_conncb UNUSED(*ctx))
Packit 6bd9ab
{
Packit 6bd9ab
}
Packit 6bd9ab
#endif /* LDAP_OPT_CONNECT_CB */
Packit 6bd9ab
Packit 6bd9ab
/* This function sets a number of properties on the connection, based
Packit 6bd9ab
   what is configured in the configfile. This function returns an
Packit 6bd9ab
   LDAP status code. */
Packit 6bd9ab
static int do_set_options(MYLDAP_SESSION *session)
Packit 6bd9ab
{
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  struct timeval tv;
Packit 6bd9ab
#ifdef LDAP_OPT_CONNECT_CB
Packit 6bd9ab
  /* make this static because OpenLDAP doesn't make its own copy */
Packit 6bd9ab
  static struct ldap_conncb cb;
Packit 6bd9ab
#endif /* LDAP_OPT_CONNECT_CB */
Packit 6bd9ab
#ifdef LDAP_OPT_X_TLS
Packit 6bd9ab
  int i;
Packit 6bd9ab
#endif /* LDAP_OPT_X_TLS */
Packit 6bd9ab
#ifdef HAVE_LDAP_SET_REBIND_PROC
Packit 6bd9ab
  /* the rebind function that is called when chasing referrals, see
Packit 6bd9ab
     http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/apis/ldap_set_rebind_proc.htm
Packit 6bd9ab
     http://www.openldap.org/software/man.cgi?query=ldap_set_rebind_proc&manpath=OpenLDAP+2.4-Release */
Packit 6bd9ab
  /* TODO: probably only set this if we should chase referrals */
Packit 6bd9ab
  log_log(LOG_DEBUG, "ldap_set_rebind_proc()");
Packit 6bd9ab
#ifndef LDAP_SET_REBIND_PROC_RETURNS_VOID /* it returns int */
Packit 6bd9ab
  rc = ldap_set_rebind_proc(session->ld, do_rebind, session);
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    myldap_err(LOG_ERR, session->ld, rc, "ldap_set_rebind_proc() failed");
Packit 6bd9ab
    return rc;
Packit 6bd9ab
  }
Packit 6bd9ab
#else /* ldap_set_rebind_proc() returns void */
Packit 6bd9ab
  ldap_set_rebind_proc(session->ld, do_rebind, session);
Packit 6bd9ab
#endif
Packit 6bd9ab
#endif /* HAVE_LDAP_SET_REBIND_PROC */
Packit 6bd9ab
  /* set the protocol version to use */
Packit 6bd9ab
  log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_PROTOCOL_VERSION,%d)",
Packit 6bd9ab
          nslcd_cfg->ldap_version);
Packit 6bd9ab
  LDAP_SET_OPTION(session->ld, LDAP_OPT_PROTOCOL_VERSION,
Packit 6bd9ab
                  &nslcd_cfg->ldap_version);
Packit 6bd9ab
  /* set some other options */
Packit 6bd9ab
  log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_DEREF,%d)",
Packit 6bd9ab
          nslcd_cfg->deref);
Packit 6bd9ab
  LDAP_SET_OPTION(session->ld, LDAP_OPT_DEREF, &nslcd_cfg->deref);
Packit 6bd9ab
  log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_TIMELIMIT,%d)",
Packit 6bd9ab
          nslcd_cfg->timelimit);
Packit 6bd9ab
  LDAP_SET_OPTION(session->ld, LDAP_OPT_TIMELIMIT, &nslcd_cfg->timelimit);
Packit 6bd9ab
  tv.tv_sec = nslcd_cfg->bind_timelimit;
Packit 6bd9ab
  tv.tv_usec = 0;
Packit 6bd9ab
#ifdef LDAP_OPT_TIMEOUT
Packit 6bd9ab
  log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_TIMEOUT,%d)",
Packit 6bd9ab
          nslcd_cfg->timelimit);
Packit 6bd9ab
  LDAP_SET_OPTION(session->ld, LDAP_OPT_TIMEOUT, &tv;;
Packit 6bd9ab
#endif /* LDAP_OPT_TIMEOUT */
Packit 6bd9ab
#ifdef LDAP_OPT_NETWORK_TIMEOUT
Packit 6bd9ab
  log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT,%d)",
Packit 6bd9ab
          nslcd_cfg->timelimit);
Packit 6bd9ab
  LDAP_SET_OPTION(session->ld, LDAP_OPT_NETWORK_TIMEOUT, &tv;;
Packit 6bd9ab
#endif /* LDAP_OPT_NETWORK_TIMEOUT */
Packit 6bd9ab
#ifdef LDAP_X_OPT_CONNECT_TIMEOUT
Packit 6bd9ab
  log_log(LOG_DEBUG, "ldap_set_option(LDAP_X_OPT_CONNECT_TIMEOUT,%d)",
Packit 6bd9ab
          nslcd_cfg->timelimit);
Packit 6bd9ab
  LDAP_SET_OPTION(session->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &tv;;
Packit 6bd9ab
#endif /* LDAP_X_OPT_CONNECT_TIMEOUT */
Packit 6bd9ab
  log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_REFERRALS,%s)",
Packit 6bd9ab
          nslcd_cfg->referrals ? "LDAP_OPT_ON" : "LDAP_OPT_OFF");
Packit 6bd9ab
  LDAP_SET_OPTION(session->ld, LDAP_OPT_REFERRALS,
Packit 6bd9ab
                  nslcd_cfg->referrals ? LDAP_OPT_ON : LDAP_OPT_OFF);
Packit 6bd9ab
  log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_RESTART,LDAP_OPT_ON)");
Packit 6bd9ab
  LDAP_SET_OPTION(session->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
Packit 6bd9ab
#ifdef LDAP_OPT_CONNECT_CB
Packit 6bd9ab
  /* register a connection callback */
Packit 6bd9ab
  cb.lc_add = connect_cb;
Packit 6bd9ab
  cb.lc_del = disconnect_cb;
Packit 6bd9ab
  cb.lc_arg = NULL;
Packit 6bd9ab
  LDAP_SET_OPTION(session->ld, LDAP_OPT_CONNECT_CB, (void *)&cb;;
Packit 6bd9ab
#endif /* LDAP_OPT_CONNECT_CB */
Packit 6bd9ab
#ifdef LDAP_OPT_X_TLS
Packit 6bd9ab
  /* if SSL is desired, then enable it */
Packit 6bd9ab
  if ((nslcd_cfg->ssl == SSL_LDAPS) ||
Packit 6bd9ab
      (strncasecmp(nslcd_cfg->uris[session->current_uri].uri, "ldaps://", 8) == 0))
Packit 6bd9ab
  {
Packit 6bd9ab
    /* use tls */
Packit 6bd9ab
    i = LDAP_OPT_X_TLS_HARD;
Packit 6bd9ab
    log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_TLS,LDAP_OPT_X_TLS_HARD)");
Packit 6bd9ab
    LDAP_SET_OPTION(session->ld, LDAP_OPT_X_TLS, &i);
Packit 6bd9ab
  }
Packit 6bd9ab
#endif /* LDAP_OPT_X_TLS */
Packit 6bd9ab
#ifdef LDAP_OPT_X_SASL_NOCANON
Packit 6bd9ab
  if (nslcd_cfg->sasl_canonicalize >= 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_X_SASL_NOCANON,%s)",
Packit 6bd9ab
            nslcd_cfg->sasl_canonicalize ? "LDAP_OPT_OFF" : "LDAP_OPT_ON");
Packit 6bd9ab
    LDAP_SET_OPTION(session->ld, LDAP_OPT_X_SASL_NOCANON,
Packit 6bd9ab
                    nslcd_cfg->sasl_canonicalize ? LDAP_OPT_OFF : LDAP_OPT_ON);
Packit 6bd9ab
  }
Packit 6bd9ab
#endif /* LDAP_OPT_X_SASL_NOCANON */
Packit 6bd9ab
  /* if nothing above failed, everything should be fine */
Packit 6bd9ab
  return LDAP_SUCCESS;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* close the connection to the server and invalidate any running searches */
Packit 6bd9ab
static void do_close(MYLDAP_SESSION *session)
Packit 6bd9ab
{
Packit 6bd9ab
  int i;
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  time_t sec;
Packit 6bd9ab
  /* if we had reachability problems with the server close the connection */
Packit 6bd9ab
  if (session->ld != NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* set timeout options on socket to avoid hang in some cases
Packit 6bd9ab
       (we set a short timeout because we don't care too much about properly
Packit 6bd9ab
       shutting down the connection) */
Packit 6bd9ab
    if (nslcd_cfg->timelimit)
Packit 6bd9ab
    {
Packit 6bd9ab
      sec = nslcd_cfg->timelimit / 2;
Packit 6bd9ab
      if (!sec)
Packit 6bd9ab
        sec = 1;
Packit 6bd9ab
      set_socket_timeout(session->ld, sec, 0);
Packit 6bd9ab
    }
Packit 6bd9ab
    /* go over the other searches and partially close them */
Packit 6bd9ab
    for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
Packit 6bd9ab
    {
Packit 6bd9ab
      if (session->searches[i] != NULL)
Packit 6bd9ab
      {
Packit 6bd9ab
        /* free any messages (because later ld is no longer valid) */
Packit 6bd9ab
        if (session->searches[i]->msg != NULL)
Packit 6bd9ab
        {
Packit 6bd9ab
          ldap_msgfree(session->searches[i]->msg);
Packit 6bd9ab
          session->searches[i]->msg = NULL;
Packit 6bd9ab
        }
Packit 6bd9ab
        /* abandon the search if there were more results to fetch */
Packit 6bd9ab
        if (session->searches[i]->msgid != -1)
Packit 6bd9ab
        {
Packit 6bd9ab
          log_log(LOG_DEBUG, "ldap_abandon()");
Packit 6bd9ab
          if (ldap_abandon(session->searches[i]->session->ld, session->searches[i]->msgid))
Packit 6bd9ab
          {
Packit 6bd9ab
            if (ldap_get_option(session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
Packit 6bd9ab
              rc = LDAP_OTHER;
Packit 6bd9ab
            myldap_err(LOG_WARNING, session->ld, rc,
Packit 6bd9ab
                       "ldap_abandon() failed to abandon search");
Packit 6bd9ab
          }
Packit 6bd9ab
          session->searches[i]->msgid = -1;
Packit 6bd9ab
        }
Packit 6bd9ab
        /* flag the search as invalid */
Packit 6bd9ab
        session->searches[i]->valid = 0;
Packit 6bd9ab
      }
Packit 6bd9ab
    }
Packit 6bd9ab
    /* close the connection to the server */
Packit 6bd9ab
    log_log(LOG_DEBUG, "ldap_unbind()");
Packit 6bd9ab
    rc = ldap_unbind(session->ld);
Packit 6bd9ab
    session->ld = NULL;
Packit 6bd9ab
    if (rc != LDAP_SUCCESS)
Packit 6bd9ab
      myldap_err(LOG_WARNING, session->ld, rc, "ldap_unbind() failed");
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
void myldap_session_check(MYLDAP_SESSION *session)
Packit 6bd9ab
{
Packit 6bd9ab
  int i;
Packit 6bd9ab
  time_t current_time;
Packit 6bd9ab
  int sd;
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  struct sockaddr sa;
Packit 6bd9ab
  socklen_t salen = sizeof(sa);
Packit 6bd9ab
  /* check parameters */
Packit 6bd9ab
  if (session == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_session_check(): invalid parameter passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return;
Packit 6bd9ab
  }
Packit 6bd9ab
  if (session->ld != NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    rc = ldap_get_option(session->ld, LDAP_OPT_DESC, &sd);
Packit 6bd9ab
    if (rc != LDAP_SUCCESS)
Packit 6bd9ab
    {
Packit 6bd9ab
      myldap_err(LOG_WARNING, session->ld, rc,
Packit 6bd9ab
                 "ldap_get_option(LDAP_OPT_DESC) failed (ignored)");
Packit 6bd9ab
    }
Packit 6bd9ab
    else
Packit 6bd9ab
    {
Packit 6bd9ab
      /* check if the connection was closed by the peer */
Packit 6bd9ab
      if (getpeername(sd, &sa, &salen) == -1)
Packit 6bd9ab
      {
Packit 6bd9ab
        if (errno == ENOTCONN)
Packit 6bd9ab
        {
Packit 6bd9ab
          log_log(LOG_DEBUG, "myldap_session_check(): connection reset by peer");
Packit 6bd9ab
          do_close(session);
Packit 6bd9ab
          return;
Packit 6bd9ab
        }
Packit 6bd9ab
      }
Packit 6bd9ab
    }
Packit 6bd9ab
    /* check if we should time out the connection */
Packit 6bd9ab
    if (nslcd_cfg->idle_timelimit > 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      /* if we have any running searches, don't time out */
Packit 6bd9ab
      for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
Packit 6bd9ab
        if ((session->searches[i] != NULL) && (session->searches[i]->valid))
Packit 6bd9ab
          return;
Packit 6bd9ab
      /* consider timeout (there are no running searches) */
Packit 6bd9ab
      time(&current_time);
Packit 6bd9ab
      if ((session->lastactivity + nslcd_cfg->idle_timelimit) < current_time)
Packit 6bd9ab
      {
Packit 6bd9ab
        log_log(LOG_DEBUG, "myldap_session_check(): idle_timelimit reached");
Packit 6bd9ab
        do_close(session);
Packit 6bd9ab
      }
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* This opens connection to an LDAP server, sets all connection options
Packit 6bd9ab
   and binds to the server. This returns an LDAP status code. */
Packit 6bd9ab
static int do_open(MYLDAP_SESSION *session)
Packit 6bd9ab
{
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  /* if the connection is still there (ie. ldap_unbind() wasn't
Packit 6bd9ab
     called) then we can return the cached connection */
Packit 6bd9ab
  if (session->ld != NULL)
Packit 6bd9ab
    return LDAP_SUCCESS;
Packit 6bd9ab
  /* we should build a new session now */
Packit 6bd9ab
  session->ld = NULL;
Packit 6bd9ab
  session->lastactivity = 0;
Packit 6bd9ab
  /* open the connection */
Packit 6bd9ab
  log_log(LOG_DEBUG, "ldap_initialize(%s)",
Packit 6bd9ab
          nslcd_cfg->uris[session->current_uri].uri);
Packit 6bd9ab
  errno = 0;
Packit 6bd9ab
  rc = ldap_initialize(&(session->ld), nslcd_cfg->uris[session->current_uri].uri);
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    myldap_err(LOG_WARNING, session->ld, rc, "ldap_initialize(%s) failed",
Packit 6bd9ab
               nslcd_cfg->uris[session->current_uri].uri);
Packit 6bd9ab
    if (session->ld != NULL)
Packit 6bd9ab
      do_close(session);
Packit 6bd9ab
    return rc;
Packit 6bd9ab
  }
Packit 6bd9ab
  else if (session->ld == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_WARNING, "ldap_initialize() returned NULL");
Packit 6bd9ab
    return LDAP_LOCAL_ERROR;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* set the options for the connection */
Packit 6bd9ab
  rc = do_set_options(session);
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    do_close(session);
Packit 6bd9ab
    return rc;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* bind to the server */
Packit 6bd9ab
  errno = 0;
Packit 6bd9ab
  rc = do_bind(session, session->ld, nslcd_cfg->uris[session->current_uri].uri);
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* log actual LDAP error code */
Packit 6bd9ab
    myldap_err((session->binddn[0] == '\0') ? LOG_WARNING : LOG_DEBUG,
Packit 6bd9ab
               session->ld, rc, "failed to bind to LDAP server %s",
Packit 6bd9ab
               nslcd_cfg->uris[session->current_uri].uri);
Packit 6bd9ab
    do_close(session);
Packit 6bd9ab
    return rc;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* update last activity and finish off state */
Packit 6bd9ab
  time(&(session->lastactivity));
Packit 6bd9ab
  return LDAP_SUCCESS;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* Perform a simple bind operation and return the ppolicy results. */
Packit 6bd9ab
int myldap_bind(MYLDAP_SESSION *session, const char *dn, const char *password,
Packit 6bd9ab
                int *response, const char **message)
Packit 6bd9ab
{
Packit 6bd9ab
  MYLDAP_SEARCH *search;
Packit 6bd9ab
  static const char *attrs[2];
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  /* error out when buffers are too small */
Packit 6bd9ab
  if (strlen(dn) >= sizeof(session->binddn))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_bind(): binddn buffer too small (%lu required)",
Packit 6bd9ab
            (unsigned long) strlen(dn));
Packit 6bd9ab
    return LDAP_LOCAL_ERROR;
Packit 6bd9ab
  }
Packit 6bd9ab
  if (strlen(password) >= sizeof(session->bindpw))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_bind(): bindpw buffer too small (%lu required)",
Packit 6bd9ab
            (unsigned long) strlen(password));
Packit 6bd9ab
    return LDAP_LOCAL_ERROR;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* copy dn and password into session */
Packit 6bd9ab
  strncpy(session->binddn, dn, sizeof(session->binddn));
Packit 6bd9ab
  session->binddn[sizeof(session->binddn) - 1] = '\0';
Packit 6bd9ab
  strncpy(session->bindpw, password, sizeof(session->bindpw));
Packit 6bd9ab
  session->bindpw[sizeof(session->bindpw) - 1] = '\0';
Packit 6bd9ab
  /* construct a fake search to trigger the BIND operation */
Packit 6bd9ab
  attrs[0] = "dn";
Packit 6bd9ab
  attrs[1] = NULL;
Packit 6bd9ab
  search = myldap_search(session, session->binddn, MYLDAP_SCOPE_BINDONLY,
Packit 6bd9ab
                         "(objectClass=*)", attrs, &rc);
Packit 6bd9ab
  if (search != NULL)
Packit 6bd9ab
    myldap_search_close(search);
Packit 6bd9ab
  /* return ppolicy results */
Packit 6bd9ab
  if (response != NULL)
Packit 6bd9ab
    *response = session->policy_response;
Packit 6bd9ab
  if (message != NULL)
Packit 6bd9ab
    *message = session->policy_message;
Packit 6bd9ab
  return rc;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* perform a search operation, the connection is assumed to be open */
Packit 6bd9ab
static int do_try_search(MYLDAP_SEARCH *search)
Packit 6bd9ab
{
Packit 6bd9ab
  int ctrlidx = 0;
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  LDAPControl *serverctrls[3];
Packit 6bd9ab
#ifdef HAVE_LDAP_CREATE_DEREF_CONTROL
Packit 6bd9ab
  int i;
Packit 6bd9ab
  struct LDAPDerefSpec ds[2];
Packit 6bd9ab
  char *deref_attrs[2];
Packit 6bd9ab
#endif /* HAVE_LDAP_CREATE_DEREF_CONTROL */
Packit 6bd9ab
  int msgid;
Packit 6bd9ab
  /* if we're using paging, build a page control */
Packit 6bd9ab
  if ((nslcd_cfg->pagesize > 0) && (search->scope != LDAP_SCOPE_BASE))
Packit 6bd9ab
  {
Packit 6bd9ab
    rc = ldap_create_page_control(search->session->ld, nslcd_cfg->pagesize,
Packit 6bd9ab
                                  search->cookie, 0, &serverctrls[ctrlidx]);
Packit 6bd9ab
    if (rc == LDAP_SUCCESS)
Packit 6bd9ab
      ctrlidx++;
Packit 6bd9ab
    else
Packit 6bd9ab
    {
Packit 6bd9ab
      myldap_err(LOG_WARNING, search->session->ld, rc,
Packit 6bd9ab
                 "ldap_create_page_control() failed");
Packit 6bd9ab
      serverctrls[ctrlidx] = NULL;
Packit 6bd9ab
      /* if we were paging, failure building the second control is fatal */
Packit 6bd9ab
      if (search->cookie != NULL)
Packit 6bd9ab
        return rc;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
#ifdef HAVE_LDAP_CREATE_DEREF_CONTROL
Packit 6bd9ab
  /* if doing group searches, add deref control to search request
Packit 6bd9ab
     (this is currently a bit of a hack and hard-coded for group searches
Packit 6bd9ab
     which are detected by requesting the attmap_group_member member
Packit 6bd9ab
     attribute) */
Packit 6bd9ab
  for (i = 0; search->attrs[i] != NULL; i++)
Packit 6bd9ab
    if (strcasecmp(search->attrs[i], attmap_group_member) == 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      /* attributes from dereff'd entries */
Packit 6bd9ab
      deref_attrs[0] = (void *)attmap_passwd_uid;
Packit 6bd9ab
      deref_attrs[1] = NULL;
Packit 6bd9ab
      /* build deref control */
Packit 6bd9ab
      ds[0].derefAttr = (void *)attmap_group_member;
Packit 6bd9ab
      ds[0].attributes = deref_attrs;
Packit 6bd9ab
      ds[1].derefAttr = NULL;
Packit 6bd9ab
      ds[1].attributes = NULL;
Packit 6bd9ab
      rc = ldap_create_deref_control(search->session->ld, ds, 0, &serverctrls[ctrlidx]);
Packit 6bd9ab
      if (rc == LDAP_SUCCESS)
Packit 6bd9ab
        ctrlidx++;
Packit 6bd9ab
      else
Packit 6bd9ab
      {
Packit 6bd9ab
        myldap_err(LOG_WARNING, search->session->ld, rc,
Packit 6bd9ab
                   "ldap_create_deref_control() failed");
Packit 6bd9ab
        serverctrls[ctrlidx] = NULL;
Packit 6bd9ab
      }
Packit 6bd9ab
    }
Packit 6bd9ab
#endif /* HAVE_LDAP_CREATE_DEREF_CONTROL */
Packit 6bd9ab
  /* NULL terminate control list */
Packit 6bd9ab
  serverctrls[ctrlidx] = NULL;
Packit 6bd9ab
  /* clear error flag (perhaps control setting failed) */
Packit 6bd9ab
  if (ctrlidx > 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    rc = LDAP_SUCCESS;
Packit 6bd9ab
    if (ldap_set_option(search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
Packit 6bd9ab
      log_log(LOG_WARNING, "failed to clear the error flag");
Packit 6bd9ab
  }
Packit 6bd9ab
  /* perform the search */
Packit 6bd9ab
  rc = ldap_search_ext(search->session->ld, search->base, search->scope,
Packit 6bd9ab
                       search->filter, (char **)(search->attrs),
Packit 6bd9ab
                       0, serverctrls[0] == NULL ? NULL : serverctrls,
Packit 6bd9ab
                       NULL, NULL, LDAP_NO_LIMIT, &msgid);
Packit 6bd9ab
  /* free the controls if we had them */
Packit 6bd9ab
  for (ctrlidx = 0; serverctrls[ctrlidx] != NULL; ctrlidx++)
Packit 6bd9ab
    ldap_control_free(serverctrls[ctrlidx]);
Packit 6bd9ab
  /* handle errors */
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_search_ext() failed");
Packit 6bd9ab
    return rc;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* update the last activity on the connection */
Packit 6bd9ab
  time(&(search->session->lastactivity));
Packit 6bd9ab
  /* save msgid */
Packit 6bd9ab
  search->msgid = msgid;
Packit 6bd9ab
  /* return the new search */
Packit 6bd9ab
  return LDAP_SUCCESS;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
MYLDAP_SESSION *myldap_create_session(void)
Packit 6bd9ab
{
Packit 6bd9ab
  return myldap_session_new();
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
void myldap_session_cleanup(MYLDAP_SESSION *session)
Packit 6bd9ab
{
Packit 6bd9ab
  int i;
Packit 6bd9ab
  /* check parameter */
Packit 6bd9ab
  if (session == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_session_cleanup(): invalid session passed");
Packit 6bd9ab
    return;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* go over all searches in the session and close them */
Packit 6bd9ab
  for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
Packit 6bd9ab
  {
Packit 6bd9ab
    if (session->searches[i] != NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      myldap_search_close(session->searches[i]);
Packit 6bd9ab
      session->searches[i] = NULL;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
void myldap_session_close(MYLDAP_SESSION *session)
Packit 6bd9ab
{
Packit 6bd9ab
  /* check parameter */
Packit 6bd9ab
  if (session == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_session_cleanup(): invalid session passed");
Packit 6bd9ab
    return;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* close pending searches */
Packit 6bd9ab
  myldap_session_cleanup(session);
Packit 6bd9ab
  /* close any open connections */
Packit 6bd9ab
  do_close(session);
Packit 6bd9ab
  /* free allocated memory */
Packit 6bd9ab
  memset(session->bindpw, 0, sizeof(session->bindpw));
Packit 6bd9ab
  free(session);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* mutex for updating the times in the uri */
Packit 6bd9ab
pthread_mutex_t uris_mutex = PTHREAD_MUTEX_INITIALIZER;
Packit 6bd9ab
Packit 6bd9ab
static int do_retry_search(MYLDAP_SEARCH *search)
Packit 6bd9ab
{
Packit 6bd9ab
  int sleeptime = 0;
Packit 6bd9ab
  int start_uri;
Packit 6bd9ab
  time_t endtime;
Packit 6bd9ab
  time_t nexttry;
Packit 6bd9ab
  time_t t;
Packit 6bd9ab
  int rc = LDAP_UNAVAILABLE;
Packit 6bd9ab
  struct myldap_uri *current_uri;
Packit 6bd9ab
  int dotry[NSS_LDAP_CONFIG_MAX_URIS];
Packit 6bd9ab
  int do_invalidate = 0;
Packit 6bd9ab
  /* clear time stamps */
Packit 6bd9ab
  for (start_uri = 0; start_uri < NSS_LDAP_CONFIG_MAX_URIS; start_uri++)
Packit 6bd9ab
    dotry[start_uri] = 1;
Packit 6bd9ab
  /* keep trying until we time out */
Packit 6bd9ab
  endtime = time(NULL) + nslcd_cfg->reconnect_retrytime;
Packit 6bd9ab
  while (1)
Packit 6bd9ab
  {
Packit 6bd9ab
    nexttry = endtime;
Packit 6bd9ab
    /* try each configured URL once */
Packit 6bd9ab
    pthread_mutex_lock(&uris_mutex);
Packit 6bd9ab
    start_uri = search->session->current_uri;
Packit 6bd9ab
    do
Packit 6bd9ab
    {
Packit 6bd9ab
      current_uri = &(nslcd_cfg->uris[search->session->current_uri]);
Packit 6bd9ab
      /* only try this URI if we should */
Packit 6bd9ab
      if (!dotry[search->session->current_uri])
Packit 6bd9ab
      { /* skip this URI */ }
Packit 6bd9ab
      else if ((current_uri->lastfail > (current_uri->firstfail + nslcd_cfg->reconnect_retrytime)) &&
Packit 6bd9ab
               ((t = time(NULL)) < (current_uri->lastfail + nslcd_cfg->reconnect_retrytime)))
Packit 6bd9ab
      {
Packit 6bd9ab
        /* we are in a hard fail state and have retried not long ago */
Packit 6bd9ab
        log_log(LOG_DEBUG, "not retrying server %s which failed just %d second(s) ago and has been failing for %d seconds",
Packit 6bd9ab
                current_uri->uri, (int)(t - current_uri->lastfail),
Packit 6bd9ab
                (int)(t - current_uri->firstfail));
Packit 6bd9ab
        dotry[search->session->current_uri] = 0;
Packit 6bd9ab
      }
Packit 6bd9ab
      else
Packit 6bd9ab
      {
Packit 6bd9ab
        /* try to start the search */
Packit 6bd9ab
        pthread_mutex_unlock(&uris_mutex);
Packit 6bd9ab
        /* ensure that we have an open connection and start a search */
Packit 6bd9ab
        rc = do_open(search->session);
Packit 6bd9ab
        /* perform the actual search, unless we were only binding */
Packit 6bd9ab
        if ((rc == LDAP_SUCCESS) && (search->scope != MYLDAP_SCOPE_BINDONLY))
Packit 6bd9ab
          rc = do_try_search(search);
Packit 6bd9ab
        /* if we are authenticating a user and get an error regarding failed
Packit 6bd9ab
           password we should error out instead of trying all servers */
Packit 6bd9ab
        if ((search->session->binddn[0] != '\0') && (rc == LDAP_INVALID_CREDENTIALS))
Packit 6bd9ab
        {
Packit 6bd9ab
          do_close(search->session);
Packit 6bd9ab
          return rc;
Packit 6bd9ab
        }
Packit 6bd9ab
        if (rc == LDAP_SUCCESS)
Packit 6bd9ab
        {
Packit 6bd9ab
          pthread_mutex_lock(&uris_mutex);
Packit 6bd9ab
          /* check if we are coming back from an error */
Packit 6bd9ab
          if ((current_uri->lastfail > 0) || (search->session->current_uri != start_uri))
Packit 6bd9ab
          {
Packit 6bd9ab
            log_log(LOG_INFO, "connected to LDAP server %s", current_uri->uri);
Packit 6bd9ab
            do_invalidate = 1;
Packit 6bd9ab
          }
Packit 6bd9ab
          if (first_search)
Packit 6bd9ab
          {
Packit 6bd9ab
            do_invalidate = 1;
Packit 6bd9ab
            first_search = 0;
Packit 6bd9ab
          }
Packit 6bd9ab
          /* update ok time */
Packit 6bd9ab
          current_uri->firstfail = 0;
Packit 6bd9ab
          current_uri->lastfail = 0;
Packit 6bd9ab
          pthread_mutex_unlock(&uris_mutex);
Packit 6bd9ab
          /* flag the search as valid */
Packit 6bd9ab
          search->valid = 1;
Packit 6bd9ab
          /* signal external invalidation of configured caches */
Packit 6bd9ab
          if (do_invalidate)
Packit 6bd9ab
            invalidator_do(LM_NONE);
Packit 6bd9ab
          return LDAP_SUCCESS;
Packit 6bd9ab
        }
Packit 6bd9ab
        /* close the current connection */
Packit 6bd9ab
        do_close(search->session);
Packit 6bd9ab
        /* update time of failure and figure out when we should retry */
Packit 6bd9ab
        pthread_mutex_lock(&uris_mutex);
Packit 6bd9ab
        t = time(NULL);
Packit 6bd9ab
        /* update timestaps unless we are doing an authentication search */
Packit 6bd9ab
        if (search->session->binddn[0] == '\0')
Packit 6bd9ab
        {
Packit 6bd9ab
          if (current_uri->firstfail == 0)
Packit 6bd9ab
            current_uri->firstfail = t;
Packit 6bd9ab
          current_uri->lastfail = t;
Packit 6bd9ab
        }
Packit 6bd9ab
        /* if it is one of these, retrying this URI is not going to help */
Packit 6bd9ab
        if ((rc == LDAP_INVALID_CREDENTIALS) || (rc == LDAP_INSUFFICIENT_ACCESS) ||
Packit 6bd9ab
            (rc == LDAP_AUTH_METHOD_NOT_SUPPORTED))
Packit 6bd9ab
          dotry[search->session->current_uri] = 0;
Packit 6bd9ab
        /* check when we should try this URI again */
Packit 6bd9ab
        else if (t <= (current_uri->firstfail + nslcd_cfg->reconnect_retrytime))
Packit 6bd9ab
        {
Packit 6bd9ab
          t += nslcd_cfg->reconnect_sleeptime;
Packit 6bd9ab
          if (t < nexttry)
Packit 6bd9ab
            nexttry = t;
Packit 6bd9ab
        }
Packit 6bd9ab
      }
Packit 6bd9ab
      /* try the next URI (with wrap-around) */
Packit 6bd9ab
      search->session->current_uri++;
Packit 6bd9ab
      if (nslcd_cfg->uris[search->session->current_uri].uri == NULL)
Packit 6bd9ab
        search->session->current_uri = 0;
Packit 6bd9ab
    }
Packit 6bd9ab
    while (search->session->current_uri != start_uri);
Packit 6bd9ab
    pthread_mutex_unlock(&uris_mutex);
Packit 6bd9ab
    /* see if it is any use sleeping */
Packit 6bd9ab
    if (nexttry >= endtime)
Packit 6bd9ab
    {
Packit 6bd9ab
      if (search->session->binddn[0] == '\0')
Packit 6bd9ab
        myldap_err(LOG_ERR, search->session->ld, rc, "no available LDAP server found");
Packit 6bd9ab
      return rc;
Packit 6bd9ab
    }
Packit 6bd9ab
    /* sleep between tries */
Packit 6bd9ab
    sleeptime = nexttry - time(NULL);
Packit 6bd9ab
    if (sleeptime > 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      log_log(LOG_WARNING, "no available LDAP server found, sleeping %d seconds",
Packit 6bd9ab
              sleeptime);
Packit 6bd9ab
      (void)sleep(sleeptime);
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* force quick retries of all failing LDAP servers */
Packit 6bd9ab
void myldap_immediate_reconnect(void)
Packit 6bd9ab
{
Packit 6bd9ab
  int i;
Packit 6bd9ab
  time_t t;
Packit 6bd9ab
  t = time(NULL) - nslcd_cfg->reconnect_retrytime;
Packit 6bd9ab
  pthread_mutex_lock(&uris_mutex);
Packit 6bd9ab
  for (i = 0; i < (NSS_LDAP_CONFIG_MAX_URIS + 1); i++)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* only adjust failing connections that are in a hard fail state */
Packit 6bd9ab
    if ((nslcd_cfg->uris[i].lastfail > t) &&
Packit 6bd9ab
        (nslcd_cfg->uris[i].lastfail > (nslcd_cfg->uris[i].firstfail + nslcd_cfg->reconnect_retrytime)))
Packit 6bd9ab
    {
Packit 6bd9ab
      /* move lastfail back to ensure quick retry */
Packit 6bd9ab
      log_log(LOG_DEBUG, "moving lastfail of %s %d second(s) back to force retry",
Packit 6bd9ab
              nslcd_cfg->uris[i].uri, (int)(nslcd_cfg->uris[i].lastfail - t));
Packit 6bd9ab
      nslcd_cfg->uris[i].lastfail = t;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  pthread_mutex_unlock(&uris_mutex);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
MYLDAP_SEARCH *myldap_search(MYLDAP_SESSION *session,
Packit 6bd9ab
                             const char *base, int scope, const char *filter,
Packit 6bd9ab
                             const char **attrs, int *rcp)
Packit 6bd9ab
{
Packit 6bd9ab
  MYLDAP_SEARCH *search;
Packit 6bd9ab
  int i;
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  /* check parameters */
Packit 6bd9ab
  if ((session == NULL) || (base == NULL) || (filter == NULL) || (attrs == NULL))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_search(): invalid parameter passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    if (rcp != NULL)
Packit 6bd9ab
      *rcp = LDAP_OPERATIONS_ERROR;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* log the call */
Packit 6bd9ab
  log_log(LOG_DEBUG, "myldap_search(base=\"%s\", filter=\"%s\")",
Packit 6bd9ab
          base, filter);
Packit 6bd9ab
  /* check if the idle time for the connection has expired */
Packit 6bd9ab
  myldap_session_check(session);
Packit 6bd9ab
  /* allocate a new search entry */
Packit 6bd9ab
  search = myldap_search_new(session, base, scope, filter, attrs);
Packit 6bd9ab
  /* find a place in the session where we can register our search */
Packit 6bd9ab
  for (i = 0; (i < MAX_SEARCHES_IN_SESSION) && (session->searches[i] != NULL); i++)
Packit 6bd9ab
    /* nothing */ ;
Packit 6bd9ab
  if (i >= MAX_SEARCHES_IN_SESSION)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_search(): too many searches registered with session (max %d)",
Packit 6bd9ab
            MAX_SEARCHES_IN_SESSION);
Packit 6bd9ab
    myldap_search_close(search);
Packit 6bd9ab
    if (rcp != NULL)
Packit 6bd9ab
      *rcp = LDAP_OPERATIONS_ERROR;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* register search with the session so we can free it later on */
Packit 6bd9ab
  session->searches[i] = search;
Packit 6bd9ab
  /* do the search with retries to all configured servers */
Packit 6bd9ab
  rc = do_retry_search(search);
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    myldap_search_close(search);
Packit 6bd9ab
    if (rcp != NULL)
Packit 6bd9ab
      *rcp = rc;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  if (rcp != NULL)
Packit 6bd9ab
    *rcp = LDAP_SUCCESS;
Packit 6bd9ab
  return search;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
void myldap_search_close(MYLDAP_SEARCH *search)
Packit 6bd9ab
{
Packit 6bd9ab
  int i;
Packit 6bd9ab
  if (search == NULL)
Packit 6bd9ab
    return;
Packit 6bd9ab
  /* free any messages */
Packit 6bd9ab
  if (search->msg != NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    ldap_msgfree(search->msg);
Packit 6bd9ab
    search->msg = NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* abandon the search if there were more results to fetch */
Packit 6bd9ab
  if ((search->session->ld != NULL) && (search->msgid != -1))
Packit 6bd9ab
  {
Packit 6bd9ab
    ldap_abandon(search->session->ld, search->msgid);
Packit 6bd9ab
    search->msgid = -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* find the reference to this search in the session */
Packit 6bd9ab
  for (i = 0; i < MAX_SEARCHES_IN_SESSION; i++)
Packit 6bd9ab
  {
Packit 6bd9ab
    if (search->session->searches[i] == search)
Packit 6bd9ab
      search->session->searches[i] = NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* free any search entries */
Packit 6bd9ab
  if (search->entry != NULL)
Packit 6bd9ab
    myldap_entry_free(search->entry);
Packit 6bd9ab
  /* clean up cookie */
Packit 6bd9ab
  if (search->cookie != NULL)
Packit 6bd9ab
    ber_bvfree(search->cookie);
Packit 6bd9ab
  /* free read messages */
Packit 6bd9ab
  if (search->msg != NULL)
Packit 6bd9ab
    ldap_msgfree(search->msg);
Packit 6bd9ab
  /* free the storage we allocated */
Packit 6bd9ab
  free(search);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search, int *rcp)
Packit 6bd9ab
{
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  int parserc;
Packit 6bd9ab
  struct timeval tv, *tvp;
Packit 6bd9ab
  LDAPControl **resultcontrols;
Packit 6bd9ab
  ber_int_t count;
Packit 6bd9ab
  /* check parameters */
Packit 6bd9ab
  if ((search == NULL) || (search->session == NULL) || (search->session->ld == NULL))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_get_entry(): invalid search passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    if (rcp != NULL)
Packit 6bd9ab
      *rcp = LDAP_OPERATIONS_ERROR;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* check if the connection wasn't closed in another search */
Packit 6bd9ab
  if (!search->valid)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_WARNING, "myldap_get_entry(): connection was closed");
Packit 6bd9ab
    /* retry the search */
Packit 6bd9ab
    if (search->may_retry_search)
Packit 6bd9ab
    {
Packit 6bd9ab
      log_log(LOG_DEBUG, "myldap_get_entry(): retry search");
Packit 6bd9ab
      search->may_retry_search = 0;
Packit 6bd9ab
      if (do_retry_search(search) == LDAP_SUCCESS)
Packit 6bd9ab
        return myldap_get_entry(search, rcp);
Packit 6bd9ab
    }
Packit 6bd9ab
    myldap_search_close(search);
Packit 6bd9ab
    if (rcp != NULL)
Packit 6bd9ab
      *rcp = LDAP_SERVER_DOWN;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* set up a timelimit value for operations */
Packit 6bd9ab
  if (nslcd_cfg->timelimit == LDAP_NO_LIMIT)
Packit 6bd9ab
    tvp = NULL;
Packit 6bd9ab
  else
Packit 6bd9ab
  {
Packit 6bd9ab
    tv.tv_sec = nslcd_cfg->timelimit;
Packit 6bd9ab
    tv.tv_usec = 0;
Packit 6bd9ab
    tvp = &tv;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* if we have an existing result entry, free it */
Packit 6bd9ab
  if (search->entry != NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    myldap_entry_free(search->entry);
Packit 6bd9ab
    search->entry = NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* try to parse results until we have a final error or ok */
Packit 6bd9ab
  while (1)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* free the previous message if there was any */
Packit 6bd9ab
    if (search->msg != NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      ldap_msgfree(search->msg);
Packit 6bd9ab
      search->msg = NULL;
Packit 6bd9ab
    }
Packit 6bd9ab
    /* get the next result */
Packit 6bd9ab
    rc = ldap_result(search->session->ld, search->msgid, LDAP_MSG_ONE, tvp,
Packit 6bd9ab
                     &(search->msg));
Packit 6bd9ab
    /* handle result */
Packit 6bd9ab
    switch (rc)
Packit 6bd9ab
    {
Packit 6bd9ab
      case LDAP_RES_SEARCH_ENTRY:
Packit 6bd9ab
        /* we have a normal search entry, update timestamp and return result */
Packit 6bd9ab
        time(&(search->session->lastactivity));
Packit 6bd9ab
        search->entry = myldap_entry_new(search);
Packit 6bd9ab
        if (rcp != NULL)
Packit 6bd9ab
          *rcp = LDAP_SUCCESS;
Packit 6bd9ab
        /* log the first couple of dns in the result (but not all, to
Packit 6bd9ab
           prevent swamping the log) */
Packit 6bd9ab
        if (search->count < MAX_DEBUG_LOG_DNS)
Packit 6bd9ab
          log_log(LOG_DEBUG, "ldap_result(): %s", myldap_get_dn(search->entry));
Packit 6bd9ab
        search->count++;
Packit 6bd9ab
        search->may_retry_search = 0;
Packit 6bd9ab
        return search->entry;
Packit 6bd9ab
      case LDAP_RES_SEARCH_RESULT:
Packit 6bd9ab
        /* we have a search result, parse it */
Packit 6bd9ab
        resultcontrols = NULL;
Packit 6bd9ab
        if (search->cookie != NULL)
Packit 6bd9ab
        {
Packit 6bd9ab
          ber_bvfree(search->cookie);
Packit 6bd9ab
          search->cookie = NULL;
Packit 6bd9ab
        }
Packit 6bd9ab
        /* NB: this frees search->msg */
Packit 6bd9ab
        parserc = ldap_parse_result(search->session->ld, search->msg, &rc,
Packit 6bd9ab
                                    NULL, NULL, NULL, &resultcontrols, 1);
Packit 6bd9ab
        search->msg = NULL;
Packit 6bd9ab
        /* check for errors during parsing */
Packit 6bd9ab
        if ((parserc != LDAP_SUCCESS) && (parserc != LDAP_MORE_RESULTS_TO_RETURN))
Packit 6bd9ab
        {
Packit 6bd9ab
          if (resultcontrols != NULL)
Packit 6bd9ab
            ldap_controls_free(resultcontrols);
Packit 6bd9ab
          myldap_err(LOG_ERR, search->session->ld, parserc, "ldap_parse_result() failed");
Packit 6bd9ab
          myldap_search_close(search);
Packit 6bd9ab
          if (rcp != NULL)
Packit 6bd9ab
            *rcp = parserc;
Packit 6bd9ab
          return NULL;
Packit 6bd9ab
        }
Packit 6bd9ab
        /* check for errors in message */
Packit 6bd9ab
        if ((rc != LDAP_SUCCESS) && (rc != LDAP_MORE_RESULTS_TO_RETURN))
Packit 6bd9ab
        {
Packit 6bd9ab
          if (resultcontrols != NULL)
Packit 6bd9ab
            ldap_controls_free(resultcontrols);
Packit 6bd9ab
          myldap_err(LOG_ERR, search->session->ld, rc, "ldap_result() failed");
Packit 6bd9ab
          /* close connection on connection problems */
Packit 6bd9ab
          if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
Packit 6bd9ab
            do_close(search->session);
Packit 6bd9ab
          myldap_search_close(search);
Packit 6bd9ab
          if (rcp != NULL)
Packit 6bd9ab
            *rcp = rc;
Packit 6bd9ab
          return NULL;
Packit 6bd9ab
        }
Packit 6bd9ab
        /* handle result controls */
Packit 6bd9ab
        if (resultcontrols != NULL)
Packit 6bd9ab
        {
Packit 6bd9ab
          /* see if there are any more pages to come */
Packit 6bd9ab
          rc = ldap_parse_page_control(search->session->ld, resultcontrols,
Packit 6bd9ab
                                       &count, &(search->cookie));
Packit 6bd9ab
          if (rc != LDAP_SUCCESS)
Packit 6bd9ab
          {
Packit 6bd9ab
            if (rc != LDAP_CONTROL_NOT_FOUND)
Packit 6bd9ab
              myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_parse_page_control() failed");
Packit 6bd9ab
            /* clear error flag */
Packit 6bd9ab
            rc = LDAP_SUCCESS;
Packit 6bd9ab
            if (ldap_set_option(search->session->ld, LDAP_OPT_ERROR_NUMBER,
Packit 6bd9ab
                                &rc) != LDAP_SUCCESS)
Packit 6bd9ab
              log_log(LOG_WARNING, "failed to clear the error flag");
Packit 6bd9ab
          }
Packit 6bd9ab
          /* TODO: handle the above return code?? */
Packit 6bd9ab
          ldap_controls_free(resultcontrols);
Packit 6bd9ab
        }
Packit 6bd9ab
        search->msgid = -1;
Packit 6bd9ab
        /* check if there are more pages to come */
Packit 6bd9ab
        if ((search->cookie == NULL) || (search->cookie->bv_len == 0))
Packit 6bd9ab
        {
Packit 6bd9ab
          if (search->count > MAX_DEBUG_LOG_DNS)
Packit 6bd9ab
            log_log(LOG_DEBUG, "ldap_result(): ... %d more results",
Packit 6bd9ab
                    search->count - MAX_DEBUG_LOG_DNS);
Packit 6bd9ab
          log_log(LOG_DEBUG, "ldap_result(): end of results (%d total)",
Packit 6bd9ab
                  search->count);
Packit 6bd9ab
          /* we are at the end of the search, no more results */
Packit 6bd9ab
          myldap_search_close(search);
Packit 6bd9ab
          if (rcp != NULL)
Packit 6bd9ab
            *rcp = LDAP_SUCCESS;
Packit 6bd9ab
          return NULL;
Packit 6bd9ab
        }
Packit 6bd9ab
        /* try the next page */
Packit 6bd9ab
        rc = do_try_search(search);
Packit 6bd9ab
        if (rc != LDAP_SUCCESS)
Packit 6bd9ab
        {
Packit 6bd9ab
          /* close connection on connection problems */
Packit 6bd9ab
          if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
Packit 6bd9ab
            do_close(search->session);
Packit 6bd9ab
          myldap_search_close(search);
Packit 6bd9ab
          if (rcp != NULL)
Packit 6bd9ab
            *rcp = rc;
Packit 6bd9ab
          return NULL;
Packit 6bd9ab
        }
Packit 6bd9ab
        /* we continue with another pass */
Packit 6bd9ab
        break;
Packit 6bd9ab
      case LDAP_RES_SEARCH_REFERENCE:
Packit 6bd9ab
        break; /* just ignore search references */
Packit 6bd9ab
      default:
Packit 6bd9ab
        /* we have some error condition, find out which */
Packit 6bd9ab
        switch (rc)
Packit 6bd9ab
        {
Packit 6bd9ab
          case -1:
Packit 6bd9ab
            /* try to get error code */
Packit 6bd9ab
            if (ldap_get_option(search->session->ld, LDAP_OPT_ERROR_NUMBER,
Packit 6bd9ab
                                &rc) != LDAP_SUCCESS)
Packit 6bd9ab
              rc = LDAP_UNAVAILABLE;
Packit 6bd9ab
            myldap_err(LOG_ERR, search->session->ld, rc, "ldap_result() failed");
Packit 6bd9ab
            break;
Packit 6bd9ab
          case 0:
Packit 6bd9ab
            /* the timeout expired */
Packit 6bd9ab
            log_log(LOG_ERR, "ldap_result() timed out");
Packit 6bd9ab
            rc = LDAP_TIMELIMIT_EXCEEDED;
Packit 6bd9ab
            break;
Packit 6bd9ab
          default:
Packit 6bd9ab
            /* unknown code */
Packit 6bd9ab
            log_log(LOG_WARNING, "ldap_result() returned unexpected result type");
Packit 6bd9ab
            rc = LDAP_PROTOCOL_ERROR;
Packit 6bd9ab
        }
Packit 6bd9ab
        /* close connection on some connection problems */
Packit 6bd9ab
        if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN) ||
Packit 6bd9ab
            (rc == LDAP_SUCCESS) || (rc == LDAP_TIMELIMIT_EXCEEDED) ||
Packit 6bd9ab
            (rc == LDAP_OPERATIONS_ERROR) || (rc == LDAP_PROTOCOL_ERROR) ||
Packit 6bd9ab
            (rc == LDAP_BUSY) || (rc == LDAP_UNWILLING_TO_PERFORM) ||
Packit 6bd9ab
            (rc == LDAP_TIMEOUT) || (rc == LDAP_CONNECT_ERROR) ||
Packit 6bd9ab
            (rc == LDAP_NOT_SUPPORTED))
Packit 6bd9ab
        {
Packit 6bd9ab
          do_close(search->session);
Packit 6bd9ab
          /* retry once if no data has been received yet */
Packit 6bd9ab
          if (search->may_retry_search)
Packit 6bd9ab
          {
Packit 6bd9ab
            log_log(LOG_DEBUG, "myldap_get_entry(): retry search");
Packit 6bd9ab
            search->may_retry_search = 0;
Packit 6bd9ab
            if (do_retry_search(search) == LDAP_SUCCESS)
Packit 6bd9ab
              return myldap_get_entry(search, rcp);
Packit 6bd9ab
          }
Packit 6bd9ab
        }
Packit 6bd9ab
        /* close search */
Packit 6bd9ab
        myldap_search_close(search);
Packit 6bd9ab
        if (rcp != NULL)
Packit 6bd9ab
          *rcp = rc;
Packit 6bd9ab
        return NULL;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* Get the DN from the entry. This function only returns NULL (and sets
Packit 6bd9ab
   errno) if an incorrect entry is passed. If the DN value cannot be
Packit 6bd9ab
   retrieved "unknown" is returned instead. */
Packit 6bd9ab
const char *myldap_get_dn(MYLDAP_ENTRY *entry)
Packit 6bd9ab
{
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  /* check parameters */
Packit 6bd9ab
  if (!is_valid_entry(entry))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_get_dn(): invalid result entry passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return "unknown";
Packit 6bd9ab
  }
Packit 6bd9ab
  /* if we don't have it yet, retreive it */
Packit 6bd9ab
  if ((entry->dn == NULL) && (entry->search->valid))
Packit 6bd9ab
  {
Packit 6bd9ab
    entry->dn = ldap_get_dn(entry->search->session->ld, entry->search->msg);
Packit 6bd9ab
    if (entry->dn == NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER,
Packit 6bd9ab
                          &rc) != LDAP_SUCCESS)
Packit 6bd9ab
        rc = LDAP_UNAVAILABLE;
Packit 6bd9ab
      myldap_err(LOG_WARNING, entry->search->session->ld, rc, "ldap_get_dn() returned NULL");
Packit 6bd9ab
      /* close connection on connection problems */
Packit 6bd9ab
      if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
Packit 6bd9ab
        do_close(entry->search->session);
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  /* if we still don't have it, return unknown */
Packit 6bd9ab
  if (entry->dn == NULL)
Packit 6bd9ab
    return "unknown";
Packit 6bd9ab
  /* return it */
Packit 6bd9ab
  return entry->dn;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
char *myldap_cpy_dn(MYLDAP_ENTRY *entry, char *buf, size_t buflen)
Packit 6bd9ab
{
Packit 6bd9ab
  const char *dn;
Packit 6bd9ab
  /* get the dn */
Packit 6bd9ab
  dn = myldap_get_dn(entry);
Packit 6bd9ab
  /* copy into buffer */
Packit 6bd9ab
  if (strlen(dn) < buflen)
Packit 6bd9ab
    strcpy(buf, dn);
Packit 6bd9ab
  else
Packit 6bd9ab
    buf = NULL;
Packit 6bd9ab
  return buf;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* Perform ranged retreival of attributes.
Packit 6bd9ab
   http://msdn.microsoft.com/en-us/library/aa367017(vs.85).aspx
Packit 6bd9ab
   http://www.tkk.fi/cc/docs/kerberos/draft-kashi-incremental-00.txt */
Packit 6bd9ab
static char **myldap_get_ranged_values(MYLDAP_ENTRY *entry, const char *attr)
Packit 6bd9ab
{
Packit 6bd9ab
  char **values;
Packit 6bd9ab
  char *attn;
Packit 6bd9ab
  const char *attrs[2];
Packit 6bd9ab
  BerElement *ber;
Packit 6bd9ab
  int i;
Packit 6bd9ab
  int startat = 0, nxt = 0;
Packit 6bd9ab
  char attbuf[80];
Packit 6bd9ab
  const char *dn = myldap_get_dn(entry);
Packit 6bd9ab
  MYLDAP_SESSION *session = entry->search->session;
Packit 6bd9ab
  MYLDAP_SEARCH *search = NULL;
Packit 6bd9ab
  SET *set = NULL;
Packit 6bd9ab
  /* build the attribute name to find */
Packit 6bd9ab
  if (mysnprintf(attbuf, sizeof(attbuf), "%s;range=0-*", attr))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_get_ranged_values(): attbuf buffer too small (%lu required)",
Packit 6bd9ab
            (unsigned long) strlen(attr) + 10);
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* keep doing lookups untul we can't get any more results */
Packit 6bd9ab
  while (1)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* go over all attributes to find the ranged attribute */
Packit 6bd9ab
    ber = NULL;
Packit 6bd9ab
    attn = ldap_first_attribute(entry->search->session->ld, entry->search->msg, &ber);
Packit 6bd9ab
    values = NULL;
Packit 6bd9ab
    while (attn != NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      if (strncasecmp(attn, attbuf, strlen(attbuf) - 1) == 0)
Packit 6bd9ab
      {
Packit 6bd9ab
        log_log(LOG_DEBUG, "found ranged results %s", attn);
Packit 6bd9ab
        nxt = atoi(attn + strlen(attbuf) - 1) + 1;
Packit 6bd9ab
        values = ldap_get_values(entry->search->session->ld, entry->search->msg, attn);
Packit 6bd9ab
        ldap_memfree(attn);
Packit 6bd9ab
        break;
Packit 6bd9ab
      }
Packit 6bd9ab
      /* free old attribute name and get next one */
Packit 6bd9ab
      ldap_memfree(attn);
Packit 6bd9ab
      attn = ldap_next_attribute(entry->search->session->ld, entry->search->msg, ber);
Packit 6bd9ab
    }
Packit 6bd9ab
    ber_free(ber, 0);
Packit 6bd9ab
    /* see if we found any values */
Packit 6bd9ab
    if ((values == NULL) || (*values == NULL))
Packit 6bd9ab
      break;
Packit 6bd9ab
    /* allocate memory */
Packit 6bd9ab
    if (set == NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      set = set_new();
Packit 6bd9ab
      if (set == NULL)
Packit 6bd9ab
      {
Packit 6bd9ab
        ldap_value_free(values);
Packit 6bd9ab
        log_log(LOG_CRIT, "myldap_get_ranged_values(): set_new() failed to allocate memory");
Packit 6bd9ab
        return NULL;
Packit 6bd9ab
      }
Packit 6bd9ab
    }
Packit 6bd9ab
    /* add to the set */
Packit 6bd9ab
    for (i = 0; values[i] != NULL; i++)
Packit 6bd9ab
      set_add(set, values[i]);
Packit 6bd9ab
    /* free results */
Packit 6bd9ab
    ldap_value_free(values);
Packit 6bd9ab
    /* check if we should start a new search */
Packit 6bd9ab
    if (nxt <= startat)
Packit 6bd9ab
      break;
Packit 6bd9ab
    startat = nxt;
Packit 6bd9ab
    /* build attributes for a new search */
Packit 6bd9ab
    if (mysnprintf(attbuf, sizeof(attbuf), "%s;range=%d-*", attr, startat))
Packit 6bd9ab
    {
Packit 6bd9ab
      log_log(LOG_ERR, "myldap_get_ranged_values(): attbuf buffer too small");
Packit 6bd9ab
      break;
Packit 6bd9ab
    }
Packit 6bd9ab
    attrs[0] = attbuf;
Packit 6bd9ab
    attrs[1] = NULL;
Packit 6bd9ab
    /* close the previous search, if any */
Packit 6bd9ab
    if (search != NULL)
Packit 6bd9ab
      myldap_search_close(search);
Packit 6bd9ab
    /* start the new search */
Packit 6bd9ab
    search = myldap_search(session, dn, LDAP_SCOPE_BASE, "(objectClass=*)", attrs, NULL);
Packit 6bd9ab
    if (search == NULL)
Packit 6bd9ab
      break;
Packit 6bd9ab
    entry = myldap_get_entry(search, NULL);
Packit 6bd9ab
    if (entry == NULL)
Packit 6bd9ab
      break;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* close any started searches */
Packit 6bd9ab
  if (search != NULL)
Packit 6bd9ab
    myldap_search_close(search);
Packit 6bd9ab
  /* return the contents of the set as a list */
Packit 6bd9ab
  if (set == NULL)
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  values = (char **)set_tolist(set);
Packit 6bd9ab
  set_free(set);
Packit 6bd9ab
  if (values == NULL)
Packit 6bd9ab
    log_log(LOG_CRIT, "myldap_get_ranged_values(): malloc() failed to allocate memory");
Packit 6bd9ab
  return values;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* Simple wrapper around ldap_get_values(). */
Packit 6bd9ab
const char **myldap_get_values(MYLDAP_ENTRY *entry, const char *attr)
Packit 6bd9ab
{
Packit 6bd9ab
  char **values;
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  int i;
Packit 6bd9ab
  /* check parameters */
Packit 6bd9ab
  if (!is_valid_entry(entry))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_get_values(): invalid result entry passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  else if (attr == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_get_values(): invalid attribute name passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  if (!entry->search->valid)
Packit 6bd9ab
    return NULL; /* search has been stopped */
Packit 6bd9ab
  /* get from LDAP */
Packit 6bd9ab
  values = ldap_get_values(entry->search->session->ld, entry->search->msg, attr);
Packit 6bd9ab
  if (values == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
Packit 6bd9ab
      rc = LDAP_UNAVAILABLE;
Packit 6bd9ab
    /* ignore decoding errors as they are just nonexisting attribute values */
Packit 6bd9ab
    if (rc == LDAP_DECODING_ERROR)
Packit 6bd9ab
    {
Packit 6bd9ab
      rc = LDAP_SUCCESS;
Packit 6bd9ab
      if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
Packit 6bd9ab
        log_log(LOG_WARNING, "failed to clear the error flag");
Packit 6bd9ab
    }
Packit 6bd9ab
    else if (rc == LDAP_SUCCESS)
Packit 6bd9ab
    {
Packit 6bd9ab
      /* we have a success code but no values, let's try to get ranged
Packit 6bd9ab
         values */
Packit 6bd9ab
      values = myldap_get_ranged_values(entry, attr);
Packit 6bd9ab
      if (values == NULL)
Packit 6bd9ab
        return NULL;
Packit 6bd9ab
      /* store values entry so we can free it later on */
Packit 6bd9ab
      for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
Packit 6bd9ab
        if (entry->buffers[i] == NULL)
Packit 6bd9ab
        {
Packit 6bd9ab
          entry->buffers[i] = values;
Packit 6bd9ab
          return (const char **)entry->buffers[i];
Packit 6bd9ab
        }
Packit 6bd9ab
      /* we found no room to store the values */
Packit 6bd9ab
      log_log(LOG_ERR, "ldap_get_values() couldn't store results, increase MAX_BUFFERS_PER_ENTRY");
Packit 6bd9ab
      free(values);
Packit 6bd9ab
      return NULL;
Packit 6bd9ab
    }
Packit 6bd9ab
    else
Packit 6bd9ab
      myldap_err(LOG_WARNING, entry->search->session->ld, rc,
Packit 6bd9ab
                 "ldap_get_values() of attribute \"%s\" on entry \"%s\" returned NULL",
Packit 6bd9ab
                 attr, myldap_get_dn(entry));
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* store values entry so we can free it later on */
Packit 6bd9ab
  for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
Packit 6bd9ab
    if (entry->attributevalues[i] == NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      entry->attributevalues[i] = values;
Packit 6bd9ab
      return (const char **)values;
Packit 6bd9ab
    }
Packit 6bd9ab
  /* we found no room to store the entry */
Packit 6bd9ab
  log_log(LOG_ERR, "ldap_get_values() couldn't store results, increase MAX_ATTRIBUTES_PER_ENTRY");
Packit 6bd9ab
  ldap_value_free(values);
Packit 6bd9ab
  return NULL;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* Convert the bervalues to a simple list of strings that can be freed
Packit 6bd9ab
   with one call to free(). */
Packit 6bd9ab
static const char **bervalues_to_values(struct berval **bvalues)
Packit 6bd9ab
{
Packit 6bd9ab
  int num_values;
Packit 6bd9ab
  int i;
Packit 6bd9ab
  size_t sz;
Packit 6bd9ab
  char *buf;
Packit 6bd9ab
  char **values;
Packit 6bd9ab
  /* figure out how much memory to allocate */
Packit 6bd9ab
  num_values = ldap_count_values_len(bvalues);
Packit 6bd9ab
  sz = (num_values + 1) * sizeof(char *);
Packit 6bd9ab
  for (i = 0; i < num_values; i++)
Packit 6bd9ab
    sz += bvalues[i]->bv_len + 1;
Packit 6bd9ab
  /* allocate the needed memory */
Packit 6bd9ab
  values = (char **)malloc(sz);
Packit 6bd9ab
  if (values == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_CRIT, "bervalues_to_values(): malloc() failed to allocate memory");
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  buf = (char *)values;
Packit 6bd9ab
  buf += (num_values + 1) * sizeof(char *);
Packit 6bd9ab
  /* copy from bvalues */
Packit 6bd9ab
  for (i = 0; i < num_values; i++)
Packit 6bd9ab
  {
Packit 6bd9ab
    values[i] = buf;
Packit 6bd9ab
    memcpy(values[i], bvalues[i]->bv_val, bvalues[i]->bv_len);
Packit 6bd9ab
    values[i][bvalues[i]->bv_len] = '\0';
Packit 6bd9ab
    buf += bvalues[i]->bv_len + 1;
Packit 6bd9ab
  }
Packit 6bd9ab
  values[i] = NULL;
Packit 6bd9ab
  return (const char **)values;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* Simple wrapper around ldap_get_values(). */
Packit 6bd9ab
const char **myldap_get_values_len(MYLDAP_ENTRY *entry, const char *attr)
Packit 6bd9ab
{
Packit 6bd9ab
  const char **values;
Packit 6bd9ab
  struct berval **bvalues;
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  int i;
Packit 6bd9ab
  /* check parameters */
Packit 6bd9ab
  if (!is_valid_entry(entry))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_get_values_len(): invalid result entry passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  else if (attr == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_get_values_len(): invalid attribute name passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  if (!entry->search->valid)
Packit 6bd9ab
    return NULL; /* search has been stopped */
Packit 6bd9ab
  /* get from LDAP */
Packit 6bd9ab
  bvalues = ldap_get_values_len(entry->search->session->ld, entry->search->msg, attr);
Packit 6bd9ab
  if (bvalues == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    if (ldap_get_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
Packit 6bd9ab
      rc = LDAP_UNAVAILABLE;
Packit 6bd9ab
    /* ignore decoding errors as they are just nonexisting attribute values */
Packit 6bd9ab
    if (rc == LDAP_DECODING_ERROR)
Packit 6bd9ab
    {
Packit 6bd9ab
      rc = LDAP_SUCCESS;
Packit 6bd9ab
      if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
Packit 6bd9ab
        log_log(LOG_WARNING, "failed to clear the error flag");
Packit 6bd9ab
      return NULL;
Packit 6bd9ab
    }
Packit 6bd9ab
    else if (rc == LDAP_SUCCESS)
Packit 6bd9ab
    {
Packit 6bd9ab
      /* we have a success code but no values, let's try to get ranged
Packit 6bd9ab
         values */
Packit 6bd9ab
      values = (const char **)myldap_get_ranged_values(entry, attr);
Packit 6bd9ab
    }
Packit 6bd9ab
    else
Packit 6bd9ab
    {
Packit 6bd9ab
      myldap_err(LOG_WARNING, entry->search->session->ld, rc,
Packit 6bd9ab
                 "myldap_get_values_len() of attribute \"%s\" on entry \"%s\" returned NULL",
Packit 6bd9ab
                 attr, myldap_get_dn(entry));
Packit 6bd9ab
      return NULL;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  else
Packit 6bd9ab
  {
Packit 6bd9ab
    values = bervalues_to_values(bvalues);
Packit 6bd9ab
    ldap_value_free_len(bvalues);
Packit 6bd9ab
  }
Packit 6bd9ab
  /* check if we got allocated memory */
Packit 6bd9ab
  if (values == NULL)
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  /* store values entry so we can free it later on */
Packit 6bd9ab
  for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
Packit 6bd9ab
    if (entry->buffers[i] == NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      entry->buffers[i] = (char **)values;
Packit 6bd9ab
      return values;
Packit 6bd9ab
    }
Packit 6bd9ab
  /* we found no room to store the values */
Packit 6bd9ab
  log_log(LOG_ERR, "myldap_get_values_len() couldn't store results, increase MAX_BUFFERS_PER_ENTRY");
Packit 6bd9ab
  free(values);
Packit 6bd9ab
  return NULL;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* Go over the entries in exploded_rdn and see if any start with
Packit 6bd9ab
   the requested attribute. Return a reference to the value part of
Packit 6bd9ab
   the DN (does not modify exploded_rdn). */
Packit 6bd9ab
static const char *find_rdn_value(char **exploded_rdn, const char *attr)
Packit 6bd9ab
{
Packit 6bd9ab
  int i, j;
Packit 6bd9ab
  int l;
Packit 6bd9ab
  if (exploded_rdn == NULL)
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  /* go over all RDNs */
Packit 6bd9ab
  l = strlen(attr);
Packit 6bd9ab
  for (i = 0; exploded_rdn[i] != NULL; i++)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* check that RDN starts with attr */
Packit 6bd9ab
    if (strncasecmp(exploded_rdn[i], attr, l) != 0)
Packit 6bd9ab
      continue;
Packit 6bd9ab
    j = l;
Packit 6bd9ab
    /* skip spaces */
Packit 6bd9ab
    while (isspace(exploded_rdn[i][j]))
Packit 6bd9ab
      j++;
Packit 6bd9ab
    /* ensure that we found an equals sign now */
Packit 6bd9ab
    if (exploded_rdn[i][j] != '=')
Packit 6bd9ab
      continue;
Packit 6bd9ab
    j++;
Packit 6bd9ab
    /* skip more spaces */
Packit 6bd9ab
    while (isspace(exploded_rdn[i][j]))
Packit 6bd9ab
      j++;
Packit 6bd9ab
    /* ensure that we're not at the end of the string */
Packit 6bd9ab
    if (exploded_rdn[i][j] == '\0')
Packit 6bd9ab
      continue;
Packit 6bd9ab
    /* we found our value */
Packit 6bd9ab
    return exploded_rdn[i] + j;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* fail */
Packit 6bd9ab
  return NULL;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* explode the first part of DN into parts
Packit 6bd9ab
   (e.g. "cn=Test", "uid=test")
Packit 6bd9ab
   The returned value should be freed with ldap_value_free(). */
Packit 6bd9ab
static char **get_exploded_rdn(const char *dn)
Packit 6bd9ab
{
Packit 6bd9ab
  char **exploded_dn;
Packit 6bd9ab
  char **exploded_rdn;
Packit 6bd9ab
  /* check if we have a DN */
Packit 6bd9ab
  if ((dn == NULL) || (strcasecmp(dn, "unknown") == 0))
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  /* explode dn into { "uid=test", "ou=people", ..., NULL } */
Packit 6bd9ab
  exploded_dn = ldap_explode_dn(dn, 0);
Packit 6bd9ab
  if ((exploded_dn == NULL) || (exploded_dn[0] == NULL))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_WARNING, "ldap_explode_dn(%s) returned NULL: %s",
Packit 6bd9ab
            dn, strerror(errno));
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* explode rdn (first part of exploded_dn),
Packit 6bd9ab
     e.g. "cn=Test User+uid=testusr" into
Packit 6bd9ab
     { "cn=Test User", "uid=testusr", NULL } */
Packit 6bd9ab
  errno = 0;
Packit 6bd9ab
  exploded_rdn = ldap_explode_rdn(exploded_dn[0], 0);
Packit 6bd9ab
  if ((exploded_rdn == NULL) || (exploded_rdn[0] == NULL))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_WARNING, "ldap_explode_rdn(%s) returned NULL: %s",
Packit 6bd9ab
            exploded_dn[0], strerror(errno));
Packit 6bd9ab
    if (exploded_rdn != NULL)
Packit 6bd9ab
      ldap_value_free(exploded_rdn);
Packit 6bd9ab
    ldap_value_free(exploded_dn);
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  ldap_value_free(exploded_dn);
Packit 6bd9ab
  return exploded_rdn;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
const char *myldap_get_rdn_value(MYLDAP_ENTRY *entry, const char *attr)
Packit 6bd9ab
{
Packit 6bd9ab
  /* check parameters */
Packit 6bd9ab
  if (!is_valid_entry(entry))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_get_rdn_value(): invalid result entry passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  else if (attr == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_get_rdn_value(): invalid attribute name passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* check if entry contains exploded_rdn */
Packit 6bd9ab
  if (entry->exploded_rdn == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    entry->exploded_rdn = get_exploded_rdn(myldap_get_dn(entry));
Packit 6bd9ab
    if (entry->exploded_rdn == NULL)
Packit 6bd9ab
      return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* find rnd value */
Packit 6bd9ab
  return find_rdn_value(entry->exploded_rdn, attr);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
const char *myldap_cpy_rdn_value(const char *dn, const char *attr,
Packit 6bd9ab
                                 char *buf, size_t buflen)
Packit 6bd9ab
{
Packit 6bd9ab
  char **exploded_rdn;
Packit 6bd9ab
  const char *value;
Packit 6bd9ab
  /* explode dn into { "cn=Test", "uid=test", NULL } */
Packit 6bd9ab
  exploded_rdn = get_exploded_rdn(dn);
Packit 6bd9ab
  if (exploded_rdn == NULL)
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  /* see if we have a match */
Packit 6bd9ab
  value = find_rdn_value(exploded_rdn, attr);
Packit 6bd9ab
  /* if we have something store it in the buffer */
Packit 6bd9ab
  if ((value != NULL) && (strlen(value) < buflen))
Packit 6bd9ab
    strcpy(buf, value);
Packit 6bd9ab
  else
Packit 6bd9ab
    value = NULL;
Packit 6bd9ab
  /* free allocated stuff */
Packit 6bd9ab
  ldap_value_free(exploded_rdn);
Packit 6bd9ab
  /* check if we have something to return */
Packit 6bd9ab
  return (value != NULL) ? buf : NULL;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
int myldap_has_objectclass(MYLDAP_ENTRY *entry, const char *objectclass)
Packit 6bd9ab
{
Packit 6bd9ab
  const char **values;
Packit 6bd9ab
  int i;
Packit 6bd9ab
  if ((!is_valid_entry(entry)) || (objectclass == NULL))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_has_objectclass(): invalid argument passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return 0;
Packit 6bd9ab
  }
Packit 6bd9ab
  values = myldap_get_values(entry, "objectClass");
Packit 6bd9ab
  if (values == NULL)
Packit 6bd9ab
    return 0;
Packit 6bd9ab
  for (i = 0; values[i] != NULL; i++)
Packit 6bd9ab
  {
Packit 6bd9ab
    if (strcasecmp(values[i], objectclass) == 0)
Packit 6bd9ab
      return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  return 0;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
#ifdef HAVE_LDAP_PARSE_DEREF_CONTROL
Packit 6bd9ab
const char ***myldap_get_deref_values(MYLDAP_ENTRY *entry,
Packit 6bd9ab
                const char *derefattr, const char *getattr)
Packit 6bd9ab
{
Packit 6bd9ab
  LDAPControl **entryctrls;
Packit 6bd9ab
  LDAPDerefRes *deref, *d;
Packit 6bd9ab
  LDAPDerefVal *a;
Packit 6bd9ab
  int i, pass;
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  int found;
Packit 6bd9ab
  int counts[2];
Packit 6bd9ab
  size_t sizes[2], size;
Packit 6bd9ab
  char *buffer = NULL;
Packit 6bd9ab
  char ***results = NULL;
Packit 6bd9ab
  rc = ldap_get_entry_controls(entry->search->session->ld, entry->search->msg,
Packit 6bd9ab
                                &entryctrls);
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    myldap_err(LOG_WARNING, entry->search->session->ld, rc,
Packit 6bd9ab
               "ldap_get_entry_controls() failed");
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  if (entryctrls == NULL)
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  /* see if we can find a deref control */
Packit 6bd9ab
  rc = ldap_parse_deref_control(entry->search->session->ld, entryctrls,
Packit 6bd9ab
                                &deref);
Packit 6bd9ab
  if ((rc != LDAP_SUCCESS) || (deref == NULL))
Packit 6bd9ab
  {
Packit 6bd9ab
    if ((rc != LDAP_SUCCESS) && (rc != LDAP_CONTROL_NOT_FOUND))
Packit 6bd9ab
      myldap_err(LOG_WARNING, entry->search->session->ld, rc,
Packit 6bd9ab
                 "ldap_parse_deref_control() failed");
Packit 6bd9ab
    /* clear error flag */
Packit 6bd9ab
    rc = LDAP_SUCCESS;
Packit 6bd9ab
    if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER,
Packit 6bd9ab
                        &rc) != LDAP_SUCCESS)
Packit 6bd9ab
      log_log(LOG_WARNING, "failed to clear the error flag");
Packit 6bd9ab
    ldap_controls_free(entryctrls);
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* two passes: one to calculate size, one to store data */
Packit 6bd9ab
  for (pass=0; pass < 2; pass++)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* reset counters and size */
Packit 6bd9ab
    for (i = 0; i < 2; i++)
Packit 6bd9ab
    {
Packit 6bd9ab
      counts[i] = 0;
Packit 6bd9ab
      sizes[i] = 0;
Packit 6bd9ab
    }
Packit 6bd9ab
    /* go over all deref'd attributes and find the one we're looking for */
Packit 6bd9ab
    for (d = deref; d != NULL; d = d->next)
Packit 6bd9ab
      if ((d->derefAttr != NULL) && (d->derefVal.bv_val != NULL) &&
Packit 6bd9ab
          (strcasecmp(derefattr, d->derefAttr) == 0))
Packit 6bd9ab
      {
Packit 6bd9ab
        /* we should have one d per original attribute value */
Packit 6bd9ab
        found = 0;
Packit 6bd9ab
        /* go over deref'd attribute values to find the ones we're looking for */
Packit 6bd9ab
        for (a = d->attrVals; a != NULL; a = a->next)
Packit 6bd9ab
          if ((a->type != NULL) && (a->vals != NULL) &&
Packit 6bd9ab
              (strcasecmp(getattr, a->type) == 0))
Packit 6bd9ab
            for (i=0; a->vals[i].bv_val != NULL; i++)
Packit 6bd9ab
            {
Packit 6bd9ab
              found = 1;
Packit 6bd9ab
              if (results == NULL)
Packit 6bd9ab
              {
Packit 6bd9ab
                log_log(LOG_DEBUG, "deref %s %s=%s -> %s=%s",
Packit 6bd9ab
                        myldap_get_dn(entry),  d->derefAttr, d->derefVal.bv_val,
Packit 6bd9ab
                        a->type, a->vals[i].bv_val);
Packit 6bd9ab
                counts[0]++;
Packit 6bd9ab
                sizes[0] += strlen(a->vals[i].bv_val) + 1;
Packit 6bd9ab
              }
Packit 6bd9ab
              else
Packit 6bd9ab
              {
Packit 6bd9ab
                strcpy(buffer, a->vals[i].bv_val);
Packit 6bd9ab
                results[0][counts[0]++] = buffer;
Packit 6bd9ab
                buffer += strlen(buffer) + 1;
Packit 6bd9ab
              }
Packit 6bd9ab
            }
Packit 6bd9ab
        if (!found)
Packit 6bd9ab
        {
Packit 6bd9ab
          if (results == NULL)
Packit 6bd9ab
          {
Packit 6bd9ab
            log_log(LOG_DEBUG, "no %s deref %s %s=%s", getattr,
Packit 6bd9ab
                    myldap_get_dn(entry),  d->derefAttr, d->derefVal.bv_val);
Packit 6bd9ab
            counts[1]++;
Packit 6bd9ab
            sizes[1] += strlen(d->derefVal.bv_val) + 1;
Packit 6bd9ab
          }
Packit 6bd9ab
          else
Packit 6bd9ab
          {
Packit 6bd9ab
            strcpy(buffer, d->derefVal.bv_val);
Packit 6bd9ab
            results[1][counts[1]++] = buffer;
Packit 6bd9ab
            buffer += strlen(buffer) + 1;
Packit 6bd9ab
          }
Packit 6bd9ab
        }
Packit 6bd9ab
      }
Packit 6bd9ab
    /* allocate memory after first pass */
Packit 6bd9ab
    if (results == NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      size = sizeof(char **) * 3;
Packit 6bd9ab
      for (i = 0; i < 2; i++)
Packit 6bd9ab
        size += sizeof(char *) * (counts[i] + 1);
Packit 6bd9ab
      for (i = 0; i < 2; i++)
Packit 6bd9ab
        size += sizeof(char) * sizes[i];
Packit 6bd9ab
      buffer = (char *)malloc(size);
Packit 6bd9ab
      if (buffer == NULL)
Packit 6bd9ab
      {
Packit 6bd9ab
        log_log(LOG_CRIT, "myldap_get_deref_values(): malloc() failed to allocate memory");
Packit 6bd9ab
        return NULL;
Packit 6bd9ab
      }
Packit 6bd9ab
      /* allocate the list of lists */
Packit 6bd9ab
      results = (void *)buffer;
Packit 6bd9ab
      buffer += sizeof(char **) * 3;
Packit 6bd9ab
      /* allocate the lists */
Packit 6bd9ab
      for (i = 0; i < 2; i++)
Packit 6bd9ab
      {
Packit 6bd9ab
        results[i] = (char **)buffer;
Packit 6bd9ab
        buffer += sizeof(char *) * (counts[i] + 1);
Packit 6bd9ab
      }
Packit 6bd9ab
      results[i] = NULL;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  /* NULL terminate the lists */
Packit 6bd9ab
  results[0][counts[0]] = NULL;
Packit 6bd9ab
  results[1][counts[1]] = NULL;
Packit 6bd9ab
  /* free control data */
Packit 6bd9ab
  ldap_derefresponse_free(deref);
Packit 6bd9ab
  ldap_controls_free(entryctrls);
Packit 6bd9ab
  /* store results so we can free it later on */
Packit 6bd9ab
  for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
Packit 6bd9ab
    if (entry->buffers[i] == NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      entry->buffers[i] = (void *)results;
Packit 6bd9ab
      return (const char ***)results;
Packit 6bd9ab
    }
Packit 6bd9ab
  /* we found no room to store the values */
Packit 6bd9ab
  log_log(LOG_ERR, "myldap_get_deref_values() couldn't store results, "
Packit 6bd9ab
          "increase MAX_BUFFERS_PER_ENTRY");
Packit 6bd9ab
  free(results);
Packit 6bd9ab
  return NULL;
Packit 6bd9ab
}
Packit 6bd9ab
#else /* not HAVE_LDAP_PARSE_DEREF_CONTROL */
Packit 6bd9ab
const char ***myldap_get_deref_values(MYLDAP_ENTRY UNUSED(*entry),
Packit 6bd9ab
                const char UNUSED(*derefattr), const char UNUSED(*getattr))
Packit 6bd9ab
{
Packit 6bd9ab
  return NULL;
Packit 6bd9ab
}
Packit 6bd9ab
#endif /* not HAVE_LDAP_PARSE_DEREF_CONTROL */
Packit 6bd9ab
Packit 6bd9ab
int myldap_escape(const char *src, char *buffer, size_t buflen)
Packit 6bd9ab
{
Packit 6bd9ab
  size_t pos = 0;
Packit 6bd9ab
  /* go over all characters in source string */
Packit 6bd9ab
  for (; *src != '\0'; src++)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* check if char will fit */
Packit 6bd9ab
    if ((pos + 4) >= buflen)
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    /* do escaping for some characters */
Packit 6bd9ab
    switch (*src)
Packit 6bd9ab
    {
Packit 6bd9ab
      case '*':
Packit 6bd9ab
        strcpy(buffer + pos, "\\2a");
Packit 6bd9ab
        pos += 3;
Packit 6bd9ab
        break;
Packit 6bd9ab
      case '(':
Packit 6bd9ab
        strcpy(buffer + pos, "\\28");
Packit 6bd9ab
        pos += 3;
Packit 6bd9ab
        break;
Packit 6bd9ab
      case ')':
Packit 6bd9ab
        strcpy(buffer + pos, "\\29");
Packit 6bd9ab
        pos += 3;
Packit 6bd9ab
        break;
Packit 6bd9ab
      case '\\':
Packit 6bd9ab
        strcpy(buffer + pos, "\\5c");
Packit 6bd9ab
        pos += 3;
Packit 6bd9ab
        break;
Packit 6bd9ab
      default:
Packit 6bd9ab
        /* just copy character */
Packit 6bd9ab
        buffer[pos++] = *src;
Packit 6bd9ab
        break;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  /* terminate destination string */
Packit 6bd9ab
  buffer[pos] = '\0';
Packit 6bd9ab
  return 0;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
int myldap_set_debuglevel(int level)
Packit 6bd9ab
{
Packit 6bd9ab
  int i;
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  /* turn on debugging */
Packit 6bd9ab
  if (level > 1)
Packit 6bd9ab
  {
Packit 6bd9ab
#ifdef LBER_OPT_LOG_PRINT_FILE
Packit 6bd9ab
    log_log(LOG_DEBUG, "ber_set_option(LBER_OPT_LOG_PRINT_FILE)");
Packit 6bd9ab
    rc = ber_set_option(NULL, LBER_OPT_LOG_PRINT_FILE, stderr);
Packit 6bd9ab
    if (rc != LDAP_SUCCESS)
Packit 6bd9ab
    {
Packit 6bd9ab
      myldap_err(LOG_ERR, NULL, rc, "ber_set_option(LBER_OPT_LOG_PRINT_FILE) failed");
Packit 6bd9ab
      return rc;
Packit 6bd9ab
    }
Packit 6bd9ab
#endif /* LBER_OPT_LOG_PRINT_FILE */
Packit 6bd9ab
#ifdef LBER_OPT_DEBUG_LEVEL
Packit 6bd9ab
    if (level > 2)
Packit 6bd9ab
    {
Packit 6bd9ab
      i = -1;
Packit 6bd9ab
      log_log(LOG_DEBUG, "ber_set_option(LBER_OPT_DEBUG_LEVEL,-1)");
Packit 6bd9ab
      rc = ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &i);
Packit 6bd9ab
      if (rc != LDAP_SUCCESS)
Packit 6bd9ab
      {
Packit 6bd9ab
        myldap_err(LOG_ERR, NULL, rc, "ber_set_option(LBER_OPT_DEBUG_LEVEL) failed");
Packit 6bd9ab
        return rc;
Packit 6bd9ab
      }
Packit 6bd9ab
    }
Packit 6bd9ab
#endif /* LBER_OPT_DEBUG_LEVEL */
Packit 6bd9ab
#ifdef LDAP_OPT_DEBUG_LEVEL
Packit 6bd9ab
    i = -1;
Packit 6bd9ab
    log_log(LOG_DEBUG, "ldap_set_option(LDAP_OPT_DEBUG_LEVEL,-1)");
Packit 6bd9ab
    rc = ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &i);
Packit 6bd9ab
    if (rc != LDAP_SUCCESS)
Packit 6bd9ab
    {
Packit 6bd9ab
      myldap_err(LOG_ERR, NULL, rc, "ldap_set_option(LDAP_OPT_DEBUG_LEVEL) failed");
Packit 6bd9ab
      return rc;
Packit 6bd9ab
    }
Packit 6bd9ab
#endif /* LDAP_OPT_DEBUG_LEVEL */
Packit 6bd9ab
  }
Packit 6bd9ab
  return LDAP_SUCCESS;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
int myldap_passwd(MYLDAP_SESSION *session,
Packit 6bd9ab
                  const char *userdn, const char *oldpassword,
Packit 6bd9ab
                  const char *newpasswd)
Packit 6bd9ab
{
Packit 6bd9ab
  int rc;
Packit 6bd9ab
  struct berval ber_userdn, ber_oldpassword, ber_newpassword, ber_retpassword;
Packit 6bd9ab
  /* check parameters */
Packit 6bd9ab
  if ((session == NULL) || (userdn == NULL) || (newpasswd == NULL))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_passwd(): invalid parameter passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return LDAP_OTHER;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* log the call */
Packit 6bd9ab
  log_log(LOG_DEBUG, "myldap_passwd(userdn=\"%s\",oldpasswd=%s,newpasswd=\"***\")",
Packit 6bd9ab
          userdn, oldpassword ? "\"***\"" : "NULL");
Packit 6bd9ab
  /* translate to ber stuff */
Packit 6bd9ab
  ber_userdn.bv_val = (char *)userdn;
Packit 6bd9ab
  ber_userdn.bv_len = strlen(userdn);
Packit 6bd9ab
  ber_newpassword.bv_val = (char *)newpasswd;
Packit 6bd9ab
  ber_newpassword.bv_len = strlen(newpasswd);
Packit 6bd9ab
  ber_retpassword.bv_val = NULL;
Packit 6bd9ab
  ber_retpassword.bv_len = 0;
Packit 6bd9ab
  /* perform request */
Packit 6bd9ab
  log_log(LOG_DEBUG, "myldap_passwd(): try ldap_passwd_s() without old password");
Packit 6bd9ab
  rc = ldap_passwd_s(session->ld, &ber_userdn, NULL, &ber_newpassword,
Packit 6bd9ab
                     &ber_retpassword, NULL, NULL);
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
    myldap_err(LOG_ERR, session->ld, rc, "ldap_passwd_s() without old password failed");
Packit 6bd9ab
  /* free returned data if needed */
Packit 6bd9ab
  if (ber_retpassword.bv_val != NULL)
Packit 6bd9ab
    ldap_memfree(ber_retpassword.bv_val);
Packit 6bd9ab
  if ((rc != LDAP_SUCCESS) && (oldpassword != NULL))
Packit 6bd9ab
  {
Packit 6bd9ab
    /* retry with old password */
Packit 6bd9ab
    log_log(LOG_DEBUG, "myldap_passwd(): try ldap_passwd_s() with old password");
Packit 6bd9ab
    ber_oldpassword.bv_val = (char *)oldpassword;
Packit 6bd9ab
    ber_oldpassword.bv_len = strlen(oldpassword);
Packit 6bd9ab
    /* perform request */
Packit 6bd9ab
    rc = ldap_passwd_s(session->ld, &ber_userdn, &ber_oldpassword,
Packit 6bd9ab
                       &ber_newpassword, &ber_retpassword, NULL, NULL);
Packit 6bd9ab
    if (rc != LDAP_SUCCESS)
Packit 6bd9ab
      myldap_err(LOG_ERR, session->ld, rc, "ldap_passwd_s() with old password failed");
Packit 6bd9ab
    /* free returned data if needed */
Packit 6bd9ab
    if (ber_retpassword.bv_val != NULL)
Packit 6bd9ab
      ldap_memfree(ber_retpassword.bv_val);
Packit 6bd9ab
  }
Packit 6bd9ab
  return rc;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
int myldap_modify(MYLDAP_SESSION *session, const char *dn, LDAPMod * mods[])
Packit 6bd9ab
{
Packit 6bd9ab
  if ((session == NULL) || (dn == NULL))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_passwd(): invalid parameter passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return LDAP_OTHER;
Packit 6bd9ab
  }
Packit 6bd9ab
  return ldap_modify_ext_s(session->ld, dn, mods, NULL, NULL);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
int myldap_error_message(MYLDAP_SESSION *session, int rc,
Packit 6bd9ab
                         char *buffer, size_t buflen)
Packit 6bd9ab
{
Packit 6bd9ab
  char *msg_diag = NULL;
Packit 6bd9ab
  if ((session == NULL) || (buffer == NULL) || (buflen <= 0))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "myldap_error_message(): invalid parameter passed");
Packit 6bd9ab
    errno = EINVAL;
Packit 6bd9ab
    return LDAP_OTHER;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* clear buffer */
Packit 6bd9ab
  buffer[0] = '\0';
Packit 6bd9ab
#ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
Packit 6bd9ab
  if (session->ld != NULL)
Packit 6bd9ab
    ldap_get_option(session->ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &msg_diag);
Packit 6bd9ab
#endif /* LDAP_OPT_DIAGNOSTIC_MESSAGE */
Packit 6bd9ab
  /* return msg_diag or generic error message */
Packit 6bd9ab
  mysnprintf(buffer, buflen - 1, "%s",
Packit 6bd9ab
             ((msg_diag != NULL) && (msg_diag[0]!='\0')) ?
Packit 6bd9ab
             msg_diag : ldap_err2string(rc));
Packit 6bd9ab
  /* free diagnostic message */
Packit 6bd9ab
  if (msg_diag != NULL)
Packit 6bd9ab
    ldap_memfree(msg_diag);
Packit 6bd9ab
  return LDAP_SUCCESS;
Packit 6bd9ab
}