1/*
2 * Copyright (c) 2001-2009 Proofpoint, Inc. and its suppliers.
3 *      All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 */
9
10/* some "deprecated" calls are used, e.g., ldap_get_values() */
11#define LDAP_DEPRECATED	1
12
13#include <sm/gen.h>
14SM_RCSID("@(#)$Id: ldap.c,v 1.86 2013-11-22 20:51:43 ca Exp $")
15
16#if LDAPMAP
17# include <sys/types.h>
18# include <errno.h>
19# include <setjmp.h>
20# include <stdlib.h>
21# include <unistd.h>
22
23# include <sm/bitops.h>
24# include <sm/clock.h>
25# include <sm/conf.h>
26# include <sm/debug.h>
27# include <sm/errstring.h>
28# include <sm/ldap.h>
29# include <sm/string.h>
30# include <sm/sysexits.h>
31# include <sm/sendmail.h>
32
33SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
34	"@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
35
36static bool	sm_ldap_has_objectclass __P((SM_LDAP_STRUCT *, LDAPMessage *, char *));
37static SM_LDAP_RECURSE_ENTRY *sm_ldap_add_recurse __P((SM_LDAP_RECURSE_LIST **, char *, int, SM_RPOOL_T *));
38
39static char *sm_ldap_geterror __P((LDAP *));
40
41/*
42**  SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
43**
44**	Parameters:
45**		lmap -- pointer to SM_LDAP_STRUCT to clear
46**
47**	Returns:
48**		None.
49**
50*/
51
52# if _FFR_LDAP_VERSION
53#  if defined(LDAP_VERSION_MAX) && _FFR_LDAP_VERSION > LDAP_VERSION_MAX
54#   error "_FFR_LDAP_VERSION > LDAP_VERSION_MAX"
55#  endif
56#  if defined(LDAP_VERSION_MIN) && _FFR_LDAP_VERSION < LDAP_VERSION_MIN
57#   error "_FFR_LDAP_VERSION < LDAP_VERSION_MAX"
58#  endif
59#  define SM_LDAP_VERSION_DEFAULT	_FFR_LDAP_VERSION
60# else /* _FFR_LDAP_VERSION */
61#  define SM_LDAP_VERSION_DEFAULT	0
62# endif /* _FFR_LDAP_VERSION */
63
64void
65sm_ldap_clear(lmap)
66	SM_LDAP_STRUCT *lmap;
67{
68	if (lmap == NULL)
69		return;
70
71	lmap->ldap_host = NULL;
72	lmap->ldap_port = LDAP_PORT;
73	lmap->ldap_uri = NULL;
74	lmap->ldap_version = SM_LDAP_VERSION_DEFAULT;
75	lmap->ldap_deref = LDAP_DEREF_NEVER;
76	lmap->ldap_timelimit = LDAP_NO_LIMIT;
77	lmap->ldap_sizelimit = LDAP_NO_LIMIT;
78# ifdef LDAP_REFERRALS
79	lmap->ldap_options = LDAP_OPT_REFERRALS;
80# else
81	lmap->ldap_options = 0;
82# endif
83	lmap->ldap_attrsep = '\0';
84# if LDAP_NETWORK_TIMEOUT
85	lmap->ldap_networktmo = 0;
86# endif
87	lmap->ldap_binddn = NULL;
88	lmap->ldap_secret = NULL;
89	lmap->ldap_method = LDAP_AUTH_SIMPLE;
90	lmap->ldap_base = NULL;
91	lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
92	lmap->ldap_attrsonly = LDAPMAP_FALSE;
93	lmap->ldap_timeout.tv_sec = 0;
94	lmap->ldap_timeout.tv_usec = 0;
95	lmap->ldap_ld = NULL;
96	lmap->ldap_filter = NULL;
97	lmap->ldap_attr[0] = NULL;
98	lmap->ldap_attr_type[0] = SM_LDAP_ATTR_NONE;
99	lmap->ldap_attr_needobjclass[0] = NULL;
100	lmap->ldap_res = NULL;
101	lmap->ldap_next = NULL;
102	lmap->ldap_pid = 0;
103	lmap->ldap_multi_args = false;
104}
105
106# if _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN)
107static void ldap_debug_cb __P((const char *msg));
108
109static void
110ldap_debug_cb(msg)
111	const char *msg;
112{
113	if (sm_debug_active(&SmLDAPTrace, 4))
114		sm_dprintf("%s", msg);
115}
116# endif /* _FFR_SM_LDAP_DBG && defined(LBER_OPT_LOG_PRINT_FN) */
117
118
119# if LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT)
120#  define SET_LDAP_TMO(ld, lmap)					\
121	do								\
122	{								\
123		if (lmap->ldap_networktmo > 0)				\
124		{							\
125			struct timeval tmo;				\
126									\
127			if (sm_debug_active(&SmLDAPTrace, 9))		\
128				sm_dprintf("ldap_networktmo=%d\n",	\
129					lmap->ldap_networktmo);		\
130			tmo.tv_sec = lmap->ldap_networktmo;		\
131			tmo.tv_usec = 0;				\
132			ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tmo); \
133		}	\
134	} while (0)
135# else /* LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT) */
136#  define SET_LDAP_TMO(ld, lmap)
137# endif /* LDAP_NETWORK_TIMEOUT && defined(LDAP_OPT_NETWORK_TIMEOUT) */
138
139/*
140**  SM_LDAP_SETOPTSG -- set some (global) LDAP options
141**
142**	Parameters:
143**		lmap -- LDAP map information
144**
145**	Returns:
146**		None.
147**
148*/
149
150# if _FFR_SM_LDAP_DBG
151static bool dbg_init = false;
152# endif
153# if SM_CONF_LDAP_INITIALIZE
154static void sm_ldap_setoptsg __P((SM_LDAP_STRUCT *lmap));
155static void
156sm_ldap_setoptsg(lmap)
157	SM_LDAP_STRUCT *lmap;
158{
159#  if USE_LDAP_SET_OPTION
160
161	SET_LDAP_TMO(NULL, lmap);
162
163#   if _FFR_SM_LDAP_DBG
164	if (!dbg_init && sm_debug_active(&SmLDAPTrace, 1) &&
165	    lmap->ldap_debug != 0)
166	{
167		int r;
168#    if defined(LBER_OPT_LOG_PRINT_FN)
169		r = ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN, ldap_debug_cb);
170#    endif
171		if (sm_debug_active(&SmLDAPTrace, 9))
172			sm_dprintf("ldap_debug0=%d\n", lmap->ldap_debug);
173		r = ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL,
174				&(lmap->ldap_debug));
175		if (sm_debug_active(&SmLDAPTrace, 9) && r != LDAP_OPT_SUCCESS)
176			sm_dprintf("ber_set_option=%d\n", r);
177		r = ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL,
178				&(lmap->ldap_debug));
179		if (sm_debug_active(&SmLDAPTrace, 9) && r != LDAP_OPT_SUCCESS)
180			sm_dprintf("ldap_set_option=%d\n", r);
181		dbg_init = true;
182	}
183#   endif /* _FFR_SM_LDAP_DBG */
184#  endif /* USE_LDAP_SET_OPTION */
185}
186# endif /* SM_CONF_LDAP_INITIALIZE */
187
188/*
189**  SM_LDAP_SETOPTS -- set LDAP options
190**
191**	Parameters:
192**		ld -- LDAP session handle
193**		lmap -- LDAP map information
194**
195**	Returns:
196**		None.
197**
198*/
199
200void
201sm_ldap_setopts(ld, lmap)
202	LDAP *ld;
203	SM_LDAP_STRUCT *lmap;
204{
205# if USE_LDAP_SET_OPTION
206	if (lmap->ldap_version != 0)
207	{
208		ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
209				&lmap->ldap_version);
210	}
211	ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
212	if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
213		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
214	else
215		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
216	ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
217	ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
218	SET_LDAP_TMO(ld, lmap);
219#  if _FFR_SM_LDAP_DBG
220	if ((!dbg_init || ld != NULL) && sm_debug_active(&SmLDAPTrace, 1)
221	    && lmap->ldap_debug > 0)
222	{
223		int r;
224
225		if (sm_debug_active(&SmLDAPTrace, 9))
226			sm_dprintf("ldap_debug=%d, dbg_init=%d\n",
227				lmap->ldap_debug, dbg_init);
228		r = ldap_set_option(ld, LDAP_OPT_DEBUG_LEVEL,
229				&(lmap->ldap_debug));
230		if (sm_debug_active(&SmLDAPTrace, 9) && r != LDAP_OPT_SUCCESS)
231			sm_dprintf("ldap_set_option=%d\n", r);
232	}
233#  endif /* _FFR_SM_LDAP_DBG */
234#  ifdef LDAP_OPT_RESTART
235	ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
236#  endif
237#  if _FFR_TESTS
238	if (sm_debug_active(&SmLDAPTrace, 101))
239	{
240		char *cert;
241		char buf[PATH_MAX];
242
243		cert = getcwd(buf, sizeof(buf));
244		if (NULL != cert)
245		{
246			int r;
247
248			(void) sm_strlcat(buf, "/ldaps.pem", sizeof(buf));
249			r = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTFILE, cert);
250			sm_dprintf("LDAP_OPT_X_TLS_CACERTFILE(%s)=%d\n", cert, r);
251		}
252	}
253#  endif /* _FFR_TESTS */
254
255# else /* USE_LDAP_SET_OPTION */
256	/* From here on in we can use ldap internal timelimits */
257	ld->ld_deref = lmap->ldap_deref;
258	ld->ld_options = lmap->ldap_options;
259	ld->ld_sizelimit = lmap->ldap_sizelimit;
260	ld->ld_timelimit = lmap->ldap_timelimit;
261# endif /* USE_LDAP_SET_OPTION */
262}
263
264/*
265**  SM_LDAP_START -- actually connect to an LDAP server
266**
267**	Parameters:
268**		name -- name of map for debug output.
269**		lmap -- the LDAP map being opened.
270**
271**	Returns:
272**		true if connection is successful, false otherwise.
273**
274**	Side Effects:
275**		Populates lmap->ldap_ld.
276*/
277
278# if !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT
279static jmp_buf	LDAPTimeout;
280static void	ldaptimeout __P((int));
281
282/* ARGSUSED */
283static void
284ldaptimeout(unused)
285	int unused;
286{
287	/*
288	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
289	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
290	**	DOING.
291	*/
292
293	errno = ETIMEDOUT;
294	longjmp(LDAPTimeout, 1);
295}
296
297
298#define SM_LDAP_SETTIMEOUT(to, where)					\
299do									\
300{									\
301	if (to != 0)							\
302	{								\
303		if (setjmp(LDAPTimeout) != 0)				\
304		{							\
305			if (sm_debug_active(&SmLDAPTrace, 9))		\
306				sm_dprintf("ldap_settimeout(%s)=triggered\n",\
307					where);				\
308			errno = ETIMEDOUT;				\
309			return false;					\
310		}							\
311		ev = sm_setevent(to, ldaptimeout, 0);			\
312	}								\
313} while (0)
314
315#define SM_LDAP_CLEARTIMEOUT()						\
316do									\
317{									\
318	if (ev != NULL)							\
319		sm_clrevent(ev);					\
320} while (0)
321# endif /* !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT */
322
323bool
324sm_ldap_start(name, lmap)
325	char *name;
326	SM_LDAP_STRUCT *lmap;
327{
328	int save_errno = 0;
329	char *id;
330	char *errmsg;
331# if !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT
332	SM_EVENT *ev = NULL;
333# endif
334	LDAP *ld = NULL;
335	struct timeval tmo;
336	int msgid, err, r;
337
338	errmsg = NULL;
339	if (sm_debug_active(&SmLDAPTrace, 2))
340		sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
341
342	if (lmap->ldap_host != NULL)
343		id = lmap->ldap_host;
344	else if (lmap->ldap_uri != NULL)
345		id = lmap->ldap_uri;
346	else
347		id = "localhost";
348
349	if (sm_debug_active(&SmLDAPTrace, 9))
350	{
351		/* Don't print a port number for LDAP URIs */
352		if (lmap->ldap_uri != NULL)
353			sm_dprintf("ldapmap_start(%s)\n", id);
354		else
355			sm_dprintf("ldapmap_start(%s, %d)\n", id,
356				   lmap->ldap_port);
357	}
358
359	if (lmap->ldap_uri != NULL)
360	{
361# if SM_CONF_LDAP_INITIALIZE
362		if (sm_debug_active(&SmLDAPTrace, 9))
363			sm_dprintf("ldap_initialize(%s)\n", lmap->ldap_uri);
364		/* LDAP server supports URIs so use them directly */
365		save_errno = ldap_initialize(&ld, lmap->ldap_uri);
366		if (sm_debug_active(&SmLDAPTrace, 9))
367			sm_dprintf("ldap_initialize(%s)=%d, ld=%p\n", lmap->ldap_uri, save_errno, ld);
368		sm_ldap_setoptsg(lmap);
369
370# else /* SM_CONF_LDAP_INITIALIZE */
371		LDAPURLDesc *ludp = NULL;
372
373		/* Blast apart URL and use the ldap_init/ldap_open below */
374		err = ldap_url_parse(lmap->ldap_uri, &ludp);
375		if (err != 0)
376		{
377			errno = err + E_LDAPURLBASE;
378			return false;
379		}
380		lmap->ldap_host = sm_strdup_x(ludp->lud_host);
381		if (lmap->ldap_host == NULL)
382		{
383			save_errno = errno;
384			ldap_free_urldesc(ludp);
385			errno = save_errno;
386			return false;
387		}
388		lmap->ldap_port = ludp->lud_port;
389		ldap_free_urldesc(ludp);
390# endif /* SM_CONF_LDAP_INITIALIZE */
391	}
392
393	if (ld == NULL)
394	{
395# if USE_LDAP_INIT
396		if (sm_debug_active(&SmLDAPTrace, 9))
397			sm_dprintf("ldap_init(%s, %d)\n", lmap->ldap_host, lmap->ldap_port);
398		ld = ldap_init(lmap->ldap_host, lmap->ldap_port);
399		save_errno = errno;
400
401# else /* USE_LDAP_INIT */
402		/*
403		**  If using ldap_open(), the actual connection to the server
404		**  happens now so we need the timeout here.  For ldap_init(),
405		**  the connection happens at bind time.
406		*/
407
408		if (sm_debug_active(&SmLDAPTrace, 9))
409			sm_dprintf("ldap_open(%s, %d)\n", lmap->ldap_host, lmap->ldap_port);
410
411		SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec, "ldap_open");
412		ld = ldap_open(lmap->ldap_host, lmap->ldap_port);
413		save_errno = errno;
414
415		/* clear the event if it has not sprung */
416		SM_LDAP_CLEARTIMEOUT();
417# endif /* USE_LDAP_INIT */
418	}
419
420	errno = save_errno;
421	if (ld == NULL)
422	{
423		if (sm_debug_active(&SmLDAPTrace, 7))
424			sm_dprintf("FAIL: ldap_open(%s, %d)=%d\n", lmap->ldap_host, lmap->ldap_port, save_errno);
425		return false;
426	}
427
428	sm_ldap_setopts(ld, lmap);
429# if USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT
430	/*
431	**  If using ldap_init(), the actual connection to the server
432	**  happens at ldap_bind_s() so we need the timeout here.
433	*/
434
435	SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec, "ldap_bind");
436# endif /* USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT */
437
438# ifdef LDAP_AUTH_KRBV4
439	if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
440	    lmap->ldap_secret != NULL)
441	{
442		/*
443		**  Need to put ticket in environment here instead of
444		**  during parseargs as there may be different tickets
445		**  for different LDAP connections.
446		*/
447
448		(void) putenv(lmap->ldap_secret);
449	}
450# endif /* LDAP_AUTH_KRBV4 */
451
452# if LDAP_NETWORK_TIMEOUT
453	tmo.tv_sec = lmap->ldap_networktmo;
454# else
455	tmo.tv_sec = lmap->ldap_timeout.tv_sec;
456# endif
457	tmo.tv_usec = 0;
458
459	if (sm_debug_active(&SmLDAPTrace, 9))
460		sm_dprintf("ldap_bind(%s)\n", lmap->ldap_uri);
461	errno = 0;
462	msgid = ldap_bind(ld, lmap->ldap_binddn, lmap->ldap_secret,
463			lmap->ldap_method);
464	save_errno = errno;
465	if (sm_debug_active(&SmLDAPTrace, 9))
466	{
467		errmsg = sm_ldap_geterror(ld);
468		sm_dprintf("ldap_bind(%s)=%d, errno=%d, ldaperr=%d, ld_error=%s, tmo=%lld\n",
469			lmap->ldap_uri, msgid, save_errno,
470			sm_ldap_geterrno(ld), errmsg, (long long) tmo.tv_sec);
471		if (NULL != errmsg)
472		{
473			ldap_memfree(errmsg);
474			errmsg = NULL;
475		}
476	}
477	if (-1 == msgid)
478	{
479		r = -1;
480		err = sm_ldap_geterrno(ld);
481		if (LDAP_SUCCESS != err)
482			save_errno = err + E_LDAPBASE;
483		goto fail;
484	}
485
486	errno = 0;
487	r = ldap_result(ld, msgid, LDAP_MSG_ALL,
488			tmo.tv_sec == 0 ? NULL : &(tmo), &(lmap->ldap_res));
489	save_errno = errno;
490	if (sm_debug_active(&SmLDAPTrace, 9))
491	{
492		errmsg = sm_ldap_geterror(ld);
493		sm_dprintf("ldap_result(%s)=%d, errno=%d, ldaperr=%d, ld_error=%s\n",
494			lmap->ldap_uri, r, errno,
495			sm_ldap_geterrno(ld), errmsg);
496		if (NULL != errmsg)
497		{
498			ldap_memfree(errmsg);
499			errmsg = NULL;
500		}
501	}
502	if (-1 == r)
503	{
504		err = sm_ldap_geterrno(ld);
505		if (LDAP_SUCCESS != err)
506			save_errno = err + E_LDAPBASE;
507		goto fail;
508	}
509	if (0 == r)
510	{
511		save_errno = ETIMEDOUT;
512		r = -1;
513		goto fail;
514	}
515	r = ldap_parse_result(ld, lmap->ldap_res, &err, NULL, &errmsg, NULL,
516				NULL, 1);
517	save_errno = errno;
518	if (sm_debug_active(&SmLDAPTrace, 9))
519		sm_dprintf("ldap_parse_result(%s)=%d, err=%d, errmsg=%s\n",
520				lmap->ldap_uri, r, err, errmsg);
521	if (r != LDAP_SUCCESS)
522		goto fail;
523	if (err != LDAP_SUCCESS)
524	{
525		r = err;
526		goto fail;
527	}
528
529# if USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT
530	/* clear the event if it has not sprung */
531	SM_LDAP_CLEARTIMEOUT();
532	if (sm_debug_active(&SmLDAPTrace, 9))
533		sm_dprintf("ldap_cleartimeout(%s)\n", lmap->ldap_uri);
534# endif /* USE_LDAP_INIT && !LDAP_NETWORK_TIMEOUT */
535
536	if (r != LDAP_SUCCESS)
537	{
538  fail:
539		if (-1 == r)
540			errno = save_errno;
541		else
542			errno = r + E_LDAPBASE;
543		if (NULL != errmsg)
544		{
545			ldap_memfree(errmsg);
546			errmsg = NULL;
547		}
548		return false;
549	}
550
551	/* Save PID to make sure only this PID closes the LDAP connection */
552	lmap->ldap_pid = getpid();
553	lmap->ldap_ld = ld;
554	if (NULL != errmsg)
555	{
556		ldap_memfree(errmsg);
557		errmsg = NULL;
558	}
559	return true;
560}
561
562/*
563**  SM_LDAP_SEARCH_M -- initiate multi-key LDAP search
564**
565**	Initiate an LDAP search, return the msgid.
566**	The calling function must collect the results.
567**
568**	Parameters:
569**		lmap -- LDAP map information
570**		argv -- key vector of substitutions in LDAP filter
571**			NOTE: argv must have SM_LDAP_ARGS elements to prevent
572**			      out of bound array references
573**
574**	Returns:
575**		<0 on failure (SM_LDAP_ERR*), msgid on success
576**
577*/
578
579int
580sm_ldap_search_m(lmap, argv)
581	SM_LDAP_STRUCT *lmap;
582	char **argv;
583{
584	int msgid;
585	char *fp, *p, *q;
586	char filter[LDAPMAP_MAX_FILTER + 1];
587
588	SM_REQUIRE(lmap != NULL);
589	SM_REQUIRE(argv != NULL);
590	SM_REQUIRE(argv[0] != NULL);
591
592	memset(filter, '\0', sizeof filter);
593	fp = filter;
594	p = lmap->ldap_filter;
595	while ((q = strchr(p, '%')) != NULL)
596	{
597		char *key;
598
599		if (lmap->ldap_multi_args)
600		{
601# if SM_LDAP_ARGS < 10
602#  error _SM_LDAP_ARGS must be 10
603# endif
604			if (q[1] == 's')
605				key = argv[0];
606			else if (q[1] >= '0' && q[1] <= '9')
607			{
608				key = argv[q[1] - '0'];
609				if (key == NULL)
610				{
611# if SM_LDAP_ERROR_ON_MISSING_ARGS
612					return SM_LDAP_ERR_ARG_MISS;
613# else
614					key = "";
615# endif
616				}
617			}
618			else
619				key = NULL;
620		}
621		else
622			key = argv[0];
623
624		if (q[1] == 's')
625		{
626			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
627					   "%.*s%s", (int) (q - p), p, key);
628			fp += strlen(fp);
629			p = q + 2;
630		}
631		else if (q[1] == '0' ||
632			 (lmap->ldap_multi_args && q[1] >= '0' && q[1] <= '9'))
633		{
634			char *k = key;
635
636			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
637					   "%.*s", (int) (q - p), p);
638			fp += strlen(fp);
639			p = q + 2;
640
641			/* Properly escape LDAP special characters */
642			while (SPACELEFT(filter, fp) > 0 &&
643			       *k != '\0')
644			{
645				if (*k == '*' || *k == '(' ||
646				    *k == ')' || *k == '\\')
647				{
648					(void) sm_strlcat(fp,
649						       (*k == '*' ? "\\2A" :
650							(*k == '(' ? "\\28" :
651							 (*k == ')' ? "\\29" :
652							  (*k == '\\' ? "\\5C" :
653							   "\00")))),
654						SPACELEFT(filter, fp));
655					fp += strlen(fp);
656					k++;
657				}
658				else
659					*fp++ = *k++;
660			}
661		}
662		else
663		{
664			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
665				"%.*s", (int) (q - p + 1), p);
666			p = q + (q[1] == '%' ? 2 : 1);
667			fp += strlen(fp);
668		}
669	}
670	(void) sm_strlcpy(fp, p, SPACELEFT(filter, fp));
671	if (sm_debug_active(&SmLDAPTrace, 20))
672		sm_dprintf("ldap search filter=%s\n", filter);
673
674	lmap->ldap_res = NULL;
675	msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base,
676			    lmap->ldap_scope, filter,
677			    (lmap->ldap_attr[0] == NULL ? NULL :
678			     lmap->ldap_attr),
679			    lmap->ldap_attrsonly);
680	return msgid;
681}
682
683/*
684**  SM_LDAP_SEARCH -- initiate LDAP search
685**
686**	Initiate an LDAP search, return the msgid.
687**	The calling function must collect the results.
688**	Note this is just a wrapper into sm_ldap_search_m()
689**
690**	Parameters:
691**		lmap -- LDAP map information
692**		key -- key to substitute in LDAP filter
693**
694**	Returns:
695**		<0 on failure, msgid on success
696**
697*/
698
699int
700sm_ldap_search(lmap, key)
701	SM_LDAP_STRUCT *lmap;
702	char *key;
703{
704	char *argv[SM_LDAP_ARGS];
705
706	memset(argv, '\0', sizeof argv);
707	argv[0] = key;
708	return sm_ldap_search_m(lmap, argv);
709}
710
711/*
712**  SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
713**			       particular objectClass
714**
715**	Parameters:
716**		lmap -- pointer to SM_LDAP_STRUCT in use
717**		entry -- current LDAP entry struct
718**		ocvalue -- particular objectclass in question.
719**			   may be of form (fee|foo|fum) meaning
720**			   any entry can be part of either fee,
721**			   foo or fum objectclass
722**
723**	Returns:
724**		true if item has that objectClass
725*/
726
727static bool
728sm_ldap_has_objectclass(lmap, entry, ocvalue)
729	SM_LDAP_STRUCT *lmap;
730	LDAPMessage *entry;
731	char *ocvalue;
732{
733	char **vals = NULL;
734	int i;
735
736	if (ocvalue == NULL)
737		return false;
738
739	vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
740	if (vals == NULL)
741		return false;
742
743	for (i = 0; vals[i] != NULL; i++)
744	{
745		char *p;
746		char *q;
747
748		p = q = ocvalue;
749		while (*p != '\0')
750		{
751			while (*p != '\0' && *p != '|')
752				p++;
753
754			if ((p - q) == strlen(vals[i]) &&
755			    sm_strncasecmp(vals[i], q, p - q) == 0)
756			{
757				ldap_value_free(vals);
758				return true;
759			}
760
761			while (*p == '|')
762				p++;
763			q = p;
764		}
765	}
766
767	ldap_value_free(vals);
768	return false;
769}
770
771/*
772**  SM_LDAP_RESULTS -- return results from an LDAP lookup in result
773**
774**	Parameters:
775**		lmap -- pointer to SM_LDAP_STRUCT in use
776**		msgid -- msgid returned by sm_ldap_search()
777**		flags -- flags for the lookup
778**		delim -- delimiter for result concatenation
779**		rpool -- memory pool for storage
780**		result -- return string
781**		recurse -- recursion list
782**
783**	Returns:
784**		status (sysexit)
785*/
786
787# define SM_LDAP_ERROR_CLEANUP()				\
788{								\
789	if (lmap->ldap_res != NULL)				\
790	{							\
791		ldap_msgfree(lmap->ldap_res);			\
792		lmap->ldap_res = NULL;				\
793	}							\
794	(void) ldap_abandon(lmap->ldap_ld, msgid);		\
795}
796
797static SM_LDAP_RECURSE_ENTRY *
798sm_ldap_add_recurse(top, item, type, rpool)
799	SM_LDAP_RECURSE_LIST **top;
800	char *item;
801	int type;
802	SM_RPOOL_T *rpool;
803{
804	int n;
805	int m;
806	int p;
807	int insertat;
808	int moveb;
809	int oldsizeb;
810	int rc;
811	SM_LDAP_RECURSE_ENTRY *newe;
812	SM_LDAP_RECURSE_ENTRY **olddata;
813
814	/*
815	**  This code will maintain a list of
816	**  SM_LDAP_RECURSE_ENTRY structures
817	**  in ascending order.
818	*/
819
820	if (*top == NULL)
821	{
822		/* Allocate an initial SM_LDAP_RECURSE_LIST struct */
823		*top = sm_rpool_malloc_x(rpool, sizeof **top);
824		(*top)->lrl_cnt = 0;
825		(*top)->lrl_size = 0;
826		(*top)->lrl_data = NULL;
827	}
828
829	if ((*top)->lrl_cnt >= (*top)->lrl_size)
830	{
831		/* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
832		olddata = (*top)->lrl_data;
833		if ((*top)->lrl_size == 0)
834		{
835			oldsizeb = 0;
836			(*top)->lrl_size = 256;
837		}
838		else
839		{
840			oldsizeb = (*top)->lrl_size * sizeof *((*top)->lrl_data);
841			(*top)->lrl_size *= 2;
842		}
843		(*top)->lrl_data = sm_rpool_malloc_x(rpool,
844						    (*top)->lrl_size * sizeof *((*top)->lrl_data));
845		if (oldsizeb > 0)
846			memcpy((*top)->lrl_data, olddata, oldsizeb);
847	}
848
849	/*
850	**  Binary search/insert item:type into list.
851	**  Return current entry pointer if already exists.
852	*/
853
854	n = 0;
855	m = (*top)->lrl_cnt - 1;
856	if (m < 0)
857		insertat = 0;
858	else
859		insertat = -1;
860
861	while (insertat == -1)
862	{
863		p = (m + n) / 2;
864
865		rc = sm_strcasecmp(item, (*top)->lrl_data[p]->lr_search);
866		if (rc == 0)
867			rc = type - (*top)->lrl_data[p]->lr_type;
868
869		if (rc < 0)
870			m = p - 1;
871		else if (rc > 0)
872			n = p + 1;
873		else
874			return (*top)->lrl_data[p];
875
876		if (m == -1)
877			insertat = 0;
878		else if (n >= (*top)->lrl_cnt)
879			insertat = (*top)->lrl_cnt;
880		else if (m < n)
881			insertat = m + 1;
882	}
883
884	/*
885	** Not found in list, make room
886	** at insert point and add it.
887	*/
888
889	newe = sm_rpool_malloc_x(rpool, sizeof *newe);
890	if (newe != NULL)
891	{
892		moveb = ((*top)->lrl_cnt - insertat) * sizeof *((*top)->lrl_data);
893		if (moveb > 0)
894			memmove(&((*top)->lrl_data[insertat + 1]),
895				&((*top)->lrl_data[insertat]),
896				moveb);
897
898		newe->lr_search = sm_rpool_strdup_x(rpool, item);
899		newe->lr_type = type;
900		newe->lr_ludp = NULL;
901		newe->lr_attrs = NULL;
902		newe->lr_done = false;
903
904		((*top)->lrl_data)[insertat] = newe;
905		(*top)->lrl_cnt++;
906	}
907	return newe;
908}
909
910int
911sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
912		resultln, resultsz, recurse)
913	SM_LDAP_STRUCT *lmap;
914	int msgid;
915	int flags;
916	int delim;
917	SM_RPOOL_T *rpool;
918	char **result;
919	int *resultln;
920	int *resultsz;
921	SM_LDAP_RECURSE_LIST *recurse;
922{
923	bool toplevel;
924	int i;
925	int statp;
926	int vsize;
927	int ret;
928	int save_errno;
929	char *p;
930	SM_LDAP_RECURSE_ENTRY *rl;
931
932	/* Are we the top top level of the search? */
933	toplevel = (recurse == NULL);
934
935	/* Get results */
936	statp = EX_NOTFOUND;
937	while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
938				  (lmap->ldap_timeout.tv_sec == 0 ? NULL :
939				   &(lmap->ldap_timeout)),
940				  &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
941	{
942		LDAPMessage *entry;
943
944		/* If we don't want multiple values and we have one, break */
945		if ((char) delim == '\0' &&
946		    !bitset(SM_LDAP_SINGLEMATCH, flags) &&
947		    *result != NULL)
948			break;
949
950		/* Cycle through all entries */
951		for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
952		     entry != NULL;
953		     entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
954		{
955			BerElement *ber;
956			char *attr;
957			char **vals = NULL;
958			char *dn;
959
960			/*
961			**  If matching only and found an entry,
962			**  no need to spin through attributes
963			*/
964
965			if (bitset(SM_LDAP_MATCHONLY, flags))
966			{
967				statp = EX_OK;
968				continue;
969			}
970
971# if _FFR_LDAP_SINGLEDN
972			if (bitset(SM_LDAP_SINGLEDN, flags) && *result != NULL)
973			{
974				/* only wanted one match */
975				SM_LDAP_ERROR_CLEANUP();
976				errno = ENOENT;
977				return EX_NOTFOUND;
978			}
979# endif /* _FFR_LDAP_SINGLEDN */
980
981			/* record completed DN's to prevent loops */
982			dn = ldap_get_dn(lmap->ldap_ld, entry);
983			if (dn == NULL)
984			{
985				save_errno = sm_ldap_geterrno(lmap->ldap_ld);
986				save_errno += E_LDAPBASE;
987				SM_LDAP_ERROR_CLEANUP();
988				errno = save_errno;
989				return EX_TEMPFAIL;
990			}
991
992			rl = sm_ldap_add_recurse(&recurse, dn,
993						 SM_LDAP_ATTR_DN,
994						 rpool);
995
996			if (rl == NULL)
997			{
998				ldap_memfree(dn);
999				SM_LDAP_ERROR_CLEANUP();
1000				errno = ENOMEM;
1001				return EX_OSERR;
1002			}
1003			else if (rl->lr_done)
1004			{
1005				/* already on list, skip it */
1006				ldap_memfree(dn);
1007				continue;
1008			}
1009			ldap_memfree(dn);
1010
1011# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
1012			/*
1013			**  Reset value to prevent lingering
1014			**  LDAP_DECODING_ERROR due to
1015			**  OpenLDAP 1.X's hack (see below)
1016			*/
1017
1018			lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
1019# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
1020
1021			for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
1022							 &ber);
1023			     attr != NULL;
1024			     attr = ldap_next_attribute(lmap->ldap_ld, entry,
1025							ber))
1026			{
1027				char *tmp, *vp_tmp;
1028				int type;
1029				char *needobjclass = NULL;
1030
1031				type = SM_LDAP_ATTR_NONE;
1032				for (i = 0; lmap->ldap_attr[i] != NULL; i++)
1033				{
1034					if (SM_STRCASEEQ(lmap->ldap_attr[i],
1035							 attr))
1036					{
1037						type = lmap->ldap_attr_type[i];
1038						needobjclass = lmap->ldap_attr_needobjclass[i];
1039						break;
1040					}
1041				}
1042
1043				if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
1044				    type == SM_LDAP_ATTR_NONE)
1045				{
1046					/* URL lookups specify attrs to use */
1047					type = SM_LDAP_ATTR_NORMAL;
1048					needobjclass = NULL;
1049				}
1050
1051				if (type == SM_LDAP_ATTR_NONE)
1052				{
1053					/* attribute not requested */
1054					ldap_memfree(attr);
1055					SM_LDAP_ERROR_CLEANUP();
1056					errno = EFAULT;
1057					return EX_SOFTWARE;
1058				}
1059
1060				/*
1061				**  For recursion on a particular attribute,
1062				**  we may need to see if this entry is
1063				**  part of a particular objectclass.
1064				**  Also, ignore objectClass attribute.
1065				**  Otherwise we just ignore this attribute.
1066				*/
1067
1068				if (type == SM_LDAP_ATTR_OBJCLASS ||
1069				    (needobjclass != NULL &&
1070				     !sm_ldap_has_objectclass(lmap, entry,
1071							      needobjclass)))
1072				{
1073					ldap_memfree(attr);
1074					continue;
1075				}
1076
1077				if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
1078				{
1079					vals = ldap_get_values(lmap->ldap_ld,
1080							       entry,
1081							       attr);
1082					if (vals == NULL)
1083					{
1084						save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1085						if (save_errno == LDAP_SUCCESS)
1086						{
1087							ldap_memfree(attr);
1088							continue;
1089						}
1090
1091						/* Must be an error */
1092						save_errno += E_LDAPBASE;
1093						ldap_memfree(attr);
1094						SM_LDAP_ERROR_CLEANUP();
1095						errno = save_errno;
1096						return EX_TEMPFAIL;
1097					}
1098				}
1099
1100				statp = EX_OK;
1101
1102# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
1103				/*
1104				**  Reset value to prevent lingering
1105				**  LDAP_DECODING_ERROR due to
1106				**  OpenLDAP 1.X's hack (see below)
1107				*/
1108
1109				lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
1110# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
1111
1112				/*
1113				**  If matching only,
1114				**  no need to spin through entries
1115				*/
1116
1117				if (bitset(SM_LDAP_MATCHONLY, flags))
1118				{
1119					if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
1120						ldap_value_free(vals);
1121					ldap_memfree(attr);
1122					continue;
1123				}
1124
1125				/*
1126				**  If we don't want multiple values,
1127				**  return first found.
1128				*/
1129
1130				if ((char) delim == '\0')
1131				{
1132					if (*result != NULL)
1133					{
1134						/* already have a value */
1135						if (bitset(SM_LDAP_SINGLEMATCH,
1136							   flags))
1137						{
1138							/* only wanted one match */
1139							SM_LDAP_ERROR_CLEANUP();
1140							errno = ENOENT;
1141							return EX_NOTFOUND;
1142						}
1143						break;
1144					}
1145
1146					if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
1147					{
1148						*result = sm_rpool_strdup_x(rpool,
1149									    attr);
1150						ldap_memfree(attr);
1151						break;
1152					}
1153
1154					if (vals[0] == NULL)
1155					{
1156						ldap_value_free(vals);
1157						ldap_memfree(attr);
1158						continue;
1159					}
1160
1161					vsize = strlen(vals[0]) + 1;
1162					if (lmap->ldap_attrsep != '\0')
1163						vsize += strlen(attr) + 1;
1164					*result = sm_rpool_malloc_x(rpool,
1165								    vsize);
1166					if (lmap->ldap_attrsep != '\0')
1167						sm_snprintf(*result, vsize,
1168							    "%s%c%s",
1169							    attr,
1170							    lmap->ldap_attrsep,
1171							    vals[0]);
1172					else
1173						sm_strlcpy(*result, vals[0],
1174							   vsize);
1175					ldap_value_free(vals);
1176					ldap_memfree(attr);
1177					break;
1178				}
1179
1180				/* attributes only */
1181				if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
1182				{
1183					if (*result == NULL)
1184						*result = sm_rpool_strdup_x(rpool,
1185									    attr);
1186					else
1187					{
1188						if (bitset(SM_LDAP_SINGLEMATCH,
1189							   flags) &&
1190						    *result != NULL)
1191						{
1192							/* only wanted one match */
1193							SM_LDAP_ERROR_CLEANUP();
1194							errno = ENOENT;
1195							return EX_NOTFOUND;
1196						}
1197
1198						vsize = strlen(*result) +
1199							strlen(attr) + 2;
1200						tmp = sm_rpool_malloc_x(rpool,
1201									vsize);
1202						(void) sm_snprintf(tmp,
1203							vsize, "%s%c%s",
1204							*result, (char) delim,
1205							attr);
1206						*result = tmp;
1207					}
1208					ldap_memfree(attr);
1209					continue;
1210				}
1211
1212				/*
1213				**  If there is more than one, munge then
1214				**  into a map_coldelim separated string.
1215				**  If we are recursing we may have an entry
1216				**  with no 'normal' values to put in the
1217				**  string.
1218				**  This is not an error.
1219				*/
1220
1221				if (type == SM_LDAP_ATTR_NORMAL &&
1222				    bitset(SM_LDAP_SINGLEMATCH, flags) &&
1223				    *result != NULL)
1224				{
1225					/* only wanted one match */
1226					SM_LDAP_ERROR_CLEANUP();
1227					errno = ENOENT;
1228					return EX_NOTFOUND;
1229				}
1230
1231				vsize = 0;
1232				for (i = 0; vals[i] != NULL; i++)
1233				{
1234					if (type == SM_LDAP_ATTR_DN ||
1235					    type == SM_LDAP_ATTR_FILTER ||
1236					    type == SM_LDAP_ATTR_URL)
1237					{
1238						/* add to recursion */
1239						if (sm_ldap_add_recurse(&recurse,
1240									vals[i],
1241									type,
1242									rpool) == NULL)
1243						{
1244							SM_LDAP_ERROR_CLEANUP();
1245							errno = ENOMEM;
1246							return EX_OSERR;
1247						}
1248						continue;
1249					}
1250
1251					vsize += strlen(vals[i]) + 1;
1252					if (lmap->ldap_attrsep != '\0')
1253						vsize += strlen(attr) + 1;
1254				}
1255
1256				/*
1257				**  Create/Append to string any normal
1258				**  attribute values.  Otherwise, just free
1259				**  memory and move on to the next
1260				**  attribute in this entry.
1261				*/
1262
1263				if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
1264				{
1265					char *pe;
1266
1267					/* Grow result string if needed */
1268					if ((*resultln + vsize) >= *resultsz)
1269					{
1270						while ((*resultln + vsize) >= *resultsz)
1271						{
1272							if (*resultsz == 0)
1273								*resultsz = 1024;
1274							else
1275								*resultsz *= 2;
1276						}
1277
1278						vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
1279						*vp_tmp = '\0';
1280
1281						if (*result != NULL)
1282							sm_strlcpy(vp_tmp,
1283								   *result,
1284								   *resultsz);
1285						*result = vp_tmp;
1286					}
1287
1288					p = *result + *resultln;
1289					pe = *result + *resultsz;
1290
1291					for (i = 0; vals[i] != NULL; i++)
1292					{
1293						if (*resultln > 0 &&
1294						    p < pe)
1295							*p++ = (char) delim;
1296
1297						if (lmap->ldap_attrsep != '\0')
1298						{
1299							p += sm_strlcpy(p, attr,
1300									pe - p);
1301							if (p < pe)
1302								*p++ = lmap->ldap_attrsep;
1303						}
1304
1305						p += sm_strlcpy(p, vals[i],
1306								pe - p);
1307						*resultln = p - (*result);
1308						if (p >= pe)
1309						{
1310							/* Internal error: buffer too small for LDAP values */
1311							SM_LDAP_ERROR_CLEANUP();
1312							errno = ENOMEM;
1313							return EX_OSERR;
1314						}
1315					}
1316				}
1317
1318				ldap_value_free(vals);
1319				ldap_memfree(attr);
1320			}
1321			save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1322
1323			/*
1324			**  We check save_errno != LDAP_DECODING_ERROR since
1325			**  OpenLDAP 1.X has a very ugly *undocumented*
1326			**  hack of returning this error code from
1327			**  ldap_next_attribute() if the library freed the
1328			**  ber attribute.  See:
1329			**  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
1330			*/
1331
1332			if (save_errno != LDAP_SUCCESS &&
1333			    save_errno != LDAP_DECODING_ERROR)
1334			{
1335				/* Must be an error */
1336				save_errno += E_LDAPBASE;
1337				SM_LDAP_ERROR_CLEANUP();
1338				errno = save_errno;
1339				return EX_TEMPFAIL;
1340			}
1341
1342			/* mark this DN as done */
1343			rl->lr_done = true;
1344			if (rl->lr_ludp != NULL)
1345			{
1346				ldap_free_urldesc(rl->lr_ludp);
1347				rl->lr_ludp = NULL;
1348			}
1349			if (rl->lr_attrs != NULL)
1350			{
1351				free(rl->lr_attrs);
1352				rl->lr_attrs = NULL;
1353			}
1354
1355			/* We don't want multiple values and we have one */
1356			if ((char) delim == '\0' &&
1357			    !bitset(SM_LDAP_SINGLEMATCH, flags) &&
1358			    *result != NULL)
1359				break;
1360		}
1361		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1362		if (save_errno != LDAP_SUCCESS &&
1363		    save_errno != LDAP_DECODING_ERROR)
1364		{
1365			/* Must be an error */
1366			save_errno += E_LDAPBASE;
1367			SM_LDAP_ERROR_CLEANUP();
1368			errno = save_errno;
1369			return EX_TEMPFAIL;
1370		}
1371		ldap_msgfree(lmap->ldap_res);
1372		lmap->ldap_res = NULL;
1373	}
1374
1375	if (ret == 0)
1376		save_errno = ETIMEDOUT;
1377	else if (ret == LDAP_RES_SEARCH_RESULT)
1378	{
1379		/*
1380		**  We may have gotten an LDAP_RES_SEARCH_RESULT response
1381		**  with an error inside it, so we have to extract that
1382		**  with ldap_parse_result().  This can happen when talking
1383		**  to an LDAP proxy whose backend has gone down.
1384		*/
1385
1386		if (lmap->ldap_res == NULL)
1387			save_errno = LDAP_UNAVAILABLE;
1388		else
1389		{
1390			int rc;
1391
1392			save_errno = ldap_parse_result(lmap->ldap_ld,
1393					lmap->ldap_res, &rc, NULL, NULL,
1394					NULL, NULL, 0);
1395			if (save_errno == LDAP_SUCCESS)
1396				save_errno = rc;
1397		}
1398	}
1399	else
1400		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1401	if (save_errno != LDAP_SUCCESS)
1402	{
1403		statp = EX_TEMPFAIL;
1404		switch (save_errno)
1405		{
1406# ifdef LDAP_SERVER_DOWN
1407		  case LDAP_SERVER_DOWN:
1408# endif
1409		  case LDAP_TIMEOUT:
1410		  case ETIMEDOUT:
1411		  case LDAP_UNAVAILABLE:
1412
1413			/*
1414			**  server disappeared,
1415			**  try reopen on next search
1416			*/
1417
1418			statp = EX_RESTART;
1419			break;
1420		}
1421		if (ret != 0)
1422			save_errno += E_LDAPBASE;
1423		SM_LDAP_ERROR_CLEANUP();
1424		errno = save_errno;
1425		return statp;
1426	}
1427
1428	if (lmap->ldap_res != NULL)
1429	{
1430		ldap_msgfree(lmap->ldap_res);
1431		lmap->ldap_res = NULL;
1432	}
1433
1434	if (toplevel)
1435	{
1436		int rlidx;
1437
1438		/*
1439		**  Spin through the built-up recurse list at the top
1440		**  of the recursion.  Since new items are added at the
1441		**  end of the shared list, we actually only ever get
1442		**  one level of recursion before things pop back to the
1443		**  top.  Any items added to the list during that recursion
1444		**  will be expanded by the top level.
1445		*/
1446
1447		for (rlidx = 0; recurse != NULL && rlidx < recurse->lrl_cnt;
1448		     rlidx++)
1449		{
1450			int newflags;
1451			int sid;
1452			int status;
1453
1454			rl = recurse->lrl_data[rlidx];
1455
1456			newflags = flags;
1457			if (rl->lr_done)
1458			{
1459				/* already expanded */
1460				continue;
1461			}
1462
1463			if (rl->lr_type == SM_LDAP_ATTR_DN)
1464			{
1465				/* do DN search */
1466				sid = ldap_search(lmap->ldap_ld,
1467						  rl->lr_search,
1468						  lmap->ldap_scope,
1469						  "(objectClass=*)",
1470						  (lmap->ldap_attr[0] == NULL ?
1471						   NULL : lmap->ldap_attr),
1472						  lmap->ldap_attrsonly);
1473			}
1474			else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
1475			{
1476				/* do new search */
1477				sid = ldap_search(lmap->ldap_ld,
1478						  lmap->ldap_base,
1479						  lmap->ldap_scope,
1480						  rl->lr_search,
1481						  (lmap->ldap_attr[0] == NULL ?
1482						   NULL : lmap->ldap_attr),
1483						  lmap->ldap_attrsonly);
1484			}
1485			else if (rl->lr_type == SM_LDAP_ATTR_URL)
1486			{
1487				/* Parse URL */
1488				sid = ldap_url_parse(rl->lr_search,
1489						     &rl->lr_ludp);
1490
1491				if (sid != 0)
1492				{
1493					errno = sid + E_LDAPURLBASE;
1494					return EX_TEMPFAIL;
1495				}
1496
1497				/* We need to add objectClass */
1498				if (rl->lr_ludp->lud_attrs != NULL)
1499				{
1500					int attrnum = 0;
1501
1502					while (rl->lr_ludp->lud_attrs[attrnum] != NULL)
1503					{
1504						if (strcasecmp(rl->lr_ludp->lud_attrs[attrnum],
1505							       "objectClass") == 0)
1506						{
1507							/* already requested */
1508							attrnum = -1;
1509							break;
1510						}
1511						attrnum++;
1512					}
1513
1514					if (attrnum >= 0)
1515					{
1516						int i;
1517
1518						rl->lr_attrs = (char **)malloc(sizeof(char *) * (attrnum + 2));
1519						if (rl->lr_attrs == NULL)
1520						{
1521							save_errno = errno;
1522							ldap_free_urldesc(rl->lr_ludp);
1523							errno = save_errno;
1524							return EX_TEMPFAIL;
1525						}
1526						for (i = 0 ; i < attrnum; i++)
1527						{
1528							rl->lr_attrs[i] = rl->lr_ludp->lud_attrs[i];
1529						}
1530						rl->lr_attrs[i++] = "objectClass";
1531						rl->lr_attrs[i++] = NULL;
1532					}
1533				}
1534
1535				/*
1536				**  Use the existing connection
1537				**  for this search.  It really
1538				**  should use lud_scheme://lud_host:lud_port/
1539				**  instead but that would require
1540				**  opening a new connection.
1541				**  This should be fixed ASAP.
1542				*/
1543
1544				sid = ldap_search(lmap->ldap_ld,
1545						  rl->lr_ludp->lud_dn,
1546						  rl->lr_ludp->lud_scope,
1547						  rl->lr_ludp->lud_filter,
1548						  rl->lr_attrs,
1549						  lmap->ldap_attrsonly);
1550
1551				/* Use the attributes specified by URL */
1552				newflags |= SM_LDAP_USE_ALLATTR;
1553			}
1554			else
1555			{
1556				/* unknown or illegal attribute type */
1557				errno = EFAULT;
1558				return EX_SOFTWARE;
1559			}
1560
1561			/* Collect results */
1562			if (sid == -1)
1563			{
1564				save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1565				statp = EX_TEMPFAIL;
1566				switch (save_errno)
1567				{
1568# ifdef LDAP_SERVER_DOWN
1569				  case LDAP_SERVER_DOWN:
1570# endif
1571				  case LDAP_TIMEOUT:
1572				  case ETIMEDOUT:
1573				  case LDAP_UNAVAILABLE:
1574
1575					/*
1576					**  server disappeared,
1577					**  try reopen on next search
1578					*/
1579
1580					statp = EX_RESTART;
1581					break;
1582				}
1583				errno = save_errno + E_LDAPBASE;
1584				return statp;
1585			}
1586
1587			status = sm_ldap_results(lmap, sid, newflags, delim,
1588						 rpool, result, resultln,
1589						 resultsz, recurse);
1590			save_errno = errno;
1591			if (status != EX_OK && status != EX_NOTFOUND)
1592			{
1593				errno = save_errno;
1594				return status;
1595			}
1596
1597			/* Mark as done */
1598			rl->lr_done = true;
1599			if (rl->lr_ludp != NULL)
1600			{
1601				ldap_free_urldesc(rl->lr_ludp);
1602				rl->lr_ludp = NULL;
1603			}
1604			if (rl->lr_attrs != NULL)
1605			{
1606				free(rl->lr_attrs);
1607				rl->lr_attrs = NULL;
1608			}
1609
1610			/* Reset rlidx as new items may have been added */
1611			rlidx = -1;
1612		}
1613	}
1614	return statp;
1615}
1616
1617/*
1618**  SM_LDAP_CLOSE -- close LDAP connection
1619**
1620**	Parameters:
1621**		lmap -- LDAP map information
1622**
1623**	Returns:
1624**		None.
1625*/
1626
1627void
1628sm_ldap_close(lmap)
1629	SM_LDAP_STRUCT *lmap;
1630{
1631	if (lmap->ldap_ld == NULL)
1632		return;
1633
1634	if (lmap->ldap_pid == getpid())
1635		ldap_unbind(lmap->ldap_ld);
1636	lmap->ldap_ld = NULL;
1637	lmap->ldap_pid = 0;
1638}
1639
1640/*
1641**  SM_LDAP_GETERRNO -- get ldap errno value
1642**
1643**	Parameters:
1644**		ld -- LDAP session handle
1645**
1646**	Returns:
1647**		LDAP errno.
1648*/
1649
1650int
1651sm_ldap_geterrno(ld)
1652	LDAP *ld;
1653{
1654	int err = LDAP_SUCCESS;
1655
1656# if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
1657#  ifdef LDAP_OPT_RESULT_CODE
1658#   define LDAP_GET_RESULT_CODE LDAP_OPT_RESULT_CODE
1659#  else
1660#   define LDAP_GET_RESULT_CODE LDAP_OPT_ERROR_NUMBER
1661#  endif
1662	(void) ldap_get_option(ld, LDAP_GET_RESULT_CODE, &err);
1663# else
1664#  ifdef LDAP_OPT_SIZELIMIT
1665	err = ldap_get_lderrno(ld, NULL, NULL);
1666#  else
1667	err = ld->ld_errno;
1668
1669	/*
1670	**  Reset value to prevent lingering LDAP_DECODING_ERROR due to
1671	**  OpenLDAP 1.X's hack (see above)
1672	*/
1673
1674	ld->ld_errno = LDAP_SUCCESS;
1675#  endif /* LDAP_OPT_SIZELIMIT */
1676# endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1677	return err;
1678}
1679
1680/*
1681**  SM_LDAP_GETERROR -- get ldap error value
1682**
1683**	Parameters:
1684**		ld -- LDAP session handle
1685**
1686**	Returns:
1687**		LDAP error
1688*/
1689
1690static char *
1691sm_ldap_geterror(ld)
1692	LDAP *ld;
1693{
1694	char *error = NULL;
1695
1696# if defined(LDAP_OPT_DIAGNOSTIC_MESSAGE)
1697	(void) ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &error);
1698# endif
1699	return error;
1700}
1701
1702
1703#endif /* LDAPMAP */
1704