18040SBaban.Kenkre@Sun.COM /*
28040SBaban.Kenkre@Sun.COM  * CDDL HEADER START
38040SBaban.Kenkre@Sun.COM  *
48040SBaban.Kenkre@Sun.COM  * The contents of this file are subject to the terms of the
58040SBaban.Kenkre@Sun.COM  * Common Development and Distribution License (the "License").
68040SBaban.Kenkre@Sun.COM  * You may not use this file except in compliance with the License.
78040SBaban.Kenkre@Sun.COM  *
88040SBaban.Kenkre@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98040SBaban.Kenkre@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108040SBaban.Kenkre@Sun.COM  * See the License for the specific language governing permissions
118040SBaban.Kenkre@Sun.COM  * and limitations under the License.
128040SBaban.Kenkre@Sun.COM  *
138040SBaban.Kenkre@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148040SBaban.Kenkre@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158040SBaban.Kenkre@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168040SBaban.Kenkre@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178040SBaban.Kenkre@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188040SBaban.Kenkre@Sun.COM  *
198040SBaban.Kenkre@Sun.COM  * CDDL HEADER END
208040SBaban.Kenkre@Sun.COM  */
218040SBaban.Kenkre@Sun.COM /*
228040SBaban.Kenkre@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
238040SBaban.Kenkre@Sun.COM  * Use is subject to license terms.
248040SBaban.Kenkre@Sun.COM  */
258040SBaban.Kenkre@Sun.COM 
268040SBaban.Kenkre@Sun.COM #include <alloca.h>
278040SBaban.Kenkre@Sun.COM #include <string.h>
288040SBaban.Kenkre@Sun.COM #include <strings.h>
298040SBaban.Kenkre@Sun.COM #include <lber.h>
308040SBaban.Kenkre@Sun.COM #include <sasl/sasl.h>
318040SBaban.Kenkre@Sun.COM #include <string.h>
328040SBaban.Kenkre@Sun.COM #include <ctype.h>
338040SBaban.Kenkre@Sun.COM #include <synch.h>
348040SBaban.Kenkre@Sun.COM #include <atomic.h>
358040SBaban.Kenkre@Sun.COM #include <errno.h>
368040SBaban.Kenkre@Sun.COM #include <assert.h>
378040SBaban.Kenkre@Sun.COM #include <limits.h>
388040SBaban.Kenkre@Sun.COM #include <sys/u8_textprep.h>
398040SBaban.Kenkre@Sun.COM #include <sys/varargs.h>
408040SBaban.Kenkre@Sun.COM #include "libadutils.h"
418040SBaban.Kenkre@Sun.COM #include "adutils_impl.h"
428040SBaban.Kenkre@Sun.COM 
438040SBaban.Kenkre@Sun.COM /* List of DSs, needed by the idle connection reaper thread */
448040SBaban.Kenkre@Sun.COM static pthread_mutex_t	adhostlock = PTHREAD_MUTEX_INITIALIZER;
458040SBaban.Kenkre@Sun.COM static adutils_host_t	*host_head = NULL;
468040SBaban.Kenkre@Sun.COM 
478040SBaban.Kenkre@Sun.COM /*
488040SBaban.Kenkre@Sun.COM  * List of query state structs -- needed so we can "route" LDAP results
498040SBaban.Kenkre@Sun.COM  * to the right context if multiple threads should be using the same
508040SBaban.Kenkre@Sun.COM  * connection concurrently
518040SBaban.Kenkre@Sun.COM  */
528040SBaban.Kenkre@Sun.COM static pthread_mutex_t		qstatelock = PTHREAD_MUTEX_INITIALIZER;
538040SBaban.Kenkre@Sun.COM static adutils_query_state_t	*qstatehead = NULL;
548040SBaban.Kenkre@Sun.COM 
558040SBaban.Kenkre@Sun.COM static char *adutils_sid_ber2str(BerValue *bvalues);
568040SBaban.Kenkre@Sun.COM static void adutils_lookup_batch_unlock(adutils_query_state_t **state);
578040SBaban.Kenkre@Sun.COM static void delete_ds(adutils_ad_t *ad, const char *host, int port);
588040SBaban.Kenkre@Sun.COM 
598040SBaban.Kenkre@Sun.COM typedef struct binary_attrs {
608040SBaban.Kenkre@Sun.COM 	const char	*name;
618040SBaban.Kenkre@Sun.COM 	char		*(*ber2str)(BerValue *bvalues);
628040SBaban.Kenkre@Sun.COM } binary_attrs_t;
638040SBaban.Kenkre@Sun.COM 
648040SBaban.Kenkre@Sun.COM static binary_attrs_t binattrs[] = {
658040SBaban.Kenkre@Sun.COM 	{"objectSID", adutils_sid_ber2str},
668040SBaban.Kenkre@Sun.COM 	{NULL, NULL}
678040SBaban.Kenkre@Sun.COM };
688040SBaban.Kenkre@Sun.COM 
69*8361SJulian.Pullen@Sun.COM 
708040SBaban.Kenkre@Sun.COM void
718040SBaban.Kenkre@Sun.COM adutils_set_log(int pri, bool_t syslog, bool_t degraded)
728040SBaban.Kenkre@Sun.COM {
738040SBaban.Kenkre@Sun.COM 	idmap_log_stderr(pri);
748040SBaban.Kenkre@Sun.COM 	idmap_log_syslog(syslog);
758040SBaban.Kenkre@Sun.COM 	idmap_log_degraded(degraded);
768040SBaban.Kenkre@Sun.COM }
778040SBaban.Kenkre@Sun.COM 
78*8361SJulian.Pullen@Sun.COM 
798040SBaban.Kenkre@Sun.COM /*
808040SBaban.Kenkre@Sun.COM  * Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com"
818040SBaban.Kenkre@Sun.COM  */
828040SBaban.Kenkre@Sun.COM static
838040SBaban.Kenkre@Sun.COM char *
848040SBaban.Kenkre@Sun.COM adutils_dns2dn(const char *dns)
858040SBaban.Kenkre@Sun.COM {
868040SBaban.Kenkre@Sun.COM 	int	nameparts;
878040SBaban.Kenkre@Sun.COM 	return (ldap_dns_to_dn((char *)dns, &nameparts));
888040SBaban.Kenkre@Sun.COM }
898040SBaban.Kenkre@Sun.COM 
90*8361SJulian.Pullen@Sun.COM 
918040SBaban.Kenkre@Sun.COM /*
928040SBaban.Kenkre@Sun.COM  * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other
938040SBaban.Kenkre@Sun.COM  * attributes (CN, etc...).
948040SBaban.Kenkre@Sun.COM  */
958040SBaban.Kenkre@Sun.COM char *
968040SBaban.Kenkre@Sun.COM adutils_dn2dns(const char *dn)
978040SBaban.Kenkre@Sun.COM {
988040SBaban.Kenkre@Sun.COM 	char **rdns = NULL;
998040SBaban.Kenkre@Sun.COM 	char **attrs = NULL;
1008040SBaban.Kenkre@Sun.COM 	char **labels = NULL;
1018040SBaban.Kenkre@Sun.COM 	char *dns = NULL;
1028040SBaban.Kenkre@Sun.COM 	char **rdn, **attr, **label;
1038040SBaban.Kenkre@Sun.COM 	int maxlabels = 5;
1048040SBaban.Kenkre@Sun.COM 	int nlabels = 0;
1058040SBaban.Kenkre@Sun.COM 	int dnslen;
1068040SBaban.Kenkre@Sun.COM 
1078040SBaban.Kenkre@Sun.COM 	/*
1088040SBaban.Kenkre@Sun.COM 	 * There is no reverse of ldap_dns_to_dn() in our libldap, so we
1098040SBaban.Kenkre@Sun.COM 	 * have to do the hard work here for now.
1108040SBaban.Kenkre@Sun.COM 	 */
1118040SBaban.Kenkre@Sun.COM 
1128040SBaban.Kenkre@Sun.COM 	/*
1138040SBaban.Kenkre@Sun.COM 	 * This code is much too liberal: it looks for "dc" attributes
1148040SBaban.Kenkre@Sun.COM 	 * in all RDNs of the DN.  In theory this could cause problems
1158040SBaban.Kenkre@Sun.COM 	 * if people were to use "dc" in nodes other than the root of
1168040SBaban.Kenkre@Sun.COM 	 * the tree, but in practice noone, least of all Active
1178040SBaban.Kenkre@Sun.COM 	 * Directory, does that.
1188040SBaban.Kenkre@Sun.COM 	 *
1198040SBaban.Kenkre@Sun.COM 	 * On the other hand, this code is much too conservative: it
1208040SBaban.Kenkre@Sun.COM 	 * does not make assumptions about ldap_explode_dn(), and _that_
1218040SBaban.Kenkre@Sun.COM 	 * is the true for looking at every attr of every RDN.
1228040SBaban.Kenkre@Sun.COM 	 *
1238040SBaban.Kenkre@Sun.COM 	 * Since we only ever look at dc and those must be DNS labels,
1248040SBaban.Kenkre@Sun.COM 	 * at least until we get around to supporting IDN here we
1258040SBaban.Kenkre@Sun.COM 	 * shouldn't see escaped labels from AD nor from libldap, though
1268040SBaban.Kenkre@Sun.COM 	 * the spec (RFC2253) does allow libldap to escape things that
1278040SBaban.Kenkre@Sun.COM 	 * don't need escaping -- if that should ever happen then
1288040SBaban.Kenkre@Sun.COM 	 * libldap will need a spanking, and we can take care of that.
1298040SBaban.Kenkre@Sun.COM 	 */
1308040SBaban.Kenkre@Sun.COM 
1318040SBaban.Kenkre@Sun.COM 	/* Explode a DN into RDNs */
1328040SBaban.Kenkre@Sun.COM 	if ((rdns = ldap_explode_dn(dn, 0)) == NULL)
1338040SBaban.Kenkre@Sun.COM 		return (NULL);
1348040SBaban.Kenkre@Sun.COM 
1358040SBaban.Kenkre@Sun.COM 	labels = calloc(maxlabels + 1, sizeof (char *));
1368040SBaban.Kenkre@Sun.COM 	label = labels;
1378040SBaban.Kenkre@Sun.COM 
1388040SBaban.Kenkre@Sun.COM 	for (rdn = rdns; *rdn != NULL; rdn++) {
1398040SBaban.Kenkre@Sun.COM 		if (attrs != NULL)
1408040SBaban.Kenkre@Sun.COM 			ldap_value_free(attrs);
1418040SBaban.Kenkre@Sun.COM 
1428040SBaban.Kenkre@Sun.COM 		/* Explode each RDN, look for DC attr, save val as DNS label */
1438040SBaban.Kenkre@Sun.COM 		if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL)
1448040SBaban.Kenkre@Sun.COM 			goto done;
1458040SBaban.Kenkre@Sun.COM 
1468040SBaban.Kenkre@Sun.COM 		for (attr = attrs; *attr != NULL; attr++) {
1478040SBaban.Kenkre@Sun.COM 			if (strncasecmp(*attr, "dc=", 3) != 0)
1488040SBaban.Kenkre@Sun.COM 				continue;
1498040SBaban.Kenkre@Sun.COM 
1508040SBaban.Kenkre@Sun.COM 			/* Found a DNS label */
1518040SBaban.Kenkre@Sun.COM 			labels[nlabels++] = strdup((*attr) + 3);
1528040SBaban.Kenkre@Sun.COM 
1538040SBaban.Kenkre@Sun.COM 			if (nlabels == maxlabels) {
1548040SBaban.Kenkre@Sun.COM 				char **tmp;
1558040SBaban.Kenkre@Sun.COM 				tmp = realloc(labels,
1568040SBaban.Kenkre@Sun.COM 				    sizeof (char *) * (maxlabels + 1));
1578040SBaban.Kenkre@Sun.COM 
1588040SBaban.Kenkre@Sun.COM 				if (tmp == NULL)
1598040SBaban.Kenkre@Sun.COM 					goto done;
1608040SBaban.Kenkre@Sun.COM 
1618040SBaban.Kenkre@Sun.COM 				labels = tmp;
1628040SBaban.Kenkre@Sun.COM 				labels[nlabels] = NULL;
1638040SBaban.Kenkre@Sun.COM 			}
1648040SBaban.Kenkre@Sun.COM 
1658040SBaban.Kenkre@Sun.COM 			/* There should be just one DC= attr per-RDN */
1668040SBaban.Kenkre@Sun.COM 			break;
1678040SBaban.Kenkre@Sun.COM 		}
1688040SBaban.Kenkre@Sun.COM 	}
1698040SBaban.Kenkre@Sun.COM 
1708040SBaban.Kenkre@Sun.COM 	/*
1718040SBaban.Kenkre@Sun.COM 	 * Got all the labels, now join with '.'
1728040SBaban.Kenkre@Sun.COM 	 *
1738040SBaban.Kenkre@Sun.COM 	 * We need room for nlabels - 1 periods ('.'), one nul
1748040SBaban.Kenkre@Sun.COM 	 * terminator, and the strlen() of each label.
1758040SBaban.Kenkre@Sun.COM 	 */
1768040SBaban.Kenkre@Sun.COM 	dnslen = nlabels;
1778040SBaban.Kenkre@Sun.COM 	for (label = labels; *label != NULL; label++)
1788040SBaban.Kenkre@Sun.COM 		dnslen += strlen(*label);
1798040SBaban.Kenkre@Sun.COM 
1808040SBaban.Kenkre@Sun.COM 	if ((dns = malloc(dnslen)) == NULL)
1818040SBaban.Kenkre@Sun.COM 		goto done;
1828040SBaban.Kenkre@Sun.COM 
1838040SBaban.Kenkre@Sun.COM 	*dns = '\0';
1848040SBaban.Kenkre@Sun.COM 
1858040SBaban.Kenkre@Sun.COM 	for (label = labels; *label != NULL; label++) {
1868040SBaban.Kenkre@Sun.COM 		(void) strlcat(dns, *label, dnslen);
1878040SBaban.Kenkre@Sun.COM 		/*
1888040SBaban.Kenkre@Sun.COM 		 * NOTE: the last '.' won't be appended -- there's no room
1898040SBaban.Kenkre@Sun.COM 		 * for it!
1908040SBaban.Kenkre@Sun.COM 		 */
1918040SBaban.Kenkre@Sun.COM 		(void) strlcat(dns, ".", dnslen);
1928040SBaban.Kenkre@Sun.COM 	}
1938040SBaban.Kenkre@Sun.COM 
1948040SBaban.Kenkre@Sun.COM done:
1958040SBaban.Kenkre@Sun.COM 	if (labels != NULL) {
1968040SBaban.Kenkre@Sun.COM 		for (label = labels; *label != NULL; label++)
1978040SBaban.Kenkre@Sun.COM 			free(*label);
1988040SBaban.Kenkre@Sun.COM 		free(labels);
1998040SBaban.Kenkre@Sun.COM 	}
2008040SBaban.Kenkre@Sun.COM 	if (attrs != NULL)
2018040SBaban.Kenkre@Sun.COM 		ldap_value_free(attrs);
2028040SBaban.Kenkre@Sun.COM 	if (rdns != NULL)
2038040SBaban.Kenkre@Sun.COM 		ldap_value_free(rdns);
2048040SBaban.Kenkre@Sun.COM 
2058040SBaban.Kenkre@Sun.COM 	return (dns);
2068040SBaban.Kenkre@Sun.COM }
2078040SBaban.Kenkre@Sun.COM 
2088040SBaban.Kenkre@Sun.COM /*
2098040SBaban.Kenkre@Sun.COM  * Convert a binary SID in a BerValue to a adutils_sid_t
2108040SBaban.Kenkre@Sun.COM  */
2118040SBaban.Kenkre@Sun.COM static
2128040SBaban.Kenkre@Sun.COM int
2138040SBaban.Kenkre@Sun.COM getsid(BerValue *bval, adutils_sid_t *sidp)
2148040SBaban.Kenkre@Sun.COM {
2158040SBaban.Kenkre@Sun.COM 	int		i, j;
2168040SBaban.Kenkre@Sun.COM 	uchar_t		*v;
2178040SBaban.Kenkre@Sun.COM 	uint32_t	a;
2188040SBaban.Kenkre@Sun.COM 
2198040SBaban.Kenkre@Sun.COM 	/*
2208040SBaban.Kenkre@Sun.COM 	 * The binary format of a SID is as follows:
2218040SBaban.Kenkre@Sun.COM 	 *
2228040SBaban.Kenkre@Sun.COM 	 * byte #0: version, always 0x01
2238040SBaban.Kenkre@Sun.COM 	 * byte #1: RID count, always <= 0x0f
2248040SBaban.Kenkre@Sun.COM 	 * bytes #2-#7: SID authority, big-endian 48-bit unsigned int
2258040SBaban.Kenkre@Sun.COM 	 *
2268040SBaban.Kenkre@Sun.COM 	 * followed by RID count RIDs, each a little-endian, unsigned
2278040SBaban.Kenkre@Sun.COM 	 * 32-bit int.
2288040SBaban.Kenkre@Sun.COM 	 */
2298040SBaban.Kenkre@Sun.COM 	/*
2308040SBaban.Kenkre@Sun.COM 	 * Sanity checks: must have at least one RID, version must be
2318040SBaban.Kenkre@Sun.COM 	 * 0x01, and the length must be 8 + rid count * 4
2328040SBaban.Kenkre@Sun.COM 	 */
2338040SBaban.Kenkre@Sun.COM 	if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 &&
2348040SBaban.Kenkre@Sun.COM 	    bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) {
2358040SBaban.Kenkre@Sun.COM 		v = (uchar_t *)bval->bv_val;
2368040SBaban.Kenkre@Sun.COM 		sidp->version = v[0];
2378040SBaban.Kenkre@Sun.COM 		sidp->sub_authority_count = v[1];
2388040SBaban.Kenkre@Sun.COM 		sidp->authority =
2398040SBaban.Kenkre@Sun.COM 		    /* big endian -- so start from the left */
2408040SBaban.Kenkre@Sun.COM 		    ((u_longlong_t)v[2] << 40) |
2418040SBaban.Kenkre@Sun.COM 		    ((u_longlong_t)v[3] << 32) |
2428040SBaban.Kenkre@Sun.COM 		    ((u_longlong_t)v[4] << 24) |
2438040SBaban.Kenkre@Sun.COM 		    ((u_longlong_t)v[5] << 16) |
2448040SBaban.Kenkre@Sun.COM 		    ((u_longlong_t)v[6] << 8) |
2458040SBaban.Kenkre@Sun.COM 		    (u_longlong_t)v[7];
2468040SBaban.Kenkre@Sun.COM 		for (i = 0; i < sidp->sub_authority_count; i++) {
2478040SBaban.Kenkre@Sun.COM 			j = 8 + (i * 4);
2488040SBaban.Kenkre@Sun.COM 			/* little endian -- so start from the right */
2498040SBaban.Kenkre@Sun.COM 			a = (v[j + 3] << 24) | (v[j + 2] << 16) |
2508040SBaban.Kenkre@Sun.COM 			    (v[j + 1] << 8) | (v[j]);
2518040SBaban.Kenkre@Sun.COM 			sidp->sub_authorities[i] = a;
2528040SBaban.Kenkre@Sun.COM 		}
2538040SBaban.Kenkre@Sun.COM 		return (0);
2548040SBaban.Kenkre@Sun.COM 	}
2558040SBaban.Kenkre@Sun.COM 	return (-1);
2568040SBaban.Kenkre@Sun.COM }
2578040SBaban.Kenkre@Sun.COM 
2588040SBaban.Kenkre@Sun.COM /*
2598040SBaban.Kenkre@Sun.COM  * Convert a adutils_sid_t to S-1-...
2608040SBaban.Kenkre@Sun.COM  */
2618040SBaban.Kenkre@Sun.COM static
2628040SBaban.Kenkre@Sun.COM char *
2638040SBaban.Kenkre@Sun.COM sid2txt(adutils_sid_t *sidp)
2648040SBaban.Kenkre@Sun.COM {
2658040SBaban.Kenkre@Sun.COM 	int	rlen, i, len;
2668040SBaban.Kenkre@Sun.COM 	char	*str, *cp;
2678040SBaban.Kenkre@Sun.COM 
2688040SBaban.Kenkre@Sun.COM 	if (sidp->version != 1)
2698040SBaban.Kenkre@Sun.COM 		return (NULL);
2708040SBaban.Kenkre@Sun.COM 
2718040SBaban.Kenkre@Sun.COM 	len = sizeof ("S-1-") - 1;
2728040SBaban.Kenkre@Sun.COM 
2738040SBaban.Kenkre@Sun.COM 	/*
2748040SBaban.Kenkre@Sun.COM 	 * We could optimize like so, but, why?
2758040SBaban.Kenkre@Sun.COM 	 *	if (sidp->authority < 10)
2768040SBaban.Kenkre@Sun.COM 	 *		len += 2;
2778040SBaban.Kenkre@Sun.COM 	 *	else if (sidp->authority < 100)
2788040SBaban.Kenkre@Sun.COM 	 *		len += 3;
2798040SBaban.Kenkre@Sun.COM 	 *	else
2808040SBaban.Kenkre@Sun.COM 	 *		len += snprintf(NULL, 0"%llu", sidp->authority);
2818040SBaban.Kenkre@Sun.COM 	 */
2828040SBaban.Kenkre@Sun.COM 	len += snprintf(NULL, 0, "%llu", sidp->authority);
2838040SBaban.Kenkre@Sun.COM 
2848040SBaban.Kenkre@Sun.COM 	/* Max length of a uint32_t printed out in ASCII is 10 bytes */
2858040SBaban.Kenkre@Sun.COM 	len += 1 + (sidp->sub_authority_count + 1) * 10;
2868040SBaban.Kenkre@Sun.COM 
2878040SBaban.Kenkre@Sun.COM 	if ((cp = str = malloc(len)) == NULL)
2888040SBaban.Kenkre@Sun.COM 		return (NULL);
2898040SBaban.Kenkre@Sun.COM 
2908040SBaban.Kenkre@Sun.COM 	rlen = snprintf(str, len, "S-1-%llu", sidp->authority);
2918040SBaban.Kenkre@Sun.COM 
2928040SBaban.Kenkre@Sun.COM 	cp += rlen;
2938040SBaban.Kenkre@Sun.COM 	len -= rlen;
2948040SBaban.Kenkre@Sun.COM 
2958040SBaban.Kenkre@Sun.COM 	for (i = 0; i < sidp->sub_authority_count; i++) {
2968040SBaban.Kenkre@Sun.COM 		assert(len > 0);
2978040SBaban.Kenkre@Sun.COM 		rlen = snprintf(cp, len, "-%u", sidp->sub_authorities[i]);
2988040SBaban.Kenkre@Sun.COM 		cp += rlen;
2998040SBaban.Kenkre@Sun.COM 		len -= rlen;
3008040SBaban.Kenkre@Sun.COM 		assert(len >= 0);
3018040SBaban.Kenkre@Sun.COM 	}
3028040SBaban.Kenkre@Sun.COM 
3038040SBaban.Kenkre@Sun.COM 	return (str);
3048040SBaban.Kenkre@Sun.COM }
3058040SBaban.Kenkre@Sun.COM 
3068040SBaban.Kenkre@Sun.COM /*
3078040SBaban.Kenkre@Sun.COM  * Convert a adutils_sid_t to on-the-wire encoding
3088040SBaban.Kenkre@Sun.COM  */
3098040SBaban.Kenkre@Sun.COM static
3108040SBaban.Kenkre@Sun.COM int
3118040SBaban.Kenkre@Sun.COM sid2binsid(adutils_sid_t *sid, uchar_t *binsid, int binsidlen)
3128040SBaban.Kenkre@Sun.COM {
3138040SBaban.Kenkre@Sun.COM 	uchar_t		*p;
3148040SBaban.Kenkre@Sun.COM 	int		i;
3158040SBaban.Kenkre@Sun.COM 	uint64_t	a;
3168040SBaban.Kenkre@Sun.COM 	uint32_t	r;
3178040SBaban.Kenkre@Sun.COM 
3188040SBaban.Kenkre@Sun.COM 	if (sid->version != 1 ||
3198040SBaban.Kenkre@Sun.COM 	    binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4))
3208040SBaban.Kenkre@Sun.COM 		return (-1);
3218040SBaban.Kenkre@Sun.COM 
3228040SBaban.Kenkre@Sun.COM 	p = binsid;
3238040SBaban.Kenkre@Sun.COM 	*p++ = 0x01;		/* version */
3248040SBaban.Kenkre@Sun.COM 	/* sub authority count */
3258040SBaban.Kenkre@Sun.COM 	*p++ = sid->sub_authority_count;
3268040SBaban.Kenkre@Sun.COM 	/* Authority */
3278040SBaban.Kenkre@Sun.COM 	a = sid->authority;
3288040SBaban.Kenkre@Sun.COM 	/* big-endian -- start from left */
3298040SBaban.Kenkre@Sun.COM 	*p++ = (a >> 40) & 0xFF;
3308040SBaban.Kenkre@Sun.COM 	*p++ = (a >> 32) & 0xFF;
3318040SBaban.Kenkre@Sun.COM 	*p++ = (a >> 24) & 0xFF;
3328040SBaban.Kenkre@Sun.COM 	*p++ = (a >> 16) & 0xFF;
3338040SBaban.Kenkre@Sun.COM 	*p++ = (a >> 8) & 0xFF;
3348040SBaban.Kenkre@Sun.COM 	*p++ = a & 0xFF;
3358040SBaban.Kenkre@Sun.COM 
3368040SBaban.Kenkre@Sun.COM 	/* sub-authorities */
3378040SBaban.Kenkre@Sun.COM 	for (i = 0; i < sid->sub_authority_count; i++) {
3388040SBaban.Kenkre@Sun.COM 		r = sid->sub_authorities[i];
3398040SBaban.Kenkre@Sun.COM 		/* little-endian -- start from right */
3408040SBaban.Kenkre@Sun.COM 		*p++ = (r & 0x000000FF);
3418040SBaban.Kenkre@Sun.COM 		*p++ = (r & 0x0000FF00) >> 8;
3428040SBaban.Kenkre@Sun.COM 		*p++ = (r & 0x00FF0000) >> 16;
3438040SBaban.Kenkre@Sun.COM 		*p++ = (r & 0xFF000000) >> 24;
3448040SBaban.Kenkre@Sun.COM 	}
3458040SBaban.Kenkre@Sun.COM 
3468040SBaban.Kenkre@Sun.COM 	return (0);
3478040SBaban.Kenkre@Sun.COM }
3488040SBaban.Kenkre@Sun.COM 
3498040SBaban.Kenkre@Sun.COM /*
3508040SBaban.Kenkre@Sun.COM  * Convert a stringified SID (S-1-...) into a hex-encoded version of the
3518040SBaban.Kenkre@Sun.COM  * on-the-wire encoding, but with each pair of hex digits pre-pended
3528040SBaban.Kenkre@Sun.COM  * with a '\', so we can pass this to libldap.
3538040SBaban.Kenkre@Sun.COM  */
3548040SBaban.Kenkre@Sun.COM int
3558040SBaban.Kenkre@Sun.COM adutils_txtsid2hexbinsid(const char *txt, const uint32_t *rid,
3568040SBaban.Kenkre@Sun.COM 	char *hexbinsid, int hexbinsidlen)
3578040SBaban.Kenkre@Sun.COM {
3588040SBaban.Kenkre@Sun.COM 	adutils_sid_t	sid = { 0 };
3598040SBaban.Kenkre@Sun.COM 	int		i, j;
3608040SBaban.Kenkre@Sun.COM 	const char	*cp;
3618040SBaban.Kenkre@Sun.COM 	char		*ecp;
3628040SBaban.Kenkre@Sun.COM 	u_longlong_t	a;
3638040SBaban.Kenkre@Sun.COM 	unsigned long	r;
3648040SBaban.Kenkre@Sun.COM 	uchar_t		*binsid, b, hb;
3658040SBaban.Kenkre@Sun.COM 
3668040SBaban.Kenkre@Sun.COM 	/* Only version 1 SIDs please */
3678040SBaban.Kenkre@Sun.COM 	if (strncmp(txt, "S-1-", strlen("S-1-")) != 0)
3688040SBaban.Kenkre@Sun.COM 		return (-1);
3698040SBaban.Kenkre@Sun.COM 
3708040SBaban.Kenkre@Sun.COM 	if (strlen(txt) < (strlen("S-1-") + 1))
3718040SBaban.Kenkre@Sun.COM 		return (-1);
3728040SBaban.Kenkre@Sun.COM 
3738040SBaban.Kenkre@Sun.COM 	/* count '-'s */
3748040SBaban.Kenkre@Sun.COM 	for (j = 0, cp = strchr(txt, '-');
3758040SBaban.Kenkre@Sun.COM 	    cp != NULL && *cp != '\0';
3768040SBaban.Kenkre@Sun.COM 	    j++, cp = strchr(cp + 1, '-')) {
3778040SBaban.Kenkre@Sun.COM 		/* can't end on a '-' */
3788040SBaban.Kenkre@Sun.COM 		if (*(cp + 1) == '\0')
3798040SBaban.Kenkre@Sun.COM 			return (-1);
3808040SBaban.Kenkre@Sun.COM 	}
3818040SBaban.Kenkre@Sun.COM 
3828040SBaban.Kenkre@Sun.COM 	/* Adjust count for version and authority */
3838040SBaban.Kenkre@Sun.COM 	j -= 2;
3848040SBaban.Kenkre@Sun.COM 
3858040SBaban.Kenkre@Sun.COM 	/* we know the version number and RID count */
3868040SBaban.Kenkre@Sun.COM 	sid.version = 1;
3878040SBaban.Kenkre@Sun.COM 	sid.sub_authority_count = (rid != NULL) ? j + 1 : j;
3888040SBaban.Kenkre@Sun.COM 
3898040SBaban.Kenkre@Sun.COM 	/* must have at least one RID, but not too many */
3908040SBaban.Kenkre@Sun.COM 	if (sid.sub_authority_count < 1 ||
3918040SBaban.Kenkre@Sun.COM 	    sid.sub_authority_count > ADUTILS_SID_MAX_SUB_AUTHORITIES)
3928040SBaban.Kenkre@Sun.COM 		return (-1);
3938040SBaban.Kenkre@Sun.COM 
3948040SBaban.Kenkre@Sun.COM 	/* check that we only have digits and '-' */
3958040SBaban.Kenkre@Sun.COM 	if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1))
3968040SBaban.Kenkre@Sun.COM 		return (-1);
3978040SBaban.Kenkre@Sun.COM 
3988040SBaban.Kenkre@Sun.COM 	cp = txt + strlen("S-1-");
3998040SBaban.Kenkre@Sun.COM 
4008040SBaban.Kenkre@Sun.COM 	/* 64-bit safe parsing of unsigned 48-bit authority value */
4018040SBaban.Kenkre@Sun.COM 	errno = 0;
4028040SBaban.Kenkre@Sun.COM 	a = strtoull(cp, &ecp, 10);
4038040SBaban.Kenkre@Sun.COM 
4048040SBaban.Kenkre@Sun.COM 	/* errors parsing the authority or too many bits */
4058040SBaban.Kenkre@Sun.COM 	if (cp == ecp || (a == 0 && errno == EINVAL) ||
4068040SBaban.Kenkre@Sun.COM 	    (a == ULLONG_MAX && errno == ERANGE) ||
4078040SBaban.Kenkre@Sun.COM 	    (a & 0x0000ffffffffffffULL) != a)
4088040SBaban.Kenkre@Sun.COM 		return (-1);
4098040SBaban.Kenkre@Sun.COM 
4108040SBaban.Kenkre@Sun.COM 	cp = ecp;
4118040SBaban.Kenkre@Sun.COM 
4128040SBaban.Kenkre@Sun.COM 	sid.authority = (uint64_t)a;
4138040SBaban.Kenkre@Sun.COM 
4148040SBaban.Kenkre@Sun.COM 	for (i = 0; i < j; i++) {
4158040SBaban.Kenkre@Sun.COM 		if (*cp++ != '-')
4168040SBaban.Kenkre@Sun.COM 			return (-1);
4178040SBaban.Kenkre@Sun.COM 		/* 64-bit safe parsing of unsigned 32-bit RID */
4188040SBaban.Kenkre@Sun.COM 		errno = 0;
4198040SBaban.Kenkre@Sun.COM 		r = strtoul(cp, &ecp, 10);
4208040SBaban.Kenkre@Sun.COM 		/* errors parsing the RID or too many bits */
4218040SBaban.Kenkre@Sun.COM 		if (cp == ecp || (r == 0 && errno == EINVAL) ||
4228040SBaban.Kenkre@Sun.COM 		    (r == ULONG_MAX && errno == ERANGE) ||
4238040SBaban.Kenkre@Sun.COM 		    (r & 0xffffffffUL) != r)
4248040SBaban.Kenkre@Sun.COM 			return (-1);
4258040SBaban.Kenkre@Sun.COM 		sid.sub_authorities[i] = (uint32_t)r;
4268040SBaban.Kenkre@Sun.COM 		cp = ecp;
4278040SBaban.Kenkre@Sun.COM 	}
4288040SBaban.Kenkre@Sun.COM 
4298040SBaban.Kenkre@Sun.COM 	/* check that all of the string SID has been consumed */
4308040SBaban.Kenkre@Sun.COM 	if (*cp != '\0')
4318040SBaban.Kenkre@Sun.COM 		return (-1);
4328040SBaban.Kenkre@Sun.COM 
4338040SBaban.Kenkre@Sun.COM 	if (rid != NULL)
4348040SBaban.Kenkre@Sun.COM 		sid.sub_authorities[j] = *rid;
4358040SBaban.Kenkre@Sun.COM 
4368040SBaban.Kenkre@Sun.COM 	j = 1 + 1 + 6 + sid.sub_authority_count * 4;
4378040SBaban.Kenkre@Sun.COM 
4388040SBaban.Kenkre@Sun.COM 	if (hexbinsidlen < (j * 3))
4398040SBaban.Kenkre@Sun.COM 		return (-2);
4408040SBaban.Kenkre@Sun.COM 
4418040SBaban.Kenkre@Sun.COM 	/* binary encode the SID */
4428040SBaban.Kenkre@Sun.COM 	binsid = (uchar_t *)alloca(j);
4438040SBaban.Kenkre@Sun.COM 	(void) sid2binsid(&sid, binsid, j);
4448040SBaban.Kenkre@Sun.COM 
4458040SBaban.Kenkre@Sun.COM 	/* hex encode, with a backslash before each byte */
4468040SBaban.Kenkre@Sun.COM 	for (ecp = hexbinsid, i = 0; i < j; i++) {
4478040SBaban.Kenkre@Sun.COM 		b = binsid[i];
4488040SBaban.Kenkre@Sun.COM 		*ecp++ = '\\';
4498040SBaban.Kenkre@Sun.COM 		hb = (b >> 4) & 0xF;
4508040SBaban.Kenkre@Sun.COM 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
4518040SBaban.Kenkre@Sun.COM 		hb = b & 0xF;
4528040SBaban.Kenkre@Sun.COM 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
4538040SBaban.Kenkre@Sun.COM 	}
4548040SBaban.Kenkre@Sun.COM 	*ecp = '\0';
4558040SBaban.Kenkre@Sun.COM 
4568040SBaban.Kenkre@Sun.COM 	return (0);
4578040SBaban.Kenkre@Sun.COM }
4588040SBaban.Kenkre@Sun.COM 
4598040SBaban.Kenkre@Sun.COM static
4608040SBaban.Kenkre@Sun.COM char *
4618040SBaban.Kenkre@Sun.COM convert_bval2sid(BerValue *bval, uint32_t *rid)
4628040SBaban.Kenkre@Sun.COM {
4638040SBaban.Kenkre@Sun.COM 	adutils_sid_t	sid;
4648040SBaban.Kenkre@Sun.COM 
4658040SBaban.Kenkre@Sun.COM 	if (getsid(bval, &sid) < 0)
4668040SBaban.Kenkre@Sun.COM 		return (NULL);
4678040SBaban.Kenkre@Sun.COM 
4688040SBaban.Kenkre@Sun.COM 	/*
4698040SBaban.Kenkre@Sun.COM 	 * If desired and if the SID is what should be a domain/computer
4708040SBaban.Kenkre@Sun.COM 	 * user or group SID (i.e., S-1-5-w-x-y-z-<user/group RID>) then
4718040SBaban.Kenkre@Sun.COM 	 * save the last RID and truncate the SID
4728040SBaban.Kenkre@Sun.COM 	 */
4738040SBaban.Kenkre@Sun.COM 	if (rid != NULL && sid.authority == 5 && sid.sub_authority_count == 5)
4748040SBaban.Kenkre@Sun.COM 		*rid = sid.sub_authorities[--sid.sub_authority_count];
4758040SBaban.Kenkre@Sun.COM 	return (sid2txt(&sid));
4768040SBaban.Kenkre@Sun.COM }
4778040SBaban.Kenkre@Sun.COM 
4788040SBaban.Kenkre@Sun.COM 
4798040SBaban.Kenkre@Sun.COM /*
4808040SBaban.Kenkre@Sun.COM  * Return a NUL-terminated stringified SID from the value of an
4818040SBaban.Kenkre@Sun.COM  * objectSid attribute and put the last RID in *rid.
4828040SBaban.Kenkre@Sun.COM  */
4838040SBaban.Kenkre@Sun.COM char *
4848040SBaban.Kenkre@Sun.COM adutils_bv_objsid2sidstr(BerValue *bval, uint32_t *rid)
4858040SBaban.Kenkre@Sun.COM {
4868040SBaban.Kenkre@Sun.COM 	char *sid;
4878040SBaban.Kenkre@Sun.COM 
4888040SBaban.Kenkre@Sun.COM 	if (bval == NULL)
4898040SBaban.Kenkre@Sun.COM 		return (NULL);
4908040SBaban.Kenkre@Sun.COM 	/* objectSid is single valued */
4918040SBaban.Kenkre@Sun.COM 	if ((sid = convert_bval2sid(bval, rid)) == NULL)
4928040SBaban.Kenkre@Sun.COM 		return (NULL);
4938040SBaban.Kenkre@Sun.COM 	return (sid);
4948040SBaban.Kenkre@Sun.COM }
4958040SBaban.Kenkre@Sun.COM 
4968040SBaban.Kenkre@Sun.COM static
4978040SBaban.Kenkre@Sun.COM char *
4988040SBaban.Kenkre@Sun.COM adutils_sid_ber2str(BerValue *bval)
4998040SBaban.Kenkre@Sun.COM {
5008040SBaban.Kenkre@Sun.COM 	return (adutils_bv_objsid2sidstr(bval, NULL));
5018040SBaban.Kenkre@Sun.COM }
5028040SBaban.Kenkre@Sun.COM 
5038040SBaban.Kenkre@Sun.COM 
5048040SBaban.Kenkre@Sun.COM /* Return a NUL-terminated string from the Ber value */
5058040SBaban.Kenkre@Sun.COM char *
5068040SBaban.Kenkre@Sun.COM adutils_bv_name2str(BerValue *bval)
5078040SBaban.Kenkre@Sun.COM {
5088040SBaban.Kenkre@Sun.COM 	char *s;
5098040SBaban.Kenkre@Sun.COM 
5108040SBaban.Kenkre@Sun.COM 	if (bval == NULL || bval->bv_val == NULL)
5118040SBaban.Kenkre@Sun.COM 		return (NULL);
5128040SBaban.Kenkre@Sun.COM 	if ((s = malloc(bval->bv_len + 1)) == NULL)
5138040SBaban.Kenkre@Sun.COM 		return (NULL);
5148040SBaban.Kenkre@Sun.COM 	(void) snprintf(s, bval->bv_len + 1, "%.*s", bval->bv_len,
5158040SBaban.Kenkre@Sun.COM 	    bval->bv_val);
5168040SBaban.Kenkre@Sun.COM 	return (s);
5178040SBaban.Kenkre@Sun.COM }
5188040SBaban.Kenkre@Sun.COM 
5198040SBaban.Kenkre@Sun.COM /*ARGSUSED*/
5208040SBaban.Kenkre@Sun.COM static
5218040SBaban.Kenkre@Sun.COM int
5228040SBaban.Kenkre@Sun.COM saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts)
5238040SBaban.Kenkre@Sun.COM {
5248040SBaban.Kenkre@Sun.COM 	sasl_interact_t	*interact;
5258040SBaban.Kenkre@Sun.COM 
5268040SBaban.Kenkre@Sun.COM 	if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE)
5278040SBaban.Kenkre@Sun.COM 		return (LDAP_PARAM_ERROR);
5288040SBaban.Kenkre@Sun.COM 
5298040SBaban.Kenkre@Sun.COM 	/* There should be no extra arguemnts for SASL/GSSAPI authentication */
5308040SBaban.Kenkre@Sun.COM 	for (interact = prompts; interact->id != SASL_CB_LIST_END;
5318040SBaban.Kenkre@Sun.COM 	    interact++) {
5328040SBaban.Kenkre@Sun.COM 		interact->result = NULL;
5338040SBaban.Kenkre@Sun.COM 		interact->len = 0;
5348040SBaban.Kenkre@Sun.COM 	}
5358040SBaban.Kenkre@Sun.COM 	return (LDAP_SUCCESS);
5368040SBaban.Kenkre@Sun.COM }
5378040SBaban.Kenkre@Sun.COM 
5388040SBaban.Kenkre@Sun.COM 
5398040SBaban.Kenkre@Sun.COM #define	ADCONN_TIME	300
5408040SBaban.Kenkre@Sun.COM 
5418040SBaban.Kenkre@Sun.COM /*
5428040SBaban.Kenkre@Sun.COM  * Idle connection reaping side of connection management
5438040SBaban.Kenkre@Sun.COM  */
5448040SBaban.Kenkre@Sun.COM void
5458040SBaban.Kenkre@Sun.COM adutils_reap_idle_connections()
5468040SBaban.Kenkre@Sun.COM {
5478040SBaban.Kenkre@Sun.COM 	adutils_host_t	*adh;
5488040SBaban.Kenkre@Sun.COM 	time_t		now;
5498040SBaban.Kenkre@Sun.COM 
5508040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&adhostlock);
5518040SBaban.Kenkre@Sun.COM 	now = time(NULL);
5528040SBaban.Kenkre@Sun.COM 	for (adh = host_head; adh != NULL; adh = adh->next) {
5538040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_lock(&adh->lock);
5548040SBaban.Kenkre@Sun.COM 		if (adh->ref == 0 && adh->idletime != 0 &&
5558040SBaban.Kenkre@Sun.COM 		    adh->idletime + ADCONN_TIME < now) {
5568040SBaban.Kenkre@Sun.COM 			if (adh->ld) {
5578040SBaban.Kenkre@Sun.COM 				(void) ldap_unbind(adh->ld);
5588040SBaban.Kenkre@Sun.COM 				adh->ld = NULL;
5598040SBaban.Kenkre@Sun.COM 				adh->idletime = 0;
5608040SBaban.Kenkre@Sun.COM 				adh->ref = 0;
5618040SBaban.Kenkre@Sun.COM 			}
5628040SBaban.Kenkre@Sun.COM 		}
5638040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_unlock(&adh->lock);
5648040SBaban.Kenkre@Sun.COM 	}
5658040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&adhostlock);
5668040SBaban.Kenkre@Sun.COM }
5678040SBaban.Kenkre@Sun.COM 
5688040SBaban.Kenkre@Sun.COM 
5698040SBaban.Kenkre@Sun.COM adutils_rc
5708040SBaban.Kenkre@Sun.COM adutils_ad_alloc(adutils_ad_t **new_ad, const char *default_domain,
5718040SBaban.Kenkre@Sun.COM 	adutils_ad_partition_t part)
5728040SBaban.Kenkre@Sun.COM {
5738040SBaban.Kenkre@Sun.COM 	adutils_ad_t *ad;
5748040SBaban.Kenkre@Sun.COM 
5758040SBaban.Kenkre@Sun.COM 	*new_ad = NULL;
5768040SBaban.Kenkre@Sun.COM 
5778040SBaban.Kenkre@Sun.COM 	if ((default_domain == NULL || *default_domain == '\0') &&
5788040SBaban.Kenkre@Sun.COM 	    part != ADUTILS_AD_GLOBAL_CATALOG)
5798040SBaban.Kenkre@Sun.COM 		return (ADUTILS_ERR_DOMAIN);
5808040SBaban.Kenkre@Sun.COM 	if ((ad = calloc(1, sizeof (*ad))) == NULL)
5818040SBaban.Kenkre@Sun.COM 		return (ADUTILS_ERR_MEMORY);
5828040SBaban.Kenkre@Sun.COM 	ad->ref = 1;
5838040SBaban.Kenkre@Sun.COM 	ad->partition = part;
5848040SBaban.Kenkre@Sun.COM 	if (default_domain == NULL)
5858040SBaban.Kenkre@Sun.COM 		default_domain = "";
5868040SBaban.Kenkre@Sun.COM 	if ((ad->dflt_w2k_dom = strdup(default_domain)) == NULL)
5878040SBaban.Kenkre@Sun.COM 		goto err;
5888040SBaban.Kenkre@Sun.COM 	if (pthread_mutex_init(&ad->lock, NULL) != 0)
5898040SBaban.Kenkre@Sun.COM 		goto err;
5908040SBaban.Kenkre@Sun.COM 	*new_ad = ad;
5918040SBaban.Kenkre@Sun.COM 	return (ADUTILS_SUCCESS);
5928040SBaban.Kenkre@Sun.COM 
5938040SBaban.Kenkre@Sun.COM err:
5948040SBaban.Kenkre@Sun.COM 	if (ad->dflt_w2k_dom != NULL)
5958040SBaban.Kenkre@Sun.COM 		free(ad->dflt_w2k_dom);
5968040SBaban.Kenkre@Sun.COM 	free(ad);
5978040SBaban.Kenkre@Sun.COM 	return (ADUTILS_ERR_MEMORY);
5988040SBaban.Kenkre@Sun.COM }
5998040SBaban.Kenkre@Sun.COM 
6008040SBaban.Kenkre@Sun.COM void
6018040SBaban.Kenkre@Sun.COM adutils_ad_free(adutils_ad_t **ad)
6028040SBaban.Kenkre@Sun.COM {
6038040SBaban.Kenkre@Sun.COM 	adutils_host_t *p;
6048040SBaban.Kenkre@Sun.COM 	adutils_host_t *prev;
6058040SBaban.Kenkre@Sun.COM 
6068040SBaban.Kenkre@Sun.COM 	if (ad == NULL || *ad == NULL)
6078040SBaban.Kenkre@Sun.COM 		return;
6088040SBaban.Kenkre@Sun.COM 
6098040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&(*ad)->lock);
6108040SBaban.Kenkre@Sun.COM 
6118040SBaban.Kenkre@Sun.COM 	if (atomic_dec_32_nv(&(*ad)->ref) > 0) {
6128040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_unlock(&(*ad)->lock);
6138040SBaban.Kenkre@Sun.COM 		*ad = NULL;
6148040SBaban.Kenkre@Sun.COM 		return;
6158040SBaban.Kenkre@Sun.COM 	}
6168040SBaban.Kenkre@Sun.COM 
6178040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&adhostlock);
6188040SBaban.Kenkre@Sun.COM 	prev = NULL;
6198040SBaban.Kenkre@Sun.COM 	p = host_head;
6208040SBaban.Kenkre@Sun.COM 	while (p != NULL) {
6218040SBaban.Kenkre@Sun.COM 		if (p->owner != (*ad)) {
6228040SBaban.Kenkre@Sun.COM 			prev = p;
6238040SBaban.Kenkre@Sun.COM 			p = p->next;
6248040SBaban.Kenkre@Sun.COM 			continue;
6258040SBaban.Kenkre@Sun.COM 		} else {
6268040SBaban.Kenkre@Sun.COM 			delete_ds((*ad), p->host, p->port);
6278040SBaban.Kenkre@Sun.COM 			if (prev == NULL)
6288040SBaban.Kenkre@Sun.COM 				p = host_head;
6298040SBaban.Kenkre@Sun.COM 			else
6308040SBaban.Kenkre@Sun.COM 				p = prev->next;
6318040SBaban.Kenkre@Sun.COM 		}
6328040SBaban.Kenkre@Sun.COM 	}
6338040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&adhostlock);
6348040SBaban.Kenkre@Sun.COM 
6358040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&(*ad)->lock);
6368040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_destroy(&(*ad)->lock);
6378040SBaban.Kenkre@Sun.COM 
638*8361SJulian.Pullen@Sun.COM 	if ((*ad)->known_domains)
639*8361SJulian.Pullen@Sun.COM 		free((*ad)->known_domains);
6408040SBaban.Kenkre@Sun.COM 	free((*ad)->dflt_w2k_dom);
6418040SBaban.Kenkre@Sun.COM 	free(*ad);
6428040SBaban.Kenkre@Sun.COM 
6438040SBaban.Kenkre@Sun.COM 	*ad = NULL;
6448040SBaban.Kenkre@Sun.COM }
6458040SBaban.Kenkre@Sun.COM 
6468040SBaban.Kenkre@Sun.COM static
6478040SBaban.Kenkre@Sun.COM int
6488040SBaban.Kenkre@Sun.COM open_conn(adutils_host_t *adh, int timeoutsecs)
6498040SBaban.Kenkre@Sun.COM {
6508040SBaban.Kenkre@Sun.COM 	int zero = 0;
6518040SBaban.Kenkre@Sun.COM 	int ldversion, rc;
6528040SBaban.Kenkre@Sun.COM 	int timeoutms = timeoutsecs * 1000;
6538040SBaban.Kenkre@Sun.COM 
6548040SBaban.Kenkre@Sun.COM 	if (adh == NULL)
6558040SBaban.Kenkre@Sun.COM 		return (0);
6568040SBaban.Kenkre@Sun.COM 
6578040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&adh->lock);
6588040SBaban.Kenkre@Sun.COM 
6598040SBaban.Kenkre@Sun.COM 	if (!adh->dead && adh->ld != NULL)
6608040SBaban.Kenkre@Sun.COM 		/* done! */
6618040SBaban.Kenkre@Sun.COM 		goto out;
6628040SBaban.Kenkre@Sun.COM 
6638040SBaban.Kenkre@Sun.COM 	if (adh->ld != NULL) {
6648040SBaban.Kenkre@Sun.COM 		(void) ldap_unbind(adh->ld);
6658040SBaban.Kenkre@Sun.COM 		adh->ld = NULL;
6668040SBaban.Kenkre@Sun.COM 	}
6678040SBaban.Kenkre@Sun.COM 	adh->num_requests = 0;
6688040SBaban.Kenkre@Sun.COM 
6698040SBaban.Kenkre@Sun.COM 	atomic_inc_64(&adh->generation);
6708040SBaban.Kenkre@Sun.COM 
6718040SBaban.Kenkre@Sun.COM 	/* Open and bind an LDAP connection */
6728040SBaban.Kenkre@Sun.COM 	adh->ld = ldap_init(adh->host, adh->port);
6738040SBaban.Kenkre@Sun.COM 	if (adh->ld == NULL) {
6748040SBaban.Kenkre@Sun.COM 		idmapdlog(LOG_INFO, "ldap_init() to server "
6758040SBaban.Kenkre@Sun.COM 		    "%s port %d failed. (%s)", adh->host,
6768040SBaban.Kenkre@Sun.COM 		    adh->port, strerror(errno));
6778040SBaban.Kenkre@Sun.COM 		goto out;
6788040SBaban.Kenkre@Sun.COM 	}
6798040SBaban.Kenkre@Sun.COM 	ldversion = LDAP_VERSION3;
6808040SBaban.Kenkre@Sun.COM 	(void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion);
6818040SBaban.Kenkre@Sun.COM 	(void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
6828040SBaban.Kenkre@Sun.COM 	(void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero);
6838040SBaban.Kenkre@Sun.COM 	(void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero);
6848040SBaban.Kenkre@Sun.COM 	(void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
6858040SBaban.Kenkre@Sun.COM 	(void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
6868040SBaban.Kenkre@Sun.COM 	rc = ldap_sasl_interactive_bind_s(adh->ld, "" /* binddn */,
6878040SBaban.Kenkre@Sun.COM 	    adh->saslmech, NULL, NULL, adh->saslflags, &saslcallback,
6888040SBaban.Kenkre@Sun.COM 	    NULL);
6898040SBaban.Kenkre@Sun.COM 
6908040SBaban.Kenkre@Sun.COM 	if (rc != LDAP_SUCCESS) {
6918040SBaban.Kenkre@Sun.COM 		(void) ldap_unbind(adh->ld);
6928040SBaban.Kenkre@Sun.COM 		adh->ld = NULL;
6938040SBaban.Kenkre@Sun.COM 		idmapdlog(LOG_INFO, "ldap_sasl_interactive_bind_s() to server "
6948040SBaban.Kenkre@Sun.COM 		    "%s port %d failed. (%s)", adh->host, adh->port,
6958040SBaban.Kenkre@Sun.COM 		    ldap_err2string(rc));
6968040SBaban.Kenkre@Sun.COM 	}
6978040SBaban.Kenkre@Sun.COM 
6988040SBaban.Kenkre@Sun.COM 	idmapdlog(LOG_DEBUG, "Using global catalog server %s:%d",
6998040SBaban.Kenkre@Sun.COM 	    adh->host, adh->port);
7008040SBaban.Kenkre@Sun.COM 
7018040SBaban.Kenkre@Sun.COM out:
7028040SBaban.Kenkre@Sun.COM 	if (adh->ld != NULL) {
7038040SBaban.Kenkre@Sun.COM 		atomic_inc_32(&adh->ref);
7048040SBaban.Kenkre@Sun.COM 		adh->idletime = time(NULL);
7058040SBaban.Kenkre@Sun.COM 		adh->dead = 0;
7068040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_unlock(&adh->lock);
7078040SBaban.Kenkre@Sun.COM 		return (1);
7088040SBaban.Kenkre@Sun.COM 	}
7098040SBaban.Kenkre@Sun.COM 
7108040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&adh->lock);
7118040SBaban.Kenkre@Sun.COM 	return (0);
7128040SBaban.Kenkre@Sun.COM }
7138040SBaban.Kenkre@Sun.COM 
7148040SBaban.Kenkre@Sun.COM 
7158040SBaban.Kenkre@Sun.COM /*
7168040SBaban.Kenkre@Sun.COM  * Connection management: find an open connection or open one
7178040SBaban.Kenkre@Sun.COM  */
7188040SBaban.Kenkre@Sun.COM static
7198040SBaban.Kenkre@Sun.COM adutils_host_t *
7208040SBaban.Kenkre@Sun.COM get_conn(adutils_ad_t *ad)
7218040SBaban.Kenkre@Sun.COM {
7228040SBaban.Kenkre@Sun.COM 	adutils_host_t	*adh = NULL;
7238040SBaban.Kenkre@Sun.COM 	int		tries;
7248040SBaban.Kenkre@Sun.COM 	int		dscount = 0;
7258040SBaban.Kenkre@Sun.COM 	int		timeoutsecs = ADUTILS_LDAP_OPEN_TIMEOUT;
7268040SBaban.Kenkre@Sun.COM 
7278040SBaban.Kenkre@Sun.COM retry:
7288040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&adhostlock);
7298040SBaban.Kenkre@Sun.COM 
7308040SBaban.Kenkre@Sun.COM 	if (host_head == NULL) {
7318040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_unlock(&adhostlock);
7328040SBaban.Kenkre@Sun.COM 		goto out;
7338040SBaban.Kenkre@Sun.COM 	}
7348040SBaban.Kenkre@Sun.COM 
7358040SBaban.Kenkre@Sun.COM 	if (dscount == 0) {
7368040SBaban.Kenkre@Sun.COM 		/*
7378040SBaban.Kenkre@Sun.COM 		 * First try: count the number of DSes.
7388040SBaban.Kenkre@Sun.COM 		 *
7398040SBaban.Kenkre@Sun.COM 		 * Integer overflow is not an issue -- we can't have so many
7408040SBaban.Kenkre@Sun.COM 		 * DSes because they won't fit even DNS over TCP, and SMF
7418040SBaban.Kenkre@Sun.COM 		 * shouldn't let you set so many.
7428040SBaban.Kenkre@Sun.COM 		 */
7438040SBaban.Kenkre@Sun.COM 		for (adh = host_head, tries = 0; adh != NULL; adh = adh->next) {
7448040SBaban.Kenkre@Sun.COM 			if (adh->owner == ad)
7458040SBaban.Kenkre@Sun.COM 				dscount++;
7468040SBaban.Kenkre@Sun.COM 		}
7478040SBaban.Kenkre@Sun.COM 
7488040SBaban.Kenkre@Sun.COM 		if (dscount == 0) {
7498040SBaban.Kenkre@Sun.COM 			(void) pthread_mutex_unlock(&adhostlock);
7508040SBaban.Kenkre@Sun.COM 			goto out;
7518040SBaban.Kenkre@Sun.COM 		}
7528040SBaban.Kenkre@Sun.COM 
7538040SBaban.Kenkre@Sun.COM 		tries = dscount * 3;	/* three tries per-ds */
7548040SBaban.Kenkre@Sun.COM 
7558040SBaban.Kenkre@Sun.COM 		/*
7568040SBaban.Kenkre@Sun.COM 		 * Begin round-robin at the next DS in the list after the last
7578040SBaban.Kenkre@Sun.COM 		 * one that we had a connection to, else start with the first
7588040SBaban.Kenkre@Sun.COM 		 * DS in the list.
7598040SBaban.Kenkre@Sun.COM 		 */
7608040SBaban.Kenkre@Sun.COM 		adh = ad->last_adh;
7618040SBaban.Kenkre@Sun.COM 	}
7628040SBaban.Kenkre@Sun.COM 
7638040SBaban.Kenkre@Sun.COM 	/*
7648040SBaban.Kenkre@Sun.COM 	 * Round-robin -- pick the next one on the list; if the list
7658040SBaban.Kenkre@Sun.COM 	 * changes on us, no big deal, we'll just potentially go
7668040SBaban.Kenkre@Sun.COM 	 * around the wrong number of times.
7678040SBaban.Kenkre@Sun.COM 	 */
7688040SBaban.Kenkre@Sun.COM 	for (;;) {
769*8361SJulian.Pullen@Sun.COM 		if (adh != NULL && adh->owner == ad && adh->ld != NULL &&
770*8361SJulian.Pullen@Sun.COM 		    !adh->dead)
7718040SBaban.Kenkre@Sun.COM 			break;
7728040SBaban.Kenkre@Sun.COM 		if (adh == NULL || (adh = adh->next) == NULL)
7738040SBaban.Kenkre@Sun.COM 			adh = host_head;
7748040SBaban.Kenkre@Sun.COM 		if (adh->owner == ad)
7758040SBaban.Kenkre@Sun.COM 			break;
7768040SBaban.Kenkre@Sun.COM 	}
7778040SBaban.Kenkre@Sun.COM 
7788040SBaban.Kenkre@Sun.COM 	ad->last_adh = adh;
7798040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&adhostlock);
7808040SBaban.Kenkre@Sun.COM 
7818040SBaban.Kenkre@Sun.COM 	/* Found suitable DS, open it if not already opened */
7828040SBaban.Kenkre@Sun.COM 	if (open_conn(adh, timeoutsecs))
7838040SBaban.Kenkre@Sun.COM 		return (adh);
7848040SBaban.Kenkre@Sun.COM 
7858040SBaban.Kenkre@Sun.COM 	tries--;
7868040SBaban.Kenkre@Sun.COM 	if ((tries % dscount) == 0)
7878040SBaban.Kenkre@Sun.COM 		timeoutsecs *= 2;
7888040SBaban.Kenkre@Sun.COM 	if (tries > 0)
7898040SBaban.Kenkre@Sun.COM 		goto retry;
7908040SBaban.Kenkre@Sun.COM 
7918040SBaban.Kenkre@Sun.COM out:
7928040SBaban.Kenkre@Sun.COM 	idmapdlog(LOG_NOTICE, "Couldn't open an LDAP connection to any global "
7938040SBaban.Kenkre@Sun.COM 	    "catalog server!");
7948040SBaban.Kenkre@Sun.COM 	return (NULL);
7958040SBaban.Kenkre@Sun.COM }
7968040SBaban.Kenkre@Sun.COM 
7978040SBaban.Kenkre@Sun.COM static
7988040SBaban.Kenkre@Sun.COM void
7998040SBaban.Kenkre@Sun.COM release_conn(adutils_host_t *adh)
8008040SBaban.Kenkre@Sun.COM {
8018040SBaban.Kenkre@Sun.COM 	int delete = 0;
8028040SBaban.Kenkre@Sun.COM 
8038040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&adh->lock);
8048040SBaban.Kenkre@Sun.COM 	if (atomic_dec_32_nv(&adh->ref) == 0) {
8058040SBaban.Kenkre@Sun.COM 		if (adh->owner == NULL)
8068040SBaban.Kenkre@Sun.COM 			delete = 1;
8078040SBaban.Kenkre@Sun.COM 		adh->idletime = time(NULL);
8088040SBaban.Kenkre@Sun.COM 	}
8098040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&adh->lock);
8108040SBaban.Kenkre@Sun.COM 
8118040SBaban.Kenkre@Sun.COM 	/* Free this host if its owner no longer exists. */
8128040SBaban.Kenkre@Sun.COM 	if (delete) {
8138040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_lock(&adhostlock);
8148040SBaban.Kenkre@Sun.COM 		delete_ds(NULL, adh->host, adh->port);
8158040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_unlock(&adhostlock);
8168040SBaban.Kenkre@Sun.COM 	}
8178040SBaban.Kenkre@Sun.COM }
8188040SBaban.Kenkre@Sun.COM 
8198040SBaban.Kenkre@Sun.COM /*
8208040SBaban.Kenkre@Sun.COM  * Create a adutils_host_t, populate it and add it to the list of hosts.
8218040SBaban.Kenkre@Sun.COM  */
8228040SBaban.Kenkre@Sun.COM adutils_rc
8238040SBaban.Kenkre@Sun.COM adutils_add_ds(adutils_ad_t *ad, const char *host, int port)
8248040SBaban.Kenkre@Sun.COM {
8258040SBaban.Kenkre@Sun.COM 	adutils_host_t	*p;
8268040SBaban.Kenkre@Sun.COM 	adutils_host_t	*new = NULL;
8278040SBaban.Kenkre@Sun.COM 	int		ret;
8288040SBaban.Kenkre@Sun.COM 	adutils_rc	rc;
8298040SBaban.Kenkre@Sun.COM 
8308040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&adhostlock);
8318040SBaban.Kenkre@Sun.COM 	for (p = host_head; p != NULL; p = p->next) {
8328040SBaban.Kenkre@Sun.COM 		if (p->owner != ad)
8338040SBaban.Kenkre@Sun.COM 			continue;
8348040SBaban.Kenkre@Sun.COM 
8358040SBaban.Kenkre@Sun.COM 		if (strcmp(host, p->host) == 0 && p->port == port) {
8368040SBaban.Kenkre@Sun.COM 			/* already added */
8378040SBaban.Kenkre@Sun.COM 			rc = ADUTILS_SUCCESS;
8388040SBaban.Kenkre@Sun.COM 			goto err;
8398040SBaban.Kenkre@Sun.COM 		}
8408040SBaban.Kenkre@Sun.COM 	}
8418040SBaban.Kenkre@Sun.COM 
8428040SBaban.Kenkre@Sun.COM 	rc = ADUTILS_ERR_MEMORY;
8438040SBaban.Kenkre@Sun.COM 
8448040SBaban.Kenkre@Sun.COM 	/* add new entry */
8458040SBaban.Kenkre@Sun.COM 	new = (adutils_host_t *)calloc(1, sizeof (*new));
8468040SBaban.Kenkre@Sun.COM 	if (new == NULL)
8478040SBaban.Kenkre@Sun.COM 		goto err;
8488040SBaban.Kenkre@Sun.COM 	new->owner = ad;
8498040SBaban.Kenkre@Sun.COM 	new->port = port;
8508040SBaban.Kenkre@Sun.COM 	new->dead = 0;
8518040SBaban.Kenkre@Sun.COM 	new->max_requests = 80;
8528040SBaban.Kenkre@Sun.COM 	new->num_requests = 0;
8538040SBaban.Kenkre@Sun.COM 	if ((new->host = strdup(host)) == NULL)
8548040SBaban.Kenkre@Sun.COM 		goto err;
8558040SBaban.Kenkre@Sun.COM 	new->saslflags = LDAP_SASL_INTERACTIVE;
8568040SBaban.Kenkre@Sun.COM 	new->saslmech = "GSSAPI";
8578040SBaban.Kenkre@Sun.COM 
8588040SBaban.Kenkre@Sun.COM 	if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) {
8598040SBaban.Kenkre@Sun.COM 		free(new->host);
8608040SBaban.Kenkre@Sun.COM 		new->host = NULL;
8618040SBaban.Kenkre@Sun.COM 		errno = ret;
8628040SBaban.Kenkre@Sun.COM 		rc = ADUTILS_ERR_INTERNAL;
8638040SBaban.Kenkre@Sun.COM 		goto err;
8648040SBaban.Kenkre@Sun.COM 	}
8658040SBaban.Kenkre@Sun.COM 
8668040SBaban.Kenkre@Sun.COM 	/* link in */
8678040SBaban.Kenkre@Sun.COM 	rc = ADUTILS_SUCCESS;
8688040SBaban.Kenkre@Sun.COM 	new->next = host_head;
8698040SBaban.Kenkre@Sun.COM 	host_head = new;
8708040SBaban.Kenkre@Sun.COM 
8718040SBaban.Kenkre@Sun.COM err:
8728040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&adhostlock);
8738040SBaban.Kenkre@Sun.COM 
8748040SBaban.Kenkre@Sun.COM 	if (rc != 0 && new != NULL) {
8758040SBaban.Kenkre@Sun.COM 		if (new->host != NULL) {
8768040SBaban.Kenkre@Sun.COM 			(void) pthread_mutex_destroy(&new->lock);
8778040SBaban.Kenkre@Sun.COM 			free(new->host);
8788040SBaban.Kenkre@Sun.COM 		}
8798040SBaban.Kenkre@Sun.COM 		free(new);
8808040SBaban.Kenkre@Sun.COM 	}
8818040SBaban.Kenkre@Sun.COM 
8828040SBaban.Kenkre@Sun.COM 	return (rc);
8838040SBaban.Kenkre@Sun.COM }
8848040SBaban.Kenkre@Sun.COM 
8858040SBaban.Kenkre@Sun.COM /*
8868040SBaban.Kenkre@Sun.COM  * Free a DS configuration.
8878040SBaban.Kenkre@Sun.COM  * Caller must lock the adhostlock mutex
8888040SBaban.Kenkre@Sun.COM  */
8898040SBaban.Kenkre@Sun.COM static
8908040SBaban.Kenkre@Sun.COM void
8918040SBaban.Kenkre@Sun.COM delete_ds(adutils_ad_t *ad, const char *host, int port)
8928040SBaban.Kenkre@Sun.COM {
8938040SBaban.Kenkre@Sun.COM 	adutils_host_t	**p, *q;
8948040SBaban.Kenkre@Sun.COM 
8958040SBaban.Kenkre@Sun.COM 	for (p = &host_head; *p != NULL; p = &((*p)->next)) {
8968040SBaban.Kenkre@Sun.COM 		if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 ||
8978040SBaban.Kenkre@Sun.COM 		    (*p)->port != port)
8988040SBaban.Kenkre@Sun.COM 			continue;
8998040SBaban.Kenkre@Sun.COM 		/* found */
9008040SBaban.Kenkre@Sun.COM 
9018040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_lock(&((*p)->lock));
9028040SBaban.Kenkre@Sun.COM 		if ((*p)->ref > 0) {
9038040SBaban.Kenkre@Sun.COM 			/*
9048040SBaban.Kenkre@Sun.COM 			 * Still in use. Set its owner to NULL so
9058040SBaban.Kenkre@Sun.COM 			 * that it can be freed when its ref count
9068040SBaban.Kenkre@Sun.COM 			 * becomes 0.
9078040SBaban.Kenkre@Sun.COM 			 */
9088040SBaban.Kenkre@Sun.COM 			(*p)->owner = NULL;
9098040SBaban.Kenkre@Sun.COM 			(void) pthread_mutex_unlock(&((*p)->lock));
9108040SBaban.Kenkre@Sun.COM 			break;
9118040SBaban.Kenkre@Sun.COM 		}
9128040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_unlock(&((*p)->lock));
9138040SBaban.Kenkre@Sun.COM 
9148040SBaban.Kenkre@Sun.COM 		q = *p;
9158040SBaban.Kenkre@Sun.COM 		*p = (*p)->next;
9168040SBaban.Kenkre@Sun.COM 
9178040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_destroy(&q->lock);
9188040SBaban.Kenkre@Sun.COM 
9198040SBaban.Kenkre@Sun.COM 		if (q->ld)
9208040SBaban.Kenkre@Sun.COM 			(void) ldap_unbind(q->ld);
9218040SBaban.Kenkre@Sun.COM 		if (q->host)
9228040SBaban.Kenkre@Sun.COM 			free(q->host);
9238040SBaban.Kenkre@Sun.COM 		free(q);
9248040SBaban.Kenkre@Sun.COM 		break;
9258040SBaban.Kenkre@Sun.COM 	}
9268040SBaban.Kenkre@Sun.COM 
9278040SBaban.Kenkre@Sun.COM }
928*8361SJulian.Pullen@Sun.COM /*
929*8361SJulian.Pullen@Sun.COM  * Add known domain name and domain SID to AD configuration.
930*8361SJulian.Pullen@Sun.COM  */
931*8361SJulian.Pullen@Sun.COM 
932*8361SJulian.Pullen@Sun.COM adutils_rc
933*8361SJulian.Pullen@Sun.COM adutils_add_domain(adutils_ad_t *ad, const char *domain, const char *sid)
934*8361SJulian.Pullen@Sun.COM {
935*8361SJulian.Pullen@Sun.COM 	struct known_domain *new;
936*8361SJulian.Pullen@Sun.COM 	int num = ad->num_known_domains;
937*8361SJulian.Pullen@Sun.COM 
938*8361SJulian.Pullen@Sun.COM 	ad->num_known_domains++;
939*8361SJulian.Pullen@Sun.COM 	new = realloc(ad->known_domains,
940*8361SJulian.Pullen@Sun.COM 	    sizeof (struct known_domain) * ad->num_known_domains);
941*8361SJulian.Pullen@Sun.COM 	if (new != NULL) {
942*8361SJulian.Pullen@Sun.COM 		ad->known_domains = new;
943*8361SJulian.Pullen@Sun.COM 		(void) strlcpy(ad->known_domains[num].name, domain,
944*8361SJulian.Pullen@Sun.COM 		    sizeof (ad->known_domains[num].name));
945*8361SJulian.Pullen@Sun.COM 		(void) strlcpy(ad->known_domains[num].sid, sid,
946*8361SJulian.Pullen@Sun.COM 		    sizeof (ad->known_domains[num].sid));
947*8361SJulian.Pullen@Sun.COM 		return (ADUTILS_SUCCESS);
948*8361SJulian.Pullen@Sun.COM 	} else {
949*8361SJulian.Pullen@Sun.COM 		if (ad->known_domains != NULL) {
950*8361SJulian.Pullen@Sun.COM 			free(ad->known_domains);
951*8361SJulian.Pullen@Sun.COM 			ad->known_domains = NULL;
952*8361SJulian.Pullen@Sun.COM 		}
953*8361SJulian.Pullen@Sun.COM 		ad->num_known_domains = 0;
954*8361SJulian.Pullen@Sun.COM 		return (ADUTILS_ERR_MEMORY);
955*8361SJulian.Pullen@Sun.COM 	}
956*8361SJulian.Pullen@Sun.COM }
957*8361SJulian.Pullen@Sun.COM 
958*8361SJulian.Pullen@Sun.COM 
959*8361SJulian.Pullen@Sun.COM /*
960*8361SJulian.Pullen@Sun.COM  * Check that this AD supports this domain.
961*8361SJulian.Pullen@Sun.COM  * If there are no known domains assume that the
962*8361SJulian.Pullen@Sun.COM  * domain is supported by this AD.
963*8361SJulian.Pullen@Sun.COM  *
964*8361SJulian.Pullen@Sun.COM  * Returns 1 if this domain is supported by this AD
965*8361SJulian.Pullen@Sun.COM  * else returns 0;
966*8361SJulian.Pullen@Sun.COM  */
967*8361SJulian.Pullen@Sun.COM 
968*8361SJulian.Pullen@Sun.COM int
969*8361SJulian.Pullen@Sun.COM adutils_lookup_check_domain(adutils_query_state_t *qs, const char *domain)
970*8361SJulian.Pullen@Sun.COM {
971*8361SJulian.Pullen@Sun.COM 	adutils_ad_t *ad = qs->qadh->owner;
972*8361SJulian.Pullen@Sun.COM 	int i, err;
973*8361SJulian.Pullen@Sun.COM 
974*8361SJulian.Pullen@Sun.COM 	for (i = 0; i < ad->num_known_domains; i++) {
975*8361SJulian.Pullen@Sun.COM 		if (u8_strcmp(domain, ad->known_domains[i].name, 0,
976*8361SJulian.Pullen@Sun.COM 		    U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) == 0 &&
977*8361SJulian.Pullen@Sun.COM 		    err == 0)
978*8361SJulian.Pullen@Sun.COM 			return (1);
979*8361SJulian.Pullen@Sun.COM 	}
980*8361SJulian.Pullen@Sun.COM 
981*8361SJulian.Pullen@Sun.COM 	return ((i == 0) ? 1 : 0);
982*8361SJulian.Pullen@Sun.COM }
983*8361SJulian.Pullen@Sun.COM 
984*8361SJulian.Pullen@Sun.COM 
985*8361SJulian.Pullen@Sun.COM /*
986*8361SJulian.Pullen@Sun.COM  * Check that this AD supports the SID prefix.
987*8361SJulian.Pullen@Sun.COM  * The SID prefix should match the domain SID.
988*8361SJulian.Pullen@Sun.COM  * If there are no known domains assume that the
989*8361SJulian.Pullen@Sun.COM  * SID prefix is supported by this AD.
990*8361SJulian.Pullen@Sun.COM  *
991*8361SJulian.Pullen@Sun.COM  * Returns 1 if this sid prefix is supported by this AD
992*8361SJulian.Pullen@Sun.COM  * else returns 0;
993*8361SJulian.Pullen@Sun.COM  */
994*8361SJulian.Pullen@Sun.COM 
995*8361SJulian.Pullen@Sun.COM int
996*8361SJulian.Pullen@Sun.COM adutils_lookup_check_sid_prefix(adutils_query_state_t *qs, const char *sid)
997*8361SJulian.Pullen@Sun.COM {
998*8361SJulian.Pullen@Sun.COM 	adutils_ad_t *ad = qs->qadh->owner;
999*8361SJulian.Pullen@Sun.COM 	int i;
1000*8361SJulian.Pullen@Sun.COM 
1001*8361SJulian.Pullen@Sun.COM 
1002*8361SJulian.Pullen@Sun.COM 	for (i = 0; i < ad->num_known_domains; i++) {
1003*8361SJulian.Pullen@Sun.COM 		if (strcmp(sid, ad->known_domains[i].sid) == 0)
1004*8361SJulian.Pullen@Sun.COM 			return (1);
1005*8361SJulian.Pullen@Sun.COM 	}
1006*8361SJulian.Pullen@Sun.COM 
1007*8361SJulian.Pullen@Sun.COM 	return ((i == 0) ? 1 : 0);
1008*8361SJulian.Pullen@Sun.COM }
1009*8361SJulian.Pullen@Sun.COM 
10108040SBaban.Kenkre@Sun.COM 
10118040SBaban.Kenkre@Sun.COM adutils_rc
10128040SBaban.Kenkre@Sun.COM adutils_lookup_batch_start(adutils_ad_t *ad, int nqueries,
10138040SBaban.Kenkre@Sun.COM 	adutils_ldap_res_search_cb ldap_res_search_cb,
10148040SBaban.Kenkre@Sun.COM 	void *ldap_res_search_argp,
10158040SBaban.Kenkre@Sun.COM 	adutils_query_state_t **state)
10168040SBaban.Kenkre@Sun.COM {
10178040SBaban.Kenkre@Sun.COM 	adutils_query_state_t	*new_state;
10188040SBaban.Kenkre@Sun.COM 	adutils_host_t		*adh = NULL;
10198040SBaban.Kenkre@Sun.COM 
10208040SBaban.Kenkre@Sun.COM 	if (ad == NULL)
10218040SBaban.Kenkre@Sun.COM 		return (ADUTILS_ERR_INTERNAL);
10228040SBaban.Kenkre@Sun.COM 
10238040SBaban.Kenkre@Sun.COM 	*state = NULL;
10248040SBaban.Kenkre@Sun.COM 	adh = get_conn(ad);
10258040SBaban.Kenkre@Sun.COM 	if (adh == NULL)
10268040SBaban.Kenkre@Sun.COM 		return (ADUTILS_ERR_RETRIABLE_NET_ERR);
10278040SBaban.Kenkre@Sun.COM 
10288040SBaban.Kenkre@Sun.COM 	new_state = calloc(1, sizeof (adutils_query_state_t) +
10298040SBaban.Kenkre@Sun.COM 	    (nqueries - 1) * sizeof (adutils_q_t));
10308040SBaban.Kenkre@Sun.COM 	if (new_state == NULL)
10318040SBaban.Kenkre@Sun.COM 		return (ADUTILS_ERR_MEMORY);
10328040SBaban.Kenkre@Sun.COM 
10338040SBaban.Kenkre@Sun.COM 	/*
10348040SBaban.Kenkre@Sun.COM 	 * Save default domain from the ad object so that we don't
10358040SBaban.Kenkre@Sun.COM 	 * have to access the 'ad' object later.
10368040SBaban.Kenkre@Sun.COM 	 */
10378040SBaban.Kenkre@Sun.COM 	new_state->default_domain = strdup(adh->owner->dflt_w2k_dom);
10388040SBaban.Kenkre@Sun.COM 	if (new_state->default_domain == NULL) {
10398040SBaban.Kenkre@Sun.COM 		free(new_state);
10408040SBaban.Kenkre@Sun.COM 		return (ADUTILS_ERR_MEMORY);
10418040SBaban.Kenkre@Sun.COM 	}
10428040SBaban.Kenkre@Sun.COM 
10438040SBaban.Kenkre@Sun.COM 	if (ad->partition == ADUTILS_AD_DATA)
10448040SBaban.Kenkre@Sun.COM 		new_state->basedn = adutils_dns2dn(new_state->default_domain);
10458040SBaban.Kenkre@Sun.COM 	else
10468040SBaban.Kenkre@Sun.COM 		new_state->basedn = strdup("");
10478040SBaban.Kenkre@Sun.COM 	if (new_state->basedn == NULL) {
10488040SBaban.Kenkre@Sun.COM 		free(new_state->default_domain);
10498040SBaban.Kenkre@Sun.COM 		free(new_state);
10508040SBaban.Kenkre@Sun.COM 		return (ADUTILS_ERR_MEMORY);
10518040SBaban.Kenkre@Sun.COM 	}
10528040SBaban.Kenkre@Sun.COM 
10538040SBaban.Kenkre@Sun.COM 	new_state->ref_cnt = 1;
10548040SBaban.Kenkre@Sun.COM 	new_state->qadh = adh;
1055*8361SJulian.Pullen@Sun.COM 	new_state->qsize = nqueries;
10568040SBaban.Kenkre@Sun.COM 	new_state->qadh_gen = adh->generation;
1057*8361SJulian.Pullen@Sun.COM 	new_state->qcount = 0;
10588040SBaban.Kenkre@Sun.COM 	new_state->ldap_res_search_cb = ldap_res_search_cb;
10598040SBaban.Kenkre@Sun.COM 	new_state->ldap_res_search_argp = ldap_res_search_argp;
10608040SBaban.Kenkre@Sun.COM 	(void) pthread_cond_init(&new_state->cv, NULL);
10618040SBaban.Kenkre@Sun.COM 
10628040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&qstatelock);
10638040SBaban.Kenkre@Sun.COM 	new_state->next = qstatehead;
10648040SBaban.Kenkre@Sun.COM 	qstatehead = new_state;
10658040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&qstatelock);
10668040SBaban.Kenkre@Sun.COM 	*state = new_state;
10678040SBaban.Kenkre@Sun.COM 
10688040SBaban.Kenkre@Sun.COM 	return (ADUTILS_SUCCESS);
10698040SBaban.Kenkre@Sun.COM }
10708040SBaban.Kenkre@Sun.COM 
10718040SBaban.Kenkre@Sun.COM /*
10728040SBaban.Kenkre@Sun.COM  * Find the adutils_query_state_t to which a given LDAP result msgid on a
10738040SBaban.Kenkre@Sun.COM  * given connection belongs. This routine increaments the reference count
10748040SBaban.Kenkre@Sun.COM  * so that the object can not be freed. adutils_lookup_batch_unlock()
10758040SBaban.Kenkre@Sun.COM  * must be called to decreament the reference count.
10768040SBaban.Kenkre@Sun.COM  */
10778040SBaban.Kenkre@Sun.COM static
10788040SBaban.Kenkre@Sun.COM int
10798040SBaban.Kenkre@Sun.COM msgid2query(adutils_host_t *adh, int msgid,
10808040SBaban.Kenkre@Sun.COM 	adutils_query_state_t **state, int *qid)
10818040SBaban.Kenkre@Sun.COM {
10828040SBaban.Kenkre@Sun.COM 	adutils_query_state_t	*p;
10838040SBaban.Kenkre@Sun.COM 	int			i;
10848040SBaban.Kenkre@Sun.COM 	int			ret;
10858040SBaban.Kenkre@Sun.COM 
10868040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&qstatelock);
10878040SBaban.Kenkre@Sun.COM 	for (p = qstatehead; p != NULL; p = p->next) {
10888040SBaban.Kenkre@Sun.COM 		if (p->qadh != adh || adh->generation != p->qadh_gen)
10898040SBaban.Kenkre@Sun.COM 			continue;
10908040SBaban.Kenkre@Sun.COM 		for (i = 0; i < p->qcount; i++) {
10918040SBaban.Kenkre@Sun.COM 			if ((p->queries[i]).msgid == msgid) {
10928040SBaban.Kenkre@Sun.COM 				if (!p->qdead) {
10938040SBaban.Kenkre@Sun.COM 					p->ref_cnt++;
10948040SBaban.Kenkre@Sun.COM 					*state = p;
10958040SBaban.Kenkre@Sun.COM 					*qid = i;
10968040SBaban.Kenkre@Sun.COM 					ret = 1;
10978040SBaban.Kenkre@Sun.COM 				} else
10988040SBaban.Kenkre@Sun.COM 					ret = 0;
10998040SBaban.Kenkre@Sun.COM 				(void) pthread_mutex_unlock(&qstatelock);
11008040SBaban.Kenkre@Sun.COM 				return (ret);
11018040SBaban.Kenkre@Sun.COM 			}
11028040SBaban.Kenkre@Sun.COM 		}
11038040SBaban.Kenkre@Sun.COM 	}
11048040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&qstatelock);
11058040SBaban.Kenkre@Sun.COM 	return (0);
11068040SBaban.Kenkre@Sun.COM }
11078040SBaban.Kenkre@Sun.COM 
11088040SBaban.Kenkre@Sun.COM static
11098040SBaban.Kenkre@Sun.COM int
11108040SBaban.Kenkre@Sun.COM check_for_binary_attrs(const char *attr)
11118040SBaban.Kenkre@Sun.COM {
11128040SBaban.Kenkre@Sun.COM 	int i;
11138040SBaban.Kenkre@Sun.COM 	for (i = 0; binattrs[i].name != NULL; i++) {
11148040SBaban.Kenkre@Sun.COM 		if (strcasecmp(binattrs[i].name, attr) == 0)
11158040SBaban.Kenkre@Sun.COM 			return (i);
11168040SBaban.Kenkre@Sun.COM 	}
11178040SBaban.Kenkre@Sun.COM 	return (-1);
11188040SBaban.Kenkre@Sun.COM }
11198040SBaban.Kenkre@Sun.COM 
11208040SBaban.Kenkre@Sun.COM static
11218040SBaban.Kenkre@Sun.COM void
11228040SBaban.Kenkre@Sun.COM free_entry(adutils_entry_t *entry)
11238040SBaban.Kenkre@Sun.COM {
11248040SBaban.Kenkre@Sun.COM 	int		i, j;
11258040SBaban.Kenkre@Sun.COM 	adutils_attr_t	*ap;
11268040SBaban.Kenkre@Sun.COM 
11278040SBaban.Kenkre@Sun.COM 	if (entry == NULL)
11288040SBaban.Kenkre@Sun.COM 		return;
11298040SBaban.Kenkre@Sun.COM 	if (entry->attr_nvpairs == NULL) {
11308040SBaban.Kenkre@Sun.COM 		free(entry);
11318040SBaban.Kenkre@Sun.COM 		return;
11328040SBaban.Kenkre@Sun.COM 	}
11338040SBaban.Kenkre@Sun.COM 	for (i = 0; i < entry->num_nvpairs; i++) {
11348040SBaban.Kenkre@Sun.COM 		ap = &entry->attr_nvpairs[i];
11358040SBaban.Kenkre@Sun.COM 		if (ap->attr_name == NULL) {
11368040SBaban.Kenkre@Sun.COM 			ldap_value_free(ap->attr_values);
11378040SBaban.Kenkre@Sun.COM 			continue;
11388040SBaban.Kenkre@Sun.COM 		}
11398040SBaban.Kenkre@Sun.COM 		if (check_for_binary_attrs(ap->attr_name) >= 0) {
11408040SBaban.Kenkre@Sun.COM 			free(ap->attr_name);
11418040SBaban.Kenkre@Sun.COM 			if (ap->attr_values == NULL)
11428040SBaban.Kenkre@Sun.COM 				continue;
11438040SBaban.Kenkre@Sun.COM 			for (j = 0; j < ap->num_values; j++)
11448040SBaban.Kenkre@Sun.COM 				free(ap->attr_values[j]);
11458040SBaban.Kenkre@Sun.COM 			free(ap->attr_values);
11468040SBaban.Kenkre@Sun.COM 		} else if (strcasecmp(ap->attr_name, "dn") == 0) {
11478040SBaban.Kenkre@Sun.COM 			free(ap->attr_name);
11488040SBaban.Kenkre@Sun.COM 			ldap_memfree(ap->attr_values[0]);
11498040SBaban.Kenkre@Sun.COM 			free(ap->attr_values);
11508040SBaban.Kenkre@Sun.COM 		} else {
11518040SBaban.Kenkre@Sun.COM 			free(ap->attr_name);
11528040SBaban.Kenkre@Sun.COM 			ldap_value_free(ap->attr_values);
11538040SBaban.Kenkre@Sun.COM 		}
11548040SBaban.Kenkre@Sun.COM 	}
11558040SBaban.Kenkre@Sun.COM 	free(entry->attr_nvpairs);
11568040SBaban.Kenkre@Sun.COM 	free(entry);
11578040SBaban.Kenkre@Sun.COM }
11588040SBaban.Kenkre@Sun.COM 
11598040SBaban.Kenkre@Sun.COM void
11608040SBaban.Kenkre@Sun.COM adutils_freeresult(adutils_result_t **result)
11618040SBaban.Kenkre@Sun.COM {
11628040SBaban.Kenkre@Sun.COM 	adutils_entry_t	*e, *next;
11638040SBaban.Kenkre@Sun.COM 
11648040SBaban.Kenkre@Sun.COM 	if (result == NULL || *result == NULL)
11658040SBaban.Kenkre@Sun.COM 		return;
11668040SBaban.Kenkre@Sun.COM 	if ((*result)->entries == NULL) {
11678040SBaban.Kenkre@Sun.COM 		free(*result);
11688040SBaban.Kenkre@Sun.COM 		*result = NULL;
11698040SBaban.Kenkre@Sun.COM 		return;
11708040SBaban.Kenkre@Sun.COM 	}
11718040SBaban.Kenkre@Sun.COM 	for (e = (*result)->entries; e != NULL; e = next) {
11728040SBaban.Kenkre@Sun.COM 		next = e->next;
11738040SBaban.Kenkre@Sun.COM 		free_entry(e);
11748040SBaban.Kenkre@Sun.COM 	}
11758040SBaban.Kenkre@Sun.COM 	free(*result);
11768040SBaban.Kenkre@Sun.COM 	*result = NULL;
11778040SBaban.Kenkre@Sun.COM }
11788040SBaban.Kenkre@Sun.COM 
11798040SBaban.Kenkre@Sun.COM const adutils_entry_t *
11808040SBaban.Kenkre@Sun.COM adutils_getfirstentry(adutils_result_t *result)
11818040SBaban.Kenkre@Sun.COM {
11828040SBaban.Kenkre@Sun.COM 	if (result != NULL)
11838040SBaban.Kenkre@Sun.COM 		return (result->entries);
11848040SBaban.Kenkre@Sun.COM 	return (NULL);
11858040SBaban.Kenkre@Sun.COM }
11868040SBaban.Kenkre@Sun.COM 
11878040SBaban.Kenkre@Sun.COM 
11888040SBaban.Kenkre@Sun.COM char **
11898040SBaban.Kenkre@Sun.COM adutils_getattr(const adutils_entry_t *entry, const char *attrname)
11908040SBaban.Kenkre@Sun.COM {
11918040SBaban.Kenkre@Sun.COM 	int		i;
11928040SBaban.Kenkre@Sun.COM 	adutils_attr_t	*ap;
11938040SBaban.Kenkre@Sun.COM 
11948040SBaban.Kenkre@Sun.COM 	if (entry == NULL || entry->attr_nvpairs == NULL)
11958040SBaban.Kenkre@Sun.COM 		return (NULL);
11968040SBaban.Kenkre@Sun.COM 	for (i = 0; i < entry->num_nvpairs; i++) {
11978040SBaban.Kenkre@Sun.COM 		ap = &entry->attr_nvpairs[i];
11988040SBaban.Kenkre@Sun.COM 		if (ap->attr_name != NULL &&
11998040SBaban.Kenkre@Sun.COM 		    strcasecmp(ap->attr_name, attrname) == 0)
12008040SBaban.Kenkre@Sun.COM 			return (ap->attr_values);
12018040SBaban.Kenkre@Sun.COM 	}
12028040SBaban.Kenkre@Sun.COM 	return (NULL);
12038040SBaban.Kenkre@Sun.COM }
12048040SBaban.Kenkre@Sun.COM 
12058040SBaban.Kenkre@Sun.COM 
12068040SBaban.Kenkre@Sun.COM /*
12078040SBaban.Kenkre@Sun.COM  * Queue LDAP result for the given query.
12088040SBaban.Kenkre@Sun.COM  *
12098040SBaban.Kenkre@Sun.COM  * Return values:
12108040SBaban.Kenkre@Sun.COM  *  0 success
12118040SBaban.Kenkre@Sun.COM  * -1 ignore result
12128040SBaban.Kenkre@Sun.COM  * -2 error
12138040SBaban.Kenkre@Sun.COM  */
12148040SBaban.Kenkre@Sun.COM static
12158040SBaban.Kenkre@Sun.COM int
12168040SBaban.Kenkre@Sun.COM make_entry(adutils_q_t *q, adutils_host_t *adh, LDAPMessage *search_res,
12178040SBaban.Kenkre@Sun.COM 	adutils_entry_t **entry)
12188040SBaban.Kenkre@Sun.COM {
12198040SBaban.Kenkre@Sun.COM 	BerElement	*ber = NULL;
12208040SBaban.Kenkre@Sun.COM 	BerValue	**bvalues = NULL;
12218040SBaban.Kenkre@Sun.COM 	char		**strvalues;
12228040SBaban.Kenkre@Sun.COM 	char		*attr = NULL, *dn = NULL, *domain = NULL;
12238040SBaban.Kenkre@Sun.COM 	adutils_entry_t	*ep;
12248040SBaban.Kenkre@Sun.COM 	adutils_attr_t	*ap;
12258040SBaban.Kenkre@Sun.COM 	int		i, j, b, err = 0, ret = -2;
12268040SBaban.Kenkre@Sun.COM 
12278040SBaban.Kenkre@Sun.COM 	*entry = NULL;
12288040SBaban.Kenkre@Sun.COM 
12298040SBaban.Kenkre@Sun.COM 	/* Check that this is the domain that we were looking for */
12308040SBaban.Kenkre@Sun.COM 	if ((dn = ldap_get_dn(adh->ld, search_res)) == NULL)
12318040SBaban.Kenkre@Sun.COM 		return (-2);
12328040SBaban.Kenkre@Sun.COM 	if ((domain = adutils_dn2dns(dn)) == NULL) {
12338040SBaban.Kenkre@Sun.COM 		ldap_memfree(dn);
12348040SBaban.Kenkre@Sun.COM 		return (-2);
12358040SBaban.Kenkre@Sun.COM 	}
12368040SBaban.Kenkre@Sun.COM 	if (q->edomain != NULL) {
12378040SBaban.Kenkre@Sun.COM 		if (u8_strcmp(q->edomain, domain, 0, U8_STRCMP_CI_LOWER,
12388040SBaban.Kenkre@Sun.COM 		    U8_UNICODE_LATEST, &err) != 0 || err != 0) {
12398040SBaban.Kenkre@Sun.COM 			ldap_memfree(dn);
12408040SBaban.Kenkre@Sun.COM 			free(domain);
12418040SBaban.Kenkre@Sun.COM 			return (-1);
12428040SBaban.Kenkre@Sun.COM 		}
12438040SBaban.Kenkre@Sun.COM 	}
12448040SBaban.Kenkre@Sun.COM 	free(domain);
12458040SBaban.Kenkre@Sun.COM 
12468040SBaban.Kenkre@Sun.COM 	/* Allocate memory for the entry */
12478040SBaban.Kenkre@Sun.COM 	if ((ep = calloc(1, sizeof (*ep))) == NULL)
12488040SBaban.Kenkre@Sun.COM 		goto out;
12498040SBaban.Kenkre@Sun.COM 
12508040SBaban.Kenkre@Sun.COM 	/* For 'dn' */
12518040SBaban.Kenkre@Sun.COM 	ep->num_nvpairs = 1;
12528040SBaban.Kenkre@Sun.COM 
12538040SBaban.Kenkre@Sun.COM 	/* Count the number of name-value pairs for this entry */
12548040SBaban.Kenkre@Sun.COM 	for (attr = ldap_first_attribute(adh->ld, search_res, &ber);
12558040SBaban.Kenkre@Sun.COM 	    attr != NULL;
12568040SBaban.Kenkre@Sun.COM 	    attr = ldap_next_attribute(adh->ld, search_res, ber)) {
12578040SBaban.Kenkre@Sun.COM 		ep->num_nvpairs++;
12588040SBaban.Kenkre@Sun.COM 		ldap_memfree(attr);
12598040SBaban.Kenkre@Sun.COM 	}
12608040SBaban.Kenkre@Sun.COM 	ber_free(ber, 0);
12618040SBaban.Kenkre@Sun.COM 	ber = NULL;
12628040SBaban.Kenkre@Sun.COM 
12638040SBaban.Kenkre@Sun.COM 	/* Allocate array for the attribute name-value pairs */
12648040SBaban.Kenkre@Sun.COM 	ep->attr_nvpairs = calloc(ep->num_nvpairs, sizeof (*ep->attr_nvpairs));
12658040SBaban.Kenkre@Sun.COM 	if (ep->attr_nvpairs == NULL) {
12668040SBaban.Kenkre@Sun.COM 		ep->num_nvpairs = 0;
12678040SBaban.Kenkre@Sun.COM 		goto out;
12688040SBaban.Kenkre@Sun.COM 	}
12698040SBaban.Kenkre@Sun.COM 
12708040SBaban.Kenkre@Sun.COM 	/* For dn */
12718040SBaban.Kenkre@Sun.COM 	ap = &ep->attr_nvpairs[0];
12728040SBaban.Kenkre@Sun.COM 	if ((ap->attr_name = strdup("dn")) == NULL)
12738040SBaban.Kenkre@Sun.COM 		goto out;
12748040SBaban.Kenkre@Sun.COM 	ap->num_values = 1;
12758040SBaban.Kenkre@Sun.COM 	ap->attr_values = calloc(ap->num_values, sizeof (*ap->attr_values));
12768040SBaban.Kenkre@Sun.COM 	if (ap->attr_values == NULL) {
12778040SBaban.Kenkre@Sun.COM 		ap->num_values = 0;
12788040SBaban.Kenkre@Sun.COM 		goto out;
12798040SBaban.Kenkre@Sun.COM 	}
12808040SBaban.Kenkre@Sun.COM 	ap->attr_values[0] = dn;
12818040SBaban.Kenkre@Sun.COM 	dn = NULL;
12828040SBaban.Kenkre@Sun.COM 
12838040SBaban.Kenkre@Sun.COM 	for (attr = ldap_first_attribute(adh->ld, search_res, &ber), i = 1;
12848040SBaban.Kenkre@Sun.COM 	    attr != NULL;
12858040SBaban.Kenkre@Sun.COM 	    ldap_memfree(attr), i++,
12868040SBaban.Kenkre@Sun.COM 	    attr = ldap_next_attribute(adh->ld, search_res, ber)) {
12878040SBaban.Kenkre@Sun.COM 		ap = &ep->attr_nvpairs[i];
12888040SBaban.Kenkre@Sun.COM 		if ((ap->attr_name = strdup(attr)) == NULL)
12898040SBaban.Kenkre@Sun.COM 			goto out;
12908040SBaban.Kenkre@Sun.COM 
12918040SBaban.Kenkre@Sun.COM 		if ((b = check_for_binary_attrs(attr)) >= 0) {
12928040SBaban.Kenkre@Sun.COM 			bvalues =
12938040SBaban.Kenkre@Sun.COM 			    ldap_get_values_len(adh->ld, search_res, attr);
12948040SBaban.Kenkre@Sun.COM 			if (bvalues == NULL)
12958040SBaban.Kenkre@Sun.COM 				continue;
12968040SBaban.Kenkre@Sun.COM 			ap->num_values = ldap_count_values_len(bvalues);
12978040SBaban.Kenkre@Sun.COM 			if (ap->num_values == 0) {
12988040SBaban.Kenkre@Sun.COM 				ldap_value_free_len(bvalues);
12998040SBaban.Kenkre@Sun.COM 				bvalues = NULL;
13008040SBaban.Kenkre@Sun.COM 				continue;
13018040SBaban.Kenkre@Sun.COM 			}
13028040SBaban.Kenkre@Sun.COM 			ap->attr_values = calloc(ap->num_values,
13038040SBaban.Kenkre@Sun.COM 			    sizeof (*ap->attr_values));
13048040SBaban.Kenkre@Sun.COM 			if (ap->attr_values == NULL) {
13058040SBaban.Kenkre@Sun.COM 				ap->num_values = 0;
13068040SBaban.Kenkre@Sun.COM 				goto out;
13078040SBaban.Kenkre@Sun.COM 			}
13088040SBaban.Kenkre@Sun.COM 			for (j = 0; j < ap->num_values; j++) {
13098040SBaban.Kenkre@Sun.COM 				ap->attr_values[j] =
13108040SBaban.Kenkre@Sun.COM 				    binattrs[b].ber2str(bvalues[j]);
13118040SBaban.Kenkre@Sun.COM 				if (ap->attr_values[j] == NULL)
13128040SBaban.Kenkre@Sun.COM 					goto out;
13138040SBaban.Kenkre@Sun.COM 			}
13148040SBaban.Kenkre@Sun.COM 			ldap_value_free_len(bvalues);
13158040SBaban.Kenkre@Sun.COM 			bvalues = NULL;
13168040SBaban.Kenkre@Sun.COM 			continue;
13178040SBaban.Kenkre@Sun.COM 		}
13188040SBaban.Kenkre@Sun.COM 
13198040SBaban.Kenkre@Sun.COM 		strvalues = ldap_get_values(adh->ld, search_res, attr);
13208040SBaban.Kenkre@Sun.COM 		if (strvalues == NULL)
13218040SBaban.Kenkre@Sun.COM 			continue;
13228040SBaban.Kenkre@Sun.COM 		ap->num_values = ldap_count_values(strvalues);
13238040SBaban.Kenkre@Sun.COM 		if (ap->num_values == 0) {
13248040SBaban.Kenkre@Sun.COM 			ldap_value_free(strvalues);
13258040SBaban.Kenkre@Sun.COM 			continue;
13268040SBaban.Kenkre@Sun.COM 		}
13278040SBaban.Kenkre@Sun.COM 		ap->attr_values = strvalues;
13288040SBaban.Kenkre@Sun.COM 	}
13298040SBaban.Kenkre@Sun.COM 
13308040SBaban.Kenkre@Sun.COM 	ret = 0;
13318040SBaban.Kenkre@Sun.COM out:
13328040SBaban.Kenkre@Sun.COM 	ldap_memfree(attr);
13338040SBaban.Kenkre@Sun.COM 	ldap_memfree(dn);
13348040SBaban.Kenkre@Sun.COM 	ber_free(ber, 0);
13358040SBaban.Kenkre@Sun.COM 	ldap_value_free_len(bvalues);
13368040SBaban.Kenkre@Sun.COM 	if (ret < 0)
13378040SBaban.Kenkre@Sun.COM 		free_entry(ep);
13388040SBaban.Kenkre@Sun.COM 	else
13398040SBaban.Kenkre@Sun.COM 		*entry = ep;
13408040SBaban.Kenkre@Sun.COM 	return (ret);
13418040SBaban.Kenkre@Sun.COM }
13428040SBaban.Kenkre@Sun.COM 
13438040SBaban.Kenkre@Sun.COM /*
13448040SBaban.Kenkre@Sun.COM  * Put the search result onto the given adutils_q_t.
13458040SBaban.Kenkre@Sun.COM  * Returns:	  0 success
13468040SBaban.Kenkre@Sun.COM  *		< 0 error
13478040SBaban.Kenkre@Sun.COM  */
13488040SBaban.Kenkre@Sun.COM static
13498040SBaban.Kenkre@Sun.COM int
13508040SBaban.Kenkre@Sun.COM add_entry(adutils_host_t *adh, adutils_q_t *q, LDAPMessage *search_res)
13518040SBaban.Kenkre@Sun.COM {
13528040SBaban.Kenkre@Sun.COM 	int			ret = -1;
13538040SBaban.Kenkre@Sun.COM 	adutils_entry_t		*entry = NULL;
13548040SBaban.Kenkre@Sun.COM 	adutils_result_t	*res;
13558040SBaban.Kenkre@Sun.COM 
13568040SBaban.Kenkre@Sun.COM 	ret = make_entry(q, adh, search_res, &entry);
13578040SBaban.Kenkre@Sun.COM 	if (ret < -1) {
13588040SBaban.Kenkre@Sun.COM 		*q->rc = ADUTILS_ERR_MEMORY;
13598040SBaban.Kenkre@Sun.COM 		goto out;
13608040SBaban.Kenkre@Sun.COM 	} else if (ret == -1) {
13618040SBaban.Kenkre@Sun.COM 		/* ignore result */
13628040SBaban.Kenkre@Sun.COM 		goto out;
13638040SBaban.Kenkre@Sun.COM 	}
13648040SBaban.Kenkre@Sun.COM 	if (*q->result == NULL) {
13658040SBaban.Kenkre@Sun.COM 		res = calloc(1, sizeof (*res));
13668040SBaban.Kenkre@Sun.COM 		if (res == NULL) {
13678040SBaban.Kenkre@Sun.COM 			*q->rc = ADUTILS_ERR_MEMORY;
13688040SBaban.Kenkre@Sun.COM 			goto out;
13698040SBaban.Kenkre@Sun.COM 		}
13708040SBaban.Kenkre@Sun.COM 		res->num_entries = 1;
13718040SBaban.Kenkre@Sun.COM 		res->entries = entry;
13728040SBaban.Kenkre@Sun.COM 		*q->result = res;
13738040SBaban.Kenkre@Sun.COM 	} else {
13748040SBaban.Kenkre@Sun.COM 		res = *q->result;
13758040SBaban.Kenkre@Sun.COM 		entry->next = res->entries;
13768040SBaban.Kenkre@Sun.COM 		res->entries = entry;
13778040SBaban.Kenkre@Sun.COM 		res->num_entries++;
13788040SBaban.Kenkre@Sun.COM 	}
13798040SBaban.Kenkre@Sun.COM 	*q->rc = ADUTILS_SUCCESS;
13808040SBaban.Kenkre@Sun.COM 	entry = NULL;
13818040SBaban.Kenkre@Sun.COM 	ret = 0;
13828040SBaban.Kenkre@Sun.COM 
13838040SBaban.Kenkre@Sun.COM out:
13848040SBaban.Kenkre@Sun.COM 	free_entry(entry);
13858040SBaban.Kenkre@Sun.COM 	return (ret);
13868040SBaban.Kenkre@Sun.COM }
13878040SBaban.Kenkre@Sun.COM 
13888040SBaban.Kenkre@Sun.COM /*
13898040SBaban.Kenkre@Sun.COM  * Try to get a result; if there is one, find the corresponding
13908040SBaban.Kenkre@Sun.COM  * adutils_q_t and process the result.
13918040SBaban.Kenkre@Sun.COM  *
13928040SBaban.Kenkre@Sun.COM  * Returns:	0 success
13938040SBaban.Kenkre@Sun.COM  *		-1 error
13948040SBaban.Kenkre@Sun.COM  */
13958040SBaban.Kenkre@Sun.COM static
13968040SBaban.Kenkre@Sun.COM int
13978040SBaban.Kenkre@Sun.COM get_adobject_batch(adutils_host_t *adh, struct timeval *timeout)
13988040SBaban.Kenkre@Sun.COM {
13998040SBaban.Kenkre@Sun.COM 	adutils_query_state_t	*query_state;
14008040SBaban.Kenkre@Sun.COM 	LDAPMessage		*res = NULL;
14018040SBaban.Kenkre@Sun.COM 	int			rc, ret, msgid, qid;
14028040SBaban.Kenkre@Sun.COM 	adutils_q_t		*que;
14038040SBaban.Kenkre@Sun.COM 	int			num;
14048040SBaban.Kenkre@Sun.COM 
14058040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&adh->lock);
14068040SBaban.Kenkre@Sun.COM 	if (adh->dead || adh->num_requests == 0) {
14078040SBaban.Kenkre@Sun.COM 		ret = (adh->dead) ? -1 : -2;
14088040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_unlock(&adh->lock);
14098040SBaban.Kenkre@Sun.COM 		return (ret);
14108040SBaban.Kenkre@Sun.COM 	}
14118040SBaban.Kenkre@Sun.COM 
14128040SBaban.Kenkre@Sun.COM 	/* Get one result */
14138040SBaban.Kenkre@Sun.COM 	rc = ldap_result(adh->ld, LDAP_RES_ANY, 0, timeout, &res);
14148040SBaban.Kenkre@Sun.COM 	if ((timeout != NULL && timeout->tv_sec > 0 && rc == LDAP_SUCCESS) ||
14158040SBaban.Kenkre@Sun.COM 	    rc < 0)
14168040SBaban.Kenkre@Sun.COM 		adh->dead = 1;
14178040SBaban.Kenkre@Sun.COM 
14188040SBaban.Kenkre@Sun.COM 	if (rc == LDAP_RES_SEARCH_RESULT && adh->num_requests > 0)
14198040SBaban.Kenkre@Sun.COM 		adh->num_requests--;
14208040SBaban.Kenkre@Sun.COM 	if (adh->dead) {
14218040SBaban.Kenkre@Sun.COM 		num = adh->num_requests;
14228040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_unlock(&adh->lock);
14238040SBaban.Kenkre@Sun.COM 		idmapdlog(LOG_DEBUG,
14248040SBaban.Kenkre@Sun.COM 		    "AD ldap_result error - %d queued requests", num);
14258040SBaban.Kenkre@Sun.COM 		return (-1);
14268040SBaban.Kenkre@Sun.COM 	}
14278040SBaban.Kenkre@Sun.COM 
14288040SBaban.Kenkre@Sun.COM 	switch (rc) {
14298040SBaban.Kenkre@Sun.COM 	case LDAP_RES_SEARCH_RESULT:
14308040SBaban.Kenkre@Sun.COM 		msgid = ldap_msgid(res);
14318040SBaban.Kenkre@Sun.COM 		if (msgid2query(adh, msgid, &query_state, &qid)) {
14328040SBaban.Kenkre@Sun.COM 			if (query_state->ldap_res_search_cb != NULL) {
14338040SBaban.Kenkre@Sun.COM 				/*
14348040SBaban.Kenkre@Sun.COM 				 * We use the caller-provided callback
14358040SBaban.Kenkre@Sun.COM 				 * to process the result.
14368040SBaban.Kenkre@Sun.COM 				 */
14378040SBaban.Kenkre@Sun.COM 				query_state->ldap_res_search_cb(
14388040SBaban.Kenkre@Sun.COM 				    adh->ld, &res, rc, qid,
14398040SBaban.Kenkre@Sun.COM 				    query_state->ldap_res_search_argp);
14408040SBaban.Kenkre@Sun.COM 				(void) pthread_mutex_unlock(&adh->lock);
14418040SBaban.Kenkre@Sun.COM 			} else {
14428040SBaban.Kenkre@Sun.COM 				/*
14438040SBaban.Kenkre@Sun.COM 				 * No callback. We fallback to our
14448040SBaban.Kenkre@Sun.COM 				 * default behaviour. All the entries
14458040SBaban.Kenkre@Sun.COM 				 * gotten from this search have been
14468040SBaban.Kenkre@Sun.COM 				 * added to the result list during
14478040SBaban.Kenkre@Sun.COM 				 * LDAP_RES_SEARCH_ENTRY (see below).
14488040SBaban.Kenkre@Sun.COM 				 * Here we set the return status to
14498040SBaban.Kenkre@Sun.COM 				 * notfound if the result is still empty.
14508040SBaban.Kenkre@Sun.COM 				 */
14518040SBaban.Kenkre@Sun.COM 				(void) pthread_mutex_unlock(&adh->lock);
14528040SBaban.Kenkre@Sun.COM 				que = &(query_state->queries[qid]);
14538040SBaban.Kenkre@Sun.COM 				if (*que->result == NULL)
14548040SBaban.Kenkre@Sun.COM 					*que->rc = ADUTILS_ERR_NOTFOUND;
14558040SBaban.Kenkre@Sun.COM 			}
14568040SBaban.Kenkre@Sun.COM 			atomic_dec_32(&query_state->qinflight);
14578040SBaban.Kenkre@Sun.COM 			adutils_lookup_batch_unlock(&query_state);
14588040SBaban.Kenkre@Sun.COM 		} else {
14598040SBaban.Kenkre@Sun.COM 			num = adh->num_requests;
14608040SBaban.Kenkre@Sun.COM 			(void) pthread_mutex_unlock(&adh->lock);
14618040SBaban.Kenkre@Sun.COM 			idmapdlog(LOG_DEBUG,
14628040SBaban.Kenkre@Sun.COM 			    "AD cannot find message ID (%d) "
14638040SBaban.Kenkre@Sun.COM 			    "- %d queued requests",
14648040SBaban.Kenkre@Sun.COM 			    msgid, num);
14658040SBaban.Kenkre@Sun.COM 		}
14668040SBaban.Kenkre@Sun.COM 		(void) ldap_msgfree(res);
14678040SBaban.Kenkre@Sun.COM 		ret = 0;
14688040SBaban.Kenkre@Sun.COM 		break;
14698040SBaban.Kenkre@Sun.COM 
14708040SBaban.Kenkre@Sun.COM 	case LDAP_RES_SEARCH_ENTRY:
14718040SBaban.Kenkre@Sun.COM 		msgid = ldap_msgid(res);
14728040SBaban.Kenkre@Sun.COM 		if (msgid2query(adh, msgid, &query_state, &qid)) {
14738040SBaban.Kenkre@Sun.COM 			if (query_state->ldap_res_search_cb != NULL) {
14748040SBaban.Kenkre@Sun.COM 				/*
14758040SBaban.Kenkre@Sun.COM 				 * We use the caller-provided callback
14768040SBaban.Kenkre@Sun.COM 				 * to process the entry.
14778040SBaban.Kenkre@Sun.COM 				 */
14788040SBaban.Kenkre@Sun.COM 				query_state->ldap_res_search_cb(
14798040SBaban.Kenkre@Sun.COM 				    adh->ld, &res, rc, qid,
14808040SBaban.Kenkre@Sun.COM 				    query_state->ldap_res_search_argp);
14818040SBaban.Kenkre@Sun.COM 				(void) pthread_mutex_unlock(&adh->lock);
14828040SBaban.Kenkre@Sun.COM 			} else {
14838040SBaban.Kenkre@Sun.COM 				/*
14848040SBaban.Kenkre@Sun.COM 				 * No callback. We fallback to our
14858040SBaban.Kenkre@Sun.COM 				 * default behaviour. This entry
14868040SBaban.Kenkre@Sun.COM 				 * will be added to the result list.
14878040SBaban.Kenkre@Sun.COM 				 */
14888040SBaban.Kenkre@Sun.COM 				que = &(query_state->queries[qid]);
14898040SBaban.Kenkre@Sun.COM 				rc = add_entry(adh, que, res);
14908040SBaban.Kenkre@Sun.COM 				(void) pthread_mutex_unlock(&adh->lock);
14918040SBaban.Kenkre@Sun.COM 				if (rc < 0) {
14928040SBaban.Kenkre@Sun.COM 					idmapdlog(LOG_DEBUG,
14938040SBaban.Kenkre@Sun.COM 					    "Failed to queue entry by "
14948040SBaban.Kenkre@Sun.COM 					    "message ID (%d) "
14958040SBaban.Kenkre@Sun.COM 					    "- %d queued requests",
14968040SBaban.Kenkre@Sun.COM 					    msgid, num);
14978040SBaban.Kenkre@Sun.COM 				}
14988040SBaban.Kenkre@Sun.COM 			}
14998040SBaban.Kenkre@Sun.COM 			adutils_lookup_batch_unlock(&query_state);
15008040SBaban.Kenkre@Sun.COM 		} else {
15018040SBaban.Kenkre@Sun.COM 			num = adh->num_requests;
15028040SBaban.Kenkre@Sun.COM 			(void) pthread_mutex_unlock(&adh->lock);
15038040SBaban.Kenkre@Sun.COM 			idmapdlog(LOG_DEBUG,
15048040SBaban.Kenkre@Sun.COM 			    "AD cannot find message ID (%d) "
15058040SBaban.Kenkre@Sun.COM 			    "- %d queued requests",
15068040SBaban.Kenkre@Sun.COM 			    msgid, num);
15078040SBaban.Kenkre@Sun.COM 		}
15088040SBaban.Kenkre@Sun.COM 		(void) ldap_msgfree(res);
15098040SBaban.Kenkre@Sun.COM 		ret = 0;
15108040SBaban.Kenkre@Sun.COM 		break;
15118040SBaban.Kenkre@Sun.COM 
15128040SBaban.Kenkre@Sun.COM 	case LDAP_RES_SEARCH_REFERENCE:
15138040SBaban.Kenkre@Sun.COM 		/*
15148040SBaban.Kenkre@Sun.COM 		 * We have no need for these at the moment.  Eventually,
15158040SBaban.Kenkre@Sun.COM 		 * when we query things that we can't expect to find in
15168040SBaban.Kenkre@Sun.COM 		 * the Global Catalog then we'll need to learn to follow
15178040SBaban.Kenkre@Sun.COM 		 * references.
15188040SBaban.Kenkre@Sun.COM 		 */
15198040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_unlock(&adh->lock);
15208040SBaban.Kenkre@Sun.COM 		(void) ldap_msgfree(res);
15218040SBaban.Kenkre@Sun.COM 		ret = 0;
15228040SBaban.Kenkre@Sun.COM 		break;
15238040SBaban.Kenkre@Sun.COM 
15248040SBaban.Kenkre@Sun.COM 	default:
15258040SBaban.Kenkre@Sun.COM 		/* timeout or error; treat the same */
15268040SBaban.Kenkre@Sun.COM 		(void) pthread_mutex_unlock(&adh->lock);
15278040SBaban.Kenkre@Sun.COM 		ret = -1;
15288040SBaban.Kenkre@Sun.COM 		break;
15298040SBaban.Kenkre@Sun.COM 	}
15308040SBaban.Kenkre@Sun.COM 
15318040SBaban.Kenkre@Sun.COM 	return (ret);
15328040SBaban.Kenkre@Sun.COM }
15338040SBaban.Kenkre@Sun.COM 
15348040SBaban.Kenkre@Sun.COM /*
15358040SBaban.Kenkre@Sun.COM  * This routine decreament the reference count of the
15368040SBaban.Kenkre@Sun.COM  * adutils_query_state_t
15378040SBaban.Kenkre@Sun.COM  */
15388040SBaban.Kenkre@Sun.COM static void
15398040SBaban.Kenkre@Sun.COM adutils_lookup_batch_unlock(adutils_query_state_t **state)
15408040SBaban.Kenkre@Sun.COM {
15418040SBaban.Kenkre@Sun.COM 	/*
15428040SBaban.Kenkre@Sun.COM 	 * Decrement reference count with qstatelock locked
15438040SBaban.Kenkre@Sun.COM 	 */
15448040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&qstatelock);
15458040SBaban.Kenkre@Sun.COM 	(*state)->ref_cnt--;
15468040SBaban.Kenkre@Sun.COM 	/*
15478040SBaban.Kenkre@Sun.COM 	 * If there are no references wakup the allocating thread
15488040SBaban.Kenkre@Sun.COM 	 */
15498040SBaban.Kenkre@Sun.COM 	if ((*state)->ref_cnt <= 1)
15508040SBaban.Kenkre@Sun.COM 		(void) pthread_cond_signal(&(*state)->cv);
15518040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&qstatelock);
15528040SBaban.Kenkre@Sun.COM 	*state = NULL;
15538040SBaban.Kenkre@Sun.COM }
15548040SBaban.Kenkre@Sun.COM 
15558040SBaban.Kenkre@Sun.COM /*
15568040SBaban.Kenkre@Sun.COM  * This routine frees the adutils_query_state_t structure
15578040SBaban.Kenkre@Sun.COM  * If the reference count is greater than 1 it waits
15588040SBaban.Kenkre@Sun.COM  * for the other threads to finish using it.
15598040SBaban.Kenkre@Sun.COM  */
15608040SBaban.Kenkre@Sun.COM void
15618040SBaban.Kenkre@Sun.COM adutils_lookup_batch_release(adutils_query_state_t **state)
15628040SBaban.Kenkre@Sun.COM {
15638040SBaban.Kenkre@Sun.COM 	adutils_query_state_t **p;
15648040SBaban.Kenkre@Sun.COM 	int			i;
15658040SBaban.Kenkre@Sun.COM 
15668040SBaban.Kenkre@Sun.COM 	if (state == NULL || *state == NULL)
15678040SBaban.Kenkre@Sun.COM 		return;
15688040SBaban.Kenkre@Sun.COM 
15698040SBaban.Kenkre@Sun.COM 	/*
15708040SBaban.Kenkre@Sun.COM 	 * Set state to dead to stop further operations.
15718040SBaban.Kenkre@Sun.COM 	 * Wait for reference count with qstatelock locked
15728040SBaban.Kenkre@Sun.COM 	 * to get to one.
15738040SBaban.Kenkre@Sun.COM 	 */
15748040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&qstatelock);
15758040SBaban.Kenkre@Sun.COM 	(*state)->qdead = 1;
15768040SBaban.Kenkre@Sun.COM 	while ((*state)->ref_cnt > 1) {
15778040SBaban.Kenkre@Sun.COM 		(void) pthread_cond_wait(&(*state)->cv, &qstatelock);
15788040SBaban.Kenkre@Sun.COM 	}
15798040SBaban.Kenkre@Sun.COM 
15808040SBaban.Kenkre@Sun.COM 	/* Remove this state struct from the list of state structs */
15818040SBaban.Kenkre@Sun.COM 	for (p = &qstatehead; *p != NULL; p = &(*p)->next) {
15828040SBaban.Kenkre@Sun.COM 		if (*p == (*state)) {
15838040SBaban.Kenkre@Sun.COM 			*p = (*state)->next;
15848040SBaban.Kenkre@Sun.COM 			break;
15858040SBaban.Kenkre@Sun.COM 		}
15868040SBaban.Kenkre@Sun.COM 	}
15878040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&qstatelock);
15888040SBaban.Kenkre@Sun.COM 	(void) pthread_cond_destroy(&(*state)->cv);
15898040SBaban.Kenkre@Sun.COM 	release_conn((*state)->qadh);
15908040SBaban.Kenkre@Sun.COM 
15918040SBaban.Kenkre@Sun.COM 	/* Clear results for queries that failed */
15928040SBaban.Kenkre@Sun.COM 	for (i = 0; i < (*state)->qcount; i++) {
15938040SBaban.Kenkre@Sun.COM 		if (*(*state)->queries[i].rc != ADUTILS_SUCCESS) {
15948040SBaban.Kenkre@Sun.COM 			adutils_freeresult((*state)->queries[i].result);
15958040SBaban.Kenkre@Sun.COM 		}
15968040SBaban.Kenkre@Sun.COM 	}
15978040SBaban.Kenkre@Sun.COM 	free((*state)->default_domain);
15988040SBaban.Kenkre@Sun.COM 	free((*state)->basedn);
15998040SBaban.Kenkre@Sun.COM 	free(*state);
16008040SBaban.Kenkre@Sun.COM 	*state = NULL;
16018040SBaban.Kenkre@Sun.COM }
16028040SBaban.Kenkre@Sun.COM 
16038040SBaban.Kenkre@Sun.COM 
16048040SBaban.Kenkre@Sun.COM /*
16058040SBaban.Kenkre@Sun.COM  * This routine waits for other threads using the
16068040SBaban.Kenkre@Sun.COM  * adutils_query_state_t structure to finish.
16078040SBaban.Kenkre@Sun.COM  * If the reference count is greater than 1 it waits
16088040SBaban.Kenkre@Sun.COM  * for the other threads to finish using it.
16098040SBaban.Kenkre@Sun.COM  */
16108040SBaban.Kenkre@Sun.COM static
16118040SBaban.Kenkre@Sun.COM void
16128040SBaban.Kenkre@Sun.COM adutils_lookup_batch_wait(adutils_query_state_t *state)
16138040SBaban.Kenkre@Sun.COM {
16148040SBaban.Kenkre@Sun.COM 	/*
16158040SBaban.Kenkre@Sun.COM 	 * Set state to dead to stop further operation.
16168040SBaban.Kenkre@Sun.COM 	 * stating.
16178040SBaban.Kenkre@Sun.COM 	 * Wait for reference count to get to one
16188040SBaban.Kenkre@Sun.COM 	 * with qstatelock locked.
16198040SBaban.Kenkre@Sun.COM 	 */
16208040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&qstatelock);
16218040SBaban.Kenkre@Sun.COM 	state->qdead = 1;
16228040SBaban.Kenkre@Sun.COM 	while (state->ref_cnt > 1) {
16238040SBaban.Kenkre@Sun.COM 		(void) pthread_cond_wait(&state->cv, &qstatelock);
16248040SBaban.Kenkre@Sun.COM 	}
16258040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&qstatelock);
16268040SBaban.Kenkre@Sun.COM }
16278040SBaban.Kenkre@Sun.COM 
16288040SBaban.Kenkre@Sun.COM /*
16298040SBaban.Kenkre@Sun.COM  * Process active queries in the AD lookup batch and then finalize the
16308040SBaban.Kenkre@Sun.COM  * result.
16318040SBaban.Kenkre@Sun.COM  */
16328040SBaban.Kenkre@Sun.COM adutils_rc
16338040SBaban.Kenkre@Sun.COM adutils_lookup_batch_end(adutils_query_state_t **state)
16348040SBaban.Kenkre@Sun.COM {
16358040SBaban.Kenkre@Sun.COM 	int		    rc = LDAP_SUCCESS;
16368040SBaban.Kenkre@Sun.COM 	adutils_rc	    ad_rc = ADUTILS_SUCCESS;
16378040SBaban.Kenkre@Sun.COM 	struct timeval	    tv;
16388040SBaban.Kenkre@Sun.COM 
16398040SBaban.Kenkre@Sun.COM 	tv.tv_sec = ADUTILS_SEARCH_TIMEOUT;
16408040SBaban.Kenkre@Sun.COM 	tv.tv_usec = 0;
16418040SBaban.Kenkre@Sun.COM 
16428040SBaban.Kenkre@Sun.COM 	/* Process results until done or until timeout, if given */
16438040SBaban.Kenkre@Sun.COM 	while ((*state)->qinflight > 0) {
16448040SBaban.Kenkre@Sun.COM 		if ((rc = get_adobject_batch((*state)->qadh,
16458040SBaban.Kenkre@Sun.COM 		    &tv)) != 0)
16468040SBaban.Kenkre@Sun.COM 			break;
16478040SBaban.Kenkre@Sun.COM 	}
16488040SBaban.Kenkre@Sun.COM 	(*state)->qdead = 1;
16498040SBaban.Kenkre@Sun.COM 	/* Wait for other threads processing search result to finish */
16508040SBaban.Kenkre@Sun.COM 	adutils_lookup_batch_wait(*state);
16518040SBaban.Kenkre@Sun.COM 	if (rc == -1 || (*state)->qinflight != 0)
16528040SBaban.Kenkre@Sun.COM 		ad_rc = ADUTILS_ERR_RETRIABLE_NET_ERR;
16538040SBaban.Kenkre@Sun.COM 	adutils_lookup_batch_release(state);
16548040SBaban.Kenkre@Sun.COM 	return (ad_rc);
16558040SBaban.Kenkre@Sun.COM }
16568040SBaban.Kenkre@Sun.COM 
16578040SBaban.Kenkre@Sun.COM const char *
16588040SBaban.Kenkre@Sun.COM adutils_lookup_batch_getdefdomain(adutils_query_state_t *state)
16598040SBaban.Kenkre@Sun.COM {
16608040SBaban.Kenkre@Sun.COM 	return (state->default_domain);
16618040SBaban.Kenkre@Sun.COM }
16628040SBaban.Kenkre@Sun.COM 
16638040SBaban.Kenkre@Sun.COM /*
16648040SBaban.Kenkre@Sun.COM  * Send one prepared search, queue up msgid, process what results are
16658040SBaban.Kenkre@Sun.COM  * available
16668040SBaban.Kenkre@Sun.COM  */
16678040SBaban.Kenkre@Sun.COM adutils_rc
16688040SBaban.Kenkre@Sun.COM adutils_lookup_batch_add(adutils_query_state_t *state,
16698040SBaban.Kenkre@Sun.COM 	const char *filter, const char **attrs, const char *edomain,
16708040SBaban.Kenkre@Sun.COM 	adutils_result_t **result, adutils_rc *rc)
16718040SBaban.Kenkre@Sun.COM {
16728040SBaban.Kenkre@Sun.COM 	adutils_rc	retcode = ADUTILS_SUCCESS;
16738040SBaban.Kenkre@Sun.COM 	int		lrc, qid;
16748040SBaban.Kenkre@Sun.COM 	int		num;
16758040SBaban.Kenkre@Sun.COM 	int		dead;
16768040SBaban.Kenkre@Sun.COM 	struct timeval	tv;
16778040SBaban.Kenkre@Sun.COM 	adutils_q_t	*q;
16788040SBaban.Kenkre@Sun.COM 
1679*8361SJulian.Pullen@Sun.COM 	qid = atomic_inc_32_nv(&state->qcount) - 1;
16808040SBaban.Kenkre@Sun.COM 	q = &(state->queries[qid]);
16818040SBaban.Kenkre@Sun.COM 
1682*8361SJulian.Pullen@Sun.COM 	assert(qid < state->qsize);
1683*8361SJulian.Pullen@Sun.COM 
16848040SBaban.Kenkre@Sun.COM 	/*
16858040SBaban.Kenkre@Sun.COM 	 * Remember the expected domain so we can check the results
16868040SBaban.Kenkre@Sun.COM 	 * against it
16878040SBaban.Kenkre@Sun.COM 	 */
16888040SBaban.Kenkre@Sun.COM 	q->edomain = edomain;
16898040SBaban.Kenkre@Sun.COM 
16908040SBaban.Kenkre@Sun.COM 	/* Remember where to put the results */
16918040SBaban.Kenkre@Sun.COM 	q->result = result;
16928040SBaban.Kenkre@Sun.COM 	q->rc = rc;
16938040SBaban.Kenkre@Sun.COM 
16948040SBaban.Kenkre@Sun.COM 	/*
16958040SBaban.Kenkre@Sun.COM 	 * Provide sane defaults for the results in case we never hear
16968040SBaban.Kenkre@Sun.COM 	 * back from the DS before closing the connection.
16978040SBaban.Kenkre@Sun.COM 	 */
16988040SBaban.Kenkre@Sun.COM 	*rc = ADUTILS_ERR_RETRIABLE_NET_ERR;
16998040SBaban.Kenkre@Sun.COM 	if (result != NULL)
17008040SBaban.Kenkre@Sun.COM 		*result = NULL;
17018040SBaban.Kenkre@Sun.COM 
17028040SBaban.Kenkre@Sun.COM 	/* Check the number of queued requests first */
17038040SBaban.Kenkre@Sun.COM 	tv.tv_sec = ADUTILS_SEARCH_TIMEOUT;
17048040SBaban.Kenkre@Sun.COM 	tv.tv_usec = 0;
17058040SBaban.Kenkre@Sun.COM 	while (!state->qadh->dead &&
17068040SBaban.Kenkre@Sun.COM 	    state->qadh->num_requests > state->qadh->max_requests) {
17078040SBaban.Kenkre@Sun.COM 		if (get_adobject_batch(state->qadh, &tv) != 0)
17088040SBaban.Kenkre@Sun.COM 			break;
17098040SBaban.Kenkre@Sun.COM 	}
17108040SBaban.Kenkre@Sun.COM 
17118040SBaban.Kenkre@Sun.COM 	/* Send this lookup, don't wait for a result here */
17128040SBaban.Kenkre@Sun.COM 	lrc = LDAP_SUCCESS;
17138040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_lock(&state->qadh->lock);
17148040SBaban.Kenkre@Sun.COM 
17158040SBaban.Kenkre@Sun.COM 	if (!state->qadh->dead) {
17168040SBaban.Kenkre@Sun.COM 		state->qadh->idletime = time(NULL);
17178040SBaban.Kenkre@Sun.COM 		lrc = ldap_search_ext(state->qadh->ld, state->basedn,
17188040SBaban.Kenkre@Sun.COM 		    LDAP_SCOPE_SUBTREE, filter, (char **)attrs,
17198040SBaban.Kenkre@Sun.COM 		    0, NULL, NULL, NULL, -1, &q->msgid);
17208040SBaban.Kenkre@Sun.COM 
17218040SBaban.Kenkre@Sun.COM 		if (lrc == LDAP_SUCCESS) {
17228040SBaban.Kenkre@Sun.COM 			state->qadh->num_requests++;
17238040SBaban.Kenkre@Sun.COM 		} else if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE ||
17248040SBaban.Kenkre@Sun.COM 		    lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN ||
17258040SBaban.Kenkre@Sun.COM 		    lrc == LDAP_UNWILLING_TO_PERFORM) {
17268040SBaban.Kenkre@Sun.COM 			retcode = ADUTILS_ERR_RETRIABLE_NET_ERR;
17278040SBaban.Kenkre@Sun.COM 			state->qadh->dead = 1;
17288040SBaban.Kenkre@Sun.COM 		} else {
17298040SBaban.Kenkre@Sun.COM 			retcode = ADUTILS_ERR_OTHER;
17308040SBaban.Kenkre@Sun.COM 			state->qadh->dead = 1;
17318040SBaban.Kenkre@Sun.COM 		}
17328040SBaban.Kenkre@Sun.COM 	}
17338040SBaban.Kenkre@Sun.COM 	dead = state->qadh->dead;
17348040SBaban.Kenkre@Sun.COM 	num = state->qadh->num_requests;
17358040SBaban.Kenkre@Sun.COM 	(void) pthread_mutex_unlock(&state->qadh->lock);
17368040SBaban.Kenkre@Sun.COM 
17378040SBaban.Kenkre@Sun.COM 	if (dead) {
17388040SBaban.Kenkre@Sun.COM 		if (lrc != LDAP_SUCCESS)
17398040SBaban.Kenkre@Sun.COM 			idmapdlog(LOG_DEBUG,
17408040SBaban.Kenkre@Sun.COM 			    "AD ldap_search_ext error (%s) "
17418040SBaban.Kenkre@Sun.COM 			    "- %d queued requests",
17428040SBaban.Kenkre@Sun.COM 			    ldap_err2string(lrc), num);
17438040SBaban.Kenkre@Sun.COM 		return (retcode);
17448040SBaban.Kenkre@Sun.COM 	}
17458040SBaban.Kenkre@Sun.COM 
17468040SBaban.Kenkre@Sun.COM 	atomic_inc_32(&state->qinflight);
17478040SBaban.Kenkre@Sun.COM 
17488040SBaban.Kenkre@Sun.COM 	/*
17498040SBaban.Kenkre@Sun.COM 	 * Reap as many requests as we can _without_ waiting to prevent
17508040SBaban.Kenkre@Sun.COM 	 * any possible TCP socket buffer starvation deadlocks.
17518040SBaban.Kenkre@Sun.COM 	 */
17528040SBaban.Kenkre@Sun.COM 	(void) memset(&tv, 0, sizeof (tv));
17538040SBaban.Kenkre@Sun.COM 	while (get_adobject_batch(state->qadh, &tv) == 0)
17548040SBaban.Kenkre@Sun.COM 		;
17558040SBaban.Kenkre@Sun.COM 
17568040SBaban.Kenkre@Sun.COM 	return (ADUTILS_SUCCESS);
17578040SBaban.Kenkre@Sun.COM }
17588040SBaban.Kenkre@Sun.COM 
17598040SBaban.Kenkre@Sun.COM /*
17608040SBaban.Kenkre@Sun.COM  * Single AD lookup request implemented on top of the batch API.
17618040SBaban.Kenkre@Sun.COM  */
17628040SBaban.Kenkre@Sun.COM adutils_rc
17638040SBaban.Kenkre@Sun.COM adutils_lookup(adutils_ad_t *ad, const char *filter, const char **attrs,
17648040SBaban.Kenkre@Sun.COM 		const char *domain, adutils_result_t **result)
17658040SBaban.Kenkre@Sun.COM {
17668040SBaban.Kenkre@Sun.COM 	adutils_rc		rc, brc;
17678040SBaban.Kenkre@Sun.COM 	adutils_query_state_t	*qs;
17688040SBaban.Kenkre@Sun.COM 
17698040SBaban.Kenkre@Sun.COM 	rc = adutils_lookup_batch_start(ad, 1, NULL, NULL, &qs);
17708040SBaban.Kenkre@Sun.COM 	if (rc != ADUTILS_SUCCESS)
17718040SBaban.Kenkre@Sun.COM 		return (rc);
17728040SBaban.Kenkre@Sun.COM 
17738040SBaban.Kenkre@Sun.COM 	rc = adutils_lookup_batch_add(qs, filter, attrs, domain, result, &brc);
17748040SBaban.Kenkre@Sun.COM 	if (rc != ADUTILS_SUCCESS) {
17758040SBaban.Kenkre@Sun.COM 		adutils_lookup_batch_release(&qs);
17768040SBaban.Kenkre@Sun.COM 		return (rc);
17778040SBaban.Kenkre@Sun.COM 	}
17788040SBaban.Kenkre@Sun.COM 
17798040SBaban.Kenkre@Sun.COM 	rc = adutils_lookup_batch_end(&qs);
17808040SBaban.Kenkre@Sun.COM 	if (rc != ADUTILS_SUCCESS)
17818040SBaban.Kenkre@Sun.COM 		return (rc);
17828040SBaban.Kenkre@Sun.COM 	return (brc);
17838040SBaban.Kenkre@Sun.COM }
1784