ldap.c revision 125820
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>
11125820SgshapiroSM_RCSID("@(#)$Id: ldap.c,v 1.44.2.5 2003/12/23 21:21:56 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
5594334Sgshapiro	lmap->ldap_target = NULL;
5690792Sgshapiro	lmap->ldap_port = LDAP_PORT;
5794334Sgshapiro#if _FFR_LDAP_URI
5894334Sgshapiro	lmap->ldap_uri = false;
5994334Sgshapiro#endif /* _FFR_LDAP_URI */
6094334Sgshapiro#  if _FFR_LDAP_SETVERSION
6194334Sgshapiro	lmap->ldap_version = 0;
6294334Sgshapiro#  endif /* _FFR_LDAP_SETVERSION */
6390792Sgshapiro	lmap->ldap_deref = LDAP_DEREF_NEVER;
6490792Sgshapiro	lmap->ldap_timelimit = LDAP_NO_LIMIT;
6590792Sgshapiro	lmap->ldap_sizelimit = LDAP_NO_LIMIT;
6690792Sgshapiro# ifdef LDAP_REFERRALS
6790792Sgshapiro	lmap->ldap_options = LDAP_OPT_REFERRALS;
6890792Sgshapiro# else /* LDAP_REFERRALS */
6990792Sgshapiro	lmap->ldap_options = 0;
7090792Sgshapiro# endif /* LDAP_REFERRALS */
7190792Sgshapiro	lmap->ldap_attrsep = '\0';
7290792Sgshapiro	lmap->ldap_binddn = NULL;
7390792Sgshapiro	lmap->ldap_secret = NULL;
7490792Sgshapiro	lmap->ldap_method = LDAP_AUTH_SIMPLE;
7590792Sgshapiro	lmap->ldap_base = NULL;
7690792Sgshapiro	lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
7790792Sgshapiro	lmap->ldap_attrsonly = LDAPMAP_FALSE;
7890792Sgshapiro	lmap->ldap_timeout.tv_sec = 0;
7990792Sgshapiro	lmap->ldap_timeout.tv_usec = 0;
8090792Sgshapiro	lmap->ldap_ld = NULL;
8190792Sgshapiro	lmap->ldap_filter = NULL;
8290792Sgshapiro	lmap->ldap_attr[0] = NULL;
8390792Sgshapiro#if _FFR_LDAP_RECURSION
8494334Sgshapiro	lmap->ldap_attr_type[0] = SM_LDAP_ATTR_NONE;
8594334Sgshapiro	lmap->ldap_attr_needobjclass[0] = NULL;
8690792Sgshapiro#endif /* _FFR_LDAP_RECURSION */
8790792Sgshapiro	lmap->ldap_res = NULL;
8890792Sgshapiro	lmap->ldap_next = NULL;
8990792Sgshapiro	lmap->ldap_pid = 0;
9090792Sgshapiro}
9190792Sgshapiro
9290792Sgshapiro/*
9390792Sgshapiro**  SM_LDAP_START -- actually connect to an LDAP server
9490792Sgshapiro**
9590792Sgshapiro**	Parameters:
9690792Sgshapiro**		name -- name of map for debug output.
9790792Sgshapiro**		lmap -- the LDAP map being opened.
9890792Sgshapiro**
9990792Sgshapiro**	Returns:
10090792Sgshapiro**		true if connection is successful, false otherwise.
10190792Sgshapiro**
10290792Sgshapiro**	Side Effects:
10390792Sgshapiro**		Populates lmap->ldap_ld.
10490792Sgshapiro*/
10590792Sgshapiro
10690792Sgshapirostatic jmp_buf	LDAPTimeout;
10790792Sgshapiro
10890792Sgshapiro#define SM_LDAP_SETTIMEOUT(to)						\
10990792Sgshapirodo									\
11090792Sgshapiro{									\
11190792Sgshapiro	if (to != 0)							\
11290792Sgshapiro	{								\
11390792Sgshapiro		if (setjmp(LDAPTimeout) != 0)				\
11490792Sgshapiro		{							\
11590792Sgshapiro			errno = ETIMEDOUT;				\
11690792Sgshapiro			return false;					\
11790792Sgshapiro		}							\
11890792Sgshapiro		ev = sm_setevent(to, ldaptimeout, 0);			\
11990792Sgshapiro	}								\
12090792Sgshapiro} while (0)
12190792Sgshapiro
12290792Sgshapiro#define SM_LDAP_CLEARTIMEOUT()						\
12390792Sgshapirodo									\
12490792Sgshapiro{									\
12590792Sgshapiro	if (ev != NULL)							\
12690792Sgshapiro		sm_clrevent(ev);					\
12790792Sgshapiro} while (0)
12890792Sgshapiro
12990792Sgshapirobool
13090792Sgshapirosm_ldap_start(name, lmap)
13190792Sgshapiro	char *name;
13290792Sgshapiro	SM_LDAP_STRUCT *lmap;
13390792Sgshapiro{
13490792Sgshapiro	int bind_result;
13590792Sgshapiro	int save_errno;
13690792Sgshapiro	SM_EVENT *ev = NULL;
13790792Sgshapiro	LDAP *ld;
13890792Sgshapiro
13990792Sgshapiro	if (sm_debug_active(&SmLDAPTrace, 2))
14090792Sgshapiro		sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
14190792Sgshapiro
14290792Sgshapiro	if (sm_debug_active(&SmLDAPTrace, 9))
14390792Sgshapiro		sm_dprintf("ldapmap_start(%s, %d)\n",
14494334Sgshapiro			   lmap->ldap_target == NULL ? "localhost" : lmap->ldap_target,
14590792Sgshapiro			   lmap->ldap_port);
14690792Sgshapiro
14790792Sgshapiro# if USE_LDAP_INIT
14894334Sgshapiro#  if _FFR_LDAP_URI
14994334Sgshapiro	if (lmap->ldap_uri)
15094334Sgshapiro		errno = ldap_initialize(&ld, lmap->ldap_target);
15194334Sgshapiro	else
15294334Sgshapiro#  endif /* _FFR_LDAP_URI */
15394334Sgshapiro		ld = ldap_init(lmap->ldap_target, lmap->ldap_port);
15490792Sgshapiro	save_errno = errno;
15590792Sgshapiro# else /* USE_LDAP_INIT */
15690792Sgshapiro	/*
15790792Sgshapiro	**  If using ldap_open(), the actual connection to the server
15890792Sgshapiro	**  happens now so we need the timeout here.  For ldap_init(),
15990792Sgshapiro	**  the connection happens at bind time.
16090792Sgshapiro	*/
16190792Sgshapiro
16290792Sgshapiro	SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
16394334Sgshapiro	ld = ldap_open(lmap->ldap_target, lmap->ldap_port);
16490792Sgshapiro	save_errno = errno;
16590792Sgshapiro
16690792Sgshapiro	/* clear the event if it has not sprung */
16790792Sgshapiro	SM_LDAP_CLEARTIMEOUT();
16890792Sgshapiro# endif /* USE_LDAP_INIT */
16990792Sgshapiro
17090792Sgshapiro	errno = save_errno;
17190792Sgshapiro	if (ld == NULL)
17290792Sgshapiro		return false;
17390792Sgshapiro
17490792Sgshapiro	sm_ldap_setopts(ld, lmap);
17590792Sgshapiro
17690792Sgshapiro# if USE_LDAP_INIT
17790792Sgshapiro	/*
17890792Sgshapiro	**  If using ldap_init(), the actual connection to the server
17990792Sgshapiro	**  happens at ldap_bind_s() so we need the timeout here.
18090792Sgshapiro	*/
18190792Sgshapiro
18290792Sgshapiro	SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
18390792Sgshapiro# endif /* USE_LDAP_INIT */
18490792Sgshapiro
18590792Sgshapiro# ifdef LDAP_AUTH_KRBV4
18690792Sgshapiro	if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
18790792Sgshapiro	    lmap->ldap_secret != NULL)
18890792Sgshapiro	{
18990792Sgshapiro		/*
19090792Sgshapiro		**  Need to put ticket in environment here instead of
19190792Sgshapiro		**  during parseargs as there may be different tickets
19290792Sgshapiro		**  for different LDAP connections.
19390792Sgshapiro		*/
19490792Sgshapiro
19590792Sgshapiro		(void) putenv(lmap->ldap_secret);
19690792Sgshapiro	}
19790792Sgshapiro# endif /* LDAP_AUTH_KRBV4 */
19890792Sgshapiro
19990792Sgshapiro	bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
20090792Sgshapiro				  lmap->ldap_secret, lmap->ldap_method);
20190792Sgshapiro
20290792Sgshapiro# if USE_LDAP_INIT
20390792Sgshapiro	/* clear the event if it has not sprung */
20490792Sgshapiro	SM_LDAP_CLEARTIMEOUT();
20590792Sgshapiro# endif /* USE_LDAP_INIT */
20690792Sgshapiro
20790792Sgshapiro	if (bind_result != LDAP_SUCCESS)
20890792Sgshapiro	{
20990792Sgshapiro		errno = bind_result + E_LDAPBASE;
21090792Sgshapiro		return false;
21190792Sgshapiro	}
21290792Sgshapiro
21390792Sgshapiro	/* Save PID to make sure only this PID closes the LDAP connection */
21490792Sgshapiro	lmap->ldap_pid = getpid();
21590792Sgshapiro	lmap->ldap_ld = ld;
21690792Sgshapiro	return true;
21790792Sgshapiro}
21890792Sgshapiro
21990792Sgshapiro/* ARGSUSED */
22090792Sgshapirostatic void
22190792Sgshapiroldaptimeout(unused)
22290792Sgshapiro	int unused;
22390792Sgshapiro{
22490792Sgshapiro	/*
22590792Sgshapiro	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
22690792Sgshapiro	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
22790792Sgshapiro	**	DOING.
22890792Sgshapiro	*/
22990792Sgshapiro
23090792Sgshapiro	errno = ETIMEDOUT;
23190792Sgshapiro	longjmp(LDAPTimeout, 1);
23290792Sgshapiro}
23390792Sgshapiro
23490792Sgshapiro/*
23590792Sgshapiro**  SM_LDAP_SEARCH -- iniate LDAP search
23690792Sgshapiro**
23790792Sgshapiro**	Initiate an LDAP search, return the msgid.
23890792Sgshapiro**	The calling function must collect the results.
23990792Sgshapiro**
24090792Sgshapiro**	Parameters:
24190792Sgshapiro**		lmap -- LDAP map information
24290792Sgshapiro**		key -- key to substitute in LDAP filter
24390792Sgshapiro**
24490792Sgshapiro**	Returns:
24590792Sgshapiro**		-1 on failure, msgid on success
24690792Sgshapiro**
24790792Sgshapiro*/
24890792Sgshapiro
24990792Sgshapiroint
25090792Sgshapirosm_ldap_search(lmap, key)
25190792Sgshapiro	SM_LDAP_STRUCT *lmap;
25290792Sgshapiro	char *key;
25390792Sgshapiro{
25490792Sgshapiro	int msgid;
25590792Sgshapiro	char *fp, *p, *q;
25690792Sgshapiro	char filter[LDAPMAP_MAX_FILTER + 1];
25790792Sgshapiro
25890792Sgshapiro	/* substitute key into filter, perhaps multiple times */
25990792Sgshapiro	memset(filter, '\0', sizeof filter);
26090792Sgshapiro	fp = filter;
26190792Sgshapiro	p = lmap->ldap_filter;
26290792Sgshapiro	while ((q = strchr(p, '%')) != NULL)
26390792Sgshapiro	{
26490792Sgshapiro		if (q[1] == 's')
26590792Sgshapiro		{
26690792Sgshapiro			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
26790792Sgshapiro					   "%.*s%s", (int) (q - p), p, key);
26890792Sgshapiro			fp += strlen(fp);
26990792Sgshapiro			p = q + 2;
27090792Sgshapiro		}
27190792Sgshapiro		else if (q[1] == '0')
27290792Sgshapiro		{
27390792Sgshapiro			char *k = key;
27490792Sgshapiro
27590792Sgshapiro			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
27690792Sgshapiro					   "%.*s", (int) (q - p), p);
27790792Sgshapiro			fp += strlen(fp);
27890792Sgshapiro			p = q + 2;
27990792Sgshapiro
28090792Sgshapiro			/* Properly escape LDAP special characters */
28190792Sgshapiro			while (SPACELEFT(filter, fp) > 0 &&
28290792Sgshapiro			       *k != '\0')
28390792Sgshapiro			{
28490792Sgshapiro				if (*k == '*' || *k == '(' ||
28590792Sgshapiro				    *k == ')' || *k == '\\')
28690792Sgshapiro				{
28790792Sgshapiro					(void) sm_strlcat(fp,
28890792Sgshapiro						       (*k == '*' ? "\\2A" :
28990792Sgshapiro							(*k == '(' ? "\\28" :
29090792Sgshapiro							 (*k == ')' ? "\\29" :
29190792Sgshapiro							  (*k == '\\' ? "\\5C" :
29290792Sgshapiro							   "\00")))),
29390792Sgshapiro						SPACELEFT(filter, fp));
29490792Sgshapiro					fp += strlen(fp);
29590792Sgshapiro					k++;
29690792Sgshapiro				}
29790792Sgshapiro				else
29890792Sgshapiro					*fp++ = *k++;
29990792Sgshapiro			}
30090792Sgshapiro		}
30190792Sgshapiro		else
30290792Sgshapiro		{
30390792Sgshapiro			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
30490792Sgshapiro				"%.*s", (int) (q - p + 1), p);
30590792Sgshapiro			p = q + (q[1] == '%' ? 2 : 1);
30690792Sgshapiro			fp += strlen(fp);
30790792Sgshapiro		}
30890792Sgshapiro	}
30990792Sgshapiro	(void) sm_strlcpy(fp, p, SPACELEFT(filter, fp));
31090792Sgshapiro	if (sm_debug_active(&SmLDAPTrace, 20))
31190792Sgshapiro		sm_dprintf("ldap search filter=%s\n", filter);
31290792Sgshapiro
31390792Sgshapiro	lmap->ldap_res = NULL;
31494334Sgshapiro	msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base,
31594334Sgshapiro			    lmap->ldap_scope, filter,
31690792Sgshapiro			    (lmap->ldap_attr[0] == NULL ? NULL :
31790792Sgshapiro			     lmap->ldap_attr),
31890792Sgshapiro			    lmap->ldap_attrsonly);
31990792Sgshapiro	return msgid;
32090792Sgshapiro}
32190792Sgshapiro
32290792Sgshapiro# if _FFR_LDAP_RECURSION
32390792Sgshapiro/*
32494334Sgshapiro**  SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
32594334Sgshapiro**			       particular objectClass
32694334Sgshapiro**
32794334Sgshapiro**	Parameters:
32894334Sgshapiro**		lmap -- pointer to SM_LDAP_STRUCT in use
32994334Sgshapiro**		entry -- current LDAP entry struct
33094334Sgshapiro**		ocvalue -- particular objectclass in question.
33194334Sgshapiro**			   may be of form (fee|foo|fum) meaning
33294334Sgshapiro**			   any entry can be part of either fee,
33394334Sgshapiro**			   foo or fum objectclass
33494334Sgshapiro**
33594334Sgshapiro**	Returns:
33694334Sgshapiro**		true if item has that objectClass
33794334Sgshapiro*/
33894334Sgshapiro
33994334Sgshapirostatic bool
34094334Sgshapirosm_ldap_has_objectclass(lmap, entry, ocvalue)
34194334Sgshapiro	SM_LDAP_STRUCT *lmap;
34294334Sgshapiro	LDAPMessage *entry;
34394334Sgshapiro	char *ocvalue;
34494334Sgshapiro{
34594334Sgshapiro	char **vals = NULL;
34694334Sgshapiro	int i;
34794334Sgshapiro
34894334Sgshapiro	if (ocvalue == NULL)
34994334Sgshapiro		return false;
35094334Sgshapiro
35194334Sgshapiro	vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
35294334Sgshapiro	if (vals == NULL)
35394334Sgshapiro		return false;
35494334Sgshapiro
35594334Sgshapiro	for (i = 0; vals[i] != NULL; i++)
35694334Sgshapiro	{
35794334Sgshapiro		char *p;
35894334Sgshapiro		char *q;
35994334Sgshapiro
36094334Sgshapiro		p = q = ocvalue;
36194334Sgshapiro		while (*p != '\0')
36294334Sgshapiro		{
36394334Sgshapiro			while (*p != '\0' && *p != '|')
36494334Sgshapiro				p++;
36594334Sgshapiro
36694334Sgshapiro			if ((p - q) == strlen(vals[i]) &&
36794334Sgshapiro			    sm_strncasecmp(vals[i], q, p - q) == 0)
36894334Sgshapiro			{
36994334Sgshapiro				ldap_value_free(vals);
37094334Sgshapiro				return true;
37194334Sgshapiro			}
37294334Sgshapiro
37394334Sgshapiro			while (*p == '|')
37494334Sgshapiro				p++;
37594334Sgshapiro			q = p;
37694334Sgshapiro		}
37794334Sgshapiro	}
37894334Sgshapiro
37994334Sgshapiro	ldap_value_free(vals);
38094334Sgshapiro	return false;
38194334Sgshapiro}
38294334Sgshapiro
38394334Sgshapiro/*
38490792Sgshapiro**  SM_LDAP_RESULTS -- return results from an LDAP lookup in result
38590792Sgshapiro**
38690792Sgshapiro**	Parameters:
38790792Sgshapiro**		lmap -- pointer to SM_LDAP_STRUCT in use
38890792Sgshapiro**		msgid -- msgid returned by sm_ldap_search()
38990792Sgshapiro**		flags -- flags for the lookup
39090792Sgshapiro**		delim -- delimiter for result concatenation
39190792Sgshapiro**		rpool -- memory pool for storage
39290792Sgshapiro**		result -- return string
39390792Sgshapiro**		recurse -- recursion list
39490792Sgshapiro**
39590792Sgshapiro**	Returns:
39690792Sgshapiro**		status (sysexit)
39790792Sgshapiro*/
39890792Sgshapiro
39994334Sgshapiro# define SM_LDAP_ERROR_CLEANUP()				\
40090792Sgshapiro{								\
40190792Sgshapiro	if (lmap->ldap_res != NULL)				\
40290792Sgshapiro	{							\
40390792Sgshapiro		ldap_msgfree(lmap->ldap_res);			\
40490792Sgshapiro		lmap->ldap_res = NULL;				\
40590792Sgshapiro	}							\
40690792Sgshapiro	(void) ldap_abandon(lmap->ldap_ld, msgid);		\
40790792Sgshapiro}
40890792Sgshapiro
40994334Sgshapirostatic SM_LDAP_RECURSE_ENTRY *
41094334Sgshapirosm_ldap_add_recurse(top, item, type, rpool)
41190792Sgshapiro	SM_LDAP_RECURSE_LIST **top;
41290792Sgshapiro	char *item;
41390792Sgshapiro	int type;
41490792Sgshapiro	SM_RPOOL_T *rpool;
41590792Sgshapiro{
41694334Sgshapiro	int n;
41794334Sgshapiro	int m;
41894334Sgshapiro	int p;
41994334Sgshapiro	int insertat;
42094334Sgshapiro	int moveb;
42194334Sgshapiro	int oldsizeb;
42294334Sgshapiro	int rc;
42394334Sgshapiro	SM_LDAP_RECURSE_ENTRY *newe;
42494334Sgshapiro	SM_LDAP_RECURSE_ENTRY **olddata;
42590792Sgshapiro
42694334Sgshapiro	/*
42794334Sgshapiro	**  This code will maintain a list of
42894334Sgshapiro	**  SM_LDAP_RECURSE_ENTRY structures
42994334Sgshapiro	**  in ascending order.
43094334Sgshapiro	*/
43194334Sgshapiro
43294334Sgshapiro	if (*top == NULL)
43390792Sgshapiro	{
43494334Sgshapiro		/* Allocate an initial SM_LDAP_RECURSE_LIST struct */
43594334Sgshapiro		*top = sm_rpool_malloc_x(rpool, sizeof **top);
43694334Sgshapiro		(*top)->lr_cnt = 0;
43794334Sgshapiro		(*top)->lr_size = 0;
43894334Sgshapiro		(*top)->lr_data = NULL;
43994334Sgshapiro	}
44094334Sgshapiro
44194334Sgshapiro	if ((*top)->lr_cnt >= (*top)->lr_size)
44294334Sgshapiro	{
44394334Sgshapiro		/* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
44494334Sgshapiro		olddata = (*top)->lr_data;
44594334Sgshapiro		if ((*top)->lr_size == 0)
44690792Sgshapiro		{
44794334Sgshapiro			oldsizeb = 0;
44894334Sgshapiro			(*top)->lr_size = 256;
44990792Sgshapiro		}
45094334Sgshapiro		else
45194334Sgshapiro		{
45294334Sgshapiro			oldsizeb = (*top)->lr_size * sizeof *((*top)->lr_data);
45394334Sgshapiro			(*top)->lr_size *= 2;
45494334Sgshapiro		}
45594334Sgshapiro		(*top)->lr_data = sm_rpool_malloc_x(rpool,
45694334Sgshapiro						    (*top)->lr_size * sizeof *((*top)->lr_data));
45794334Sgshapiro		if (oldsizeb > 0)
45894334Sgshapiro			memcpy((*top)->lr_data, olddata, oldsizeb);
45990792Sgshapiro	}
46090792Sgshapiro
46194334Sgshapiro	/*
46294334Sgshapiro	**  Binary search/insert item:type into list.
46394334Sgshapiro	**  Return current entry pointer if already exists.
46494334Sgshapiro	*/
46594334Sgshapiro
46694334Sgshapiro	n = 0;
46794334Sgshapiro	m = (*top)->lr_cnt - 1;
46894334Sgshapiro	if (m < 0)
46994334Sgshapiro		insertat = 0;
47090792Sgshapiro	else
47194334Sgshapiro		insertat = -1;
47294334Sgshapiro
47394334Sgshapiro	while (insertat == -1)
47494334Sgshapiro	{
47594334Sgshapiro		p = (m + n) / 2;
47694334Sgshapiro
47794334Sgshapiro		rc = sm_strcasecmp(item, (*top)->lr_data[p]->lr_search);
47894334Sgshapiro		if (rc == 0)
47994334Sgshapiro			rc = type - (*top)->lr_data[p]->lr_type;
48094334Sgshapiro
48194334Sgshapiro		if (rc < 0)
48294334Sgshapiro			m = p - 1;
48394334Sgshapiro		else if (rc > 0)
48494334Sgshapiro			n = p + 1;
48594334Sgshapiro		else
48694334Sgshapiro			return (*top)->lr_data[p];
48794334Sgshapiro
48894334Sgshapiro		if (m == -1)
48994334Sgshapiro			insertat = 0;
49094334Sgshapiro		else if (n >= (*top)->lr_cnt)
49194334Sgshapiro			insertat = (*top)->lr_cnt;
49294334Sgshapiro		else if (m < n)
49394334Sgshapiro			insertat = m + 1;
49494334Sgshapiro	}
49594334Sgshapiro
49694334Sgshapiro	/*
49794334Sgshapiro	** Not found in list, make room
49894334Sgshapiro	** at insert point and add it.
49994334Sgshapiro	*/
50094334Sgshapiro
50194334Sgshapiro	newe = sm_rpool_malloc_x(rpool, sizeof *newe);
50294334Sgshapiro	if (newe != NULL)
50394334Sgshapiro	{
50494334Sgshapiro		moveb = ((*top)->lr_cnt - insertat) * sizeof *((*top)->lr_data);
50594334Sgshapiro		if (moveb > 0)
50694334Sgshapiro			memmove(&((*top)->lr_data[insertat + 1]),
50794334Sgshapiro				&((*top)->lr_data[insertat]),
50894334Sgshapiro				moveb);
50994334Sgshapiro
51094334Sgshapiro		newe->lr_search = sm_rpool_strdup_x(rpool, item);
51194334Sgshapiro		newe->lr_type = type;
51294334Sgshapiro		newe->lr_done = false;
51394334Sgshapiro
51494334Sgshapiro		((*top)->lr_data)[insertat] = newe;
51594334Sgshapiro		(*top)->lr_cnt++;
51694334Sgshapiro	}
51794334Sgshapiro	return newe;
51890792Sgshapiro}
51990792Sgshapiro
52090792Sgshapiroint
52194334Sgshapirosm_ldap_results(lmap, msgid, flags, delim, rpool, result,
52294334Sgshapiro		resultln, resultsz, recurse)
52390792Sgshapiro	SM_LDAP_STRUCT *lmap;
52490792Sgshapiro	int msgid;
52590792Sgshapiro	int flags;
52694334Sgshapiro	int delim;
52790792Sgshapiro	SM_RPOOL_T *rpool;
52890792Sgshapiro	char **result;
52994334Sgshapiro	int *resultln;
53094334Sgshapiro	int *resultsz;
53190792Sgshapiro	SM_LDAP_RECURSE_LIST *recurse;
53290792Sgshapiro{
53390792Sgshapiro	bool toplevel;
53490792Sgshapiro	int i;
53590792Sgshapiro	int statp;
53690792Sgshapiro	int vsize;
53790792Sgshapiro	int ret;
53890792Sgshapiro	int save_errno;
53990792Sgshapiro	char *p;
54094334Sgshapiro	SM_LDAP_RECURSE_ENTRY *rl;
54190792Sgshapiro
54290792Sgshapiro	/* Are we the top top level of the search? */
54390792Sgshapiro	toplevel = (recurse == NULL);
54490792Sgshapiro
54590792Sgshapiro	/* Get results */
54690792Sgshapiro	statp = EX_NOTFOUND;
54790792Sgshapiro	while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
54890792Sgshapiro				  (lmap->ldap_timeout.tv_sec == 0 ? NULL :
54990792Sgshapiro				   &(lmap->ldap_timeout)),
55090792Sgshapiro				  &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
55190792Sgshapiro	{
55290792Sgshapiro		LDAPMessage *entry;
55390792Sgshapiro
55490792Sgshapiro		/* If we don't want multiple values and we have one, break */
55594334Sgshapiro		if ((char) delim == '\0' && *result != NULL)
55690792Sgshapiro			break;
55790792Sgshapiro
55890792Sgshapiro		/* Cycle through all entries */
55990792Sgshapiro		for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
56090792Sgshapiro		     entry != NULL;
56190792Sgshapiro		     entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
56290792Sgshapiro		{
56390792Sgshapiro			BerElement *ber;
56490792Sgshapiro			char *attr;
56590792Sgshapiro			char **vals = NULL;
56690792Sgshapiro			char *dn;
56790792Sgshapiro
56890792Sgshapiro			/*
56990792Sgshapiro			**  If matching only and found an entry,
57090792Sgshapiro			**  no need to spin through attributes
57190792Sgshapiro			*/
57290792Sgshapiro
573125820Sgshapiro			if (bitset(SM_LDAP_MATCHONLY, flags))
574125820Sgshapiro			{
575125820Sgshapiro				statp = EX_OK;
57690792Sgshapiro				continue;
577125820Sgshapiro			}
57890792Sgshapiro
57990792Sgshapiro			/* record completed DN's to prevent loops */
58090792Sgshapiro			dn = ldap_get_dn(lmap->ldap_ld, entry);
58190792Sgshapiro			if (dn == NULL)
58290792Sgshapiro			{
58390792Sgshapiro				save_errno = sm_ldap_geterrno(lmap->ldap_ld);
58490792Sgshapiro				save_errno += E_LDAPBASE;
58594334Sgshapiro				SM_LDAP_ERROR_CLEANUP();
58690792Sgshapiro				errno = save_errno;
587120256Sgshapiro				return EX_TEMPFAIL;
58890792Sgshapiro			}
58990792Sgshapiro
59094334Sgshapiro			rl = sm_ldap_add_recurse(&recurse, dn,
59194334Sgshapiro						 SM_LDAP_ATTR_DN,
59294334Sgshapiro						 rpool);
59394334Sgshapiro
59494334Sgshapiro			if (rl == NULL)
59590792Sgshapiro			{
59690792Sgshapiro				ldap_memfree(dn);
59794334Sgshapiro				SM_LDAP_ERROR_CLEANUP();
59890792Sgshapiro				errno = ENOMEM;
59990792Sgshapiro				return EX_OSERR;
60094334Sgshapiro			}
60194334Sgshapiro			else if (rl->lr_done)
60294334Sgshapiro			{
60390792Sgshapiro				/* already on list, skip it */
60490792Sgshapiro				ldap_memfree(dn);
60590792Sgshapiro				continue;
60690792Sgshapiro			}
60790792Sgshapiro			ldap_memfree(dn);
60890792Sgshapiro
60990792Sgshapiro# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
61090792Sgshapiro			/*
61190792Sgshapiro			**  Reset value to prevent lingering
61290792Sgshapiro			**  LDAP_DECODING_ERROR due to
61390792Sgshapiro			**  OpenLDAP 1.X's hack (see below)
61490792Sgshapiro			*/
61590792Sgshapiro
61690792Sgshapiro			lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
61790792Sgshapiro# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
61890792Sgshapiro
61990792Sgshapiro			for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
62090792Sgshapiro							 &ber);
62190792Sgshapiro			     attr != NULL;
62290792Sgshapiro			     attr = ldap_next_attribute(lmap->ldap_ld, entry,
62390792Sgshapiro							ber))
62490792Sgshapiro			{
62590792Sgshapiro				char *tmp, *vp_tmp;
62690792Sgshapiro				int type;
62794334Sgshapiro				char *needobjclass = NULL;
62890792Sgshapiro
62994334Sgshapiro				type = SM_LDAP_ATTR_NONE;
63090792Sgshapiro				for (i = 0; lmap->ldap_attr[i] != NULL; i++)
63190792Sgshapiro				{
63290792Sgshapiro					if (sm_strcasecmp(lmap->ldap_attr[i],
63390792Sgshapiro							  attr) == 0)
63490792Sgshapiro					{
63590792Sgshapiro						type = lmap->ldap_attr_type[i];
63694334Sgshapiro						needobjclass = lmap->ldap_attr_needobjclass[i];
63790792Sgshapiro						break;
63890792Sgshapiro					}
63990792Sgshapiro				}
64094334Sgshapiro
64194334Sgshapiro				if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
64294334Sgshapiro				    type == SM_LDAP_ATTR_NONE)
64390792Sgshapiro				{
64494334Sgshapiro					/* URL lookups specify attrs to use */
64594334Sgshapiro					type = SM_LDAP_ATTR_NORMAL;
64694334Sgshapiro					needobjclass = NULL;
64794334Sgshapiro				}
64894334Sgshapiro
64994334Sgshapiro				if (type == SM_LDAP_ATTR_NONE)
65094334Sgshapiro				{
65190792Sgshapiro					/* attribute not requested */
65290792Sgshapiro					ldap_memfree(attr);
65394334Sgshapiro					SM_LDAP_ERROR_CLEANUP();
65490792Sgshapiro					errno = EFAULT;
65590792Sgshapiro					return EX_SOFTWARE;
65690792Sgshapiro				}
65790792Sgshapiro
65894334Sgshapiro				/*
65994334Sgshapiro				**  For recursion on a particular attribute,
66094334Sgshapiro				**  we may need to see if this entry is
66194334Sgshapiro				**  part of a particular objectclass.
66294334Sgshapiro				**  Also, ignore objectClass attribute.
66394334Sgshapiro				**  Otherwise we just ignore this attribute.
66494334Sgshapiro				*/
66594334Sgshapiro
66694334Sgshapiro				if (type == SM_LDAP_ATTR_OBJCLASS ||
66794334Sgshapiro				    (needobjclass != NULL &&
66894334Sgshapiro				     !sm_ldap_has_objectclass(lmap, entry,
66994334Sgshapiro							      needobjclass)))
67094334Sgshapiro				{
67194334Sgshapiro					ldap_memfree(attr);
67294334Sgshapiro					continue;
67394334Sgshapiro				}
67494334Sgshapiro
67590792Sgshapiro				if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
67690792Sgshapiro				{
67790792Sgshapiro					vals = ldap_get_values(lmap->ldap_ld,
67890792Sgshapiro							       entry,
67990792Sgshapiro							       attr);
68090792Sgshapiro					if (vals == NULL)
68190792Sgshapiro					{
68290792Sgshapiro						save_errno = sm_ldap_geterrno(lmap->ldap_ld);
68390792Sgshapiro						if (save_errno == LDAP_SUCCESS)
68490792Sgshapiro						{
68590792Sgshapiro							ldap_memfree(attr);
68690792Sgshapiro							continue;
68790792Sgshapiro						}
68890792Sgshapiro
68990792Sgshapiro						/* Must be an error */
69090792Sgshapiro						save_errno += E_LDAPBASE;
69190792Sgshapiro						ldap_memfree(attr);
69294334Sgshapiro						SM_LDAP_ERROR_CLEANUP();
69390792Sgshapiro						errno = save_errno;
69490792Sgshapiro						return EX_TEMPFAIL;
69590792Sgshapiro					}
69690792Sgshapiro				}
69790792Sgshapiro
69890792Sgshapiro				statp = EX_OK;
69990792Sgshapiro
70090792Sgshapiro# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
70190792Sgshapiro				/*
70290792Sgshapiro				**  Reset value to prevent lingering
70390792Sgshapiro				**  LDAP_DECODING_ERROR due to
70490792Sgshapiro				**  OpenLDAP 1.X's hack (see below)
70590792Sgshapiro				*/
70690792Sgshapiro
70790792Sgshapiro				lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
70890792Sgshapiro# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
70990792Sgshapiro
71090792Sgshapiro				/*
71190792Sgshapiro				**  If matching only,
71290792Sgshapiro				**  no need to spin through entries
71390792Sgshapiro				*/
71490792Sgshapiro
71590792Sgshapiro				if (bitset(SM_LDAP_MATCHONLY, flags))
71690792Sgshapiro				{
71790792Sgshapiro					if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
71890792Sgshapiro						ldap_value_free(vals);
71990792Sgshapiro					ldap_memfree(attr);
72090792Sgshapiro					continue;
72190792Sgshapiro				}
72290792Sgshapiro
72390792Sgshapiro				/*
72490792Sgshapiro				**  If we don't want multiple values,
72590792Sgshapiro				**  return first found.
72690792Sgshapiro				*/
72790792Sgshapiro
72894334Sgshapiro				if ((char) delim == '\0')
72990792Sgshapiro				{
73094334Sgshapiro					if (*result != NULL)
73194334Sgshapiro					{
73294334Sgshapiro						/* already have a value */
73394334Sgshapiro						break;
73494334Sgshapiro					}
73594334Sgshapiro
73694334Sgshapiro					if (bitset(SM_LDAP_SINGLEMATCH,
73794334Sgshapiro						   flags) &&
73894334Sgshapiro					    *result != NULL)
73994334Sgshapiro					{
74094334Sgshapiro						/* only wanted one match */
74194334Sgshapiro						SM_LDAP_ERROR_CLEANUP();
74294334Sgshapiro						errno = ENOENT;
74394334Sgshapiro						return EX_NOTFOUND;
74494334Sgshapiro					}
74594334Sgshapiro
74690792Sgshapiro					if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
74790792Sgshapiro					{
74890792Sgshapiro						*result = sm_rpool_strdup_x(rpool,
74990792Sgshapiro									    attr);
75090792Sgshapiro						ldap_memfree(attr);
75190792Sgshapiro						break;
75290792Sgshapiro					}
75390792Sgshapiro
75490792Sgshapiro					if (vals[0] == NULL)
75590792Sgshapiro					{
75690792Sgshapiro						ldap_value_free(vals);
75790792Sgshapiro						ldap_memfree(attr);
75890792Sgshapiro						continue;
75990792Sgshapiro					}
76090792Sgshapiro
76190792Sgshapiro					vsize = strlen(vals[0]) + 1;
76290792Sgshapiro					if (lmap->ldap_attrsep != '\0')
76390792Sgshapiro						vsize += strlen(attr) + 1;
76490792Sgshapiro					*result = sm_rpool_malloc_x(rpool,
76590792Sgshapiro								    vsize);
76690792Sgshapiro					if (lmap->ldap_attrsep != '\0')
76790792Sgshapiro						sm_snprintf(*result, vsize,
76890792Sgshapiro							    "%s%c%s",
76990792Sgshapiro							    attr,
77090792Sgshapiro							    lmap->ldap_attrsep,
77190792Sgshapiro							    vals[0]);
77290792Sgshapiro					else
77390792Sgshapiro						sm_strlcpy(*result, vals[0],
77490792Sgshapiro							   vsize);
77590792Sgshapiro					ldap_value_free(vals);
77690792Sgshapiro					ldap_memfree(attr);
77790792Sgshapiro					break;
77890792Sgshapiro				}
77990792Sgshapiro
78090792Sgshapiro				/* attributes only */
78190792Sgshapiro				if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
78290792Sgshapiro				{
78390792Sgshapiro					if (*result == NULL)
78490792Sgshapiro						*result = sm_rpool_strdup_x(rpool,
78590792Sgshapiro									    attr);
78690792Sgshapiro					else
78790792Sgshapiro					{
78894334Sgshapiro						if (bitset(SM_LDAP_SINGLEMATCH,
78994334Sgshapiro							   flags) &&
79094334Sgshapiro						    *result != NULL)
79194334Sgshapiro						{
79294334Sgshapiro							/* only wanted one match */
79394334Sgshapiro							SM_LDAP_ERROR_CLEANUP();
79494334Sgshapiro							errno = ENOENT;
79594334Sgshapiro							return EX_NOTFOUND;
79694334Sgshapiro						}
79794334Sgshapiro
79890792Sgshapiro						vsize = strlen(*result) +
79990792Sgshapiro							strlen(attr) + 2;
80090792Sgshapiro						tmp = sm_rpool_malloc_x(rpool,
80190792Sgshapiro									vsize);
80290792Sgshapiro						(void) sm_snprintf(tmp,
80390792Sgshapiro							vsize, "%s%c%s",
80494334Sgshapiro							*result, (char) delim,
80590792Sgshapiro							attr);
80690792Sgshapiro						*result = tmp;
80790792Sgshapiro					}
80890792Sgshapiro					ldap_memfree(attr);
80990792Sgshapiro					continue;
81090792Sgshapiro				}
81190792Sgshapiro
81290792Sgshapiro				/*
81394334Sgshapiro				**  If there is more than one, munge then
81494334Sgshapiro				**  into a map_coldelim separated string.
81594334Sgshapiro				**  If we are recursing we may have an entry
81694334Sgshapiro				**  with no 'normal' values to put in the
81794334Sgshapiro				**  string.
81894334Sgshapiro				**  This is not an error.
81990792Sgshapiro				*/
82090792Sgshapiro
82194334Sgshapiro				if (type == SM_LDAP_ATTR_NORMAL &&
82294334Sgshapiro				    bitset(SM_LDAP_SINGLEMATCH, flags) &&
82394334Sgshapiro				    *result != NULL)
82494334Sgshapiro				{
82594334Sgshapiro					/* only wanted one match */
82694334Sgshapiro					SM_LDAP_ERROR_CLEANUP();
82794334Sgshapiro					errno = ENOENT;
82894334Sgshapiro					return EX_NOTFOUND;
82994334Sgshapiro				}
83094334Sgshapiro
83190792Sgshapiro				vsize = 0;
83290792Sgshapiro				for (i = 0; vals[i] != NULL; i++)
83390792Sgshapiro				{
83494334Sgshapiro					if (type == SM_LDAP_ATTR_DN ||
83594334Sgshapiro					    type == SM_LDAP_ATTR_FILTER ||
83694334Sgshapiro					    type == SM_LDAP_ATTR_URL)
83790792Sgshapiro					{
83894334Sgshapiro						/* add to recursion */
83994334Sgshapiro						if (sm_ldap_add_recurse(&recurse,
84090792Sgshapiro									vals[i],
84194334Sgshapiro									type,
84294334Sgshapiro									rpool) == NULL)
84390792Sgshapiro						{
84494334Sgshapiro							SM_LDAP_ERROR_CLEANUP();
84590792Sgshapiro							errno = ENOMEM;
84690792Sgshapiro							return EX_OSERR;
84790792Sgshapiro						}
84890792Sgshapiro						continue;
84990792Sgshapiro					}
85094334Sgshapiro
85190792Sgshapiro					vsize += strlen(vals[i]) + 1;
85290792Sgshapiro					if (lmap->ldap_attrsep != '\0')
85390792Sgshapiro						vsize += strlen(attr) + 1;
85490792Sgshapiro				}
85590792Sgshapiro
85694334Sgshapiro				/*
85794334Sgshapiro				**  Create/Append to string any normal
85894334Sgshapiro				**  attribute values.  Otherwise, just free
85994334Sgshapiro				**  memory and move on to the next
86094334Sgshapiro				**  attribute in this entry.
86194334Sgshapiro				*/
86294334Sgshapiro
86394334Sgshapiro				if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
86490792Sgshapiro				{
86594334Sgshapiro					char *pe;
86694334Sgshapiro
86794334Sgshapiro					/* Grow result string if needed */
86894334Sgshapiro					if ((*resultln + vsize) >= *resultsz)
86990792Sgshapiro					{
87094334Sgshapiro						while ((*resultln + vsize) >= *resultsz)
87194334Sgshapiro						{
87294334Sgshapiro							if (*resultsz == 0)
87394334Sgshapiro								*resultsz = 1024;
87494334Sgshapiro							else
87594334Sgshapiro								*resultsz *= 2;
87694334Sgshapiro						}
87794334Sgshapiro
87894334Sgshapiro						vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
87994334Sgshapiro						*vp_tmp = '\0';
88094334Sgshapiro
88194334Sgshapiro						if (*result != NULL)
88294334Sgshapiro							sm_strlcpy(vp_tmp,
88394334Sgshapiro								   *result,
88494334Sgshapiro								   *resultsz);
88594334Sgshapiro						*result = vp_tmp;
88690792Sgshapiro					}
88794334Sgshapiro
88894334Sgshapiro					p = *result + *resultln;
88994334Sgshapiro					pe = *result + *resultsz;
89094334Sgshapiro
89194334Sgshapiro					for (i = 0; vals[i] != NULL; i++)
89290792Sgshapiro					{
893102528Sgshapiro						if (*resultln > 0 &&
894102528Sgshapiro						    p < pe)
89594334Sgshapiro							*p++ = (char) delim;
89694334Sgshapiro
89794334Sgshapiro						if (lmap->ldap_attrsep != '\0')
89894334Sgshapiro						{
89994334Sgshapiro							p += sm_strlcpy(p, attr,
90094334Sgshapiro									pe - p);
90194334Sgshapiro							if (p < pe)
90294334Sgshapiro								*p++ = lmap->ldap_attrsep;
90394334Sgshapiro						}
90494334Sgshapiro
90594334Sgshapiro						p += sm_strlcpy(p, vals[i],
90694334Sgshapiro								pe - p);
90794334Sgshapiro						*resultln = p - (*result);
90894334Sgshapiro						if (p >= pe)
90994334Sgshapiro						{
91094334Sgshapiro							/* Internal error: buffer too small for LDAP values */
91194334Sgshapiro							SM_LDAP_ERROR_CLEANUP();
91294334Sgshapiro							errno = ENOMEM;
91394334Sgshapiro							return EX_OSERR;
91494334Sgshapiro						}
91590792Sgshapiro					}
91690792Sgshapiro				}
91790792Sgshapiro
91890792Sgshapiro				ldap_value_free(vals);
91990792Sgshapiro				ldap_memfree(attr);
92090792Sgshapiro			}
92190792Sgshapiro			save_errno = sm_ldap_geterrno(lmap->ldap_ld);
92290792Sgshapiro
92390792Sgshapiro			/*
92490792Sgshapiro			**  We check save_errno != LDAP_DECODING_ERROR since
92590792Sgshapiro			**  OpenLDAP 1.X has a very ugly *undocumented*
92690792Sgshapiro			**  hack of returning this error code from
92790792Sgshapiro			**  ldap_next_attribute() if the library freed the
92890792Sgshapiro			**  ber attribute.  See:
92990792Sgshapiro			**  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
93090792Sgshapiro			*/
93190792Sgshapiro
93290792Sgshapiro			if (save_errno != LDAP_SUCCESS &&
93390792Sgshapiro			    save_errno != LDAP_DECODING_ERROR)
93490792Sgshapiro			{
93590792Sgshapiro				/* Must be an error */
93690792Sgshapiro				save_errno += E_LDAPBASE;
93794334Sgshapiro				SM_LDAP_ERROR_CLEANUP();
93890792Sgshapiro				errno = save_errno;
93990792Sgshapiro				return EX_TEMPFAIL;
94090792Sgshapiro			}
94190792Sgshapiro
94294334Sgshapiro			/* mark this DN as done */
94394334Sgshapiro			rl->lr_done = true;
94494334Sgshapiro
94590792Sgshapiro			/* We don't want multiple values and we have one */
94694334Sgshapiro			if ((char) delim == '\0' && *result != NULL)
94790792Sgshapiro				break;
94890792Sgshapiro		}
94990792Sgshapiro		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
95090792Sgshapiro		if (save_errno != LDAP_SUCCESS &&
95190792Sgshapiro		    save_errno != LDAP_DECODING_ERROR)
95290792Sgshapiro		{
95390792Sgshapiro			/* Must be an error */
95490792Sgshapiro			save_errno += E_LDAPBASE;
95594334Sgshapiro			SM_LDAP_ERROR_CLEANUP();
95690792Sgshapiro			errno = save_errno;
95790792Sgshapiro			return EX_TEMPFAIL;
95890792Sgshapiro		}
95990792Sgshapiro		ldap_msgfree(lmap->ldap_res);
96090792Sgshapiro		lmap->ldap_res = NULL;
96190792Sgshapiro	}
96290792Sgshapiro
96390792Sgshapiro	if (ret == 0)
96490792Sgshapiro		save_errno = ETIMEDOUT;
96590792Sgshapiro	else
96690792Sgshapiro		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
96790792Sgshapiro	if (save_errno != LDAP_SUCCESS)
96890792Sgshapiro	{
96990792Sgshapiro		statp = EX_TEMPFAIL;
97090792Sgshapiro		if (ret != 0)
97190792Sgshapiro		{
97290792Sgshapiro			switch (save_errno)
97390792Sgshapiro			{
97490792Sgshapiro#ifdef LDAP_SERVER_DOWN
97590792Sgshapiro			  case LDAP_SERVER_DOWN:
97690792Sgshapiro#endif /* LDAP_SERVER_DOWN */
97790792Sgshapiro			  case LDAP_TIMEOUT:
97890792Sgshapiro			  case LDAP_UNAVAILABLE:
97994334Sgshapiro
98094334Sgshapiro				/*
98194334Sgshapiro				**  server disappeared,
98294334Sgshapiro				**  try reopen on next search
98394334Sgshapiro				*/
98494334Sgshapiro
98590792Sgshapiro				statp = EX_RESTART;
98690792Sgshapiro				break;
98790792Sgshapiro			}
98890792Sgshapiro			save_errno += E_LDAPBASE;
98990792Sgshapiro		}
99094334Sgshapiro		SM_LDAP_ERROR_CLEANUP();
99190792Sgshapiro		errno = save_errno;
99290792Sgshapiro		return statp;
99390792Sgshapiro	}
99490792Sgshapiro
99590792Sgshapiro	if (lmap->ldap_res != NULL)
99690792Sgshapiro	{
99790792Sgshapiro		ldap_msgfree(lmap->ldap_res);
99890792Sgshapiro		lmap->ldap_res = NULL;
99990792Sgshapiro	}
100090792Sgshapiro
100190792Sgshapiro	if (toplevel)
100290792Sgshapiro	{
100394334Sgshapiro		int rlidx;
100490792Sgshapiro
100590792Sgshapiro		/*
100690792Sgshapiro		**  Spin through the built-up recurse list at the top
100790792Sgshapiro		**  of the recursion.  Since new items are added at the
100890792Sgshapiro		**  end of the shared list, we actually only ever get
100990792Sgshapiro		**  one level of recursion before things pop back to the
101090792Sgshapiro		**  top.  Any items added to the list during that recursion
101190792Sgshapiro		**  will be expanded by the top level.
101290792Sgshapiro		*/
101390792Sgshapiro
101494334Sgshapiro		for (rlidx = 0; recurse != NULL && rlidx < recurse->lr_cnt; rlidx++)
101590792Sgshapiro		{
101694334Sgshapiro			int newflags;
101790792Sgshapiro			int sid;
101890792Sgshapiro			int status;
101990792Sgshapiro
102094334Sgshapiro			rl = recurse->lr_data[rlidx];
102194334Sgshapiro
102294334Sgshapiro			newflags = flags;
102394334Sgshapiro			if (rl->lr_done)
102490792Sgshapiro			{
102590792Sgshapiro				/* already expanded */
102690792Sgshapiro				continue;
102790792Sgshapiro			}
102894334Sgshapiro
102994334Sgshapiro			if (rl->lr_type == SM_LDAP_ATTR_DN)
103090792Sgshapiro			{
103190792Sgshapiro				/* do DN search */
103290792Sgshapiro				sid = ldap_search(lmap->ldap_ld,
103390792Sgshapiro						  rl->lr_search,
103490792Sgshapiro						  lmap->ldap_scope,
103590792Sgshapiro						  "(objectClass=*)",
103694334Sgshapiro						  (lmap->ldap_attr[0] == NULL ?
103794334Sgshapiro						   NULL : lmap->ldap_attr),
103890792Sgshapiro						  lmap->ldap_attrsonly);
103990792Sgshapiro			}
104094334Sgshapiro			else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
104190792Sgshapiro			{
104290792Sgshapiro				/* do new search */
104390792Sgshapiro				sid = ldap_search(lmap->ldap_ld,
104490792Sgshapiro						  lmap->ldap_base,
104590792Sgshapiro						  lmap->ldap_scope,
104690792Sgshapiro						  rl->lr_search,
104794334Sgshapiro						  (lmap->ldap_attr[0] == NULL ?
104894334Sgshapiro						   NULL : lmap->ldap_attr),
104990792Sgshapiro						  lmap->ldap_attrsonly);
105090792Sgshapiro			}
105194334Sgshapiro			else if (rl->lr_type == SM_LDAP_ATTR_URL)
105290792Sgshapiro			{
105390792Sgshapiro				/* do new URL search */
105490792Sgshapiro				sid = ldap_url_search(lmap->ldap_ld,
105590792Sgshapiro						      rl->lr_search,
105690792Sgshapiro						      lmap->ldap_attrsonly);
105794334Sgshapiro				newflags |= SM_LDAP_USE_ALLATTR;
105890792Sgshapiro			}
105990792Sgshapiro			else
106090792Sgshapiro			{
106190792Sgshapiro				/* unknown or illegal attribute type */
106290792Sgshapiro				errno = EFAULT;
106390792Sgshapiro				return EX_SOFTWARE;
106490792Sgshapiro			}
106590792Sgshapiro
106690792Sgshapiro			/* Collect results */
106790792Sgshapiro			if (sid == -1)
106890792Sgshapiro			{
106990792Sgshapiro				save_errno = sm_ldap_geterrno(lmap->ldap_ld);
107090792Sgshapiro				statp = EX_TEMPFAIL;
107190792Sgshapiro				switch (save_errno)
107290792Sgshapiro				{
107390792Sgshapiro#ifdef LDAP_SERVER_DOWN
107490792Sgshapiro				  case LDAP_SERVER_DOWN:
107590792Sgshapiro#endif /* LDAP_SERVER_DOWN */
107690792Sgshapiro				  case LDAP_TIMEOUT:
107790792Sgshapiro				  case LDAP_UNAVAILABLE:
107894334Sgshapiro
107994334Sgshapiro					/*
108094334Sgshapiro					**  server disappeared,
108194334Sgshapiro					**  try reopen on next search
108294334Sgshapiro					*/
108394334Sgshapiro
108490792Sgshapiro					statp = EX_RESTART;
108590792Sgshapiro					break;
108690792Sgshapiro				}
108790792Sgshapiro				errno = save_errno + E_LDAPBASE;
108890792Sgshapiro				return statp;
108990792Sgshapiro			}
109090792Sgshapiro
109194334Sgshapiro			status = sm_ldap_results(lmap, sid, newflags, delim,
109294334Sgshapiro						 rpool, result, resultln,
109394334Sgshapiro						 resultsz, recurse);
109490792Sgshapiro			save_errno = errno;
109590792Sgshapiro			if (status != EX_OK && status != EX_NOTFOUND)
109690792Sgshapiro			{
109790792Sgshapiro				errno = save_errno;
109890792Sgshapiro				return status;
109990792Sgshapiro			}
110090792Sgshapiro
110190792Sgshapiro			/* Mark as done */
110294334Sgshapiro			rl->lr_done = true;
110394334Sgshapiro
110494334Sgshapiro			/* Reset rlidx as new items may have been added */
110594334Sgshapiro			rlidx = -1;
110690792Sgshapiro		}
110790792Sgshapiro	}
110890792Sgshapiro	return statp;
110990792Sgshapiro}
111090792Sgshapiro#endif /* _FFR_LDAP_RECURSION */
111190792Sgshapiro
111290792Sgshapiro/*
111390792Sgshapiro**  SM_LDAP_CLOSE -- close LDAP connection
111490792Sgshapiro**
111590792Sgshapiro**	Parameters:
111690792Sgshapiro**		lmap -- LDAP map information
111790792Sgshapiro**
111890792Sgshapiro**	Returns:
111990792Sgshapiro**		None.
112090792Sgshapiro**
112190792Sgshapiro*/
112290792Sgshapiro
112390792Sgshapirovoid
112490792Sgshapirosm_ldap_close(lmap)
112590792Sgshapiro	SM_LDAP_STRUCT *lmap;
112690792Sgshapiro{
112790792Sgshapiro	if (lmap->ldap_ld == NULL)
112890792Sgshapiro		return;
112990792Sgshapiro
113090792Sgshapiro	if (lmap->ldap_pid == getpid())
113190792Sgshapiro		ldap_unbind(lmap->ldap_ld);
113290792Sgshapiro	lmap->ldap_ld = NULL;
113390792Sgshapiro	lmap->ldap_pid = 0;
113490792Sgshapiro}
113590792Sgshapiro
113690792Sgshapiro/*
113790792Sgshapiro**  SM_LDAP_SETOPTS -- set LDAP options
113890792Sgshapiro**
113990792Sgshapiro**	Parameters:
114090792Sgshapiro**		ld -- LDAP session handle
114190792Sgshapiro**		lmap -- LDAP map information
114290792Sgshapiro**
114390792Sgshapiro**	Returns:
114490792Sgshapiro**		None.
114590792Sgshapiro**
114690792Sgshapiro*/
114790792Sgshapiro
114890792Sgshapirovoid
114990792Sgshapirosm_ldap_setopts(ld, lmap)
115090792Sgshapiro	LDAP *ld;
115190792Sgshapiro	SM_LDAP_STRUCT *lmap;
115290792Sgshapiro{
115390792Sgshapiro# if USE_LDAP_SET_OPTION
115494334Sgshapiro#  if _FFR_LDAP_SETVERSION
115594334Sgshapiro	if (lmap->ldap_version != 0)
115694334Sgshapiro	{
115794334Sgshapiro		ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
115894334Sgshapiro				&lmap->ldap_version);
115994334Sgshapiro	}
116094334Sgshapiro#  endif /* _FFR_LDAP_SETVERSION */
116190792Sgshapiro	ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
116290792Sgshapiro	if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
116390792Sgshapiro		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
116490792Sgshapiro	else
116590792Sgshapiro		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
116690792Sgshapiro	ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
116790792Sgshapiro	ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
1168102528Sgshapiro#  ifdef LDAP_OPT_RESTART
1169102528Sgshapiro	ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
1170102528Sgshapiro#  endif /* LDAP_OPT_RESTART */
117190792Sgshapiro# else /* USE_LDAP_SET_OPTION */
117290792Sgshapiro	/* From here on in we can use ldap internal timelimits */
117390792Sgshapiro	ld->ld_deref = lmap->ldap_deref;
117490792Sgshapiro	ld->ld_options = lmap->ldap_options;
117590792Sgshapiro	ld->ld_sizelimit = lmap->ldap_sizelimit;
117690792Sgshapiro	ld->ld_timelimit = lmap->ldap_timelimit;
117790792Sgshapiro# endif /* USE_LDAP_SET_OPTION */
117890792Sgshapiro}
117990792Sgshapiro
118090792Sgshapiro/*
118190792Sgshapiro**  SM_LDAP_GETERRNO -- get ldap errno value
118290792Sgshapiro**
118390792Sgshapiro**	Parameters:
118490792Sgshapiro**		ld -- LDAP session handle
118590792Sgshapiro**
118690792Sgshapiro**	Returns:
118790792Sgshapiro**		LDAP errno.
118890792Sgshapiro**
118990792Sgshapiro*/
119090792Sgshapiro
119190792Sgshapiroint
119290792Sgshapirosm_ldap_geterrno(ld)
119390792Sgshapiro	LDAP *ld;
119490792Sgshapiro{
119590792Sgshapiro	int err = LDAP_SUCCESS;
119690792Sgshapiro
119790792Sgshapiro# if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
119890792Sgshapiro	(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
119990792Sgshapiro# else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
120090792Sgshapiro#  ifdef LDAP_OPT_SIZELIMIT
120190792Sgshapiro	err = ldap_get_lderrno(ld, NULL, NULL);
120290792Sgshapiro#  else /* LDAP_OPT_SIZELIMIT */
120390792Sgshapiro	err = ld->ld_errno;
120490792Sgshapiro
120590792Sgshapiro	/*
120690792Sgshapiro	**  Reset value to prevent lingering LDAP_DECODING_ERROR due to
120790792Sgshapiro	**  OpenLDAP 1.X's hack (see above)
120890792Sgshapiro	*/
120990792Sgshapiro
121090792Sgshapiro	ld->ld_errno = LDAP_SUCCESS;
121190792Sgshapiro#  endif /* LDAP_OPT_SIZELIMIT */
121290792Sgshapiro# endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
121390792Sgshapiro	return err;
121490792Sgshapiro}
121590792Sgshapiro# endif /* LDAPMAP */
1216