ldap.c revision 363466
1/*
2 * Copyright (c) 2001-2009 Proofpoint, Inc. and its suppliers.
3 *      All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 */
9
10/* some "deprecated" calls are used, e.g., ldap_get_values() */
11#define LDAP_DEPRECATED	1
12
13#include <sm/gen.h>
14SM_RCSID("@(#)$Id: ldap.c,v 1.86 2013-11-22 20:51:43 ca Exp $")
15
16#if LDAPMAP
17# include <sys/types.h>
18# include <errno.h>
19# include <setjmp.h>
20# include <stdlib.h>
21# include <unistd.h>
22
23# include <sm/bitops.h>
24# include <sm/clock.h>
25# include <sm/conf.h>
26# include <sm/debug.h>
27# include <sm/errstring.h>
28# include <sm/ldap.h>
29# include <sm/string.h>
30#  ifdef EX_OK
31#   undef EX_OK			/* for SVr4.2 SMP */
32#  endif
33# include <sm/sysexits.h>
34
35SM_DEBUG_T SmLDAPTrace = SM_DEBUG_INITIALIZER("sm_trace_ldap",
36	"@(#)$Debug: sm_trace_ldap - trace LDAP operations $");
37
38static bool	sm_ldap_has_objectclass __P((SM_LDAP_STRUCT *, LDAPMessage *, char *));
39static 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
64void
65sm_ldap_clear(lmap)
66	SM_LDAP_STRUCT *lmap;
67{
68	if (lmap == NULL)
69		return;
70
71	lmap->ldap_host = NULL;
72	lmap->ldap_port = LDAP_PORT;
73	lmap->ldap_uri = NULL;
74	lmap->ldap_version = SM_LDAP_VERSION_DEFAULT;
75	lmap->ldap_deref = LDAP_DEREF_NEVER;
76	lmap->ldap_timelimit = LDAP_NO_LIMIT;
77	lmap->ldap_sizelimit = LDAP_NO_LIMIT;
78# ifdef LDAP_REFERRALS
79	lmap->ldap_options = LDAP_OPT_REFERRALS;
80# else
81	lmap->ldap_options = 0;
82# endif
83	lmap->ldap_attrsep = '\0';
84	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)
104static void ldap_debug_cb __P((const char *msg));
105
106static void
107ldap_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
148static bool dbg_init = false;
149# endif
150# if SM_CONF_LDAP_INITIALIZE
151static void sm_ldap_setoptsg __P((SM_LDAP_STRUCT *lmap));
152static void
153sm_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
197void
198sm_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
259static jmp_buf	LDAPTimeout;
260static void	ldaptimeout __P((int));
261
262/* ARGSUSED */
263static void
264ldaptimeout(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)					\
279do									\
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()						\
296do									\
297{									\
298	if (ev != NULL)							\
299		sm_clrevent(ev);					\
300} while (0)
301#endif /* !USE_LDAP_INIT || !LDAP_NETWORK_TIMEOUT */
302
303bool
304sm_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
518int
519sm_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
638int
639sm_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
666static bool
667sm_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
736static SM_LDAP_RECURSE_ENTRY *
737sm_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
849int
850sm_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
1567void
1568sm_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
1590int
1591sm_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