14520Snw141292 /*
24520Snw141292  * CDDL HEADER START
34520Snw141292  *
44520Snw141292  * The contents of this file are subject to the terms of the
54520Snw141292  * Common Development and Distribution License (the "License").
64520Snw141292  * You may not use this file except in compliance with the License.
74520Snw141292  *
84520Snw141292  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94520Snw141292  * or http://www.opensolaris.org/os/licensing.
104520Snw141292  * See the License for the specific language governing permissions
114520Snw141292  * and limitations under the License.
124520Snw141292  *
134520Snw141292  * When distributing Covered Code, include this CDDL HEADER in each
144520Snw141292  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154520Snw141292  * If applicable, add the following below this CDDL HEADER, with the
164520Snw141292  * fields enclosed by brackets "[]" replaced with your own identifying
174520Snw141292  * information: Portions Copyright [yyyy] [name of copyright owner]
184520Snw141292  *
194520Snw141292  * CDDL HEADER END
204520Snw141292  */
214520Snw141292 
224520Snw141292 /*
234520Snw141292  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
244520Snw141292  * Use is subject to license terms.
254520Snw141292  */
264520Snw141292 
274520Snw141292 #pragma ident	"%Z%%M%	%I%	%E% SMI"
284520Snw141292 
294520Snw141292 /*
304520Snw141292  * Processes name2sid & sid2name batched lookups for a given user or
314520Snw141292  * computer from an AD Directory server using GSSAPI authentication
324520Snw141292  */
334520Snw141292 
344520Snw141292 #include <stdio.h>
354520Snw141292 #include <stdlib.h>
364520Snw141292 #include <alloca.h>
374520Snw141292 #include <string.h>
384520Snw141292 #include <strings.h>
394520Snw141292 #include <lber.h>
404520Snw141292 #include <ldap.h>
414520Snw141292 #include <sasl/sasl.h>
424520Snw141292 #include <string.h>
434520Snw141292 #include <ctype.h>
444520Snw141292 #include <pthread.h>
454520Snw141292 #include <synch.h>
464520Snw141292 #include <atomic.h>
474520Snw141292 #include <errno.h>
484520Snw141292 #include <assert.h>
494520Snw141292 #include <limits.h>
504520Snw141292 #include "idmapd.h"
514520Snw141292 
524520Snw141292 /*
534520Snw141292  * Internal data structures for this code
544520Snw141292  */
554520Snw141292 
564520Snw141292 /* Attribute names and filter format strings */
574520Snw141292 #define	OBJECTSID	"objectSid"
584520Snw141292 #define	OBJECTSIDFILTER	"(objectSid=%s)"
594520Snw141292 #define	SAMACCOUNTNAME	"sAMAccountName"
604520Snw141292 #define	SANFILTER	"(sAMAccountName=%.*s)"
614520Snw141292 #define	OBJECTCLASS	"objectClass"
624520Snw141292 
634520Snw141292 /*
644520Snw141292  * This should really be in some <sys/sid.h> file or so; we have a
654520Snw141292  * private version of sid_t, and so must other components of ON until we
664520Snw141292  * rationalize this.
674520Snw141292  */
684520Snw141292 typedef struct sid {
694520Snw141292 	uchar_t		version;
704520Snw141292 	uchar_t		sub_authority_count;
714520Snw141292 	uint64_t	authority;  /* really, 48-bits */
724520Snw141292 	rid_t		sub_authorities[SID_MAX_SUB_AUTHORITIES];
734520Snw141292 } sid_t;
744520Snw141292 
754520Snw141292 /* A single DS */
764520Snw141292 typedef struct ad_host {
774520Snw141292 	struct ad_host		*next;
784520Snw141292 	ad_t			*owner;		/* ad_t to which this belongs */
794520Snw141292 	pthread_mutex_t		lock;
804520Snw141292 	LDAP			*ld;		/* LDAP connection */
814520Snw141292 	uint32_t		ref;		/* ref count */
824520Snw141292 	time_t			idletime;	/* time since last activity */
834520Snw141292 	int			dead;		/* error on LDAP connection */
844520Snw141292 	/*
854520Snw141292 	 * Used to distinguish between different instances of LDAP
864520Snw141292 	 * connections to this same DS.  We need this so we never mix up
874520Snw141292 	 * results for a given msgID from one connection with those of
884520Snw141292 	 * another earlier connection where two batch state structures
894520Snw141292 	 * share this ad_host object but used different LDAP connections
904520Snw141292 	 * to send their LDAP searches.
914520Snw141292 	 */
924520Snw141292 	uint64_t		generation;
934520Snw141292 
944520Snw141292 	/* LDAP DS info */
954520Snw141292 	char			*host;
964520Snw141292 	int			port;
974520Snw141292 
984520Snw141292 	/* hardwired to SASL GSSAPI only for now */
994520Snw141292 	char			*saslmech;
1004520Snw141292 	unsigned		saslflags;
1014520Snw141292 } ad_host_t;
1024520Snw141292 
1034520Snw141292 /* A set of DSs for a given AD partition; ad_t typedef comes from  adutil.h */
1044520Snw141292 struct ad {
1054520Snw141292 	char			*dflt_w2k_dom;	/* used to qualify bare names */
1064520Snw141292 	char			*basedn;	/* derived from dflt domain */
1074520Snw141292 	pthread_mutex_t		lock;
1084520Snw141292 	uint32_t		ref;
1094520Snw141292 	idmap_ad_partition_t	partition;	/* Data or global catalog? */
1104520Snw141292 };
1114520Snw141292 
1124520Snw141292 /*
1134520Snw141292  * A place to put the results of a batched (async) query
1144520Snw141292  *
1154520Snw141292  * There is one of these for every query added to a batch object
1164520Snw141292  * (idmap_query_state, see below).
1174520Snw141292  */
1184520Snw141292 typedef struct idmap_q {
1194520Snw141292 	char			**result;	/* name or stringified SID */
1204520Snw141292 	char			**domain;	/* name of domain of object */
1214520Snw141292 	rid_t			*rid;		/* for n2s, if not NULL */
1224520Snw141292 	int			*sid_type;	/* if not NULL */
1234520Snw141292 	idmap_retcode		*rc;
1244520Snw141292 	int			msgid;		/* LDAP message ID */
1254520Snw141292 	/*
1264520Snw141292 	 * Bitfield containing state needed to know when we're done
1274520Snw141292 	 * processing search results related to this query's LDAP
1284520Snw141292 	 * searches.  Mostly self-explanatory.
1294520Snw141292 	 */
1304520Snw141292 	uint16_t		n2s : 1;	/* name->SID or SID->name? */
1314520Snw141292 	uint16_t		got_reply : 1;
1324520Snw141292 	uint16_t		got_results : 1;
1334520Snw141292 	uint16_t		got_objectSid : 1;
1344520Snw141292 	uint16_t		got_objectClass : 1;
1354520Snw141292 	uint16_t		got_samAcctName : 1;
1364520Snw141292 } idmap_q_t;
1374520Snw141292 
1384520Snw141292 /* Batch context structure; typedef is in header file */
1394520Snw141292 struct idmap_query_state {
1404520Snw141292 	idmap_query_state_t	*next;
1414520Snw141292 	int			qcount;		/* how many queries */
1424520Snw141292 	uint32_t		qlastsent;
1434520Snw141292 	uint32_t		qinflight;	/* how many queries in flight */
1444520Snw141292 	uint16_t		qdead;		/* oops, lost LDAP connection */
1454520Snw141292 	ad_host_t		*qadh;		/* LDAP connection */
1464520Snw141292 	uint64_t		qadh_gen;	/* same as qadh->generation */
1474520Snw141292 	idmap_q_t		queries[1];	/* array of query results */
1484520Snw141292 };
1494520Snw141292 
1504520Snw141292 /*
1514520Snw141292  * List of query state structs -- needed so we can "route" LDAP results
1524520Snw141292  * to the right context if multiple threads should be using the same
1534520Snw141292  * connection concurrently
1544520Snw141292  */
1554520Snw141292 static idmap_query_state_t	*qstatehead = NULL;
1564520Snw141292 static pthread_mutex_t		qstatelock = PTHREAD_MUTEX_INITIALIZER;
1574520Snw141292 
1584520Snw141292 /*
1594520Snw141292  * List of DSs, needed by the idle connection reaper thread
1604520Snw141292  */
1614520Snw141292 static ad_host_t	*host_head = NULL;
162*4644Sbaban static pthread_t	reaperid = 0;
1634520Snw141292 static pthread_mutex_t	adhostlock = PTHREAD_MUTEX_INITIALIZER;
1644520Snw141292 
1654520Snw141292 /*ARGSUSED*/
1664520Snw141292 static int
1674520Snw141292 idmap_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts) {
1684520Snw141292 	sasl_interact_t	*interact;
1694520Snw141292 
1704520Snw141292 	if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE)
1714520Snw141292 		return (LDAP_PARAM_ERROR);
1724520Snw141292 
1734520Snw141292 	/* There should be no extra arguemnts for SASL/GSSAPI authentication */
1744520Snw141292 	for (interact = prompts; interact->id != SASL_CB_LIST_END;
1754520Snw141292 			interact++) {
1764520Snw141292 		interact->result = NULL;
1774520Snw141292 		interact->len = 0;
1784520Snw141292 	}
1794520Snw141292 	return (LDAP_SUCCESS);
1804520Snw141292 }
1814520Snw141292 
1824520Snw141292 /* Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com" */
1834520Snw141292 static
1844520Snw141292 char *
1854520Snw141292 dns2dn(const char *dns)
1864520Snw141292 {
1874520Snw141292 	int nameparts;
1884520Snw141292 
1894520Snw141292 	/* Sigh, ldap_dns_to_dn()'s first arg should be a const char * */
1904520Snw141292 	return (ldap_dns_to_dn((char *)dns, &nameparts));
1914520Snw141292 }
1924520Snw141292 
1934520Snw141292 /*
1944520Snw141292  * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other
1954520Snw141292  * attributes (CN, etc...)
1964520Snw141292  */
1974520Snw141292 static
1984520Snw141292 char *
1994520Snw141292 dn2dns(const char *dn)
2004520Snw141292 {
2014520Snw141292 	char **rdns = NULL;
2024520Snw141292 	char **attrs = NULL;
2034520Snw141292 	char **labels = NULL;
2044520Snw141292 	char *dns = NULL;
2054520Snw141292 	char **rdn, **attr, **label;
2064520Snw141292 	int maxlabels = 5;
2074520Snw141292 	int nlabels = 0;
2084520Snw141292 	int dnslen;
2094520Snw141292 
2104520Snw141292 	/*
2114520Snw141292 	 * There is no reverse of ldap_dns_to_dn() in our libldap, so we
2124520Snw141292 	 * have to do the hard work here for now.
2134520Snw141292 	 */
2144520Snw141292 
2154520Snw141292 	/*
2164520Snw141292 	 * This code is much too liberal: it looks for "dc" attributes
2174520Snw141292 	 * in all RDNs of the DN.  In theory this could cause problems
2184520Snw141292 	 * if people were to use "dc" in nodes other than the root of
2194520Snw141292 	 * the tree, but in practice noone, least of all Active
2204520Snw141292 	 * Directory, does that.
2214520Snw141292 	 *
2224520Snw141292 	 * On the other hand, this code is much too conservative: it
2234520Snw141292 	 * does not make assumptions about ldap_explode_dn(), and _that_
2244520Snw141292 	 * is the true for looking at every attr of every RDN.
2254520Snw141292 	 *
2264520Snw141292 	 * Since we only ever look at dc and those must be DNS labels,
2274520Snw141292 	 * at least until we get around to supporting IDN here we
2284520Snw141292 	 * shouldn't see escaped labels from AD nor from libldap, though
2294520Snw141292 	 * the spec (RFC2253) does allow libldap to escape things that
2304520Snw141292 	 * don't need escaping -- if that should ever happen then
2314520Snw141292 	 * libldap will need a spanking, and we can take care of that.
2324520Snw141292 	 */
2334520Snw141292 
2344520Snw141292 	/* Explode a DN into RDNs */
2354520Snw141292 	if ((rdns = ldap_explode_dn(dn, 0)) == NULL)
2364520Snw141292 		return (NULL);
2374520Snw141292 
2384520Snw141292 	labels = calloc(maxlabels + 1, sizeof (char *));
2394520Snw141292 	label = labels;
2404520Snw141292 
2414520Snw141292 	for (rdn = rdns; *rdn != NULL; rdn++) {
2424520Snw141292 		if (attrs != NULL)
2434520Snw141292 			ldap_value_free(attrs);
2444520Snw141292 
2454520Snw141292 		/* Explode each RDN, look for DC attr, save val as DNS label */
2464520Snw141292 		if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL)
2474520Snw141292 			goto done;
2484520Snw141292 
2494520Snw141292 		for (attr = attrs; *attr != NULL; attr++) {
2504520Snw141292 			if (strncasecmp(*attr, "dc=", 3) != 0)
2514520Snw141292 				continue;
2524520Snw141292 
2534520Snw141292 			/* Found a DNS label */
2544520Snw141292 			labels[nlabels++] = strdup((*attr) + 3);
2554520Snw141292 
2564520Snw141292 			if (nlabels == maxlabels) {
2574520Snw141292 				char **tmp;
2584520Snw141292 				tmp = realloc(labels,
2594520Snw141292 				    sizeof (char *) * (maxlabels + 1));
2604520Snw141292 
2614520Snw141292 				if (tmp == NULL)
2624520Snw141292 					goto done;
2634520Snw141292 
2644520Snw141292 				labels = tmp;
2654520Snw141292 				labels[nlabels] = NULL;
2664520Snw141292 			}
2674520Snw141292 
2684520Snw141292 			/* There should be just one DC= attr per-RDN */
2694520Snw141292 			break;
2704520Snw141292 		}
2714520Snw141292 	}
2724520Snw141292 
2734520Snw141292 	/*
2744520Snw141292 	 * Got all the labels, now join with '.'
2754520Snw141292 	 *
2764520Snw141292 	 * We need room for nlabels - 1 periods ('.'), one nul
2774520Snw141292 	 * terminator, and the strlen() of each label.
2784520Snw141292 	 */
2794520Snw141292 	dnslen = nlabels;
2804520Snw141292 	for (label = labels; *label != NULL; label++)
2814520Snw141292 		dnslen += strlen(*label);
2824520Snw141292 
2834520Snw141292 	if ((dns = malloc(dnslen)) == NULL)
2844520Snw141292 		goto done;
2854520Snw141292 
2864520Snw141292 	*dns = '\0';
2874520Snw141292 
2884520Snw141292 	for (label = labels; *label != NULL; label++) {
2894520Snw141292 		(void) strlcat(dns, *label, dnslen);
2904520Snw141292 		/*
2914520Snw141292 		 * NOTE: the last '.' won't be appended -- there's no room
2924520Snw141292 		 * for it!
2934520Snw141292 		 */
2944520Snw141292 		(void) strlcat(dns, ".", dnslen);
2954520Snw141292 	}
2964520Snw141292 
2974520Snw141292 done:
2984520Snw141292 	if (labels != NULL) {
2994520Snw141292 		for (label = labels; *label != NULL; label++)
3004520Snw141292 			free(*label);
3014520Snw141292 		free(labels);
3024520Snw141292 	}
3034520Snw141292 	if (attrs != NULL)
3044520Snw141292 		ldap_value_free(attrs);
3054520Snw141292 	if (rdns != NULL)
3064520Snw141292 		ldap_value_free(rdns);
3074520Snw141292 
3084520Snw141292 	return (dns);
3094520Snw141292 }
3104520Snw141292 
3114520Snw141292 /*
3124520Snw141292  * Keep connection management simple for now, extend or replace later
3134520Snw141292  * with updated libsldap code.
3144520Snw141292  */
3154520Snw141292 #define	ADREAPERSLEEP	60
3164520Snw141292 #define	ADCONN_TIME	300
3174520Snw141292 
3184520Snw141292 /*
3194520Snw141292  * Idle connection reaping side of connection management
3204520Snw141292  *
3214520Snw141292  * Every minute wake up and look for connections that have been idle for
3224520Snw141292  * five minutes or more and close them.
3234520Snw141292  */
3244520Snw141292 /*ARGSUSED*/
3254520Snw141292 static
3264520Snw141292 void
3274520Snw141292 adreaper(void *arg)
3284520Snw141292 {
3294520Snw141292 	ad_host_t	*adh;
3304520Snw141292 	time_t		now;
3314520Snw141292 	timespec_t	ts;
3324520Snw141292 
3334520Snw141292 	ts.tv_sec = ADREAPERSLEEP;
3344520Snw141292 	ts.tv_nsec = 0;
3354520Snw141292 
3364520Snw141292 	for (;;) {
3374520Snw141292 		/*
3384520Snw141292 		 * nanosleep(3RT) is thead-safe (no SIGALRM) and more
3394520Snw141292 		 * portable than usleep(3C)
3404520Snw141292 		 */
3414520Snw141292 		(void) nanosleep(&ts, NULL);
3424520Snw141292 		(void) pthread_mutex_lock(&adhostlock);
3434520Snw141292 		now = time(NULL);
3444520Snw141292 		for (adh = host_head; adh != NULL; adh = adh->next) {
3454520Snw141292 			(void) pthread_mutex_lock(&adh->lock);
3464520Snw141292 			if (adh->ref == 0 && adh->idletime != 0 &&
3474520Snw141292 			    adh->idletime + ADCONN_TIME < now) {
3484520Snw141292 				if (adh->ld) {
3494520Snw141292 					(void) ldap_unbind(adh->ld);
3504520Snw141292 					adh->ld = NULL;
3514520Snw141292 					adh->idletime = 0;
3524520Snw141292 					adh->ref = 0;
3534520Snw141292 				}
3544520Snw141292 			}
3554520Snw141292 			(void) pthread_mutex_unlock(&adh->lock);
3564520Snw141292 		}
3574520Snw141292 		(void) pthread_mutex_unlock(&adhostlock);
3584520Snw141292 	}
3594520Snw141292 }
3604520Snw141292 
3614520Snw141292 int
3624520Snw141292 idmap_ad_alloc(ad_t **new_ad, const char *default_domain,
3634520Snw141292 		idmap_ad_partition_t part)
3644520Snw141292 {
3654520Snw141292 	ad_t *ad;
3664520Snw141292 
3674520Snw141292 	*new_ad = NULL;
3684520Snw141292 
3694520Snw141292 	if ((default_domain == NULL || *default_domain == '\0') &&
3704520Snw141292 	    part != IDMAP_AD_GLOBAL_CATALOG)
3714520Snw141292 		return (-1);
3724520Snw141292 
3734520Snw141292 	if ((ad = calloc(1, sizeof (ad_t))) == NULL)
3744520Snw141292 		return (-1);
3754520Snw141292 
3764520Snw141292 	ad->ref = 1;
3774520Snw141292 	ad->partition = part;
3784520Snw141292 
3794520Snw141292 	/*
3804520Snw141292 	 * If default_domain is NULL, deal, deferring errors until
3814520Snw141292 	 * idmap_lookup_batch_start() -- this makes it easier on the
3824520Snw141292 	 * caller, who can simply observe lookups failing as opposed to
3834520Snw141292 	 * having to conditionalize calls to lookups according to
3844520Snw141292 	 * whether it has a non-NULL ad_t *.
3854520Snw141292 	 */
3864520Snw141292 	if (default_domain == NULL)
3874520Snw141292 		default_domain = "";
3884520Snw141292 
3894520Snw141292 	if ((ad->dflt_w2k_dom = strdup(default_domain)) == NULL)
3904520Snw141292 		goto err;
3914520Snw141292 
3924520Snw141292 	/* If default_domain is empty, deal; see above */
3934520Snw141292 	if (*default_domain == '\0') {
3944520Snw141292 		if ((ad->basedn = strdup("")) == NULL)
3954520Snw141292 			goto err;
3964520Snw141292 	} else if ((ad->basedn = dns2dn(default_domain)) == NULL) {
3974520Snw141292 		goto err;
3984520Snw141292 	}
3994520Snw141292 
4004520Snw141292 	if (pthread_mutex_init(&ad->lock, NULL) != 0)
4014520Snw141292 		goto err;
4024520Snw141292 
4034520Snw141292 	*new_ad = ad;
4044520Snw141292 
4054520Snw141292 	return (0);
4064520Snw141292 err:
4074520Snw141292 	if (ad->dflt_w2k_dom != NULL)
4084520Snw141292 		free(ad->dflt_w2k_dom);
4094520Snw141292 	if (ad->basedn != NULL)
4104520Snw141292 		free(ad->basedn);
4114520Snw141292 	free(ad);
4124520Snw141292 	return (-1);
4134520Snw141292 }
4144520Snw141292 
4154520Snw141292 
4164520Snw141292 void
4174520Snw141292 idmap_ad_free(ad_t **ad)
4184520Snw141292 {
4194520Snw141292 	ad_host_t *p;
4204520Snw141292 
4214520Snw141292 	if (ad == NULL || *ad == NULL)
4224520Snw141292 		return;
4234520Snw141292 
4244520Snw141292 	(void) pthread_mutex_lock(&(*ad)->lock);
4254520Snw141292 
4264520Snw141292 	if (atomic_dec_32_nv(&(*ad)->ref) > 0) {
4274520Snw141292 		(void) pthread_mutex_unlock(&(*ad)->lock);
4284520Snw141292 		*ad = NULL;
4294520Snw141292 		return;
4304520Snw141292 	}
4314520Snw141292 
4324520Snw141292 	for (p = host_head; p != NULL; p = p->next) {
4334520Snw141292 		if (p->owner != (*ad))
4344520Snw141292 			continue;
4354520Snw141292 		idmap_delete_ds((*ad), p->host, p->port);
4364520Snw141292 	}
4374520Snw141292 
4384520Snw141292 	free((*ad)->basedn);
4394520Snw141292 
4404520Snw141292 	(void) pthread_mutex_unlock(&(*ad)->lock);
4414520Snw141292 	(void) pthread_mutex_destroy(&(*ad)->lock);
4424520Snw141292 
4434520Snw141292 	free(*ad);
4444520Snw141292 
4454520Snw141292 	*ad = NULL;
4464520Snw141292 }
4474520Snw141292 
4484520Snw141292 static
4494520Snw141292 int
4504520Snw141292 idmap_open_conn(ad_host_t *adh)
4514520Snw141292 {
4524520Snw141292 	int	rc, ldversion;
4534520Snw141292 
4544520Snw141292 	if (adh->dead && adh->ld != NULL) {
4554520Snw141292 		(void) ldap_unbind(adh->ld);
4564520Snw141292 		adh->ld = NULL;
4574520Snw141292 		adh->dead = 0;
4584520Snw141292 	}
4594520Snw141292 
4604520Snw141292 	if (adh->ld == NULL) {
4614520Snw141292 		int zero = 0;
4624520Snw141292 		int timeoutms = 30 * 1000;
4634520Snw141292 
4644520Snw141292 		atomic_inc_64(&adh->generation);
4654520Snw141292 		adh->ld = ldap_init(adh->host, adh->port);
4664520Snw141292 		if (adh->ld == NULL)
4674520Snw141292 			return (-1);
4684520Snw141292 
4694520Snw141292 		ldversion = LDAP_VERSION3;
4704520Snw141292 		(void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION,
4714520Snw141292 		    &ldversion);
4724520Snw141292 
4734520Snw141292 		(void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS,
4744520Snw141292 		    LDAP_OPT_OFF);
4754520Snw141292 		(void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero);
4764520Snw141292 		(void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero);
4774520Snw141292 		/* setup TCP/IP connect timeout */
4784520Snw141292 		(void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT,
4794520Snw141292 		    &timeoutms);
4804520Snw141292 		(void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
4814520Snw141292 		rc = ldap_sasl_interactive_bind_s(adh->ld,
4824520Snw141292 		    "" /* binddn */, adh->saslmech, NULL, NULL, adh->saslflags,
4834520Snw141292 		    &idmap_saslcallback, NULL /* defaults */);
4844520Snw141292 
4854520Snw141292 		if (rc != LDAP_SUCCESS) {
4864520Snw141292 			idmapdlog(LOG_ERR, "Could not authenticate to the "
4874520Snw141292 			    "LDAP server.  (Check that the host keys are "
4884520Snw141292 			    "correct?)");
4894520Snw141292 			return (rc);
4904520Snw141292 		}
4914520Snw141292 	}
4924520Snw141292 
4934520Snw141292 	adh->idletime = time(NULL);
4944520Snw141292 
4954520Snw141292 	return (LDAP_SUCCESS);
4964520Snw141292 }
4974520Snw141292 
4984520Snw141292 
4994520Snw141292 /*
5004520Snw141292  * Connection management: find an open connection or open one
5014520Snw141292  */
5024520Snw141292 static
5034520Snw141292 ad_host_t *
5044520Snw141292 idmap_get_conn(const ad_t *ad)
5054520Snw141292 {
5064520Snw141292 	ad_host_t	*adh = NULL;
5074520Snw141292 	int		rc;
5084520Snw141292 
5094520Snw141292 	(void) pthread_mutex_lock(&adhostlock);
5104520Snw141292 
5114520Snw141292 	/*
5124520Snw141292 	 * Search for any ad_host_t, preferably one with an open
5134520Snw141292 	 * connection
5144520Snw141292 	 */
5154520Snw141292 	for (adh = host_head; adh != NULL; adh = adh->next) {
5164520Snw141292 		if (adh->owner == ad) {
5174520Snw141292 			break;
5184520Snw141292 		}
5194520Snw141292 	}
5204520Snw141292 
5214520Snw141292 	if (adh != NULL)
5224520Snw141292 		atomic_inc_32(&adh->ref);
5234520Snw141292 
5244520Snw141292 	(void) pthread_mutex_unlock(&adhostlock);
5254520Snw141292 
5264520Snw141292 	if (adh == NULL)
5274520Snw141292 		return (NULL);
5284520Snw141292 
5294520Snw141292 	/* found connection, open it if not opened */
5304520Snw141292 	(void) pthread_mutex_lock(&adh->lock);
5314520Snw141292 	rc = idmap_open_conn(adh);
5324520Snw141292 	(void) pthread_mutex_unlock(&adh->lock);
5334520Snw141292 	if (rc != LDAP_SUCCESS)
5344520Snw141292 		return (NULL);
5354520Snw141292 
5364520Snw141292 	return (adh);
5374520Snw141292 }
5384520Snw141292 
5394520Snw141292 static
5404520Snw141292 void
5414520Snw141292 idmap_release_conn(ad_host_t *adh)
5424520Snw141292 {
5434520Snw141292 	(void) pthread_mutex_lock(&adh->lock);
5444520Snw141292 	if (atomic_dec_32_nv(&adh->ref) == 0)
5454520Snw141292 		adh->idletime = time(NULL);
5464520Snw141292 	(void) pthread_mutex_unlock(&adh->lock);
5474520Snw141292 }
5484520Snw141292 
5494520Snw141292 /*
5504520Snw141292  * Take ad_host_config_t information, create a ad_host_t,
5514520Snw141292  * populate it and add it to the list of hosts.
5524520Snw141292  */
5534520Snw141292 
5544520Snw141292 int
5554520Snw141292 idmap_add_ds(ad_t *ad, const char *host, int port)
5564520Snw141292 {
5574520Snw141292 	ad_host_t	*p;
5584520Snw141292 	ad_host_t	*new = NULL;
5594520Snw141292 	int		ret = -1;
5604520Snw141292 
5614520Snw141292 	if (port == 0)
5624520Snw141292 		port = (int)ad->partition;
5634520Snw141292 
5644520Snw141292 	(void) pthread_mutex_lock(&adhostlock);
5654520Snw141292 	for (p = host_head; p != NULL; p = p->next) {
5664520Snw141292 		if (p->owner != ad)
5674520Snw141292 			continue;
5684520Snw141292 
5694520Snw141292 		if (strcmp(host, p->host) == 0 && p->port == port) {
5704520Snw141292 			/* already added */
5714520Snw141292 			ret = -2;
5724520Snw141292 			goto err;
5734520Snw141292 		}
5744520Snw141292 	}
5754520Snw141292 
5764520Snw141292 	/* add new entry */
5774520Snw141292 	new = (ad_host_t *)calloc(1, sizeof (ad_host_t));
5784520Snw141292 	if (new == NULL)
5794520Snw141292 		goto err;
5804520Snw141292 	new->owner = ad;
5814520Snw141292 	new->port = port;
5824520Snw141292 	new->dead = 0;
5834520Snw141292 	if ((new->host = strdup(host)) == NULL)
5844520Snw141292 		goto err;
5854520Snw141292 
5864520Snw141292 	/* default to SASL GSSAPI only for now */
5874520Snw141292 	new->saslflags = LDAP_SASL_INTERACTIVE;
5884520Snw141292 	new->saslmech = "GSSAPI";
5894520Snw141292 
5904520Snw141292 	if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) {
5914520Snw141292 		free(new->host);
5924520Snw141292 		new->host = NULL;
5934520Snw141292 		errno = ret;
5944520Snw141292 		ret = -1;
5954520Snw141292 		goto err;
5964520Snw141292 	}
5974520Snw141292 
5984520Snw141292 	/* link in */
5994520Snw141292 	new->next = host_head;
6004520Snw141292 	host_head = new;
6014520Snw141292 
6024520Snw141292 	/* Start reaper if it doesn't exist */
6034520Snw141292 	if (reaperid == 0)
6044520Snw141292 		(void) pthread_create(&reaperid, NULL,
6054520Snw141292 		    (void *(*)(void *))adreaper, (void *)NULL);
6064520Snw141292 
6074520Snw141292 err:
6084520Snw141292 	(void) pthread_mutex_unlock(&adhostlock);
6094520Snw141292 
6104520Snw141292 	if (ret != 0 && new != NULL) {
6114520Snw141292 		if (new->host != NULL) {
6124520Snw141292 			(void) pthread_mutex_destroy(&new->lock);
6134520Snw141292 			free(new->host);
6144520Snw141292 		}
6154520Snw141292 		free(new);
6164520Snw141292 	}
6174520Snw141292 
6184520Snw141292 	return (ret);
6194520Snw141292 }
6204520Snw141292 
6214520Snw141292 /*
6224520Snw141292  * free a DS configuration
6234520Snw141292  */
6244520Snw141292 void
6254520Snw141292 idmap_delete_ds(ad_t *ad, const char *host, int port)
6264520Snw141292 {
6274520Snw141292 	ad_host_t	**p, *q;
6284520Snw141292 
6294520Snw141292 	(void) pthread_mutex_lock(&adhostlock);
6304520Snw141292 	for (p = &host_head; *p != NULL; p = &((*p)->next)) {
6314520Snw141292 		if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 ||
6324520Snw141292 		    (*p)->port != port)
6334520Snw141292 			continue;
6344520Snw141292 		/* found */
6354520Snw141292 		if (atomic_dec_32_nv(&((*p)->ref)) > 0)
6364520Snw141292 			break;	/* still in use */
6374520Snw141292 
6384520Snw141292 		q = *p;
6394520Snw141292 		*p = (*p)->next;
6404520Snw141292 
6414520Snw141292 		(void) pthread_mutex_destroy(&q->lock);
6424520Snw141292 
6434520Snw141292 		if (q->ld)
6444520Snw141292 			(void) ldap_unbind(q->ld);
6454520Snw141292 		if (q->host)
6464520Snw141292 			free(q->host);
6474520Snw141292 		free(q);
6484520Snw141292 		break;
6494520Snw141292 	}
6504520Snw141292 	(void) pthread_mutex_unlock(&adhostlock);
6514520Snw141292 }
6524520Snw141292 
6534520Snw141292 /*
6544520Snw141292  * Convert a binary SID in a BerValue to a sid_t
6554520Snw141292  */
6564520Snw141292 static
6574520Snw141292 int
6584520Snw141292 idmap_getsid(BerValue *bval, sid_t *sidp)
6594520Snw141292 {
6604520Snw141292 	int		i, j;
6614520Snw141292 	uchar_t		*v;
6624520Snw141292 	uint32_t	a;
6634520Snw141292 
6644520Snw141292 	/*
6654520Snw141292 	 * The binary format of a SID is as follows:
6664520Snw141292 	 *
6674520Snw141292 	 * byte #0: version, always 0x01
6684520Snw141292 	 * byte #1: RID count, always <= 0x0f
6694520Snw141292 	 * bytes #2-#7: SID authority, big-endian 48-bit unsigned int
6704520Snw141292 	 *
6714520Snw141292 	 * followed by RID count RIDs, each a little-endian, unsigned
6724520Snw141292 	 * 32-bit int.
6734520Snw141292 	 */
6744520Snw141292 	/*
6754520Snw141292 	 * Sanity checks: must have at least one RID, version must be
6764520Snw141292 	 * 0x01, and the length must be 8 + rid count * 4
6774520Snw141292 	 */
6784520Snw141292 	if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 &&
6794520Snw141292 	    bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) {
6804520Snw141292 		v = (uchar_t *)bval->bv_val;
6814520Snw141292 		sidp->version = v[0];
6824520Snw141292 		sidp->sub_authority_count = v[1];
6834520Snw141292 		sidp->authority =
6844520Snw141292 		    /* big endian -- so start from the left */
6854520Snw141292 		    ((u_longlong_t)v[2] << 40) |
6864520Snw141292 		    ((u_longlong_t)v[3] << 32) |
6874520Snw141292 		    ((u_longlong_t)v[4] << 24) |
6884520Snw141292 		    ((u_longlong_t)v[5] << 16) |
6894520Snw141292 		    ((u_longlong_t)v[6] << 8) |
6904520Snw141292 		    (u_longlong_t)v[7];
6914520Snw141292 		for (i = 0; i < sidp->sub_authority_count; i++) {
6924520Snw141292 			j = 8 + (i * 4);
6934520Snw141292 			/* little endian -- so start from the right */
6944520Snw141292 			a = (v[j + 3] << 24) | (v[j + 2] << 16) |
6954520Snw141292 			    (v[j + 1] << 8) | (v[j]);
6964520Snw141292 			sidp->sub_authorities[i] = a;
6974520Snw141292 		}
6984520Snw141292 		return (0);
6994520Snw141292 	}
7004520Snw141292 	return (-1);
7014520Snw141292 }
7024520Snw141292 
7034520Snw141292 /*
7044520Snw141292  * Convert a sid_t to S-1-...
7054520Snw141292  */
7064520Snw141292 static
7074520Snw141292 char *
7084520Snw141292 idmap_sid2txt(sid_t *sidp)
7094520Snw141292 {
7104520Snw141292 	int	rlen, i, len;
7114520Snw141292 	char	*str, *cp;
7124520Snw141292 
7134520Snw141292 	if (sidp->version != 1)
7144520Snw141292 		return (NULL);
7154520Snw141292 
7164520Snw141292 	len = sizeof ("S-1-") - 1;
7174520Snw141292 
7184520Snw141292 	/*
7194520Snw141292 	 * We could optimize like so, but, why?
7204520Snw141292 	 *	if (sidp->authority < 10)
7214520Snw141292 	 *		len += 2;
7224520Snw141292 	 *	else if (sidp->authority < 100)
7234520Snw141292 	 *		len += 3;
7244520Snw141292 	 *	else
7254520Snw141292 	 *		len += snprintf(NULL, 0"%llu", sidp->authority);
7264520Snw141292 	 */
7274520Snw141292 	len += snprintf(NULL, 0, "%llu", sidp->authority);
7284520Snw141292 
7294520Snw141292 	/* Max length of a uint32_t printed out in ASCII is 10 bytes */
7304520Snw141292 	len += 1 + (sidp->sub_authority_count + 1) * 10;
7314520Snw141292 
7324520Snw141292 	if ((cp = str = malloc(len)) == NULL)
7334520Snw141292 		return (NULL);
7344520Snw141292 
7354520Snw141292 	rlen = snprintf(str, len, "S-1-%llu", sidp->authority);
7364520Snw141292 
7374520Snw141292 	cp += rlen;
7384520Snw141292 	len -= rlen;
7394520Snw141292 
7404520Snw141292 	for (i = 0; i < sidp->sub_authority_count; i++) {
7414520Snw141292 		assert(len > 0);
7424520Snw141292 		rlen = snprintf(cp, len, "-%u", sidp->sub_authorities[i]);
7434520Snw141292 		cp += rlen;
7444520Snw141292 		len -= rlen;
7454520Snw141292 		assert(len >= 0);
7464520Snw141292 	}
7474520Snw141292 
7484520Snw141292 	return (str);
7494520Snw141292 }
7504520Snw141292 
7514520Snw141292 /*
7524520Snw141292  * Convert a sid_t to on-the-wire encoding
7534520Snw141292  */
7544520Snw141292 static
7554520Snw141292 int
7564520Snw141292 idmap_sid2binsid(sid_t *sid, uchar_t *binsid, int binsidlen)
7574520Snw141292 {
7584520Snw141292 	uchar_t		*p;
7594520Snw141292 	int		i;
7604520Snw141292 	uint64_t	a;
7614520Snw141292 	uint32_t	r;
7624520Snw141292 
7634520Snw141292 	if (sid->version != 1 ||
7644520Snw141292 	    binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4))
7654520Snw141292 		return (-1);
7664520Snw141292 
7674520Snw141292 	p = binsid;
7684520Snw141292 	*p++ = 0x01;		/* version */
7694520Snw141292 	/* sub authority count */
7704520Snw141292 	*p++ = sid->sub_authority_count;
7714520Snw141292 	/* Authority */
7724520Snw141292 	a = sid->authority;
7734520Snw141292 	/* big-endian -- start from left */
7744520Snw141292 	*p++ = (a >> 40) & 0xFF;
7754520Snw141292 	*p++ = (a >> 32) & 0xFF;
7764520Snw141292 	*p++ = (a >> 24) & 0xFF;
7774520Snw141292 	*p++ = (a >> 16) & 0xFF;
7784520Snw141292 	*p++ = (a >> 8) & 0xFF;
7794520Snw141292 	*p++ = a & 0xFF;
7804520Snw141292 
7814520Snw141292 	/* sub-authorities */
7824520Snw141292 	for (i = 0; i < sid->sub_authority_count; i++) {
7834520Snw141292 		r = sid->sub_authorities[i];
7844520Snw141292 		/* little-endian -- start from right */
7854520Snw141292 		*p++ = (r & 0x000000FF);
7864520Snw141292 		*p++ = (r & 0x0000FF00) >> 8;
7874520Snw141292 		*p++ = (r & 0x00FF0000) >> 16;
7884520Snw141292 		*p++ = (r & 0xFF000000) >> 24;
7894520Snw141292 	}
7904520Snw141292 
7914520Snw141292 	return (0);
7924520Snw141292 }
7934520Snw141292 
7944520Snw141292 /*
7954520Snw141292  * Convert a stringified SID (S-1-...) into a hex-encoded version of the
7964520Snw141292  * on-the-wire encoding, but with each pair of hex digits pre-pended
7974520Snw141292  * with a '\', so we can pass this to libldap.
7984520Snw141292  */
7994520Snw141292 static
8004520Snw141292 int
8014520Snw141292 idmap_txtsid2hexbinsid(const char *txt, const rid_t *rid,
8024520Snw141292 	char *hexbinsid, int hexbinsidlen)
8034520Snw141292 {
8044520Snw141292 	sid_t		sid = { 0 };
8054520Snw141292 	int		i, j;
8064520Snw141292 	const char	*cp;
8074520Snw141292 	char		*ecp;
8084520Snw141292 	u_longlong_t	a;
8094520Snw141292 	unsigned long	r;
8104520Snw141292 	uchar_t		*binsid, b, hb;
8114520Snw141292 
8124520Snw141292 	/* Only version 1 SIDs please */
8134520Snw141292 	if (strncmp(txt, "S-1-", strlen("S-1-")) != 0)
8144520Snw141292 		return (-1);
8154520Snw141292 
8164520Snw141292 	if (strlen(txt) < (strlen("S-1-") + 1))
8174520Snw141292 		return (-1);
8184520Snw141292 
8194520Snw141292 	/* count '-'s */
8204520Snw141292 	for (j = 0, cp = strchr(txt, '-');
8214520Snw141292 	    cp != NULL && *cp != '\0';
8224520Snw141292 	    j++, cp = strchr(cp + 1, '-')) {
8234520Snw141292 		/* can't end on a '-' */
8244520Snw141292 		if (*(cp + 1) == '\0')
8254520Snw141292 			return (-1);
8264520Snw141292 	}
8274520Snw141292 
8284520Snw141292 	/* must have at least one RID, but not too many */
8294520Snw141292 	if (j < 3 || (j - 1) > SID_MAX_SUB_AUTHORITIES ||
8304520Snw141292 	    (rid != NULL && j > SID_MAX_SUB_AUTHORITIES))
8314520Snw141292 		return (-1);
8324520Snw141292 
8334520Snw141292 	/* check that we only have digits and '-' */
8344520Snw141292 	if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1))
8354520Snw141292 		return (-1);
8364520Snw141292 
8374520Snw141292 	/* we know the version number and RID count */
8384520Snw141292 	sid.version = 1;
8394520Snw141292 	sid.sub_authority_count = j - 2;
8404520Snw141292 
8414520Snw141292 	cp = txt + strlen("S-1-");
8424520Snw141292 
8434520Snw141292 	/* 64-bit safe parsing of unsigned 48-bit authority value */
8444520Snw141292 	errno = 0;
8454520Snw141292 	a = strtoull(cp, &ecp, 10);
8464520Snw141292 
8474520Snw141292 	/* errors parsing the authority or too many bits */
8484520Snw141292 	if (cp == ecp || (a == 0 && errno == EINVAL) ||
8494520Snw141292 	    (a == ULLONG_MAX && errno == ERANGE) ||
8504520Snw141292 	    (a & 0x0000ffffffffffffULL) != a)
8514520Snw141292 		return (-1);
8524520Snw141292 
8534520Snw141292 	cp = ecp;
8544520Snw141292 
8554520Snw141292 	sid.authority = (uint64_t)a;
8564520Snw141292 
8574520Snw141292 	for (i = 0; i < sid.sub_authority_count; i++) {
8584520Snw141292 		if (*cp++ != '-')
8594520Snw141292 			return (-1);
8604520Snw141292 		/* 64-bit safe parsing of unsigned 32-bit RID */
8614520Snw141292 		errno = 0;
8624520Snw141292 		r = strtoul(cp, &ecp, 10);
8634520Snw141292 		/* errors parsing the RID or too many bits */
8644520Snw141292 		if (cp == ecp || (r == 0 && errno == EINVAL) ||
8654520Snw141292 		    (r == ULONG_MAX && errno == ERANGE) ||
8664520Snw141292 		    (r & 0xffffffffUL) != r)
8674520Snw141292 			return (-1);
8684520Snw141292 		sid.sub_authorities[i] = (uint32_t)r;
8694520Snw141292 		cp = ecp;
8704520Snw141292 	}
8714520Snw141292 
8724520Snw141292 	/* check that all of the string SID has been consumed */
8734520Snw141292 	if (*cp != '\0')
8744520Snw141292 		return (-1);
8754520Snw141292 
8764520Snw141292 	if (rid != NULL) {
8774520Snw141292 		sid.sub_authorities[sid.sub_authority_count++] = *rid;
8784520Snw141292 	}
8794520Snw141292 
8804520Snw141292 	j = 1 + 1 + 6 + sid.sub_authority_count * 4;
8814520Snw141292 
8824520Snw141292 	if (hexbinsidlen < (j * 3))
8834520Snw141292 		return (-2);
8844520Snw141292 
8854520Snw141292 	/* binary encode the SID */
8864520Snw141292 	binsid = (uchar_t *)alloca(j);
8874520Snw141292 	(void) idmap_sid2binsid(&sid, binsid, j);
8884520Snw141292 
8894520Snw141292 	/* hex encode, with a backslash before each byte */
8904520Snw141292 	for (ecp = hexbinsid, i = 0; i < j; i++) {
8914520Snw141292 		b = binsid[i];
8924520Snw141292 		*ecp++ = '\\';
8934520Snw141292 		hb = (b >> 4) & 0xF;
8944520Snw141292 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
8954520Snw141292 		hb = b & 0xF;
8964520Snw141292 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
8974520Snw141292 	}
8984520Snw141292 	*ecp = '\0';
8994520Snw141292 
9004520Snw141292 	return (0);
9014520Snw141292 }
9024520Snw141292 
9034520Snw141292 static
9044520Snw141292 char *
9054520Snw141292 convert_bval2sid(BerValue *bval, rid_t *rid)
9064520Snw141292 {
9074520Snw141292 	sid_t	sid;
9084520Snw141292 
9094520Snw141292 	if (idmap_getsid(bval, &sid) < 0)
9104520Snw141292 		return (NULL);
9114520Snw141292 
9124520Snw141292 	/*
9134520Snw141292 	 * If desired and if the SID is what should be a domain/computer
9144520Snw141292 	 * user or group SID (i.e., S-1-5-w-x-y-z-<user/group RID>) then
9154520Snw141292 	 * save the last RID and truncate the SID
9164520Snw141292 	 */
9174520Snw141292 	if (rid != NULL && sid.authority == 5 && sid.sub_authority_count == 5)
9184520Snw141292 		*rid = sid.sub_authorities[--sid.sub_authority_count];
9194520Snw141292 	return (idmap_sid2txt(&sid));
9204520Snw141292 }
9214520Snw141292 
9224520Snw141292 
9234520Snw141292 idmap_retcode
9244520Snw141292 idmap_lookup_batch_start(ad_t *ad, int nqueries, idmap_query_state_t **state)
9254520Snw141292 {
9264520Snw141292 	idmap_query_state_t *new_state;
9274520Snw141292 	ad_host_t	*adh = NULL;
9284520Snw141292 
9294520Snw141292 	*state = NULL;
9304520Snw141292 
9314520Snw141292 	if (*ad->dflt_w2k_dom == '\0')
9324520Snw141292 		return (-1);
9334520Snw141292 
9344520Snw141292 	adh = idmap_get_conn(ad);
9354520Snw141292 	if (adh == NULL)
9364520Snw141292 		return (IDMAP_ERR_OTHER);
9374520Snw141292 
9384520Snw141292 	new_state = calloc(1, sizeof (idmap_query_state_t) +
9394520Snw141292 	    (nqueries - 1) * sizeof (idmap_q_t));
9404520Snw141292 
9414520Snw141292 	if (new_state == NULL)
9424520Snw141292 		return (IDMAP_ERR_MEMORY);
9434520Snw141292 
9444520Snw141292 	new_state->qadh = adh;
9454520Snw141292 	new_state->qcount = nqueries;
9464520Snw141292 	new_state->qadh_gen = adh->generation;
9474520Snw141292 	/* should be -1, but the atomic routines want unsigned */
9484520Snw141292 	new_state->qlastsent = 0;
9494520Snw141292 
9504520Snw141292 	(void) pthread_mutex_lock(&qstatelock);
9514520Snw141292 	new_state->next = qstatehead;
9524520Snw141292 	qstatehead = new_state;
9534520Snw141292 	(void) pthread_mutex_unlock(&qstatelock);
9544520Snw141292 
9554520Snw141292 	*state = new_state;
9564520Snw141292 
9574520Snw141292 	return (IDMAP_SUCCESS);
9584520Snw141292 }
9594520Snw141292 
9604520Snw141292 /*
9614520Snw141292  * Find the idmap_query_state_t to which a given LDAP result msgid on a
9624520Snw141292  * given connection belongs
9634520Snw141292  */
9644520Snw141292 static
9654520Snw141292 int
9664520Snw141292 idmap_msgid2query(ad_host_t *adh, int msgid,
9674520Snw141292 	idmap_query_state_t **state, int *qid)
9684520Snw141292 {
9694520Snw141292 	idmap_query_state_t *p;
9704520Snw141292 	int		    i;
9714520Snw141292 
9724520Snw141292 	(void) pthread_mutex_lock(&qstatelock);
9734520Snw141292 	for (p = qstatehead; p != NULL; p = p->next) {
9744520Snw141292 		if (p->qadh != adh || adh->generation != p->qadh_gen)
9754520Snw141292 			continue;
9764520Snw141292 		for (i = 0; i < p->qcount; i++) {
9774520Snw141292 			if ((p->queries[i]).msgid == msgid) {
9784520Snw141292 				*state = p;
9794520Snw141292 				*qid = i;
9804520Snw141292 				(void) pthread_mutex_unlock(&qstatelock);
9814520Snw141292 				return (1);
9824520Snw141292 			}
9834520Snw141292 		}
9844520Snw141292 	}
9854520Snw141292 	(void) pthread_mutex_unlock(&qstatelock);
9864520Snw141292 	return (0);
9874520Snw141292 }
9884520Snw141292 
9894520Snw141292 /*
9904520Snw141292  * Handle an objectSid attr from a result
9914520Snw141292  */
9924520Snw141292 static
9934520Snw141292 void
9944520Snw141292 idmap_bv_objsid2sidstr(BerValue **bvalues, idmap_q_t *q)
9954520Snw141292 {
9964520Snw141292 	if (bvalues == NULL)
9974520Snw141292 		return;
9984520Snw141292 	/* objectSid is single valued */
9994520Snw141292 	*(q->result) = convert_bval2sid(bvalues[0], q->rid);
10004520Snw141292 	q->got_objectSid = 1;
10014520Snw141292 }
10024520Snw141292 
10034520Snw141292 /*
10044520Snw141292  * Handle a sAMAccountName attr from a result
10054520Snw141292  */
10064520Snw141292 static
10074520Snw141292 void
10084520Snw141292 idmap_bv_samaccountname2name(BerValue **bvalues, idmap_q_t *q, const char *dn)
10094520Snw141292 {
10104520Snw141292 	char *result, *domain;
10114520Snw141292 	int len;
10124520Snw141292 
10134520Snw141292 	if (bvalues == NULL)
10144520Snw141292 		return;
10154520Snw141292 
10164520Snw141292 	if ((domain = dn2dns(dn)) == NULL)
10174520Snw141292 		return;
10184520Snw141292 
10194520Snw141292 	if (bvalues == NULL || bvalues[0] == NULL ||
10204520Snw141292 	    bvalues[0]->bv_val == NULL)
10214520Snw141292 		return;
10224520Snw141292 
10234520Snw141292 	len = bvalues[0]->bv_len + 1;
10244520Snw141292 
10254520Snw141292 	if (q->domain != NULL)
10264520Snw141292 		*(q->domain) = domain;
10274520Snw141292 	else
10284520Snw141292 		len += strlen(domain) + 1;
10294520Snw141292 
10304520Snw141292 	if ((result = malloc(len)) == NULL) {
10314520Snw141292 		if (q->domain != NULL)
10324520Snw141292 			*(q->domain) = NULL;
10334520Snw141292 		free(domain);
10344520Snw141292 		return;
10354520Snw141292 	}
10364520Snw141292 
10374520Snw141292 	(void) memcpy(result, bvalues[0]->bv_val, (size_t)bvalues[0]->bv_len);
10384520Snw141292 	result[bvalues[0]->bv_len] = '\0';
10394520Snw141292 
10404520Snw141292 	if (q->domain == NULL) {
10414520Snw141292 		(void) strlcat(result, "@", len);
10424520Snw141292 		(void) strlcat(result, domain, len);
10434520Snw141292 		free(domain);
10444520Snw141292 	}
10454520Snw141292 
10464520Snw141292 	*(q->result) = result;
10474520Snw141292 	q->got_samAcctName = 1;
10484520Snw141292 }
10494520Snw141292 
10504520Snw141292 
10514520Snw141292 #define	BVAL_CASEEQ(bv, str) \
10524520Snw141292 		(((*(bv))->bv_len == (sizeof (str) - 1)) && \
10534520Snw141292 		    strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0)
10544520Snw141292 
10554520Snw141292 /*
10564520Snw141292  * Handle an objectClass attr from a result
10574520Snw141292  */
10584520Snw141292 static
10594520Snw141292 void
10604520Snw141292 idmap_bv_objclass2sidtype(BerValue **bvalues, idmap_q_t *q)
10614520Snw141292 {
10624520Snw141292 	BerValue	**cbval;
10634520Snw141292 
10644520Snw141292 	if (bvalues == NULL)
10654520Snw141292 		return;
10664520Snw141292 
10674520Snw141292 	for (cbval = bvalues; *cbval != NULL; cbval++) {
10684520Snw141292 		/* don't clobber sid_type */
10694520Snw141292 		if (*(q->sid_type) == _IDMAP_T_COMPUTER ||
10704520Snw141292 		    *(q->sid_type) == _IDMAP_T_GROUP ||
10714520Snw141292 		    *(q->sid_type) == _IDMAP_T_USER)
10724520Snw141292 			continue;
10734520Snw141292 
10744520Snw141292 		if (BVAL_CASEEQ(cbval, "Computer")) {
10754520Snw141292 			*(q->sid_type) = _IDMAP_T_COMPUTER;
10764520Snw141292 			return;
10774520Snw141292 		} else if (BVAL_CASEEQ(cbval, "Group")) {
10784520Snw141292 			*(q->sid_type) = _IDMAP_T_GROUP;
10794520Snw141292 		} else if (BVAL_CASEEQ(cbval, "USER")) {
10804520Snw141292 			*(q->sid_type) = _IDMAP_T_USER;
10814520Snw141292 		} else
10824520Snw141292 			*(q->sid_type) = _IDMAP_T_OTHER;
10834520Snw141292 		q->got_objectClass = 1;
10844520Snw141292 	}
10854520Snw141292 }
10864520Snw141292 
10874520Snw141292 /*
10884520Snw141292  * Handle a given search result entry
10894520Snw141292  */
10904520Snw141292 static
10914520Snw141292 void
10924520Snw141292 idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res)
10934520Snw141292 {
10944520Snw141292 	char			*dn, *attr;
10954520Snw141292 	BerElement		*ber = NULL;
10964520Snw141292 	BerValue		**bvalues;
10974520Snw141292 	ad_host_t		*adh;
10984520Snw141292 	idmap_q_t		*q;
10994520Snw141292 	idmap_retcode		orc;
11004520Snw141292 
11014520Snw141292 	adh = state->qadh;
11024520Snw141292 
11034520Snw141292 	(void) pthread_mutex_lock(&adh->lock);
11044520Snw141292 
11054520Snw141292 	if (adh->dead || (dn = ldap_get_dn(adh->ld, res)) == NULL) {
11064520Snw141292 		(void) pthread_mutex_unlock(&adh->lock);
11074520Snw141292 		return;
11084520Snw141292 	}
11094520Snw141292 
11104520Snw141292 	q = &(state->queries[qid]);
11114520Snw141292 
11124520Snw141292 	for (attr = ldap_first_attribute(adh->ld, res, &ber); attr != NULL;
11134520Snw141292 	    attr = ldap_next_attribute(adh->ld, res, ber)) {
11144520Snw141292 		orc = *q->rc;
11154520Snw141292 		bvalues = NULL;	/* for memory management below */
11164520Snw141292 
11174520Snw141292 		/*
11184520Snw141292 		 * If this is an attribute we are looking for and
11194520Snw141292 		 * haven't seen it yet, parse it
11204520Snw141292 		 */
11214520Snw141292 		if (orc != IDMAP_SUCCESS && q->n2s && !q->got_objectSid &&
11224520Snw141292 		    strcasecmp(attr, OBJECTSID) == 0) {
11234520Snw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
11244520Snw141292 			idmap_bv_objsid2sidstr(bvalues, q);
11254520Snw141292 		} else if (orc != IDMAP_SUCCESS && !q->n2s &&
11264520Snw141292 		    !q->got_samAcctName &&
11274520Snw141292 		    strcasecmp(attr, SAMACCOUNTNAME) == 0) {
11284520Snw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
11294520Snw141292 			idmap_bv_samaccountname2name(bvalues, q, dn);
11304520Snw141292 		} else if (orc != IDMAP_SUCCESS && !q->got_objectClass &&
11314520Snw141292 		    strcasecmp(attr, OBJECTCLASS) == 0) {
11324520Snw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
11334520Snw141292 			idmap_bv_objclass2sidtype(bvalues, q);
11344520Snw141292 		}
11354520Snw141292 
11364520Snw141292 		if (bvalues != NULL)
11374520Snw141292 			ldap_value_free_len(bvalues);
11384520Snw141292 		ldap_memfree(attr);
11394520Snw141292 
11404520Snw141292 		if (q->n2s)
11414520Snw141292 			*q->rc = (q->got_objectSid &&
11424520Snw141292 			    q->got_objectClass) ?
11434520Snw141292 			    IDMAP_SUCCESS : IDMAP_ERR_NORESULT;
11444520Snw141292 		else
11454520Snw141292 			*q->rc = (q->got_samAcctName &&
11464520Snw141292 			    q->got_objectClass) ?
11474520Snw141292 			    IDMAP_SUCCESS : IDMAP_ERR_NORESULT;
11484520Snw141292 
11494520Snw141292 		if (*q->rc == IDMAP_SUCCESS && *q->result == NULL)
11504520Snw141292 			*q->rc = IDMAP_ERR_NORESULT;
11514520Snw141292 	}
11524520Snw141292 	(void) pthread_mutex_unlock(&adh->lock);
11534520Snw141292 
11544520Snw141292 	/*
11554520Snw141292 	 * If there should be multiple partial results for different
11564520Snw141292 	 * entities (there should not be, but, if it should happen) then
11574520Snw141292 	 * it's possible that they could get mixed up here and we could
11584520Snw141292 	 * get bogus results.  We just mark the query's results as
11594520Snw141292 	 * toxic (IDMAP_ERR_INTERNAL).
11604520Snw141292 	 *
11614520Snw141292 	 * Between this and ignoring results when we've already filled
11624520Snw141292 	 * out a query's results we should be OK.  The first full reply
11634520Snw141292 	 * wins.  In practice we should never get multiple results.
11644520Snw141292 	 */
11654520Snw141292 	if (orc == IDMAP_ERR_INTERNAL)
11664520Snw141292 		*q->rc = IDMAP_ERR_INTERNAL;
11674520Snw141292 	else if (*q->rc != IDMAP_SUCCESS)
11684520Snw141292 		*q->rc = IDMAP_ERR_INTERNAL;
11694520Snw141292 
11704520Snw141292 	if (ber != NULL)
11714520Snw141292 		ber_free(ber, 0);
11724520Snw141292 
11734520Snw141292 	ldap_memfree(dn);
11744520Snw141292 }
11754520Snw141292 
11764520Snw141292 /*
11774520Snw141292  * Try to get a result; if there is one, find the corresponding
11784520Snw141292  * idmap_q_t and process the result.
11794520Snw141292  */
11804520Snw141292 static
11814520Snw141292 int
11824520Snw141292 idmap_get_adobject_batch(ad_host_t *adh, struct timeval *timeout)
11834520Snw141292 {
11844520Snw141292 	idmap_query_state_t	*query_state;
11854520Snw141292 	LDAPMessage		*res = NULL;
11864520Snw141292 	int			rc, ret, msgid, qid;
11874520Snw141292 
11884520Snw141292 	(void) pthread_mutex_lock(&adh->lock);
11894520Snw141292 	if (adh->dead) {
11904520Snw141292 		(void) pthread_mutex_unlock(&adh->lock);
11914520Snw141292 		return (-1);
11924520Snw141292 	}
11934520Snw141292 
11944520Snw141292 	/* Get one result */
11954520Snw141292 	rc = ldap_result(adh->ld, LDAP_RES_ANY, 0,
11964520Snw141292 	    timeout, &res);
11974520Snw141292 	if (rc == LDAP_UNAVAILABLE || rc == LDAP_UNWILLING_TO_PERFORM ||
11984520Snw141292 	    rc == LDAP_CONNECT_ERROR || rc == LDAP_SERVER_DOWN ||
11994520Snw141292 	    rc == LDAP_BUSY)
12004520Snw141292 		adh->dead = 1;
12014520Snw141292 	(void) pthread_mutex_unlock(&adh->lock);
12024520Snw141292 
12034520Snw141292 	if (adh->dead)
12044520Snw141292 		return (-1);
12054520Snw141292 
12064520Snw141292 	switch (rc) {
12074520Snw141292 	case LDAP_RES_SEARCH_RESULT:
12084520Snw141292 		/* We have all the LDAP replies for some search... */
12094520Snw141292 		msgid = ldap_msgid(res);
12104520Snw141292 		if (idmap_msgid2query(adh, msgid,
12114520Snw141292 		    &query_state, &qid)) {
12124520Snw141292 			/* ...so we can decrement qinflight */
12134520Snw141292 			atomic_dec_32(&query_state->qinflight);
12144520Snw141292 			/* we saw at least one reply */
12154520Snw141292 			query_state->queries[qid].got_reply = 1;
12164520Snw141292 		}
12174520Snw141292 		(void) ldap_msgfree(res);
12184520Snw141292 		ret = 0;
12194520Snw141292 		break;
12204520Snw141292 	case LDAP_RES_SEARCH_REFERENCE:
12214520Snw141292 		/*
12224520Snw141292 		 * We have no need for these at the moment.  Eventually,
12234520Snw141292 		 * when we query things that we can't expect to find in
12244520Snw141292 		 * the Global Catalog then we'll need to learn to follow
12254520Snw141292 		 * references.
12264520Snw141292 		 */
12274520Snw141292 		(void) ldap_msgfree(res);
12284520Snw141292 		ret = 0;
12294520Snw141292 		break;
12304520Snw141292 	case LDAP_RES_SEARCH_ENTRY:
12314520Snw141292 		/* Got a result */
12324520Snw141292 		msgid = ldap_msgid(res);
12334520Snw141292 		if (idmap_msgid2query(adh, msgid,
12344520Snw141292 		    &query_state, &qid)) {
12354520Snw141292 			idmap_extract_object(query_state, qid, res);
12364520Snw141292 			/* we saw at least one result */
12374520Snw141292 			query_state->queries[qid].got_reply = 1;
12384520Snw141292 			query_state->queries[qid].got_results = 1;
12394520Snw141292 		}
12404520Snw141292 		(void) ldap_msgfree(res);
12414520Snw141292 		ret = 0;
12424520Snw141292 		break;
12434520Snw141292 	default:
12444520Snw141292 		/* timeout or error; treat the same */
12454520Snw141292 		ret = -1;
12464520Snw141292 		break;
12474520Snw141292 	}
12484520Snw141292 
12494520Snw141292 	return (ret);
12504520Snw141292 }
12514520Snw141292 
12524520Snw141292 void
12534520Snw141292 idmap_lookup_free_batch(idmap_query_state_t **state)
12544520Snw141292 {
12554520Snw141292 	idmap_query_state_t **p;
12564520Snw141292 
12574520Snw141292 	idmap_release_conn((*state)->qadh);
12584520Snw141292 
12594520Snw141292 	/* Remove this state struct from the list of state structs */
12604520Snw141292 	(void) pthread_mutex_lock(&qstatelock);
12614520Snw141292 	for (p = &qstatehead; *p != NULL; p = &(*p)->next) {
12624520Snw141292 		if (*p == (*state)) {
12634520Snw141292 			*p = (*state)->next;
12644520Snw141292 			break;
12654520Snw141292 		}
12664520Snw141292 	}
12674520Snw141292 	(void) pthread_mutex_unlock(&qstatelock);
12684520Snw141292 
12694520Snw141292 	free(*state);
12704520Snw141292 	*state = NULL;
12714520Snw141292 }
12724520Snw141292 
12734520Snw141292 idmap_retcode
12744520Snw141292 idmap_lookup_batch_end(idmap_query_state_t **state,
12754520Snw141292 	struct timeval *timeout)
12764520Snw141292 {
12774520Snw141292 	idmap_q_t	    *q;
12784520Snw141292 	int		    i;
12794520Snw141292 	int		    rc = LDAP_SUCCESS;
12804520Snw141292 	idmap_retcode	    retcode = IDMAP_SUCCESS;
12814520Snw141292 
12824520Snw141292 	(*state)->qdead = 1;
12834520Snw141292 
12844520Snw141292 	/* Process results until done or until timeout, if given */
12854520Snw141292 	while ((*state)->qinflight > 0) {
12864520Snw141292 		if ((rc = idmap_get_adobject_batch((*state)->qadh,
12874520Snw141292 		    timeout)) != 0)
12884520Snw141292 			break;
12894520Snw141292 	}
12904520Snw141292 
12914520Snw141292 	if (rc == LDAP_UNAVAILABLE || rc == LDAP_UNWILLING_TO_PERFORM ||
12924520Snw141292 	    rc == LDAP_CONNECT_ERROR || rc == LDAP_SERVER_DOWN ||
12934520Snw141292 	    rc == LDAP_BUSY) {
12944520Snw141292 		retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
12954520Snw141292 		(*state)->qadh->dead = 1;
12964520Snw141292 	}
12974520Snw141292 
12984520Snw141292 	for (i = 0; i < (*state)->qcount; i++) {
12994520Snw141292 		q = &((*state)->queries[i]);
13004520Snw141292 		if (q->got_reply && !q->got_results) {
13014520Snw141292 			if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR)
13024520Snw141292 				*q->rc = IDMAP_ERR_RETRIABLE_NET_ERR;
13034520Snw141292 			else
13044520Snw141292 				*q->rc = IDMAP_ERR_NOTFOUND;
13054520Snw141292 		}
13064520Snw141292 	}
13074520Snw141292 
13084520Snw141292 	idmap_lookup_free_batch(state);
13094520Snw141292 
13104520Snw141292 	return (retcode);
13114520Snw141292 }
13124520Snw141292 
13134520Snw141292 /*
13144520Snw141292  * Send one prepared search, queue up msgid, process what results are
13154520Snw141292  * available
13164520Snw141292  */
13174520Snw141292 static
13184520Snw141292 idmap_retcode
13194520Snw141292 idmap_batch_add1(idmap_query_state_t *state, int n2s,
13204520Snw141292 	const char *filter, const char *basedn,
13214520Snw141292 	char **result, char **dname, rid_t *rid, int *sid_type,
13224520Snw141292 	idmap_retcode *rc)
13234520Snw141292 {
13244520Snw141292 	idmap_retcode	retcode = IDMAP_SUCCESS;
13254520Snw141292 	int		lrc, qid;
13264520Snw141292 	struct timeval	tv;
13274520Snw141292 	idmap_q_t	*q;
13284520Snw141292 
13294520Snw141292 	if (state->qdead) {
13304520Snw141292 		*rc = IDMAP_ERR_NORESULT;
13314520Snw141292 		return (IDMAP_ERR_RETRIABLE_NET_ERR);
13324520Snw141292 	}
13334520Snw141292 
13344520Snw141292 	qid = atomic_inc_32_nv(&state->qlastsent) - 1;
13354520Snw141292 
13364520Snw141292 	q = &(state->queries[qid]);
13374520Snw141292 
13384520Snw141292 	/* Remember where to put the results */
13394520Snw141292 	q->result = result;
13404520Snw141292 	q->domain = dname;
13414520Snw141292 	q->rid = rid;
13424520Snw141292 	q->sid_type = sid_type;
13434520Snw141292 	q->rc = rc;
13444520Snw141292 	q->n2s = n2s ? 1 : 0;
13454520Snw141292 	q->got_objectSid = 0;
13464520Snw141292 	q->got_objectClass = 0;
13474520Snw141292 	q->got_samAcctName = 0;
13484520Snw141292 
13494520Snw141292 	/*
13504520Snw141292 	 * Provide sane defaults for the results in case we never hear
13514520Snw141292 	 * back from the DS before closing the connection.
13524520Snw141292 	 */
13534520Snw141292 	*rc = IDMAP_ERR_RETRIABLE_NET_ERR;
13544520Snw141292 	*sid_type = _IDMAP_T_OTHER;
13554520Snw141292 	*result = NULL;
13564520Snw141292 	if (dname != NULL)
13574520Snw141292 		*dname = NULL;
13584520Snw141292 	if (rid != NULL)
13594520Snw141292 		*rid = 0;
13604520Snw141292 
13614520Snw141292 	/* Send this lookup, don't wait for a result here */
13624520Snw141292 	(void) pthread_mutex_lock(&state->qadh->lock);
13634520Snw141292 
13644520Snw141292 	if (!state->qadh->dead) {
13654520Snw141292 		state->qadh->idletime = time(NULL);
13664520Snw141292 		lrc = ldap_search_ext(state->qadh->ld, basedn,
13674520Snw141292 		    LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
13684520Snw141292 		    NULL, -1, &q->msgid);
13694520Snw141292 		if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE ||
13704520Snw141292 		    lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN ||
13714520Snw141292 		    lrc == LDAP_UNWILLING_TO_PERFORM) {
13724520Snw141292 			retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
13734520Snw141292 			state->qadh->dead = 1;
13744520Snw141292 		} else if (lrc != LDAP_SUCCESS) {
13754520Snw141292 			retcode = IDMAP_ERR_OTHER;
13764520Snw141292 			state->qadh->dead = 1;
13774520Snw141292 		}
13784520Snw141292 	}
13794520Snw141292 	(void) pthread_mutex_unlock(&state->qadh->lock);
13804520Snw141292 
13814520Snw141292 	if (state->qadh->dead)
13824520Snw141292 		return (retcode);
13834520Snw141292 
13844520Snw141292 	atomic_inc_32(&state->qinflight);
13854520Snw141292 
13864520Snw141292 	/*
13874520Snw141292 	 * Reap as many requests as we can _without_ waiting
13884520Snw141292 	 *
13894520Snw141292 	 * We do this to prevent any possible TCP socket buffer
13904520Snw141292 	 * starvation deadlocks.
13914520Snw141292 	 */
13924520Snw141292 	(void) memset(&tv, 0, sizeof (tv));
13934520Snw141292 	while (idmap_get_adobject_batch(state->qadh, &tv) == 0)
13944520Snw141292 		;
13954520Snw141292 
13964520Snw141292 	return (IDMAP_SUCCESS);
13974520Snw141292 }
13984520Snw141292 
13994520Snw141292 idmap_retcode
14004520Snw141292 idmap_name2sid_batch_add1(idmap_query_state_t *state,
14014520Snw141292 	const char *name, const char *dname,
14024520Snw141292 	char **sid, rid_t *rid, int *sid_type, idmap_retcode *rc)
14034520Snw141292 {
14044520Snw141292 	idmap_retcode	retcode;
14054520Snw141292 	int		flen, samAcctNameLen;
14064520Snw141292 	char		*filter = NULL;
14074520Snw141292 	char		*basedn = NULL;
14084520Snw141292 	char		*cp;
14094520Snw141292 
14104520Snw141292 	/*
14114520Snw141292 	 * Strategy: search [the global catalog] for user/group by
14124520Snw141292 	 * sAMAccountName = user/groupname with base DN derived from the
14134520Snw141292 	 * domain name.  The objectSid and objectClass of the result are
14144520Snw141292 	 * all we need to figure out the SID of the user/group and
14154520Snw141292 	 * whether it is a user or a group.
14164520Snw141292 	 */
14174520Snw141292 
14184520Snw141292 	/*
14194520Snw141292 	 * Handle optional domain parameter and default domain
14204520Snw141292 	 * semantics.  The get a basedn from the domainname.
14214520Snw141292 	 */
14224520Snw141292 	if (dname == NULL || *dname != '\0') {
14234520Snw141292 		/* domain name not given separately */
14244520Snw141292 		if ((cp = strchr(name, '@')) == NULL) {
14254520Snw141292 			/* nor is the name qualified */
14264520Snw141292 			dname = state->qadh->owner->dflt_w2k_dom;
14274520Snw141292 			basedn = state->qadh->owner->basedn;
14284520Snw141292 			samAcctNameLen = strlen(name);
14294520Snw141292 		} else {
14304520Snw141292 			/* the name is qualified */
14314520Snw141292 			/* LINTED */
14324520Snw141292 			samAcctNameLen = cp - name;
14334520Snw141292 			dname = cp + 1;
14344520Snw141292 		}
14354520Snw141292 	}
14364520Snw141292 
14374520Snw141292 	if (basedn == NULL)
14384520Snw141292 		basedn = dns2dn(dname);
14394520Snw141292 
14404520Snw141292 	/* Assemble filter */
14414520Snw141292 	flen = snprintf(NULL, 0, SANFILTER, samAcctNameLen, name) + 1;
14424520Snw141292 	if ((filter = (char *)malloc(flen)) == NULL) {
14434520Snw141292 		if (basedn != state->qadh->owner->basedn)
14444520Snw141292 			free(basedn);
14454520Snw141292 		return (IDMAP_ERR_MEMORY);
14464520Snw141292 	}
14474520Snw141292 	(void) snprintf(filter, flen, SANFILTER, samAcctNameLen, name);
14484520Snw141292 
14494520Snw141292 	retcode = idmap_batch_add1(state, 1, filter, basedn,
14504520Snw141292 	    sid, NULL, rid, sid_type, rc);
14514520Snw141292 
14524520Snw141292 	if (basedn != state->qadh->owner->basedn)
14534520Snw141292 		free(basedn);
14544520Snw141292 	free(filter);
14554520Snw141292 
14564520Snw141292 	return (retcode);
14574520Snw141292 }
14584520Snw141292 
14594520Snw141292 idmap_retcode
14604520Snw141292 idmap_sid2name_batch_add1(idmap_query_state_t *state,
14614520Snw141292 	const char *sid, const rid_t *rid,
14624520Snw141292 	char **name, char **dname, int *sid_type, idmap_retcode *rc)
14634520Snw141292 {
14644520Snw141292 	idmap_retcode	retcode;
14654520Snw141292 	int		flen, ret;
14664520Snw141292 	char		*filter = NULL;
14674520Snw141292 	char		cbinsid[MAXHEXBINSID + 1];
14684520Snw141292 
14694520Snw141292 	/*
14704520Snw141292 	 * Strategy: search [the global catalog] for user/group by
14714520Snw141292 	 * objectSid = SID with empty base DN.  The DN, sAMAccountName
14724520Snw141292 	 * and objectClass of the result are all we need to figure out
14734520Snw141292 	 * the name of the SID and whether it is a user, a group or a
14744520Snw141292 	 * computer.
14754520Snw141292 	 */
14764520Snw141292 
14774520Snw141292 	ret = idmap_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid));
14784520Snw141292 	if (ret != 0)
14794520Snw141292 		return (IDMAP_ERR_SID);
14804520Snw141292 
14814520Snw141292 	/* Assemble filter */
14824520Snw141292 	flen = snprintf(NULL, 0, OBJECTSIDFILTER, cbinsid) + 1;
14834520Snw141292 	if ((filter = (char *)malloc(flen)) == NULL)
14844520Snw141292 		return (IDMAP_ERR_MEMORY);
14854520Snw141292 	(void) snprintf(filter, flen, OBJECTSIDFILTER, cbinsid);
14864520Snw141292 
14874520Snw141292 	retcode = idmap_batch_add1(state, 0, filter, NULL, name, dname,
14884520Snw141292 	    NULL, sid_type, rc);
14894520Snw141292 
14904520Snw141292 	free(filter);
14914520Snw141292 
14924520Snw141292 	return (retcode);
14934520Snw141292 }
1494