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