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