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