xref: /onnv-gate/usr/src/lib/libnisdb/ldap_op.c (revision 8035:86852a46987e)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*8035SSreedhar.Chalamalasetti@Sun.COM  * Common Development and Distribution License (the "License").
6*8035SSreedhar.Chalamalasetti@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*8035SSreedhar.Chalamalasetti@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <synch.h>
270Sstevel@tonic-gate #include <strings.h>
280Sstevel@tonic-gate #include <sys/time.h>
290Sstevel@tonic-gate #include <ctype.h>
300Sstevel@tonic-gate 
310Sstevel@tonic-gate #include "ldap_op.h"
320Sstevel@tonic-gate #include "ldap_util.h"
330Sstevel@tonic-gate #include "ldap_structs.h"
340Sstevel@tonic-gate #include "ldap_ruleval.h"
350Sstevel@tonic-gate #include "ldap_attr.h"
360Sstevel@tonic-gate #include "ldap_print.h"
370Sstevel@tonic-gate #include "ldap_glob.h"
380Sstevel@tonic-gate 
390Sstevel@tonic-gate #include "nis_parse_ldap_conf.h"
400Sstevel@tonic-gate 
410Sstevel@tonic-gate #ifndef LDAPS_PORT
420Sstevel@tonic-gate #define	LDAPS_PORT	636
430Sstevel@tonic-gate #endif
440Sstevel@tonic-gate 
45702Sth160488 static int setupConList(char *serverList, char *who,
46702Sth160488 			char *cred, auth_method_t method);
47702Sth160488 
48702Sth160488 
490Sstevel@tonic-gate /*
500Sstevel@tonic-gate  * Build one of our internal LDAP search structures, containing copies of
510Sstevel@tonic-gate  * the supplied input. return NULL in case of error.
520Sstevel@tonic-gate  *
530Sstevel@tonic-gate  * If 'filter' is NULL, build an AND-filter using the filter components.
540Sstevel@tonic-gate  */
550Sstevel@tonic-gate __nis_ldap_search_t *
buildLdapSearch(char * base,int scope,int numFilterComps,char ** filterComp,char * filter,char ** attrs,int attrsonly,int isDN)560Sstevel@tonic-gate buildLdapSearch(char *base, int scope, int numFilterComps, char **filterComp,
570Sstevel@tonic-gate 		char *filter, char **attrs, int attrsonly, int isDN) {
580Sstevel@tonic-gate 	__nis_ldap_search_t	*ls;
590Sstevel@tonic-gate 	char			**a;
600Sstevel@tonic-gate 	int			i, na, err = 0;
610Sstevel@tonic-gate 	char			*myself = "buildLdapSearch";
620Sstevel@tonic-gate 
630Sstevel@tonic-gate 	ls = am(myself, sizeof (*ls));
640Sstevel@tonic-gate 	if (ls == 0)
650Sstevel@tonic-gate 		return (0);
660Sstevel@tonic-gate 
670Sstevel@tonic-gate 	ls->base = sdup(myself, T, base);
680Sstevel@tonic-gate 	if (ls->base == 0 && base != 0)
690Sstevel@tonic-gate 		err++;
700Sstevel@tonic-gate 	ls->scope = scope;
710Sstevel@tonic-gate 
720Sstevel@tonic-gate 	if (filterComp != 0 && numFilterComps > 0) {
730Sstevel@tonic-gate 		ls->filterComp = am(myself, numFilterComps *
740Sstevel@tonic-gate 					sizeof (ls->filterComp[0]));
750Sstevel@tonic-gate 		if (ls->filterComp == 0) {
760Sstevel@tonic-gate 			err++;
770Sstevel@tonic-gate 			numFilterComps = 0;
780Sstevel@tonic-gate 		}
790Sstevel@tonic-gate 		for (i = 0; i < numFilterComps; i++) {
800Sstevel@tonic-gate 			ls->filterComp[i] = sdup(myself, T, filterComp[i]);
810Sstevel@tonic-gate 			if (ls->filterComp[i] == 0 && filterComp[i] != 0)
820Sstevel@tonic-gate 				err++;
830Sstevel@tonic-gate 		}
840Sstevel@tonic-gate 		ls->numFilterComps = numFilterComps;
850Sstevel@tonic-gate 		if (filter == 0) {
860Sstevel@tonic-gate 			ls->filter = concatenateFilterComps(ls->numFilterComps,
870Sstevel@tonic-gate 					ls->filterComp);
880Sstevel@tonic-gate 			if (ls->filter == 0)
890Sstevel@tonic-gate 				err++;
900Sstevel@tonic-gate 		}
910Sstevel@tonic-gate 	} else {
920Sstevel@tonic-gate 		ls->filterComp = 0;
930Sstevel@tonic-gate 		ls->numFilterComps = 0;
940Sstevel@tonic-gate 		ls->filter = sdup(myself, T, filter);
950Sstevel@tonic-gate 		if (ls->filter == 0 && filter != 0)
960Sstevel@tonic-gate 			err++;
970Sstevel@tonic-gate 	}
980Sstevel@tonic-gate 
990Sstevel@tonic-gate 	if (attrs != 0) {
1000Sstevel@tonic-gate 		for (na = 0, a = attrs; *a != 0; a++, na++);
1010Sstevel@tonic-gate 		ls->attrs = am(myself, (na + 1) * sizeof (ls->attrs[0]));
1020Sstevel@tonic-gate 		if (ls->attrs != 0) {
1030Sstevel@tonic-gate 			for (i = 0; i < na; i++) {
1040Sstevel@tonic-gate 				ls->attrs[i] = sdup(myself, T, attrs[i]);
1050Sstevel@tonic-gate 				if (ls->attrs[i] == 0 && attrs[i] != 0)
1060Sstevel@tonic-gate 					err++;
1070Sstevel@tonic-gate 			}
1080Sstevel@tonic-gate 			ls->attrs[na] = 0;
1090Sstevel@tonic-gate 			ls->numAttrs = na;
1100Sstevel@tonic-gate 		} else {
1110Sstevel@tonic-gate 			err++;
1120Sstevel@tonic-gate 		}
1130Sstevel@tonic-gate 	} else {
1140Sstevel@tonic-gate 		ls->attrs = 0;
1150Sstevel@tonic-gate 		ls->numAttrs = 0;
1160Sstevel@tonic-gate 	}
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate 	ls->attrsonly = attrsonly;
1190Sstevel@tonic-gate 	ls->isDN = isDN;
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 	if (err > 0) {
1220Sstevel@tonic-gate 		freeLdapSearch(ls);
1230Sstevel@tonic-gate 		ls = 0;
1240Sstevel@tonic-gate 	}
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 	return (ls);
1270Sstevel@tonic-gate }
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate void
freeLdapSearch(__nis_ldap_search_t * ls)1300Sstevel@tonic-gate freeLdapSearch(__nis_ldap_search_t *ls) {
1310Sstevel@tonic-gate 	int	i;
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate 	if (ls == 0)
1340Sstevel@tonic-gate 		return;
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate 	sfree(ls->base);
1370Sstevel@tonic-gate 	if (ls->filterComp != 0) {
1380Sstevel@tonic-gate 		for (i = 0; i < ls->numFilterComps; i++) {
1390Sstevel@tonic-gate 			sfree(ls->filterComp[i]);
1400Sstevel@tonic-gate 		}
1410Sstevel@tonic-gate 		sfree(ls->filterComp);
1420Sstevel@tonic-gate 	}
1430Sstevel@tonic-gate 	sfree(ls->filter);
1440Sstevel@tonic-gate 	if (ls->attrs != 0) {
1450Sstevel@tonic-gate 		for (i = 0; i < ls->numAttrs; i++) {
1460Sstevel@tonic-gate 			sfree(ls->attrs[i]);
1470Sstevel@tonic-gate 		}
1480Sstevel@tonic-gate 		sfree(ls->attrs);
1490Sstevel@tonic-gate 	}
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	free(ls);
1520Sstevel@tonic-gate }
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate /*
1550Sstevel@tonic-gate  * Given a table mapping, and a rule/value pointer,
1560Sstevel@tonic-gate  * return an LDAP search structure with values suitable for use
1570Sstevel@tonic-gate  * by ldap_search() or (if dn != 0) ldap_modify(). The rule/value
1580Sstevel@tonic-gate  * may be modified.
1590Sstevel@tonic-gate  *
1600Sstevel@tonic-gate  * If dn != 0 and *dn == 0, the function attemps to return a pointer
1610Sstevel@tonic-gate  * to the DN. This may necessitate an ldapSearch, if the rule set doesn't
1620Sstevel@tonic-gate  * produce a DN directly.
1630Sstevel@tonic-gate  *
1640Sstevel@tonic-gate  * if dn == 0, and the rule set produces a DN as well as other attribute/
1650Sstevel@tonic-gate  * value pairs, the function returns an LDAP search structure with the
1660Sstevel@tonic-gate  * DN only.
1670Sstevel@tonic-gate  *
1680Sstevel@tonic-gate  * If 'fromLDAP' is set, the caller wants base/scope/filter from
1690Sstevel@tonic-gate  * t->objectDN->read; otherwise, from t->objectDN->write.
1700Sstevel@tonic-gate  *
1710Sstevel@tonic-gate  * If 'rv' is NULL, the caller wants an enumeration of the container.
1720Sstevel@tonic-gate  *
1730Sstevel@tonic-gate  * Note that this function only creates a search structure for 't' itself;
1740Sstevel@tonic-gate  * if there are alternative mappings for the table, those must be handled
1750Sstevel@tonic-gate  * by our caller.
1760Sstevel@tonic-gate  */
1770Sstevel@tonic-gate __nis_ldap_search_t *
createLdapRequest(__nis_table_mapping_t * t,__nis_rule_value_t * rv,char ** dn,int fromLDAP,int * res,__nis_object_dn_t * obj_dn)1780Sstevel@tonic-gate createLdapRequest(__nis_table_mapping_t *t,
1790Sstevel@tonic-gate 		__nis_rule_value_t *rv, char **dn, int fromLDAP,
1800Sstevel@tonic-gate 		int *res, __nis_object_dn_t *obj_dn) {
1810Sstevel@tonic-gate 	int			i, j;
1820Sstevel@tonic-gate 	__nis_ldap_search_t	*ls = 0;
1830Sstevel@tonic-gate 	char			**locDN;
1840Sstevel@tonic-gate 	int			numLocDN, stat = 0, count = 0;
1850Sstevel@tonic-gate 	char			*myself = "createLdapRequest";
1860Sstevel@tonic-gate 	__nis_object_dn_t 	*objectDN = NULL;
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	if (t == 0)
1890Sstevel@tonic-gate 		return (0);
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	if (obj_dn == NULL)
1920Sstevel@tonic-gate 		objectDN = t->objectDN;
1930Sstevel@tonic-gate 	else
1940Sstevel@tonic-gate 		objectDN = obj_dn;
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	if (rv == 0) {
1970Sstevel@tonic-gate 		char	*base;
1980Sstevel@tonic-gate 		char	*filter;
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 		if (fromLDAP) {
2010Sstevel@tonic-gate 			base = objectDN->read.base;
2020Sstevel@tonic-gate 			filter = makeFilter(objectDN->read.attrs);
2030Sstevel@tonic-gate 		} else {
2040Sstevel@tonic-gate 			base = objectDN->write.base;
2050Sstevel@tonic-gate 			filter = makeFilter(objectDN->write.attrs);
2060Sstevel@tonic-gate 		}
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 		/* Create request to enumerate container */
2090Sstevel@tonic-gate 		ls = buildLdapSearch(base, objectDN->read.scope, 0, 0, filter,
2100Sstevel@tonic-gate 					0, 0, 0);
2110Sstevel@tonic-gate 		sfree(filter);
2120Sstevel@tonic-gate 		return (ls);
2130Sstevel@tonic-gate 	}
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate 	for (i = 0; i < t->numRulesToLDAP; i++) {
2160Sstevel@tonic-gate 		rv = addLdapRuleValue(t, t->ruleToLDAP[i],
2170Sstevel@tonic-gate 				mit_ldap, mit_nisplus, rv, !fromLDAP, &stat);
2180Sstevel@tonic-gate 		if (rv == 0)
2190Sstevel@tonic-gate 			return (0);
2200Sstevel@tonic-gate 		if (stat == NP_LDAP_RULES_NO_VALUE)
2210Sstevel@tonic-gate 			count++;
2220Sstevel@tonic-gate 		stat = 0;
2230Sstevel@tonic-gate 	}
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	/*
2260Sstevel@tonic-gate 	 * If none of the rules produced a value despite
2270Sstevel@tonic-gate 	 * having enough NIS+ columns, return error.
2280Sstevel@tonic-gate 	 */
2290Sstevel@tonic-gate 	if (rv->numAttrs == 0 && count > 0) {
2300Sstevel@tonic-gate 		*res = NP_LDAP_RULES_NO_VALUE;
2310Sstevel@tonic-gate 		return (0);
2320Sstevel@tonic-gate 	}
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	/*
2350Sstevel@tonic-gate 	 * 'rv' now contains everything we know about the attributes and
2360Sstevel@tonic-gate 	 * values. Build an LDAP search structure from it.
2370Sstevel@tonic-gate 	 */
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	/* Look for a single-valued DN */
2400Sstevel@tonic-gate 	locDN = findDNs(myself, rv, 1,
2410Sstevel@tonic-gate 			fromLDAP ? objectDN->read.base :
2420Sstevel@tonic-gate 					objectDN->write.base,
2430Sstevel@tonic-gate 			&numLocDN);
2440Sstevel@tonic-gate 	if (locDN != 0 && numLocDN == 1) {
2450Sstevel@tonic-gate 		if (dn != 0 && *dn == 0) {
2460Sstevel@tonic-gate 			*dn = locDN[0];
2470Sstevel@tonic-gate 			sfree(locDN);
2480Sstevel@tonic-gate 		} else {
2490Sstevel@tonic-gate 			char	*filter;
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 			if (fromLDAP)
2520Sstevel@tonic-gate 				filter = makeFilter(objectDN->read.attrs);
2530Sstevel@tonic-gate 			else
2540Sstevel@tonic-gate 				filter = makeFilter(objectDN->write.attrs);
2550Sstevel@tonic-gate 			ls = buildLdapSearch(locDN[0], LDAP_SCOPE_BASE, 0, 0,
2560Sstevel@tonic-gate 						filter, 0, 0, 1);
2570Sstevel@tonic-gate 			sfree(filter);
2580Sstevel@tonic-gate 			freeDNs(locDN, numLocDN);
2590Sstevel@tonic-gate 		}
2600Sstevel@tonic-gate 	} else {
2610Sstevel@tonic-gate 		freeDNs(locDN, numLocDN);
2620Sstevel@tonic-gate 	}
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 	if (ls != 0) {
2650Sstevel@tonic-gate 		ls->useCon = 1;
2660Sstevel@tonic-gate 		return (ls);
2670Sstevel@tonic-gate 	}
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	/*
2700Sstevel@tonic-gate 	 * No DN, or caller wanted a search structure with the non-DN
2710Sstevel@tonic-gate 	 * attributes.
2720Sstevel@tonic-gate 	 */
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	/* Initialize search structure */
2750Sstevel@tonic-gate 	{
2760Sstevel@tonic-gate 		char	*filter = (fromLDAP) ?
2770Sstevel@tonic-gate 				makeFilter(objectDN->read.attrs) :
2780Sstevel@tonic-gate 				makeFilter(objectDN->write.attrs);
2790Sstevel@tonic-gate 		char	**ofc;
2800Sstevel@tonic-gate 		int	nofc = 0;
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 		ofc = makeFilterComp(filter, &nofc);
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 		if (filter != 0 && ofc == 0) {
2850Sstevel@tonic-gate 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
2860Sstevel@tonic-gate 			"%s: Unable to break filter into components: \"%s\"",
2870Sstevel@tonic-gate 				myself, NIL(filter));
2880Sstevel@tonic-gate 			sfree(filter);
2890Sstevel@tonic-gate 			return (0);
2900Sstevel@tonic-gate 		}
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 		if (fromLDAP)
2930Sstevel@tonic-gate 			ls = buildLdapSearch(objectDN->read.base,
2940Sstevel@tonic-gate 				objectDN->read.scope,
2950Sstevel@tonic-gate 				nofc, ofc, 0, 0, 0, 0);
2960Sstevel@tonic-gate 		else
2970Sstevel@tonic-gate 			ls = buildLdapSearch(objectDN->write.base,
2980Sstevel@tonic-gate 				objectDN->write.scope,
2990Sstevel@tonic-gate 				nofc, ofc, 0, 0, 0, 0);
3000Sstevel@tonic-gate 		sfree(filter);
3010Sstevel@tonic-gate 		freeFilterComp(ofc, nofc);
3020Sstevel@tonic-gate 		if (ls == 0)
3030Sstevel@tonic-gate 			return (0);
3040Sstevel@tonic-gate 	}
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	/* Build and add the filter components */
3070Sstevel@tonic-gate 	for (i = 0; i < rv->numAttrs; i++) {
3080Sstevel@tonic-gate 		/* Skip DN */
3090Sstevel@tonic-gate 		if (strcasecmp("dn", rv->attrName[i]) == 0)
3100Sstevel@tonic-gate 			continue;
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 		/* Skip vt_ber values */
3130Sstevel@tonic-gate 		if (rv->attrVal[i].type == vt_ber)
3140Sstevel@tonic-gate 			continue;
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 		for (j = 0; j < rv->attrVal[i].numVals; j++) {
3170Sstevel@tonic-gate 			__nis_buffer_t	b = {0, 0};
3180Sstevel@tonic-gate 			char		**tmpComp;
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 			bp2buf(myself, &b, "%s=%s",
3210Sstevel@tonic-gate 				rv->attrName[i], rv->attrVal[i].val[j].value);
3220Sstevel@tonic-gate 			tmpComp = addFilterComp(b.buf, ls->filterComp,
3230Sstevel@tonic-gate 						&ls->numFilterComps);
3240Sstevel@tonic-gate 			if (tmpComp == 0) {
3250Sstevel@tonic-gate 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
3260Sstevel@tonic-gate 				"%s: Unable to add filter component \"%s\"",
3270Sstevel@tonic-gate 					myself, NIL(b.buf));
3280Sstevel@tonic-gate 				sfree(b.buf);
3290Sstevel@tonic-gate 				freeLdapSearch(ls);
3300Sstevel@tonic-gate 				return (0);
3310Sstevel@tonic-gate 			}
3320Sstevel@tonic-gate 			ls->filterComp = tmpComp;
3330Sstevel@tonic-gate 			sfree(b.buf);
3340Sstevel@tonic-gate 		}
3350Sstevel@tonic-gate 	}
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 	if (ls->numFilterComps > 0) {
3380Sstevel@tonic-gate 		sfree(ls->filter);
3390Sstevel@tonic-gate 		ls->filter = concatenateFilterComps(ls->numFilterComps,
3400Sstevel@tonic-gate 							ls->filterComp);
3410Sstevel@tonic-gate 		if (ls->filter == 0) {
3420Sstevel@tonic-gate 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
3430Sstevel@tonic-gate 			"%s: Unable to concatenate filter components",
3440Sstevel@tonic-gate 				myself);
3450Sstevel@tonic-gate 			freeLdapSearch(ls);
3460Sstevel@tonic-gate 			return (0);
3470Sstevel@tonic-gate 		}
3480Sstevel@tonic-gate 	}
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	if (dn != 0 && *dn == 0) {
3510Sstevel@tonic-gate 		/*
3520Sstevel@tonic-gate 		 * The caller wants a DN, but we didn't get one from the
3530Sstevel@tonic-gate 		 * the rule set. We have an 'ls', so use it to ldapSearch()
3540Sstevel@tonic-gate 		 * for an entry from which we can extract the DN.
3550Sstevel@tonic-gate 		 */
3560Sstevel@tonic-gate 		__nis_rule_value_t	*rvtmp;
3570Sstevel@tonic-gate 		char			**locDN;
3580Sstevel@tonic-gate 		int			nv = 0, numLocDN;
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 		rvtmp = ldapSearch(ls, &nv, 0, 0);
3610Sstevel@tonic-gate 		locDN = findDNs(myself, rvtmp, nv, 0, &numLocDN);
3620Sstevel@tonic-gate 		if (locDN != 0 && numLocDN == 1) {
3630Sstevel@tonic-gate 			*dn = locDN[0];
3640Sstevel@tonic-gate 			sfree(locDN);
3650Sstevel@tonic-gate 		} else {
3660Sstevel@tonic-gate 			freeDNs(locDN, numLocDN);
3670Sstevel@tonic-gate 		}
3680Sstevel@tonic-gate 		freeRuleValue(rvtmp, nv);
3690Sstevel@tonic-gate 	}
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate 	ls->useCon = 1;
3720Sstevel@tonic-gate 	return (ls);
3730Sstevel@tonic-gate }
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate int	ldapConnAttemptRetryTimeout = 60;	/* seconds */
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate typedef struct {
3780Sstevel@tonic-gate 	LDAP		*ld;
3790Sstevel@tonic-gate 	mutex_t		mutex;		/* Mutex for update of structure */
3800Sstevel@tonic-gate 	pthread_t	owner;		/* Thread holding mutex */
3810Sstevel@tonic-gate 	mutex_t		rcMutex;	/* Mutex for refCount */
3820Sstevel@tonic-gate 	int		refCount;	/* Reference count */
3830Sstevel@tonic-gate 	int		isBound;	/* Is connection open and usable ? */
3840Sstevel@tonic-gate 	time_t		retryTime;	/* When should open be retried */
3850Sstevel@tonic-gate 	int		status;		/* Status of last operation */
3860Sstevel@tonic-gate 	int		doDis;		/* To be disconnected if refCount==0 */
3870Sstevel@tonic-gate 	int		doDel;		/* To be deleted if refCount zero */
3880Sstevel@tonic-gate 	int		onList;		/* True if on the 'ldapCon' list */
3890Sstevel@tonic-gate 	char		*sp;		/* server string */
3900Sstevel@tonic-gate 	char		*who;
3910Sstevel@tonic-gate 	char		*cred;
3920Sstevel@tonic-gate 	auth_method_t	method;
3930Sstevel@tonic-gate 	int		port;
3940Sstevel@tonic-gate 	struct timeval	bindTimeout;
3950Sstevel@tonic-gate 	struct timeval	searchTimeout;
3960Sstevel@tonic-gate 	struct timeval	modifyTimeout;
3970Sstevel@tonic-gate 	struct timeval	addTimeout;
3980Sstevel@tonic-gate 	struct timeval	deleteTimeout;
3990Sstevel@tonic-gate 	int		simplePage;	/* Can do simple-page */
4000Sstevel@tonic-gate 	int		vlv;		/* Can do VLV */
4010Sstevel@tonic-gate 	uint_t		batchFrom;	/* # entries read in one operation */
4020Sstevel@tonic-gate 	void		*next;
4030Sstevel@tonic-gate } __nis_ldap_conn_t;
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate /*
4060Sstevel@tonic-gate  * List of connections, 'ldapCon', protected by an RW lock.
4070Sstevel@tonic-gate  *
4080Sstevel@tonic-gate  * The following locking scheme is used:
4090Sstevel@tonic-gate  *
4100Sstevel@tonic-gate  * (1)	Find a connection structure to use to talk to LDAP
4110Sstevel@tonic-gate  *		Rlock list
4120Sstevel@tonic-gate  *			Locate structure
4130Sstevel@tonic-gate  *			Acquire 'mutex'
4140Sstevel@tonic-gate  *				Acquire 'rcMutex'
4150Sstevel@tonic-gate  *					update refCount
4160Sstevel@tonic-gate  *				Release 'rcMutex'
4170Sstevel@tonic-gate  *			release 'mutex'
4180Sstevel@tonic-gate  *		Unlock list
4190Sstevel@tonic-gate  *		Use structure
4200Sstevel@tonic-gate  *		Release structure when done
4210Sstevel@tonic-gate  * (2)	Insert/delete structure(s) on/from list
4220Sstevel@tonic-gate  *		Wlock list
4230Sstevel@tonic-gate  *			Insert/delete structure; if deleting, must
4240Sstevel@tonic-gate  *			acquire 'mutex', and 'rcMutex' (in that order),
4250Sstevel@tonic-gate  *			and 'refCount' must be zero.
4260Sstevel@tonic-gate  *		Unlock list
4270Sstevel@tonic-gate  * (3)	Modify structure
4280Sstevel@tonic-gate  *		Find structure
4290Sstevel@tonic-gate  *		Acquire 'mutex'
4300Sstevel@tonic-gate  *			Modify (except refCount)
4310Sstevel@tonic-gate  *		Release 'mutex'
4320Sstevel@tonic-gate  *		Release structure
4330Sstevel@tonic-gate  */
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate __nis_ldap_conn_t		*ldapCon = 0;
4360Sstevel@tonic-gate __nis_ldap_conn_t		*ldapReferralCon = 0;
4370Sstevel@tonic-gate static rwlock_t			ldapConLock = DEFAULTRWLOCK;
4380Sstevel@tonic-gate static rwlock_t			referralConLock = DEFAULTRWLOCK;
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate void
exclusiveLC(__nis_ldap_conn_t * lc)4410Sstevel@tonic-gate exclusiveLC(__nis_ldap_conn_t *lc) {
4420Sstevel@tonic-gate 	pthread_t	me = pthread_self();
4430Sstevel@tonic-gate 	int		stat;
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 	if (lc == 0)
4460Sstevel@tonic-gate 		return;
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 	stat = mutex_trylock(&lc->mutex);
4490Sstevel@tonic-gate 	if (stat == EBUSY && lc->owner != me)
4500Sstevel@tonic-gate 		mutex_lock(&lc->mutex);
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	lc->owner = me;
4530Sstevel@tonic-gate }
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate /* Return 1 if mutex held by this thread, 0 otherwise */
4560Sstevel@tonic-gate int
assertExclusive(__nis_ldap_conn_t * lc)4570Sstevel@tonic-gate assertExclusive(__nis_ldap_conn_t *lc) {
4580Sstevel@tonic-gate 	pthread_t	me;
4590Sstevel@tonic-gate 	int		stat;
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	if (lc == 0)
4620Sstevel@tonic-gate 		return (0);
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	stat = mutex_trylock(&lc->mutex);
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	if (stat == 0) {
4670Sstevel@tonic-gate 		mutex_unlock(&lc->mutex);
4680Sstevel@tonic-gate 		return (0);
4690Sstevel@tonic-gate 	}
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 	me = pthread_self();
4720Sstevel@tonic-gate 	if (stat != EBUSY || lc->owner != me)
4730Sstevel@tonic-gate 		return (0);
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	return (1);
4760Sstevel@tonic-gate }
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate void
releaseLC(__nis_ldap_conn_t * lc)4790Sstevel@tonic-gate releaseLC(__nis_ldap_conn_t *lc) {
4800Sstevel@tonic-gate 	pthread_t	me = pthread_self();
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	if (lc == 0 || lc->owner != me)
4830Sstevel@tonic-gate 		return;
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	lc->owner = 0;
4860Sstevel@tonic-gate 	(void) mutex_unlock(&lc->mutex);
4870Sstevel@tonic-gate }
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate void
incrementRC(__nis_ldap_conn_t * lc)4900Sstevel@tonic-gate incrementRC(__nis_ldap_conn_t *lc) {
4910Sstevel@tonic-gate 	if (lc == 0)
4920Sstevel@tonic-gate 		return;
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 	(void) mutex_lock(&lc->rcMutex);
4950Sstevel@tonic-gate 	lc->refCount++;
4960Sstevel@tonic-gate 	(void) mutex_unlock(&lc->rcMutex);
4970Sstevel@tonic-gate }
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate void
decrementRC(__nis_ldap_conn_t * lc)5000Sstevel@tonic-gate decrementRC(__nis_ldap_conn_t *lc) {
5010Sstevel@tonic-gate 	if (lc == 0)
5020Sstevel@tonic-gate 		return;
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 	(void) mutex_lock(&lc->rcMutex);
5050Sstevel@tonic-gate 	if (lc->refCount > 0)
5060Sstevel@tonic-gate 		lc->refCount--;
5070Sstevel@tonic-gate 	(void) mutex_unlock(&lc->rcMutex);
5080Sstevel@tonic-gate }
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate /* Accept a server/port indication, and call ldap_init() */
5110Sstevel@tonic-gate static LDAP *
ldapInit(char * srv,int port,bool_t use_ssl)5120Sstevel@tonic-gate ldapInit(char *srv, int port, bool_t use_ssl) {
5130Sstevel@tonic-gate 	LDAP			*ld;
5140Sstevel@tonic-gate 	int			ldapVersion = LDAP_VERSION3;
5150Sstevel@tonic-gate 	int			derefOption = LDAP_DEREF_ALWAYS;
5160Sstevel@tonic-gate 	int			timelimit = proxyInfo.search_time_limit;
5170Sstevel@tonic-gate 	int			sizelimit = proxyInfo.search_size_limit;
5180Sstevel@tonic-gate 	char			*myself = "ldapInit";
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 	if (srv == 0)
5210Sstevel@tonic-gate 		return (0);
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	if (use_ssl) {
5240Sstevel@tonic-gate 		ld = ldapssl_init(srv, port, 1);
5250Sstevel@tonic-gate 	} else {
5260Sstevel@tonic-gate 		ld = ldap_init(srv, port);
5270Sstevel@tonic-gate 	}
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 	if (ld != 0) {
5300Sstevel@tonic-gate 		(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
5310Sstevel@tonic-gate 					&ldapVersion);
5320Sstevel@tonic-gate 		(void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
5330Sstevel@tonic-gate 		(void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
5340Sstevel@tonic-gate 		(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &timelimit);
5350Sstevel@tonic-gate 		(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit);
5360Sstevel@tonic-gate 		(void) ldap_set_option(ld, LDAP_OPT_REBIND_ARG, 0);
5370Sstevel@tonic-gate 	}
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 	return (ld);
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate /*
5430Sstevel@tonic-gate  * Bind the specified LDAP structure per the supplied authentication.
5440Sstevel@tonic-gate  * Note: tested with none, simple, and digest_md5. May or may not
5450Sstevel@tonic-gate  * work with other authentication methods, mostly depending on whether
5460Sstevel@tonic-gate  * or not 'who' and 'cred' contain sufficient information.
5470Sstevel@tonic-gate  */
5480Sstevel@tonic-gate static int
ldapBind(LDAP ** ldP,char * who,char * cred,auth_method_t method,struct timeval timeout)5490Sstevel@tonic-gate ldapBind(LDAP **ldP, char *who, char *cred, auth_method_t method,
5500Sstevel@tonic-gate 		struct timeval timeout) {
5510Sstevel@tonic-gate 	int		ret;
5520Sstevel@tonic-gate 	LDAP		*ld;
5530Sstevel@tonic-gate 	char		*myself = "ldapBind";
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	if (ldP == 0 || (ld = *ldP) == 0)
5560Sstevel@tonic-gate 		return (LDAP_PARAM_ERROR);
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 	if (method == none) {
5590Sstevel@tonic-gate 		/* No ldap_bind() required (or even possible) */
5600Sstevel@tonic-gate 		ret = LDAP_SUCCESS;
5610Sstevel@tonic-gate 	} else if (method == simple) {
5620Sstevel@tonic-gate 		struct timeval	tv;
5630Sstevel@tonic-gate 		LDAPMessage	*msg = 0;
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 		tv = timeout;
5660Sstevel@tonic-gate 		ret = ldap_bind(ld, who, cred, LDAP_AUTH_SIMPLE);
5670Sstevel@tonic-gate 		if (ret != -1) {
5680Sstevel@tonic-gate 			ret = ldap_result(ld, ret, 0, &tv, &msg);
5690Sstevel@tonic-gate 			if (ret == 0) {
5700Sstevel@tonic-gate 				ret = LDAP_TIMEOUT;
5710Sstevel@tonic-gate 			} else if (ret == -1) {
5720Sstevel@tonic-gate 				(void) ldap_get_option(ld,
5730Sstevel@tonic-gate 							LDAP_OPT_ERROR_NUMBER,
5740Sstevel@tonic-gate 							&ret);
5750Sstevel@tonic-gate 			} else {
5760Sstevel@tonic-gate 				ret = ldap_result2error(ld, msg, 0);
5770Sstevel@tonic-gate 			}
5780Sstevel@tonic-gate 			if (msg != 0)
5790Sstevel@tonic-gate 				(void) ldap_msgfree(msg);
5800Sstevel@tonic-gate 		} else {
5810Sstevel@tonic-gate 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
5820Sstevel@tonic-gate 						&ret);
5830Sstevel@tonic-gate 		}
5840Sstevel@tonic-gate 	} else if (method == cram_md5) {
5850Sstevel@tonic-gate 		/* Note: there is only a synchronous call for cram-md5 */
5860Sstevel@tonic-gate 		struct berval ber_cred;
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 		ber_cred.bv_len = strlen(cred);
5890Sstevel@tonic-gate 		ber_cred.bv_val = cred;
5900Sstevel@tonic-gate 		ret = ldap_sasl_cram_md5_bind_s(ld, who, &ber_cred, NULL, NULL);
5910Sstevel@tonic-gate 	} else if (method == digest_md5) {
5920Sstevel@tonic-gate 		/* Note: there is only a synchronous call for digest-md5 */
5930Sstevel@tonic-gate 		struct berval ber_cred;
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 		ber_cred.bv_len = strlen(cred);
5960Sstevel@tonic-gate 		ber_cred.bv_val = cred;
5970Sstevel@tonic-gate 		ret = ldap_x_sasl_digest_md5_bind_s(ld, who, &ber_cred, NULL,
5980Sstevel@tonic-gate 			NULL);
5990Sstevel@tonic-gate 	} else {
6000Sstevel@tonic-gate 		ret = LDAP_AUTH_METHOD_NOT_SUPPORTED;
6010Sstevel@tonic-gate 	}
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 	if (ret != LDAP_SUCCESS) {
6040Sstevel@tonic-gate 		(void) ldap_unbind_s(ld);
6050Sstevel@tonic-gate 		*ldP = 0;
6060Sstevel@tonic-gate 		logmsg(MSG_NOTIMECHECK, LOG_WARNING,
6070Sstevel@tonic-gate 			"%s: Unable to bind as: %s: %s",
6080Sstevel@tonic-gate 			myself, who, ldap_err2string(ret));
6090Sstevel@tonic-gate 	}
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate 	return (ret);
6120Sstevel@tonic-gate }
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate /*
6150Sstevel@tonic-gate  * Free 'lc' and all related memory. Caller must hold the exclusive lock.
6160Sstevel@tonic-gate  * Return LDAP_UNAVAILABLE upon success, in which case the caller mustn't
6170Sstevel@tonic-gate  * try to use the structure pointer in any way.
6180Sstevel@tonic-gate  */
6190Sstevel@tonic-gate static int
freeCon(__nis_ldap_conn_t * lc)6200Sstevel@tonic-gate freeCon(__nis_ldap_conn_t *lc) {
6210Sstevel@tonic-gate 	char			*myself = "freeCon";
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 	if (!assertExclusive(lc))
6240Sstevel@tonic-gate 		return (LDAP_PARAM_ERROR);
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 	incrementRC(lc);
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 	/* Must be unused, unbound, and not on the 'ldapCon' list */
6290Sstevel@tonic-gate 	if (lc->onList || lc->refCount != 1 || lc->isBound) {
6300Sstevel@tonic-gate 		lc->doDel++;
6310Sstevel@tonic-gate 		decrementRC(lc);
6320Sstevel@tonic-gate 		return (LDAP_BUSY);
6330Sstevel@tonic-gate 	}
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	sfree(lc->sp);
6360Sstevel@tonic-gate 	sfree(lc->who);
6370Sstevel@tonic-gate 	sfree(lc->cred);
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 	/* Delete structure with both mutex:es held */
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	free(lc);
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 	return (LDAP_UNAVAILABLE);
6440Sstevel@tonic-gate }
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate /*
6470Sstevel@tonic-gate  * Disconnect the specified LDAP connection. Caller must have acquired 'mutex'.
6480Sstevel@tonic-gate  *
6490Sstevel@tonic-gate  * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch
6500Sstevel@tonic-gate  * the structure in any way.
6510Sstevel@tonic-gate  */
6520Sstevel@tonic-gate static int
disconnectCon(__nis_ldap_conn_t * lc)6530Sstevel@tonic-gate disconnectCon(__nis_ldap_conn_t *lc) {
6540Sstevel@tonic-gate 	int	stat;
6550Sstevel@tonic-gate 	char	*myself = "disconnectCon";
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	if (lc == 0)
6580Sstevel@tonic-gate 		return (LDAP_SUCCESS);
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 	if (!assertExclusive(lc))
6610Sstevel@tonic-gate 		return (LDAP_UNAVAILABLE);
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	if (lc->doDis) {
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 		/* Increment refCount to protect against interference */
6660Sstevel@tonic-gate 		incrementRC(lc);
6670Sstevel@tonic-gate 		/* refCount must be one (i.e., just us) */
6680Sstevel@tonic-gate 		if (lc->refCount != 1) {
6690Sstevel@tonic-gate 			/*
6700Sstevel@tonic-gate 			 * In use; already marked for disconnect,
6710Sstevel@tonic-gate 			 * so do nothing.
6720Sstevel@tonic-gate 			 */
6730Sstevel@tonic-gate 			decrementRC(lc);
6740Sstevel@tonic-gate 			return (LDAP_BUSY);
6750Sstevel@tonic-gate 		}
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 		stat = ldap_unbind_s(lc->ld);
6780Sstevel@tonic-gate 		if (stat == LDAP_SUCCESS) {
6790Sstevel@tonic-gate 			lc->ld = 0;
6800Sstevel@tonic-gate 			lc->isBound = 0;
6810Sstevel@tonic-gate 			lc->doDis = 0;
6820Sstevel@tonic-gate 			/* Reset simple page and vlv indication */
6830Sstevel@tonic-gate 			lc->simplePage = 0;
6840Sstevel@tonic-gate 			lc->vlv = 0;
6850Sstevel@tonic-gate 		} else if (verbose) {
6860Sstevel@tonic-gate 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
6870Sstevel@tonic-gate 				"%s: ldap_unbind_s() => %d (%s)",
6880Sstevel@tonic-gate 				myself, stat, ldap_err2string(stat));
6890Sstevel@tonic-gate 		}
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 		decrementRC(lc);
6920Sstevel@tonic-gate 	}
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 	if (lc->doDel) {
6950Sstevel@tonic-gate 		if (LDAP_UNAVAILABLE == freeCon(lc))
6960Sstevel@tonic-gate 			stat = LDAP_UNAVAILABLE;
6970Sstevel@tonic-gate 	}
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 	return (stat);
7000Sstevel@tonic-gate }
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate /*
7030Sstevel@tonic-gate  * controlSupported will determine for a given connection whether a set
7040Sstevel@tonic-gate  * of controls is supported or not. The input parameters:
7050Sstevel@tonic-gate  *	lc	The connection
7060Sstevel@tonic-gate  *	ctrl	A an array of OID strings, the terminal string should be NULL
7070Sstevel@tonic-gate  * The returned values if LDAP_SUCCESS is returned:
7080Sstevel@tonic-gate  *	supported	A caller supplied array which will be set to TRUE or
7090Sstevel@tonic-gate  *			FALSE depending on whether the corresponding control
7100Sstevel@tonic-gate  *			is reported as supported.
7110Sstevel@tonic-gate  * Returns LDAP_SUCCESS if the supportedControl attribute is read.
7120Sstevel@tonic-gate  */
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate static int
controlSupported(__nis_ldap_conn_t * lc,char ** ctrl,bool_t * supported)7150Sstevel@tonic-gate controlSupported(__nis_ldap_conn_t *lc, char **ctrl, bool_t *supported) {
7160Sstevel@tonic-gate 	LDAPMessage	*res, *e;
7170Sstevel@tonic-gate 	char		*attr[2], *a, **val;
7180Sstevel@tonic-gate 	int		stat, i;
7190Sstevel@tonic-gate 	BerElement	*ber = 0;
7200Sstevel@tonic-gate 	char		*myself = "controlSupported";
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 	attr[0] = "supportedControl";
7230Sstevel@tonic-gate 	attr[1] = 0;
7240Sstevel@tonic-gate 
7250Sstevel@tonic-gate 	stat = ldap_search_st(lc->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
7260Sstevel@tonic-gate 				attr, 0, &lc->searchTimeout, &res);
7270Sstevel@tonic-gate 	if (stat != LDAP_SUCCESS) {
7280Sstevel@tonic-gate 		logmsg(MSG_NOTIMECHECK, LOG_WARNING,
7290Sstevel@tonic-gate 	"%s: Unable to retrieve supported control information for %s: %s",
7300Sstevel@tonic-gate 			myself, NIL(lc->sp), ldap_err2string(stat));
7310Sstevel@tonic-gate 		return (stat);
7320Sstevel@tonic-gate 	}
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	e = ldap_first_entry(lc->ld, res);
7350Sstevel@tonic-gate 	if (e != 0) {
7360Sstevel@tonic-gate 		a = ldap_first_attribute(lc->ld, e, &ber);
7370Sstevel@tonic-gate 		if (a != 0) {
7380Sstevel@tonic-gate 			val = ldap_get_values(lc->ld, e, a);
7390Sstevel@tonic-gate 			if (val == 0) {
7400Sstevel@tonic-gate 				ldap_memfree(a);
7410Sstevel@tonic-gate 				if (ber != 0)
7420Sstevel@tonic-gate 					ber_free(ber, 0);
7430Sstevel@tonic-gate 			}
7440Sstevel@tonic-gate 		}
7450Sstevel@tonic-gate 	}
7460Sstevel@tonic-gate 	if (e == 0 || a == 0 || val == 0) {
7470Sstevel@tonic-gate 		ldap_msgfree(res);
7480Sstevel@tonic-gate 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
7490Sstevel@tonic-gate 			"%s: Unable to get root DSE for %s",
7500Sstevel@tonic-gate 			myself, NIL(lc->sp));
7510Sstevel@tonic-gate 		return (LDAP_OPERATIONS_ERROR);
7520Sstevel@tonic-gate 	}
7530Sstevel@tonic-gate 
7540Sstevel@tonic-gate 	while (*ctrl != NULL) {
7550Sstevel@tonic-gate 		*supported = FALSE;
7560Sstevel@tonic-gate 		for (i = 0; val[i] != 0; i++) {
7570Sstevel@tonic-gate 			if (strstr(val[i], *ctrl) != 0) {
7580Sstevel@tonic-gate 				*supported = TRUE;
7590Sstevel@tonic-gate 				break;
7600Sstevel@tonic-gate 			}
7610Sstevel@tonic-gate 		}
7620Sstevel@tonic-gate 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
7630Sstevel@tonic-gate 			"%s: %s: %s: %s",
7640Sstevel@tonic-gate 			myself, NIL(lc->sp), NIL(*ctrl),
7650Sstevel@tonic-gate 			*supported ? "enabled" : "disabled");
7660Sstevel@tonic-gate 		ctrl++;
7670Sstevel@tonic-gate 		supported++;
7680Sstevel@tonic-gate 	}
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 	ldap_value_free(val);
7710Sstevel@tonic-gate 	ldap_memfree(a);
7720Sstevel@tonic-gate 	if (ber != 0)
7730Sstevel@tonic-gate 		ber_free(ber, 0);
7740Sstevel@tonic-gate 	ldap_msgfree(res);
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	return (stat);
7770Sstevel@tonic-gate }
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate /*
7800Sstevel@tonic-gate  * Connect the LDAP connection 'lc'. Caller must have acquired the 'mutex',
7810Sstevel@tonic-gate  * and the refCount must be zero.
7820Sstevel@tonic-gate  *
7830Sstevel@tonic-gate  * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch
7840Sstevel@tonic-gate  * the structure in any way.
7850Sstevel@tonic-gate  */
7860Sstevel@tonic-gate static int
connectCon(__nis_ldap_conn_t * lc,int check_ctrl)7870Sstevel@tonic-gate connectCon(__nis_ldap_conn_t *lc, int check_ctrl) {
7880Sstevel@tonic-gate 	struct timeval	tp;
7890Sstevel@tonic-gate 	int		stat;
7900Sstevel@tonic-gate 	bool_t		supported[2] = {FALSE, FALSE};
7910Sstevel@tonic-gate 	char		*ctrl[3] = {LDAP_CONTROL_SIMPLE_PAGE,
7920Sstevel@tonic-gate 					LDAP_CONTROL_VLVREQUEST,
7930Sstevel@tonic-gate 					NULL};
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	if (lc == 0)
7960Sstevel@tonic-gate 		return (LDAP_SUCCESS);
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate 	if (!assertExclusive(lc))
7990Sstevel@tonic-gate 		return (LDAP_PARAM_ERROR);
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 	incrementRC(lc);
8020Sstevel@tonic-gate 	if (lc->refCount != 1) {
8030Sstevel@tonic-gate 		/*
8040Sstevel@tonic-gate 		 * Don't want to step on structure when it's used by someone
8050Sstevel@tonic-gate 		 * else.
8060Sstevel@tonic-gate 		 */
8070Sstevel@tonic-gate 		decrementRC(lc);
8080Sstevel@tonic-gate 		return (LDAP_BUSY);
8090Sstevel@tonic-gate 	}
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate 	(void) gettimeofday(&tp, 0);
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 	if (lc->ld != 0) {
8140Sstevel@tonic-gate 		/* Try to disconnect */
8150Sstevel@tonic-gate 		lc->doDis++;
8160Sstevel@tonic-gate 		decrementRC(lc);
8170Sstevel@tonic-gate 		/* disconnctCon() will do the delete if required */
8180Sstevel@tonic-gate 		stat = disconnectCon(lc);
8190Sstevel@tonic-gate 		if (stat != LDAP_SUCCESS)
8200Sstevel@tonic-gate 			return (stat);
8210Sstevel@tonic-gate 		incrementRC(lc);
8220Sstevel@tonic-gate 		if (lc->refCount != 1 || lc->ld != 0) {
8230Sstevel@tonic-gate 			decrementRC(lc);
8240Sstevel@tonic-gate 			return (lc->ld != 0) ? LDAP_SUCCESS :
8250Sstevel@tonic-gate 						LDAP_BUSY;
8260Sstevel@tonic-gate 		}
8270Sstevel@tonic-gate 	} else if (tp.tv_sec < lc->retryTime) {
8280Sstevel@tonic-gate 		/* Too early to retry connect */
8290Sstevel@tonic-gate 		decrementRC(lc);
8300Sstevel@tonic-gate 		return (LDAP_SERVER_DOWN);
8310Sstevel@tonic-gate 	}
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 	/* Set new retry time in case we fail below */
8340Sstevel@tonic-gate 	lc->retryTime = tp.tv_sec + ldapConnAttemptRetryTimeout;
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 	lc->ld = ldapInit(lc->sp, lc->port, proxyInfo.tls_method != no_tls);
8370Sstevel@tonic-gate 	if (lc->ld == 0) {
8380Sstevel@tonic-gate 		decrementRC(lc);
8390Sstevel@tonic-gate 		return (LDAP_LOCAL_ERROR);
8400Sstevel@tonic-gate 	}
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 	stat = lc->status = ldapBind(&lc->ld, lc->who, lc->cred, lc->method,
8430Sstevel@tonic-gate 		lc->bindTimeout);
8440Sstevel@tonic-gate 	if (lc->status == LDAP_SUCCESS) {
8450Sstevel@tonic-gate 		lc->isBound = 1;
8460Sstevel@tonic-gate 		lc->retryTime = 0;
8470Sstevel@tonic-gate 		if (check_ctrl) {
8480Sstevel@tonic-gate 			(void) controlSupported(lc, ctrl, supported);
8490Sstevel@tonic-gate 			lc->simplePage = supported[0];
8500Sstevel@tonic-gate 			lc->vlv = supported[1];
8510Sstevel@tonic-gate 			lc->batchFrom = 50000;
8520Sstevel@tonic-gate 		}
8530Sstevel@tonic-gate 	}
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 	decrementRC(lc);
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 	return (stat);
8580Sstevel@tonic-gate }
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate /*
8610Sstevel@tonic-gate  * Find and return a connection believed to be OK.
8620Sstevel@tonic-gate  */
8630Sstevel@tonic-gate static __nis_ldap_conn_t *
findCon(int * stat)8640Sstevel@tonic-gate findCon(int *stat) {
8650Sstevel@tonic-gate 	__nis_ldap_conn_t	*lc;
8660Sstevel@tonic-gate 	int			ldapStat;
8670Sstevel@tonic-gate 	char			*myself = "findCon";
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate 	if (stat == 0)
8700Sstevel@tonic-gate 		stat = &ldapStat;
8710Sstevel@tonic-gate 
8720Sstevel@tonic-gate 	(void) rw_rdlock(&ldapConLock);
8730Sstevel@tonic-gate 
8740Sstevel@tonic-gate 	if (ldapCon == 0) {
8750Sstevel@tonic-gate 		/* Probably first call; try to set up the connection list */
8760Sstevel@tonic-gate 		(void) rw_unlock(&ldapConLock);
8770Sstevel@tonic-gate 		if ((*stat = setupConList(proxyInfo.default_servers,
8780Sstevel@tonic-gate 					proxyInfo.proxy_dn,
8790Sstevel@tonic-gate 					proxyInfo.proxy_passwd,
8800Sstevel@tonic-gate 					proxyInfo.auth_method)) !=
8810Sstevel@tonic-gate 					LDAP_SUCCESS)
8820Sstevel@tonic-gate 			return (0);
8830Sstevel@tonic-gate 		(void) rw_rdlock(&ldapConLock);
8840Sstevel@tonic-gate 	}
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 	for (lc = ldapCon; lc != 0; lc = lc->next) {
8870Sstevel@tonic-gate 		exclusiveLC(lc);
8880Sstevel@tonic-gate 		if (!lc->isBound) {
8890Sstevel@tonic-gate 			*stat = connectCon(lc, 1);
8900Sstevel@tonic-gate 			if (*stat != LDAP_SUCCESS) {
8910Sstevel@tonic-gate 				if (*stat != LDAP_UNAVAILABLE) {
8920Sstevel@tonic-gate 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
8930Sstevel@tonic-gate 		"%s: Cannot open connection to LDAP server (%s): %s",
8940Sstevel@tonic-gate 						myself, NIL(lc->sp),
8950Sstevel@tonic-gate 						ldap_err2string(*stat));
8960Sstevel@tonic-gate 					releaseLC(lc);
8970Sstevel@tonic-gate 				}
8980Sstevel@tonic-gate 				continue;
8990Sstevel@tonic-gate 			}
9000Sstevel@tonic-gate 		} else if (lc->doDis || lc->doDel) {
9010Sstevel@tonic-gate 			*stat = disconnectCon(lc);
9020Sstevel@tonic-gate 			if (*stat != LDAP_UNAVAILABLE)
9030Sstevel@tonic-gate 				releaseLC(lc);
9040Sstevel@tonic-gate 			continue;
9050Sstevel@tonic-gate 		}
9060Sstevel@tonic-gate 		incrementRC(lc);
9070Sstevel@tonic-gate 		releaseLC(lc);
9080Sstevel@tonic-gate 		break;
9090Sstevel@tonic-gate 	}
9100Sstevel@tonic-gate 
9110Sstevel@tonic-gate 	(void) rw_unlock(&ldapConLock);
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 	return (lc);
9140Sstevel@tonic-gate }
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate /* Release connection; decrements ref count for the connection */
9170Sstevel@tonic-gate static void
releaseCon(__nis_ldap_conn_t * lc,int status)9180Sstevel@tonic-gate releaseCon(__nis_ldap_conn_t *lc, int status) {
9190Sstevel@tonic-gate 	int	stat;
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	if (lc == 0)
9220Sstevel@tonic-gate 		return;
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate 	exclusiveLC(lc);
9250Sstevel@tonic-gate 
9260Sstevel@tonic-gate 	lc->status = status;
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate 	decrementRC(lc);
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate 	if (lc->doDis)
9310Sstevel@tonic-gate 		stat = disconnectCon(lc);
9320Sstevel@tonic-gate 	else
9330Sstevel@tonic-gate 		stat = LDAP_SUCCESS;
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	if (stat != LDAP_UNAVAILABLE)
9360Sstevel@tonic-gate 		releaseLC(lc);
9370Sstevel@tonic-gate }
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate static __nis_ldap_conn_t *
createCon(char * sp,char * who,char * cred,auth_method_t method,int port)9400Sstevel@tonic-gate createCon(char *sp, char *who, char *cred, auth_method_t method, int port) {
9410Sstevel@tonic-gate 	__nis_ldap_conn_t	*lc;
9420Sstevel@tonic-gate 	char			*myself = "createCon";
9430Sstevel@tonic-gate 	char			*r;
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 	if (sp == 0)
9460Sstevel@tonic-gate 		return (0);
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	lc = am(myself, sizeof (*lc));
9490Sstevel@tonic-gate 	if (lc == 0)
9500Sstevel@tonic-gate 		return (0);
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate 	(void) mutex_init(&lc->mutex, 0, 0);
9530Sstevel@tonic-gate 	(void) mutex_init(&lc->rcMutex, 0, 0);
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate 	/* If we need to delete 'lc', freeCon() wants the mutex held */
9560Sstevel@tonic-gate 	exclusiveLC(lc);
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 	lc->sp = sdup(myself, T, sp);
9590Sstevel@tonic-gate 	if (lc->sp == 0) {
9600Sstevel@tonic-gate 		(void) freeCon(lc);
9610Sstevel@tonic-gate 		return (0);
9620Sstevel@tonic-gate 	}
9630Sstevel@tonic-gate 
9640Sstevel@tonic-gate 	if ((r = strchr(lc->sp, ']')) != 0) {
9650Sstevel@tonic-gate 		/*
9660Sstevel@tonic-gate 		 * IPv6 address. Does libldap want this with the
9670Sstevel@tonic-gate 		 * '[' and ']' left in place ? Assume so for now.
9680Sstevel@tonic-gate 		 */
9690Sstevel@tonic-gate 		r = strchr(r, ':');
9700Sstevel@tonic-gate 	} else {
9710Sstevel@tonic-gate 		r = strchr(lc->sp, ':');
9720Sstevel@tonic-gate 	}
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate 	if (r != NULL) {
9750Sstevel@tonic-gate 		*r++ = '\0';
9760Sstevel@tonic-gate 		port = atoi(r);
9770Sstevel@tonic-gate 	} else if (port == 0)
9780Sstevel@tonic-gate 		port = proxyInfo.tls_method == ssl_tls ? LDAPS_PORT : LDAP_PORT;
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 	if (who != 0) {
9810Sstevel@tonic-gate 		lc->who = sdup(myself, T, who);
9820Sstevel@tonic-gate 		if (lc->who == 0) {
9830Sstevel@tonic-gate 			(void) freeCon(lc);
9840Sstevel@tonic-gate 			return (0);
9850Sstevel@tonic-gate 		}
9860Sstevel@tonic-gate 	}
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 	if (cred != 0) {
9890Sstevel@tonic-gate 		lc->cred = sdup(myself, T, cred);
9900Sstevel@tonic-gate 		if (lc->cred == 0) {
9910Sstevel@tonic-gate 			(void) freeCon(lc);
9920Sstevel@tonic-gate 			return (0);
9930Sstevel@tonic-gate 		}
9940Sstevel@tonic-gate 	}
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 	lc->method = method;
9970Sstevel@tonic-gate 	lc->port = port;
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 	lc->bindTimeout = proxyInfo.bind_timeout;
10000Sstevel@tonic-gate 	lc->searchTimeout = proxyInfo.search_timeout;
10010Sstevel@tonic-gate 	lc->modifyTimeout = proxyInfo.modify_timeout;
10020Sstevel@tonic-gate 	lc->addTimeout = proxyInfo.add_timeout;
10030Sstevel@tonic-gate 	lc->deleteTimeout = proxyInfo.delete_timeout;
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 	/* All other fields OK at zero */
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate 	releaseLC(lc);
10080Sstevel@tonic-gate 
10090Sstevel@tonic-gate 	return (lc);
10100Sstevel@tonic-gate }
10110Sstevel@tonic-gate 
10120Sstevel@tonic-gate static int
setupConList(char * serverList,char * who,char * cred,auth_method_t method)10130Sstevel@tonic-gate setupConList(char *serverList, char *who, char *cred, auth_method_t method) {
10140Sstevel@tonic-gate 	char			*sls, *sl, *s, *e;
10150Sstevel@tonic-gate 	__nis_ldap_conn_t	*lc, *tmp;
10160Sstevel@tonic-gate 	char			*myself = "setupConList";
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate 	if (serverList == 0)
10190Sstevel@tonic-gate 		return (LDAP_PARAM_ERROR);
10200Sstevel@tonic-gate 
10210Sstevel@tonic-gate 	(void) rw_wrlock(&ldapConLock);
10220Sstevel@tonic-gate 
10230Sstevel@tonic-gate 	if (ldapCon != 0) {
10240Sstevel@tonic-gate 		/* Assume we've already been called and done the set-up */
10250Sstevel@tonic-gate 		(void) rw_unlock(&ldapConLock);
10260Sstevel@tonic-gate 		return (LDAP_SUCCESS);
10270Sstevel@tonic-gate 	}
10280Sstevel@tonic-gate 
10290Sstevel@tonic-gate 	/* Work on a copy of 'serverList' */
10300Sstevel@tonic-gate 	sl = sls = sdup(myself, T, serverList);
10310Sstevel@tonic-gate 	if (sl == 0) {
10320Sstevel@tonic-gate 		(void) rw_unlock(&ldapConLock);
10330Sstevel@tonic-gate 		return (LDAP_NO_MEMORY);
10340Sstevel@tonic-gate 	}
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	/* Remove leading white space */
10370Sstevel@tonic-gate 	for (0; *sl == ' ' || *sl == '\t'; sl++);
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 	/* Create connection for each server on the list */
10400Sstevel@tonic-gate 	for (s = sl; *s != '\0'; s = e+1) {
10410Sstevel@tonic-gate 		int	l;
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 		/* Find end of server/port token */
10440Sstevel@tonic-gate 		for (e = s; *e != ' ' && *e != '\t' && *e != '\0'; e++);
10450Sstevel@tonic-gate 		if (*e != '\0')
10460Sstevel@tonic-gate 			*e = '\0';
10470Sstevel@tonic-gate 		else
10480Sstevel@tonic-gate 			e--;
10490Sstevel@tonic-gate 		l = slen(s);
10500Sstevel@tonic-gate 
10510Sstevel@tonic-gate 		if (l > 0) {
10520Sstevel@tonic-gate 			lc = createCon(s, who, cred, method, 0);
10530Sstevel@tonic-gate 			if (lc == 0) {
10540Sstevel@tonic-gate 				free(sls);
10550Sstevel@tonic-gate 				(void) rw_unlock(&ldapConLock);
10560Sstevel@tonic-gate 				return (LDAP_NO_MEMORY);
10570Sstevel@tonic-gate 			}
10580Sstevel@tonic-gate 			lc->onList = 1;
10590Sstevel@tonic-gate 			if (ldapCon == 0) {
10600Sstevel@tonic-gate 				ldapCon = lc;
10610Sstevel@tonic-gate 			} else {
10620Sstevel@tonic-gate 				/* Insert at end of list */
10630Sstevel@tonic-gate 				for (tmp = ldapCon; tmp->next != 0;
10640Sstevel@tonic-gate 					tmp = tmp->next);
10650Sstevel@tonic-gate 				tmp->next = lc;
10660Sstevel@tonic-gate 			}
10670Sstevel@tonic-gate 		}
10680Sstevel@tonic-gate 	}
10690Sstevel@tonic-gate 
10700Sstevel@tonic-gate 	free(sls);
10710Sstevel@tonic-gate 
10720Sstevel@tonic-gate 	(void) rw_unlock(&ldapConLock);
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate 	return (LDAP_SUCCESS);
10750Sstevel@tonic-gate }
10760Sstevel@tonic-gate 
10770Sstevel@tonic-gate static bool_t
is_same_connection(__nis_ldap_conn_t * lc,LDAPURLDesc * ludpp)10780Sstevel@tonic-gate is_same_connection(__nis_ldap_conn_t *lc, LDAPURLDesc *ludpp)
10790Sstevel@tonic-gate {
10800Sstevel@tonic-gate 	return (strcasecmp(ludpp->lud_host, lc->sp) == 0 &&
1081*8035SSreedhar.Chalamalasetti@Sun.COM 	    ludpp->lud_port == lc->port);
10820Sstevel@tonic-gate }
10830Sstevel@tonic-gate 
10840Sstevel@tonic-gate static __nis_ldap_conn_t *
find_connection_from_list(__nis_ldap_conn_t * list,LDAPURLDesc * ludpp,int * stat)10850Sstevel@tonic-gate find_connection_from_list(__nis_ldap_conn_t *list,
10860Sstevel@tonic-gate 			LDAPURLDesc *ludpp, int *stat)
10870Sstevel@tonic-gate {
10880Sstevel@tonic-gate 	int			ldapStat;
10890Sstevel@tonic-gate 	__nis_ldap_conn_t	*lc	= NULL;
10900Sstevel@tonic-gate 	if (stat == 0)
10910Sstevel@tonic-gate 		stat = &ldapStat;
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 	*stat = LDAP_SUCCESS;
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 	for (lc = list; lc != 0; lc = lc->next) {
10960Sstevel@tonic-gate 		exclusiveLC(lc);
10970Sstevel@tonic-gate 		if (is_same_connection(lc, ludpp)) {
10980Sstevel@tonic-gate 			if (!lc->isBound) {
10990Sstevel@tonic-gate 				*stat = connectCon(lc, 1);
11000Sstevel@tonic-gate 				if (*stat != LDAP_SUCCESS) {
11010Sstevel@tonic-gate 					releaseLC(lc);
11020Sstevel@tonic-gate 					continue;
11030Sstevel@tonic-gate 				}
11040Sstevel@tonic-gate 			} else if (lc->doDis || lc->doDel) {
11050Sstevel@tonic-gate 				(void) disconnectCon(lc);
11060Sstevel@tonic-gate 				releaseLC(lc);
11070Sstevel@tonic-gate 				continue;
11080Sstevel@tonic-gate 			}
11090Sstevel@tonic-gate 			incrementRC(lc);
11100Sstevel@tonic-gate 			releaseLC(lc);
11110Sstevel@tonic-gate 			break;
11120Sstevel@tonic-gate 		}
11130Sstevel@tonic-gate 		releaseLC(lc);
11140Sstevel@tonic-gate 	}
11150Sstevel@tonic-gate 	return (lc);
11160Sstevel@tonic-gate }
11170Sstevel@tonic-gate 
11180Sstevel@tonic-gate static __nis_ldap_conn_t *
findReferralCon(char ** referralsp,int * stat)11190Sstevel@tonic-gate findReferralCon(char **referralsp, int *stat)
11200Sstevel@tonic-gate {
11210Sstevel@tonic-gate 	__nis_ldap_conn_t	*lc	= NULL;
11220Sstevel@tonic-gate 	__nis_ldap_conn_t	*tmp;
11230Sstevel@tonic-gate 	int			ldapStat;
11240Sstevel@tonic-gate 	int			i;
11250Sstevel@tonic-gate 	LDAPURLDesc		*ludpp	= NULL;
11260Sstevel@tonic-gate 	char			*myself = "findReferralCon";
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate 	if (stat == 0)
11290Sstevel@tonic-gate 		stat = &ldapStat;
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 	*stat = LDAP_SUCCESS;
11320Sstevel@tonic-gate 
11330Sstevel@tonic-gate 	/*
11340Sstevel@tonic-gate 	 * We have the referral lock - to prevent multiple
11350Sstevel@tonic-gate 	 * threads from creating a referred connection simultaneously
11360Sstevel@tonic-gate 	 *
11370Sstevel@tonic-gate 	 * Note that this code assumes that the ldapCon list is a
11380Sstevel@tonic-gate 	 * static list - that it has previously been created
11390Sstevel@tonic-gate 	 * (otherwise we wouldn't have gotten a referral) and that
11400Sstevel@tonic-gate 	 * it will neither grow or shrink - elements may have new
11410Sstevel@tonic-gate 	 * connections or unbound. If this assumption is no longer valid,
11420Sstevel@tonic-gate 	 * the locking needs to be reworked.
11430Sstevel@tonic-gate 	 */
11440Sstevel@tonic-gate 	(void) rw_rdlock(&referralConLock);
11450Sstevel@tonic-gate 
11460Sstevel@tonic-gate 	for (i = 0; referralsp[i] != NULL; i++) {
11470Sstevel@tonic-gate 		if (ldap_url_parse(referralsp[i], &ludpp) != LDAP_SUCCESS)
11480Sstevel@tonic-gate 			continue;
11490Sstevel@tonic-gate 		/* Ignore referrals if not at the appropriate tls level */
11500Sstevel@tonic-gate #ifdef LDAP_URL_OPT_SECURE
11510Sstevel@tonic-gate 		if (ludpp->lud_options & LDAP_URL_OPT_SECURE) {
11520Sstevel@tonic-gate 			if (proxyInfo.tls_method != ssl_tls) {
11530Sstevel@tonic-gate 				ldap_free_urldesc(ludpp);
11540Sstevel@tonic-gate 				continue;
11550Sstevel@tonic-gate 			}
11560Sstevel@tonic-gate 		} else {
11570Sstevel@tonic-gate 			if (proxyInfo.tls_method != no_tls) {
11580Sstevel@tonic-gate 				ldap_free_urldesc(ludpp);
11590Sstevel@tonic-gate 				continue;
11600Sstevel@tonic-gate 			}
11610Sstevel@tonic-gate 		}
11620Sstevel@tonic-gate #endif
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 		/* Determine if we already have a connection to the server */
11650Sstevel@tonic-gate 		lc = find_connection_from_list(ldapReferralCon, ludpp, stat);
11660Sstevel@tonic-gate 		if (lc == NULL)
11670Sstevel@tonic-gate 			lc = find_connection_from_list(ldapCon, ludpp, stat);
11680Sstevel@tonic-gate 		ldap_free_urldesc(ludpp);
11690Sstevel@tonic-gate 		if (lc != NULL) {
11700Sstevel@tonic-gate 			(void) rw_unlock(&referralConLock);
11710Sstevel@tonic-gate 			return (lc);
11720Sstevel@tonic-gate 		}
11730Sstevel@tonic-gate 	}
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate 	for (i = 0; referralsp[i] != NULL; i++) {
11760Sstevel@tonic-gate 		if (ldap_url_parse(referralsp[i], &ludpp) != LDAP_SUCCESS)
11770Sstevel@tonic-gate 			continue;
11780Sstevel@tonic-gate 		/* Ignore referrals if not at the appropriate tls level */
11790Sstevel@tonic-gate #ifdef LDAP_URL_OPT_SECURE
11800Sstevel@tonic-gate 		if (ludpp->lud_options & LDAP_URL_OPT_SECURE) {
11810Sstevel@tonic-gate 			if (proxyInfo.tls_method != ssl_tls) {
11820Sstevel@tonic-gate 				ldap_free_urldesc(ludpp);
11830Sstevel@tonic-gate 				continue;
11840Sstevel@tonic-gate 			}
11850Sstevel@tonic-gate 		} else {
11860Sstevel@tonic-gate 			if (proxyInfo.tls_method != no_tls) {
11870Sstevel@tonic-gate 				ldap_free_urldesc(ludpp);
11880Sstevel@tonic-gate 				continue;
11890Sstevel@tonic-gate 			}
11900Sstevel@tonic-gate 		}
11910Sstevel@tonic-gate #endif
11920Sstevel@tonic-gate 		lc = createCon(ludpp->lud_host, proxyInfo.proxy_dn,
1193*8035SSreedhar.Chalamalasetti@Sun.COM 		    proxyInfo.proxy_passwd,
1194*8035SSreedhar.Chalamalasetti@Sun.COM 		    proxyInfo.auth_method,
1195*8035SSreedhar.Chalamalasetti@Sun.COM 		    ludpp->lud_port);
11960Sstevel@tonic-gate 		if (lc == 0) {
11970Sstevel@tonic-gate 			ldap_free_urldesc(ludpp);
11980Sstevel@tonic-gate 			(void) rw_unlock(&referralConLock);
11990Sstevel@tonic-gate 			*stat = LDAP_NO_MEMORY;
12000Sstevel@tonic-gate 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
1201*8035SSreedhar.Chalamalasetti@Sun.COM 			    "%s: Could not connect to host: %s",
1202*8035SSreedhar.Chalamalasetti@Sun.COM 			    myself, NIL(ludpp->lud_host));
12030Sstevel@tonic-gate 			return (NULL);
12040Sstevel@tonic-gate 		}
12050Sstevel@tonic-gate 
12060Sstevel@tonic-gate 		lc->onList = 1;
12070Sstevel@tonic-gate 		if (ldapReferralCon == 0) {
12080Sstevel@tonic-gate 			ldapReferralCon = lc;
12090Sstevel@tonic-gate 		} else {
12100Sstevel@tonic-gate 			/* Insert at end of list */
12110Sstevel@tonic-gate 			for (tmp = ldapReferralCon; tmp->next != 0;
1212*8035SSreedhar.Chalamalasetti@Sun.COM 			    tmp = tmp->next) {}
12130Sstevel@tonic-gate 			tmp->next = lc;
12140Sstevel@tonic-gate 		}
12150Sstevel@tonic-gate 		lc = find_connection_from_list(ldapReferralCon, ludpp, stat);
12160Sstevel@tonic-gate 		ldap_free_urldesc(ludpp);
12170Sstevel@tonic-gate 		if (lc != NULL)
12180Sstevel@tonic-gate 			break;
12190Sstevel@tonic-gate 	}
12200Sstevel@tonic-gate 	(void) rw_unlock(&referralConLock);
12210Sstevel@tonic-gate 	if (lc == NULL) {
12220Sstevel@tonic-gate 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
1223*8035SSreedhar.Chalamalasetti@Sun.COM 		    "%s: Could not find a connection to %s, ...",
1224*8035SSreedhar.Chalamalasetti@Sun.COM 		    myself, NIL(referralsp[0]));
12250Sstevel@tonic-gate 	}
12260Sstevel@tonic-gate 
12270Sstevel@tonic-gate 	return (lc);
12280Sstevel@tonic-gate }
12290Sstevel@tonic-gate 
12300Sstevel@tonic-gate /*
12310Sstevel@tonic-gate  * Find and return a connection believed to be OK and ensure children
12320Sstevel@tonic-gate  * will never use parent's connection.
12330Sstevel@tonic-gate  */
12340Sstevel@tonic-gate static __nis_ldap_conn_t *
findYPCon(__nis_ldap_search_t * ls,int * stat)12350Sstevel@tonic-gate findYPCon(__nis_ldap_search_t *ls, int *stat) {
12360Sstevel@tonic-gate 	__nis_ldap_conn_t	*lc, *newlc;
12370Sstevel@tonic-gate 	int			ldapStat, newstat;
12380Sstevel@tonic-gate 	char			*myself = "findYPCon";
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 	if (stat == 0)
12410Sstevel@tonic-gate 		stat = &ldapStat;
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 	(void) rw_rdlock(&ldapConLock);
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate 	if (ldapCon == 0) {
12460Sstevel@tonic-gate 		/* Probably first call; try to set up the connection list */
12470Sstevel@tonic-gate 		(void) rw_unlock(&ldapConLock);
12480Sstevel@tonic-gate 		if ((*stat = setupConList(proxyInfo.default_servers,
12490Sstevel@tonic-gate 					proxyInfo.proxy_dn,
12500Sstevel@tonic-gate 					proxyInfo.proxy_passwd,
12510Sstevel@tonic-gate 					proxyInfo.auth_method)) !=
12520Sstevel@tonic-gate 					LDAP_SUCCESS)
12530Sstevel@tonic-gate 			return (0);
12540Sstevel@tonic-gate 		(void) rw_rdlock(&ldapConLock);
12550Sstevel@tonic-gate 	}
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 	for (lc = ldapCon; lc != 0; lc = lc->next) {
12580Sstevel@tonic-gate 		exclusiveLC(lc);
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate 		if (lc->isBound && (lc->doDis || lc->doDel)) {
12610Sstevel@tonic-gate 			*stat = disconnectCon(lc);
12620Sstevel@tonic-gate 			if (*stat != LDAP_UNAVAILABLE)
12630Sstevel@tonic-gate 				releaseLC(lc);
12640Sstevel@tonic-gate 			continue;
12650Sstevel@tonic-gate 		}
12660Sstevel@tonic-gate 
12670Sstevel@tonic-gate 		/*
12680Sstevel@tonic-gate 		 * Use a new connection for all cases except when
12690Sstevel@tonic-gate 		 * requested by the main thread in the parent ypserv
12700Sstevel@tonic-gate 		 * process.
12710Sstevel@tonic-gate 		 */
12720Sstevel@tonic-gate 		if (ls->useCon == 0) {
12730Sstevel@tonic-gate 			newlc = createCon(lc->sp, lc->who, lc->cred,
12740Sstevel@tonic-gate 						lc->method, lc->port);
12750Sstevel@tonic-gate 			if (!newlc) {
12760Sstevel@tonic-gate 				releaseLC(lc);
12770Sstevel@tonic-gate 				continue;
12780Sstevel@tonic-gate 			}
12790Sstevel@tonic-gate 			if (lc->ld != 0) {
12800Sstevel@tonic-gate 				newlc->simplePage = lc->simplePage;
12810Sstevel@tonic-gate 				newlc->vlv = lc->vlv;
12820Sstevel@tonic-gate 				newlc->batchFrom = lc->batchFrom;
12830Sstevel@tonic-gate 			}
12840Sstevel@tonic-gate 			releaseLC(lc);
12850Sstevel@tonic-gate 			exclusiveLC(newlc);
12860Sstevel@tonic-gate 			newstat = connectCon(newlc, 0);
12870Sstevel@tonic-gate 			if (newstat != LDAP_SUCCESS) {
12880Sstevel@tonic-gate 				if (newstat != LDAP_UNAVAILABLE) {
12890Sstevel@tonic-gate 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
12900Sstevel@tonic-gate 			"%s: Cannot open connection to LDAP server (%s): %s",
12910Sstevel@tonic-gate 						myself, NIL(newlc->sp),
12920Sstevel@tonic-gate 						ldap_err2string(*stat));
12930Sstevel@tonic-gate 				}
12940Sstevel@tonic-gate 				(void) freeCon(newlc);
12950Sstevel@tonic-gate 				newlc = 0;
12960Sstevel@tonic-gate 				continue;
12970Sstevel@tonic-gate 			}
12980Sstevel@tonic-gate 
12990Sstevel@tonic-gate 			/*
13000Sstevel@tonic-gate 			 * No need to put newlc on the ldapCon list as this
13010Sstevel@tonic-gate 			 * connection will be freed after use.
13020Sstevel@tonic-gate 			 */
13030Sstevel@tonic-gate 			newlc->onList = 0;
13040Sstevel@tonic-gate 
13050Sstevel@tonic-gate 			lc = newlc;
13060Sstevel@tonic-gate 		} else  if (!lc->isBound) {
13070Sstevel@tonic-gate 			*stat = connectCon(lc, 1);
13080Sstevel@tonic-gate 			if (*stat != LDAP_SUCCESS) {
13090Sstevel@tonic-gate 				if (*stat != LDAP_UNAVAILABLE) {
13100Sstevel@tonic-gate 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
13110Sstevel@tonic-gate 		"%s: Cannot open connection to LDAP server (%s): %s",
13120Sstevel@tonic-gate 						myself, NIL(lc->sp),
13130Sstevel@tonic-gate 						ldap_err2string(*stat));
13140Sstevel@tonic-gate 					releaseLC(lc);
13150Sstevel@tonic-gate 				}
13160Sstevel@tonic-gate 				continue;
13170Sstevel@tonic-gate 			}
13180Sstevel@tonic-gate 		}
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate 		incrementRC(lc);
13210Sstevel@tonic-gate 		releaseLC(lc);
13220Sstevel@tonic-gate 		break;
13230Sstevel@tonic-gate 	}
13240Sstevel@tonic-gate 
13250Sstevel@tonic-gate 	(void) rw_unlock(&ldapConLock);
13260Sstevel@tonic-gate 
13270Sstevel@tonic-gate 	return (lc);
13280Sstevel@tonic-gate }
13290Sstevel@tonic-gate 
13300Sstevel@tonic-gate #define	SORTKEYLIST	"cn uid"
13310Sstevel@tonic-gate 
13320Sstevel@tonic-gate /*
13330Sstevel@tonic-gate  * Perform an LDAP search operation per 'ls', adding the result(s) to
13340Sstevel@tonic-gate  * a copy of the 'rvIn' structure; the copy becomes the return value.
13350Sstevel@tonic-gate  * The caller must deallocate both 'rvIn' and the result, if any.
13360Sstevel@tonic-gate  *
13370Sstevel@tonic-gate  * On entry, '*numValues' contains a hint regarding the expected
13380Sstevel@tonic-gate  * number of entries. Zero is the same as one, and negative values
13390Sstevel@tonic-gate  * imply no information. This is used to decide whether or not to
13400Sstevel@tonic-gate  * try an indexed search.
13410Sstevel@tonic-gate  *
13420Sstevel@tonic-gate  * On successful (non-NULL) return, '*numValues' contains the number
13430Sstevel@tonic-gate  * of __nis_rule_value_t elements in the returned array, and '*stat'
13440Sstevel@tonic-gate  * the LDAP operations status.
13450Sstevel@tonic-gate  */
13460Sstevel@tonic-gate __nis_rule_value_t *
ldapSearch(__nis_ldap_search_t * ls,int * numValues,__nis_rule_value_t * rvIn,int * ldapStat)13470Sstevel@tonic-gate ldapSearch(__nis_ldap_search_t *ls, int *numValues, __nis_rule_value_t *rvIn,
13480Sstevel@tonic-gate 		int *ldapStat) {
13490Sstevel@tonic-gate 	__nis_rule_value_t	*rv = 0;
13500Sstevel@tonic-gate 	int			stat, numEntries, numVals, tnv, done, lprEc;
13510Sstevel@tonic-gate 	LDAPMessage		*msg = 0, *m;
13520Sstevel@tonic-gate 	__nis_ldap_conn_t	*lc;
13530Sstevel@tonic-gate 	struct timeval		tv, start, now;
13540Sstevel@tonic-gate 	LDAPsortkey		**sortKeyList = 0;
13550Sstevel@tonic-gate 	LDAPControl		*ctrls[3], *sortCtrl = 0, *vlvCtrl = 0;
13560Sstevel@tonic-gate 	LDAPControl		**retCtrls = 0;
13570Sstevel@tonic-gate 	LDAPVirtualList		vList;
13580Sstevel@tonic-gate 	struct berval		*spCookie = 0;
13590Sstevel@tonic-gate 	int			doVLV = 0;
13600Sstevel@tonic-gate 	int			doSP = 0;
13610Sstevel@tonic-gate 	long			index;
13620Sstevel@tonic-gate 	char			*myself = "ldapSearch";
13630Sstevel@tonic-gate 	bool_t			follow_referral =
13640Sstevel@tonic-gate 					proxyInfo.follow_referral == follow;
13650Sstevel@tonic-gate 	int			doIndex = 1;
13660Sstevel@tonic-gate 	char			**referralsp = NULL;
13670Sstevel@tonic-gate 
1368*8035SSreedhar.Chalamalasetti@Sun.COM 	ctrls[0] = ctrls[1] = ctrls[2] = 0;
1369*8035SSreedhar.Chalamalasetti@Sun.COM 
13700Sstevel@tonic-gate 	if (ldapStat == 0)
13710Sstevel@tonic-gate 		ldapStat = &stat;
13720Sstevel@tonic-gate 
13730Sstevel@tonic-gate 	if (ls == 0) {
13740Sstevel@tonic-gate 		*ldapStat = LDAP_PARAM_ERROR;
13750Sstevel@tonic-gate 		return (0);
13760Sstevel@tonic-gate 	}
13770Sstevel@tonic-gate 
13780Sstevel@tonic-gate 	if (yp2ldap) {
13790Sstevel@tonic-gate 		/* make sure the parent's connection is not used by child */
13800Sstevel@tonic-gate 		if ((lc = findYPCon(ls, ldapStat)) == 0) {
13810Sstevel@tonic-gate 			*ldapStat = LDAP_SERVER_DOWN;
13820Sstevel@tonic-gate 			return (0);
13830Sstevel@tonic-gate 		}
13840Sstevel@tonic-gate 	} else {
13850Sstevel@tonic-gate 		if ((lc = findCon(ldapStat)) == 0) {
13860Sstevel@tonic-gate 			*ldapStat = LDAP_SERVER_DOWN;
13870Sstevel@tonic-gate 			return (0);
13880Sstevel@tonic-gate 		}
13890Sstevel@tonic-gate 	}
13900Sstevel@tonic-gate 
13910Sstevel@tonic-gate 	if (numValues != 0 && (*numValues == 0 || *numValues == 1))
13920Sstevel@tonic-gate 		doIndex = 0;
13930Sstevel@tonic-gate 
13940Sstevel@tonic-gate retry_new_conn:
13950Sstevel@tonic-gate 	/* Prefer VLV over simple page, and SP over nothing */
13960Sstevel@tonic-gate 	if (doIndex && lc->vlv) {
13970Sstevel@tonic-gate 		stat = ldap_create_sort_keylist(&sortKeyList, SORTKEYLIST);
13980Sstevel@tonic-gate 		if (stat != LDAP_SUCCESS) {
13990Sstevel@tonic-gate 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
14000Sstevel@tonic-gate 				"%s: Error creating sort keylist: %s",
14010Sstevel@tonic-gate 				myself, ldap_err2string(stat));
14020Sstevel@tonic-gate 			freeRuleValue(rv, numVals);
14030Sstevel@tonic-gate 			*ldapStat = stat;
14040Sstevel@tonic-gate 			rv = 0;
14050Sstevel@tonic-gate 			goto retry_noVLV;
14060Sstevel@tonic-gate 		}
14070Sstevel@tonic-gate 		stat = ldap_create_sort_control(lc->ld, sortKeyList, 1,
14080Sstevel@tonic-gate 						&sortCtrl);
14090Sstevel@tonic-gate 		if (stat == LDAP_SUCCESS) {
14100Sstevel@tonic-gate 			vList.ldvlist_before_count = 0;
14110Sstevel@tonic-gate 			vList.ldvlist_after_count = lc->batchFrom - 1;
14120Sstevel@tonic-gate 			vList.ldvlist_attrvalue = 0;
14130Sstevel@tonic-gate 			vList.ldvlist_extradata = 0;
14140Sstevel@tonic-gate 			index = 1;
14150Sstevel@tonic-gate 			doVLV = 1;
14160Sstevel@tonic-gate 		} else {
14170Sstevel@tonic-gate 			ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat);
14180Sstevel@tonic-gate 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
14190Sstevel@tonic-gate 				"%s: Error creating VLV sort control: %s",
14200Sstevel@tonic-gate 				myself, ldap_err2string(stat));
14210Sstevel@tonic-gate 			freeRuleValue(rv, numVals);
14220Sstevel@tonic-gate 			*ldapStat = stat;
14230Sstevel@tonic-gate 			rv = 0;
14240Sstevel@tonic-gate 		}
14250Sstevel@tonic-gate 	}
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate retry_noVLV:
14280Sstevel@tonic-gate 
14290Sstevel@tonic-gate 	if (doIndex && !doVLV && lc->simplePage) {
14300Sstevel@tonic-gate 		spCookie = am(myself, sizeof (*spCookie));
14310Sstevel@tonic-gate 		if (spCookie != 0 &&
14320Sstevel@tonic-gate 				(spCookie->bv_val = sdup(myself, T, "")) != 0) {
14330Sstevel@tonic-gate 			spCookie->bv_len = 0;
14340Sstevel@tonic-gate 			doSP = 1;
14350Sstevel@tonic-gate 		} else {
14360Sstevel@tonic-gate 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
14370Sstevel@tonic-gate 	"%s: No memory for simple page cookie; using un-paged LDAP search",
14380Sstevel@tonic-gate 				myself);
14390Sstevel@tonic-gate 			freeRuleValue(rv, numVals);
14400Sstevel@tonic-gate 			*ldapStat = stat;
14410Sstevel@tonic-gate 			rv = 0;
14420Sstevel@tonic-gate 			goto cleanup;
14430Sstevel@tonic-gate 		}
14440Sstevel@tonic-gate 	}
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate 	if (!doVLV && !doSP)
14470Sstevel@tonic-gate 		ctrls[0] = ctrls[1] = 0;
14480Sstevel@tonic-gate 
14490Sstevel@tonic-gate 	numVals = 0;
14500Sstevel@tonic-gate 	done = 0;
14510Sstevel@tonic-gate 
14520Sstevel@tonic-gate 	if (ls->timeout.tv_sec || ls->timeout.tv_usec) {
14530Sstevel@tonic-gate 		tv = ls->timeout;
14540Sstevel@tonic-gate 	} else {
14550Sstevel@tonic-gate 		tv = lc->searchTimeout;
14560Sstevel@tonic-gate 	}
14570Sstevel@tonic-gate 	(void) gettimeofday(&start, 0);
14580Sstevel@tonic-gate 
14590Sstevel@tonic-gate 	do {
14600Sstevel@tonic-gate 		/* don't do vlv or simple page for base level searches */
14610Sstevel@tonic-gate 		if (doVLV && ls->base != LDAP_SCOPE_BASE) {
14620Sstevel@tonic-gate 			vList.ldvlist_index = index;
14630Sstevel@tonic-gate 			vList.ldvlist_size = 0;
14640Sstevel@tonic-gate 			if (vlvCtrl != 0)
14650Sstevel@tonic-gate 				ldap_control_free(vlvCtrl);
14660Sstevel@tonic-gate 			stat = ldap_create_virtuallist_control(lc->ld,
14670Sstevel@tonic-gate 					&vList, &vlvCtrl);
14680Sstevel@tonic-gate 			if (stat != LDAP_SUCCESS) {
14690Sstevel@tonic-gate 				ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
14700Sstevel@tonic-gate 						&stat);
14710Sstevel@tonic-gate 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
14720Sstevel@tonic-gate 				"%s: Error creating VLV at index %ld: %s",
14730Sstevel@tonic-gate 					myself, index, ldap_err2string(stat));
14740Sstevel@tonic-gate 				*ldapStat = stat;
14750Sstevel@tonic-gate 				freeRuleValue(rv, numVals);
14760Sstevel@tonic-gate 				rv = 0;
14770Sstevel@tonic-gate 				goto cleanup;
14780Sstevel@tonic-gate 			}
14790Sstevel@tonic-gate 			ctrls[0] = sortCtrl;
14800Sstevel@tonic-gate 			ctrls[1] = vlvCtrl;
14810Sstevel@tonic-gate 			ctrls[2] = 0;
14820Sstevel@tonic-gate 			stat = ldap_search_ext_s(lc->ld, ls->base,
14830Sstevel@tonic-gate 					ls->scope, ls->filter, ls->attrs,
14840Sstevel@tonic-gate 					ls->attrsonly, ctrls, 0, &tv,
14850Sstevel@tonic-gate 					proxyInfo.search_size_limit, &msg);
14860Sstevel@tonic-gate 		/* don't do vlv or simple page for base level searches */
14870Sstevel@tonic-gate 		} else if (doSP && ls->base != LDAP_SCOPE_BASE) {
14880Sstevel@tonic-gate 			if (ctrls[0] != 0)
14890Sstevel@tonic-gate 				ldap_control_free(ctrls[0]);
14900Sstevel@tonic-gate 			stat = ldap_create_page_control(lc->ld,
14910Sstevel@tonic-gate 					lc->batchFrom, spCookie, 0, &ctrls[0]);
14920Sstevel@tonic-gate 			if (stat != LDAP_SUCCESS) {
14930Sstevel@tonic-gate 				ber_bvfree(spCookie);
14940Sstevel@tonic-gate 				spCookie = 0;
14950Sstevel@tonic-gate 				ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
14960Sstevel@tonic-gate 						&stat);
14970Sstevel@tonic-gate 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
14980Sstevel@tonic-gate 					"%s: Simple page error: %s",
14990Sstevel@tonic-gate 					myself, ldap_err2string(stat));
15000Sstevel@tonic-gate 				freeRuleValue(rv, numVals);
15010Sstevel@tonic-gate 				*ldapStat = stat;
15020Sstevel@tonic-gate 				rv = 0;
15030Sstevel@tonic-gate 				goto cleanup;
15040Sstevel@tonic-gate 			}
15050Sstevel@tonic-gate 			ctrls[1] = 0;
15060Sstevel@tonic-gate 			stat = ldap_search_ext_s(lc->ld, ls->base,
15070Sstevel@tonic-gate 					ls->scope, ls->filter, ls->attrs,
15080Sstevel@tonic-gate 					ls->attrsonly, ctrls, 0, &tv,
15090Sstevel@tonic-gate 					proxyInfo.search_size_limit, &msg);
15100Sstevel@tonic-gate 		} else {
15110Sstevel@tonic-gate 			stat = ldap_search_st(lc->ld, ls->base, ls->scope,
15120Sstevel@tonic-gate 					ls->filter, ls->attrs, ls->attrsonly,
15130Sstevel@tonic-gate 					&tv, &msg);
15140Sstevel@tonic-gate 		}
15150Sstevel@tonic-gate 		if (stat == LDAP_SUCCESS)
15160Sstevel@tonic-gate 			ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat);
15170Sstevel@tonic-gate 
15180Sstevel@tonic-gate 		if (stat == LDAP_SERVER_DOWN) {
15190Sstevel@tonic-gate 			lc->doDis++;
15200Sstevel@tonic-gate 			releaseCon(lc, stat);
15210Sstevel@tonic-gate 			lc = (yp2ldap)?findYPCon(ls, ldapStat):
15220Sstevel@tonic-gate 				findCon(ldapStat);
15230Sstevel@tonic-gate 			if (lc == 0) {
15240Sstevel@tonic-gate 				*ldapStat = LDAP_SERVER_DOWN;
15250Sstevel@tonic-gate 				rv =  0;
15260Sstevel@tonic-gate 				goto cleanup;
15270Sstevel@tonic-gate 			}
15280Sstevel@tonic-gate 			goto retry_new_conn;
15290Sstevel@tonic-gate 		}
15300Sstevel@tonic-gate 
15310Sstevel@tonic-gate 		if (stat == LDAP_REFERRAL && follow_referral) {
15320Sstevel@tonic-gate 			(void) ldap_parse_result(lc->ld, msg, NULL, NULL, NULL,
15330Sstevel@tonic-gate 				&referralsp, NULL, 0);
15340Sstevel@tonic-gate 			if (referralsp != NULL) {
15350Sstevel@tonic-gate 				/* We support at most one level of referrals */
15360Sstevel@tonic-gate 				follow_referral = FALSE;
15370Sstevel@tonic-gate 				releaseCon(lc, stat);
15380Sstevel@tonic-gate 				lc = findReferralCon(referralsp, &stat);
15390Sstevel@tonic-gate 				ldap_value_free(referralsp);
15400Sstevel@tonic-gate 				if (lc == NULL) {
15410Sstevel@tonic-gate 					freeRuleValue(rv, numVals);
15420Sstevel@tonic-gate 					rv = 0;
15430Sstevel@tonic-gate 					*ldapStat = stat;
15440Sstevel@tonic-gate 					goto cleanup;
15450Sstevel@tonic-gate 				}
15460Sstevel@tonic-gate 				stat = LDAP_SUCCESS;
15470Sstevel@tonic-gate 				goto retry_new_conn;
15480Sstevel@tonic-gate 			}
15490Sstevel@tonic-gate 		}
15500Sstevel@tonic-gate 		*ldapStat = stat;
15510Sstevel@tonic-gate 
15520Sstevel@tonic-gate 		if (*ldapStat == LDAP_NO_SUCH_OBJECT) {
15530Sstevel@tonic-gate 			freeRuleValue(rv, numVals);
15540Sstevel@tonic-gate 			rv = 0;
15550Sstevel@tonic-gate 			goto cleanup;
15560Sstevel@tonic-gate 		} else if (doVLV && *ldapStat == LDAP_INSUFFICIENT_ACCESS) {
15570Sstevel@tonic-gate 			/*
15580Sstevel@tonic-gate 			 * The LDAP server (at least Netscape 4.x) can return
15590Sstevel@tonic-gate 			 * LDAP_INSUFFICIENT_ACCESS when VLV is supported,
15600Sstevel@tonic-gate 			 * but not for the bind DN specified. So, just in
15610Sstevel@tonic-gate 			 * case, we clean up, and try again without VLV.
15620Sstevel@tonic-gate 			 */
15630Sstevel@tonic-gate 			doVLV = 0;
15640Sstevel@tonic-gate 			if (msg != 0) {
15650Sstevel@tonic-gate 				(void) ldap_msgfree(msg);
15660Sstevel@tonic-gate 				msg = 0;
15670Sstevel@tonic-gate 			}
15680Sstevel@tonic-gate 			if (ctrls[0] != 0) {
15690Sstevel@tonic-gate 				ldap_control_free(ctrls[0]);
15700Sstevel@tonic-gate 				ctrls[0] = 0;
15710Sstevel@tonic-gate 			}
15720Sstevel@tonic-gate 			if (ctrls[1] != 0) {
15730Sstevel@tonic-gate 				ldap_control_free(ctrls[1]);
15740Sstevel@tonic-gate 				ctrls[1] = 0;
15750Sstevel@tonic-gate 			}
15760Sstevel@tonic-gate 			logmsg(MSG_VLV_INSUFF_ACC, LOG_WARNING,
15770Sstevel@tonic-gate 	"%s: VLV insufficient access from server %s; retrying without VLV",
15780Sstevel@tonic-gate 				myself, NIL(lc->sp));
15790Sstevel@tonic-gate 			goto retry_noVLV;
15800Sstevel@tonic-gate 		} else if (*ldapStat != LDAP_SUCCESS) {
15810Sstevel@tonic-gate 			logmsg(MSG_NOTIMECHECK, LOG_WARNING,
15820Sstevel@tonic-gate 				"ldap_search(0x%x,\n\t\"%s\",\n\t %d,",
15830Sstevel@tonic-gate 				lc->ld, NIL(ls->base), ls->scope);
15840Sstevel@tonic-gate 			logmsg(MSG_NOTIMECHECK, LOG_WARNING,
15850Sstevel@tonic-gate 				"\t\"%s\",\n\t0x%x,\n\t%d) => %d (%s)",
15860Sstevel@tonic-gate 				NIL(ls->filter), ls->attrs, ls->attrsonly,
15870Sstevel@tonic-gate 				*ldapStat, ldap_err2string(stat));
15880Sstevel@tonic-gate 			freeRuleValue(rv, numVals);
15890Sstevel@tonic-gate 			rv = 0;
15900Sstevel@tonic-gate 			goto cleanup;
15910Sstevel@tonic-gate 		}
15920Sstevel@tonic-gate 
15930Sstevel@tonic-gate 		numEntries = ldap_count_entries(lc->ld, msg);
15940Sstevel@tonic-gate 		if (numEntries == 0 && *ldapStat == LDAP_SUCCESS) {
15950Sstevel@tonic-gate 			/*
15960Sstevel@tonic-gate 			 * This is a bit weird, but the server (or, at least,
15970Sstevel@tonic-gate 			 * ldap_search_ext()) can sometimes return
15980Sstevel@tonic-gate 			 * LDAP_SUCCESS and no entries when it didn't
15990Sstevel@tonic-gate 			 * find what we were looking for. Seems it ought to
16000Sstevel@tonic-gate 			 * return LDAP_NO_SUCH_OBJECT or some such.
16010Sstevel@tonic-gate 			 */
16020Sstevel@tonic-gate 			freeRuleValue(rv, numVals);
16030Sstevel@tonic-gate 			rv = 0;
16040Sstevel@tonic-gate 			*ldapStat = LDAP_NO_SUCH_OBJECT;
16050Sstevel@tonic-gate 			goto cleanup;
16060Sstevel@tonic-gate 		}
16070Sstevel@tonic-gate 
16080Sstevel@tonic-gate 		tnv = numVals + numEntries;
16090Sstevel@tonic-gate 		if ((rv = growRuleValue(numVals, tnv, rv, rvIn)) == 0) {
16100Sstevel@tonic-gate 			*ldapStat = LDAP_NO_MEMORY;
16110Sstevel@tonic-gate 			goto cleanup;
16120Sstevel@tonic-gate 		}
16130Sstevel@tonic-gate 
16140Sstevel@tonic-gate 		for (m = ldap_first_entry(lc->ld, msg); m != 0;
16150Sstevel@tonic-gate 				m = ldap_next_entry(lc->ld, m), numVals++) {
16160Sstevel@tonic-gate 			char		*nm;
16170Sstevel@tonic-gate 			BerElement	*ber = 0;
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 			if (numVals > tnv) {
16200Sstevel@tonic-gate 				logmsg(MSG_NOTIMECHECK, LOG_INFO,
16210Sstevel@tonic-gate 				"%s: Inconsistent LDAP entry count > %d",
16220Sstevel@tonic-gate 					myself, numEntries);
16230Sstevel@tonic-gate 				break;
16240Sstevel@tonic-gate 			}
16250Sstevel@tonic-gate 
16260Sstevel@tonic-gate 			nm = ldap_get_dn(lc->ld, m);
16270Sstevel@tonic-gate 			if (nm == 0 || addSAttr2RuleValue("dn", nm,
16280Sstevel@tonic-gate 					&rv[numVals])) {
16290Sstevel@tonic-gate 				sfree(nm);
16300Sstevel@tonic-gate 				*ldapStat = LDAP_NO_MEMORY;
16310Sstevel@tonic-gate 				freeRuleValue(rv, tnv);
16320Sstevel@tonic-gate 				rv = 0;
16330Sstevel@tonic-gate 				goto cleanup;
16340Sstevel@tonic-gate 			}
16350Sstevel@tonic-gate 			sfree(nm);
16360Sstevel@tonic-gate 
16370Sstevel@tonic-gate 			for (nm = ldap_first_attribute(lc->ld, m, &ber);
16380Sstevel@tonic-gate 					nm != 0;
16390Sstevel@tonic-gate 				nm = ldap_next_attribute(lc->ld, m, ber)) {
16400Sstevel@tonic-gate 				struct berval	**val;
16410Sstevel@tonic-gate 				int		i, nv;
16420Sstevel@tonic-gate 
16430Sstevel@tonic-gate 				val = ldap_get_values_len(lc->ld, m, nm);
16440Sstevel@tonic-gate 				nv = (val == 0) ? 0 :
16450Sstevel@tonic-gate 						ldap_count_values_len(val);
16460Sstevel@tonic-gate 				for (i = 0; i < nv; i++) {
16470Sstevel@tonic-gate 					/*
16480Sstevel@tonic-gate 					 * Since we don't know if the value is
16490Sstevel@tonic-gate 					 * BER-encoded or not, we mark it as a
16500Sstevel@tonic-gate 					 * string. All is well as long as we
16510Sstevel@tonic-gate 					 * don't insist on 'vt_ber' when
16520Sstevel@tonic-gate 					 * interpreting.
16530Sstevel@tonic-gate 					 */
16540Sstevel@tonic-gate 					if (addAttr2RuleValue(vt_string, nm,
16550Sstevel@tonic-gate 							val[i]->bv_val,
16560Sstevel@tonic-gate 							val[i]->bv_len,
16570Sstevel@tonic-gate 							&rv[numVals])) {
16580Sstevel@tonic-gate 						if (ber != 0)
16590Sstevel@tonic-gate 							ber_free(ber, 0);
16600Sstevel@tonic-gate 						ldap_value_free_len(val);
16610Sstevel@tonic-gate 						*ldapStat = LDAP_NO_MEMORY;
16620Sstevel@tonic-gate 						freeRuleValue(rv, tnv);
16630Sstevel@tonic-gate 						rv = 0;
16640Sstevel@tonic-gate 						goto cleanup;
16650Sstevel@tonic-gate 					}
16660Sstevel@tonic-gate 				}
16670Sstevel@tonic-gate 				/*
16680Sstevel@tonic-gate 				 * XXX the ldap_first_attribute(3LDAP) man
16690Sstevel@tonic-gate 				 * page says that the ldap_first_attribute/
16700Sstevel@tonic-gate 				 * ldap_next_attribute should be treated as
16710Sstevel@tonic-gate 				 * static, but the libldap.so.4 code mallocs
16720Sstevel@tonic-gate 				 * (and it's not TSD). So, in order to avoid
16730Sstevel@tonic-gate 				 * a leak, we free the return value.
16740Sstevel@tonic-gate 				 */
16750Sstevel@tonic-gate 				ldap_memfree(nm);
16760Sstevel@tonic-gate 				if (val != 0)
16770Sstevel@tonic-gate 					ldap_value_free_len(val);
16780Sstevel@tonic-gate 			}
16790Sstevel@tonic-gate 			/*
16800Sstevel@tonic-gate 			 * XXX ldap_next_attribute(3LDAP) says that the 'ber'
16810Sstevel@tonic-gate 			 * pointer is freed when it returns NULL, but that's
16820Sstevel@tonic-gate 			 * not implemented in the libldap.so.4 code, so we
16830Sstevel@tonic-gate 			 * free it here in order to avoid a memory leak.
16840Sstevel@tonic-gate 			 */
16850Sstevel@tonic-gate 			if (ber != 0)
16860Sstevel@tonic-gate 				ber_free(ber, 0);
16870Sstevel@tonic-gate 		}
16880Sstevel@tonic-gate 
16890Sstevel@tonic-gate 		if (numVals != tnv) {
16900Sstevel@tonic-gate 			logmsg(MSG_NOTIMECHECK, LOG_WARNING,
16910Sstevel@tonic-gate 		"%s: Inconsistent LDAP entry count, found = %d, expected %d",
16920Sstevel@tonic-gate 				myself, numVals, tnv);
16930Sstevel@tonic-gate 		}
16940Sstevel@tonic-gate 
16950Sstevel@tonic-gate 		if (doVLV) {
16960Sstevel@tonic-gate 			stat = ldap_parse_result(lc->ld, msg, &lprEc, 0, 0, 0,
16970Sstevel@tonic-gate 						&retCtrls, 0);
16980Sstevel@tonic-gate 			if (stat != LDAP_SUCCESS) {
16990Sstevel@tonic-gate 				ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
17000Sstevel@tonic-gate 						&stat);
17010Sstevel@tonic-gate 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
17020Sstevel@tonic-gate 					"%s: VLV parse result error: %s",
17030Sstevel@tonic-gate 					myself, ldap_err2string(stat));
17040Sstevel@tonic-gate 				*ldapStat = stat;
17050Sstevel@tonic-gate 				freeRuleValue(rv, tnv);
17060Sstevel@tonic-gate 				rv = 0;
17070Sstevel@tonic-gate 				goto cleanup;
17080Sstevel@tonic-gate 			}
17090Sstevel@tonic-gate 			if (retCtrls != 0) {
17100Sstevel@tonic-gate 				unsigned long	targetPosP = 0;
17110Sstevel@tonic-gate 				unsigned long	listSize = 0;
17120Sstevel@tonic-gate 
17130Sstevel@tonic-gate 				stat = ldap_parse_virtuallist_control(lc->ld,
17140Sstevel@tonic-gate 					retCtrls, &targetPosP, &listSize,
17150Sstevel@tonic-gate 					&lprEc);
17160Sstevel@tonic-gate 				if (stat == LDAP_SUCCESS) {
17170Sstevel@tonic-gate 					index = targetPosP + lc->batchFrom;
17180Sstevel@tonic-gate 					if (index >= listSize)
17190Sstevel@tonic-gate 						done = 1;
17200Sstevel@tonic-gate 				}
17210Sstevel@tonic-gate 				ldap_controls_free(retCtrls);
17220Sstevel@tonic-gate 				retCtrls = 0;
17230Sstevel@tonic-gate 			} else {
17240Sstevel@tonic-gate 				done = 1;
17250Sstevel@tonic-gate 			}
17260Sstevel@tonic-gate 		} else if (doSP) {
17270Sstevel@tonic-gate 			stat = ldap_parse_result(lc->ld, msg, &lprEc, 0, 0, 0,
17280Sstevel@tonic-gate 						&retCtrls, 0);
17290Sstevel@tonic-gate 			if (stat != LDAP_SUCCESS) {
17300Sstevel@tonic-gate 				ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
17310Sstevel@tonic-gate 						&stat);
17320Sstevel@tonic-gate 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
17330Sstevel@tonic-gate 				"%s: Simple page parse result error: %s",
17340Sstevel@tonic-gate 					myself, ldap_err2string(stat));
17350Sstevel@tonic-gate 				*ldapStat = stat;
17360Sstevel@tonic-gate 				freeRuleValue(rv, tnv);
17370Sstevel@tonic-gate 				rv = 0;
17380Sstevel@tonic-gate 				goto cleanup;
17390Sstevel@tonic-gate 			}
17400Sstevel@tonic-gate 			if (retCtrls != 0) {
17410Sstevel@tonic-gate 				unsigned int	count;
17420Sstevel@tonic-gate 
17430Sstevel@tonic-gate 				if (spCookie != 0) {
17440Sstevel@tonic-gate 					ber_bvfree(spCookie);
17450Sstevel@tonic-gate 					spCookie = 0;
17460Sstevel@tonic-gate 				}
17470Sstevel@tonic-gate 				stat = ldap_parse_page_control(lc->ld,
17480Sstevel@tonic-gate 						retCtrls, &count, &spCookie);
17490Sstevel@tonic-gate 				if (stat == LDAP_SUCCESS) {
17500Sstevel@tonic-gate 					if (spCookie == 0 ||
17510Sstevel@tonic-gate 						spCookie->bv_val == 0 ||
17520Sstevel@tonic-gate 						spCookie->bv_len == 0)
17530Sstevel@tonic-gate 						done = 1;
17540Sstevel@tonic-gate 				}
17550Sstevel@tonic-gate 				ldap_controls_free(retCtrls);
17560Sstevel@tonic-gate 				retCtrls = 0;
17570Sstevel@tonic-gate 			} else {
17580Sstevel@tonic-gate 				done = 1;
17590Sstevel@tonic-gate 			}
17600Sstevel@tonic-gate 		} else {
17610Sstevel@tonic-gate 			done = 1;
17620Sstevel@tonic-gate 		}
17630Sstevel@tonic-gate 
17640Sstevel@tonic-gate 		(void) ldap_msgfree(msg);
17650Sstevel@tonic-gate 		msg = 0;
17660Sstevel@tonic-gate 
17670Sstevel@tonic-gate 		/*
17680Sstevel@tonic-gate 		 * If we're using VLV or SP, the timeout should apply
17690Sstevel@tonic-gate 		 * to all calls as an aggregate, so we need to reduce
17700Sstevel@tonic-gate 		 * 'tv' with the time spent on this chunk of data.
17710Sstevel@tonic-gate 		 */
17720Sstevel@tonic-gate 		if (!done) {
17730Sstevel@tonic-gate 			struct timeval	tmp;
17740Sstevel@tonic-gate 
17750Sstevel@tonic-gate 			(void) gettimeofday(&now, 0);
17760Sstevel@tonic-gate 			tmp = now;
17770Sstevel@tonic-gate 			now.tv_sec -= start.tv_sec;
17780Sstevel@tonic-gate 			now.tv_usec -= start.tv_usec;
17790Sstevel@tonic-gate 			if (now.tv_usec < 0) {
17800Sstevel@tonic-gate 				now.tv_usec += 1000000;
17810Sstevel@tonic-gate 				now.tv_sec -= 1;
17820Sstevel@tonic-gate 			}
17830Sstevel@tonic-gate 			tv.tv_sec -= now.tv_sec;
17840Sstevel@tonic-gate 			tv.tv_usec -= now.tv_usec;
17850Sstevel@tonic-gate 			if (tv.tv_usec < 0) {
17860Sstevel@tonic-gate 				tv.tv_usec += 1000000;
17870Sstevel@tonic-gate 				tv.tv_sec -= 1;
17880Sstevel@tonic-gate 			}
17890Sstevel@tonic-gate 			if (tv.tv_sec < 0) {
17900Sstevel@tonic-gate 				*ldapStat = LDAP_TIMEOUT;
17910Sstevel@tonic-gate 				freeRuleValue(rv, tnv);
17920Sstevel@tonic-gate 				rv = 0;
17930Sstevel@tonic-gate 				goto cleanup;
17940Sstevel@tonic-gate 			}
17950Sstevel@tonic-gate 			start = tmp;
17960Sstevel@tonic-gate 		}
17970Sstevel@tonic-gate 
17980Sstevel@tonic-gate 	} while (!done);
17990Sstevel@tonic-gate 
18000Sstevel@tonic-gate 	if (numValues != 0)
18010Sstevel@tonic-gate 		*numValues = numVals;
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate cleanup:
18040Sstevel@tonic-gate 	if (NULL != lc) {
18050Sstevel@tonic-gate 		if (yp2ldap && ls->useCon == 0) {
18060Sstevel@tonic-gate 			/* Disconnect and free the connection */
18070Sstevel@tonic-gate 			lc->doDis++;
18080Sstevel@tonic-gate 			lc->doDel++;
18090Sstevel@tonic-gate 			releaseCon(lc, stat);
18100Sstevel@tonic-gate 			releaseLC(lc);
18110Sstevel@tonic-gate 
18120Sstevel@tonic-gate 		} else {
18130Sstevel@tonic-gate 			releaseCon(lc, stat);
18140Sstevel@tonic-gate 		}
18150Sstevel@tonic-gate 	}
18160Sstevel@tonic-gate 	if (msg != 0)
18170Sstevel@tonic-gate 		(void) ldap_msgfree(msg);
18180Sstevel@tonic-gate 	if (ctrls[0] != 0)
18190Sstevel@tonic-gate 		ldap_control_free(ctrls[0]);
18200Sstevel@tonic-gate 	if (ctrls[1] != 0)
18210Sstevel@tonic-gate 		ldap_control_free(ctrls[1]);
18220Sstevel@tonic-gate 	if (spCookie != 0)
18230Sstevel@tonic-gate 		ber_bvfree(spCookie);
18240Sstevel@tonic-gate 	if (sortKeyList != 0)
18250Sstevel@tonic-gate 		ldap_free_sort_keylist(sortKeyList);
18260Sstevel@tonic-gate 
18270Sstevel@tonic-gate 	return (rv);
18280Sstevel@tonic-gate }
18290Sstevel@tonic-gate 
18300Sstevel@tonic-gate static void
freeLdapModEntry(LDAPMod * m)18310Sstevel@tonic-gate freeLdapModEntry(LDAPMod *m) {
18320Sstevel@tonic-gate 
18330Sstevel@tonic-gate 	if (m == 0)
18340Sstevel@tonic-gate 		return;
18350Sstevel@tonic-gate 
18360Sstevel@tonic-gate 	sfree(m->mod_type);
18370Sstevel@tonic-gate 	if ((m->mod_op & LDAP_MOD_BVALUES) == 0) {
18380Sstevel@tonic-gate 		char	**v = m->mod_values;
18390Sstevel@tonic-gate 
18400Sstevel@tonic-gate 		if (v != 0) {
18410Sstevel@tonic-gate 			while (*v != 0) {
18420Sstevel@tonic-gate 				sfree(*v);
18430Sstevel@tonic-gate 				v++;
18440Sstevel@tonic-gate 			}
18450Sstevel@tonic-gate 			free(m->mod_values);
18460Sstevel@tonic-gate 		}
18470Sstevel@tonic-gate 	} else {
18480Sstevel@tonic-gate 		struct berval	**b = m->mod_bvalues;
18490Sstevel@tonic-gate 
18500Sstevel@tonic-gate 		if (b != 0) {
18510Sstevel@tonic-gate 			while (*b != 0) {
18520Sstevel@tonic-gate 				sfree((*b)->bv_val);
18530Sstevel@tonic-gate 				free(*b);
18540Sstevel@tonic-gate 				b++;
18550Sstevel@tonic-gate 			}
18560Sstevel@tonic-gate 			free(m->mod_bvalues);
18570Sstevel@tonic-gate 		}
18580Sstevel@tonic-gate 	}
18590Sstevel@tonic-gate 
18600Sstevel@tonic-gate 	free(m);
18610Sstevel@tonic-gate }
18620Sstevel@tonic-gate 
18630Sstevel@tonic-gate static void
freeLdapMod(LDAPMod ** mods)18640Sstevel@tonic-gate freeLdapMod(LDAPMod **mods) {
18650Sstevel@tonic-gate 	LDAPMod		*m, **org = mods;
18660Sstevel@tonic-gate 
18670Sstevel@tonic-gate 	if (mods == 0)
18680Sstevel@tonic-gate 		return;
18690Sstevel@tonic-gate 
18700Sstevel@tonic-gate 	while ((m = *mods) != 0) {
18710Sstevel@tonic-gate 		freeLdapModEntry(m);
18720Sstevel@tonic-gate 		mods++;
18730Sstevel@tonic-gate 	}
18740Sstevel@tonic-gate 
18750Sstevel@tonic-gate 	free(org);
18760Sstevel@tonic-gate }
18770Sstevel@tonic-gate 
18780Sstevel@tonic-gate /*
18790Sstevel@tonic-gate  * Convert a rule-value structure to the corresponding LDAPMod.
18800Sstevel@tonic-gate  * If 'add' is set, attributes/values are added; object classes
18810Sstevel@tonic-gate  * are also added. If 'add' is cleared, attributes/values are modified,
18820Sstevel@tonic-gate  * and 'oc' controls whether or not object classes are added.
18830Sstevel@tonic-gate  */
18840Sstevel@tonic-gate LDAPMod **
search2LdapMod(__nis_rule_value_t * rv,int add,int oc)18850Sstevel@tonic-gate search2LdapMod(__nis_rule_value_t *rv, int add, int oc) {
18860Sstevel@tonic-gate 	LDAPMod		**mods;
18870Sstevel@tonic-gate 	int		i, j, nm;
18880Sstevel@tonic-gate 	char		*myself = "search2LdapMod";
18890Sstevel@tonic-gate 
18900Sstevel@tonic-gate 	if (rv == 0 || rv->numAttrs <= 0)
18910Sstevel@tonic-gate 		return (0);
18920Sstevel@tonic-gate 
18930Sstevel@tonic-gate 	mods = am(myself, (rv->numAttrs + 1) * sizeof (mods[0]));
18940Sstevel@tonic-gate 	if (mods == 0)
18950Sstevel@tonic-gate 		return (0);
18960Sstevel@tonic-gate 
18970Sstevel@tonic-gate 	for (i = 0, nm = 0; i < rv->numAttrs; i++) {
18980Sstevel@tonic-gate 		int	isOc;
18990Sstevel@tonic-gate 		/*
19000Sstevel@tonic-gate 		 * If we're creating an LDAPMod array for an add operation,
19010Sstevel@tonic-gate 		 * just skip attributes that should be deleted.
19020Sstevel@tonic-gate 		 */
19030Sstevel@tonic-gate 		if (add && rv->attrVal[i].numVals < 0)
19040Sstevel@tonic-gate 			continue;
19050Sstevel@tonic-gate 
19060Sstevel@tonic-gate 		/*
19070Sstevel@tonic-gate 		 * Skip DN; it's specified separately to ldap_modify()
19080Sstevel@tonic-gate 		 * and ldap_add(), and mustn't appear among the
19090Sstevel@tonic-gate 		 * attributes to be modified/added.
19100Sstevel@tonic-gate 		 */
19110Sstevel@tonic-gate 		if (strcasecmp("dn", rv->attrName[i]) == 0)
19120Sstevel@tonic-gate 			continue;
19130Sstevel@tonic-gate 
19140Sstevel@tonic-gate 		/*
19150Sstevel@tonic-gate 		 * If modifying, and 'oc' is off, skip object class
19160Sstevel@tonic-gate 		 * attributes.
19170Sstevel@tonic-gate 		 */
19180Sstevel@tonic-gate 		isOc = (strcasecmp("objectclass", rv->attrName[i]) == 0);
19190Sstevel@tonic-gate 		if (!add && !oc && isOc)
19200Sstevel@tonic-gate 			continue;
19210Sstevel@tonic-gate 
19220Sstevel@tonic-gate 		mods[nm] = am(myself, sizeof (*mods[nm]));
19230Sstevel@tonic-gate 		if (mods[nm] == 0) {
19240Sstevel@tonic-gate 			freeLdapMod(mods);
19250Sstevel@tonic-gate 			return (0);
19260Sstevel@tonic-gate 		}
19270Sstevel@tonic-gate 
19280Sstevel@tonic-gate 		/* 'mod_type' is the attribute name */
19290Sstevel@tonic-gate 		mods[nm]->mod_type = sdup(myself, T, rv->attrName[i]);
19300Sstevel@tonic-gate 		if (mods[nm]->mod_type == 0) {
19310Sstevel@tonic-gate 			freeLdapMod(mods);
19320Sstevel@tonic-gate 			return (0);
19330Sstevel@tonic-gate 		}
19340Sstevel@tonic-gate 
19350Sstevel@tonic-gate 		/*
19360Sstevel@tonic-gate 		 * numVals < 0 means attribute and all values should
19370Sstevel@tonic-gate 		 * be deleted.
19380Sstevel@tonic-gate 		 */
19390Sstevel@tonic-gate 		if (rv->attrVal[i].numVals < 0) {
19400Sstevel@tonic-gate 			mods[nm]->mod_op = LDAP_MOD_DELETE;
19410Sstevel@tonic-gate 			mods[nm]->mod_values = 0;
19420Sstevel@tonic-gate 			nm++;
19430Sstevel@tonic-gate 			continue;
19440Sstevel@tonic-gate 		}
19450Sstevel@tonic-gate 
19460Sstevel@tonic-gate 		/* objectClass attributes always added */
19470Sstevel@tonic-gate 		mods[nm]->mod_op = (add) ? 0 : ((isOc) ? 0 : LDAP_MOD_REPLACE);
19480Sstevel@tonic-gate 
19490Sstevel@tonic-gate 		if (rv->attrVal[i].type == vt_string) {
19500Sstevel@tonic-gate 			/*
19510Sstevel@tonic-gate 			 * mods[]->mod_values is a NULL-terminated array
19520Sstevel@tonic-gate 			 * of (char *)'s.
19530Sstevel@tonic-gate 			 */
19540Sstevel@tonic-gate 			mods[nm]->mod_values = am(myself,
19550Sstevel@tonic-gate 					(rv->attrVal[i].numVals + 1) *
19560Sstevel@tonic-gate 					sizeof (mods[nm]->mod_values[0]));
19570Sstevel@tonic-gate 			if (mods[nm]->mod_values == 0) {
19580Sstevel@tonic-gate 				freeLdapMod(mods);
19590Sstevel@tonic-gate 				return (0);
19600Sstevel@tonic-gate 			}
19610Sstevel@tonic-gate 			for (j = 0; j < rv->attrVal[i].numVals; j++) {
19620Sstevel@tonic-gate 				/*
19630Sstevel@tonic-gate 				 * Just in case the string isn't NUL
19640Sstevel@tonic-gate 				 * terminated, add one byte to the
19650Sstevel@tonic-gate 				 * allocated length; am() will initialize
19660Sstevel@tonic-gate 				 * the buffer to zero.
19670Sstevel@tonic-gate 				 */
19680Sstevel@tonic-gate 				mods[nm]->mod_values[j] = am(myself,
19690Sstevel@tonic-gate 					rv->attrVal[i].val[j].length + 1);
19700Sstevel@tonic-gate 				if (mods[nm]->mod_values[j] == 0) {
19710Sstevel@tonic-gate 					freeLdapMod(mods);
19720Sstevel@tonic-gate 					return (0);
19730Sstevel@tonic-gate 				}
19740Sstevel@tonic-gate 				memcpy(mods[nm]->mod_values[j],
19750Sstevel@tonic-gate 					rv->attrVal[i].val[j].value,
19760Sstevel@tonic-gate 					rv->attrVal[i].val[j].length);
19770Sstevel@tonic-gate 			}
19780Sstevel@tonic-gate 		} else {
19790Sstevel@tonic-gate 			mods[nm]->mod_op |= LDAP_MOD_BVALUES;
19800Sstevel@tonic-gate 			mods[nm]->mod_bvalues = am(myself,
19810Sstevel@tonic-gate 					(rv->attrVal[i].numVals+1) *
19820Sstevel@tonic-gate 					sizeof (mods[nm]->mod_bvalues[0]));
19830Sstevel@tonic-gate 			if (mods[nm]->mod_bvalues == 0) {
19840Sstevel@tonic-gate 				freeLdapMod(mods);
19850Sstevel@tonic-gate 				return (0);
19860Sstevel@tonic-gate 			}
19870Sstevel@tonic-gate 			for (j = 0; j < rv->attrVal[i].numVals; j++) {
19880Sstevel@tonic-gate 				mods[nm]->mod_bvalues[j] = am(myself,
19890Sstevel@tonic-gate 					sizeof (*mods[nm]->mod_bvalues[j]));
19900Sstevel@tonic-gate 				if (mods[nm]->mod_bvalues[j] == 0) {
19910Sstevel@tonic-gate 					freeLdapMod(mods);
19920Sstevel@tonic-gate 					return (0);
19930Sstevel@tonic-gate 				}
19940Sstevel@tonic-gate 				mods[nm]->mod_bvalues[j]->bv_val = am(myself,
19950Sstevel@tonic-gate 					rv->attrVal[i].val[j].length);
19960Sstevel@tonic-gate 				if (mods[nm]->mod_bvalues[j]->bv_val == 0) {
19970Sstevel@tonic-gate 					freeLdapMod(mods);
19980Sstevel@tonic-gate 					return (0);
19990Sstevel@tonic-gate 				}
20000Sstevel@tonic-gate 				mods[nm]->mod_bvalues[j]->bv_len =
20010Sstevel@tonic-gate 					rv->attrVal[i].val[j].length;
20020Sstevel@tonic-gate 				memcpy(mods[nm]->mod_bvalues[j]->bv_val,
20030Sstevel@tonic-gate 					rv->attrVal[i].val[j].value,
20040Sstevel@tonic-gate 					mods[nm]->mod_bvalues[j]->bv_len);
20050Sstevel@tonic-gate 			}
20060Sstevel@tonic-gate 		}
20070Sstevel@tonic-gate 		nm++;
20080Sstevel@tonic-gate 	}
20090Sstevel@tonic-gate 
20100Sstevel@tonic-gate 	return (mods);
20110Sstevel@tonic-gate }
20120Sstevel@tonic-gate 
20130Sstevel@tonic-gate /*
20140Sstevel@tonic-gate  * Remove 'value' from 'val'. If value==0, remove the entire
20150Sstevel@tonic-gate  * __nis_single_value_t array from 'val'.
20160Sstevel@tonic-gate  */
20170Sstevel@tonic-gate static void
removeSingleValue(__nis_value_t * val,void * value,int length)20180Sstevel@tonic-gate removeSingleValue(__nis_value_t *val, void *value, int length) {
20190Sstevel@tonic-gate 	int	i;
20200Sstevel@tonic-gate 
20210Sstevel@tonic-gate 	if (val == 0)
20220Sstevel@tonic-gate 		return;
20230Sstevel@tonic-gate 
20240Sstevel@tonic-gate 	if (value == 0) {
20250Sstevel@tonic-gate 		for (i = 0; i < val->numVals; i++) {
20260Sstevel@tonic-gate 			sfree(val->val[i].value);
20270Sstevel@tonic-gate 		}
20280Sstevel@tonic-gate 		sfree(val->val);
20290Sstevel@tonic-gate 		val->val = 0;
20300Sstevel@tonic-gate 		val->numVals = 0;
20310Sstevel@tonic-gate 		return;
20320Sstevel@tonic-gate 	}
20330Sstevel@tonic-gate 
20340Sstevel@tonic-gate 	for (i = 0; i < val->numVals; i++) {
20350Sstevel@tonic-gate 		if (val->val[i].value == 0 || (val->val[i].length != length))
20360Sstevel@tonic-gate 			continue;
20370Sstevel@tonic-gate 		if (memcmp(val->val[i].value, value, length) != 0)
20380Sstevel@tonic-gate 			continue;
20390Sstevel@tonic-gate 		sfree(val->val[i].value);
20400Sstevel@tonic-gate 		if (i != (val->numVals - 1)) {
20410Sstevel@tonic-gate 			(void) memmove(&val->val[i], &val->val[i+1],
20420Sstevel@tonic-gate 				(val->numVals - 1 - i) * sizeof (val->val[0]));
20430Sstevel@tonic-gate 		}
20440Sstevel@tonic-gate 		val->numVals -= 1;
20450Sstevel@tonic-gate 		break;
20460Sstevel@tonic-gate 	}
20470Sstevel@tonic-gate }
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate /*
20500Sstevel@tonic-gate  * Helper function for LdapModify
20510Sstevel@tonic-gate  * When a modify operation fails with an object class violation,
20520Sstevel@tonic-gate  * the most probable reason is that the attributes we're modifying are new,
20530Sstevel@tonic-gate  * and the needed object class are not present. So, try the modify again,
20540Sstevel@tonic-gate  * but add the object classes this time.
20550Sstevel@tonic-gate  */
20560Sstevel@tonic-gate 
20570Sstevel@tonic-gate static int
ldapModifyObjectClass(__nis_ldap_conn_t ** lc,char * dn,__nis_rule_value_t * rvIn,char * objClassAttrs)20580Sstevel@tonic-gate ldapModifyObjectClass(__nis_ldap_conn_t **lc, char *dn,
20590Sstevel@tonic-gate 		__nis_rule_value_t *rvIn, char *objClassAttrs)
20600Sstevel@tonic-gate {
20610Sstevel@tonic-gate 	LDAPMod			**mods = 0;
20620Sstevel@tonic-gate 	int			msgid;
20630Sstevel@tonic-gate 	int			lderr;
20640Sstevel@tonic-gate 	struct timeval		tv;
20650Sstevel@tonic-gate 	int			stat;
20660Sstevel@tonic-gate 	LDAPMessage		*msg = 0;
20670Sstevel@tonic-gate 	char			**referralsp = NULL;
20680Sstevel@tonic-gate 	__nis_rule_value_t	*rv, *rvldap;
20690Sstevel@tonic-gate 	__nis_ldap_search_t	*ls;
20700Sstevel@tonic-gate 	int			i, ocrv, ocrvldap, nv;
20710Sstevel@tonic-gate 	char			*oc[2] = { "objectClass", 0};
20720Sstevel@tonic-gate 	char			*myself = "ldapModifyObjectClass";
20730Sstevel@tonic-gate 
20740Sstevel@tonic-gate 	rv = initRuleValue(1, rvIn);
20750Sstevel@tonic-gate 	if (rv == 0)
20760Sstevel@tonic-gate 		return (LDAP_NO_MEMORY);
20770Sstevel@tonic-gate 
20780Sstevel@tonic-gate 	delAttrFromRuleValue(rv, "objectClass");
20790Sstevel@tonic-gate 	rv = addObjectClasses(rv, objClassAttrs);
20800Sstevel@tonic-gate 	if (rv == 0) {
20810Sstevel@tonic-gate 		stat = LDAP_OPERATIONS_ERROR;
20820Sstevel@tonic-gate 		logmsg(MSG_NOTIMECHECK, LOG_WARNING,
2083*8035SSreedhar.Chalamalasetti@Sun.COM 		    "%s: addObjectClasses failed for %s",
2084*8035SSreedhar.Chalamalasetti@Sun.COM 		    myself, NIL(dn));
20850Sstevel@tonic-gate 		goto cleanup;
20860Sstevel@tonic-gate 	}
20870Sstevel@tonic-gate 
20880Sstevel@tonic-gate 	/*
20890Sstevel@tonic-gate 	 * Before adding the object classes whole-sale, try retrieving
20900Sstevel@tonic-gate 	 * the entry specified by the 'dn'. If it exists, we filter out
20910Sstevel@tonic-gate 	 * those object classes that already are present in LDAP from our
20920Sstevel@tonic-gate 	 * update.
20930Sstevel@tonic-gate 	 */
20940Sstevel@tonic-gate 	ls = buildLdapSearch(dn, LDAP_SCOPE_BASE, 0, 0, "objectClass=*",
2095*8035SSreedhar.Chalamalasetti@Sun.COM 	    oc, 0, 1);
20960Sstevel@tonic-gate 	if (ls == 0) {
20970Sstevel@tonic-gate 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
2098*8035SSreedhar.Chalamalasetti@Sun.COM 		    "%s: Unable to build DN search for \"%s\"",
2099*8035SSreedhar.Chalamalasetti@Sun.COM 		    myself, NIL(dn));
21000Sstevel@tonic-gate 		/* Fall through to try just adding the object classes */
21010Sstevel@tonic-gate 		goto addObjectClasses;
21020Sstevel@tonic-gate 	}
21030Sstevel@tonic-gate 
21040Sstevel@tonic-gate 	nv = 0;
21050Sstevel@tonic-gate 	rvldap = ldapSearch(ls, &nv, 0, &lderr);
21060Sstevel@tonic-gate 	freeLdapSearch(ls);
21070Sstevel@tonic-gate 	if (rvldap == 0) {
21080Sstevel@tonic-gate 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
2109*8035SSreedhar.Chalamalasetti@Sun.COM 		    "%s: No data for DN search (\"%s\"); LDAP status %d",
2110*8035SSreedhar.Chalamalasetti@Sun.COM 		    myself, NIL(dn), lderr);
21110Sstevel@tonic-gate 		/* Fall through to try just adding the object classes */
21120Sstevel@tonic-gate 		goto addObjectClasses;
21130Sstevel@tonic-gate 	}
21140Sstevel@tonic-gate 
21150Sstevel@tonic-gate 	/*
21160Sstevel@tonic-gate 	 * Find the indices of the 'objectClass' attribute
21170Sstevel@tonic-gate 	 * in 'rvldap' and 'rv'.
21180Sstevel@tonic-gate 	 */
21190Sstevel@tonic-gate 	for (i = 0, ocrvldap = -1; i < rvldap->numAttrs; i++) {
21200Sstevel@tonic-gate 		if (rvldap->attrName[i] != 0 &&
2121*8035SSreedhar.Chalamalasetti@Sun.COM 		    strcasecmp("objectClass", rvldap->attrName[i]) == 0) {
21220Sstevel@tonic-gate 			ocrvldap = i;
21230Sstevel@tonic-gate 			break;
21240Sstevel@tonic-gate 		}
21250Sstevel@tonic-gate 	}
21260Sstevel@tonic-gate 	for (i = 0, ocrv = -1; i < rv->numAttrs; i++) {
21270Sstevel@tonic-gate 		if (rv->attrName[i] != 0 &&
2128*8035SSreedhar.Chalamalasetti@Sun.COM 		    strcasecmp("objectClass", rv->attrName[i]) == 0) {
21290Sstevel@tonic-gate 			ocrv = i;
21300Sstevel@tonic-gate 			break;
21310Sstevel@tonic-gate 		}
21320Sstevel@tonic-gate 	}
21330Sstevel@tonic-gate 
21340Sstevel@tonic-gate 	/*
21350Sstevel@tonic-gate 	 * Remove those object classes that already exist
21360Sstevel@tonic-gate 	 * in LDAP (i.e., in 'rvldap') from 'rv'.
21370Sstevel@tonic-gate 	 */
21380Sstevel@tonic-gate 	if (ocrv >= 0 && ocrvldap >= 0) {
21390Sstevel@tonic-gate 		for (i = 0; i < rvldap->attrVal[ocrvldap].numVals; i++) {
21400Sstevel@tonic-gate 			removeSingleValue(&rv->attrVal[ocrv],
2141*8035SSreedhar.Chalamalasetti@Sun.COM 			    rvldap->attrVal[ocrvldap].val[i].value,
2142*8035SSreedhar.Chalamalasetti@Sun.COM 			    rvldap->attrVal[ocrvldap].val[i].length);
21430Sstevel@tonic-gate 		}
21440Sstevel@tonic-gate 		/*
21450Sstevel@tonic-gate 		 * If no 'objectClass' values left in 'rv', delete
21460Sstevel@tonic-gate 		 * 'objectClass' from 'rv'.
21470Sstevel@tonic-gate 		 */
21480Sstevel@tonic-gate 		if (rv->attrVal[ocrv].numVals == 0)
21490Sstevel@tonic-gate 			delAttrFromRuleValue(rv, "objectClass");
21500Sstevel@tonic-gate 	}
21510Sstevel@tonic-gate 
21520Sstevel@tonic-gate 	/*
21530Sstevel@tonic-gate 	 * 'rv' now contains the update we want to make, with just the
21540Sstevel@tonic-gate 	 * object class(es) that need to be added. Fall through to the
21550Sstevel@tonic-gate 	 * actual LDAP modify operation.
21560Sstevel@tonic-gate 	 */
21570Sstevel@tonic-gate 	freeRuleValue(rvldap, 1);
21580Sstevel@tonic-gate 
21590Sstevel@tonic-gate addObjectClasses:
21600Sstevel@tonic-gate 
21610Sstevel@tonic-gate 	mods = search2LdapMod(rv, 0, 1);
21620Sstevel@tonic-gate 	if (mods == 0) {
21630Sstevel@tonic-gate 		stat = LDAP_OPERATIONS_ERROR;
21640Sstevel@tonic-gate 		logmsg(MSG_NOTIMECHECK, LOG_WARNING,
21650Sstevel@tonic-gate 	"%s: Unable to create LDAP modify changes with object classes for %s",
2166*8035SSreedhar.Chalamalasetti@Sun.COM 		    myself, NIL(dn));
21670Sstevel@tonic-gate 		goto cleanup;
21680Sstevel@tonic-gate 	}
21690Sstevel@tonic-gate 	msgid = ldap_modify((*lc)->ld, dn, mods);
21700Sstevel@tonic-gate 	if (msgid != -1) {
21710Sstevel@tonic-gate 		tv = (*lc)->modifyTimeout;
21720Sstevel@tonic-gate 		stat = ldap_result((*lc)->ld, msgid, 0, &tv, &msg);
21730Sstevel@tonic-gate 		if (stat == 0) {
21740Sstevel@tonic-gate 			stat = LDAP_TIMEOUT;
21750Sstevel@tonic-gate 		} else if (stat == -1) {
21760Sstevel@tonic-gate 			(void) ldap_get_option((*lc)->ld,
2177*8035SSreedhar.Chalamalasetti@Sun.COM 			    LDAP_OPT_ERROR_NUMBER, &stat);
21780Sstevel@tonic-gate 		} else {
21790Sstevel@tonic-gate 			stat = ldap_parse_result((*lc)->ld, msg, &lderr, NULL,
2180*8035SSreedhar.Chalamalasetti@Sun.COM 			    NULL, &referralsp, NULL, 0);
21810Sstevel@tonic-gate 			if (stat == LDAP_SUCCESS)
21820Sstevel@tonic-gate 				stat = lderr;
21830Sstevel@tonic-gate 			stat = ldap_result2error((*lc)->ld, msg, 0);
21840Sstevel@tonic-gate 		}
21850Sstevel@tonic-gate 	} else {
21860Sstevel@tonic-gate 		(void) ldap_get_option((*lc)->ld, LDAP_OPT_ERROR_NUMBER,
2187*8035SSreedhar.Chalamalasetti@Sun.COM 		    &stat);
21880Sstevel@tonic-gate 	}
21890Sstevel@tonic-gate 	if (proxyInfo.follow_referral == follow &&
2190*8035SSreedhar.Chalamalasetti@Sun.COM 	    stat == LDAP_REFERRAL && referralsp != NULL) {
21910Sstevel@tonic-gate 		releaseCon(*lc, stat);
21920Sstevel@tonic-gate 		if (msg != NULL)
21930Sstevel@tonic-gate 			(void) ldap_msgfree(msg);
21940Sstevel@tonic-gate 		msg = NULL;
21950Sstevel@tonic-gate 		*lc = findReferralCon(referralsp, &stat);
21960Sstevel@tonic-gate 		ldap_value_free(referralsp);
21970Sstevel@tonic-gate 		referralsp = NULL;
21980Sstevel@tonic-gate 		if (*lc == NULL)
21990Sstevel@tonic-gate 			goto cleanup;
22000Sstevel@tonic-gate 		msgid = ldap_modify((*lc)->ld, dn, mods);
22010Sstevel@tonic-gate 		if (msgid == -1) {
22020Sstevel@tonic-gate 			(void) ldap_get_option((*lc)->ld,
2203*8035SSreedhar.Chalamalasetti@Sun.COM 			    LDAP_OPT_ERROR_NUMBER, &stat);
22040Sstevel@tonic-gate 			goto cleanup;
22050Sstevel@tonic-gate 		}
22060Sstevel@tonic-gate 		stat = ldap_result((*lc)->ld, msgid, 0, &tv, &msg);
22070Sstevel@tonic-gate 		if (stat == 0) {
22080Sstevel@tonic-gate 			stat = LDAP_TIMEOUT;
22090Sstevel@tonic-gate 		} else if (stat == -1) {
22100Sstevel@tonic-gate 			(void) ldap_get_option((*lc)->ld,
2211*8035SSreedhar.Chalamalasetti@Sun.COM 			    LDAP_OPT_ERROR_NUMBER, &stat);
22120Sstevel@tonic-gate 		} else {
22130Sstevel@tonic-gate 			stat = ldap_parse_result((*lc)->ld, msg, &lderr,
2214*8035SSreedhar.Chalamalasetti@Sun.COM 			    NULL, NULL, NULL, NULL, 0);
22150Sstevel@tonic-gate 			if (stat == LDAP_SUCCESS)
22160Sstevel@tonic-gate 				stat = lderr;
22170Sstevel@tonic-gate 		}
22180Sstevel@tonic-gate 	}
22190Sstevel@tonic-gate cleanup:
22200Sstevel@tonic-gate 	if (mods != 0)
22210Sstevel@tonic-gate 		freeLdapMod(mods);
22220Sstevel@tonic-gate 	freeRuleValue(rv, 1);
22230Sstevel@tonic-gate 	return (stat);
22240Sstevel@tonic-gate }
22250Sstevel@tonic-gate 
22260Sstevel@tonic-gate /*
22270Sstevel@tonic-gate  * Modify the specified 'dn' per the attribute names/values in 'rv'.
22280Sstevel@tonic-gate  * If 'rv' is NULL, we attempt to delete the entire entry.
22290Sstevel@tonic-gate  *
22300Sstevel@tonic-gate  * The 'objClassAttrs' parameter is needed if the entry must be added
22310Sstevel@tonic-gate  * (i.e., created), or a modify fails with an object class violation.
22320Sstevel@tonic-gate  *
22330Sstevel@tonic-gate  * If 'addFirst' is set, we try an add before a modify; modify before
22340Sstevel@tonic-gate  * add otherwise (ignored if we're deleting).
22350Sstevel@tonic-gate  */
22360Sstevel@tonic-gate int
ldapModify(char * dn,__nis_rule_value_t * rv,char * objClassAttrs,int addFirst)22370Sstevel@tonic-gate ldapModify(char *dn, __nis_rule_value_t *rv, char *objClassAttrs,
22380Sstevel@tonic-gate 		int addFirst) {
22390Sstevel@tonic-gate 	int			stat, add = 0;
22400Sstevel@tonic-gate 	LDAPMod			**mods = 0;
22410Sstevel@tonic-gate 	__nis_ldap_conn_t	*lc;
22420Sstevel@tonic-gate 	struct timeval		tv;
22430Sstevel@tonic-gate 	LDAPMessage		*msg = 0;
22440Sstevel@tonic-gate 	char			*myself = "ldapModify";
22450Sstevel@tonic-gate 	int			msgid;
22460Sstevel@tonic-gate 	int			lderr;
22470Sstevel@tonic-gate 	char			**referralsp = NULL;
22480Sstevel@tonic-gate 	bool_t			delete = FALSE;
22490Sstevel@tonic-gate 
22500Sstevel@tonic-gate 	if (dn == 0)
22510Sstevel@tonic-gate 		return (LDAP_PARAM_ERROR);
22520Sstevel@tonic-gate 
22530Sstevel@tonic-gate 	if ((lc = findCon(&stat)) == 0)
22540Sstevel@tonic-gate 		return (stat);
22550Sstevel@tonic-gate 
22560Sstevel@tonic-gate 	if (rv == 0) {
22570Sstevel@tonic-gate 		delete = TRUE;
22580Sstevel@tonic-gate 		/* Simple case: if rv == 0, try to delete the entire entry */
22590Sstevel@tonic-gate 		msgid = ldap_delete(lc->ld, dn);
22600Sstevel@tonic-gate 		if (msgid == -1) {
22610Sstevel@tonic-gate 			(void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
22620Sstevel@tonic-gate 						&stat);
22630Sstevel@tonic-gate 			goto cleanup;
22640Sstevel@tonic-gate 		}
22650Sstevel@tonic-gate 		tv = lc->deleteTimeout;
22660Sstevel@tonic-gate 		stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
22670Sstevel@tonic-gate 
22680Sstevel@tonic-gate 		if (stat == 0) {
22690Sstevel@tonic-gate 			stat = LDAP_TIMEOUT;
22700Sstevel@tonic-gate 		} else if (stat == -1) {
22710Sstevel@tonic-gate 			(void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
22720Sstevel@tonic-gate 						&stat);
22730Sstevel@tonic-gate 		} else {
22740Sstevel@tonic-gate 			stat = ldap_parse_result(lc->ld, msg, &lderr, NULL,
22750Sstevel@tonic-gate 				NULL, &referralsp, NULL, 0);
22760Sstevel@tonic-gate 			if (stat == LDAP_SUCCESS)
22770Sstevel@tonic-gate 				stat = lderr;
22780Sstevel@tonic-gate 		}
22790Sstevel@tonic-gate 		if (proxyInfo.follow_referral == follow &&
22800Sstevel@tonic-gate 				stat == LDAP_REFERRAL && referralsp != NULL) {
22810Sstevel@tonic-gate 			releaseCon(lc, stat);
22820Sstevel@tonic-gate 			if (msg != NULL)
22830Sstevel@tonic-gate 				(void) ldap_msgfree(msg);
22840Sstevel@tonic-gate 			msg = NULL;
22850Sstevel@tonic-gate 			lc = findReferralCon(referralsp, &stat);
22860Sstevel@tonic-gate 			ldap_value_free(referralsp);
22870Sstevel@tonic-gate 			if (lc == NULL)
22880Sstevel@tonic-gate 				goto cleanup;
22890Sstevel@tonic-gate 			msgid = ldap_delete(lc->ld, dn);
22900Sstevel@tonic-gate 			if (msgid == -1) {
22910Sstevel@tonic-gate 				(void) ldap_get_option(lc->ld,
22920Sstevel@tonic-gate 					LDAP_OPT_ERROR_NUMBER, &stat);
22930Sstevel@tonic-gate 				goto cleanup;
22940Sstevel@tonic-gate 			}
22950Sstevel@tonic-gate 			stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
22960Sstevel@tonic-gate 			if (stat == 0) {
22970Sstevel@tonic-gate 				stat = LDAP_TIMEOUT;
22980Sstevel@tonic-gate 			} else if (stat == -1) {
22990Sstevel@tonic-gate 				(void) ldap_get_option(lc->ld,
23000Sstevel@tonic-gate 					LDAP_OPT_ERROR_NUMBER, &stat);
23010Sstevel@tonic-gate 			} else {
23020Sstevel@tonic-gate 				stat = ldap_parse_result(lc->ld, msg, &lderr,
23030Sstevel@tonic-gate 					NULL, NULL, NULL, NULL, 0);
23040Sstevel@tonic-gate 				if (stat == LDAP_SUCCESS)
23050Sstevel@tonic-gate 					stat = lderr;
23060Sstevel@tonic-gate 			}
23070Sstevel@tonic-gate 		}
23080Sstevel@tonic-gate 		/* No such object means someone else has done our job */
23090Sstevel@tonic-gate 		if (stat == LDAP_NO_SUCH_OBJECT)
23100Sstevel@tonic-gate 			stat = LDAP_SUCCESS;
23110Sstevel@tonic-gate 	} else {
23120Sstevel@tonic-gate 		if (addFirst) {
23130Sstevel@tonic-gate 			stat = ldapAdd(dn, rv, objClassAttrs, lc);
23140Sstevel@tonic-gate 			lc = NULL;
23150Sstevel@tonic-gate 			if (stat != LDAP_ALREADY_EXISTS)
23160Sstevel@tonic-gate 				goto cleanup;
23170Sstevel@tonic-gate 			if ((lc = findCon(&stat)) == 0)
23180Sstevel@tonic-gate 				return (stat);
23190Sstevel@tonic-gate 		}
23200Sstevel@tonic-gate 
23210Sstevel@tonic-gate 		/*
23220Sstevel@tonic-gate 		 * First try the modify without specifying object classes
23230Sstevel@tonic-gate 		 * (i.e., assume they're already present).
23240Sstevel@tonic-gate 		 */
23250Sstevel@tonic-gate 		mods = search2LdapMod(rv, 0, 0);
23260Sstevel@tonic-gate 		if (mods == 0) {
23270Sstevel@tonic-gate 			stat = LDAP_PARAM_ERROR;
23280Sstevel@tonic-gate 			goto cleanup;
23290Sstevel@tonic-gate 		}
23300Sstevel@tonic-gate 
23310Sstevel@tonic-gate 		msgid = ldap_modify(lc->ld, dn, mods);
23320Sstevel@tonic-gate 		if (msgid == -1) {
23330Sstevel@tonic-gate 			(void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
23340Sstevel@tonic-gate 						&stat);
23350Sstevel@tonic-gate 			goto cleanup;
23360Sstevel@tonic-gate 		}
23370Sstevel@tonic-gate 		tv = lc->modifyTimeout;
23380Sstevel@tonic-gate 		stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
23390Sstevel@tonic-gate 		if (stat == 0) {
23400Sstevel@tonic-gate 			stat = LDAP_TIMEOUT;
23410Sstevel@tonic-gate 		} else if (stat == -1) {
23420Sstevel@tonic-gate 			(void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
23430Sstevel@tonic-gate 						&stat);
23440Sstevel@tonic-gate 		} else {
23450Sstevel@tonic-gate 			stat = ldap_parse_result(lc->ld, msg, &lderr, NULL,
23460Sstevel@tonic-gate 				NULL, &referralsp, NULL, 0);
23470Sstevel@tonic-gate 			if (stat == LDAP_SUCCESS)
23480Sstevel@tonic-gate 				stat = lderr;
23490Sstevel@tonic-gate 		}
23500Sstevel@tonic-gate 		if (proxyInfo.follow_referral == follow &&
23510Sstevel@tonic-gate 				stat == LDAP_REFERRAL && referralsp != NULL) {
23520Sstevel@tonic-gate 			releaseCon(lc, stat);
23530Sstevel@tonic-gate 			if (msg != NULL)
23540Sstevel@tonic-gate 				(void) ldap_msgfree(msg);
23550Sstevel@tonic-gate 			msg = NULL;
23560Sstevel@tonic-gate 			lc = findReferralCon(referralsp, &stat);
23570Sstevel@tonic-gate 			ldap_value_free(referralsp);
23580Sstevel@tonic-gate 			referralsp = NULL;
23590Sstevel@tonic-gate 			if (lc == NULL)
23600Sstevel@tonic-gate 				goto cleanup;
23610Sstevel@tonic-gate 			msgid = ldap_modify(lc->ld, dn, mods);
23620Sstevel@tonic-gate 			if (msgid == -1) {
23630Sstevel@tonic-gate 				(void) ldap_get_option(lc->ld,
23640Sstevel@tonic-gate 					LDAP_OPT_ERROR_NUMBER, &stat);
23650Sstevel@tonic-gate 				goto cleanup;
23660Sstevel@tonic-gate 			}
23670Sstevel@tonic-gate 			stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
23680Sstevel@tonic-gate 			if (stat == 0) {
23690Sstevel@tonic-gate 				stat = LDAP_TIMEOUT;
23700Sstevel@tonic-gate 			} else if (stat == -1) {
23710Sstevel@tonic-gate 				(void) ldap_get_option(lc->ld,
23720Sstevel@tonic-gate 					LDAP_OPT_ERROR_NUMBER, &stat);
23730Sstevel@tonic-gate 			} else {
23740Sstevel@tonic-gate 				stat = ldap_parse_result(lc->ld, msg, &lderr,
23750Sstevel@tonic-gate 					NULL, NULL, NULL, NULL, 0);
23760Sstevel@tonic-gate 				if (stat == LDAP_SUCCESS)
23770Sstevel@tonic-gate 					stat = lderr;
23780Sstevel@tonic-gate 			}
23790Sstevel@tonic-gate 		}
23800Sstevel@tonic-gate 
23810Sstevel@tonic-gate 		/*
23820Sstevel@tonic-gate 		 * If the modify failed with an object class violation,
23830Sstevel@tonic-gate 		 * the most probable reason is that at least on of the
23840Sstevel@tonic-gate 		 * attributes we're modifying didn't exist before, and
23850Sstevel@tonic-gate 		 * neither did its object class. So, try the modify again,
23860Sstevel@tonic-gate 		 * but add the object classes this time.
23870Sstevel@tonic-gate 		 */
23880Sstevel@tonic-gate 		if (stat == LDAP_OBJECT_CLASS_VIOLATION &&
23890Sstevel@tonic-gate 				objClassAttrs != 0) {
23900Sstevel@tonic-gate 			freeLdapMod(mods);
23910Sstevel@tonic-gate 			mods = 0;
23920Sstevel@tonic-gate 			stat = ldapModifyObjectClass(&lc, dn, rv,
23930Sstevel@tonic-gate 				objClassAttrs);
23940Sstevel@tonic-gate 		}
23950Sstevel@tonic-gate 
23960Sstevel@tonic-gate 		if (stat == LDAP_NO_SUCH_ATTRIBUTE) {
23970Sstevel@tonic-gate 			/*
23980Sstevel@tonic-gate 			 * If there was at least one attribute delete, then
23990Sstevel@tonic-gate 			 * the cause of this error could be that said attribute
24000Sstevel@tonic-gate 			 * didn't exist in LDAP. So, do things the slow way,
24010Sstevel@tonic-gate 			 * and try to delete one attribute at a time.
24020Sstevel@tonic-gate 			 */
24030Sstevel@tonic-gate 			int			d, numDelete, st;
24040Sstevel@tonic-gate 			__nis_rule_value_t	*rvt;
24050Sstevel@tonic-gate 
24060Sstevel@tonic-gate 			for (d = 0, numDelete = 0; d < rv->numAttrs; d++) {
24070Sstevel@tonic-gate 				if (rv->attrVal[d].numVals < 0)
24080Sstevel@tonic-gate 					numDelete++;
24090Sstevel@tonic-gate 			}
24100Sstevel@tonic-gate 
24110Sstevel@tonic-gate 			/* If there's just one, we've already tried */
24120Sstevel@tonic-gate 			if (numDelete <= 1)
24130Sstevel@tonic-gate 				goto cleanup;
24140Sstevel@tonic-gate 
24150Sstevel@tonic-gate 			/* Make a copy of the rule value */
24160Sstevel@tonic-gate 			rvt = initRuleValue(1, rv);
24170Sstevel@tonic-gate 			if (rvt == 0)
24180Sstevel@tonic-gate 				goto cleanup;
24190Sstevel@tonic-gate 
24200Sstevel@tonic-gate 			/*
24210Sstevel@tonic-gate 			 * Remove all delete attributes from the tmp
24220Sstevel@tonic-gate 			 * rule value.
24230Sstevel@tonic-gate 			 */
24240Sstevel@tonic-gate 			for (d = 0; d < rv->numAttrs; d++) {
24250Sstevel@tonic-gate 				if (rv->attrVal[d].numVals < 0) {
24260Sstevel@tonic-gate 					delAttrFromRuleValue(rvt,
24270Sstevel@tonic-gate 						rv->attrName[d]);
24280Sstevel@tonic-gate 				}
24290Sstevel@tonic-gate 			}
24300Sstevel@tonic-gate 
24310Sstevel@tonic-gate 			/*
24320Sstevel@tonic-gate 			 * Now put the attributes back in one by one, and
24330Sstevel@tonic-gate 			 * invoke ourselves.
24340Sstevel@tonic-gate 			 */
24350Sstevel@tonic-gate 			for (d = 0; d < rv->numAttrs; d++) {
24360Sstevel@tonic-gate 				if (rv->attrVal[d].numVals >= 0)
24370Sstevel@tonic-gate 					continue;
24380Sstevel@tonic-gate 				st = addAttr2RuleValue(rv->attrVal[d].type,
24390Sstevel@tonic-gate 					rv->attrName[d], 0, 0, rvt);
24400Sstevel@tonic-gate 				if (st != 0) {
24410Sstevel@tonic-gate 					logmsg(MSG_NOMEM, LOG_ERR,
24420Sstevel@tonic-gate 					"%s: Error deleting \"%s\" for \"%s\"",
24430Sstevel@tonic-gate 						NIL(rv->attrName[d]), NIL(dn));
24440Sstevel@tonic-gate 					stat = LDAP_NO_MEMORY;
24450Sstevel@tonic-gate 					freeRuleValue(rvt, 1);
24460Sstevel@tonic-gate 					goto cleanup;
24470Sstevel@tonic-gate 				}
24480Sstevel@tonic-gate 				stat = ldapModify(dn, rvt, objClassAttrs, 0);
24490Sstevel@tonic-gate 				if (stat != LDAP_SUCCESS &&
24500Sstevel@tonic-gate 					stat != LDAP_NO_SUCH_ATTRIBUTE) {
24510Sstevel@tonic-gate 					freeRuleValue(rvt, 1);
24520Sstevel@tonic-gate 					goto cleanup;
24530Sstevel@tonic-gate 				}
24540Sstevel@tonic-gate 				delAttrFromRuleValue(rvt, rv->attrName[d]);
24550Sstevel@tonic-gate 			}
24560Sstevel@tonic-gate 
24570Sstevel@tonic-gate 			/*
24580Sstevel@tonic-gate 			 * If we got here, then all attributes that should
24590Sstevel@tonic-gate 			 * be deleted either have been, or didn't exist. For
24600Sstevel@tonic-gate 			 * our purposes, the latter is as good as the former.
24610Sstevel@tonic-gate 			 */
24620Sstevel@tonic-gate 			stat = LDAP_SUCCESS;
24630Sstevel@tonic-gate 			freeRuleValue(rvt, 1);
24640Sstevel@tonic-gate 		}
24650Sstevel@tonic-gate 
24660Sstevel@tonic-gate 		if (stat == LDAP_NO_SUCH_OBJECT && !addFirst) {
24670Sstevel@tonic-gate 			/*
24680Sstevel@tonic-gate 			 * Entry doesn't exist, so try an ldap_add(). If the
24690Sstevel@tonic-gate 			 * ldap_add() also fails, that could be because someone
24700Sstevel@tonic-gate 			 * else added it between our modify and add operations.
24710Sstevel@tonic-gate 			 * If so, we consider that foreign add to be
24720Sstevel@tonic-gate 			 * authoritative (meaning we don't retry our modify).
24730Sstevel@tonic-gate 			 *
24740Sstevel@tonic-gate 			 * Also, if all modify operations specified by 'mods'
24750Sstevel@tonic-gate 			 * are deletes, LDAP_NO_SUCH_OBJECT is a kind of
24760Sstevel@tonic-gate 			 * success; we certainly don't want to create the
24770Sstevel@tonic-gate 			 * entry.
24780Sstevel@tonic-gate 			 */
24790Sstevel@tonic-gate 			int	allDelete;
24800Sstevel@tonic-gate 			LDAPMod	**m;
24810Sstevel@tonic-gate 
24820Sstevel@tonic-gate 			for (m = mods, allDelete = 1; *m != 0 && allDelete;
24830Sstevel@tonic-gate 					m++) {
24840Sstevel@tonic-gate 				if (((*m)->mod_op & LDAP_MOD_DELETE) == 0)
24850Sstevel@tonic-gate 					allDelete = 0;
24860Sstevel@tonic-gate 			}
24870Sstevel@tonic-gate 
24880Sstevel@tonic-gate 			add = 1;
24890Sstevel@tonic-gate 
24900Sstevel@tonic-gate 			if (allDelete) {
24910Sstevel@tonic-gate 				stat = LDAP_SUCCESS;
24920Sstevel@tonic-gate 			} else if (objClassAttrs == 0) {
24930Sstevel@tonic-gate 				/* Now we need it, so this is fatal */
24940Sstevel@tonic-gate 				stat = LDAP_PARAM_ERROR;
24950Sstevel@tonic-gate 			} else {
24960Sstevel@tonic-gate 				stat = ldapAdd(dn, rv, objClassAttrs, lc);
24970Sstevel@tonic-gate 				lc = NULL;
24980Sstevel@tonic-gate 			}
24990Sstevel@tonic-gate 		}
25000Sstevel@tonic-gate 	}
25010Sstevel@tonic-gate 
25020Sstevel@tonic-gate cleanup:
25030Sstevel@tonic-gate 	if (stat != LDAP_SUCCESS) {
25040Sstevel@tonic-gate 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
25050Sstevel@tonic-gate 			"%s(0x%x (%s), \"%s\") => %d (%s)\n",
25060Sstevel@tonic-gate 			!delete ? (add ? "ldap_add" : "ldap_modify") :
25070Sstevel@tonic-gate 				"ldap_delete",
25080Sstevel@tonic-gate 			lc != NULL ? lc->ld : 0,
25090Sstevel@tonic-gate 			lc != NULL ? NIL(lc->sp) : "nil",
25100Sstevel@tonic-gate 			dn, stat, ldap_err2string(stat));
25110Sstevel@tonic-gate 	}
25120Sstevel@tonic-gate 
25130Sstevel@tonic-gate 	releaseCon(lc, stat);
25140Sstevel@tonic-gate 	freeLdapMod(mods);
25150Sstevel@tonic-gate 	if (msg != 0)
25160Sstevel@tonic-gate 		(void) ldap_msgfree(msg);
25170Sstevel@tonic-gate 
25180Sstevel@tonic-gate 	return (stat);
25190Sstevel@tonic-gate }
25200Sstevel@tonic-gate 
25210Sstevel@tonic-gate /*
25220Sstevel@tonic-gate  * Create the entry specified by 'dn' to have the values per 'rv'.
25230Sstevel@tonic-gate  * The 'objClassAttrs' are the extra object classes we need when
25240Sstevel@tonic-gate  * creating an entry.
25250Sstevel@tonic-gate  *
25260Sstevel@tonic-gate  * If 'lc' is non-NULL, we use that connection; otherwise, we find
25270Sstevel@tonic-gate  * our own. CAUTION: This connection will be released on return. Regardless
25280Sstevel@tonic-gate  * of return value, this connection should not subsequently used by the
25290Sstevel@tonic-gate  * caller.
25300Sstevel@tonic-gate  *
25310Sstevel@tonic-gate  * Returns an LDAP status.
25320Sstevel@tonic-gate  */
25330Sstevel@tonic-gate int
ldapAdd(char * dn,__nis_rule_value_t * rv,char * objClassAttrs,void * lcv)25340Sstevel@tonic-gate ldapAdd(char *dn, __nis_rule_value_t *rv, char *objClassAttrs, void *lcv) {
25350Sstevel@tonic-gate 	int			stat;
25360Sstevel@tonic-gate 	LDAPMod			**mods = 0;
25370Sstevel@tonic-gate 	struct timeval		tv;
25380Sstevel@tonic-gate 	LDAPMessage		*msg = 0;
25390Sstevel@tonic-gate 	__nis_ldap_conn_t	*lc = lcv;
25400Sstevel@tonic-gate 	int			msgid;
25410Sstevel@tonic-gate 	int			lderr;
25420Sstevel@tonic-gate 	char			**referralsp = NULL;
25430Sstevel@tonic-gate 
25440Sstevel@tonic-gate 	if (dn == 0 || rv == 0 || objClassAttrs == 0) {
25450Sstevel@tonic-gate 		releaseCon(lc, LDAP_SUCCESS);
25460Sstevel@tonic-gate 		return (LDAP_PARAM_ERROR);
25470Sstevel@tonic-gate 	}
25480Sstevel@tonic-gate 
25490Sstevel@tonic-gate 	if (lc == 0) {
25500Sstevel@tonic-gate 		if ((lc = findCon(&stat)) == 0)
25510Sstevel@tonic-gate 			return (stat);
25520Sstevel@tonic-gate 	}
25530Sstevel@tonic-gate 
25540Sstevel@tonic-gate 	rv = addObjectClasses(rv, objClassAttrs);
25550Sstevel@tonic-gate 	if (rv == 0) {
25560Sstevel@tonic-gate 		stat = LDAP_OPERATIONS_ERROR;
25570Sstevel@tonic-gate 		goto cleanup;
25580Sstevel@tonic-gate 	}
25590Sstevel@tonic-gate 
25600Sstevel@tonic-gate 	mods = search2LdapMod(rv, 1, 0);
25610Sstevel@tonic-gate 	if (mods == 0) {
25620Sstevel@tonic-gate 		stat = LDAP_OPERATIONS_ERROR;
25630Sstevel@tonic-gate 		goto cleanup;
25640Sstevel@tonic-gate 	}
25650Sstevel@tonic-gate 
25660Sstevel@tonic-gate 	msgid = ldap_add(lc->ld, dn, mods);
25670Sstevel@tonic-gate 	if (msgid == -1) {
25680Sstevel@tonic-gate 		(void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat);
25690Sstevel@tonic-gate 		goto cleanup;
25700Sstevel@tonic-gate 	}
25710Sstevel@tonic-gate 	tv = lc->addTimeout;
25720Sstevel@tonic-gate 	stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
25730Sstevel@tonic-gate 	if (stat == 0) {
25740Sstevel@tonic-gate 		stat = LDAP_TIMEOUT;
25750Sstevel@tonic-gate 	} else if (stat == -1) {
25760Sstevel@tonic-gate 		(void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat);
25770Sstevel@tonic-gate 	} else {
25780Sstevel@tonic-gate 		stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, NULL,
25790Sstevel@tonic-gate 			&referralsp, NULL, 0);
25800Sstevel@tonic-gate 		if (stat == LDAP_SUCCESS)
25810Sstevel@tonic-gate 			stat = lderr;
25820Sstevel@tonic-gate 	}
25830Sstevel@tonic-gate 	if (proxyInfo.follow_referral == follow && stat == LDAP_REFERRAL &&
25840Sstevel@tonic-gate 			referralsp != NULL) {
25850Sstevel@tonic-gate 		releaseCon(lc, stat);
25860Sstevel@tonic-gate 		if (msg != NULL)
25870Sstevel@tonic-gate 			(void) ldap_msgfree(msg);
25880Sstevel@tonic-gate 		msg = NULL;
25890Sstevel@tonic-gate 		lc = findReferralCon(referralsp, &stat);
25900Sstevel@tonic-gate 		ldap_value_free(referralsp);
25910Sstevel@tonic-gate 		if (lc == NULL)
25920Sstevel@tonic-gate 			goto cleanup;
25930Sstevel@tonic-gate 		msgid = ldap_add(lc->ld, dn, mods);
25940Sstevel@tonic-gate 		if (msgid == -1) {
25950Sstevel@tonic-gate 			(void) ldap_get_option(lc->ld,
25960Sstevel@tonic-gate 				LDAP_OPT_ERROR_NUMBER, &stat);
25970Sstevel@tonic-gate 			goto cleanup;
25980Sstevel@tonic-gate 		}
25990Sstevel@tonic-gate 		stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
26000Sstevel@tonic-gate 		if (stat == 0) {
26010Sstevel@tonic-gate 			stat = LDAP_TIMEOUT;
26020Sstevel@tonic-gate 		} else if (stat == -1) {
26030Sstevel@tonic-gate 			(void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
26040Sstevel@tonic-gate 						&stat);
26050Sstevel@tonic-gate 		} else {
26060Sstevel@tonic-gate 			stat = ldap_parse_result(lc->ld, msg, &lderr, NULL,
26070Sstevel@tonic-gate 				NULL, NULL, NULL, 0);
26080Sstevel@tonic-gate 			if (stat == LDAP_SUCCESS)
26090Sstevel@tonic-gate 				stat = lderr;
26100Sstevel@tonic-gate 		}
26110Sstevel@tonic-gate 	}
26120Sstevel@tonic-gate 
26130Sstevel@tonic-gate cleanup:
26140Sstevel@tonic-gate 	if (stat != LDAP_SUCCESS) {
26150Sstevel@tonic-gate 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
26160Sstevel@tonic-gate 			"ldap_add(0x%x (%s), \"%s\") => %d (%s)\n",
26170Sstevel@tonic-gate 			lc != NULL ? lc->ld : 0,
26180Sstevel@tonic-gate 			lc != NULL ? NIL(lc->sp) : "nil",
26190Sstevel@tonic-gate 			dn, stat, ldap_err2string(stat));
26200Sstevel@tonic-gate 	}
26210Sstevel@tonic-gate 
26220Sstevel@tonic-gate 	releaseCon(lc, stat);
26230Sstevel@tonic-gate 	freeLdapMod(mods);
26240Sstevel@tonic-gate 	if (msg != 0)
26250Sstevel@tonic-gate 		(void) ldap_msgfree(msg);
26260Sstevel@tonic-gate 
26270Sstevel@tonic-gate 	return (stat);
26280Sstevel@tonic-gate }
26290Sstevel@tonic-gate 
26300Sstevel@tonic-gate /*
26310Sstevel@tonic-gate  * Change the entry at 'oldDn' to have the new DN (not RDN) 'dn'.
26320Sstevel@tonic-gate  * Returns an LDAP error status.
26330Sstevel@tonic-gate  */
26340Sstevel@tonic-gate int
ldapChangeDN(char * oldDn,char * dn)26350Sstevel@tonic-gate ldapChangeDN(char *oldDn, char *dn) {
26360Sstevel@tonic-gate 	int			stat;
26370Sstevel@tonic-gate 	__nis_ldap_conn_t	*lc;
26380Sstevel@tonic-gate 	int			i, j, lo, ln;
26390Sstevel@tonic-gate 	char			*rdn;
26400Sstevel@tonic-gate 	int			msgid;
26410Sstevel@tonic-gate 	int			lderr;
26420Sstevel@tonic-gate 	struct timeval		tv;
26430Sstevel@tonic-gate 	LDAPMessage		*msg = 0;
26440Sstevel@tonic-gate 	char			**referralsp = NULL;
26450Sstevel@tonic-gate 	char			*myself = "ldapChangeDN";
26460Sstevel@tonic-gate 
26470Sstevel@tonic-gate 	if ((lo = slen(oldDn)) <= 0 || (ln = slen(dn)) <= 0)
26480Sstevel@tonic-gate 		return (LDAP_PARAM_ERROR);
26490Sstevel@tonic-gate 
26500Sstevel@tonic-gate 	if (strcasecmp(oldDn, dn) == 0)
26510Sstevel@tonic-gate 		return (LDAP_SUCCESS);
26520Sstevel@tonic-gate 
26530Sstevel@tonic-gate 	if ((lc = findCon(&stat)) == 0)
26540Sstevel@tonic-gate 		return (stat);
26550Sstevel@tonic-gate 
26560Sstevel@tonic-gate 	rdn = sdup(myself, T, dn);
26570Sstevel@tonic-gate 	if (rdn == 0) {
26580Sstevel@tonic-gate 		releaseCon(lc, LDAP_SUCCESS);
26590Sstevel@tonic-gate 		return (LDAP_NO_MEMORY);
26600Sstevel@tonic-gate 	}
26610Sstevel@tonic-gate 
26620Sstevel@tonic-gate 	/* Compare old and new DN from the end */
26630Sstevel@tonic-gate 	for (i = lo-1, j = ln-1; i >= 0 && j >= 0; i--, j--) {
26640Sstevel@tonic-gate 		if (tolower(oldDn[i]) != tolower(rdn[j])) {
26650Sstevel@tonic-gate 			/*
26660Sstevel@tonic-gate 			 * Terminate 'rdn' after this character in order
26670Sstevel@tonic-gate 			 * to snip off the portion of the new DN that is
26680Sstevel@tonic-gate 			 * the same as the old DN. What remains in 'rdn'
26690Sstevel@tonic-gate 			 * is the relative DN.
26700Sstevel@tonic-gate 			 */
26710Sstevel@tonic-gate 			rdn[j+1] = '\0';
26720Sstevel@tonic-gate 			break;
26730Sstevel@tonic-gate 		}
26740Sstevel@tonic-gate 	}
26750Sstevel@tonic-gate 
26760Sstevel@tonic-gate 	stat = ldap_rename(lc->ld, oldDn, rdn, NULL, 1, NULL, NULL, &msgid);
26770Sstevel@tonic-gate 
26780Sstevel@tonic-gate 	if (msgid != -1) {
26790Sstevel@tonic-gate 		tv = lc->modifyTimeout;
26800Sstevel@tonic-gate 		stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
26810Sstevel@tonic-gate 		if (stat == 0) {
26820Sstevel@tonic-gate 			stat = LDAP_TIMEOUT;
26830Sstevel@tonic-gate 		} else if (stat == -1) {
26840Sstevel@tonic-gate 			(void) ldap_get_option(lc->ld,
26850Sstevel@tonic-gate 				LDAP_OPT_ERROR_NUMBER, &stat);
26860Sstevel@tonic-gate 		} else {
26870Sstevel@tonic-gate 			stat = ldap_parse_result(lc->ld, msg, &lderr, NULL,
26880Sstevel@tonic-gate 				NULL, &referralsp, NULL, 0);
26890Sstevel@tonic-gate 			if (stat == LDAP_SUCCESS)
26900Sstevel@tonic-gate 				stat = lderr;
26910Sstevel@tonic-gate 			stat = ldap_result2error(lc->ld, msg, 0);
26920Sstevel@tonic-gate 		}
26930Sstevel@tonic-gate 	} else {
26940Sstevel@tonic-gate 		(void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
26950Sstevel@tonic-gate 			&stat);
26960Sstevel@tonic-gate 	}
26970Sstevel@tonic-gate 	if (proxyInfo.follow_referral == follow &&
26980Sstevel@tonic-gate 			stat == LDAP_REFERRAL && referralsp != NULL) {
26990Sstevel@tonic-gate 		releaseCon(lc, stat);
27000Sstevel@tonic-gate 		if (msg != NULL)
27010Sstevel@tonic-gate 			(void) ldap_msgfree(msg);
27020Sstevel@tonic-gate 		msg = NULL;
27030Sstevel@tonic-gate 		lc = findReferralCon(referralsp, &stat);
27040Sstevel@tonic-gate 		ldap_value_free(referralsp);
27050Sstevel@tonic-gate 		referralsp = NULL;
27060Sstevel@tonic-gate 		if (lc == NULL)
27070Sstevel@tonic-gate 			goto cleanup;
27080Sstevel@tonic-gate 		msgid = ldap_rename(lc->ld, oldDn, rdn, NULL, 1, NULL, NULL,
27090Sstevel@tonic-gate 			&msgid);
27100Sstevel@tonic-gate 		if (msgid == -1) {
27110Sstevel@tonic-gate 			(void) ldap_get_option(lc->ld,
27120Sstevel@tonic-gate 				LDAP_OPT_ERROR_NUMBER, &stat);
27130Sstevel@tonic-gate 			goto cleanup;
27140Sstevel@tonic-gate 		}
27150Sstevel@tonic-gate 		stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
27160Sstevel@tonic-gate 		if (stat == 0) {
27170Sstevel@tonic-gate 			stat = LDAP_TIMEOUT;
27180Sstevel@tonic-gate 		} else if (stat == -1) {
27190Sstevel@tonic-gate 			(void) ldap_get_option(lc->ld,
27200Sstevel@tonic-gate 				LDAP_OPT_ERROR_NUMBER, &stat);
27210Sstevel@tonic-gate 		} else {
27220Sstevel@tonic-gate 			stat = ldap_parse_result(lc->ld, msg, &lderr,
27230Sstevel@tonic-gate 				NULL, NULL, NULL, NULL, 0);
27240Sstevel@tonic-gate 			if (stat == LDAP_SUCCESS)
27250Sstevel@tonic-gate 				stat = lderr;
27260Sstevel@tonic-gate 		}
27270Sstevel@tonic-gate 	}
27280Sstevel@tonic-gate 
27290Sstevel@tonic-gate cleanup:
27300Sstevel@tonic-gate 	if (msg != NULL)
27310Sstevel@tonic-gate 		(void) ldap_msgfree(msg);
27320Sstevel@tonic-gate 
27330Sstevel@tonic-gate #if	1
27340Sstevel@tonic-gate 	fprintf(stderr, "%s: ldap_modrdn_s(0x%x, %s, %s, 1) => %s\n",
27350Sstevel@tonic-gate 		myself, lc == NULL ? 0: lc->ld, NIL(oldDn), NIL(rdn),
27360Sstevel@tonic-gate 		ldap_err2string(stat));
27370Sstevel@tonic-gate 	logmsg(MSG_NOTIMECHECK, LOG_WARNING,
27380Sstevel@tonic-gate 		"%s: ldap_modrdn_s(0x%x, %s, %s, 1) => %s",
27390Sstevel@tonic-gate 		myself, lc == NULL ? 0: lc->ld, NIL(oldDn), NIL(rdn),
27400Sstevel@tonic-gate 		ldap_err2string(stat));
27410Sstevel@tonic-gate #endif
27420Sstevel@tonic-gate 
27430Sstevel@tonic-gate 	if (stat == LDAP_NO_SUCH_OBJECT) {
27440Sstevel@tonic-gate 		/*
27450Sstevel@tonic-gate 		 * Fine from our point of view, since all we want to do
27460Sstevel@tonic-gate 		 * is to make sure that an update to the new DN doesn't
27470Sstevel@tonic-gate 		 * leave the old entry around.
27480Sstevel@tonic-gate 		 */
27490Sstevel@tonic-gate 		stat = LDAP_SUCCESS;
27500Sstevel@tonic-gate 	}
27510Sstevel@tonic-gate 
27520Sstevel@tonic-gate 	releaseCon(lc, stat);
27530Sstevel@tonic-gate 	sfree(rdn);
27540Sstevel@tonic-gate 
27550Sstevel@tonic-gate 	return (stat);
27560Sstevel@tonic-gate }
2757