Home | History | Annotate | Download | only in libnisdb
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <synch.h>
     30 #include <strings.h>
     31 #include <sys/time.h>
     32 #include <ctype.h>
     33 
     34 #include "ldap_op.h"
     35 #include "ldap_util.h"
     36 #include "ldap_structs.h"
     37 #include "ldap_ruleval.h"
     38 #include "ldap_attr.h"
     39 #include "ldap_print.h"
     40 #include "ldap_glob.h"
     41 
     42 #include "nis_parse_ldap_conf.h"
     43 
     44 #ifndef LDAPS_PORT
     45 #define	LDAPS_PORT	636
     46 #endif
     47 
     48 static int setupConList(char *serverList, char *who,
     49 			char *cred, auth_method_t method);
     50 
     51 
     52 /*
     53  * Build one of our internal LDAP search structures, containing copies of
     54  * the supplied input. return NULL in case of error.
     55  *
     56  * If 'filter' is NULL, build an AND-filter using the filter components.
     57  */
     58 __nis_ldap_search_t *
     59 buildLdapSearch(char *base, int scope, int numFilterComps, char **filterComp,
     60 		char *filter, char **attrs, int attrsonly, int isDN) {
     61 	__nis_ldap_search_t	*ls;
     62 	char			**a;
     63 	int			i, na, err = 0;
     64 	char			*myself = "buildLdapSearch";
     65 
     66 	ls = am(myself, sizeof (*ls));
     67 	if (ls == 0)
     68 		return (0);
     69 
     70 	ls->base = sdup(myself, T, base);
     71 	if (ls->base == 0 && base != 0)
     72 		err++;
     73 	ls->scope = scope;
     74 
     75 	if (filterComp != 0 && numFilterComps > 0) {
     76 		ls->filterComp = am(myself, numFilterComps *
     77 					sizeof (ls->filterComp[0]));
     78 		if (ls->filterComp == 0) {
     79 			err++;
     80 			numFilterComps = 0;
     81 		}
     82 		for (i = 0; i < numFilterComps; i++) {
     83 			ls->filterComp[i] = sdup(myself, T, filterComp[i]);
     84 			if (ls->filterComp[i] == 0 && filterComp[i] != 0)
     85 				err++;
     86 		}
     87 		ls->numFilterComps = numFilterComps;
     88 		if (filter == 0) {
     89 			ls->filter = concatenateFilterComps(ls->numFilterComps,
     90 					ls->filterComp);
     91 			if (ls->filter == 0)
     92 				err++;
     93 		}
     94 	} else {
     95 		ls->filterComp = 0;
     96 		ls->numFilterComps = 0;
     97 		ls->filter = sdup(myself, T, filter);
     98 		if (ls->filter == 0 && filter != 0)
     99 			err++;
    100 	}
    101 
    102 	if (attrs != 0) {
    103 		for (na = 0, a = attrs; *a != 0; a++, na++);
    104 		ls->attrs = am(myself, (na + 1) * sizeof (ls->attrs[0]));
    105 		if (ls->attrs != 0) {
    106 			for (i = 0; i < na; i++) {
    107 				ls->attrs[i] = sdup(myself, T, attrs[i]);
    108 				if (ls->attrs[i] == 0 && attrs[i] != 0)
    109 					err++;
    110 			}
    111 			ls->attrs[na] = 0;
    112 			ls->numAttrs = na;
    113 		} else {
    114 			err++;
    115 		}
    116 	} else {
    117 		ls->attrs = 0;
    118 		ls->numAttrs = 0;
    119 	}
    120 
    121 	ls->attrsonly = attrsonly;
    122 	ls->isDN = isDN;
    123 
    124 	if (err > 0) {
    125 		freeLdapSearch(ls);
    126 		ls = 0;
    127 	}
    128 
    129 	return (ls);
    130 }
    131 
    132 void
    133 freeLdapSearch(__nis_ldap_search_t *ls) {
    134 	int	i;
    135 
    136 	if (ls == 0)
    137 		return;
    138 
    139 	sfree(ls->base);
    140 	if (ls->filterComp != 0) {
    141 		for (i = 0; i < ls->numFilterComps; i++) {
    142 			sfree(ls->filterComp[i]);
    143 		}
    144 		sfree(ls->filterComp);
    145 	}
    146 	sfree(ls->filter);
    147 	if (ls->attrs != 0) {
    148 		for (i = 0; i < ls->numAttrs; i++) {
    149 			sfree(ls->attrs[i]);
    150 		}
    151 		sfree(ls->attrs);
    152 	}
    153 
    154 	free(ls);
    155 }
    156 
    157 /*
    158  * Given a table mapping, and a rule/value pointer,
    159  * return an LDAP search structure with values suitable for use
    160  * by ldap_search() or (if dn != 0) ldap_modify(). The rule/value
    161  * may be modified.
    162  *
    163  * If dn != 0 and *dn == 0, the function attemps to return a pointer
    164  * to the DN. This may necessitate an ldapSearch, if the rule set doesn't
    165  * produce a DN directly.
    166  *
    167  * if dn == 0, and the rule set produces a DN as well as other attribute/
    168  * value pairs, the function returns an LDAP search structure with the
    169  * DN only.
    170  *
    171  * If 'fromLDAP' is set, the caller wants base/scope/filter from
    172  * t->objectDN->read; otherwise, from t->objectDN->write.
    173  *
    174  * If 'rv' is NULL, the caller wants an enumeration of the container.
    175  *
    176  * Note that this function only creates a search structure for 't' itself;
    177  * if there are alternative mappings for the table, those must be handled
    178  * by our caller.
    179  */
    180 __nis_ldap_search_t *
    181 createLdapRequest(__nis_table_mapping_t *t,
    182 		__nis_rule_value_t *rv, char **dn, int fromLDAP,
    183 		int *res, __nis_object_dn_t *obj_dn) {
    184 	int			i, j;
    185 	__nis_ldap_search_t	*ls = 0;
    186 	char			**locDN;
    187 	int			numLocDN, stat = 0, count = 0;
    188 	char			*myself = "createLdapRequest";
    189 	__nis_object_dn_t 	*objectDN = NULL;
    190 
    191 	if (t == 0)
    192 		return (0);
    193 
    194 	if (obj_dn == NULL)
    195 		objectDN = t->objectDN;
    196 	else
    197 		objectDN = obj_dn;
    198 
    199 	if (rv == 0) {
    200 		char	*base;
    201 		char	*filter;
    202 
    203 		if (fromLDAP) {
    204 			base = objectDN->read.base;
    205 			filter = makeFilter(objectDN->read.attrs);
    206 		} else {
    207 			base = objectDN->write.base;
    208 			filter = makeFilter(objectDN->write.attrs);
    209 		}
    210 
    211 		/* Create request to enumerate container */
    212 		ls = buildLdapSearch(base, objectDN->read.scope, 0, 0, filter,
    213 					0, 0, 0);
    214 		sfree(filter);
    215 		return (ls);
    216 	}
    217 
    218 	for (i = 0; i < t->numRulesToLDAP; i++) {
    219 		rv = addLdapRuleValue(t, t->ruleToLDAP[i],
    220 				mit_ldap, mit_nisplus, rv, !fromLDAP, &stat);
    221 		if (rv == 0)
    222 			return (0);
    223 		if (stat == NP_LDAP_RULES_NO_VALUE)
    224 			count++;
    225 		stat = 0;
    226 	}
    227 
    228 	/*
    229 	 * If none of the rules produced a value despite
    230 	 * having enough NIS+ columns, return error.
    231 	 */
    232 	if (rv->numAttrs == 0 && count > 0) {
    233 		*res = NP_LDAP_RULES_NO_VALUE;
    234 		return (0);
    235 	}
    236 
    237 	/*
    238 	 * 'rv' now contains everything we know about the attributes and
    239 	 * values. Build an LDAP search structure from it.
    240 	 */
    241 
    242 	/* Look for a single-valued DN */
    243 	locDN = findDNs(myself, rv, 1,
    244 			fromLDAP ? objectDN->read.base :
    245 					objectDN->write.base,
    246 			&numLocDN);
    247 	if (locDN != 0 && numLocDN == 1) {
    248 		if (dn != 0 && *dn == 0) {
    249 			*dn = locDN[0];
    250 			sfree(locDN);
    251 		} else {
    252 			char	*filter;
    253 
    254 			if (fromLDAP)
    255 				filter = makeFilter(objectDN->read.attrs);
    256 			else
    257 				filter = makeFilter(objectDN->write.attrs);
    258 			ls = buildLdapSearch(locDN[0], LDAP_SCOPE_BASE, 0, 0,
    259 						filter, 0, 0, 1);
    260 			sfree(filter);
    261 			freeDNs(locDN, numLocDN);
    262 		}
    263 	} else {
    264 		freeDNs(locDN, numLocDN);
    265 	}
    266 
    267 	if (ls != 0) {
    268 		ls->useCon = 1;
    269 		return (ls);
    270 	}
    271 
    272 	/*
    273 	 * No DN, or caller wanted a search structure with the non-DN
    274 	 * attributes.
    275 	 */
    276 
    277 	/* Initialize search structure */
    278 	{
    279 		char	*filter = (fromLDAP) ?
    280 				makeFilter(objectDN->read.attrs) :
    281 				makeFilter(objectDN->write.attrs);
    282 		char	**ofc;
    283 		int	nofc = 0;
    284 
    285 		ofc = makeFilterComp(filter, &nofc);
    286 
    287 		if (filter != 0 && ofc == 0) {
    288 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
    289 			"%s: Unable to break filter into components: \"%s\"",
    290 				myself, NIL(filter));
    291 			sfree(filter);
    292 			return (0);
    293 		}
    294 
    295 		if (fromLDAP)
    296 			ls = buildLdapSearch(objectDN->read.base,
    297 				objectDN->read.scope,
    298 				nofc, ofc, 0, 0, 0, 0);
    299 		else
    300 			ls = buildLdapSearch(objectDN->write.base,
    301 				objectDN->write.scope,
    302 				nofc, ofc, 0, 0, 0, 0);
    303 		sfree(filter);
    304 		freeFilterComp(ofc, nofc);
    305 		if (ls == 0)
    306 			return (0);
    307 	}
    308 
    309 	/* Build and add the filter components */
    310 	for (i = 0; i < rv->numAttrs; i++) {
    311 		/* Skip DN */
    312 		if (strcasecmp("dn", rv->attrName[i]) == 0)
    313 			continue;
    314 
    315 		/* Skip vt_ber values */
    316 		if (rv->attrVal[i].type == vt_ber)
    317 			continue;
    318 
    319 		for (j = 0; j < rv->attrVal[i].numVals; j++) {
    320 			__nis_buffer_t	b = {0, 0};
    321 			char		**tmpComp;
    322 
    323 			bp2buf(myself, &b, "%s=%s",
    324 				rv->attrName[i], rv->attrVal[i].val[j].value);
    325 			tmpComp = addFilterComp(b.buf, ls->filterComp,
    326 						&ls->numFilterComps);
    327 			if (tmpComp == 0) {
    328 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
    329 				"%s: Unable to add filter component \"%s\"",
    330 					myself, NIL(b.buf));
    331 				sfree(b.buf);
    332 				freeLdapSearch(ls);
    333 				return (0);
    334 			}
    335 			ls->filterComp = tmpComp;
    336 			sfree(b.buf);
    337 		}
    338 	}
    339 
    340 	if (ls->numFilterComps > 0) {
    341 		sfree(ls->filter);
    342 		ls->filter = concatenateFilterComps(ls->numFilterComps,
    343 							ls->filterComp);
    344 		if (ls->filter == 0) {
    345 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
    346 			"%s: Unable to concatenate filter components",
    347 				myself);
    348 			freeLdapSearch(ls);
    349 			return (0);
    350 		}
    351 	}
    352 
    353 	if (dn != 0 && *dn == 0) {
    354 		/*
    355 		 * The caller wants a DN, but we didn't get one from the
    356 		 * the rule set. We have an 'ls', so use it to ldapSearch()
    357 		 * for an entry from which we can extract the DN.
    358 		 */
    359 		__nis_rule_value_t	*rvtmp;
    360 		char			**locDN;
    361 		int			nv = 0, numLocDN;
    362 
    363 		rvtmp = ldapSearch(ls, &nv, 0, 0);
    364 		locDN = findDNs(myself, rvtmp, nv, 0, &numLocDN);
    365 		if (locDN != 0 && numLocDN == 1) {
    366 			*dn = locDN[0];
    367 			sfree(locDN);
    368 		} else {
    369 			freeDNs(locDN, numLocDN);
    370 		}
    371 		freeRuleValue(rvtmp, nv);
    372 	}
    373 
    374 	ls->useCon = 1;
    375 	return (ls);
    376 }
    377 
    378 int	ldapConnAttemptRetryTimeout = 60;	/* seconds */
    379 
    380 typedef struct {
    381 	LDAP		*ld;
    382 	mutex_t		mutex;		/* Mutex for update of structure */
    383 	pthread_t	owner;		/* Thread holding mutex */
    384 	mutex_t		rcMutex;	/* Mutex for refCount */
    385 	int		refCount;	/* Reference count */
    386 	int		isBound;	/* Is connection open and usable ? */
    387 	time_t		retryTime;	/* When should open be retried */
    388 	int		status;		/* Status of last operation */
    389 	int		doDis;		/* To be disconnected if refCount==0 */
    390 	int		doDel;		/* To be deleted if refCount zero */
    391 	int		onList;		/* True if on the 'ldapCon' list */
    392 	char		*sp;		/* server string */
    393 	char		*who;
    394 	char		*cred;
    395 	auth_method_t	method;
    396 	int		port;
    397 	struct timeval	bindTimeout;
    398 	struct timeval	searchTimeout;
    399 	struct timeval	modifyTimeout;
    400 	struct timeval	addTimeout;
    401 	struct timeval	deleteTimeout;
    402 	int		simplePage;	/* Can do simple-page */
    403 	int		vlv;		/* Can do VLV */
    404 	uint_t		batchFrom;	/* # entries read in one operation */
    405 	void		*next;
    406 } __nis_ldap_conn_t;
    407 
    408 /*
    409  * List of connections, 'ldapCon', protected by an RW lock.
    410  *
    411  * The following locking scheme is used:
    412  *
    413  * (1)	Find a connection structure to use to talk to LDAP
    414  *		Rlock list
    415  *			Locate structure
    416  *			Acquire 'mutex'
    417  *				Acquire 'rcMutex'
    418  *					update refCount
    419  *				Release 'rcMutex'
    420  *			release 'mutex'
    421  *		Unlock list
    422  *		Use structure
    423  *		Release structure when done
    424  * (2)	Insert/delete structure(s) on/from list
    425  *		Wlock list
    426  *			Insert/delete structure; if deleting, must
    427  *			acquire 'mutex', and 'rcMutex' (in that order),
    428  *			and 'refCount' must be zero.
    429  *		Unlock list
    430  * (3)	Modify structure
    431  *		Find structure
    432  *		Acquire 'mutex'
    433  *			Modify (except refCount)
    434  *		Release 'mutex'
    435  *		Release structure
    436  */
    437 
    438 __nis_ldap_conn_t		*ldapCon = 0;
    439 __nis_ldap_conn_t		*ldapReferralCon = 0;
    440 static rwlock_t			ldapConLock = DEFAULTRWLOCK;
    441 static rwlock_t			referralConLock = DEFAULTRWLOCK;
    442 
    443 void
    444 exclusiveLC(__nis_ldap_conn_t *lc) {
    445 	pthread_t	me = pthread_self();
    446 	int		stat;
    447 
    448 	if (lc == 0)
    449 		return;
    450 
    451 	stat = mutex_trylock(&lc->mutex);
    452 	if (stat == EBUSY && lc->owner != me)
    453 		mutex_lock(&lc->mutex);
    454 
    455 	lc->owner = me;
    456 }
    457 
    458 /* Return 1 if mutex held by this thread, 0 otherwise */
    459 int
    460 assertExclusive(__nis_ldap_conn_t *lc) {
    461 	pthread_t	me;
    462 	int		stat;
    463 
    464 	if (lc == 0)
    465 		return (0);
    466 
    467 	stat = mutex_trylock(&lc->mutex);
    468 
    469 	if (stat == 0) {
    470 		mutex_unlock(&lc->mutex);
    471 		return (0);
    472 	}
    473 
    474 	me = pthread_self();
    475 	if (stat != EBUSY || lc->owner != me)
    476 		return (0);
    477 
    478 	return (1);
    479 }
    480 
    481 void
    482 releaseLC(__nis_ldap_conn_t *lc) {
    483 	pthread_t	me = pthread_self();
    484 
    485 	if (lc == 0 || lc->owner != me)
    486 		return;
    487 
    488 	lc->owner = 0;
    489 	(void) mutex_unlock(&lc->mutex);
    490 }
    491 
    492 void
    493 incrementRC(__nis_ldap_conn_t *lc) {
    494 	if (lc == 0)
    495 		return;
    496 
    497 	(void) mutex_lock(&lc->rcMutex);
    498 	lc->refCount++;
    499 	(void) mutex_unlock(&lc->rcMutex);
    500 }
    501 
    502 void
    503 decrementRC(__nis_ldap_conn_t *lc) {
    504 	if (lc == 0)
    505 		return;
    506 
    507 	(void) mutex_lock(&lc->rcMutex);
    508 	if (lc->refCount > 0)
    509 		lc->refCount--;
    510 	(void) mutex_unlock(&lc->rcMutex);
    511 }
    512 
    513 /* Accept a server/port indication, and call ldap_init() */
    514 static LDAP *
    515 ldapInit(char *srv, int port, bool_t use_ssl) {
    516 	LDAP			*ld;
    517 	int			ldapVersion = LDAP_VERSION3;
    518 	int			derefOption = LDAP_DEREF_ALWAYS;
    519 	int			timelimit = proxyInfo.search_time_limit;
    520 	int			sizelimit = proxyInfo.search_size_limit;
    521 	char			*myself = "ldapInit";
    522 
    523 	if (srv == 0)
    524 		return (0);
    525 
    526 	if (use_ssl) {
    527 		ld = ldapssl_init(srv, port, 1);
    528 	} else {
    529 		ld = ldap_init(srv, port);
    530 	}
    531 
    532 	if (ld != 0) {
    533 		(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
    534 					&ldapVersion);
    535 		(void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
    536 		(void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
    537 		(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &timelimit);
    538 		(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
    539 		(void) ldap_set_option(ld, LDAP_OPT_REBIND_ARG, 0);
    540 	}
    541 
    542 	return (ld);
    543 }
    544 
    545 /*
    546  * Bind the specified LDAP structure per the supplied authentication.
    547  * Note: tested with none, simple, and digest_md5. May or may not
    548  * work with other authentication methods, mostly depending on whether
    549  * or not 'who' and 'cred' contain sufficient information.
    550  */
    551 static int
    552 ldapBind(LDAP **ldP, char *who, char *cred, auth_method_t method,
    553 		struct timeval timeout) {
    554 	int		ret;
    555 	LDAP		*ld;
    556 	char		*myself = "ldapBind";
    557 
    558 	if (ldP == 0 || (ld = *ldP) == 0)
    559 		return (LDAP_PARAM_ERROR);
    560 
    561 	if (method == none) {
    562 		/* No ldap_bind() required (or even possible) */
    563 		ret = LDAP_SUCCESS;
    564 	} else if (method == simple) {
    565 		struct timeval	tv;
    566 		LDAPMessage	*msg = 0;
    567 
    568 		tv = timeout;
    569 		ret = ldap_bind(ld, who, cred, LDAP_AUTH_SIMPLE);
    570 		if (ret != -1) {
    571 			ret = ldap_result(ld, ret, 0, &tv, &msg);
    572 			if (ret == 0) {
    573 				ret = LDAP_TIMEOUT;
    574 			} else if (ret == -1) {
    575 				(void) ldap_get_option(ld,
    576 							LDAP_OPT_ERROR_NUMBER,
    577 							&ret);
    578 			} else {
    579 				ret = ldap_result2error(ld, msg, 0);
    580 			}
    581 			if (msg != 0)
    582 				(void) ldap_msgfree(msg);
    583 		} else {
    584 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
    585 						&ret);
    586 		}
    587 	} else if (method == cram_md5) {
    588 		/* Note: there is only a synchronous call for cram-md5 */
    589 		struct berval ber_cred;
    590 
    591 		ber_cred.bv_len = strlen(cred);
    592 		ber_cred.bv_val = cred;
    593 		ret = ldap_sasl_cram_md5_bind_s(ld, who, &ber_cred, NULL, NULL);
    594 	} else if (method == digest_md5) {
    595 		/* Note: there is only a synchronous call for digest-md5 */
    596 		struct berval ber_cred;
    597 
    598 		ber_cred.bv_len = strlen(cred);
    599 		ber_cred.bv_val = cred;
    600 		ret = ldap_x_sasl_digest_md5_bind_s(ld, who, &ber_cred, NULL,
    601 			NULL);
    602 	} else {
    603 		ret = LDAP_AUTH_METHOD_NOT_SUPPORTED;
    604 	}
    605 
    606 	if (ret != LDAP_SUCCESS) {
    607 		(void) ldap_unbind_s(ld);
    608 		*ldP = 0;
    609 		logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    610 			"%s: Unable to bind as: %s: %s",
    611 			myself, who, ldap_err2string(ret));
    612 	}
    613 
    614 	return (ret);
    615 }
    616 
    617 /*
    618  * Free 'lc' and all related memory. Caller must hold the exclusive lock.
    619  * Return LDAP_UNAVAILABLE upon success, in which case the caller mustn't
    620  * try to use the structure pointer in any way.
    621  */
    622 static int
    623 freeCon(__nis_ldap_conn_t *lc) {
    624 	char			*myself = "freeCon";
    625 
    626 	if (!assertExclusive(lc))
    627 		return (LDAP_PARAM_ERROR);
    628 
    629 	incrementRC(lc);
    630 
    631 	/* Must be unused, unbound, and not on the 'ldapCon' list */
    632 	if (lc->onList || lc->refCount != 1 || lc->isBound) {
    633 		lc->doDel++;
    634 		decrementRC(lc);
    635 		return (LDAP_BUSY);
    636 	}
    637 
    638 	sfree(lc->sp);
    639 	sfree(lc->who);
    640 	sfree(lc->cred);
    641 
    642 	/* Delete structure with both mutex:es held */
    643 
    644 	free(lc);
    645 
    646 	return (LDAP_UNAVAILABLE);
    647 }
    648 
    649 /*
    650  * Disconnect the specified LDAP connection. Caller must have acquired 'mutex'.
    651  *
    652  * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch
    653  * the structure in any way.
    654  */
    655 static int
    656 disconnectCon(__nis_ldap_conn_t *lc) {
    657 	int	stat;
    658 	char	*myself = "disconnectCon";
    659 
    660 	if (lc == 0)
    661 		return (LDAP_SUCCESS);
    662 
    663 	if (!assertExclusive(lc))
    664 		return (LDAP_UNAVAILABLE);
    665 
    666 	if (lc->doDis) {
    667 
    668 		/* Increment refCount to protect against interference */
    669 		incrementRC(lc);
    670 		/* refCount must be one (i.e., just us) */
    671 		if (lc->refCount != 1) {
    672 			/*
    673 			 * In use; already marked for disconnect,
    674 			 * so do nothing.
    675 			 */
    676 			decrementRC(lc);
    677 			return (LDAP_BUSY);
    678 		}
    679 
    680 		stat = ldap_unbind_s(lc->ld);
    681 		if (stat == LDAP_SUCCESS) {
    682 			lc->ld = 0;
    683 			lc->isBound = 0;
    684 			lc->doDis = 0;
    685 			/* Reset simple page and vlv indication */
    686 			lc->simplePage = 0;
    687 			lc->vlv = 0;
    688 		} else if (verbose) {
    689 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
    690 				"%s: ldap_unbind_s() => %d (%s)",
    691 				myself, stat, ldap_err2string(stat));
    692 		}
    693 
    694 		decrementRC(lc);
    695 	}
    696 
    697 	if (lc->doDel) {
    698 		if (LDAP_UNAVAILABLE == freeCon(lc))
    699 			stat = LDAP_UNAVAILABLE;
    700 	}
    701 
    702 	return (stat);
    703 }
    704 
    705 /*
    706  * controlSupported will determine for a given connection whether a set
    707  * of controls is supported or not. The input parameters:
    708  *	lc	The connection
    709  *	ctrl	A an array of OID strings, the terminal string should be NULL
    710  * The returned values if LDAP_SUCCESS is returned:
    711  *	supported	A caller supplied array which will be set to TRUE or
    712  *			FALSE depending on whether the corresponding control
    713  *			is reported as supported.
    714  * Returns LDAP_SUCCESS if the supportedControl attribute is read.
    715  */
    716 
    717 static int
    718 controlSupported(__nis_ldap_conn_t *lc, char **ctrl, bool_t *supported) {
    719 	LDAPMessage	*res, *e;
    720 	char		*attr[2], *a, **val;
    721 	int		stat, i;
    722 	BerElement	*ber = 0;
    723 	char		*myself = "controlSupported";
    724 
    725 	attr[0] = "supportedControl";
    726 	attr[1] = 0;
    727 
    728 	stat = ldap_search_st(lc->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
    729 				attr, 0, &lc->searchTimeout, &res);
    730 	if (stat != LDAP_SUCCESS) {
    731 		logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    732 	"%s: Unable to retrieve supported control information for %s: %s",
    733 			myself, NIL(lc->sp), ldap_err2string(stat));
    734 		return (stat);
    735 	}
    736 
    737 	e = ldap_first_entry(lc->ld, res);
    738 	if (e != 0) {
    739 		a = ldap_first_attribute(lc->ld, e, &ber);
    740 		if (a != 0) {
    741 			val = ldap_get_values(lc->ld, e, a);
    742 			if (val == 0) {
    743 				ldap_memfree(a);
    744 				if (ber != 0)
    745 					ber_free(ber, 0);
    746 			}
    747 		}
    748 	}
    749 	if (e == 0 || a == 0 || val == 0) {
    750 		ldap_msgfree(res);
    751 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
    752 			"%s: Unable to get root DSE for %s",
    753 			myself, NIL(lc->sp));
    754 		return (LDAP_OPERATIONS_ERROR);
    755 	}
    756 
    757 	while (*ctrl != NULL) {
    758 		*supported = FALSE;
    759 		for (i = 0; val[i] != 0; i++) {
    760 			if (strstr(val[i], *ctrl) != 0) {
    761 				*supported = TRUE;
    762 				break;
    763 			}
    764 		}
    765 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
    766 			"%s: %s: %s: %s",
    767 			myself, NIL(lc->sp), NIL(*ctrl),
    768 			*supported ? "enabled" : "disabled");
    769 		ctrl++;
    770 		supported++;
    771 	}
    772 
    773 	ldap_value_free(val);
    774 	ldap_memfree(a);
    775 	if (ber != 0)
    776 		ber_free(ber, 0);
    777 	ldap_msgfree(res);
    778 
    779 	return (stat);
    780 }
    781 
    782 /*
    783  * Connect the LDAP connection 'lc'. Caller must have acquired the 'mutex',
    784  * and the refCount must be zero.
    785  *
    786  * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch
    787  * the structure in any way.
    788  */
    789 static int
    790 connectCon(__nis_ldap_conn_t *lc, int check_ctrl) {
    791 	struct timeval	tp;
    792 	int		stat;
    793 	bool_t		supported[2] = {FALSE, FALSE};
    794 	char		*ctrl[3] = {LDAP_CONTROL_SIMPLE_PAGE,
    795 					LDAP_CONTROL_VLVREQUEST,
    796 					NULL};
    797 
    798 	if (lc == 0)
    799 		return (LDAP_SUCCESS);
    800 
    801 	if (!assertExclusive(lc))
    802 		return (LDAP_PARAM_ERROR);
    803 
    804 	incrementRC(lc);
    805 	if (lc->refCount != 1) {
    806 		/*
    807 		 * Don't want to step on structure when it's used by someone
    808 		 * else.
    809 		 */
    810 		decrementRC(lc);
    811 		return (LDAP_BUSY);
    812 	}
    813 
    814 	(void) gettimeofday(&tp, 0);
    815 
    816 	if (lc->ld != 0) {
    817 		/* Try to disconnect */
    818 		lc->doDis++;
    819 		decrementRC(lc);
    820 		/* disconnctCon() will do the delete if required */
    821 		stat = disconnectCon(lc);
    822 		if (stat != LDAP_SUCCESS)
    823 			return (stat);
    824 		incrementRC(lc);
    825 		if (lc->refCount != 1 || lc->ld != 0) {
    826 			decrementRC(lc);
    827 			return (lc->ld != 0) ? LDAP_SUCCESS :
    828 						LDAP_BUSY;
    829 		}
    830 	} else if (tp.tv_sec < lc->retryTime) {
    831 		/* Too early to retry connect */
    832 		decrementRC(lc);
    833 		return (LDAP_SERVER_DOWN);
    834 	}
    835 
    836 	/* Set new retry time in case we fail below */
    837 	lc->retryTime = tp.tv_sec + ldapConnAttemptRetryTimeout;
    838 
    839 	lc->ld = ldapInit(lc->sp, lc->port, proxyInfo.tls_method != no_tls);
    840 	if (lc->ld == 0) {
    841 		decrementRC(lc);
    842 		return (LDAP_LOCAL_ERROR);
    843 	}
    844 
    845 	stat = lc->status = ldapBind(&lc->ld, lc->who, lc->cred, lc->method,
    846 		lc->bindTimeout);
    847 	if (lc->status == LDAP_SUCCESS) {
    848 		lc->isBound = 1;
    849 		lc->retryTime = 0;
    850 		if (check_ctrl) {
    851 			(void) controlSupported(lc, ctrl, supported);
    852 			lc->simplePage = supported[0];
    853 			lc->vlv = supported[1];
    854 			lc->batchFrom = 50000;
    855 		}
    856 	}
    857 
    858 	decrementRC(lc);
    859 
    860 	return (stat);
    861 }
    862 
    863 /*
    864  * Find and return a connection believed to be OK.
    865  */
    866 static __nis_ldap_conn_t *
    867 findCon(int *stat) {
    868 	__nis_ldap_conn_t	*lc;
    869 	int			ldapStat;
    870 	char			*myself = "findCon";
    871 
    872 	if (stat == 0)
    873 		stat = &ldapStat;
    874 
    875 	(void) rw_rdlock(&ldapConLock);
    876 
    877 	if (ldapCon == 0) {
    878 		/* Probably first call; try to set up the connection list */
    879 		(void) rw_unlock(&ldapConLock);
    880 		if ((*stat = setupConList(proxyInfo.default_servers,
    881 					proxyInfo.proxy_dn,
    882 					proxyInfo.proxy_passwd,
    883 					proxyInfo.auth_method)) !=
    884 					LDAP_SUCCESS)
    885 			return (0);
    886 		(void) rw_rdlock(&ldapConLock);
    887 	}
    888 
    889 	for (lc = ldapCon; lc != 0; lc = lc->next) {
    890 		exclusiveLC(lc);
    891 		if (!lc->isBound) {
    892 			*stat = connectCon(lc, 1);
    893 			if (*stat != LDAP_SUCCESS) {
    894 				if (*stat != LDAP_UNAVAILABLE) {
    895 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
    896 		"%s: Cannot open connection to LDAP server (%s): %s",
    897 						myself, NIL(lc->sp),
    898 						ldap_err2string(*stat));
    899 					releaseLC(lc);
    900 				}
    901 				continue;
    902 			}
    903 		} else if (lc->doDis || lc->doDel) {
    904 			*stat = disconnectCon(lc);
    905 			if (*stat != LDAP_UNAVAILABLE)
    906 				releaseLC(lc);
    907 			continue;
    908 		}
    909 		incrementRC(lc);
    910 		releaseLC(lc);
    911 		break;
    912 	}
    913 
    914 	(void) rw_unlock(&ldapConLock);
    915 
    916 	return (lc);
    917 }
    918 
    919 /* Release connection; decrements ref count for the connection */
    920 static void
    921 releaseCon(__nis_ldap_conn_t *lc, int status) {
    922 	int	stat;
    923 
    924 	if (lc == 0)
    925 		return;
    926 
    927 	exclusiveLC(lc);
    928 
    929 	lc->status = status;
    930 
    931 	decrementRC(lc);
    932 
    933 	if (lc->doDis)
    934 		stat = disconnectCon(lc);
    935 	else
    936 		stat = LDAP_SUCCESS;
    937 
    938 	if (stat != LDAP_UNAVAILABLE)
    939 		releaseLC(lc);
    940 }
    941 
    942 static __nis_ldap_conn_t *
    943 createCon(char *sp, char *who, char *cred, auth_method_t method, int port) {
    944 	__nis_ldap_conn_t	*lc;
    945 	char			*myself = "createCon";
    946 	char			*r;
    947 
    948 	if (sp == 0)
    949 		return (0);
    950 
    951 	lc = am(myself, sizeof (*lc));
    952 	if (lc == 0)
    953 		return (0);
    954 
    955 	(void) mutex_init(&lc->mutex, 0, 0);
    956 	(void) mutex_init(&lc->rcMutex, 0, 0);
    957 
    958 	/* If we need to delete 'lc', freeCon() wants the mutex held */
    959 	exclusiveLC(lc);
    960 
    961 	lc->sp = sdup(myself, T, sp);
    962 	if (lc->sp == 0) {
    963 		(void) freeCon(lc);
    964 		return (0);
    965 	}
    966 
    967 	if ((r = strchr(lc->sp, ']')) != 0) {
    968 		/*
    969 		 * IPv6 address. Does libldap want this with the
    970 		 * '[' and ']' left in place ? Assume so for now.
    971 		 */
    972 		r = strchr(r, ':');
    973 	} else {
    974 		r = strchr(lc->sp, ':');
    975 	}
    976 
    977 	if (r != NULL) {
    978 		*r++ = '\0';
    979 		port = atoi(r);
    980 	} else if (port == 0)
    981 		port = proxyInfo.tls_method == ssl_tls ? LDAPS_PORT : LDAP_PORT;
    982 
    983 	if (who != 0) {
    984 		lc->who = sdup(myself, T, who);
    985 		if (lc->who == 0) {
    986 			(void) freeCon(lc);
    987 			return (0);
    988 		}
    989 	}
    990 
    991 	if (cred != 0) {
    992 		lc->cred = sdup(myself, T, cred);
    993 		if (lc->cred == 0) {
    994 			(void) freeCon(lc);
    995 			return (0);
    996 		}
    997 	}
    998 
    999 	lc->method = method;
   1000 	lc->port = port;
   1001 
   1002 	lc->bindTimeout = proxyInfo.bind_timeout;
   1003 	lc->searchTimeout = proxyInfo.search_timeout;
   1004 	lc->modifyTimeout = proxyInfo.modify_timeout;
   1005 	lc->addTimeout = proxyInfo.add_timeout;
   1006 	lc->deleteTimeout = proxyInfo.delete_timeout;
   1007 
   1008 	/* All other fields OK at zero */
   1009 
   1010 	releaseLC(lc);
   1011 
   1012 	return (lc);
   1013 }
   1014 
   1015 static int
   1016 setupConList(char *serverList, char *who, char *cred, auth_method_t method) {
   1017 	char			*sls, *sl, *s, *e;
   1018 	__nis_ldap_conn_t	*lc, *tmp;
   1019 	char			*myself = "setupConList";
   1020 
   1021 	if (serverList == 0)
   1022 		return (LDAP_PARAM_ERROR);
   1023 
   1024 	(void) rw_wrlock(&ldapConLock);
   1025 
   1026 	if (ldapCon != 0) {
   1027 		/* Assume we've already been called and done the set-up */
   1028 		(void) rw_unlock(&ldapConLock);
   1029 		return (LDAP_SUCCESS);
   1030 	}
   1031 
   1032 	/* Work on a copy of 'serverList' */
   1033 	sl = sls = sdup(myself, T, serverList);
   1034 	if (sl == 0) {
   1035 		(void) rw_unlock(&ldapConLock);
   1036 		return (LDAP_NO_MEMORY);
   1037 	}
   1038 
   1039 	/* Remove leading white space */
   1040 	for (0; *sl == ' ' || *sl == '\t'; sl++);
   1041 
   1042 	/* Create connection for each server on the list */
   1043 	for (s = sl; *s != '\0'; s = e+1) {
   1044 		int	l;
   1045 
   1046 		/* Find end of server/port token */
   1047 		for (e = s; *e != ' ' && *e != '\t' && *e != '\0'; e++);
   1048 		if (*e != '\0')
   1049 			*e = '\0';
   1050 		else
   1051 			e--;
   1052 		l = slen(s);
   1053 
   1054 		if (l > 0) {
   1055 			lc = createCon(s, who, cred, method, 0);
   1056 			if (lc == 0) {
   1057 				free(sls);
   1058 				(void) rw_unlock(&ldapConLock);
   1059 				return (LDAP_NO_MEMORY);
   1060 			}
   1061 			lc->onList = 1;
   1062 			if (ldapCon == 0) {
   1063 				ldapCon = lc;
   1064 			} else {
   1065 				/* Insert at end of list */
   1066 				for (tmp = ldapCon; tmp->next != 0;
   1067 					tmp = tmp->next);
   1068 				tmp->next = lc;
   1069 			}
   1070 		}
   1071 	}
   1072 
   1073 	free(sls);
   1074 
   1075 	(void) rw_unlock(&ldapConLock);
   1076 
   1077 	return (LDAP_SUCCESS);
   1078 }
   1079 
   1080 static bool_t
   1081 is_same_connection(__nis_ldap_conn_t *lc, LDAPURLDesc *ludpp)
   1082 {
   1083 	return (strcasecmp(ludpp->lud_host, lc->sp) == 0 &&
   1084 		ludpp->lud_port == lc->port);
   1085 }
   1086 
   1087 static __nis_ldap_conn_t *
   1088 find_connection_from_list(__nis_ldap_conn_t *list,
   1089 			LDAPURLDesc *ludpp, int *stat)
   1090 {
   1091 	int			ldapStat;
   1092 	__nis_ldap_conn_t	*lc	= NULL;
   1093 	if (stat == 0)
   1094 		stat = &ldapStat;
   1095 
   1096 	*stat = LDAP_SUCCESS;
   1097 
   1098 	for (lc = list; lc != 0; lc = lc->next) {
   1099 		exclusiveLC(lc);
   1100 		if (is_same_connection(lc, ludpp)) {
   1101 			if (!lc->isBound) {
   1102 				*stat = connectCon(lc, 1);
   1103 				if (*stat != LDAP_SUCCESS) {
   1104 					releaseLC(lc);
   1105 					continue;
   1106 				}
   1107 			} else if (lc->doDis || lc->doDel) {
   1108 				(void) disconnectCon(lc);
   1109 				releaseLC(lc);
   1110 				continue;
   1111 			}
   1112 			incrementRC(lc);
   1113 			releaseLC(lc