aboutsummaryrefslogtreecommitdiff
path: root/contrib/sendmail/libsm/ldap.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sendmail/libsm/ldap.c')
-rw-r--r--contrib/sendmail/libsm/ldap.c1703
1 files changed, 1703 insertions, 0 deletions
diff --git a/contrib/sendmail/libsm/ldap.c b/contrib/sendmail/libsm/ldap.c
new file mode 100644
index 000000000000..cf5aa18ab9d7
--- /dev/null
+++ b/contrib/sendmail/libsm/ldap.c
@@ -0,0 +1,1703 @@
+/*
+ * Copyright (c) 2001-2009 Proofpoint, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+/* some "deprecated" calls are used, e.g., ldap_get_values() */
+#define LDAP_DEPRECATED 1
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: ldap.c,v 1.86 2013-11-22 20:51:43 ca Exp $")
+
+#if LDAPMAP
+# include <sys/types.h>
+# include <errno.h>
+# include <setjmp.h>
+# include <stdlib.h>
+# include <unistd.h>
+
+# include <sm/bitops.h>
+# include <sm/clock.h>
+# include <sm/conf.h>
+# include <sm/debug.h>
+# include <sm/errstring.h>
+# include <sm/ldap.h>
+# include <sm/string.h>
+# include <sm/sysexits.h>
+# include <sm/sendmail.h>
+
+SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
+ "@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
+
+static bool sm_ldap_has_objectclass __P((SM_LDAP_STRUCT *, LDAPMessage *, char *));
+static SM_LDAP_RECURSE_ENTRY *sm_ldap_add_recurse __P((SM_LDAP_RECURSE_LIST **, char *, int, SM_RPOOL_T *));
+
+static char *sm_ldap_geterror __P((LDAP *));
+
+/*
+** SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
+**
+** Parameters:
+** lmap -- pointer to SM_LDAP_STRUCT to clear
+**
+** Returns:
+** None.
+**
+*/
+
+# if _FFR_LDAP_VERSION
+# if defined(LDAP_VERSION_MAX) && _FFR_LDAP_VERSION > LDAP_VERSION_MAX
+# error "_FFR_LDAP_VERSION > LDAP_VERSION_MAX"
+# endif
+# if defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN
+# error "_FFR_LDAP_VERSION < LDAP_VERSION_MAX"
+# endif
+# define SM_LDAP_VERSION_DEFAULT _FFR_LDAP_VERSION
+# else /* _FFR_LDAP_VERSION */
+# define SM_LDAP_VERSION_DEFAULT 0
+# endif /* _FFR_LDAP_VERSION */
+
+void
+sm_ldap_clear(lmap)
+ SM_LDAP_STRUCT *lmap;
+{
+ if (lmap == NULL)
+ return;
+
+ lmap->ldap_host = NULL;
+ lmap->ldap_port = LDAP_PORT;
+ lmap->ldap_uri = NULL;
+ lmap->ldap_version = SM_LDAP_VERSION_DEFAULT;
+ lmap->ldap_deref = LDAP_DEREF_NEVER;
+ lmap->ldap_timelimit = LDAP_NO_LIMIT;
+ lmap->ldap_sizelimit = LDAP_NO_LIMIT;
+# ifdef LDAP_REFERRALS
+ lmap->ldap_options = LDAP_OPT_REFERRALS;
+# else
+ lmap->ldap_options = 0;
+# endif
+ lmap->ldap_attrsep = '\0';
+# if LDAP_NETWORK_TIMEOUT
+ lmap->ldap_networktmo = 0;
+# endif
+ lmap->ldap_binddn = NULL;
+ lmap->ldap_secret = NULL;
+ lmap->ldap_method = LDAP_AUTH_SIMPLE;
+ lmap->ldap_base = NULL;
+ lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
+ lmap->ldap_attrsonly = LDAPMAP_FALSE;
+ lmap->ldap_timeout.tv_sec = 0;
+ lmap->ldap_timeout.tv_usec = 0;
+ lmap->ldap_ld = NULL;
+ lmap->ldap_filter = NULL;
+ lmap->ldap_attr[0] = NULL;
+ lmap->ldap_attr_type[0] = SM_LDAP_ATTR_NONE;
+ lmap->ldap_attr_needobjclass[0] = NULL;
+ lmap->ldap_res = NULL;
+ lmap->ldap_next = NULL;
+ lmap->ldap_pid = 0;
+ lmap->ldap_multi_args = false;
+}
+
+# if _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN)
+static void ldap_debug_cb __P((const char *msg));
+
+static void
+ldap_debug_cb(msg)
+ const char *msg;
+{
+ if (sm_debug_active(&SmLDAPTrace, 4))
+ sm_dprintf("%s", msg);
+}
+# endif /* _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN) */
+
+
+# if LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT)
+# define SET_LDAP_TMO(ld, lmap) \
+ do \
+ { \
+ if (lmap->ldap_networktmo > 0) \
+ { \
+ struct timeval tmo; \
+ \
+ if (sm_debug_active(&SmLDAPTrace, 9)) \
+ sm_dprintf("ldap_networktmo=%d\n", \
+ lmap->ldap_networktmo); \
+ tmo.tv_sec = lmap->ldap_networktmo; \
+ tmo.tv_usec = 0; \
+ ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tmo); \
+ } \
+ } while (0)
+# else /* LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT) */
+# define SET_LDAP_TMO(ld, lmap)
+# endif /* LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT) */
+
+/*
+** SM_LDAP_SETOPTSG -- set some (global) LDAP options
+**
+** Parameters:
+** lmap -- LDAP map information
+**
+** Returns:
+** None.
+**
+*/
+
+# if _FFR_SM_LDAP_DBG
+static bool dbg_init = false;
+# endif
+# if SM_CONF_LDAP_INITIALIZE
+static void sm_ldap_setoptsg __P((SM_LDAP_STRUCT *lmap));
+static void
+sm_ldap_setoptsg(lmap)
+ SM_LDAP_STRUCT *lmap;
+{
+# if USE_LDAP_SET_OPTION
+
+ SET_LDAP_TMO(NULL, lmap);
+
+# if _FFR_SM_LDAP_DBG
+ if (!dbg_init && sm_debug_active(&SmLDAPTrace, 1) &&
+ lmap->ldap_debug != 0)
+ {
+ int r;
+# if defined(LBER_OPT_LOG_PRINT_FN)
+ r = ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN, ldap_debug_cb);
+# endif
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ sm_dprintf("ldap_debug0=%d\n", lmap->ldap_debug);
+ r = ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL,
+ &(lmap->ldap_debug));
+ if (sm_debug_active(&SmLDAPTrace, 9) && r != LDAP_OPT_SUCCESS)
+ sm_dprintf("ber_set_option=%d\n", r);
+ r = ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL,
+ &(lmap->ldap_debug));
+ if (sm_debug_active(&SmLDAPTrace, 9) && r != LDAP_OPT_SUCCESS)
+ sm_dprintf("ldap_set_option=%d\n", r);
+ dbg_init = true;
+ }
+# endif /* _FFR_SM_LDAP_DBG */
+# endif /* USE_LDAP_SET_OPTION */
+}
+# endif /* SM_CONF_LDAP_INITIALIZE */
+
+/*
+** SM_LDAP_SETOPTS -- set LDAP options
+**
+** Parameters:
+** ld -- LDAP session handle
+** lmap -- LDAP map information
+**
+** Returns:
+** None.
+**
+*/
+
+void
+sm_ldap_setopts(ld, lmap)
+ LDAP *ld;
+ SM_LDAP_STRUCT *lmap;
+{
+# if USE_LDAP_SET_OPTION
+ if (lmap->ldap_version != 0)
+ {
+ ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
+ &lmap->ldap_version);
+ }
+ ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
+ if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
+ ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
+ else
+ ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+ ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
+ ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
+ SET_LDAP_TMO(ld, lmap);
+# if _FFR_SM_LDAP_DBG
+ if ((!dbg_init || ld != NULL) && sm_debug_active(&SmLDAPTrace, 1)
+ && lmap->ldap_debug > 0)
+ {
+ int r;
+
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ sm_dprintf("ldap_debug=%d, dbg_init=%d\n",
+ lmap->ldap_debug, dbg_init);
+ r = ldap_set_option(ld, LDAP_OPT_DEBUG_LEVEL,
+ &(lmap->ldap_debug));
+ if (sm_debug_active(&SmLDAPTrace, 9) && r != LDAP_OPT_SUCCESS)
+ sm_dprintf("ldap_set_option=%d\n", r);
+ }
+# endif /* _FFR_SM_LDAP_DBG */
+# ifdef LDAP_OPT_RESTART
+ ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
+# endif
+# if _FFR_TESTS
+ if (sm_debug_active(&SmLDAPTrace, 101))
+ {
+ char *cert;
+ char buf[PATH_MAX];
+
+ cert = getcwd(buf, sizeof(buf));
+ if (NULL != cert)
+ {
+ int r;
+
+ (void) sm_strlcat(buf, "/ldaps.pem", sizeof(buf));
+ r = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTFILE, cert);
+ sm_dprintf("LDAP_OPT_X_TLS_CACERTFILE(%s)=%d\n", cert, r);
+ }
+ }
+# endif /* _FFR_TESTS */
+
+# else /* USE_LDAP_SET_OPTION */
+ /* From here on in we can use ldap internal timelimits */
+ ld->ld_deref = lmap->ldap_deref;
+ ld->ld_options = lmap->ldap_options;
+ ld->ld_sizelimit = lmap->ldap_sizelimit;
+ ld->ld_timelimit = lmap->ldap_timelimit;
+# endif /* USE_LDAP_SET_OPTION */
+}
+
+/*
+** SM_LDAP_START -- actually connect to an LDAP server
+**
+** Parameters:
+** name -- name of map for debug output.
+** lmap -- the LDAP map being opened.
+**
+** Returns:
+** true if connection is successful, false otherwise.
+**
+** Side Effects:
+** Populates lmap->ldap_ld.
+*/
+
+# if !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT
+static jmp_buf LDAPTimeout;
+static void ldaptimeout __P((int));
+
+/* ARGSUSED */
+static void
+ldaptimeout(unused)
+ int unused;
+{
+ /*
+ ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+ ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+ ** DOING.
+ */
+
+ errno = ETIMEDOUT;
+ longjmp(LDAPTimeout, 1);
+}
+
+
+#define SM_LDAP_SETTIMEOUT(to, where) \
+do \
+{ \
+ if (to != 0) \
+ { \
+ if (setjmp(LDAPTimeout) != 0) \
+ { \
+ if (sm_debug_active(&SmLDAPTrace, 9)) \
+ sm_dprintf("ldap_settimeout(%s)=triggered\n",\
+ where); \
+ errno = ETIMEDOUT; \
+ return false; \
+ } \
+ ev = sm_setevent(to, ldaptimeout, 0); \
+ } \
+} while (0)
+
+#define SM_LDAP_CLEARTIMEOUT() \
+do \
+{ \
+ if (ev != NULL) \
+ sm_clrevent(ev); \
+} while (0)
+# endif /* !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT */
+
+bool
+sm_ldap_start(name, lmap)
+ char *name;
+ SM_LDAP_STRUCT *lmap;
+{
+ int save_errno = 0;
+ char *id;
+ char *errmsg;
+# if !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT
+ SM_EVENT *ev = NULL;
+# endif
+ LDAP *ld = NULL;
+ struct timeval tmo;
+ int msgid, err, r;
+
+ errmsg = NULL;
+ if (sm_debug_active(&SmLDAPTrace, 2))
+ sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
+
+ if (lmap->ldap_host != NULL)
+ id = lmap->ldap_host;
+ else if (lmap->ldap_uri != NULL)
+ id = lmap->ldap_uri;
+ else
+ id = "localhost";
+
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ {
+ /* Don't print a port number for LDAP URIs */
+ if (lmap->ldap_uri != NULL)
+ sm_dprintf("ldapmap_start(%s)\n", id);
+ else
+ sm_dprintf("ldapmap_start(%s, %d)\n", id,
+ lmap->ldap_port);
+ }
+
+ if (lmap->ldap_uri != NULL)
+ {
+# if SM_CONF_LDAP_INITIALIZE
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ sm_dprintf("ldap_initialize(%s)\n", lmap->ldap_uri);
+ /* LDAP server supports URIs so use them directly */
+ save_errno = ldap_initialize(&ld, lmap->ldap_uri);
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ sm_dprintf("ldap_initialize(%s)=%d, ld=%p\n", lmap->ldap_uri, save_errno, ld);
+ sm_ldap_setoptsg(lmap);
+
+# else /* SM_CONF_LDAP_INITIALIZE */
+ LDAPURLDesc *ludp = NULL;
+
+ /* Blast apart URL and use the ldap_init/ldap_open below */
+ err = ldap_url_parse(lmap->ldap_uri, &ludp);
+ if (err != 0)
+ {
+ errno = err + E_LDAPURLBASE;
+ return false;
+ }
+ lmap->ldap_host = sm_strdup_x(ludp->lud_host);
+ if (lmap->ldap_host == NULL)
+ {
+ save_errno = errno;
+ ldap_free_urldesc(ludp);
+ errno = save_errno;
+ return false;
+ }
+ lmap->ldap_port = ludp->lud_port;
+ ldap_free_urldesc(ludp);
+# endif /* SM_CONF_LDAP_INITIALIZE */
+ }
+
+ if (ld == NULL)
+ {
+# if USE_LDAP_INIT
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ sm_dprintf("ldap_init(%s, %d)\n", lmap->ldap_host, lmap->ldap_port);
+ ld = ldap_init(lmap->ldap_host, lmap->ldap_port);
+ save_errno = errno;
+
+# else /* USE_LDAP_INIT */
+ /*
+ ** If using ldap_open(), the actual connection to the server
+ ** happens now so we need the timeout here. For ldap_init(),
+ ** the connection happens at bind time.
+ */
+
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ sm_dprintf("ldap_open(%s, %d)\n", lmap->ldap_host, lmap->ldap_port);
+
+ SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec, "ldap_open");
+ ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
+ save_errno = errno;
+
+ /* clear the event if it has not sprung */
+ SM_LDAP_CLEARTIMEOUT();
+# endif /* USE_LDAP_INIT */
+ }
+
+ errno = save_errno;
+ if (ld == NULL)
+ {
+ if (sm_debug_active(&SmLDAPTrace, 7))
+ sm_dprintf("FAIL: ldap_open(%s, %d)=%d\n", lmap->ldap_host, lmap->ldap_port, save_errno);
+ return false;
+ }
+
+ sm_ldap_setopts(ld, lmap);
+# if USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT
+ /*
+ ** If using ldap_init(), the actual connection to the server
+ ** happens at ldap_bind_s() so we need the timeout here.
+ */
+
+ SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec, "ldap_bind");
+# endif /* USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT */
+
+# ifdef LDAP_AUTH_KRBV4
+ if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
+ lmap->ldap_secret != NULL)
+ {
+ /*
+ ** Need to put ticket in environment here instead of
+ ** during parseargs as there may be different tickets
+ ** for different LDAP connections.
+ */
+
+ (void) putenv(lmap->ldap_secret);
+ }
+# endif /* LDAP_AUTH_KRBV4 */
+
+# if LDAP_NETWORK_TIMEOUT
+ tmo.tv_sec = lmap->ldap_networktmo;
+# else
+ tmo.tv_sec = lmap->ldap_timeout.tv_sec;
+# endif
+ tmo.tv_usec = 0;
+
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ sm_dprintf("ldap_bind(%s)\n", lmap->ldap_uri);
+ errno = 0;
+ msgid = ldap_bind(ld, lmap->ldap_binddn, lmap->ldap_secret,
+ lmap->ldap_method);
+ save_errno = errno;
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ {
+ errmsg = sm_ldap_geterror(ld);
+ sm_dprintf("ldap_bind(%s)=%d, errno=%d, ldaperr=%d, ld_error=%s, tmo=%lld\n",
+ lmap->ldap_uri, msgid, save_errno,
+ sm_ldap_geterrno(ld), errmsg, (long long) tmo.tv_sec);
+ if (NULL != errmsg)
+ {
+ ldap_memfree(errmsg);
+ errmsg = NULL;
+ }
+ }
+ if (-1 == msgid)
+ {
+ r = -1;
+ err = sm_ldap_geterrno(ld);
+ if (LDAP_SUCCESS != err)
+ save_errno = err + E_LDAPBASE;
+ goto fail;
+ }
+
+ errno = 0;
+ r = ldap_result(ld, msgid, LDAP_MSG_ALL,
+ tmo.tv_sec == 0 ? NULL : &(tmo), &(lmap->ldap_res));
+ save_errno = errno;
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ {
+ errmsg = sm_ldap_geterror(ld);
+ sm_dprintf("ldap_result(%s)=%d, errno=%d, ldaperr=%d, ld_error=%s\n",
+ lmap->ldap_uri, r, errno,
+ sm_ldap_geterrno(ld), errmsg);
+ if (NULL != errmsg)
+ {
+ ldap_memfree(errmsg);
+ errmsg = NULL;
+ }
+ }
+ if (-1 == r)
+ {
+ err = sm_ldap_geterrno(ld);
+ if (LDAP_SUCCESS != err)
+ save_errno = err + E_LDAPBASE;
+ goto fail;
+ }
+ if (0 == r)
+ {
+ save_errno = ETIMEDOUT;
+ r = -1;
+ goto fail;
+ }
+ r = ldap_parse_result(ld, lmap->ldap_res, &err, NULL, &errmsg, NULL,
+ NULL, 1);
+ save_errno = errno;
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ sm_dprintf("ldap_parse_result(%s)=%d, err=%d, errmsg=%s\n",
+ lmap->ldap_uri, r, err, errmsg);
+ if (r != LDAP_SUCCESS)
+ goto fail;
+ if (err != LDAP_SUCCESS)
+ {
+ r = err;
+ goto fail;
+ }
+
+# if USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT
+ /* clear the event if it has not sprung */
+ SM_LDAP_CLEARTIMEOUT();
+ if (sm_debug_active(&SmLDAPTrace, 9))
+ sm_dprintf("ldap_cleartimeout(%s)\n", lmap->ldap_uri);
+# endif /* USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT */
+
+ if (r != LDAP_SUCCESS)
+ {
+ fail:
+ if (-1 == r)
+ errno = save_errno;
+ else
+ errno = r + E_LDAPBASE;
+ if (NULL != errmsg)
+ {
+ ldap_memfree(errmsg);
+ errmsg = NULL;
+ }
+ return false;
+ }
+
+ /* Save PID to make sure only this PID closes the LDAP connection */
+ lmap->ldap_pid = getpid();
+ lmap->ldap_ld = ld;
+ if (NULL != errmsg)
+ {
+ ldap_memfree(errmsg);
+ errmsg = NULL;
+ }
+ return true;
+}
+
+/*
+** SM_LDAP_SEARCH_M -- initiate multi-key LDAP search
+**
+** Initiate an LDAP search, return the msgid.
+** The calling function must collect the results.
+**
+** Parameters:
+** lmap -- LDAP map information
+** argv -- key vector of substitutions in LDAP filter
+** NOTE: argv must have SM_LDAP_ARGS elements to prevent
+** out of bound array references
+**
+** Returns:
+** <0 on failure (SM_LDAP_ERR*), msgid on success
+**
+*/
+
+int
+sm_ldap_search_m(lmap, argv)
+ SM_LDAP_STRUCT *lmap;
+ char **argv;
+{
+ int msgid;
+ char *fp, *p, *q;
+ char filter[LDAPMAP_MAX_FILTER + 1];
+
+ SM_REQUIRE(lmap != NULL);
+ SM_REQUIRE(argv != NULL);
+ SM_REQUIRE(argv[0] != NULL);
+
+ memset(filter, '\0', sizeof filter);
+ fp = filter;
+ p = lmap->ldap_filter;
+ while ((q = strchr(p, '%')) != NULL)
+ {
+ char *key;
+
+ if (lmap->ldap_multi_args)
+ {
+# if SM_LDAP_ARGS < 10
+# error _SM_LDAP_ARGS must be 10
+# endif
+ if (q[1] == 's')
+ key = argv[0];
+ else if (q[1] >= '0' && q[1] <= '9')
+ {
+ key = argv[q[1] - '0'];
+ if (key == NULL)
+ {
+# if SM_LDAP_ERROR_ON_MISSING_ARGS
+ return SM_LDAP_ERR_ARG_MISS;
+# else
+ key = "";
+# endif
+ }
+ }
+ else
+ key = NULL;
+ }
+ else
+ key = argv[0];
+
+ if (q[1] == 's')
+ {
+ (void) sm_snprintf(fp, SPACELEFT(filter, fp),
+ "%.*s%s", (int) (q - p), p, key);
+ fp += strlen(fp);
+ p = q + 2;
+ }
+ else if (q[1] == '0' ||
+ (lmap->ldap_multi_args && q[1] >= '0' && q[1] <= '9'))
+ {
+ char *k = key;
+
+ (void) sm_snprintf(fp, SPACELEFT(filter, fp),
+ "%.*s", (int) (q - p), p);
+ fp += strlen(fp);
+ p = q + 2;
+
+ /* Properly escape LDAP special characters */
+ while (SPACELEFT(filter, fp) > 0 &&
+ *k != '\0')
+ {
+ if (*k == '*' || *k == '(' ||
+ *k == ')' || *k == '\\')
+ {
+ (void) sm_strlcat(fp,
+ (*k == '*' ? "\\2A" :
+ (*k == '(' ? "\\28" :
+ (*k == ')' ? "\\29" :
+ (*k == '\\' ? "\\5C" :
+ "\00")))),
+ SPACELEFT(filter, fp));
+ fp += strlen(fp);
+ k++;
+ }
+ else
+ *fp++ = *k++;
+ }
+ }
+ else
+ {
+ (void) sm_snprintf(fp, SPACELEFT(filter, fp),
+ "%.*s", (int) (q - p + 1), p);
+ p = q + (q[1] == '%' ? 2 : 1);
+ fp += strlen(fp);
+ }
+ }
+ (void) sm_strlcpy(fp, p, SPACELEFT(filter, fp));
+ if (sm_debug_active(&SmLDAPTrace, 20))
+ sm_dprintf("ldap search filter=%s\n", filter);
+
+ lmap->ldap_res = NULL;
+ msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base,
+ lmap->ldap_scope, filter,
+ (lmap->ldap_attr[0] == NULL ? NULL :
+ lmap->ldap_attr),
+ lmap->ldap_attrsonly);
+ return msgid;
+}
+
+/*
+** SM_LDAP_SEARCH -- initiate LDAP search
+**
+** Initiate an LDAP search, return the msgid.
+** The calling function must collect the results.
+** Note this is just a wrapper into sm_ldap_search_m()
+**
+** Parameters:
+** lmap -- LDAP map information
+** key -- key to substitute in LDAP filter
+**
+** Returns:
+** <0 on failure, msgid on success
+**
+*/
+
+int
+sm_ldap_search(lmap, key)
+ SM_LDAP_STRUCT *lmap;
+ char *key;
+{
+ char *argv[SM_LDAP_ARGS];
+
+ memset(argv, '\0', sizeof argv);
+ argv[0] = key;
+ return sm_ldap_search_m(lmap, argv);
+}
+
+/*
+** SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
+** particular objectClass
+**
+** Parameters:
+** lmap -- pointer to SM_LDAP_STRUCT in use
+** entry -- current LDAP entry struct
+** ocvalue -- particular objectclass in question.
+** may be of form (fee|foo|fum) meaning
+** any entry can be part of either fee,
+** foo or fum objectclass
+**
+** Returns:
+** true if item has that objectClass
+*/
+
+static bool
+sm_ldap_has_objectclass(lmap, entry, ocvalue)
+ SM_LDAP_STRUCT *lmap;
+ LDAPMessage *entry;
+ char *ocvalue;
+{
+ char **vals = NULL;
+ int i;
+
+ if (ocvalue == NULL)
+ return false;
+
+ vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
+ if (vals == NULL)
+ return false;
+
+ for (i = 0; vals[i] != NULL; i++)
+ {
+ char *p;
+ char *q;
+
+ p = q = ocvalue;
+ while (*p != '\0')
+ {
+ while (*p != '\0' && *p != '|')
+ p++;
+
+ if ((p - q) == strlen(vals[i]) &&
+ sm_strncasecmp(vals[i], q, p - q) == 0)
+ {
+ ldap_value_free(vals);
+ return true;
+ }
+
+ while (*p == '|')
+ p++;
+ q = p;
+ }
+ }
+
+ ldap_value_free(vals);
+ return false;
+}
+
+/*
+** SM_LDAP_RESULTS -- return results from an LDAP lookup in result
+**
+** Parameters:
+** lmap -- pointer to SM_LDAP_STRUCT in use
+** msgid -- msgid returned by sm_ldap_search()
+** flags -- flags for the lookup
+** delim -- delimiter for result concatenation
+** rpool -- memory pool for storage
+** result -- return string
+** recurse -- recursion list
+**
+** Returns:
+** status (sysexit)
+*/
+
+# define SM_LDAP_ERROR_CLEANUP() \
+{ \
+ if (lmap->ldap_res != NULL) \
+ { \
+ ldap_msgfree(lmap->ldap_res); \
+ lmap->ldap_res = NULL; \
+ } \
+ (void) ldap_abandon(lmap->ldap_ld, msgid); \
+}
+
+static SM_LDAP_RECURSE_ENTRY *
+sm_ldap_add_recurse(top, item, type, rpool)
+ SM_LDAP_RECURSE_LIST **top;
+ char *item;
+ int type;
+ SM_RPOOL_T *rpool;
+{
+ int n;
+ int m;
+ int p;
+ int insertat;
+ int moveb;
+ int oldsizeb;
+ int rc;
+ SM_LDAP_RECURSE_ENTRY *newe;
+ SM_LDAP_RECURSE_ENTRY **olddata;
+
+ /*
+ ** This code will maintain a list of
+ ** SM_LDAP_RECURSE_ENTRY structures
+ ** in ascending order.
+ */
+
+ if (*top == NULL)
+ {
+ /* Allocate an initial SM_LDAP_RECURSE_LIST struct */
+ *top = sm_rpool_malloc_x(rpool, sizeof **top);
+ (*top)->lrl_cnt = 0;
+ (*top)->lrl_size = 0;
+ (*top)->lrl_data = NULL;
+ }
+
+ if ((*top)->lrl_cnt >= (*top)->lrl_size)
+ {
+ /* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
+ olddata = (*top)->lrl_data;
+ if ((*top)->lrl_size == 0)
+ {
+ oldsizeb = 0;
+ (*top)->lrl_size = 256;
+ }
+ else
+ {
+ oldsizeb = (*top)->lrl_size * sizeof *((*top)->lrl_data);
+ (*top)->lrl_size *= 2;
+ }
+ (*top)->lrl_data = sm_rpool_malloc_x(rpool,
+ (*top)->lrl_size * sizeof *((*top)->lrl_data));
+ if (oldsizeb > 0)
+ memcpy((*top)->lrl_data, olddata, oldsizeb);
+ }
+
+ /*
+ ** Binary search/insert item:type into list.
+ ** Return current entry pointer if already exists.
+ */
+
+ n = 0;
+ m = (*top)->lrl_cnt - 1;
+ if (m < 0)
+ insertat = 0;
+ else
+ insertat = -1;
+
+ while (insertat == -1)
+ {
+ p = (m + n) / 2;
+
+ rc = sm_strcasecmp(item, (*top)->lrl_data[p]->lr_search);
+ if (rc == 0)
+ rc = type - (*top)->lrl_data[p]->lr_type;
+
+ if (rc < 0)
+ m = p - 1;
+ else if (rc > 0)
+ n = p + 1;
+ else
+ return (*top)->lrl_data[p];
+
+ if (m == -1)
+ insertat = 0;
+ else if (n >= (*top)->lrl_cnt)
+ insertat = (*top)->lrl_cnt;
+ else if (m < n)
+ insertat = m + 1;
+ }
+
+ /*
+ ** Not found in list, make room
+ ** at insert point and add it.
+ */
+
+ newe = sm_rpool_malloc_x(rpool, sizeof *newe);
+ if (newe != NULL)
+ {
+ moveb = ((*top)->lrl_cnt - insertat) * sizeof *((*top)->lrl_data);
+ if (moveb > 0)
+ memmove(&((*top)->lrl_data[insertat + 1]),
+ &((*top)->lrl_data[insertat]),
+ moveb);
+
+ newe->lr_search = sm_rpool_strdup_x(rpool, item);
+ newe->lr_type = type;
+ newe->lr_ludp = NULL;
+ newe->lr_attrs = NULL;
+ newe->lr_done = false;
+
+ ((*top)->lrl_data)[insertat] = newe;
+ (*top)->lrl_cnt++;
+ }
+ return newe;
+}
+
+int
+sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
+ resultln, resultsz, recurse)
+ SM_LDAP_STRUCT *lmap;
+ int msgid;
+ int flags;
+ int delim;
+ SM_RPOOL_T *rpool;
+ char **result;
+ int *resultln;
+ int *resultsz;
+ SM_LDAP_RECURSE_LIST *recurse;
+{
+ bool toplevel;
+ int i;
+ int statp;
+ int vsize;
+ int ret;
+ int save_errno;
+ char *p;
+ SM_LDAP_RECURSE_ENTRY *rl;
+
+ /* Are we the top top level of the search? */
+ toplevel = (recurse == NULL);
+
+ /* Get results */
+ statp = EX_NOTFOUND;
+ while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
+ (lmap->ldap_timeout.tv_sec == 0 ? NULL :
+ &(lmap->ldap_timeout)),
+ &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
+ {
+ LDAPMessage *entry;
+
+ /* If we don't want multiple values and we have one, break */
+ if ((char) delim == '\0' &&
+ !bitset(SM_LDAP_SINGLEMATCH, flags) &&
+ *result != NULL)
+ break;
+
+ /* Cycle through all entries */
+ for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
+ entry != NULL;
+ entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
+ {
+ BerElement *ber;
+ char *attr;
+ char **vals = NULL;
+ char *dn;
+
+ /*
+ ** If matching only and found an entry,
+ ** no need to spin through attributes
+ */
+
+ if (bitset(SM_LDAP_MATCHONLY, flags))
+ {
+ statp = EX_OK;
+ continue;
+ }
+
+# if _FFR_LDAP_SINGLEDN
+ if (bitset(SM_LDAP_SINGLEDN, flags) && *result != NULL)
+ {
+ /* only wanted one match */
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOENT;
+ return EX_NOTFOUND;
+ }
+# endif /* _FFR_LDAP_SINGLEDN */
+
+ /* record completed DN's to prevent loops */
+ dn = ldap_get_dn(lmap->ldap_ld, entry);
+ if (dn == NULL)
+ {
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ save_errno += E_LDAPBASE;
+ SM_LDAP_ERROR_CLEANUP();
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+
+ rl = sm_ldap_add_recurse(&recurse, dn,
+ SM_LDAP_ATTR_DN,
+ rpool);
+
+ if (rl == NULL)
+ {
+ ldap_memfree(dn);
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOMEM;
+ return EX_OSERR;
+ }
+ else if (rl->lr_done)
+ {
+ /* already on list, skip it */
+ ldap_memfree(dn);
+ continue;
+ }
+ ldap_memfree(dn);
+
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
+ /*
+ ** Reset value to prevent lingering
+ ** LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see below)
+ */
+
+ lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
+ for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
+ &ber);
+ attr != NULL;
+ attr = ldap_next_attribute(lmap->ldap_ld, entry,
+ ber))
+ {
+ char *tmp, *vp_tmp;
+ int type;
+ char *needobjclass = NULL;
+
+ type = SM_LDAP_ATTR_NONE;
+ for (i = 0; lmap->ldap_attr[i] != NULL; i++)
+ {
+ if (SM_STRCASEEQ(lmap->ldap_attr[i],
+ attr))
+ {
+ type = lmap->ldap_attr_type[i];
+ needobjclass = lmap->ldap_attr_needobjclass[i];
+ break;
+ }
+ }
+
+ if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
+ type == SM_LDAP_ATTR_NONE)
+ {
+ /* URL lookups specify attrs to use */
+ type = SM_LDAP_ATTR_NORMAL;
+ needobjclass = NULL;
+ }
+
+ if (type == SM_LDAP_ATTR_NONE)
+ {
+ /* attribute not requested */
+ ldap_memfree(attr);
+ SM_LDAP_ERROR_CLEANUP();
+ errno = EFAULT;
+ return EX_SOFTWARE;
+ }
+
+ /*
+ ** For recursion on a particular attribute,
+ ** we may need to see if this entry is
+ ** part of a particular objectclass.
+ ** Also, ignore objectClass attribute.
+ ** Otherwise we just ignore this attribute.
+ */
+
+ if (type == SM_LDAP_ATTR_OBJCLASS ||
+ (needobjclass != NULL &&
+ !sm_ldap_has_objectclass(lmap, entry,
+ needobjclass)))
+ {
+ ldap_memfree(attr);
+ continue;
+ }
+
+ if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
+ {
+ vals = ldap_get_values(lmap->ldap_ld,
+ entry,
+ attr);
+ if (vals == NULL)
+ {
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ if (save_errno == LDAP_SUCCESS)
+ {
+ ldap_memfree(attr);
+ continue;
+ }
+
+ /* Must be an error */
+ save_errno += E_LDAPBASE;
+ ldap_memfree(attr);
+ SM_LDAP_ERROR_CLEANUP();
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+ }
+
+ statp = EX_OK;
+
+# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
+ /*
+ ** Reset value to prevent lingering
+ ** LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see below)
+ */
+
+ lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
+# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
+
+ /*
+ ** If matching only,
+ ** no need to spin through entries
+ */
+
+ if (bitset(SM_LDAP_MATCHONLY, flags))
+ {
+ if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
+ ldap_value_free(vals);
+ ldap_memfree(attr);
+ continue;
+ }
+
+ /*
+ ** If we don't want multiple values,
+ ** return first found.
+ */
+
+ if ((char) delim == '\0')
+ {
+ if (*result != NULL)
+ {
+ /* already have a value */
+ if (bitset(SM_LDAP_SINGLEMATCH,
+ flags))
+ {
+ /* only wanted one match */
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOENT;
+ return EX_NOTFOUND;
+ }
+ break;
+ }
+
+ if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
+ {
+ *result = sm_rpool_strdup_x(rpool,
+ attr);
+ ldap_memfree(attr);
+ break;
+ }
+
+ if (vals[0] == NULL)
+ {
+ ldap_value_free(vals);
+ ldap_memfree(attr);
+ continue;
+ }
+
+ vsize = strlen(vals[0]) + 1;
+ if (lmap->ldap_attrsep != '\0')
+ vsize += strlen(attr) + 1;
+ *result = sm_rpool_malloc_x(rpool,
+ vsize);
+ if (lmap->ldap_attrsep != '\0')
+ sm_snprintf(*result, vsize,
+ "%s%c%s",
+ attr,
+ lmap->ldap_attrsep,
+ vals[0]);
+ else
+ sm_strlcpy(*result, vals[0],
+ vsize);
+ ldap_value_free(vals);
+ ldap_memfree(attr);
+ break;
+ }
+
+ /* attributes only */
+ if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
+ {
+ if (*result == NULL)
+ *result = sm_rpool_strdup_x(rpool,
+ attr);
+ else
+ {
+ if (bitset(SM_LDAP_SINGLEMATCH,
+ flags) &&
+ *result != NULL)
+ {
+ /* only wanted one match */
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOENT;
+ return EX_NOTFOUND;
+ }
+
+ vsize = strlen(*result) +
+ strlen(attr) + 2;
+ tmp = sm_rpool_malloc_x(rpool,
+ vsize);
+ (void) sm_snprintf(tmp,
+ vsize, "%s%c%s",
+ *result, (char) delim,
+ attr);
+ *result = tmp;
+ }
+ ldap_memfree(attr);
+ continue;
+ }
+
+ /*
+ ** If there is more than one, munge then
+ ** into a map_coldelim separated string.
+ ** If we are recursing we may have an entry
+ ** with no 'normal' values to put in the
+ ** string.
+ ** This is not an error.
+ */
+
+ if (type == SM_LDAP_ATTR_NORMAL &&
+ bitset(SM_LDAP_SINGLEMATCH, flags) &&
+ *result != NULL)
+ {
+ /* only wanted one match */
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOENT;
+ return EX_NOTFOUND;
+ }
+
+ vsize = 0;
+ for (i = 0; vals[i] != NULL; i++)
+ {
+ if (type == SM_LDAP_ATTR_DN ||
+ type == SM_LDAP_ATTR_FILTER ||
+ type == SM_LDAP_ATTR_URL)
+ {
+ /* add to recursion */
+ if (sm_ldap_add_recurse(&recurse,
+ vals[i],
+ type,
+ rpool) == NULL)
+ {
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOMEM;
+ return EX_OSERR;
+ }
+ continue;
+ }
+
+ vsize += strlen(vals[i]) + 1;
+ if (lmap->ldap_attrsep != '\0')
+ vsize += strlen(attr) + 1;
+ }
+
+ /*
+ ** Create/Append to string any normal
+ ** attribute values. Otherwise, just free
+ ** memory and move on to the next
+ ** attribute in this entry.
+ */
+
+ if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
+ {
+ char *pe;
+
+ /* Grow result string if needed */
+ if ((*resultln + vsize) >= *resultsz)
+ {
+ while ((*resultln + vsize) >= *resultsz)
+ {
+ if (*resultsz == 0)
+ *resultsz = 1024;
+ else
+ *resultsz *= 2;
+ }
+
+ vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
+ *vp_tmp = '\0';
+
+ if (*result != NULL)
+ sm_strlcpy(vp_tmp,
+ *result,
+ *resultsz);
+ *result = vp_tmp;
+ }
+
+ p = *result + *resultln;
+ pe = *result + *resultsz;
+
+ for (i = 0; vals[i] != NULL; i++)
+ {
+ if (*resultln > 0 &&
+ p < pe)
+ *p++ = (char) delim;
+
+ if (lmap->ldap_attrsep != '\0')
+ {
+ p += sm_strlcpy(p, attr,
+ pe - p);
+ if (p < pe)
+ *p++ = lmap->ldap_attrsep;
+ }
+
+ p += sm_strlcpy(p, vals[i],
+ pe - p);
+ *resultln = p - (*result);
+ if (p >= pe)
+ {
+ /* Internal error: buffer too small for LDAP values */
+ SM_LDAP_ERROR_CLEANUP();
+ errno = ENOMEM;
+ return EX_OSERR;
+ }
+ }
+ }
+
+ ldap_value_free(vals);
+ ldap_memfree(attr);
+ }
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+
+ /*
+ ** We check save_errno != LDAP_DECODING_ERROR since
+ ** OpenLDAP 1.X has a very ugly *undocumented*
+ ** hack of returning this error code from
+ ** ldap_next_attribute() if the library freed the
+ ** ber attribute. See:
+ ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
+ */
+
+ if (save_errno != LDAP_SUCCESS &&
+ save_errno != LDAP_DECODING_ERROR)
+ {
+ /* Must be an error */
+ save_errno += E_LDAPBASE;
+ SM_LDAP_ERROR_CLEANUP();
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+
+ /* mark this DN as done */
+ rl->lr_done = true;
+ if (rl->lr_ludp != NULL)
+ {
+ ldap_free_urldesc(rl->lr_ludp);
+ rl->lr_ludp = NULL;
+ }
+ if (rl->lr_attrs != NULL)
+ {
+ free(rl->lr_attrs);
+ rl->lr_attrs = NULL;
+ }
+
+ /* We don't want multiple values and we have one */
+ if ((char) delim == '\0' &&
+ !bitset(SM_LDAP_SINGLEMATCH, flags) &&
+ *result != NULL)
+ break;
+ }
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ if (save_errno != LDAP_SUCCESS &&
+ save_errno != LDAP_DECODING_ERROR)
+ {
+ /* Must be an error */
+ save_errno += E_LDAPBASE;
+ SM_LDAP_ERROR_CLEANUP();
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+ ldap_msgfree(lmap->ldap_res);
+ lmap->ldap_res = NULL;
+ }
+
+ if (ret == 0)
+ save_errno = ETIMEDOUT;
+ else if (ret == LDAP_RES_SEARCH_RESULT)
+ {
+ /*
+ ** We may have gotten an LDAP_RES_SEARCH_RESULT response
+ ** with an error inside it, so we have to extract that
+ ** with ldap_parse_result(). This can happen when talking
+ ** to an LDAP proxy whose backend has gone down.
+ */
+
+ if (lmap->ldap_res == NULL)
+ save_errno = LDAP_UNAVAILABLE;
+ else
+ {
+ int rc;
+
+ save_errno = ldap_parse_result(lmap->ldap_ld,
+ lmap->ldap_res, &rc, NULL, NULL,
+ NULL, NULL, 0);
+ if (save_errno == LDAP_SUCCESS)
+ save_errno = rc;
+ }
+ }
+ else
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ if (save_errno != LDAP_SUCCESS)
+ {
+ statp = EX_TEMPFAIL;
+ switch (save_errno)
+ {
+# ifdef LDAP_SERVER_DOWN
+ case LDAP_SERVER_DOWN:
+# endif
+ case LDAP_TIMEOUT:
+ case ETIMEDOUT:
+ case LDAP_UNAVAILABLE:
+
+ /*
+ ** server disappeared,
+ ** try reopen on next search
+ */
+
+ statp = EX_RESTART;
+ break;
+ }
+ if (ret != 0)
+ save_errno += E_LDAPBASE;
+ SM_LDAP_ERROR_CLEANUP();
+ errno = save_errno;
+ return statp;
+ }
+
+ if (lmap->ldap_res != NULL)
+ {
+ ldap_msgfree(lmap->ldap_res);
+ lmap->ldap_res = NULL;
+ }
+
+ if (toplevel)
+ {
+ int rlidx;
+
+ /*
+ ** Spin through the built-up recurse list at the top
+ ** of the recursion. Since new items are added at the
+ ** end of the shared list, we actually only ever get
+ ** one level of recursion before things pop back to the
+ ** top. Any items added to the list during that recursion
+ ** will be expanded by the top level.
+ */
+
+ for (rlidx = 0; recurse != NULL && rlidx < recurse->lrl_cnt;
+ rlidx++)
+ {
+ int newflags;
+ int sid;
+ int status;
+
+ rl = recurse->lrl_data[rlidx];
+
+ newflags = flags;
+ if (rl->lr_done)
+ {
+ /* already expanded */
+ continue;
+ }
+
+ if (rl->lr_type == SM_LDAP_ATTR_DN)
+ {
+ /* do DN search */
+ sid = ldap_search(lmap->ldap_ld,
+ rl->lr_search,
+ lmap->ldap_scope,
+ "(objectClass=*)",
+ (lmap->ldap_attr[0] == NULL ?
+ NULL : lmap->ldap_attr),
+ lmap->ldap_attrsonly);
+ }
+ else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
+ {
+ /* do new search */
+ sid = ldap_search(lmap->ldap_ld,
+ lmap->ldap_base,
+ lmap->ldap_scope,
+ rl->lr_search,
+ (lmap->ldap_attr[0] == NULL ?
+ NULL : lmap->ldap_attr),
+ lmap->ldap_attrsonly);
+ }
+ else if (rl->lr_type == SM_LDAP_ATTR_URL)
+ {
+ /* Parse URL */
+ sid = ldap_url_parse(rl->lr_search,
+ &rl->lr_ludp);
+
+ if (sid != 0)
+ {
+ errno = sid + E_LDAPURLBASE;
+ return EX_TEMPFAIL;
+ }
+
+ /* We need to add objectClass */
+ if (rl->lr_ludp->lud_attrs != NULL)
+ {
+ int attrnum = 0;
+
+ while (rl->lr_ludp->lud_attrs[attrnum] != NULL)
+ {
+ if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum],
+ "objectClass") == 0)
+ {
+ /* already requested */
+ attrnum = -1;
+ break;
+ }
+ attrnum++;
+ }
+
+ if (attrnum >= 0)
+ {
+ int i;
+
+ rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2));
+ if (rl->lr_attrs == NULL)
+ {
+ save_errno = errno;
+ ldap_free_urldesc(rl->lr_ludp);
+ errno = save_errno;
+ return EX_TEMPFAIL;
+ }
+ for (i = 0 ; i < attrnum; i++)
+ {
+ rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i];
+ }
+ rl->lr_attrs[i++] = "objectClass";
+ rl->lr_attrs[i++] = NULL;
+ }
+ }
+
+ /*
+ ** Use the existing connection
+ ** for this search. It really
+ ** should use lud_scheme://lud_host:lud_port/
+ ** instead but that would require
+ ** opening a new connection.
+ ** This should be fixed ASAP.
+ */
+
+ sid = ldap_search(lmap->ldap_ld,
+ rl->lr_ludp->lud_dn,
+ rl->lr_ludp->lud_scope,
+ rl->lr_ludp->lud_filter,
+ rl->lr_attrs,
+ lmap->ldap_attrsonly);
+
+ /* Use the attributes specified by URL */
+ newflags |= SM_LDAP_USE_ALLATTR;
+ }
+ else
+ {
+ /* unknown or illegal attribute type */
+ errno = EFAULT;
+ return EX_SOFTWARE;
+ }
+
+ /* Collect results */
+ if (sid == -1)
+ {
+ save_errno = sm_ldap_geterrno(lmap->ldap_ld);
+ statp = EX_TEMPFAIL;
+ switch (save_errno)
+ {
+# ifdef LDAP_SERVER_DOWN
+ case LDAP_SERVER_DOWN:
+# endif
+ case LDAP_TIMEOUT:
+ case ETIMEDOUT:
+ case LDAP_UNAVAILABLE:
+
+ /*
+ ** server disappeared,
+ ** try reopen on next search
+ */
+
+ statp = EX_RESTART;
+ break;
+ }
+ errno = save_errno + E_LDAPBASE;
+ return statp;
+ }
+
+ status = sm_ldap_results(lmap, sid, newflags, delim,
+ rpool, result, resultln,
+ resultsz, recurse);
+ save_errno = errno;
+ if (status != EX_OK && status != EX_NOTFOUND)
+ {
+ errno = save_errno;
+ return status;
+ }
+
+ /* Mark as done */
+ rl->lr_done = true;
+ if (rl->lr_ludp != NULL)
+ {
+ ldap_free_urldesc(rl->lr_ludp);
+ rl->lr_ludp = NULL;
+ }
+ if (rl->lr_attrs != NULL)
+ {
+ free(rl->lr_attrs);
+ rl->lr_attrs = NULL;
+ }
+
+ /* Reset rlidx as new items may have been added */
+ rlidx = -1;
+ }
+ }
+ return statp;
+}
+
+/*
+** SM_LDAP_CLOSE -- close LDAP connection
+**
+** Parameters:
+** lmap -- LDAP map information
+**
+** Returns:
+** None.
+*/
+
+void
+sm_ldap_close(lmap)
+ SM_LDAP_STRUCT *lmap;
+{
+ if (lmap->ldap_ld == NULL)
+ return;
+
+ if (lmap->ldap_pid == getpid())
+ ldap_unbind(lmap->ldap_ld);
+ lmap->ldap_ld = NULL;
+ lmap->ldap_pid = 0;
+}
+
+/*
+** SM_LDAP_GETERRNO -- get ldap errno value
+**
+** Parameters:
+** ld -- LDAP session handle
+**
+** Returns:
+** LDAP errno.
+*/
+
+int
+sm_ldap_geterrno(ld)
+ LDAP *ld;
+{
+ int err = LDAP_SUCCESS;
+
+# if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
+# ifdef LDAP_OPT_RESULT_CODE
+# define LDAP_GET_RESULT_CODE LDAP_OPT_RESULT_CODE
+# else
+# define LDAP_GET_RESULT_CODE LDAP_OPT_ERROR_NUMBER
+# endif
+ (void) ldap_get_option(ld, LDAP_GET_RESULT_CODE, &err);
+# else
+# ifdef LDAP_OPT_SIZELIMIT
+ err = ldap_get_lderrno(ld, NULL, NULL);
+# else
+ err = ld->ld_errno;
+
+ /*
+ ** Reset value to prevent lingering LDAP_DECODING_ERROR due to
+ ** OpenLDAP 1.X's hack (see above)
+ */
+
+ ld->ld_errno = LDAP_SUCCESS;
+# endif /* LDAP_OPT_SIZELIMIT */
+# endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
+ return err;
+}
+
+/*
+** SM_LDAP_GETERROR -- get ldap error value
+**
+** Parameters:
+** ld -- LDAP session handle
+**
+** Returns:
+** LDAP error
+*/
+
+static char *
+sm_ldap_geterror(ld)
+ LDAP *ld;
+{
+ char *error = NULL;
+
+# if defined(LDAP_OPT_DIAGNOSTIC_MESSAGE)
+ (void) ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &error);
+# endif
+ return error;
+}
+
+
+#endif /* LDAPMAP */