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