190792Sgshapiro/*
2261370Sgshapiro * Copyright (c) 2001-2009 Proofpoint, Inc. and its suppliers.
390792Sgshapiro *      All rights reserved.
490792Sgshapiro *
590792Sgshapiro * By using this file, you agree to the terms and conditions set
690792Sgshapiro * forth in the LICENSE file which can be found at the top level of
790792Sgshapiro * the sendmail distribution.
890792Sgshapiro */
990792Sgshapiro
10168515Sgshapiro/* some "deprecated" calls are used, e.g., ldap_get_values() */
11168515Sgshapiro#define LDAP_DEPRECATED	1
12168515Sgshapiro
1390792Sgshapiro#include <sm/gen.h>
14266711SgshapiroSM_RCSID("@(#)$Id: ldap.c,v 1.86 2013-11-22 20:51:43 ca Exp $")
1590792Sgshapiro
1690792Sgshapiro#if LDAPMAP
1790792Sgshapiro# include <sys/types.h>
1890792Sgshapiro# include <errno.h>
1990792Sgshapiro# include <setjmp.h>
2090792Sgshapiro# include <stdlib.h>
2190792Sgshapiro# include <unistd.h>
2290792Sgshapiro
2390792Sgshapiro# include <sm/bitops.h>
2490792Sgshapiro# include <sm/clock.h>
2590792Sgshapiro# include <sm/conf.h>
2690792Sgshapiro# include <sm/debug.h>
2790792Sgshapiro# include <sm/errstring.h>
2890792Sgshapiro# include <sm/ldap.h>
2990792Sgshapiro# include <sm/string.h>
3094334Sgshapiro#  ifdef EX_OK
3194334Sgshapiro#   undef EX_OK			/* for SVr4.2 SMP */
3294334Sgshapiro#  endif /* EX_OK */
3390792Sgshapiro# include <sm/sysexits.h>
3490792Sgshapiro
3590792SgshapiroSM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
3690792Sgshapiro	"@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
3790792Sgshapiro
3890792Sgshapirostatic void	ldaptimeout __P((int));
39141858Sgshapirostatic bool	sm_ldap_has_objectclass __P((SM_LDAP_STRUCT *, LDAPMessage *, char *));
40141858Sgshapirostatic SM_LDAP_RECURSE_ENTRY *sm_ldap_add_recurse __P((SM_LDAP_RECURSE_LIST **, char *, int, SM_RPOOL_T *));
4190792Sgshapiro
4290792Sgshapiro/*
4390792Sgshapiro**  SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
4490792Sgshapiro**
4590792Sgshapiro**	Parameters:
4690792Sgshapiro**		lmap -- pointer to SM_LDAP_STRUCT to clear
4790792Sgshapiro**
4890792Sgshapiro**	Returns:
4990792Sgshapiro**		None.
5090792Sgshapiro**
5190792Sgshapiro*/
5290792Sgshapiro
53157001Sgshapiro#if _FFR_LDAP_VERSION
54157001Sgshapiro# if defined(LDAP_VERSION_MAX) && _FFR_LDAP_VERSION > LDAP_VERSION_MAX
55157001Sgshapiro    ERROR FFR_LDAP_VERSION > _LDAP_VERSION_MAX
56157001Sgshapiro# endif /* defined(LDAP_VERSION_MAX) && _FFR_LDAP_VERSION > LDAP_VERSION_MAX */
57157001Sgshapiro# if defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN
58157001Sgshapiro    ERROR FFR_LDAP_VERSION < _LDAP_VERSION_MIN
59157001Sgshapiro# endif /* defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN */
60157001Sgshapiro# define SM_LDAP_VERSION_DEFAULT	_FFR_LDAP_VERSION
61157001Sgshapiro#else /* _FFR_LDAP_VERSION */
62157001Sgshapiro# define SM_LDAP_VERSION_DEFAULT	0
63157001Sgshapiro#endif /* _FFR_LDAP_VERSION */
64157001Sgshapiro
6590792Sgshapirovoid
6690792Sgshapirosm_ldap_clear(lmap)
6790792Sgshapiro	SM_LDAP_STRUCT *lmap;
6890792Sgshapiro{
6990792Sgshapiro	if (lmap == NULL)
7090792Sgshapiro		return;
7190792Sgshapiro
72132943Sgshapiro	lmap->ldap_host = NULL;
7390792Sgshapiro	lmap->ldap_port = LDAP_PORT;
74132943Sgshapiro	lmap->ldap_uri = NULL;
75157001Sgshapiro	lmap->ldap_version = SM_LDAP_VERSION_DEFAULT;
7690792Sgshapiro	lmap->ldap_deref = LDAP_DEREF_NEVER;
7790792Sgshapiro	lmap->ldap_timelimit = LDAP_NO_LIMIT;
7890792Sgshapiro	lmap->ldap_sizelimit = LDAP_NO_LIMIT;
7990792Sgshapiro# ifdef LDAP_REFERRALS
8090792Sgshapiro	lmap->ldap_options = LDAP_OPT_REFERRALS;
8190792Sgshapiro# else /* LDAP_REFERRALS */
8290792Sgshapiro	lmap->ldap_options = 0;
8390792Sgshapiro# endif /* LDAP_REFERRALS */
8490792Sgshapiro	lmap->ldap_attrsep = '\0';
8590792Sgshapiro	lmap->ldap_binddn = NULL;
8690792Sgshapiro	lmap->ldap_secret = NULL;
8790792Sgshapiro	lmap->ldap_method = LDAP_AUTH_SIMPLE;
8890792Sgshapiro	lmap->ldap_base = NULL;
8990792Sgshapiro	lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
9090792Sgshapiro	lmap->ldap_attrsonly = LDAPMAP_FALSE;
9190792Sgshapiro	lmap->ldap_timeout.tv_sec = 0;
9290792Sgshapiro	lmap->ldap_timeout.tv_usec = 0;
9390792Sgshapiro	lmap->ldap_ld = NULL;
9490792Sgshapiro	lmap->ldap_filter = NULL;
9590792Sgshapiro	lmap->ldap_attr[0] = NULL;
9694334Sgshapiro	lmap->ldap_attr_type[0] = SM_LDAP_ATTR_NONE;
9794334Sgshapiro	lmap->ldap_attr_needobjclass[0] = NULL;
9890792Sgshapiro	lmap->ldap_res = NULL;
9990792Sgshapiro	lmap->ldap_next = NULL;
10090792Sgshapiro	lmap->ldap_pid = 0;
101168515Sgshapiro	lmap->ldap_multi_args = false;
10290792Sgshapiro}
10390792Sgshapiro
10490792Sgshapiro/*
10590792Sgshapiro**  SM_LDAP_START -- actually connect to an LDAP server
10690792Sgshapiro**
10790792Sgshapiro**	Parameters:
10890792Sgshapiro**		name -- name of map for debug output.
10990792Sgshapiro**		lmap -- the LDAP map being opened.
11090792Sgshapiro**
11190792Sgshapiro**	Returns:
11290792Sgshapiro**		true if connection is successful, false otherwise.
11390792Sgshapiro**
11490792Sgshapiro**	Side Effects:
11590792Sgshapiro**		Populates lmap->ldap_ld.
11690792Sgshapiro*/
11790792Sgshapiro
11890792Sgshapirostatic jmp_buf	LDAPTimeout;
11990792Sgshapiro
12090792Sgshapiro#define SM_LDAP_SETTIMEOUT(to)						\
12190792Sgshapirodo									\
12290792Sgshapiro{									\
12390792Sgshapiro	if (to != 0)							\
12490792Sgshapiro	{								\
12590792Sgshapiro		if (setjmp(LDAPTimeout) != 0)				\
12690792Sgshapiro		{							\
12790792Sgshapiro			errno = ETIMEDOUT;				\
12890792Sgshapiro			return false;					\
12990792Sgshapiro		}							\
13090792Sgshapiro		ev = sm_setevent(to, ldaptimeout, 0);			\
13190792Sgshapiro	}								\
13290792Sgshapiro} while (0)
13390792Sgshapiro
13490792Sgshapiro#define SM_LDAP_CLEARTIMEOUT()						\
13590792Sgshapirodo									\
13690792Sgshapiro{									\
13790792Sgshapiro	if (ev != NULL)							\
13890792Sgshapiro		sm_clrevent(ev);					\
13990792Sgshapiro} while (0)
14090792Sgshapiro
14190792Sgshapirobool
14290792Sgshapirosm_ldap_start(name, lmap)
14390792Sgshapiro	char *name;
14490792Sgshapiro	SM_LDAP_STRUCT *lmap;
14590792Sgshapiro{
14690792Sgshapiro	int bind_result;
147141858Sgshapiro	int save_errno = 0;
148132943Sgshapiro	char *id;
14990792Sgshapiro	SM_EVENT *ev = NULL;
150132943Sgshapiro	LDAP *ld = NULL;
15190792Sgshapiro
15290792Sgshapiro	if (sm_debug_active(&SmLDAPTrace, 2))
15390792Sgshapiro		sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
15490792Sgshapiro
155132943Sgshapiro	if (lmap->ldap_host != NULL)
156132943Sgshapiro		id = lmap->ldap_host;
157132943Sgshapiro	else if (lmap->ldap_uri != NULL)
158132943Sgshapiro		id = lmap->ldap_uri;
159132943Sgshapiro	else
160132943Sgshapiro		id = "localhost";
161132943Sgshapiro
16290792Sgshapiro	if (sm_debug_active(&SmLDAPTrace, 9))
163132943Sgshapiro	{
164132943Sgshapiro		/* Don't print a port number for LDAP URIs */
165132943Sgshapiro		if (lmap->ldap_uri != NULL)
166132943Sgshapiro			sm_dprintf("ldapmap_start(%s)\n", id);
167132943Sgshapiro		else
168132943Sgshapiro			sm_dprintf("ldapmap_start(%s, %d)\n", id,
169132943Sgshapiro				   lmap->ldap_port);
170132943Sgshapiro	}
17190792Sgshapiro
172132943Sgshapiro	if (lmap->ldap_uri != NULL)
173132943Sgshapiro	{
174132943Sgshapiro#if SM_CONF_LDAP_INITIALIZE
175132943Sgshapiro		/* LDAP server supports URIs so use them directly */
176132943Sgshapiro		save_errno = ldap_initialize(&ld, lmap->ldap_uri);
177132943Sgshapiro#else /* SM_CONF_LDAP_INITIALIZE */
178132943Sgshapiro		int err;
179132943Sgshapiro		LDAPURLDesc *ludp = NULL;
180132943Sgshapiro
181132943Sgshapiro		/* Blast apart URL and use the ldap_init/ldap_open below */
182132943Sgshapiro		err = ldap_url_parse(lmap->ldap_uri, &ludp);
183132943Sgshapiro		if (err != 0)
184132943Sgshapiro		{
185132943Sgshapiro			errno = err + E_LDAPURLBASE;
186132943Sgshapiro			return false;
187132943Sgshapiro		}
188132943Sgshapiro		lmap->ldap_host = sm_strdup_x(ludp->lud_host);
189132943Sgshapiro		if (lmap->ldap_host == NULL)
190132943Sgshapiro		{
191132943Sgshapiro			save_errno = errno;
192132943Sgshapiro			ldap_free_urldesc(ludp);
193132943Sgshapiro			errno = save_errno;
194132943Sgshapiro			return false;
195132943Sgshapiro		}
196132943Sgshapiro		lmap->ldap_port = ludp->lud_port;
197132943Sgshapiro		ldap_free_urldesc(ludp);
198132943Sgshapiro#endif /* SM_CONF_LDAP_INITIALIZE */
199132943Sgshapiro	}
200132943Sgshapiro
201132943Sgshapiro	if (ld == NULL)
202132943Sgshapiro	{
20390792Sgshapiro# if USE_LDAP_INIT
204132943Sgshapiro		ld = ldap_init(lmap->ldap_host, lmap->ldap_port);
205132943Sgshapiro		save_errno = errno;
20690792Sgshapiro# else /* USE_LDAP_INIT */
207132943Sgshapiro		/*
208132943Sgshapiro		**  If using ldap_open(), the actual connection to the server
209132943Sgshapiro		**  happens now so we need the timeout here.  For ldap_init(),
210132943Sgshapiro		**  the connection happens at bind time.
211132943Sgshapiro		*/
21290792Sgshapiro
213132943Sgshapiro		SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
214132943Sgshapiro		ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
215132943Sgshapiro		save_errno = errno;
21690792Sgshapiro
217132943Sgshapiro		/* clear the event if it has not sprung */
218132943Sgshapiro		SM_LDAP_CLEARTIMEOUT();
21990792Sgshapiro# endif /* USE_LDAP_INIT */
220132943Sgshapiro	}
22190792Sgshapiro
22290792Sgshapiro	errno = save_errno;
22390792Sgshapiro	if (ld == NULL)
22490792Sgshapiro		return false;
22590792Sgshapiro
22690792Sgshapiro	sm_ldap_setopts(ld, lmap);
22790792Sgshapiro
22890792Sgshapiro# if USE_LDAP_INIT
22990792Sgshapiro	/*
23090792Sgshapiro	**  If using ldap_init(), the actual connection to the server
23190792Sgshapiro	**  happens at ldap_bind_s() so we need the timeout here.
23290792Sgshapiro	*/
23390792Sgshapiro
23490792Sgshapiro	SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
23590792Sgshapiro# endif /* USE_LDAP_INIT */
23690792Sgshapiro
23790792Sgshapiro# ifdef LDAP_AUTH_KRBV4
23890792Sgshapiro	if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
23990792Sgshapiro	    lmap->ldap_secret != NULL)
24090792Sgshapiro	{
24190792Sgshapiro		/*
24290792Sgshapiro		**  Need to put ticket in environment here instead of
24390792Sgshapiro		**  during parseargs as there may be different tickets
24490792Sgshapiro		**  for different LDAP connections.
24590792Sgshapiro		*/
24690792Sgshapiro
24790792Sgshapiro		(void) putenv(lmap->ldap_secret);
24890792Sgshapiro	}
24990792Sgshapiro# endif /* LDAP_AUTH_KRBV4 */
25090792Sgshapiro
25190792Sgshapiro	bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
25290792Sgshapiro				  lmap->ldap_secret, lmap->ldap_method);
25390792Sgshapiro
25490792Sgshapiro# if USE_LDAP_INIT
25590792Sgshapiro	/* clear the event if it has not sprung */
25690792Sgshapiro	SM_LDAP_CLEARTIMEOUT();
25790792Sgshapiro# endif /* USE_LDAP_INIT */
25890792Sgshapiro
25990792Sgshapiro	if (bind_result != LDAP_SUCCESS)
26090792Sgshapiro	{
26190792Sgshapiro		errno = bind_result + E_LDAPBASE;
26290792Sgshapiro		return false;
26390792Sgshapiro	}
26490792Sgshapiro
26590792Sgshapiro	/* Save PID to make sure only this PID closes the LDAP connection */
26690792Sgshapiro	lmap->ldap_pid = getpid();
26790792Sgshapiro	lmap->ldap_ld = ld;
26890792Sgshapiro	return true;
26990792Sgshapiro}
27090792Sgshapiro
27190792Sgshapiro/* ARGSUSED */
27290792Sgshapirostatic void
27390792Sgshapiroldaptimeout(unused)
27490792Sgshapiro	int unused;
27590792Sgshapiro{
27690792Sgshapiro	/*
27790792Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
27890792Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
27990792Sgshapiro	**	DOING.
28090792Sgshapiro	*/
28190792Sgshapiro
28290792Sgshapiro	errno = ETIMEDOUT;
28390792Sgshapiro	longjmp(LDAPTimeout, 1);
28490792Sgshapiro}
28590792Sgshapiro
28690792Sgshapiro/*
287168515Sgshapiro**  SM_LDAP_SEARCH_M -- initiate multi-key LDAP search
28890792Sgshapiro**
28990792Sgshapiro**	Initiate an LDAP search, return the msgid.
29090792Sgshapiro**	The calling function must collect the results.
29190792Sgshapiro**
29290792Sgshapiro**	Parameters:
29390792Sgshapiro**		lmap -- LDAP map information
294168515Sgshapiro**		argv -- key vector of substitutions in LDAP filter
295168515Sgshapiro**		        NOTE: argv must have SM_LDAP_ARGS elements to prevent
296168515Sgshapiro**			      out of bound array references
29790792Sgshapiro**
29890792Sgshapiro**	Returns:
299168515Sgshapiro**		<0 on failure (SM_LDAP_ERR*), msgid on success
30090792Sgshapiro**
30190792Sgshapiro*/
30290792Sgshapiro
30390792Sgshapiroint
304168515Sgshapirosm_ldap_search_m(lmap, argv)
30590792Sgshapiro	SM_LDAP_STRUCT *lmap;
306168515Sgshapiro	char **argv;
30790792Sgshapiro{
30890792Sgshapiro	int msgid;
30990792Sgshapiro	char *fp, *p, *q;
31090792Sgshapiro	char filter[LDAPMAP_MAX_FILTER + 1];
31190792Sgshapiro
312168515Sgshapiro	SM_REQUIRE(lmap != NULL);
313168515Sgshapiro	SM_REQUIRE(argv != NULL);
314168515Sgshapiro	SM_REQUIRE(argv[0] != NULL);
315168515Sgshapiro
31690792Sgshapiro	memset(filter, '\0', sizeof filter);
31790792Sgshapiro	fp = filter;
31890792Sgshapiro	p = lmap->ldap_filter;
31990792Sgshapiro	while ((q = strchr(p, '%')) != NULL)
32090792Sgshapiro	{
321168515Sgshapiro		char *key;
322168515Sgshapiro
323168515Sgshapiro		if (lmap->ldap_multi_args)
324168515Sgshapiro		{
325168515Sgshapiro#if SM_LDAP_ARGS < 10
326168515Sgshapiro# ERROR _SM_LDAP_ARGS must be 10
327168515Sgshapiro#endif /* SM_LDAP_ARGS < 10 */
328168515Sgshapiro			if (q[1] == 's')
329168515Sgshapiro				key = argv[0];
330168515Sgshapiro			else if (q[1] >= '0' && q[1] <= '9')
331168515Sgshapiro			{
332168515Sgshapiro				key = argv[q[1] - '0'];
333168515Sgshapiro				if (key == NULL)
334168515Sgshapiro				{
335168515Sgshapiro# if SM_LDAP_ERROR_ON_MISSING_ARGS
336168515Sgshapiro					return SM_LDAP_ERR_ARG_MISS;
337168515Sgshapiro# else /* SM_LDAP_ERROR_ON_MISSING_ARGS */
338168515Sgshapiro					key = "";
339168515Sgshapiro# endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
340168515Sgshapiro				}
341168515Sgshapiro			}
342168515Sgshapiro			else
343168515Sgshapiro				key = NULL;
344168515Sgshapiro		}
345168515Sgshapiro		else
346168515Sgshapiro			key = argv[0];
347168515Sgshapiro
34890792Sgshapiro		if (q[1] == 's')
34990792Sgshapiro		{
35090792Sgshapiro			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
35190792Sgshapiro					   "%.*s%s", (int) (q - p), p, key);
35290792Sgshapiro			fp += strlen(fp);
35390792Sgshapiro			p = q + 2;
35490792Sgshapiro		}
355168515Sgshapiro		else if (q[1] == '0' ||
356168515Sgshapiro			 (lmap->ldap_multi_args && q[1] >= '0' && q[1] <= '9'))
35790792Sgshapiro		{
35890792Sgshapiro			char *k = key;
35990792Sgshapiro
36090792Sgshapiro			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
36190792Sgshapiro					   "%.*s", (int) (q - p), p);
36290792Sgshapiro			fp += strlen(fp);
36390792Sgshapiro			p = q + 2;
36490792Sgshapiro
36590792Sgshapiro			/* Properly escape LDAP special characters */
36690792Sgshapiro			while (SPACELEFT(filter, fp) > 0 &&
36790792Sgshapiro			       *k != '\0')
36890792Sgshapiro			{
36990792Sgshapiro				if (*k == '*' || *k == '(' ||
37090792Sgshapiro				    *k == ')' || *k == '\\')
37190792Sgshapiro				{
37290792Sgshapiro					(void) sm_strlcat(fp,
37390792Sgshapiro						       (*k == '*' ? "\\2A" :
37490792Sgshapiro							(*k == '(' ? "\\28" :
37590792Sgshapiro							 (*k == ')' ? "\\29" :
37690792Sgshapiro							  (*k == '\\' ? "\\5C" :
37790792Sgshapiro							   "\00")))),
37890792Sgshapiro						SPACELEFT(filter, fp));
37990792Sgshapiro					fp += strlen(fp);
38090792Sgshapiro					k++;
38190792Sgshapiro				}
38290792Sgshapiro				else
38390792Sgshapiro					*fp++ = *k++;
38490792Sgshapiro			}
38590792Sgshapiro		}
38690792Sgshapiro		else
38790792Sgshapiro		{
38890792Sgshapiro			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
38990792Sgshapiro				"%.*s", (int) (q - p + 1), p);
39090792Sgshapiro			p = q + (q[1] == '%' ? 2 : 1);
39190792Sgshapiro			fp += strlen(fp);
39290792Sgshapiro		}
39390792Sgshapiro	}
39490792Sgshapiro	(void) sm_strlcpy(fp, p, SPACELEFT(filter, fp));
39590792Sgshapiro	if (sm_debug_active(&SmLDAPTrace, 20))
39690792Sgshapiro		sm_dprintf("ldap search filter=%s\n", filter);
39790792Sgshapiro
39890792Sgshapiro	lmap->ldap_res = NULL;
39994334Sgshapiro	msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base,
40094334Sgshapiro			    lmap->ldap_scope, filter,
40190792Sgshapiro			    (lmap->ldap_attr[0] == NULL ? NULL :
40290792Sgshapiro			     lmap->ldap_attr),
40390792Sgshapiro			    lmap->ldap_attrsonly);
40490792Sgshapiro	return msgid;
40590792Sgshapiro}
40690792Sgshapiro
40790792Sgshapiro/*
408168515Sgshapiro**  SM_LDAP_SEARCH -- initiate LDAP search
409168515Sgshapiro**
410168515Sgshapiro**	Initiate an LDAP search, return the msgid.
411168515Sgshapiro**	The calling function must collect the results.
412168515Sgshapiro**	Note this is just a wrapper into sm_ldap_search_m()
413168515Sgshapiro**
414168515Sgshapiro**	Parameters:
415168515Sgshapiro**		lmap -- LDAP map information
416168515Sgshapiro**		key -- key to substitute in LDAP filter
417168515Sgshapiro**
418168515Sgshapiro**	Returns:
419168515Sgshapiro**		<0 on failure, msgid on success
420168515Sgshapiro**
421168515Sgshapiro*/
422168515Sgshapiro
423168515Sgshapiroint
424168515Sgshapirosm_ldap_search(lmap, key)
425168515Sgshapiro	SM_LDAP_STRUCT *lmap;
426168515Sgshapiro	char *key;
427168515Sgshapiro{
428168515Sgshapiro	char *argv[SM_LDAP_ARGS];
429168515Sgshapiro
430168515Sgshapiro	memset(argv, '\0', sizeof argv);
431168515Sgshapiro	argv[0] = key;
432168515Sgshapiro	return sm_ldap_search_m(lmap, argv);
433168515Sgshapiro}
434168515Sgshapiro
435168515Sgshapiro/*
43694334Sgshapiro**  SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
43794334Sgshapiro**			       particular objectClass
43894334Sgshapiro**
43994334Sgshapiro**	Parameters:
44094334Sgshapiro**		lmap -- pointer to SM_LDAP_STRUCT in use
44194334Sgshapiro**		entry -- current LDAP entry struct
44294334Sgshapiro**		ocvalue -- particular objectclass in question.
44394334Sgshapiro**			   may be of form (fee|foo|fum) meaning
44494334Sgshapiro**			   any entry can be part of either fee,
44594334Sgshapiro**			   foo or fum objectclass
44694334Sgshapiro**
44794334Sgshapiro**	Returns:
44894334Sgshapiro**		true if item has that objectClass
44994334Sgshapiro*/
45094334Sgshapiro
45194334Sgshapirostatic bool
45294334Sgshapirosm_ldap_has_objectclass(lmap, entry, ocvalue)
45394334Sgshapiro	SM_LDAP_STRUCT *lmap;
45494334Sgshapiro	LDAPMessage *entry;
45594334Sgshapiro	char *ocvalue;
45694334Sgshapiro{
45794334Sgshapiro	char **vals = NULL;
45894334Sgshapiro	int i;
45994334Sgshapiro
46094334Sgshapiro	if (ocvalue == NULL)
46194334Sgshapiro		return false;
46294334Sgshapiro
46394334Sgshapiro	vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
46494334Sgshapiro	if (vals == NULL)
46594334Sgshapiro		return false;
46694334Sgshapiro
46794334Sgshapiro	for (i = 0; vals[i] != NULL; i++)
46894334Sgshapiro	{
46994334Sgshapiro		char *p;
47094334Sgshapiro		char *q;
47194334Sgshapiro
47294334Sgshapiro		p = q = ocvalue;
47394334Sgshapiro		while (*p != '\0')
47494334Sgshapiro		{
47594334Sgshapiro			while (*p != '\0' && *p != '|')
47694334Sgshapiro				p++;
47794334Sgshapiro
47894334Sgshapiro			if ((p - q) == strlen(vals[i]) &&
47994334Sgshapiro			    sm_strncasecmp(vals[i], q, p - q) == 0)
48094334Sgshapiro			{
48194334Sgshapiro				ldap_value_free(vals);
48294334Sgshapiro				return true;
48394334Sgshapiro			}
48494334Sgshapiro
48594334Sgshapiro			while (*p == '|')
48694334Sgshapiro				p++;
48794334Sgshapiro			q = p;
48894334Sgshapiro		}
48994334Sgshapiro	}
49094334Sgshapiro
49194334Sgshapiro	ldap_value_free(vals);
49294334Sgshapiro	return false;
49394334Sgshapiro}
49494334Sgshapiro
49594334Sgshapiro/*
49690792Sgshapiro**  SM_LDAP_RESULTS -- return results from an LDAP lookup in result
49790792Sgshapiro**
49890792Sgshapiro**	Parameters:
49990792Sgshapiro**		lmap -- pointer to SM_LDAP_STRUCT in use
50090792Sgshapiro**		msgid -- msgid returned by sm_ldap_search()
50190792Sgshapiro**		flags -- flags for the lookup
50290792Sgshapiro**		delim -- delimiter for result concatenation
50390792Sgshapiro**		rpool -- memory pool for storage
50490792Sgshapiro**		result -- return string
50590792Sgshapiro**		recurse -- recursion list
50690792Sgshapiro**
50790792Sgshapiro**	Returns:
50890792Sgshapiro**		status (sysexit)
50990792Sgshapiro*/
51090792Sgshapiro
51194334Sgshapiro# define SM_LDAP_ERROR_CLEANUP()				\
51290792Sgshapiro{								\
51390792Sgshapiro	if (lmap->ldap_res != NULL)				\
51490792Sgshapiro	{							\
51590792Sgshapiro		ldap_msgfree(lmap->ldap_res);			\
51690792Sgshapiro		lmap->ldap_res = NULL;				\
51790792Sgshapiro	}							\
51890792Sgshapiro	(void) ldap_abandon(lmap->ldap_ld, msgid);		\
51990792Sgshapiro}
52090792Sgshapiro
52194334Sgshapirostatic SM_LDAP_RECURSE_ENTRY *
52294334Sgshapirosm_ldap_add_recurse(top, item, type, rpool)
52390792Sgshapiro	SM_LDAP_RECURSE_LIST **top;
52490792Sgshapiro	char *item;
52590792Sgshapiro	int type;
52690792Sgshapiro	SM_RPOOL_T *rpool;
52790792Sgshapiro{
52894334Sgshapiro	int n;
52994334Sgshapiro	int m;
53094334Sgshapiro	int p;
53194334Sgshapiro	int insertat;
53294334Sgshapiro	int moveb;
53394334Sgshapiro	int oldsizeb;
53494334Sgshapiro	int rc;
53594334Sgshapiro	SM_LDAP_RECURSE_ENTRY *newe;
53694334Sgshapiro	SM_LDAP_RECURSE_ENTRY **olddata;
53790792Sgshapiro
53894334Sgshapiro	/*
53994334Sgshapiro	**  This code will maintain a list of
54094334Sgshapiro	**  SM_LDAP_RECURSE_ENTRY structures
54194334Sgshapiro	**  in ascending order.
54294334Sgshapiro	*/
54394334Sgshapiro
54494334Sgshapiro	if (*top == NULL)
54590792Sgshapiro	{
54694334Sgshapiro		/* Allocate an initial SM_LDAP_RECURSE_LIST struct */
54794334Sgshapiro		*top = sm_rpool_malloc_x(rpool, sizeof **top);
548168515Sgshapiro		(*top)->lrl_cnt = 0;
549168515Sgshapiro		(*top)->lrl_size = 0;
550168515Sgshapiro		(*top)->lrl_data = NULL;
55194334Sgshapiro	}
55294334Sgshapiro
553168515Sgshapiro	if ((*top)->lrl_cnt >= (*top)->lrl_size)
55494334Sgshapiro	{
55594334Sgshapiro		/* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
556168515Sgshapiro		olddata = (*top)->lrl_data;
557168515Sgshapiro		if ((*top)->lrl_size == 0)
55890792Sgshapiro		{
55994334Sgshapiro			oldsizeb = 0;
560168515Sgshapiro			(*top)->lrl_size = 256;
56190792Sgshapiro		}
56294334Sgshapiro		else
56394334Sgshapiro		{
564168515Sgshapiro			oldsizeb = (*top)->lrl_size * sizeof *((*top)->lrl_data);
565168515Sgshapiro			(*top)->lrl_size *= 2;
56694334Sgshapiro		}
567168515Sgshapiro		(*top)->lrl_data = sm_rpool_malloc_x(rpool,
568168515Sgshapiro						    (*top)->lrl_size * sizeof *((*top)->lrl_data));
56994334Sgshapiro		if (oldsizeb > 0)
570168515Sgshapiro			memcpy((*top)->lrl_data, olddata, oldsizeb);
57190792Sgshapiro	}
57290792Sgshapiro
57394334Sgshapiro	/*
57494334Sgshapiro	**  Binary search/insert item:type into list.
57594334Sgshapiro	**  Return current entry pointer if already exists.
57694334Sgshapiro	*/
57794334Sgshapiro
57894334Sgshapiro	n = 0;
579168515Sgshapiro	m = (*top)->lrl_cnt - 1;
58094334Sgshapiro	if (m < 0)
58194334Sgshapiro		insertat = 0;
58290792Sgshapiro	else
58394334Sgshapiro		insertat = -1;
58494334Sgshapiro
58594334Sgshapiro	while (insertat == -1)
58694334Sgshapiro	{
58794334Sgshapiro		p = (m + n) / 2;
58894334Sgshapiro
589168515Sgshapiro		rc = sm_strcasecmp(item, (*top)->lrl_data[p]->lr_search);
59094334Sgshapiro		if (rc == 0)
591168515Sgshapiro			rc = type - (*top)->lrl_data[p]->lr_type;
59294334Sgshapiro
59394334Sgshapiro		if (rc < 0)
59494334Sgshapiro			m = p - 1;
59594334Sgshapiro		else if (rc > 0)
59694334Sgshapiro			n = p + 1;
59794334Sgshapiro		else
598168515Sgshapiro			return (*top)->lrl_data[p];
59994334Sgshapiro
60094334Sgshapiro		if (m == -1)
60194334Sgshapiro			insertat = 0;
602168515Sgshapiro		else if (n >= (*top)->lrl_cnt)
603168515Sgshapiro			insertat = (*top)->lrl_cnt;
60494334Sgshapiro		else if (m < n)
60594334Sgshapiro			insertat = m + 1;
60694334Sgshapiro	}
60794334Sgshapiro
60894334Sgshapiro	/*
60994334Sgshapiro	** Not found in list, make room
61094334Sgshapiro	** at insert point and add it.
61194334Sgshapiro	*/
61294334Sgshapiro
61394334Sgshapiro	newe = sm_rpool_malloc_x(rpool, sizeof *newe);
61494334Sgshapiro	if (newe != NULL)
61594334Sgshapiro	{
616168515Sgshapiro		moveb = ((*top)->lrl_cnt - insertat) * sizeof *((*top)->lrl_data);
61794334Sgshapiro		if (moveb > 0)
618168515Sgshapiro			memmove(&((*top)->lrl_data[insertat + 1]),
619168515Sgshapiro				&((*top)->lrl_data[insertat]),
62094334Sgshapiro				moveb);
62194334Sgshapiro
62294334Sgshapiro		newe->lr_search = sm_rpool_strdup_x(rpool, item);
62394334Sgshapiro		newe->lr_type = type;
624132943Sgshapiro		newe->lr_ludp = NULL;
625132943Sgshapiro		newe->lr_attrs = NULL;
62694334Sgshapiro		newe->lr_done = false;
62794334Sgshapiro
628168515Sgshapiro		((*top)->lrl_data)[insertat] = newe;
629168515Sgshapiro		(*top)->lrl_cnt++;
63094334Sgshapiro	}
63194334Sgshapiro	return newe;
63290792Sgshapiro}
63390792Sgshapiro
63490792Sgshapiroint
63594334Sgshapirosm_ldap_results(lmap, msgid, flags, delim, rpool, result,
63694334Sgshapiro		resultln, resultsz, recurse)
63790792Sgshapiro	SM_LDAP_STRUCT *lmap;
63890792Sgshapiro	int msgid;
63990792Sgshapiro	int flags;
64094334Sgshapiro	int delim;
64190792Sgshapiro	SM_RPOOL_T *rpool;
64290792Sgshapiro	char **result;
64394334Sgshapiro	int *resultln;
64494334Sgshapiro	int *resultsz;
64590792Sgshapiro	SM_LDAP_RECURSE_LIST *recurse;
64690792Sgshapiro{
64790792Sgshapiro	bool toplevel;
64890792Sgshapiro	int i;
64990792Sgshapiro	int statp;
65090792Sgshapiro	int vsize;
65190792Sgshapiro	int ret;
65290792Sgshapiro	int save_errno;
65390792Sgshapiro	char *p;
65494334Sgshapiro	SM_LDAP_RECURSE_ENTRY *rl;
65590792Sgshapiro
65690792Sgshapiro	/* Are we the top top level of the search? */
65790792Sgshapiro	toplevel = (recurse == NULL);
65890792Sgshapiro
65990792Sgshapiro	/* Get results */
66090792Sgshapiro	statp = EX_NOTFOUND;
66190792Sgshapiro	while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
66290792Sgshapiro				  (lmap->ldap_timeout.tv_sec == 0 ? NULL :
66390792Sgshapiro				   &(lmap->ldap_timeout)),
66490792Sgshapiro				  &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
66590792Sgshapiro	{
66690792Sgshapiro		LDAPMessage *entry;
66790792Sgshapiro
66890792Sgshapiro		/* If we don't want multiple values and we have one, break */
669147078Sgshapiro		if ((char) delim == '\0' &&
670147078Sgshapiro		    !bitset(SM_LDAP_SINGLEMATCH, flags) &&
671147078Sgshapiro		    *result != NULL)
67290792Sgshapiro			break;
67390792Sgshapiro
67490792Sgshapiro		/* Cycle through all entries */
67590792Sgshapiro		for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
67690792Sgshapiro		     entry != NULL;
67790792Sgshapiro		     entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
67890792Sgshapiro		{
67990792Sgshapiro			BerElement *ber;
68090792Sgshapiro			char *attr;
68190792Sgshapiro			char **vals = NULL;
68290792Sgshapiro			char *dn;
68390792Sgshapiro
68490792Sgshapiro			/*
68590792Sgshapiro			**  If matching only and found an entry,
68690792Sgshapiro			**  no need to spin through attributes
68790792Sgshapiro			*/
68890792Sgshapiro
689125820Sgshapiro			if (bitset(SM_LDAP_MATCHONLY, flags))
690125820Sgshapiro			{
691125820Sgshapiro				statp = EX_OK;
69290792Sgshapiro				continue;
693125820Sgshapiro			}
69490792Sgshapiro
695157001Sgshapiro#if _FFR_LDAP_SINGLEDN
696157001Sgshapiro			if (bitset(SM_LDAP_SINGLEDN, flags) && *result != NULL)
697157001Sgshapiro			{
698157001Sgshapiro				/* only wanted one match */
699157001Sgshapiro				SM_LDAP_ERROR_CLEANUP();
700157001Sgshapiro				errno = ENOENT;
701157001Sgshapiro				return EX_NOTFOUND;
702157001Sgshapiro			}
703157001Sgshapiro#endif /* _FFR_LDAP_SINGLEDN */
704157001Sgshapiro
70590792Sgshapiro			/* record completed DN's to prevent loops */
70690792Sgshapiro			dn = ldap_get_dn(lmap->ldap_ld, entry);
70790792Sgshapiro			if (dn == NULL)
70890792Sgshapiro			{
70990792Sgshapiro				save_errno = sm_ldap_geterrno(lmap->ldap_ld);
71090792Sgshapiro				save_errno += E_LDAPBASE;
71194334Sgshapiro				SM_LDAP_ERROR_CLEANUP();
71290792Sgshapiro				errno = save_errno;
713120256Sgshapiro				return EX_TEMPFAIL;
71490792Sgshapiro			}
71590792Sgshapiro
71694334Sgshapiro			rl = sm_ldap_add_recurse(&recurse, dn,
71794334Sgshapiro						 SM_LDAP_ATTR_DN,
71894334Sgshapiro						 rpool);
71994334Sgshapiro
72094334Sgshapiro			if (rl == NULL)
72190792Sgshapiro			{
72290792Sgshapiro				ldap_memfree(dn);
72394334Sgshapiro				SM_LDAP_ERROR_CLEANUP();
72490792Sgshapiro				errno = ENOMEM;
72590792Sgshapiro				return EX_OSERR;
72694334Sgshapiro			}
72794334Sgshapiro			else if (rl->lr_done)
72894334Sgshapiro			{
72990792Sgshapiro				/* already on list, skip it */
73090792Sgshapiro				ldap_memfree(dn);
73190792Sgshapiro				continue;
73290792Sgshapiro			}
73390792Sgshapiro			ldap_memfree(dn);
73490792Sgshapiro
73590792Sgshapiro# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
73690792Sgshapiro			/*
73790792Sgshapiro			**  Reset value to prevent lingering
73890792Sgshapiro			**  LDAP_DECODING_ERROR due to
73990792Sgshapiro			**  OpenLDAP 1.X's hack (see below)
74090792Sgshapiro			*/
74190792Sgshapiro
74290792Sgshapiro			lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
74390792Sgshapiro# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
74490792Sgshapiro
74590792Sgshapiro			for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
74690792Sgshapiro							 &ber);
74790792Sgshapiro			     attr != NULL;
74890792Sgshapiro			     attr = ldap_next_attribute(lmap->ldap_ld, entry,
74990792Sgshapiro							ber))
75090792Sgshapiro			{
75190792Sgshapiro				char *tmp, *vp_tmp;
75290792Sgshapiro				int type;
75394334Sgshapiro				char *needobjclass = NULL;
75490792Sgshapiro
75594334Sgshapiro				type = SM_LDAP_ATTR_NONE;
75690792Sgshapiro				for (i = 0; lmap->ldap_attr[i] != NULL; i++)
75790792Sgshapiro				{
75890792Sgshapiro					if (sm_strcasecmp(lmap->ldap_attr[i],
75990792Sgshapiro							  attr) == 0)
76090792Sgshapiro					{
76190792Sgshapiro						type = lmap->ldap_attr_type[i];
76294334Sgshapiro						needobjclass = lmap->ldap_attr_needobjclass[i];
76390792Sgshapiro						break;
76490792Sgshapiro					}
76590792Sgshapiro				}
76694334Sgshapiro
76794334Sgshapiro				if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
76894334Sgshapiro				    type == SM_LDAP_ATTR_NONE)
76990792Sgshapiro				{
77094334Sgshapiro					/* URL lookups specify attrs to use */
77194334Sgshapiro					type = SM_LDAP_ATTR_NORMAL;
77294334Sgshapiro					needobjclass = NULL;
77394334Sgshapiro				}
77494334Sgshapiro
77594334Sgshapiro				if (type == SM_LDAP_ATTR_NONE)
77694334Sgshapiro				{
77790792Sgshapiro					/* attribute not requested */
77890792Sgshapiro					ldap_memfree(attr);
77994334Sgshapiro					SM_LDAP_ERROR_CLEANUP();
78090792Sgshapiro					errno = EFAULT;
78190792Sgshapiro					return EX_SOFTWARE;
78290792Sgshapiro				}
78390792Sgshapiro
78494334Sgshapiro				/*
78594334Sgshapiro				**  For recursion on a particular attribute,
78694334Sgshapiro				**  we may need to see if this entry is
78794334Sgshapiro				**  part of a particular objectclass.
78894334Sgshapiro				**  Also, ignore objectClass attribute.
78994334Sgshapiro				**  Otherwise we just ignore this attribute.
79094334Sgshapiro				*/
79194334Sgshapiro
79294334Sgshapiro				if (type == SM_LDAP_ATTR_OBJCLASS ||
79394334Sgshapiro				    (needobjclass != NULL &&
79494334Sgshapiro				     !sm_ldap_has_objectclass(lmap, entry,
79594334Sgshapiro							      needobjclass)))
79694334Sgshapiro				{
79794334Sgshapiro					ldap_memfree(attr);
79894334Sgshapiro					continue;
79994334Sgshapiro				}
80094334Sgshapiro
80190792Sgshapiro				if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
80290792Sgshapiro				{
80390792Sgshapiro					vals = ldap_get_values(lmap->ldap_ld,
80490792Sgshapiro							       entry,
80590792Sgshapiro							       attr);
80690792Sgshapiro					if (vals == NULL)
80790792Sgshapiro					{
80890792Sgshapiro						save_errno = sm_ldap_geterrno(lmap->ldap_ld);
80990792Sgshapiro						if (save_errno == LDAP_SUCCESS)
81090792Sgshapiro						{
81190792Sgshapiro							ldap_memfree(attr);
81290792Sgshapiro							continue;
81390792Sgshapiro						}
81490792Sgshapiro
81590792Sgshapiro						/* Must be an error */
81690792Sgshapiro						save_errno += E_LDAPBASE;
81790792Sgshapiro						ldap_memfree(attr);
81894334Sgshapiro						SM_LDAP_ERROR_CLEANUP();
81990792Sgshapiro						errno = save_errno;
82090792Sgshapiro						return EX_TEMPFAIL;
82190792Sgshapiro					}
82290792Sgshapiro				}
82390792Sgshapiro
82490792Sgshapiro				statp = EX_OK;
82590792Sgshapiro
82690792Sgshapiro# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
82790792Sgshapiro				/*
82890792Sgshapiro				**  Reset value to prevent lingering
82990792Sgshapiro				**  LDAP_DECODING_ERROR due to
83090792Sgshapiro				**  OpenLDAP 1.X's hack (see below)
83190792Sgshapiro				*/
83290792Sgshapiro
83390792Sgshapiro				lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
83490792Sgshapiro# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
83590792Sgshapiro
83690792Sgshapiro				/*
83790792Sgshapiro				**  If matching only,
83890792Sgshapiro				**  no need to spin through entries
83990792Sgshapiro				*/
84090792Sgshapiro
84190792Sgshapiro				if (bitset(SM_LDAP_MATCHONLY, flags))
84290792Sgshapiro				{
84390792Sgshapiro					if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
84490792Sgshapiro						ldap_value_free(vals);
84590792Sgshapiro					ldap_memfree(attr);
84690792Sgshapiro					continue;
84790792Sgshapiro				}
84890792Sgshapiro
84990792Sgshapiro				/*
85090792Sgshapiro				**  If we don't want multiple values,
85190792Sgshapiro				**  return first found.
85290792Sgshapiro				*/
85390792Sgshapiro
85494334Sgshapiro				if ((char) delim == '\0')
85590792Sgshapiro				{
85694334Sgshapiro					if (*result != NULL)
85794334Sgshapiro					{
85894334Sgshapiro						/* already have a value */
859147078Sgshapiro						if (bitset(SM_LDAP_SINGLEMATCH,
860147078Sgshapiro							   flags))
861147078Sgshapiro						{
862147078Sgshapiro							/* only wanted one match */
863147078Sgshapiro							SM_LDAP_ERROR_CLEANUP();
864147078Sgshapiro							errno = ENOENT;
865147078Sgshapiro							return EX_NOTFOUND;
866147078Sgshapiro						}
86794334Sgshapiro						break;
86894334Sgshapiro					}
86994334Sgshapiro
87090792Sgshapiro					if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
87190792Sgshapiro					{
87290792Sgshapiro						*result = sm_rpool_strdup_x(rpool,
87390792Sgshapiro									    attr);
87490792Sgshapiro						ldap_memfree(attr);
87590792Sgshapiro						break;
87690792Sgshapiro					}
87790792Sgshapiro
87890792Sgshapiro					if (vals[0] == NULL)
87990792Sgshapiro					{
88090792Sgshapiro						ldap_value_free(vals);
88190792Sgshapiro						ldap_memfree(attr);
88290792Sgshapiro						continue;
88390792Sgshapiro					}
88490792Sgshapiro
88590792Sgshapiro					vsize = strlen(vals[0]) + 1;
88690792Sgshapiro					if (lmap->ldap_attrsep != '\0')
88790792Sgshapiro						vsize += strlen(attr) + 1;
88890792Sgshapiro					*result = sm_rpool_malloc_x(rpool,
88990792Sgshapiro								    vsize);
89090792Sgshapiro					if (lmap->ldap_attrsep != '\0')
89190792Sgshapiro						sm_snprintf(*result, vsize,
89290792Sgshapiro							    "%s%c%s",
89390792Sgshapiro							    attr,
89490792Sgshapiro							    lmap->ldap_attrsep,
89590792Sgshapiro							    vals[0]);
89690792Sgshapiro					else
89790792Sgshapiro						sm_strlcpy(*result, vals[0],
89890792Sgshapiro							   vsize);
89990792Sgshapiro					ldap_value_free(vals);
90090792Sgshapiro					ldap_memfree(attr);
90190792Sgshapiro					break;
90290792Sgshapiro				}
90390792Sgshapiro
90490792Sgshapiro				/* attributes only */
90590792Sgshapiro				if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
90690792Sgshapiro				{
90790792Sgshapiro					if (*result == NULL)
90890792Sgshapiro						*result = sm_rpool_strdup_x(rpool,
90990792Sgshapiro									    attr);
91090792Sgshapiro					else
91190792Sgshapiro					{
91294334Sgshapiro						if (bitset(SM_LDAP_SINGLEMATCH,
91394334Sgshapiro							   flags) &&
91494334Sgshapiro						    *result != NULL)
91594334Sgshapiro						{
91694334Sgshapiro							/* only wanted one match */
91794334Sgshapiro							SM_LDAP_ERROR_CLEANUP();
91894334Sgshapiro							errno = ENOENT;
91994334Sgshapiro							return EX_NOTFOUND;
92094334Sgshapiro						}
92194334Sgshapiro
92290792Sgshapiro						vsize = strlen(*result) +
92390792Sgshapiro							strlen(attr) + 2;
92490792Sgshapiro						tmp = sm_rpool_malloc_x(rpool,
92590792Sgshapiro									vsize);
92690792Sgshapiro						(void) sm_snprintf(tmp,
92790792Sgshapiro							vsize, "%s%c%s",
92894334Sgshapiro							*result, (char) delim,
92990792Sgshapiro							attr);
93090792Sgshapiro						*result = tmp;
93190792Sgshapiro					}
93290792Sgshapiro					ldap_memfree(attr);
93390792Sgshapiro					continue;
93490792Sgshapiro				}
93590792Sgshapiro
93690792Sgshapiro				/*
93794334Sgshapiro				**  If there is more than one, munge then
93894334Sgshapiro				**  into a map_coldelim separated string.
93994334Sgshapiro				**  If we are recursing we may have an entry
94094334Sgshapiro				**  with no 'normal' values to put in the
94194334Sgshapiro				**  string.
94294334Sgshapiro				**  This is not an error.
94390792Sgshapiro				*/
94490792Sgshapiro
94594334Sgshapiro				if (type == SM_LDAP_ATTR_NORMAL &&
94694334Sgshapiro				    bitset(SM_LDAP_SINGLEMATCH, flags) &&
94794334Sgshapiro				    *result != NULL)
94894334Sgshapiro				{
94994334Sgshapiro					/* only wanted one match */
95094334Sgshapiro					SM_LDAP_ERROR_CLEANUP();
95194334Sgshapiro					errno = ENOENT;
95294334Sgshapiro					return EX_NOTFOUND;
95394334Sgshapiro				}
95494334Sgshapiro
95590792Sgshapiro				vsize = 0;
95690792Sgshapiro				for (i = 0; vals[i] != NULL; i++)
95790792Sgshapiro				{
95894334Sgshapiro					if (type == SM_LDAP_ATTR_DN ||
95994334Sgshapiro					    type == SM_LDAP_ATTR_FILTER ||
96094334Sgshapiro					    type == SM_LDAP_ATTR_URL)
96190792Sgshapiro					{
96294334Sgshapiro						/* add to recursion */
96394334Sgshapiro						if (sm_ldap_add_recurse(&recurse,
96490792Sgshapiro									vals[i],
96594334Sgshapiro									type,
96694334Sgshapiro									rpool) == NULL)
96790792Sgshapiro						{
96894334Sgshapiro							SM_LDAP_ERROR_CLEANUP();
96990792Sgshapiro							errno = ENOMEM;
97090792Sgshapiro							return EX_OSERR;
97190792Sgshapiro						}
97290792Sgshapiro						continue;
97390792Sgshapiro					}
97494334Sgshapiro
97590792Sgshapiro					vsize += strlen(vals[i]) + 1;
97690792Sgshapiro					if (lmap->ldap_attrsep != '\0')
97790792Sgshapiro						vsize += strlen(attr) + 1;
97890792Sgshapiro				}
97990792Sgshapiro
98094334Sgshapiro				/*
98194334Sgshapiro				**  Create/Append to string any normal
98294334Sgshapiro				**  attribute values.  Otherwise, just free
98394334Sgshapiro				**  memory and move on to the next
98494334Sgshapiro				**  attribute in this entry.
98594334Sgshapiro				*/
98694334Sgshapiro
98794334Sgshapiro				if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
98890792Sgshapiro				{
98994334Sgshapiro					char *pe;
99094334Sgshapiro
99194334Sgshapiro					/* Grow result string if needed */
99294334Sgshapiro					if ((*resultln + vsize) >= *resultsz)
99390792Sgshapiro					{
99494334Sgshapiro						while ((*resultln + vsize) >= *resultsz)
99594334Sgshapiro						{
99694334Sgshapiro							if (*resultsz == 0)
99794334Sgshapiro								*resultsz = 1024;
99894334Sgshapiro							else
99994334Sgshapiro								*resultsz *= 2;
100094334Sgshapiro						}
100194334Sgshapiro
100294334Sgshapiro						vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
100394334Sgshapiro						*vp_tmp = '\0';
100494334Sgshapiro
100594334Sgshapiro						if (*result != NULL)
100694334Sgshapiro							sm_strlcpy(vp_tmp,
100794334Sgshapiro								   *result,
100894334Sgshapiro								   *resultsz);
100994334Sgshapiro						*result = vp_tmp;
101090792Sgshapiro					}
101194334Sgshapiro
101294334Sgshapiro					p = *result + *resultln;
101394334Sgshapiro					pe = *result + *resultsz;
101494334Sgshapiro
101594334Sgshapiro					for (i = 0; vals[i] != NULL; i++)
101690792Sgshapiro					{
1017102528Sgshapiro						if (*resultln > 0 &&
1018102528Sgshapiro						    p < pe)
101994334Sgshapiro							*p++ = (char) delim;
102094334Sgshapiro
102194334Sgshapiro						if (lmap->ldap_attrsep != '\0')
102294334Sgshapiro						{
102394334Sgshapiro							p += sm_strlcpy(p, attr,
102494334Sgshapiro									pe - p);
102594334Sgshapiro							if (p < pe)
102694334Sgshapiro								*p++ = lmap->ldap_attrsep;
102794334Sgshapiro						}
102894334Sgshapiro
102994334Sgshapiro						p += sm_strlcpy(p, vals[i],
103094334Sgshapiro								pe - p);
103194334Sgshapiro						*resultln = p - (*result);
103294334Sgshapiro						if (p >= pe)
103394334Sgshapiro						{
103494334Sgshapiro							/* Internal error: buffer too small for LDAP values */
103594334Sgshapiro							SM_LDAP_ERROR_CLEANUP();
103694334Sgshapiro							errno = ENOMEM;
103794334Sgshapiro							return EX_OSERR;
103894334Sgshapiro						}
103990792Sgshapiro					}
104090792Sgshapiro				}
104190792Sgshapiro
104290792Sgshapiro				ldap_value_free(vals);
104390792Sgshapiro				ldap_memfree(attr);
104490792Sgshapiro			}
104590792Sgshapiro			save_errno = sm_ldap_geterrno(lmap->ldap_ld);
104690792Sgshapiro
104790792Sgshapiro			/*
104890792Sgshapiro			**  We check save_errno != LDAP_DECODING_ERROR since
104990792Sgshapiro			**  OpenLDAP 1.X has a very ugly *undocumented*
105090792Sgshapiro			**  hack of returning this error code from
105190792Sgshapiro			**  ldap_next_attribute() if the library freed the
105290792Sgshapiro			**  ber attribute.  See:
105390792Sgshapiro			**  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
105490792Sgshapiro			*/
105590792Sgshapiro
105690792Sgshapiro			if (save_errno != LDAP_SUCCESS &&
105790792Sgshapiro			    save_errno != LDAP_DECODING_ERROR)
105890792Sgshapiro			{
105990792Sgshapiro				/* Must be an error */
106090792Sgshapiro				save_errno += E_LDAPBASE;
106194334Sgshapiro				SM_LDAP_ERROR_CLEANUP();
106290792Sgshapiro				errno = save_errno;
106390792Sgshapiro				return EX_TEMPFAIL;
106490792Sgshapiro			}
106590792Sgshapiro
106694334Sgshapiro			/* mark this DN as done */
106794334Sgshapiro			rl->lr_done = true;
1068132943Sgshapiro			if (rl->lr_ludp != NULL)
1069132943Sgshapiro			{
1070132943Sgshapiro				ldap_free_urldesc(rl->lr_ludp);
1071132943Sgshapiro				rl->lr_ludp = NULL;
1072132943Sgshapiro			}
1073132943Sgshapiro			if (rl->lr_attrs != NULL)
1074132943Sgshapiro			{
1075132943Sgshapiro				free(rl->lr_attrs);
1076132943Sgshapiro				rl->lr_attrs = NULL;
1077132943Sgshapiro			}
107894334Sgshapiro
107990792Sgshapiro			/* We don't want multiple values and we have one */
1080147078Sgshapiro			if ((char) delim == '\0' &&
1081147078Sgshapiro			    !bitset(SM_LDAP_SINGLEMATCH, flags) &&
1082147078Sgshapiro			    *result != NULL)
108390792Sgshapiro				break;
108490792Sgshapiro		}
108590792Sgshapiro		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
108690792Sgshapiro		if (save_errno != LDAP_SUCCESS &&
108790792Sgshapiro		    save_errno != LDAP_DECODING_ERROR)
108890792Sgshapiro		{
108990792Sgshapiro			/* Must be an error */
109090792Sgshapiro			save_errno += E_LDAPBASE;
109194334Sgshapiro			SM_LDAP_ERROR_CLEANUP();
109290792Sgshapiro			errno = save_errno;
109390792Sgshapiro			return EX_TEMPFAIL;
109490792Sgshapiro		}
109590792Sgshapiro		ldap_msgfree(lmap->ldap_res);
109690792Sgshapiro		lmap->ldap_res = NULL;
109790792Sgshapiro	}
109890792Sgshapiro
109990792Sgshapiro	if (ret == 0)
110090792Sgshapiro		save_errno = ETIMEDOUT;
1101223067Sgshapiro	else if (ret == LDAP_RES_SEARCH_RESULT)
1102203004Sgshapiro	{
1103203004Sgshapiro		/*
1104203004Sgshapiro		**  We may have gotten an LDAP_RES_SEARCH_RESULT response
1105203004Sgshapiro		**  with an error inside it, so we have to extract that
1106203004Sgshapiro		**  with ldap_parse_result().  This can happen when talking
1107203004Sgshapiro		**  to an LDAP proxy whose backend has gone down.
1108203004Sgshapiro		*/
1109203004Sgshapiro
1110223067Sgshapiro		if (lmap->ldap_res == NULL)
1111223067Sgshapiro			save_errno = LDAP_UNAVAILABLE;
1112223067Sgshapiro		else
1113223067Sgshapiro		{
1114223067Sgshapiro			int rc;
1115223067Sgshapiro
1116223067Sgshapiro			save_errno = ldap_parse_result(lmap->ldap_ld,
1117223067Sgshapiro					lmap->ldap_res, &rc, NULL, NULL,
1118223067Sgshapiro					NULL, NULL, 0);
1119223067Sgshapiro			if (save_errno == LDAP_SUCCESS)
1120223067Sgshapiro				save_errno = rc;
1121223067Sgshapiro		}
1122203004Sgshapiro	}
1123223067Sgshapiro	else
1124223067Sgshapiro		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
112590792Sgshapiro	if (save_errno != LDAP_SUCCESS)
112690792Sgshapiro	{
112790792Sgshapiro		statp = EX_TEMPFAIL;
1128173340Sgshapiro		switch (save_errno)
112990792Sgshapiro		{
113090792Sgshapiro#ifdef LDAP_SERVER_DOWN
1131173340Sgshapiro		  case LDAP_SERVER_DOWN:
113290792Sgshapiro#endif /* LDAP_SERVER_DOWN */
1133173340Sgshapiro		  case LDAP_TIMEOUT:
1134173340Sgshapiro		  case ETIMEDOUT:
1135173340Sgshapiro		  case LDAP_UNAVAILABLE:
113694334Sgshapiro
1137173340Sgshapiro			/*
1138173340Sgshapiro			**  server disappeared,
1139173340Sgshapiro			**  try reopen on next search
1140173340Sgshapiro			*/
114194334Sgshapiro
1142173340Sgshapiro			statp = EX_RESTART;
1143173340Sgshapiro			break;
1144173340Sgshapiro		}
1145173340Sgshapiro		if (ret != 0)
114690792Sgshapiro			save_errno += E_LDAPBASE;
114794334Sgshapiro		SM_LDAP_ERROR_CLEANUP();
114890792Sgshapiro		errno = save_errno;
114990792Sgshapiro		return statp;
115090792Sgshapiro	}
115190792Sgshapiro
115290792Sgshapiro	if (lmap->ldap_res != NULL)
115390792Sgshapiro	{
115490792Sgshapiro		ldap_msgfree(lmap->ldap_res);
115590792Sgshapiro		lmap->ldap_res = NULL;
115690792Sgshapiro	}
115790792Sgshapiro
115890792Sgshapiro	if (toplevel)
115990792Sgshapiro	{
116094334Sgshapiro		int rlidx;
116190792Sgshapiro
116290792Sgshapiro		/*
116390792Sgshapiro		**  Spin through the built-up recurse list at the top
116490792Sgshapiro		**  of the recursion.  Since new items are added at the
116590792Sgshapiro		**  end of the shared list, we actually only ever get
116690792Sgshapiro		**  one level of recursion before things pop back to the
116790792Sgshapiro		**  top.  Any items added to the list during that recursion
116890792Sgshapiro		**  will be expanded by the top level.
116990792Sgshapiro		*/
117090792Sgshapiro
1171168515Sgshapiro		for (rlidx = 0; recurse != NULL && rlidx < recurse->lrl_cnt;
1172168515Sgshapiro		     rlidx++)
117390792Sgshapiro		{
117494334Sgshapiro			int newflags;
117590792Sgshapiro			int sid;
117690792Sgshapiro			int status;
117790792Sgshapiro
1178168515Sgshapiro			rl = recurse->lrl_data[rlidx];
117994334Sgshapiro
118094334Sgshapiro			newflags = flags;
118194334Sgshapiro			if (rl->lr_done)
118290792Sgshapiro			{
118390792Sgshapiro				/* already expanded */
118490792Sgshapiro				continue;
118590792Sgshapiro			}
118694334Sgshapiro
118794334Sgshapiro			if (rl->lr_type == SM_LDAP_ATTR_DN)
118890792Sgshapiro			{
118990792Sgshapiro				/* do DN search */
119090792Sgshapiro				sid = ldap_search(lmap->ldap_ld,
119190792Sgshapiro						  rl->lr_search,
119290792Sgshapiro						  lmap->ldap_scope,
119390792Sgshapiro						  "(objectClass=*)",
119494334Sgshapiro						  (lmap->ldap_attr[0] == NULL ?
119594334Sgshapiro						   NULL : lmap->ldap_attr),
119690792Sgshapiro						  lmap->ldap_attrsonly);
119790792Sgshapiro			}
119894334Sgshapiro			else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
119990792Sgshapiro			{
120090792Sgshapiro				/* do new search */
120190792Sgshapiro				sid = ldap_search(lmap->ldap_ld,
120290792Sgshapiro						  lmap->ldap_base,
120390792Sgshapiro						  lmap->ldap_scope,
120490792Sgshapiro						  rl->lr_search,
120594334Sgshapiro						  (lmap->ldap_attr[0] == NULL ?
120694334Sgshapiro						   NULL : lmap->ldap_attr),
120790792Sgshapiro						  lmap->ldap_attrsonly);
120890792Sgshapiro			}
120994334Sgshapiro			else if (rl->lr_type == SM_LDAP_ATTR_URL)
121090792Sgshapiro			{
1211132943Sgshapiro				/* Parse URL */
1212132943Sgshapiro				sid = ldap_url_parse(rl->lr_search,
1213132943Sgshapiro						     &rl->lr_ludp);
1214132943Sgshapiro
1215132943Sgshapiro				if (sid != 0)
1216132943Sgshapiro				{
1217132943Sgshapiro					errno = sid + E_LDAPURLBASE;
1218132943Sgshapiro					return EX_TEMPFAIL;
1219132943Sgshapiro				}
1220132943Sgshapiro
1221132943Sgshapiro				/* We need to add objectClass */
1222132943Sgshapiro				if (rl->lr_ludp->lud_attrs != NULL)
1223132943Sgshapiro				{
1224132943Sgshapiro					int attrnum = 0;
1225132943Sgshapiro
1226132943Sgshapiro					while (rl->lr_ludp->lud_attrs[attrnum] != NULL)
1227132943Sgshapiro					{
1228132943Sgshapiro						if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum],
1229132943Sgshapiro							       "objectClass") == 0)
1230132943Sgshapiro						{
1231132943Sgshapiro							/* already requested */
1232132943Sgshapiro							attrnum = -1;
1233132943Sgshapiro							break;
1234132943Sgshapiro						}
1235132943Sgshapiro						attrnum++;
1236132943Sgshapiro					}
1237132943Sgshapiro
1238132943Sgshapiro					if (attrnum >= 0)
1239132943Sgshapiro					{
1240132943Sgshapiro						int i;
1241132943Sgshapiro
1242132943Sgshapiro						rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2));
1243132943Sgshapiro						if (rl->lr_attrs == NULL)
1244132943Sgshapiro						{
1245132943Sgshapiro							save_errno = errno;
1246132943Sgshapiro							ldap_free_urldesc(rl->lr_ludp);
1247132943Sgshapiro							errno = save_errno;
1248132943Sgshapiro							return EX_TEMPFAIL;
1249132943Sgshapiro						}
1250132943Sgshapiro						for (i = 0 ; i < attrnum; i++)
1251132943Sgshapiro						{
1252132943Sgshapiro							rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i];
1253132943Sgshapiro						}
1254132943Sgshapiro						rl->lr_attrs[i++] = "objectClass";
1255132943Sgshapiro						rl->lr_attrs[i++] = NULL;
1256132943Sgshapiro					}
1257132943Sgshapiro				}
1258132943Sgshapiro
1259132943Sgshapiro				/*
1260132943Sgshapiro				**  Use the existing connection
1261132943Sgshapiro				**  for this search.  It really
1262132943Sgshapiro				**  should use lud_scheme://lud_host:lud_port/
1263132943Sgshapiro				**  instead but that would require
1264132943Sgshapiro				**  opening a new connection.
1265132943Sgshapiro				**  This should be fixed ASAP.
1266132943Sgshapiro				*/
1267132943Sgshapiro
1268132943Sgshapiro				sid = ldap_search(lmap->ldap_ld,
1269132943Sgshapiro						  rl->lr_ludp->lud_dn,
1270132943Sgshapiro						  rl->lr_ludp->lud_scope,
1271132943Sgshapiro						  rl->lr_ludp->lud_filter,
1272132943Sgshapiro						  rl->lr_attrs,
1273132943Sgshapiro						  lmap->ldap_attrsonly);
1274132943Sgshapiro
1275132943Sgshapiro				/* Use the attributes specified by URL */
127694334Sgshapiro				newflags |= SM_LDAP_USE_ALLATTR;
127790792Sgshapiro			}
127890792Sgshapiro			else
127990792Sgshapiro			{
128090792Sgshapiro				/* unknown or illegal attribute type */
128190792Sgshapiro				errno = EFAULT;
128290792Sgshapiro				return EX_SOFTWARE;
128390792Sgshapiro			}
128490792Sgshapiro
128590792Sgshapiro			/* Collect results */
128690792Sgshapiro			if (sid == -1)
128790792Sgshapiro			{
128890792Sgshapiro				save_errno = sm_ldap_geterrno(lmap->ldap_ld);
128990792Sgshapiro				statp = EX_TEMPFAIL;
129090792Sgshapiro				switch (save_errno)
129190792Sgshapiro				{
129290792Sgshapiro#ifdef LDAP_SERVER_DOWN
129390792Sgshapiro				  case LDAP_SERVER_DOWN:
129490792Sgshapiro#endif /* LDAP_SERVER_DOWN */
129590792Sgshapiro				  case LDAP_TIMEOUT:
1296173340Sgshapiro				  case ETIMEDOUT:
129790792Sgshapiro				  case LDAP_UNAVAILABLE:
129894334Sgshapiro
129994334Sgshapiro					/*
130094334Sgshapiro					**  server disappeared,
130194334Sgshapiro					**  try reopen on next search
130294334Sgshapiro					*/
130394334Sgshapiro
130490792Sgshapiro					statp = EX_RESTART;
130590792Sgshapiro					break;
130690792Sgshapiro				}
130790792Sgshapiro				errno = save_errno + E_LDAPBASE;
130890792Sgshapiro				return statp;
130990792Sgshapiro			}
131090792Sgshapiro
131194334Sgshapiro			status = sm_ldap_results(lmap, sid, newflags, delim,
131294334Sgshapiro						 rpool, result, resultln,
131394334Sgshapiro						 resultsz, recurse);
131490792Sgshapiro			save_errno = errno;
131590792Sgshapiro			if (status != EX_OK && status != EX_NOTFOUND)
131690792Sgshapiro			{
131790792Sgshapiro				errno = save_errno;
131890792Sgshapiro				return status;
131990792Sgshapiro			}
132090792Sgshapiro
132190792Sgshapiro			/* Mark as done */
132294334Sgshapiro			rl->lr_done = true;
1323132943Sgshapiro			if (rl->lr_ludp != NULL)
1324132943Sgshapiro			{
1325132943Sgshapiro				ldap_free_urldesc(rl->lr_ludp);
1326132943Sgshapiro				rl->lr_ludp = NULL;
1327132943Sgshapiro			}
1328132943Sgshapiro			if (rl->lr_attrs != NULL)
1329132943Sgshapiro			{
1330132943Sgshapiro				free(rl->lr_attrs);
1331132943Sgshapiro				rl->lr_attrs = NULL;
1332132943Sgshapiro			}
133394334Sgshapiro
133494334Sgshapiro			/* Reset rlidx as new items may have been added */
133594334Sgshapiro			rlidx = -1;
133690792Sgshapiro		}
133790792Sgshapiro	}
133890792Sgshapiro	return statp;
133990792Sgshapiro}
134090792Sgshapiro
134190792Sgshapiro/*
134290792Sgshapiro**  SM_LDAP_CLOSE -- close LDAP connection
134390792Sgshapiro**
134490792Sgshapiro**	Parameters:
134590792Sgshapiro**		lmap -- LDAP map information
134690792Sgshapiro**
134790792Sgshapiro**	Returns:
134890792Sgshapiro**		None.
134990792Sgshapiro**
135090792Sgshapiro*/
135190792Sgshapiro
135290792Sgshapirovoid
135390792Sgshapirosm_ldap_close(lmap)
135490792Sgshapiro	SM_LDAP_STRUCT *lmap;
135590792Sgshapiro{
135690792Sgshapiro	if (lmap->ldap_ld == NULL)
135790792Sgshapiro		return;
135890792Sgshapiro
135990792Sgshapiro	if (lmap->ldap_pid == getpid())
136090792Sgshapiro		ldap_unbind(lmap->ldap_ld);
136190792Sgshapiro	lmap->ldap_ld = NULL;
136290792Sgshapiro	lmap->ldap_pid = 0;
136390792Sgshapiro}
136490792Sgshapiro
136590792Sgshapiro/*
136690792Sgshapiro**  SM_LDAP_SETOPTS -- set LDAP options
136790792Sgshapiro**
136890792Sgshapiro**	Parameters:
136990792Sgshapiro**		ld -- LDAP session handle
137090792Sgshapiro**		lmap -- LDAP map information
137190792Sgshapiro**
137290792Sgshapiro**	Returns:
137390792Sgshapiro**		None.
137490792Sgshapiro**
137590792Sgshapiro*/
137690792Sgshapiro
137790792Sgshapirovoid
137890792Sgshapirosm_ldap_setopts(ld, lmap)
137990792Sgshapiro	LDAP *ld;
138090792Sgshapiro	SM_LDAP_STRUCT *lmap;
138190792Sgshapiro{
138290792Sgshapiro# if USE_LDAP_SET_OPTION
138394334Sgshapiro	if (lmap->ldap_version != 0)
138494334Sgshapiro	{
138594334Sgshapiro		ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
138694334Sgshapiro				&lmap->ldap_version);
138794334Sgshapiro	}
138890792Sgshapiro	ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
138990792Sgshapiro	if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
139090792Sgshapiro		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
139190792Sgshapiro	else
139290792Sgshapiro		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
139390792Sgshapiro	ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
139490792Sgshapiro	ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
1395203004Sgshapiro#  if _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT)
1396203004Sgshapiro	if (lmap->ldap_networktmo > 0)
1397203004Sgshapiro	{
1398203004Sgshapiro		struct timeval tmo;
1399203004Sgshapiro
1400203004Sgshapiro		tmo.tv_sec = lmap->ldap_networktmo;
1401203004Sgshapiro		tmo.tv_usec = 0;
1402203004Sgshapiro		ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tmo);
1403203004Sgshapiro	}
1404203004Sgshapiro#  endif /* _FFR_LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT) */
1405102528Sgshapiro#  ifdef LDAP_OPT_RESTART
1406102528Sgshapiro	ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
1407102528Sgshapiro#  endif /* LDAP_OPT_RESTART */
140890792Sgshapiro# else /* USE_LDAP_SET_OPTION */
140990792Sgshapiro	/* From here on in we can use ldap internal timelimits */
141090792Sgshapiro	ld->ld_deref = lmap->ldap_deref;
141190792Sgshapiro	ld->ld_options = lmap->ldap_options;
141290792Sgshapiro	ld->ld_sizelimit = lmap->ldap_sizelimit;
141390792Sgshapiro	ld->ld_timelimit = lmap->ldap_timelimit;
141490792Sgshapiro# endif /* USE_LDAP_SET_OPTION */
141590792Sgshapiro}
141690792Sgshapiro
141790792Sgshapiro/*
141890792Sgshapiro**  SM_LDAP_GETERRNO -- get ldap errno value
141990792Sgshapiro**
142090792Sgshapiro**	Parameters:
142190792Sgshapiro**		ld -- LDAP session handle
142290792Sgshapiro**
142390792Sgshapiro**	Returns:
142490792Sgshapiro**		LDAP errno.
142590792Sgshapiro**
142690792Sgshapiro*/
142790792Sgshapiro
142890792Sgshapiroint
142990792Sgshapirosm_ldap_geterrno(ld)
143090792Sgshapiro	LDAP *ld;
143190792Sgshapiro{
143290792Sgshapiro	int err = LDAP_SUCCESS;
143390792Sgshapiro
143490792Sgshapiro# if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
143590792Sgshapiro	(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
143690792Sgshapiro# else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
143790792Sgshapiro#  ifdef LDAP_OPT_SIZELIMIT
143890792Sgshapiro	err = ldap_get_lderrno(ld, NULL, NULL);
143990792Sgshapiro#  else /* LDAP_OPT_SIZELIMIT */
144090792Sgshapiro	err = ld->ld_errno;
144190792Sgshapiro
144290792Sgshapiro	/*
144390792Sgshapiro	**  Reset value to prevent lingering LDAP_DECODING_ERROR due to
144490792Sgshapiro	**  OpenLDAP 1.X's hack (see above)
144590792Sgshapiro	*/
144690792Sgshapiro
144790792Sgshapiro	ld->ld_errno = LDAP_SUCCESS;
144890792Sgshapiro#  endif /* LDAP_OPT_SIZELIMIT */
144990792Sgshapiro# endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
145090792Sgshapiro	return err;
145190792Sgshapiro}
145290792Sgshapiro# endif /* LDAPMAP */
1453