ldap.c revision 132943
190792Sgshapiro/*
2125820Sgshapiro * Copyright (c) 2001-2003 Sendmail, 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
1090792Sgshapiro#include <sm/gen.h>
11132943SgshapiroSM_RCSID("@(#)$Id: ldap.c,v 1.59 2003/12/23 21:20:15 gshapiro Exp $")
1290792Sgshapiro
1390792Sgshapiro#if LDAPMAP
1490792Sgshapiro# include <sys/types.h>
1590792Sgshapiro# include <errno.h>
1690792Sgshapiro# include <setjmp.h>
1790792Sgshapiro# include <stdlib.h>
1890792Sgshapiro# include <unistd.h>
1990792Sgshapiro
2090792Sgshapiro# include <sm/bitops.h>
2190792Sgshapiro# include <sm/clock.h>
2290792Sgshapiro# include <sm/conf.h>
2390792Sgshapiro# include <sm/debug.h>
2490792Sgshapiro# include <sm/errstring.h>
2590792Sgshapiro# include <sm/ldap.h>
2690792Sgshapiro# include <sm/string.h>
2794334Sgshapiro#  ifdef EX_OK
2894334Sgshapiro#   undef EX_OK			/* for SVr4.2 SMP */
2994334Sgshapiro#  endif /* EX_OK */
3090792Sgshapiro# include <sm/sysexits.h>
3190792Sgshapiro
3290792SgshapiroSM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
3390792Sgshapiro	"@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
3490792Sgshapiro
3590792Sgshapirostatic void	ldaptimeout __P((int));
3690792Sgshapiro
3790792Sgshapiro/*
3890792Sgshapiro**  SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
3990792Sgshapiro**
4090792Sgshapiro**	Parameters:
4190792Sgshapiro**		lmap -- pointer to SM_LDAP_STRUCT to clear
4290792Sgshapiro**
4390792Sgshapiro**	Returns:
4490792Sgshapiro**		None.
4590792Sgshapiro**
4690792Sgshapiro*/
4790792Sgshapiro
4890792Sgshapirovoid
4990792Sgshapirosm_ldap_clear(lmap)
5090792Sgshapiro	SM_LDAP_STRUCT *lmap;
5190792Sgshapiro{
5290792Sgshapiro	if (lmap == NULL)
5390792Sgshapiro		return;
5490792Sgshapiro
55132943Sgshapiro	lmap->ldap_host = NULL;
5690792Sgshapiro	lmap->ldap_port = LDAP_PORT;
57132943Sgshapiro	lmap->ldap_uri = NULL;
5894334Sgshapiro	lmap->ldap_version = 0;
5990792Sgshapiro	lmap->ldap_deref = LDAP_DEREF_NEVER;
6090792Sgshapiro	lmap->ldap_timelimit = LDAP_NO_LIMIT;
6190792Sgshapiro	lmap->ldap_sizelimit = LDAP_NO_LIMIT;
6290792Sgshapiro# ifdef LDAP_REFERRALS
6390792Sgshapiro	lmap->ldap_options = LDAP_OPT_REFERRALS;
6490792Sgshapiro# else /* LDAP_REFERRALS */
6590792Sgshapiro	lmap->ldap_options = 0;
6690792Sgshapiro# endif /* LDAP_REFERRALS */
6790792Sgshapiro	lmap->ldap_attrsep = '\0';
6890792Sgshapiro	lmap->ldap_binddn = NULL;
6990792Sgshapiro	lmap->ldap_secret = NULL;
7090792Sgshapiro	lmap->ldap_method = LDAP_AUTH_SIMPLE;
7190792Sgshapiro	lmap->ldap_base = NULL;
7290792Sgshapiro	lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
7390792Sgshapiro	lmap->ldap_attrsonly = LDAPMAP_FALSE;
7490792Sgshapiro	lmap->ldap_timeout.tv_sec = 0;
7590792Sgshapiro	lmap->ldap_timeout.tv_usec = 0;
7690792Sgshapiro	lmap->ldap_ld = NULL;
7790792Sgshapiro	lmap->ldap_filter = NULL;
7890792Sgshapiro	lmap->ldap_attr[0] = NULL;
7994334Sgshapiro	lmap->ldap_attr_type[0] = SM_LDAP_ATTR_NONE;
8094334Sgshapiro	lmap->ldap_attr_needobjclass[0] = NULL;
8190792Sgshapiro	lmap->ldap_res = NULL;
8290792Sgshapiro	lmap->ldap_next = NULL;
8390792Sgshapiro	lmap->ldap_pid = 0;
8490792Sgshapiro}
8590792Sgshapiro
8690792Sgshapiro/*
8790792Sgshapiro**  SM_LDAP_START -- actually connect to an LDAP server
8890792Sgshapiro**
8990792Sgshapiro**	Parameters:
9090792Sgshapiro**		name -- name of map for debug output.
9190792Sgshapiro**		lmap -- the LDAP map being opened.
9290792Sgshapiro**
9390792Sgshapiro**	Returns:
9490792Sgshapiro**		true if connection is successful, false otherwise.
9590792Sgshapiro**
9690792Sgshapiro**	Side Effects:
9790792Sgshapiro**		Populates lmap->ldap_ld.
9890792Sgshapiro*/
9990792Sgshapiro
10090792Sgshapirostatic jmp_buf	LDAPTimeout;
10190792Sgshapiro
10290792Sgshapiro#define SM_LDAP_SETTIMEOUT(to)						\
10390792Sgshapirodo									\
10490792Sgshapiro{									\
10590792Sgshapiro	if (to != 0)							\
10690792Sgshapiro	{								\
10790792Sgshapiro		if (setjmp(LDAPTimeout) != 0)				\
10890792Sgshapiro		{							\
10990792Sgshapiro			errno = ETIMEDOUT;				\
11090792Sgshapiro			return false;					\
11190792Sgshapiro		}							\
11290792Sgshapiro		ev = sm_setevent(to, ldaptimeout, 0);			\
11390792Sgshapiro	}								\
11490792Sgshapiro} while (0)
11590792Sgshapiro
11690792Sgshapiro#define SM_LDAP_CLEARTIMEOUT()						\
11790792Sgshapirodo									\
11890792Sgshapiro{									\
11990792Sgshapiro	if (ev != NULL)							\
12090792Sgshapiro		sm_clrevent(ev);					\
12190792Sgshapiro} while (0)
12290792Sgshapiro
12390792Sgshapirobool
12490792Sgshapirosm_ldap_start(name, lmap)
12590792Sgshapiro	char *name;
12690792Sgshapiro	SM_LDAP_STRUCT *lmap;
12790792Sgshapiro{
12890792Sgshapiro	int bind_result;
12990792Sgshapiro	int save_errno;
130132943Sgshapiro	char *id;
13190792Sgshapiro	SM_EVENT *ev = NULL;
132132943Sgshapiro	LDAP *ld = NULL;
13390792Sgshapiro
13490792Sgshapiro	if (sm_debug_active(&SmLDAPTrace, 2))
13590792Sgshapiro		sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
13690792Sgshapiro
137132943Sgshapiro	if (lmap->ldap_host != NULL)
138132943Sgshapiro		id = lmap->ldap_host;
139132943Sgshapiro	else if (lmap->ldap_uri != NULL)
140132943Sgshapiro		id = lmap->ldap_uri;
141132943Sgshapiro	else
142132943Sgshapiro		id = "localhost";
143132943Sgshapiro
14490792Sgshapiro	if (sm_debug_active(&SmLDAPTrace, 9))
145132943Sgshapiro	{
146132943Sgshapiro		/* Don't print a port number for LDAP URIs */
147132943Sgshapiro		if (lmap->ldap_uri != NULL)
148132943Sgshapiro			sm_dprintf("ldapmap_start(%s)\n", id);
149132943Sgshapiro		else
150132943Sgshapiro			sm_dprintf("ldapmap_start(%s, %d)\n", id,
151132943Sgshapiro				   lmap->ldap_port);
152132943Sgshapiro	}
15390792Sgshapiro
154132943Sgshapiro	if (lmap->ldap_uri != NULL)
155132943Sgshapiro	{
156132943Sgshapiro#if SM_CONF_LDAP_INITIALIZE
157132943Sgshapiro		/* LDAP server supports URIs so use them directly */
158132943Sgshapiro		save_errno = ldap_initialize(&ld, lmap->ldap_uri);
159132943Sgshapiro#else /* SM_CONF_LDAP_INITIALIZE */
160132943Sgshapiro		int err;
161132943Sgshapiro		LDAPURLDesc *ludp = NULL;
162132943Sgshapiro
163132943Sgshapiro		/* Blast apart URL and use the ldap_init/ldap_open below */
164132943Sgshapiro		err = ldap_url_parse(lmap->ldap_uri, &ludp);
165132943Sgshapiro		if (err != 0)
166132943Sgshapiro		{
167132943Sgshapiro			errno = err + E_LDAPURLBASE;
168132943Sgshapiro			return false;
169132943Sgshapiro		}
170132943Sgshapiro		lmap->ldap_host = sm_strdup_x(ludp->lud_host);
171132943Sgshapiro		if (lmap->ldap_host == NULL)
172132943Sgshapiro		{
173132943Sgshapiro			save_errno = errno;
174132943Sgshapiro			ldap_free_urldesc(ludp);
175132943Sgshapiro			errno = save_errno;
176132943Sgshapiro			return false;
177132943Sgshapiro		}
178132943Sgshapiro		lmap->ldap_port = ludp->lud_port;
179132943Sgshapiro		ldap_free_urldesc(ludp);
180132943Sgshapiro#endif /* SM_CONF_LDAP_INITIALIZE */
181132943Sgshapiro	}
182132943Sgshapiro
183132943Sgshapiro	if (ld == NULL)
184132943Sgshapiro	{
18590792Sgshapiro# if USE_LDAP_INIT
186132943Sgshapiro		ld = ldap_init(lmap->ldap_host, lmap->ldap_port);
187132943Sgshapiro		save_errno = errno;
18890792Sgshapiro# else /* USE_LDAP_INIT */
189132943Sgshapiro		/*
190132943Sgshapiro		**  If using ldap_open(), the actual connection to the server
191132943Sgshapiro		**  happens now so we need the timeout here.  For ldap_init(),
192132943Sgshapiro		**  the connection happens at bind time.
193132943Sgshapiro		*/
19490792Sgshapiro
195132943Sgshapiro		SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
196132943Sgshapiro		ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
197132943Sgshapiro		save_errno = errno;
19890792Sgshapiro
199132943Sgshapiro		/* clear the event if it has not sprung */
200132943Sgshapiro		SM_LDAP_CLEARTIMEOUT();
20190792Sgshapiro# endif /* USE_LDAP_INIT */
202132943Sgshapiro	}
20390792Sgshapiro
20490792Sgshapiro	errno = save_errno;
20590792Sgshapiro	if (ld == NULL)
20690792Sgshapiro		return false;
20790792Sgshapiro
20890792Sgshapiro	sm_ldap_setopts(ld, lmap);
20990792Sgshapiro
21090792Sgshapiro# if USE_LDAP_INIT
21190792Sgshapiro	/*
21290792Sgshapiro	**  If using ldap_init(), the actual connection to the server
21390792Sgshapiro	**  happens at ldap_bind_s() so we need the timeout here.
21490792Sgshapiro	*/
21590792Sgshapiro
21690792Sgshapiro	SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
21790792Sgshapiro# endif /* USE_LDAP_INIT */
21890792Sgshapiro
21990792Sgshapiro# ifdef LDAP_AUTH_KRBV4
22090792Sgshapiro	if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
22190792Sgshapiro	    lmap->ldap_secret != NULL)
22290792Sgshapiro	{
22390792Sgshapiro		/*
22490792Sgshapiro		**  Need to put ticket in environment here instead of
22590792Sgshapiro		**  during parseargs as there may be different tickets
22690792Sgshapiro		**  for different LDAP connections.
22790792Sgshapiro		*/
22890792Sgshapiro
22990792Sgshapiro		(void) putenv(lmap->ldap_secret);
23090792Sgshapiro	}
23190792Sgshapiro# endif /* LDAP_AUTH_KRBV4 */
23290792Sgshapiro
23390792Sgshapiro	bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
23490792Sgshapiro				  lmap->ldap_secret, lmap->ldap_method);
23590792Sgshapiro
23690792Sgshapiro# if USE_LDAP_INIT
23790792Sgshapiro	/* clear the event if it has not sprung */
23890792Sgshapiro	SM_LDAP_CLEARTIMEOUT();
23990792Sgshapiro# endif /* USE_LDAP_INIT */
24090792Sgshapiro
24190792Sgshapiro	if (bind_result != LDAP_SUCCESS)
24290792Sgshapiro	{
24390792Sgshapiro		errno = bind_result + E_LDAPBASE;
24490792Sgshapiro		return false;
24590792Sgshapiro	}
24690792Sgshapiro
24790792Sgshapiro	/* Save PID to make sure only this PID closes the LDAP connection */
24890792Sgshapiro	lmap->ldap_pid = getpid();
24990792Sgshapiro	lmap->ldap_ld = ld;
25090792Sgshapiro	return true;
25190792Sgshapiro}
25290792Sgshapiro
25390792Sgshapiro/* ARGSUSED */
25490792Sgshapirostatic void
25590792Sgshapiroldaptimeout(unused)
25690792Sgshapiro	int unused;
25790792Sgshapiro{
25890792Sgshapiro	/*
25990792Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
26090792Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
26190792Sgshapiro	**	DOING.
26290792Sgshapiro	*/
26390792Sgshapiro
26490792Sgshapiro	errno = ETIMEDOUT;
26590792Sgshapiro	longjmp(LDAPTimeout, 1);
26690792Sgshapiro}
26790792Sgshapiro
26890792Sgshapiro/*
269132943Sgshapiro**  SM_LDAP_SEARCH -- initiate LDAP search
27090792Sgshapiro**
27190792Sgshapiro**	Initiate an LDAP search, return the msgid.
27290792Sgshapiro**	The calling function must collect the results.
27390792Sgshapiro**
27490792Sgshapiro**	Parameters:
27590792Sgshapiro**		lmap -- LDAP map information
27690792Sgshapiro**		key -- key to substitute in LDAP filter
27790792Sgshapiro**
27890792Sgshapiro**	Returns:
27990792Sgshapiro**		-1 on failure, msgid on success
28090792Sgshapiro**
28190792Sgshapiro*/
28290792Sgshapiro
28390792Sgshapiroint
28490792Sgshapirosm_ldap_search(lmap, key)
28590792Sgshapiro	SM_LDAP_STRUCT *lmap;
28690792Sgshapiro	char *key;
28790792Sgshapiro{
28890792Sgshapiro	int msgid;
28990792Sgshapiro	char *fp, *p, *q;
29090792Sgshapiro	char filter[LDAPMAP_MAX_FILTER + 1];
29190792Sgshapiro
29290792Sgshapiro	/* substitute key into filter, perhaps multiple times */
29390792Sgshapiro	memset(filter, '\0', sizeof filter);
29490792Sgshapiro	fp = filter;
29590792Sgshapiro	p = lmap->ldap_filter;
29690792Sgshapiro	while ((q = strchr(p, '%')) != NULL)
29790792Sgshapiro	{
29890792Sgshapiro		if (q[1] == 's')
29990792Sgshapiro		{
30090792Sgshapiro			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
30190792Sgshapiro					   "%.*s%s", (int) (q - p), p, key);
30290792Sgshapiro			fp += strlen(fp);
30390792Sgshapiro			p = q + 2;
30490792Sgshapiro		}
30590792Sgshapiro		else if (q[1] == '0')
30690792Sgshapiro		{
30790792Sgshapiro			char *k = key;
30890792Sgshapiro
30990792Sgshapiro			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
31090792Sgshapiro					   "%.*s", (int) (q - p), p);
31190792Sgshapiro			fp += strlen(fp);
31290792Sgshapiro			p = q + 2;
31390792Sgshapiro
31490792Sgshapiro			/* Properly escape LDAP special characters */
31590792Sgshapiro			while (SPACELEFT(filter, fp) > 0 &&
31690792Sgshapiro			       *k != '\0')
31790792Sgshapiro			{
31890792Sgshapiro				if (*k == '*' || *k == '(' ||
31990792Sgshapiro				    *k == ')' || *k == '\\')
32090792Sgshapiro				{
32190792Sgshapiro					(void) sm_strlcat(fp,
32290792Sgshapiro						       (*k == '*' ? "\\2A" :
32390792Sgshapiro							(*k == '(' ? "\\28" :
32490792Sgshapiro							 (*k == ')' ? "\\29" :
32590792Sgshapiro							  (*k == '\\' ? "\\5C" :
32690792Sgshapiro							   "\00")))),
32790792Sgshapiro						SPACELEFT(filter, fp));
32890792Sgshapiro					fp += strlen(fp);
32990792Sgshapiro					k++;
33090792Sgshapiro				}
33190792Sgshapiro				else
33290792Sgshapiro					*fp++ = *k++;
33390792Sgshapiro			}
33490792Sgshapiro		}
33590792Sgshapiro		else
33690792Sgshapiro		{
33790792Sgshapiro			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
33890792Sgshapiro				"%.*s", (int) (q - p + 1), p);
33990792Sgshapiro			p = q + (q[1] == '%' ? 2 : 1);
34090792Sgshapiro			fp += strlen(fp);
34190792Sgshapiro		}
34290792Sgshapiro	}
34390792Sgshapiro	(void) sm_strlcpy(fp, p, SPACELEFT(filter, fp));
34490792Sgshapiro	if (sm_debug_active(&SmLDAPTrace, 20))
34590792Sgshapiro		sm_dprintf("ldap search filter=%s\n", filter);
34690792Sgshapiro
34790792Sgshapiro	lmap->ldap_res = NULL;
34894334Sgshapiro	msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base,
34994334Sgshapiro			    lmap->ldap_scope, filter,
35090792Sgshapiro			    (lmap->ldap_attr[0] == NULL ? NULL :
35190792Sgshapiro			     lmap->ldap_attr),
35290792Sgshapiro			    lmap->ldap_attrsonly);
35390792Sgshapiro	return msgid;
35490792Sgshapiro}
35590792Sgshapiro
35690792Sgshapiro/*
35794334Sgshapiro**  SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
35894334Sgshapiro**			       particular objectClass
35994334Sgshapiro**
36094334Sgshapiro**	Parameters:
36194334Sgshapiro**		lmap -- pointer to SM_LDAP_STRUCT in use
36294334Sgshapiro**		entry -- current LDAP entry struct
36394334Sgshapiro**		ocvalue -- particular objectclass in question.
36494334Sgshapiro**			   may be of form (fee|foo|fum) meaning
36594334Sgshapiro**			   any entry can be part of either fee,
36694334Sgshapiro**			   foo or fum objectclass
36794334Sgshapiro**
36894334Sgshapiro**	Returns:
36994334Sgshapiro**		true if item has that objectClass
37094334Sgshapiro*/
37194334Sgshapiro
37294334Sgshapirostatic bool
37394334Sgshapirosm_ldap_has_objectclass(lmap, entry, ocvalue)
37494334Sgshapiro	SM_LDAP_STRUCT *lmap;
37594334Sgshapiro	LDAPMessage *entry;
37694334Sgshapiro	char *ocvalue;
37794334Sgshapiro{
37894334Sgshapiro	char **vals = NULL;
37994334Sgshapiro	int i;
38094334Sgshapiro
38194334Sgshapiro	if (ocvalue == NULL)
38294334Sgshapiro		return false;
38394334Sgshapiro
38494334Sgshapiro	vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
38594334Sgshapiro	if (vals == NULL)
38694334Sgshapiro		return false;
38794334Sgshapiro
38894334Sgshapiro	for (i = 0; vals[i] != NULL; i++)
38994334Sgshapiro	{
39094334Sgshapiro		char *p;
39194334Sgshapiro		char *q;
39294334Sgshapiro
39394334Sgshapiro		p = q = ocvalue;
39494334Sgshapiro		while (*p != '\0')
39594334Sgshapiro		{
39694334Sgshapiro			while (*p != '\0' && *p != '|')
39794334Sgshapiro				p++;
39894334Sgshapiro
39994334Sgshapiro			if ((p - q) == strlen(vals[i]) &&
40094334Sgshapiro			    sm_strncasecmp(vals[i], q, p - q) == 0)
40194334Sgshapiro			{
40294334Sgshapiro				ldap_value_free(vals);
40394334Sgshapiro				return true;
40494334Sgshapiro			}
40594334Sgshapiro
40694334Sgshapiro			while (*p == '|')
40794334Sgshapiro				p++;
40894334Sgshapiro			q = p;
40994334Sgshapiro		}
41094334Sgshapiro	}
41194334Sgshapiro
41294334Sgshapiro	ldap_value_free(vals);
41394334Sgshapiro	return false;
41494334Sgshapiro}
41594334Sgshapiro
41694334Sgshapiro/*
41790792Sgshapiro**  SM_LDAP_RESULTS -- return results from an LDAP lookup in result
41890792Sgshapiro**
41990792Sgshapiro**	Parameters:
42090792Sgshapiro**		lmap -- pointer to SM_LDAP_STRUCT in use
42190792Sgshapiro**		msgid -- msgid returned by sm_ldap_search()
42290792Sgshapiro**		flags -- flags for the lookup
42390792Sgshapiro**		delim -- delimiter for result concatenation
42490792Sgshapiro**		rpool -- memory pool for storage
42590792Sgshapiro**		result -- return string
42690792Sgshapiro**		recurse -- recursion list
42790792Sgshapiro**
42890792Sgshapiro**	Returns:
42990792Sgshapiro**		status (sysexit)
43090792Sgshapiro*/
43190792Sgshapiro
43294334Sgshapiro# define SM_LDAP_ERROR_CLEANUP()				\
43390792Sgshapiro{								\
43490792Sgshapiro	if (lmap->ldap_res != NULL)				\
43590792Sgshapiro	{							\
43690792Sgshapiro		ldap_msgfree(lmap->ldap_res);			\
43790792Sgshapiro		lmap->ldap_res = NULL;				\
43890792Sgshapiro	}							\
43990792Sgshapiro	(void) ldap_abandon(lmap->ldap_ld, msgid);		\
44090792Sgshapiro}
44190792Sgshapiro
44294334Sgshapirostatic SM_LDAP_RECURSE_ENTRY *
44394334Sgshapirosm_ldap_add_recurse(top, item, type, rpool)
44490792Sgshapiro	SM_LDAP_RECURSE_LIST **top;
44590792Sgshapiro	char *item;
44690792Sgshapiro	int type;
44790792Sgshapiro	SM_RPOOL_T *rpool;
44890792Sgshapiro{
44994334Sgshapiro	int n;
45094334Sgshapiro	int m;
45194334Sgshapiro	int p;
45294334Sgshapiro	int insertat;
45394334Sgshapiro	int moveb;
45494334Sgshapiro	int oldsizeb;
45594334Sgshapiro	int rc;
45694334Sgshapiro	SM_LDAP_RECURSE_ENTRY *newe;
45794334Sgshapiro	SM_LDAP_RECURSE_ENTRY **olddata;
45890792Sgshapiro
45994334Sgshapiro	/*
46094334Sgshapiro	**  This code will maintain a list of
46194334Sgshapiro	**  SM_LDAP_RECURSE_ENTRY structures
46294334Sgshapiro	**  in ascending order.
46394334Sgshapiro	*/
46494334Sgshapiro
46594334Sgshapiro	if (*top == NULL)
46690792Sgshapiro	{
46794334Sgshapiro		/* Allocate an initial SM_LDAP_RECURSE_LIST struct */
46894334Sgshapiro		*top = sm_rpool_malloc_x(rpool, sizeof **top);
46994334Sgshapiro		(*top)->lr_cnt = 0;
47094334Sgshapiro		(*top)->lr_size = 0;
47194334Sgshapiro		(*top)->lr_data = NULL;
47294334Sgshapiro	}
47394334Sgshapiro
47494334Sgshapiro	if ((*top)->lr_cnt >= (*top)->lr_size)
47594334Sgshapiro	{
47694334Sgshapiro		/* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
47794334Sgshapiro		olddata = (*top)->lr_data;
47894334Sgshapiro		if ((*top)->lr_size == 0)
47990792Sgshapiro		{
48094334Sgshapiro			oldsizeb = 0;
48194334Sgshapiro			(*top)->lr_size = 256;
48290792Sgshapiro		}
48394334Sgshapiro		else
48494334Sgshapiro		{
48594334Sgshapiro			oldsizeb = (*top)->lr_size * sizeof *((*top)->lr_data);
48694334Sgshapiro			(*top)->lr_size *= 2;
48794334Sgshapiro		}
48894334Sgshapiro		(*top)->lr_data = sm_rpool_malloc_x(rpool,
48994334Sgshapiro						    (*top)->lr_size * sizeof *((*top)->lr_data));
49094334Sgshapiro		if (oldsizeb > 0)
49194334Sgshapiro			memcpy((*top)->lr_data, olddata, oldsizeb);
49290792Sgshapiro	}
49390792Sgshapiro
49494334Sgshapiro	/*
49594334Sgshapiro	**  Binary search/insert item:type into list.
49694334Sgshapiro	**  Return current entry pointer if already exists.
49794334Sgshapiro	*/
49894334Sgshapiro
49994334Sgshapiro	n = 0;
50094334Sgshapiro	m = (*top)->lr_cnt - 1;
50194334Sgshapiro	if (m < 0)
50294334Sgshapiro		insertat = 0;
50390792Sgshapiro	else
50494334Sgshapiro		insertat = -1;
50594334Sgshapiro
50694334Sgshapiro	while (insertat == -1)
50794334Sgshapiro	{
50894334Sgshapiro		p = (m + n) / 2;
50994334Sgshapiro
51094334Sgshapiro		rc = sm_strcasecmp(item, (*top)->lr_data[p]->lr_search);
51194334Sgshapiro		if (rc == 0)
51294334Sgshapiro			rc = type - (*top)->lr_data[p]->lr_type;
51394334Sgshapiro
51494334Sgshapiro		if (rc < 0)
51594334Sgshapiro			m = p - 1;
51694334Sgshapiro		else if (rc > 0)
51794334Sgshapiro			n = p + 1;
51894334Sgshapiro		else
51994334Sgshapiro			return (*top)->lr_data[p];
52094334Sgshapiro
52194334Sgshapiro		if (m == -1)
52294334Sgshapiro			insertat = 0;
52394334Sgshapiro		else if (n >= (*top)->lr_cnt)
52494334Sgshapiro			insertat = (*top)->lr_cnt;
52594334Sgshapiro		else if (m < n)
52694334Sgshapiro			insertat = m + 1;
52794334Sgshapiro	}
52894334Sgshapiro
52994334Sgshapiro	/*
53094334Sgshapiro	** Not found in list, make room
53194334Sgshapiro	** at insert point and add it.
53294334Sgshapiro	*/
53394334Sgshapiro
53494334Sgshapiro	newe = sm_rpool_malloc_x(rpool, sizeof *newe);
53594334Sgshapiro	if (newe != NULL)
53694334Sgshapiro	{
53794334Sgshapiro		moveb = ((*top)->lr_cnt - insertat) * sizeof *((*top)->lr_data);
53894334Sgshapiro		if (moveb > 0)
53994334Sgshapiro			memmove(&((*top)->lr_data[insertat + 1]),
54094334Sgshapiro				&((*top)->lr_data[insertat]),
54194334Sgshapiro				moveb);
54294334Sgshapiro
54394334Sgshapiro		newe->lr_search = sm_rpool_strdup_x(rpool, item);
54494334Sgshapiro		newe->lr_type = type;
545132943Sgshapiro		newe->lr_ludp = NULL;
546132943Sgshapiro		newe->lr_attrs = NULL;
54794334Sgshapiro		newe->lr_done = false;
54894334Sgshapiro
54994334Sgshapiro		((*top)->lr_data)[insertat] = newe;
55094334Sgshapiro		(*top)->lr_cnt++;
55194334Sgshapiro	}
55294334Sgshapiro	return newe;
55390792Sgshapiro}
55490792Sgshapiro
55590792Sgshapiroint
55694334Sgshapirosm_ldap_results(lmap, msgid, flags, delim, rpool, result,
55794334Sgshapiro		resultln, resultsz, recurse)
55890792Sgshapiro	SM_LDAP_STRUCT *lmap;
55990792Sgshapiro	int msgid;
56090792Sgshapiro	int flags;
56194334Sgshapiro	int delim;
56290792Sgshapiro	SM_RPOOL_T *rpool;
56390792Sgshapiro	char **result;
56494334Sgshapiro	int *resultln;
56594334Sgshapiro	int *resultsz;
56690792Sgshapiro	SM_LDAP_RECURSE_LIST *recurse;
56790792Sgshapiro{
56890792Sgshapiro	bool toplevel;
56990792Sgshapiro	int i;
57090792Sgshapiro	int statp;
57190792Sgshapiro	int vsize;
57290792Sgshapiro	int ret;
57390792Sgshapiro	int save_errno;
57490792Sgshapiro	char *p;
57594334Sgshapiro	SM_LDAP_RECURSE_ENTRY *rl;
57690792Sgshapiro
57790792Sgshapiro	/* Are we the top top level of the search? */
57890792Sgshapiro	toplevel = (recurse == NULL);
57990792Sgshapiro
58090792Sgshapiro	/* Get results */
58190792Sgshapiro	statp = EX_NOTFOUND;
58290792Sgshapiro	while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
58390792Sgshapiro				  (lmap->ldap_timeout.tv_sec == 0 ? NULL :
58490792Sgshapiro				   &(lmap->ldap_timeout)),
58590792Sgshapiro				  &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
58690792Sgshapiro	{
58790792Sgshapiro		LDAPMessage *entry;
58890792Sgshapiro
58990792Sgshapiro		/* If we don't want multiple values and we have one, break */
59094334Sgshapiro		if ((char) delim == '\0' && *result != NULL)
59190792Sgshapiro			break;
59290792Sgshapiro
59390792Sgshapiro		/* Cycle through all entries */
59490792Sgshapiro		for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
59590792Sgshapiro		     entry != NULL;
59690792Sgshapiro		     entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
59790792Sgshapiro		{
59890792Sgshapiro			BerElement *ber;
59990792Sgshapiro			char *attr;
60090792Sgshapiro			char **vals = NULL;
60190792Sgshapiro			char *dn;
60290792Sgshapiro
60390792Sgshapiro			/*
60490792Sgshapiro			**  If matching only and found an entry,
60590792Sgshapiro			**  no need to spin through attributes
60690792Sgshapiro			*/
60790792Sgshapiro
608125820Sgshapiro			if (bitset(SM_LDAP_MATCHONLY, flags))
609125820Sgshapiro			{
610125820Sgshapiro				statp = EX_OK;
61190792Sgshapiro				continue;
612125820Sgshapiro			}
61390792Sgshapiro
61490792Sgshapiro			/* record completed DN's to prevent loops */
61590792Sgshapiro			dn = ldap_get_dn(lmap->ldap_ld, entry);
61690792Sgshapiro			if (dn == NULL)
61790792Sgshapiro			{
61890792Sgshapiro				save_errno = sm_ldap_geterrno(lmap->ldap_ld);
61990792Sgshapiro				save_errno += E_LDAPBASE;
62094334Sgshapiro				SM_LDAP_ERROR_CLEANUP();
62190792Sgshapiro				errno = save_errno;
622120256Sgshapiro				return EX_TEMPFAIL;
62390792Sgshapiro			}
62490792Sgshapiro
62594334Sgshapiro			rl = sm_ldap_add_recurse(&recurse, dn,
62694334Sgshapiro						 SM_LDAP_ATTR_DN,
62794334Sgshapiro						 rpool);
62894334Sgshapiro
62994334Sgshapiro			if (rl == NULL)
63090792Sgshapiro			{
63190792Sgshapiro				ldap_memfree(dn);
63294334Sgshapiro				SM_LDAP_ERROR_CLEANUP();
63390792Sgshapiro				errno = ENOMEM;
63490792Sgshapiro				return EX_OSERR;
63594334Sgshapiro			}
63694334Sgshapiro			else if (rl->lr_done)
63794334Sgshapiro			{
63890792Sgshapiro				/* already on list, skip it */
63990792Sgshapiro				ldap_memfree(dn);
64090792Sgshapiro				continue;
64190792Sgshapiro			}
64290792Sgshapiro			ldap_memfree(dn);
64390792Sgshapiro
64490792Sgshapiro# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
64590792Sgshapiro			/*
64690792Sgshapiro			**  Reset value to prevent lingering
64790792Sgshapiro			**  LDAP_DECODING_ERROR due to
64890792Sgshapiro			**  OpenLDAP 1.X's hack (see below)
64990792Sgshapiro			*/
65090792Sgshapiro
65190792Sgshapiro			lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
65290792Sgshapiro# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
65390792Sgshapiro
65490792Sgshapiro			for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
65590792Sgshapiro							 &ber);
65690792Sgshapiro			     attr != NULL;
65790792Sgshapiro			     attr = ldap_next_attribute(lmap->ldap_ld, entry,
65890792Sgshapiro							ber))
65990792Sgshapiro			{
66090792Sgshapiro				char *tmp, *vp_tmp;
66190792Sgshapiro				int type;
66294334Sgshapiro				char *needobjclass = NULL;
66390792Sgshapiro
66494334Sgshapiro				type = SM_LDAP_ATTR_NONE;
66590792Sgshapiro				for (i = 0; lmap->ldap_attr[i] != NULL; i++)
66690792Sgshapiro				{
66790792Sgshapiro					if (sm_strcasecmp(lmap->ldap_attr[i],
66890792Sgshapiro							  attr) == 0)
66990792Sgshapiro					{
67090792Sgshapiro						type = lmap->ldap_attr_type[i];
67194334Sgshapiro						needobjclass = lmap->ldap_attr_needobjclass[i];
67290792Sgshapiro						break;
67390792Sgshapiro					}
67490792Sgshapiro				}
67594334Sgshapiro
67694334Sgshapiro				if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
67794334Sgshapiro				    type == SM_LDAP_ATTR_NONE)
67890792Sgshapiro				{
67994334Sgshapiro					/* URL lookups specify attrs to use */
68094334Sgshapiro					type = SM_LDAP_ATTR_NORMAL;
68194334Sgshapiro					needobjclass = NULL;
68294334Sgshapiro				}
68394334Sgshapiro
68494334Sgshapiro				if (type == SM_LDAP_ATTR_NONE)
68594334Sgshapiro				{
68690792Sgshapiro					/* attribute not requested */
68790792Sgshapiro					ldap_memfree(attr);
68894334Sgshapiro					SM_LDAP_ERROR_CLEANUP();
68990792Sgshapiro					errno = EFAULT;
69090792Sgshapiro					return EX_SOFTWARE;
69190792Sgshapiro				}
69290792Sgshapiro
69394334Sgshapiro				/*
69494334Sgshapiro				**  For recursion on a particular attribute,
69594334Sgshapiro				**  we may need to see if this entry is
69694334Sgshapiro				**  part of a particular objectclass.
69794334Sgshapiro				**  Also, ignore objectClass attribute.
69894334Sgshapiro				**  Otherwise we just ignore this attribute.
69994334Sgshapiro				*/
70094334Sgshapiro
70194334Sgshapiro				if (type == SM_LDAP_ATTR_OBJCLASS ||
70294334Sgshapiro				    (needobjclass != NULL &&
70394334Sgshapiro				     !sm_ldap_has_objectclass(lmap, entry,
70494334Sgshapiro							      needobjclass)))
70594334Sgshapiro				{
70694334Sgshapiro					ldap_memfree(attr);
70794334Sgshapiro					continue;
70894334Sgshapiro				}
70994334Sgshapiro
71090792Sgshapiro				if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
71190792Sgshapiro				{
71290792Sgshapiro					vals = ldap_get_values(lmap->ldap_ld,
71390792Sgshapiro							       entry,
71490792Sgshapiro							       attr);
71590792Sgshapiro					if (vals == NULL)
71690792Sgshapiro					{
71790792Sgshapiro						save_errno = sm_ldap_geterrno(lmap->ldap_ld);
71890792Sgshapiro						if (save_errno == LDAP_SUCCESS)
71990792Sgshapiro						{
72090792Sgshapiro							ldap_memfree(attr);
72190792Sgshapiro							continue;
72290792Sgshapiro						}
72390792Sgshapiro
72490792Sgshapiro						/* Must be an error */
72590792Sgshapiro						save_errno += E_LDAPBASE;
72690792Sgshapiro						ldap_memfree(attr);
72794334Sgshapiro						SM_LDAP_ERROR_CLEANUP();
72890792Sgshapiro						errno = save_errno;
72990792Sgshapiro						return EX_TEMPFAIL;
73090792Sgshapiro					}
73190792Sgshapiro				}
73290792Sgshapiro
73390792Sgshapiro				statp = EX_OK;
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				/*
74690792Sgshapiro				**  If matching only,
74790792Sgshapiro				**  no need to spin through entries
74890792Sgshapiro				*/
74990792Sgshapiro
75090792Sgshapiro				if (bitset(SM_LDAP_MATCHONLY, flags))
75190792Sgshapiro				{
75290792Sgshapiro					if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
75390792Sgshapiro						ldap_value_free(vals);
75490792Sgshapiro					ldap_memfree(attr);
75590792Sgshapiro					continue;
75690792Sgshapiro				}
75790792Sgshapiro
75890792Sgshapiro				/*
75990792Sgshapiro				**  If we don't want multiple values,
76090792Sgshapiro				**  return first found.
76190792Sgshapiro				*/
76290792Sgshapiro
76394334Sgshapiro				if ((char) delim == '\0')
76490792Sgshapiro				{
76594334Sgshapiro					if (*result != NULL)
76694334Sgshapiro					{
76794334Sgshapiro						/* already have a value */
76894334Sgshapiro						break;
76994334Sgshapiro					}
77094334Sgshapiro
77194334Sgshapiro					if (bitset(SM_LDAP_SINGLEMATCH,
77294334Sgshapiro						   flags) &&
77394334Sgshapiro					    *result != NULL)
77494334Sgshapiro					{
77594334Sgshapiro						/* only wanted one match */
77694334Sgshapiro						SM_LDAP_ERROR_CLEANUP();
77794334Sgshapiro						errno = ENOENT;
77894334Sgshapiro						return EX_NOTFOUND;
77994334Sgshapiro					}
78094334Sgshapiro
78190792Sgshapiro					if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
78290792Sgshapiro					{
78390792Sgshapiro						*result = sm_rpool_strdup_x(rpool,
78490792Sgshapiro									    attr);
78590792Sgshapiro						ldap_memfree(attr);
78690792Sgshapiro						break;
78790792Sgshapiro					}
78890792Sgshapiro
78990792Sgshapiro					if (vals[0] == NULL)
79090792Sgshapiro					{
79190792Sgshapiro						ldap_value_free(vals);
79290792Sgshapiro						ldap_memfree(attr);
79390792Sgshapiro						continue;
79490792Sgshapiro					}
79590792Sgshapiro
79690792Sgshapiro					vsize = strlen(vals[0]) + 1;
79790792Sgshapiro					if (lmap->ldap_attrsep != '\0')
79890792Sgshapiro						vsize += strlen(attr) + 1;
79990792Sgshapiro					*result = sm_rpool_malloc_x(rpool,
80090792Sgshapiro								    vsize);
80190792Sgshapiro					if (lmap->ldap_attrsep != '\0')
80290792Sgshapiro						sm_snprintf(*result, vsize,
80390792Sgshapiro							    "%s%c%s",
80490792Sgshapiro							    attr,
80590792Sgshapiro							    lmap->ldap_attrsep,
80690792Sgshapiro							    vals[0]);
80790792Sgshapiro					else
80890792Sgshapiro						sm_strlcpy(*result, vals[0],
80990792Sgshapiro							   vsize);
81090792Sgshapiro					ldap_value_free(vals);
81190792Sgshapiro					ldap_memfree(attr);
81290792Sgshapiro					break;
81390792Sgshapiro				}
81490792Sgshapiro
81590792Sgshapiro				/* attributes only */
81690792Sgshapiro				if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
81790792Sgshapiro				{
81890792Sgshapiro					if (*result == NULL)
81990792Sgshapiro						*result = sm_rpool_strdup_x(rpool,
82090792Sgshapiro									    attr);
82190792Sgshapiro					else
82290792Sgshapiro					{
82394334Sgshapiro						if (bitset(SM_LDAP_SINGLEMATCH,
82494334Sgshapiro							   flags) &&
82594334Sgshapiro						    *result != NULL)
82694334Sgshapiro						{
82794334Sgshapiro							/* only wanted one match */
82894334Sgshapiro							SM_LDAP_ERROR_CLEANUP();
82994334Sgshapiro							errno = ENOENT;
83094334Sgshapiro							return EX_NOTFOUND;
83194334Sgshapiro						}
83294334Sgshapiro
83390792Sgshapiro						vsize = strlen(*result) +
83490792Sgshapiro							strlen(attr) + 2;
83590792Sgshapiro						tmp = sm_rpool_malloc_x(rpool,
83690792Sgshapiro									vsize);
83790792Sgshapiro						(void) sm_snprintf(tmp,
83890792Sgshapiro							vsize, "%s%c%s",
83994334Sgshapiro							*result, (char) delim,
84090792Sgshapiro							attr);
84190792Sgshapiro						*result = tmp;
84290792Sgshapiro					}
84390792Sgshapiro					ldap_memfree(attr);
84490792Sgshapiro					continue;
84590792Sgshapiro				}
84690792Sgshapiro
84790792Sgshapiro				/*
84894334Sgshapiro				**  If there is more than one, munge then
84994334Sgshapiro				**  into a map_coldelim separated string.
85094334Sgshapiro				**  If we are recursing we may have an entry
85194334Sgshapiro				**  with no 'normal' values to put in the
85294334Sgshapiro				**  string.
85394334Sgshapiro				**  This is not an error.
85490792Sgshapiro				*/
85590792Sgshapiro
85694334Sgshapiro				if (type == SM_LDAP_ATTR_NORMAL &&
85794334Sgshapiro				    bitset(SM_LDAP_SINGLEMATCH, flags) &&
85894334Sgshapiro				    *result != NULL)
85994334Sgshapiro				{
86094334Sgshapiro					/* only wanted one match */
86194334Sgshapiro					SM_LDAP_ERROR_CLEANUP();
86294334Sgshapiro					errno = ENOENT;
86394334Sgshapiro					return EX_NOTFOUND;
86494334Sgshapiro				}
86594334Sgshapiro
86690792Sgshapiro				vsize = 0;
86790792Sgshapiro				for (i = 0; vals[i] != NULL; i++)
86890792Sgshapiro				{
86994334Sgshapiro					if (type == SM_LDAP_ATTR_DN ||
87094334Sgshapiro					    type == SM_LDAP_ATTR_FILTER ||
87194334Sgshapiro					    type == SM_LDAP_ATTR_URL)
87290792Sgshapiro					{
87394334Sgshapiro						/* add to recursion */
87494334Sgshapiro						if (sm_ldap_add_recurse(&recurse,
87590792Sgshapiro									vals[i],
87694334Sgshapiro									type,
87794334Sgshapiro									rpool) == NULL)
87890792Sgshapiro						{
87994334Sgshapiro							SM_LDAP_ERROR_CLEANUP();
88090792Sgshapiro							errno = ENOMEM;
88190792Sgshapiro							return EX_OSERR;
88290792Sgshapiro						}
88390792Sgshapiro						continue;
88490792Sgshapiro					}
88594334Sgshapiro
88690792Sgshapiro					vsize += strlen(vals[i]) + 1;
88790792Sgshapiro					if (lmap->ldap_attrsep != '\0')
88890792Sgshapiro						vsize += strlen(attr) + 1;
88990792Sgshapiro				}
89090792Sgshapiro
89194334Sgshapiro				/*
89294334Sgshapiro				**  Create/Append to string any normal
89394334Sgshapiro				**  attribute values.  Otherwise, just free
89494334Sgshapiro				**  memory and move on to the next
89594334Sgshapiro				**  attribute in this entry.
89694334Sgshapiro				*/
89794334Sgshapiro
89894334Sgshapiro				if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
89990792Sgshapiro				{
90094334Sgshapiro					char *pe;
90194334Sgshapiro
90294334Sgshapiro					/* Grow result string if needed */
90394334Sgshapiro					if ((*resultln + vsize) >= *resultsz)
90490792Sgshapiro					{
90594334Sgshapiro						while ((*resultln + vsize) >= *resultsz)
90694334Sgshapiro						{
90794334Sgshapiro							if (*resultsz == 0)
90894334Sgshapiro								*resultsz = 1024;
90994334Sgshapiro							else
91094334Sgshapiro								*resultsz *= 2;
91194334Sgshapiro						}
91294334Sgshapiro
91394334Sgshapiro						vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
91494334Sgshapiro						*vp_tmp = '\0';
91594334Sgshapiro
91694334Sgshapiro						if (*result != NULL)
91794334Sgshapiro							sm_strlcpy(vp_tmp,
91894334Sgshapiro								   *result,
91994334Sgshapiro								   *resultsz);
92094334Sgshapiro						*result = vp_tmp;
92190792Sgshapiro					}
92294334Sgshapiro
92394334Sgshapiro					p = *result + *resultln;
92494334Sgshapiro					pe = *result + *resultsz;
92594334Sgshapiro
92694334Sgshapiro					for (i = 0; vals[i] != NULL; i++)
92790792Sgshapiro					{
928102528Sgshapiro						if (*resultln > 0 &&
929102528Sgshapiro						    p < pe)
93094334Sgshapiro							*p++ = (char) delim;
93194334Sgshapiro
93294334Sgshapiro						if (lmap->ldap_attrsep != '\0')
93394334Sgshapiro						{
93494334Sgshapiro							p += sm_strlcpy(p, attr,
93594334Sgshapiro									pe - p);
93694334Sgshapiro							if (p < pe)
93794334Sgshapiro								*p++ = lmap->ldap_attrsep;
93894334Sgshapiro						}
93994334Sgshapiro
94094334Sgshapiro						p += sm_strlcpy(p, vals[i],
94194334Sgshapiro								pe - p);
94294334Sgshapiro						*resultln = p - (*result);
94394334Sgshapiro						if (p >= pe)
94494334Sgshapiro						{
94594334Sgshapiro							/* Internal error: buffer too small for LDAP values */
94694334Sgshapiro							SM_LDAP_ERROR_CLEANUP();
94794334Sgshapiro							errno = ENOMEM;
94894334Sgshapiro							return EX_OSERR;
94994334Sgshapiro						}
95090792Sgshapiro					}
95190792Sgshapiro				}
95290792Sgshapiro
95390792Sgshapiro				ldap_value_free(vals);
95490792Sgshapiro				ldap_memfree(attr);
95590792Sgshapiro			}
95690792Sgshapiro			save_errno = sm_ldap_geterrno(lmap->ldap_ld);
95790792Sgshapiro
95890792Sgshapiro			/*
95990792Sgshapiro			**  We check save_errno != LDAP_DECODING_ERROR since
96090792Sgshapiro			**  OpenLDAP 1.X has a very ugly *undocumented*
96190792Sgshapiro			**  hack of returning this error code from
96290792Sgshapiro			**  ldap_next_attribute() if the library freed the
96390792Sgshapiro			**  ber attribute.  See:
96490792Sgshapiro			**  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
96590792Sgshapiro			*/
96690792Sgshapiro
96790792Sgshapiro			if (save_errno != LDAP_SUCCESS &&
96890792Sgshapiro			    save_errno != LDAP_DECODING_ERROR)
96990792Sgshapiro			{
97090792Sgshapiro				/* Must be an error */
97190792Sgshapiro				save_errno += E_LDAPBASE;
97294334Sgshapiro				SM_LDAP_ERROR_CLEANUP();
97390792Sgshapiro				errno = save_errno;
97490792Sgshapiro				return EX_TEMPFAIL;
97590792Sgshapiro			}
97690792Sgshapiro
97794334Sgshapiro			/* mark this DN as done */
97894334Sgshapiro			rl->lr_done = true;
979132943Sgshapiro			if (rl->lr_ludp != NULL)
980132943Sgshapiro			{
981132943Sgshapiro				ldap_free_urldesc(rl->lr_ludp);
982132943Sgshapiro				rl->lr_ludp = NULL;
983132943Sgshapiro			}
984132943Sgshapiro			if (rl->lr_attrs != NULL)
985132943Sgshapiro			{
986132943Sgshapiro				free(rl->lr_attrs);
987132943Sgshapiro				rl->lr_attrs = NULL;
988132943Sgshapiro			}
98994334Sgshapiro
99090792Sgshapiro			/* We don't want multiple values and we have one */
99194334Sgshapiro			if ((char) delim == '\0' && *result != NULL)
99290792Sgshapiro				break;
99390792Sgshapiro		}
99490792Sgshapiro		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
99590792Sgshapiro		if (save_errno != LDAP_SUCCESS &&
99690792Sgshapiro		    save_errno != LDAP_DECODING_ERROR)
99790792Sgshapiro		{
99890792Sgshapiro			/* Must be an error */
99990792Sgshapiro			save_errno += E_LDAPBASE;
100094334Sgshapiro			SM_LDAP_ERROR_CLEANUP();
100190792Sgshapiro			errno = save_errno;
100290792Sgshapiro			return EX_TEMPFAIL;
100390792Sgshapiro		}
100490792Sgshapiro		ldap_msgfree(lmap->ldap_res);
100590792Sgshapiro		lmap->ldap_res = NULL;
100690792Sgshapiro	}
100790792Sgshapiro
100890792Sgshapiro	if (ret == 0)
100990792Sgshapiro		save_errno = ETIMEDOUT;
101090792Sgshapiro	else
101190792Sgshapiro		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
101290792Sgshapiro	if (save_errno != LDAP_SUCCESS)
101390792Sgshapiro	{
101490792Sgshapiro		statp = EX_TEMPFAIL;
101590792Sgshapiro		if (ret != 0)
101690792Sgshapiro		{
101790792Sgshapiro			switch (save_errno)
101890792Sgshapiro			{
101990792Sgshapiro#ifdef LDAP_SERVER_DOWN
102090792Sgshapiro			  case LDAP_SERVER_DOWN:
102190792Sgshapiro#endif /* LDAP_SERVER_DOWN */
102290792Sgshapiro			  case LDAP_TIMEOUT:
102390792Sgshapiro			  case LDAP_UNAVAILABLE:
102494334Sgshapiro
102594334Sgshapiro				/*
102694334Sgshapiro				**  server disappeared,
102794334Sgshapiro				**  try reopen on next search
102894334Sgshapiro				*/
102994334Sgshapiro
103090792Sgshapiro				statp = EX_RESTART;
103190792Sgshapiro				break;
103290792Sgshapiro			}
103390792Sgshapiro			save_errno += E_LDAPBASE;
103490792Sgshapiro		}
103594334Sgshapiro		SM_LDAP_ERROR_CLEANUP();
103690792Sgshapiro		errno = save_errno;
103790792Sgshapiro		return statp;
103890792Sgshapiro	}
103990792Sgshapiro
104090792Sgshapiro	if (lmap->ldap_res != NULL)
104190792Sgshapiro	{
104290792Sgshapiro		ldap_msgfree(lmap->ldap_res);
104390792Sgshapiro		lmap->ldap_res = NULL;
104490792Sgshapiro	}
104590792Sgshapiro
104690792Sgshapiro	if (toplevel)
104790792Sgshapiro	{
104894334Sgshapiro		int rlidx;
104990792Sgshapiro
105090792Sgshapiro		/*
105190792Sgshapiro		**  Spin through the built-up recurse list at the top
105290792Sgshapiro		**  of the recursion.  Since new items are added at the
105390792Sgshapiro		**  end of the shared list, we actually only ever get
105490792Sgshapiro		**  one level of recursion before things pop back to the
105590792Sgshapiro		**  top.  Any items added to the list during that recursion
105690792Sgshapiro		**  will be expanded by the top level.
105790792Sgshapiro		*/
105890792Sgshapiro
105994334Sgshapiro		for (rlidx = 0; recurse != NULL && rlidx < recurse->lr_cnt; rlidx++)
106090792Sgshapiro		{
106194334Sgshapiro			int newflags;
106290792Sgshapiro			int sid;
106390792Sgshapiro			int status;
106490792Sgshapiro
106594334Sgshapiro			rl = recurse->lr_data[rlidx];
106694334Sgshapiro
106794334Sgshapiro			newflags = flags;
106894334Sgshapiro			if (rl->lr_done)
106990792Sgshapiro			{
107090792Sgshapiro				/* already expanded */
107190792Sgshapiro				continue;
107290792Sgshapiro			}
107394334Sgshapiro
107494334Sgshapiro			if (rl->lr_type == SM_LDAP_ATTR_DN)
107590792Sgshapiro			{
107690792Sgshapiro				/* do DN search */
107790792Sgshapiro				sid = ldap_search(lmap->ldap_ld,
107890792Sgshapiro						  rl->lr_search,
107990792Sgshapiro						  lmap->ldap_scope,
108090792Sgshapiro						  "(objectClass=*)",
108194334Sgshapiro						  (lmap->ldap_attr[0] == NULL ?
108294334Sgshapiro						   NULL : lmap->ldap_attr),
108390792Sgshapiro						  lmap->ldap_attrsonly);
108490792Sgshapiro			}
108594334Sgshapiro			else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
108690792Sgshapiro			{
108790792Sgshapiro				/* do new search */
108890792Sgshapiro				sid = ldap_search(lmap->ldap_ld,
108990792Sgshapiro						  lmap->ldap_base,
109090792Sgshapiro						  lmap->ldap_scope,
109190792Sgshapiro						  rl->lr_search,
109294334Sgshapiro						  (lmap->ldap_attr[0] == NULL ?
109394334Sgshapiro						   NULL : lmap->ldap_attr),
109490792Sgshapiro						  lmap->ldap_attrsonly);
109590792Sgshapiro			}
109694334Sgshapiro			else if (rl->lr_type == SM_LDAP_ATTR_URL)
109790792Sgshapiro			{
1098132943Sgshapiro				/* Parse URL */
1099132943Sgshapiro				sid = ldap_url_parse(rl->lr_search,
1100132943Sgshapiro						     &rl->lr_ludp);
1101132943Sgshapiro
1102132943Sgshapiro				if (sid != 0)
1103132943Sgshapiro				{
1104132943Sgshapiro					errno = sid + E_LDAPURLBASE;
1105132943Sgshapiro					return EX_TEMPFAIL;
1106132943Sgshapiro				}
1107132943Sgshapiro
1108132943Sgshapiro				/* We need to add objectClass */
1109132943Sgshapiro				if (rl->lr_ludp->lud_attrs != NULL)
1110132943Sgshapiro				{
1111132943Sgshapiro					int attrnum = 0;
1112132943Sgshapiro
1113132943Sgshapiro					while (rl->lr_ludp->lud_attrs[attrnum] != NULL)
1114132943Sgshapiro					{
1115132943Sgshapiro						if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum],
1116132943Sgshapiro							       "objectClass") == 0)
1117132943Sgshapiro						{
1118132943Sgshapiro							/* already requested */
1119132943Sgshapiro							attrnum = -1;
1120132943Sgshapiro							break;
1121132943Sgshapiro						}
1122132943Sgshapiro						attrnum++;
1123132943Sgshapiro					}
1124132943Sgshapiro
1125132943Sgshapiro					if (attrnum >= 0)
1126132943Sgshapiro					{
1127132943Sgshapiro						int i;
1128132943Sgshapiro
1129132943Sgshapiro						rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2));
1130132943Sgshapiro						if (rl->lr_attrs == NULL)
1131132943Sgshapiro						{
1132132943Sgshapiro							save_errno = errno;
1133132943Sgshapiro							ldap_free_urldesc(rl->lr_ludp);
1134132943Sgshapiro							errno = save_errno;
1135132943Sgshapiro							return EX_TEMPFAIL;
1136132943Sgshapiro						}
1137132943Sgshapiro						for (i = 0 ; i < attrnum; i++)
1138132943Sgshapiro						{
1139132943Sgshapiro							rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i];
1140132943Sgshapiro						}
1141132943Sgshapiro						rl->lr_attrs[i++] = "objectClass";
1142132943Sgshapiro						rl->lr_attrs[i++] = NULL;
1143132943Sgshapiro					}
1144132943Sgshapiro				}
1145132943Sgshapiro
1146132943Sgshapiro				/*
1147132943Sgshapiro				**  Use the existing connection
1148132943Sgshapiro				**  for this search.  It really
1149132943Sgshapiro				**  should use lud_scheme://lud_host:lud_port/
1150132943Sgshapiro				**  instead but that would require
1151132943Sgshapiro				**  opening a new connection.
1152132943Sgshapiro				**  This should be fixed ASAP.
1153132943Sgshapiro				*/
1154132943Sgshapiro
1155132943Sgshapiro				sid = ldap_search(lmap->ldap_ld,
1156132943Sgshapiro						  rl->lr_ludp->lud_dn,
1157132943Sgshapiro						  rl->lr_ludp->lud_scope,
1158132943Sgshapiro						  rl->lr_ludp->lud_filter,
1159132943Sgshapiro						  rl->lr_attrs,
1160132943Sgshapiro						  lmap->ldap_attrsonly);
1161132943Sgshapiro
1162132943Sgshapiro				/* Use the attributes specified by URL */
116394334Sgshapiro				newflags |= SM_LDAP_USE_ALLATTR;
116490792Sgshapiro			}
116590792Sgshapiro			else
116690792Sgshapiro			{
116790792Sgshapiro				/* unknown or illegal attribute type */
116890792Sgshapiro				errno = EFAULT;
116990792Sgshapiro				return EX_SOFTWARE;
117090792Sgshapiro			}
117190792Sgshapiro
117290792Sgshapiro			/* Collect results */
117390792Sgshapiro			if (sid == -1)
117490792Sgshapiro			{
117590792Sgshapiro				save_errno = sm_ldap_geterrno(lmap->ldap_ld);
117690792Sgshapiro				statp = EX_TEMPFAIL;
117790792Sgshapiro				switch (save_errno)
117890792Sgshapiro				{
117990792Sgshapiro#ifdef LDAP_SERVER_DOWN
118090792Sgshapiro				  case LDAP_SERVER_DOWN:
118190792Sgshapiro#endif /* LDAP_SERVER_DOWN */
118290792Sgshapiro				  case LDAP_TIMEOUT:
118390792Sgshapiro				  case LDAP_UNAVAILABLE:
118494334Sgshapiro
118594334Sgshapiro					/*
118694334Sgshapiro					**  server disappeared,
118794334Sgshapiro					**  try reopen on next search
118894334Sgshapiro					*/
118994334Sgshapiro
119090792Sgshapiro					statp = EX_RESTART;
119190792Sgshapiro					break;
119290792Sgshapiro				}
119390792Sgshapiro				errno = save_errno + E_LDAPBASE;
119490792Sgshapiro				return statp;
119590792Sgshapiro			}
119690792Sgshapiro
119794334Sgshapiro			status = sm_ldap_results(lmap, sid, newflags, delim,
119894334Sgshapiro						 rpool, result, resultln,
119994334Sgshapiro						 resultsz, recurse);
120090792Sgshapiro			save_errno = errno;
120190792Sgshapiro			if (status != EX_OK && status != EX_NOTFOUND)
120290792Sgshapiro			{
120390792Sgshapiro				errno = save_errno;
120490792Sgshapiro				return status;
120590792Sgshapiro			}
120690792Sgshapiro
120790792Sgshapiro			/* Mark as done */
120894334Sgshapiro			rl->lr_done = true;
1209132943Sgshapiro			if (rl->lr_ludp != NULL)
1210132943Sgshapiro			{
1211132943Sgshapiro				ldap_free_urldesc(rl->lr_ludp);
1212132943Sgshapiro				rl->lr_ludp = NULL;
1213132943Sgshapiro			}
1214132943Sgshapiro			if (rl->lr_attrs != NULL)
1215132943Sgshapiro			{
1216132943Sgshapiro				free(rl->lr_attrs);
1217132943Sgshapiro				rl->lr_attrs = NULL;
1218132943Sgshapiro			}
121994334Sgshapiro
122094334Sgshapiro			/* Reset rlidx as new items may have been added */
122194334Sgshapiro			rlidx = -1;
122290792Sgshapiro		}
122390792Sgshapiro	}
122490792Sgshapiro	return statp;
122590792Sgshapiro}
122690792Sgshapiro
122790792Sgshapiro/*
122890792Sgshapiro**  SM_LDAP_CLOSE -- close LDAP connection
122990792Sgshapiro**
123090792Sgshapiro**	Parameters:
123190792Sgshapiro**		lmap -- LDAP map information
123290792Sgshapiro**
123390792Sgshapiro**	Returns:
123490792Sgshapiro**		None.
123590792Sgshapiro**
123690792Sgshapiro*/
123790792Sgshapiro
123890792Sgshapirovoid
123990792Sgshapirosm_ldap_close(lmap)
124090792Sgshapiro	SM_LDAP_STRUCT *lmap;
124190792Sgshapiro{
124290792Sgshapiro	if (lmap->ldap_ld == NULL)
124390792Sgshapiro		return;
124490792Sgshapiro
124590792Sgshapiro	if (lmap->ldap_pid == getpid())
124690792Sgshapiro		ldap_unbind(lmap->ldap_ld);
124790792Sgshapiro	lmap->ldap_ld = NULL;
124890792Sgshapiro	lmap->ldap_pid = 0;
124990792Sgshapiro}
125090792Sgshapiro
125190792Sgshapiro/*
125290792Sgshapiro**  SM_LDAP_SETOPTS -- set LDAP options
125390792Sgshapiro**
125490792Sgshapiro**	Parameters:
125590792Sgshapiro**		ld -- LDAP session handle
125690792Sgshapiro**		lmap -- LDAP map information
125790792Sgshapiro**
125890792Sgshapiro**	Returns:
125990792Sgshapiro**		None.
126090792Sgshapiro**
126190792Sgshapiro*/
126290792Sgshapiro
126390792Sgshapirovoid
126490792Sgshapirosm_ldap_setopts(ld, lmap)
126590792Sgshapiro	LDAP *ld;
126690792Sgshapiro	SM_LDAP_STRUCT *lmap;
126790792Sgshapiro{
126890792Sgshapiro# if USE_LDAP_SET_OPTION
126994334Sgshapiro	if (lmap->ldap_version != 0)
127094334Sgshapiro	{
127194334Sgshapiro		ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
127294334Sgshapiro				&lmap->ldap_version);
127394334Sgshapiro	}
127490792Sgshapiro	ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
127590792Sgshapiro	if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
127690792Sgshapiro		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
127790792Sgshapiro	else
127890792Sgshapiro		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
127990792Sgshapiro	ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
128090792Sgshapiro	ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
1281102528Sgshapiro#  ifdef LDAP_OPT_RESTART
1282102528Sgshapiro	ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
1283102528Sgshapiro#  endif /* LDAP_OPT_RESTART */
128490792Sgshapiro# else /* USE_LDAP_SET_OPTION */
128590792Sgshapiro	/* From here on in we can use ldap internal timelimits */
128690792Sgshapiro	ld->ld_deref = lmap->ldap_deref;
128790792Sgshapiro	ld->ld_options = lmap->ldap_options;
128890792Sgshapiro	ld->ld_sizelimit = lmap->ldap_sizelimit;
128990792Sgshapiro	ld->ld_timelimit = lmap->ldap_timelimit;
129090792Sgshapiro# endif /* USE_LDAP_SET_OPTION */
129190792Sgshapiro}
129290792Sgshapiro
129390792Sgshapiro/*
129490792Sgshapiro**  SM_LDAP_GETERRNO -- get ldap errno value
129590792Sgshapiro**
129690792Sgshapiro**	Parameters:
129790792Sgshapiro**		ld -- LDAP session handle
129890792Sgshapiro**
129990792Sgshapiro**	Returns:
130090792Sgshapiro**		LDAP errno.
130190792Sgshapiro**
130290792Sgshapiro*/
130390792Sgshapiro
130490792Sgshapiroint
130590792Sgshapirosm_ldap_geterrno(ld)
130690792Sgshapiro	LDAP *ld;
130790792Sgshapiro{
130890792Sgshapiro	int err = LDAP_SUCCESS;
130990792Sgshapiro
131090792Sgshapiro# if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
131190792Sgshapiro	(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
131290792Sgshapiro# else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
131390792Sgshapiro#  ifdef LDAP_OPT_SIZELIMIT
131490792Sgshapiro	err = ldap_get_lderrno(ld, NULL, NULL);
131590792Sgshapiro#  else /* LDAP_OPT_SIZELIMIT */
131690792Sgshapiro	err = ld->ld_errno;
131790792Sgshapiro
131890792Sgshapiro	/*
131990792Sgshapiro	**  Reset value to prevent lingering LDAP_DECODING_ERROR due to
132090792Sgshapiro	**  OpenLDAP 1.X's hack (see above)
132190792Sgshapiro	*/
132290792Sgshapiro
132390792Sgshapiro	ld->ld_errno = LDAP_SUCCESS;
132490792Sgshapiro#  endif /* LDAP_OPT_SIZELIMIT */
132590792Sgshapiro# endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
132690792Sgshapiro	return err;
132790792Sgshapiro}
132890792Sgshapiro# endif /* LDAPMAP */
1329