xref: /freebsd/contrib/sendmail/libsm/ldap.c (revision d2387d42b8da231a5b95cbc313825fb2aadf26f6)
1 /*
2  * Copyright (c) 2001-2003 Sendmail, 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 #include <sm/gen.h>
11 SM_RCSID("@(#)$Id: ldap.c,v 1.44.2.5 2003/12/23 21:21:56 gshapiro Exp $")
12 
13 #if LDAPMAP
14 # include <sys/types.h>
15 # include <errno.h>
16 # include <setjmp.h>
17 # include <stdlib.h>
18 # include <unistd.h>
19 
20 # include <sm/bitops.h>
21 # include <sm/clock.h>
22 # include <sm/conf.h>
23 # include <sm/debug.h>
24 # include <sm/errstring.h>
25 # include <sm/ldap.h>
26 # include <sm/string.h>
27 #  ifdef EX_OK
28 #   undef EX_OK			/* for SVr4.2 SMP */
29 #  endif /* EX_OK */
30 # include <sm/sysexits.h>
31 
32 SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
33 	"@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
34 
35 static void	ldaptimeout __P((int));
36 
37 /*
38 **  SM_LDAP_CLEAR -- set default values for SM_LDAP_STRUCT
39 **
40 **	Parameters:
41 **		lmap -- pointer to SM_LDAP_STRUCT to clear
42 **
43 **	Returns:
44 **		None.
45 **
46 */
47 
48 void
49 sm_ldap_clear(lmap)
50 	SM_LDAP_STRUCT *lmap;
51 {
52 	if (lmap == NULL)
53 		return;
54 
55 	lmap->ldap_target = NULL;
56 	lmap->ldap_port = LDAP_PORT;
57 #if _FFR_LDAP_URI
58 	lmap->ldap_uri = false;
59 #endif /* _FFR_LDAP_URI */
60 #  if _FFR_LDAP_SETVERSION
61 	lmap->ldap_version = 0;
62 #  endif /* _FFR_LDAP_SETVERSION */
63 	lmap->ldap_deref = LDAP_DEREF_NEVER;
64 	lmap->ldap_timelimit = LDAP_NO_LIMIT;
65 	lmap->ldap_sizelimit = LDAP_NO_LIMIT;
66 # ifdef LDAP_REFERRALS
67 	lmap->ldap_options = LDAP_OPT_REFERRALS;
68 # else /* LDAP_REFERRALS */
69 	lmap->ldap_options = 0;
70 # endif /* LDAP_REFERRALS */
71 	lmap->ldap_attrsep = '\0';
72 	lmap->ldap_binddn = NULL;
73 	lmap->ldap_secret = NULL;
74 	lmap->ldap_method = LDAP_AUTH_SIMPLE;
75 	lmap->ldap_base = NULL;
76 	lmap->ldap_scope = LDAP_SCOPE_SUBTREE;
77 	lmap->ldap_attrsonly = LDAPMAP_FALSE;
78 	lmap->ldap_timeout.tv_sec = 0;
79 	lmap->ldap_timeout.tv_usec = 0;
80 	lmap->ldap_ld = NULL;
81 	lmap->ldap_filter = NULL;
82 	lmap->ldap_attr[0] = NULL;
83 #if _FFR_LDAP_RECURSION
84 	lmap->ldap_attr_type[0] = SM_LDAP_ATTR_NONE;
85 	lmap->ldap_attr_needobjclass[0] = NULL;
86 #endif /* _FFR_LDAP_RECURSION */
87 	lmap->ldap_res = NULL;
88 	lmap->ldap_next = NULL;
89 	lmap->ldap_pid = 0;
90 }
91 
92 /*
93 **  SM_LDAP_START -- actually connect to an LDAP server
94 **
95 **	Parameters:
96 **		name -- name of map for debug output.
97 **		lmap -- the LDAP map being opened.
98 **
99 **	Returns:
100 **		true if connection is successful, false otherwise.
101 **
102 **	Side Effects:
103 **		Populates lmap->ldap_ld.
104 */
105 
106 static jmp_buf	LDAPTimeout;
107 
108 #define SM_LDAP_SETTIMEOUT(to)						\
109 do									\
110 {									\
111 	if (to != 0)							\
112 	{								\
113 		if (setjmp(LDAPTimeout) != 0)				\
114 		{							\
115 			errno = ETIMEDOUT;				\
116 			return false;					\
117 		}							\
118 		ev = sm_setevent(to, ldaptimeout, 0);			\
119 	}								\
120 } while (0)
121 
122 #define SM_LDAP_CLEARTIMEOUT()						\
123 do									\
124 {									\
125 	if (ev != NULL)							\
126 		sm_clrevent(ev);					\
127 } while (0)
128 
129 bool
130 sm_ldap_start(name, lmap)
131 	char *name;
132 	SM_LDAP_STRUCT *lmap;
133 {
134 	int bind_result;
135 	int save_errno;
136 	SM_EVENT *ev = NULL;
137 	LDAP *ld;
138 
139 	if (sm_debug_active(&SmLDAPTrace, 2))
140 		sm_dprintf("ldapmap_start(%s)\n", name == NULL ? "" : name);
141 
142 	if (sm_debug_active(&SmLDAPTrace, 9))
143 		sm_dprintf("ldapmap_start(%s, %d)\n",
144 			   lmap->ldap_target == NULL ? "localhost" : lmap->ldap_target,
145 			   lmap->ldap_port);
146 
147 # if USE_LDAP_INIT
148 #  if _FFR_LDAP_URI
149 	if (lmap->ldap_uri)
150 		errno = ldap_initialize(&ld, lmap->ldap_target);
151 	else
152 #  endif /* _FFR_LDAP_URI */
153 		ld = ldap_init(lmap->ldap_target, lmap->ldap_port);
154 	save_errno = errno;
155 # else /* USE_LDAP_INIT */
156 	/*
157 	**  If using ldap_open(), the actual connection to the server
158 	**  happens now so we need the timeout here.  For ldap_init(),
159 	**  the connection happens at bind time.
160 	*/
161 
162 	SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
163 	ld = ldap_open(lmap->ldap_target, lmap->ldap_port);
164 	save_errno = errno;
165 
166 	/* clear the event if it has not sprung */
167 	SM_LDAP_CLEARTIMEOUT();
168 # endif /* USE_LDAP_INIT */
169 
170 	errno = save_errno;
171 	if (ld == NULL)
172 		return false;
173 
174 	sm_ldap_setopts(ld, lmap);
175 
176 # if USE_LDAP_INIT
177 	/*
178 	**  If using ldap_init(), the actual connection to the server
179 	**  happens at ldap_bind_s() so we need the timeout here.
180 	*/
181 
182 	SM_LDAP_SETTIMEOUT(lmap->ldap_timeout.tv_sec);
183 # endif /* USE_LDAP_INIT */
184 
185 # ifdef LDAP_AUTH_KRBV4
186 	if (lmap->ldap_method == LDAP_AUTH_KRBV4 &&
187 	    lmap->ldap_secret != NULL)
188 	{
189 		/*
190 		**  Need to put ticket in environment here instead of
191 		**  during parseargs as there may be different tickets
192 		**  for different LDAP connections.
193 		*/
194 
195 		(void) putenv(lmap->ldap_secret);
196 	}
197 # endif /* LDAP_AUTH_KRBV4 */
198 
199 	bind_result = ldap_bind_s(ld, lmap->ldap_binddn,
200 				  lmap->ldap_secret, lmap->ldap_method);
201 
202 # if USE_LDAP_INIT
203 	/* clear the event if it has not sprung */
204 	SM_LDAP_CLEARTIMEOUT();
205 # endif /* USE_LDAP_INIT */
206 
207 	if (bind_result != LDAP_SUCCESS)
208 	{
209 		errno = bind_result + E_LDAPBASE;
210 		return false;
211 	}
212 
213 	/* Save PID to make sure only this PID closes the LDAP connection */
214 	lmap->ldap_pid = getpid();
215 	lmap->ldap_ld = ld;
216 	return true;
217 }
218 
219 /* ARGSUSED */
220 static void
221 ldaptimeout(unused)
222 	int unused;
223 {
224 	/*
225 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
226 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
227 	**	DOING.
228 	*/
229 
230 	errno = ETIMEDOUT;
231 	longjmp(LDAPTimeout, 1);
232 }
233 
234 /*
235 **  SM_LDAP_SEARCH -- iniate LDAP search
236 **
237 **	Initiate an LDAP search, return the msgid.
238 **	The calling function must collect the results.
239 **
240 **	Parameters:
241 **		lmap -- LDAP map information
242 **		key -- key to substitute in LDAP filter
243 **
244 **	Returns:
245 **		-1 on failure, msgid on success
246 **
247 */
248 
249 int
250 sm_ldap_search(lmap, key)
251 	SM_LDAP_STRUCT *lmap;
252 	char *key;
253 {
254 	int msgid;
255 	char *fp, *p, *q;
256 	char filter[LDAPMAP_MAX_FILTER + 1];
257 
258 	/* substitute key into filter, perhaps multiple times */
259 	memset(filter, '\0', sizeof filter);
260 	fp = filter;
261 	p = lmap->ldap_filter;
262 	while ((q = strchr(p, '%')) != NULL)
263 	{
264 		if (q[1] == 's')
265 		{
266 			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
267 					   "%.*s%s", (int) (q - p), p, key);
268 			fp += strlen(fp);
269 			p = q + 2;
270 		}
271 		else if (q[1] == '0')
272 		{
273 			char *k = key;
274 
275 			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
276 					   "%.*s", (int) (q - p), p);
277 			fp += strlen(fp);
278 			p = q + 2;
279 
280 			/* Properly escape LDAP special characters */
281 			while (SPACELEFT(filter, fp) > 0 &&
282 			       *k != '\0')
283 			{
284 				if (*k == '*' || *k == '(' ||
285 				    *k == ')' || *k == '\\')
286 				{
287 					(void) sm_strlcat(fp,
288 						       (*k == '*' ? "\\2A" :
289 							(*k == '(' ? "\\28" :
290 							 (*k == ')' ? "\\29" :
291 							  (*k == '\\' ? "\\5C" :
292 							   "\00")))),
293 						SPACELEFT(filter, fp));
294 					fp += strlen(fp);
295 					k++;
296 				}
297 				else
298 					*fp++ = *k++;
299 			}
300 		}
301 		else
302 		{
303 			(void) sm_snprintf(fp, SPACELEFT(filter, fp),
304 				"%.*s", (int) (q - p + 1), p);
305 			p = q + (q[1] == '%' ? 2 : 1);
306 			fp += strlen(fp);
307 		}
308 	}
309 	(void) sm_strlcpy(fp, p, SPACELEFT(filter, fp));
310 	if (sm_debug_active(&SmLDAPTrace, 20))
311 		sm_dprintf("ldap search filter=%s\n", filter);
312 
313 	lmap->ldap_res = NULL;
314 	msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base,
315 			    lmap->ldap_scope, filter,
316 			    (lmap->ldap_attr[0] == NULL ? NULL :
317 			     lmap->ldap_attr),
318 			    lmap->ldap_attrsonly);
319 	return msgid;
320 }
321 
322 # if _FFR_LDAP_RECURSION
323 /*
324 **  SM_LDAP_HAS_OBJECTCLASS -- determine if an LDAP entry is part of a
325 **			       particular objectClass
326 **
327 **	Parameters:
328 **		lmap -- pointer to SM_LDAP_STRUCT in use
329 **		entry -- current LDAP entry struct
330 **		ocvalue -- particular objectclass in question.
331 **			   may be of form (fee|foo|fum) meaning
332 **			   any entry can be part of either fee,
333 **			   foo or fum objectclass
334 **
335 **	Returns:
336 **		true if item has that objectClass
337 */
338 
339 static bool
340 sm_ldap_has_objectclass(lmap, entry, ocvalue)
341 	SM_LDAP_STRUCT *lmap;
342 	LDAPMessage *entry;
343 	char *ocvalue;
344 {
345 	char **vals = NULL;
346 	int i;
347 
348 	if (ocvalue == NULL)
349 		return false;
350 
351 	vals = ldap_get_values(lmap->ldap_ld, entry, "objectClass");
352 	if (vals == NULL)
353 		return false;
354 
355 	for (i = 0; vals[i] != NULL; i++)
356 	{
357 		char *p;
358 		char *q;
359 
360 		p = q = ocvalue;
361 		while (*p != '\0')
362 		{
363 			while (*p != '\0' && *p != '|')
364 				p++;
365 
366 			if ((p - q) == strlen(vals[i]) &&
367 			    sm_strncasecmp(vals[i], q, p - q) == 0)
368 			{
369 				ldap_value_free(vals);
370 				return true;
371 			}
372 
373 			while (*p == '|')
374 				p++;
375 			q = p;
376 		}
377 	}
378 
379 	ldap_value_free(vals);
380 	return false;
381 }
382 
383 /*
384 **  SM_LDAP_RESULTS -- return results from an LDAP lookup in result
385 **
386 **	Parameters:
387 **		lmap -- pointer to SM_LDAP_STRUCT in use
388 **		msgid -- msgid returned by sm_ldap_search()
389 **		flags -- flags for the lookup
390 **		delim -- delimiter for result concatenation
391 **		rpool -- memory pool for storage
392 **		result -- return string
393 **		recurse -- recursion list
394 **
395 **	Returns:
396 **		status (sysexit)
397 */
398 
399 # define SM_LDAP_ERROR_CLEANUP()				\
400 {								\
401 	if (lmap->ldap_res != NULL)				\
402 	{							\
403 		ldap_msgfree(lmap->ldap_res);			\
404 		lmap->ldap_res = NULL;				\
405 	}							\
406 	(void) ldap_abandon(lmap->ldap_ld, msgid);		\
407 }
408 
409 static SM_LDAP_RECURSE_ENTRY *
410 sm_ldap_add_recurse(top, item, type, rpool)
411 	SM_LDAP_RECURSE_LIST **top;
412 	char *item;
413 	int type;
414 	SM_RPOOL_T *rpool;
415 {
416 	int n;
417 	int m;
418 	int p;
419 	int insertat;
420 	int moveb;
421 	int oldsizeb;
422 	int rc;
423 	SM_LDAP_RECURSE_ENTRY *newe;
424 	SM_LDAP_RECURSE_ENTRY **olddata;
425 
426 	/*
427 	**  This code will maintain a list of
428 	**  SM_LDAP_RECURSE_ENTRY structures
429 	**  in ascending order.
430 	*/
431 
432 	if (*top == NULL)
433 	{
434 		/* Allocate an initial SM_LDAP_RECURSE_LIST struct */
435 		*top = sm_rpool_malloc_x(rpool, sizeof **top);
436 		(*top)->lr_cnt = 0;
437 		(*top)->lr_size = 0;
438 		(*top)->lr_data = NULL;
439 	}
440 
441 	if ((*top)->lr_cnt >= (*top)->lr_size)
442 	{
443 		/* Grow the list of SM_LDAP_RECURSE_ENTRY ptrs */
444 		olddata = (*top)->lr_data;
445 		if ((*top)->lr_size == 0)
446 		{
447 			oldsizeb = 0;
448 			(*top)->lr_size = 256;
449 		}
450 		else
451 		{
452 			oldsizeb = (*top)->lr_size * sizeof *((*top)->lr_data);
453 			(*top)->lr_size *= 2;
454 		}
455 		(*top)->lr_data = sm_rpool_malloc_x(rpool,
456 						    (*top)->lr_size * sizeof *((*top)->lr_data));
457 		if (oldsizeb > 0)
458 			memcpy((*top)->lr_data, olddata, oldsizeb);
459 	}
460 
461 	/*
462 	**  Binary search/insert item:type into list.
463 	**  Return current entry pointer if already exists.
464 	*/
465 
466 	n = 0;
467 	m = (*top)->lr_cnt - 1;
468 	if (m < 0)
469 		insertat = 0;
470 	else
471 		insertat = -1;
472 
473 	while (insertat == -1)
474 	{
475 		p = (m + n) / 2;
476 
477 		rc = sm_strcasecmp(item, (*top)->lr_data[p]->lr_search);
478 		if (rc == 0)
479 			rc = type - (*top)->lr_data[p]->lr_type;
480 
481 		if (rc < 0)
482 			m = p - 1;
483 		else if (rc > 0)
484 			n = p + 1;
485 		else
486 			return (*top)->lr_data[p];
487 
488 		if (m == -1)
489 			insertat = 0;
490 		else if (n >= (*top)->lr_cnt)
491 			insertat = (*top)->lr_cnt;
492 		else if (m < n)
493 			insertat = m + 1;
494 	}
495 
496 	/*
497 	** Not found in list, make room
498 	** at insert point and add it.
499 	*/
500 
501 	newe = sm_rpool_malloc_x(rpool, sizeof *newe);
502 	if (newe != NULL)
503 	{
504 		moveb = ((*top)->lr_cnt - insertat) * sizeof *((*top)->lr_data);
505 		if (moveb > 0)
506 			memmove(&((*top)->lr_data[insertat + 1]),
507 				&((*top)->lr_data[insertat]),
508 				moveb);
509 
510 		newe->lr_search = sm_rpool_strdup_x(rpool, item);
511 		newe->lr_type = type;
512 		newe->lr_done = false;
513 
514 		((*top)->lr_data)[insertat] = newe;
515 		(*top)->lr_cnt++;
516 	}
517 	return newe;
518 }
519 
520 int
521 sm_ldap_results(lmap, msgid, flags, delim, rpool, result,
522 		resultln, resultsz, recurse)
523 	SM_LDAP_STRUCT *lmap;
524 	int msgid;
525 	int flags;
526 	int delim;
527 	SM_RPOOL_T *rpool;
528 	char **result;
529 	int *resultln;
530 	int *resultsz;
531 	SM_LDAP_RECURSE_LIST *recurse;
532 {
533 	bool toplevel;
534 	int i;
535 	int statp;
536 	int vsize;
537 	int ret;
538 	int save_errno;
539 	char *p;
540 	SM_LDAP_RECURSE_ENTRY *rl;
541 
542 	/* Are we the top top level of the search? */
543 	toplevel = (recurse == NULL);
544 
545 	/* Get results */
546 	statp = EX_NOTFOUND;
547 	while ((ret = ldap_result(lmap->ldap_ld, msgid, 0,
548 				  (lmap->ldap_timeout.tv_sec == 0 ? NULL :
549 				   &(lmap->ldap_timeout)),
550 				  &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY)
551 	{
552 		LDAPMessage *entry;
553 
554 		/* If we don't want multiple values and we have one, break */
555 		if ((char) delim == '\0' && *result != NULL)
556 			break;
557 
558 		/* Cycle through all entries */
559 		for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res);
560 		     entry != NULL;
561 		     entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res))
562 		{
563 			BerElement *ber;
564 			char *attr;
565 			char **vals = NULL;
566 			char *dn;
567 
568 			/*
569 			**  If matching only and found an entry,
570 			**  no need to spin through attributes
571 			*/
572 
573 			if (bitset(SM_LDAP_MATCHONLY, flags))
574 			{
575 				statp = EX_OK;
576 				continue;
577 			}
578 
579 			/* record completed DN's to prevent loops */
580 			dn = ldap_get_dn(lmap->ldap_ld, entry);
581 			if (dn == NULL)
582 			{
583 				save_errno = sm_ldap_geterrno(lmap->ldap_ld);
584 				save_errno += E_LDAPBASE;
585 				SM_LDAP_ERROR_CLEANUP();
586 				errno = save_errno;
587 				return EX_TEMPFAIL;
588 			}
589 
590 			rl = sm_ldap_add_recurse(&recurse, dn,
591 						 SM_LDAP_ATTR_DN,
592 						 rpool);
593 
594 			if (rl == NULL)
595 			{
596 				ldap_memfree(dn);
597 				SM_LDAP_ERROR_CLEANUP();
598 				errno = ENOMEM;
599 				return EX_OSERR;
600 			}
601 			else if (rl->lr_done)
602 			{
603 				/* already on list, skip it */
604 				ldap_memfree(dn);
605 				continue;
606 			}
607 			ldap_memfree(dn);
608 
609 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
610 			/*
611 			**  Reset value to prevent lingering
612 			**  LDAP_DECODING_ERROR due to
613 			**  OpenLDAP 1.X's hack (see below)
614 			*/
615 
616 			lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
617 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
618 
619 			for (attr = ldap_first_attribute(lmap->ldap_ld, entry,
620 							 &ber);
621 			     attr != NULL;
622 			     attr = ldap_next_attribute(lmap->ldap_ld, entry,
623 							ber))
624 			{
625 				char *tmp, *vp_tmp;
626 				int type;
627 				char *needobjclass = NULL;
628 
629 				type = SM_LDAP_ATTR_NONE;
630 				for (i = 0; lmap->ldap_attr[i] != NULL; i++)
631 				{
632 					if (sm_strcasecmp(lmap->ldap_attr[i],
633 							  attr) == 0)
634 					{
635 						type = lmap->ldap_attr_type[i];
636 						needobjclass = lmap->ldap_attr_needobjclass[i];
637 						break;
638 					}
639 				}
640 
641 				if (bitset(SM_LDAP_USE_ALLATTR, flags) &&
642 				    type == SM_LDAP_ATTR_NONE)
643 				{
644 					/* URL lookups specify attrs to use */
645 					type = SM_LDAP_ATTR_NORMAL;
646 					needobjclass = NULL;
647 				}
648 
649 				if (type == SM_LDAP_ATTR_NONE)
650 				{
651 					/* attribute not requested */
652 					ldap_memfree(attr);
653 					SM_LDAP_ERROR_CLEANUP();
654 					errno = EFAULT;
655 					return EX_SOFTWARE;
656 				}
657 
658 				/*
659 				**  For recursion on a particular attribute,
660 				**  we may need to see if this entry is
661 				**  part of a particular objectclass.
662 				**  Also, ignore objectClass attribute.
663 				**  Otherwise we just ignore this attribute.
664 				*/
665 
666 				if (type == SM_LDAP_ATTR_OBJCLASS ||
667 				    (needobjclass != NULL &&
668 				     !sm_ldap_has_objectclass(lmap, entry,
669 							      needobjclass)))
670 				{
671 					ldap_memfree(attr);
672 					continue;
673 				}
674 
675 				if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
676 				{
677 					vals = ldap_get_values(lmap->ldap_ld,
678 							       entry,
679 							       attr);
680 					if (vals == NULL)
681 					{
682 						save_errno = sm_ldap_geterrno(lmap->ldap_ld);
683 						if (save_errno == LDAP_SUCCESS)
684 						{
685 							ldap_memfree(attr);
686 							continue;
687 						}
688 
689 						/* Must be an error */
690 						save_errno += E_LDAPBASE;
691 						ldap_memfree(attr);
692 						SM_LDAP_ERROR_CLEANUP();
693 						errno = save_errno;
694 						return EX_TEMPFAIL;
695 					}
696 				}
697 
698 				statp = EX_OK;
699 
700 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
701 				/*
702 				**  Reset value to prevent lingering
703 				**  LDAP_DECODING_ERROR due to
704 				**  OpenLDAP 1.X's hack (see below)
705 				*/
706 
707 				lmap->ldap_ld->ld_errno = LDAP_SUCCESS;
708 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
709 
710 				/*
711 				**  If matching only,
712 				**  no need to spin through entries
713 				*/
714 
715 				if (bitset(SM_LDAP_MATCHONLY, flags))
716 				{
717 					if (lmap->ldap_attrsonly == LDAPMAP_FALSE)
718 						ldap_value_free(vals);
719 					ldap_memfree(attr);
720 					continue;
721 				}
722 
723 				/*
724 				**  If we don't want multiple values,
725 				**  return first found.
726 				*/
727 
728 				if ((char) delim == '\0')
729 				{
730 					if (*result != NULL)
731 					{
732 						/* already have a value */
733 						break;
734 					}
735 
736 					if (bitset(SM_LDAP_SINGLEMATCH,
737 						   flags) &&
738 					    *result != NULL)
739 					{
740 						/* only wanted one match */
741 						SM_LDAP_ERROR_CLEANUP();
742 						errno = ENOENT;
743 						return EX_NOTFOUND;
744 					}
745 
746 					if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
747 					{
748 						*result = sm_rpool_strdup_x(rpool,
749 									    attr);
750 						ldap_memfree(attr);
751 						break;
752 					}
753 
754 					if (vals[0] == NULL)
755 					{
756 						ldap_value_free(vals);
757 						ldap_memfree(attr);
758 						continue;
759 					}
760 
761 					vsize = strlen(vals[0]) + 1;
762 					if (lmap->ldap_attrsep != '\0')
763 						vsize += strlen(attr) + 1;
764 					*result = sm_rpool_malloc_x(rpool,
765 								    vsize);
766 					if (lmap->ldap_attrsep != '\0')
767 						sm_snprintf(*result, vsize,
768 							    "%s%c%s",
769 							    attr,
770 							    lmap->ldap_attrsep,
771 							    vals[0]);
772 					else
773 						sm_strlcpy(*result, vals[0],
774 							   vsize);
775 					ldap_value_free(vals);
776 					ldap_memfree(attr);
777 					break;
778 				}
779 
780 				/* attributes only */
781 				if (lmap->ldap_attrsonly == LDAPMAP_TRUE)
782 				{
783 					if (*result == NULL)
784 						*result = sm_rpool_strdup_x(rpool,
785 									    attr);
786 					else
787 					{
788 						if (bitset(SM_LDAP_SINGLEMATCH,
789 							   flags) &&
790 						    *result != NULL)
791 						{
792 							/* only wanted one match */
793 							SM_LDAP_ERROR_CLEANUP();
794 							errno = ENOENT;
795 							return EX_NOTFOUND;
796 						}
797 
798 						vsize = strlen(*result) +
799 							strlen(attr) + 2;
800 						tmp = sm_rpool_malloc_x(rpool,
801 									vsize);
802 						(void) sm_snprintf(tmp,
803 							vsize, "%s%c%s",
804 							*result, (char) delim,
805 							attr);
806 						*result = tmp;
807 					}
808 					ldap_memfree(attr);
809 					continue;
810 				}
811 
812 				/*
813 				**  If there is more than one, munge then
814 				**  into a map_coldelim separated string.
815 				**  If we are recursing we may have an entry
816 				**  with no 'normal' values to put in the
817 				**  string.
818 				**  This is not an error.
819 				*/
820 
821 				if (type == SM_LDAP_ATTR_NORMAL &&
822 				    bitset(SM_LDAP_SINGLEMATCH, flags) &&
823 				    *result != NULL)
824 				{
825 					/* only wanted one match */
826 					SM_LDAP_ERROR_CLEANUP();
827 					errno = ENOENT;
828 					return EX_NOTFOUND;
829 				}
830 
831 				vsize = 0;
832 				for (i = 0; vals[i] != NULL; i++)
833 				{
834 					if (type == SM_LDAP_ATTR_DN ||
835 					    type == SM_LDAP_ATTR_FILTER ||
836 					    type == SM_LDAP_ATTR_URL)
837 					{
838 						/* add to recursion */
839 						if (sm_ldap_add_recurse(&recurse,
840 									vals[i],
841 									type,
842 									rpool) == NULL)
843 						{
844 							SM_LDAP_ERROR_CLEANUP();
845 							errno = ENOMEM;
846 							return EX_OSERR;
847 						}
848 						continue;
849 					}
850 
851 					vsize += strlen(vals[i]) + 1;
852 					if (lmap->ldap_attrsep != '\0')
853 						vsize += strlen(attr) + 1;
854 				}
855 
856 				/*
857 				**  Create/Append to string any normal
858 				**  attribute values.  Otherwise, just free
859 				**  memory and move on to the next
860 				**  attribute in this entry.
861 				*/
862 
863 				if (type == SM_LDAP_ATTR_NORMAL && vsize > 0)
864 				{
865 					char *pe;
866 
867 					/* Grow result string if needed */
868 					if ((*resultln + vsize) >= *resultsz)
869 					{
870 						while ((*resultln + vsize) >= *resultsz)
871 						{
872 							if (*resultsz == 0)
873 								*resultsz = 1024;
874 							else
875 								*resultsz *= 2;
876 						}
877 
878 						vp_tmp = sm_rpool_malloc_x(rpool, *resultsz);
879 						*vp_tmp = '\0';
880 
881 						if (*result != NULL)
882 							sm_strlcpy(vp_tmp,
883 								   *result,
884 								   *resultsz);
885 						*result = vp_tmp;
886 					}
887 
888 					p = *result + *resultln;
889 					pe = *result + *resultsz;
890 
891 					for (i = 0; vals[i] != NULL; i++)
892 					{
893 						if (*resultln > 0 &&
894 						    p < pe)
895 							*p++ = (char) delim;
896 
897 						if (lmap->ldap_attrsep != '\0')
898 						{
899 							p += sm_strlcpy(p, attr,
900 									pe - p);
901 							if (p < pe)
902 								*p++ = lmap->ldap_attrsep;
903 						}
904 
905 						p += sm_strlcpy(p, vals[i],
906 								pe - p);
907 						*resultln = p - (*result);
908 						if (p >= pe)
909 						{
910 							/* Internal error: buffer too small for LDAP values */
911 							SM_LDAP_ERROR_CLEANUP();
912 							errno = ENOMEM;
913 							return EX_OSERR;
914 						}
915 					}
916 				}
917 
918 				ldap_value_free(vals);
919 				ldap_memfree(attr);
920 			}
921 			save_errno = sm_ldap_geterrno(lmap->ldap_ld);
922 
923 			/*
924 			**  We check save_errno != LDAP_DECODING_ERROR since
925 			**  OpenLDAP 1.X has a very ugly *undocumented*
926 			**  hack of returning this error code from
927 			**  ldap_next_attribute() if the library freed the
928 			**  ber attribute.  See:
929 			**  http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
930 			*/
931 
932 			if (save_errno != LDAP_SUCCESS &&
933 			    save_errno != LDAP_DECODING_ERROR)
934 			{
935 				/* Must be an error */
936 				save_errno += E_LDAPBASE;
937 				SM_LDAP_ERROR_CLEANUP();
938 				errno = save_errno;
939 				return EX_TEMPFAIL;
940 			}
941 
942 			/* mark this DN as done */
943 			rl->lr_done = true;
944 
945 			/* We don't want multiple values and we have one */
946 			if ((char) delim == '\0' && *result != NULL)
947 				break;
948 		}
949 		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
950 		if (save_errno != LDAP_SUCCESS &&
951 		    save_errno != LDAP_DECODING_ERROR)
952 		{
953 			/* Must be an error */
954 			save_errno += E_LDAPBASE;
955 			SM_LDAP_ERROR_CLEANUP();
956 			errno = save_errno;
957 			return EX_TEMPFAIL;
958 		}
959 		ldap_msgfree(lmap->ldap_res);
960 		lmap->ldap_res = NULL;
961 	}
962 
963 	if (ret == 0)
964 		save_errno = ETIMEDOUT;
965 	else
966 		save_errno = sm_ldap_geterrno(lmap->ldap_ld);
967 	if (save_errno != LDAP_SUCCESS)
968 	{
969 		statp = EX_TEMPFAIL;
970 		if (ret != 0)
971 		{
972 			switch (save_errno)
973 			{
974 #ifdef LDAP_SERVER_DOWN
975 			  case LDAP_SERVER_DOWN:
976 #endif /* LDAP_SERVER_DOWN */
977 			  case LDAP_TIMEOUT:
978 			  case LDAP_UNAVAILABLE:
979 
980 				/*
981 				**  server disappeared,
982 				**  try reopen on next search
983 				*/
984 
985 				statp = EX_RESTART;
986 				break;
987 			}
988 			save_errno += E_LDAPBASE;
989 		}
990 		SM_LDAP_ERROR_CLEANUP();
991 		errno = save_errno;
992 		return statp;
993 	}
994 
995 	if (lmap->ldap_res != NULL)
996 	{
997 		ldap_msgfree(lmap->ldap_res);
998 		lmap->ldap_res = NULL;
999 	}
1000 
1001 	if (toplevel)
1002 	{
1003 		int rlidx;
1004 
1005 		/*
1006 		**  Spin through the built-up recurse list at the top
1007 		**  of the recursion.  Since new items are added at the
1008 		**  end of the shared list, we actually only ever get
1009 		**  one level of recursion before things pop back to the
1010 		**  top.  Any items added to the list during that recursion
1011 		**  will be expanded by the top level.
1012 		*/
1013 
1014 		for (rlidx = 0; recurse != NULL && rlidx < recurse->lr_cnt; rlidx++)
1015 		{
1016 			int newflags;
1017 			int sid;
1018 			int status;
1019 
1020 			rl = recurse->lr_data[rlidx];
1021 
1022 			newflags = flags;
1023 			if (rl->lr_done)
1024 			{
1025 				/* already expanded */
1026 				continue;
1027 			}
1028 
1029 			if (rl->lr_type == SM_LDAP_ATTR_DN)
1030 			{
1031 				/* do DN search */
1032 				sid = ldap_search(lmap->ldap_ld,
1033 						  rl->lr_search,
1034 						  lmap->ldap_scope,
1035 						  "(objectClass=*)",
1036 						  (lmap->ldap_attr[0] == NULL ?
1037 						   NULL : lmap->ldap_attr),
1038 						  lmap->ldap_attrsonly);
1039 			}
1040 			else if (rl->lr_type == SM_LDAP_ATTR_FILTER)
1041 			{
1042 				/* do new search */
1043 				sid = ldap_search(lmap->ldap_ld,
1044 						  lmap->ldap_base,
1045 						  lmap->ldap_scope,
1046 						  rl->lr_search,
1047 						  (lmap->ldap_attr[0] == NULL ?
1048 						   NULL : lmap->ldap_attr),
1049 						  lmap->ldap_attrsonly);
1050 			}
1051 			else if (rl->lr_type == SM_LDAP_ATTR_URL)
1052 			{
1053 				/* do new URL search */
1054 				sid = ldap_url_search(lmap->ldap_ld,
1055 						      rl->lr_search,
1056 						      lmap->ldap_attrsonly);
1057 				newflags |= SM_LDAP_USE_ALLATTR;
1058 			}
1059 			else
1060 			{
1061 				/* unknown or illegal attribute type */
1062 				errno = EFAULT;
1063 				return EX_SOFTWARE;
1064 			}
1065 
1066 			/* Collect results */
1067 			if (sid == -1)
1068 			{
1069 				save_errno = sm_ldap_geterrno(lmap->ldap_ld);
1070 				statp = EX_TEMPFAIL;
1071 				switch (save_errno)
1072 				{
1073 #ifdef LDAP_SERVER_DOWN
1074 				  case LDAP_SERVER_DOWN:
1075 #endif /* LDAP_SERVER_DOWN */
1076 				  case LDAP_TIMEOUT:
1077 				  case LDAP_UNAVAILABLE:
1078 
1079 					/*
1080 					**  server disappeared,
1081 					**  try reopen on next search
1082 					*/
1083 
1084 					statp = EX_RESTART;
1085 					break;
1086 				}
1087 				errno = save_errno + E_LDAPBASE;
1088 				return statp;
1089 			}
1090 
1091 			status = sm_ldap_results(lmap, sid, newflags, delim,
1092 						 rpool, result, resultln,
1093 						 resultsz, recurse);
1094 			save_errno = errno;
1095 			if (status != EX_OK && status != EX_NOTFOUND)
1096 			{
1097 				errno = save_errno;
1098 				return status;
1099 			}
1100 
1101 			/* Mark as done */
1102 			rl->lr_done = true;
1103 
1104 			/* Reset rlidx as new items may have been added */
1105 			rlidx = -1;
1106 		}
1107 	}
1108 	return statp;
1109 }
1110 #endif /* _FFR_LDAP_RECURSION */
1111 
1112 /*
1113 **  SM_LDAP_CLOSE -- close LDAP connection
1114 **
1115 **	Parameters:
1116 **		lmap -- LDAP map information
1117 **
1118 **	Returns:
1119 **		None.
1120 **
1121 */
1122 
1123 void
1124 sm_ldap_close(lmap)
1125 	SM_LDAP_STRUCT *lmap;
1126 {
1127 	if (lmap->ldap_ld == NULL)
1128 		return;
1129 
1130 	if (lmap->ldap_pid == getpid())
1131 		ldap_unbind(lmap->ldap_ld);
1132 	lmap->ldap_ld = NULL;
1133 	lmap->ldap_pid = 0;
1134 }
1135 
1136 /*
1137 **  SM_LDAP_SETOPTS -- set LDAP options
1138 **
1139 **	Parameters:
1140 **		ld -- LDAP session handle
1141 **		lmap -- LDAP map information
1142 **
1143 **	Returns:
1144 **		None.
1145 **
1146 */
1147 
1148 void
1149 sm_ldap_setopts(ld, lmap)
1150 	LDAP *ld;
1151 	SM_LDAP_STRUCT *lmap;
1152 {
1153 # if USE_LDAP_SET_OPTION
1154 #  if _FFR_LDAP_SETVERSION
1155 	if (lmap->ldap_version != 0)
1156 	{
1157 		ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
1158 				&lmap->ldap_version);
1159 	}
1160 #  endif /* _FFR_LDAP_SETVERSION */
1161 	ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref);
1162 	if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options))
1163 		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
1164 	else
1165 		ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1166 	ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit);
1167 	ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit);
1168 #  ifdef LDAP_OPT_RESTART
1169 	ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
1170 #  endif /* LDAP_OPT_RESTART */
1171 # else /* USE_LDAP_SET_OPTION */
1172 	/* From here on in we can use ldap internal timelimits */
1173 	ld->ld_deref = lmap->ldap_deref;
1174 	ld->ld_options = lmap->ldap_options;
1175 	ld->ld_sizelimit = lmap->ldap_sizelimit;
1176 	ld->ld_timelimit = lmap->ldap_timelimit;
1177 # endif /* USE_LDAP_SET_OPTION */
1178 }
1179 
1180 /*
1181 **  SM_LDAP_GETERRNO -- get ldap errno value
1182 **
1183 **	Parameters:
1184 **		ld -- LDAP session handle
1185 **
1186 **	Returns:
1187 **		LDAP errno.
1188 **
1189 */
1190 
1191 int
1192 sm_ldap_geterrno(ld)
1193 	LDAP *ld;
1194 {
1195 	int err = LDAP_SUCCESS;
1196 
1197 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3
1198 	(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err);
1199 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1200 #  ifdef LDAP_OPT_SIZELIMIT
1201 	err = ldap_get_lderrno(ld, NULL, NULL);
1202 #  else /* LDAP_OPT_SIZELIMIT */
1203 	err = ld->ld_errno;
1204 
1205 	/*
1206 	**  Reset value to prevent lingering LDAP_DECODING_ERROR due to
1207 	**  OpenLDAP 1.X's hack (see above)
1208 	*/
1209 
1210 	ld->ld_errno = LDAP_SUCCESS;
1211 #  endif /* LDAP_OPT_SIZELIMIT */
1212 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */
1213 	return err;
1214 }
1215 # endif /* LDAPMAP */
1216