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 /*
235908Sjp151216  * Copyright 2008 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>
505696Snw141292 #include <sys/u8_textprep.h>
51*6616Sdm199847 #include "nldaputils.h"
524520Snw141292 #include "idmapd.h"
534520Snw141292 
544520Snw141292 /*
554520Snw141292  * Internal data structures for this code
564520Snw141292  */
574520Snw141292 
584520Snw141292 /* Attribute names and filter format strings */
595447Snw141292 #define	SAN		"sAMAccountName"
605447Snw141292 #define	OBJSID		"objectSid"
615447Snw141292 #define	OBJCLASS	"objectClass"
624520Snw141292 #define	SANFILTER	"(sAMAccountName=%.*s)"
635447Snw141292 #define	OBJSIDFILTER	"(objectSid=%s)"
644520Snw141292 
654520Snw141292 /*
664520Snw141292  * This should really be in some <sys/sid.h> file or so; we have a
674520Snw141292  * private version of sid_t, and so must other components of ON until we
684520Snw141292  * rationalize this.
694520Snw141292  */
704520Snw141292 typedef struct sid {
714520Snw141292 	uchar_t		version;
724520Snw141292 	uchar_t		sub_authority_count;
734520Snw141292 	uint64_t	authority;  /* really, 48-bits */
744520Snw141292 	rid_t		sub_authorities[SID_MAX_SUB_AUTHORITIES];
754520Snw141292 } sid_t;
764520Snw141292 
774520Snw141292 /* A single DS */
784520Snw141292 typedef struct ad_host {
794520Snw141292 	struct ad_host		*next;
804520Snw141292 	ad_t			*owner;		/* ad_t to which this belongs */
814520Snw141292 	pthread_mutex_t		lock;
824520Snw141292 	LDAP			*ld;		/* LDAP connection */
834520Snw141292 	uint32_t		ref;		/* ref count */
844520Snw141292 	time_t			idletime;	/* time since last activity */
854520Snw141292 	int			dead;		/* error on LDAP connection */
864520Snw141292 	/*
874520Snw141292 	 * Used to distinguish between different instances of LDAP
884520Snw141292 	 * connections to this same DS.  We need this so we never mix up
894520Snw141292 	 * results for a given msgID from one connection with those of
904520Snw141292 	 * another earlier connection where two batch state structures
914520Snw141292 	 * share this ad_host object but used different LDAP connections
924520Snw141292 	 * to send their LDAP searches.
934520Snw141292 	 */
944520Snw141292 	uint64_t		generation;
954520Snw141292 
964520Snw141292 	/* LDAP DS info */
974520Snw141292 	char			*host;
984520Snw141292 	int			port;
994520Snw141292 
1004520Snw141292 	/* hardwired to SASL GSSAPI only for now */
1014520Snw141292 	char			*saslmech;
1024520Snw141292 	unsigned		saslflags;
1036531Sjp151216 
1046531Sjp151216 	/* Number of outstanding search requests */
1056531Sjp151216 	uint32_t		max_requests;
1066531Sjp151216 	uint32_t		num_requests;
1074520Snw141292 } ad_host_t;
1084520Snw141292 
1094520Snw141292 /* A set of DSs for a given AD partition; ad_t typedef comes from  adutil.h */
1104520Snw141292 struct ad {
1114520Snw141292 	char			*dflt_w2k_dom;	/* used to qualify bare names */
1124520Snw141292 	pthread_mutex_t		lock;
1134520Snw141292 	uint32_t		ref;
1145317Sjp151216 	ad_host_t		*last_adh;
1154520Snw141292 	idmap_ad_partition_t	partition;	/* Data or global catalog? */
1164520Snw141292 };
1174520Snw141292 
1184520Snw141292 /*
1194520Snw141292  * A place to put the results of a batched (async) query
1204520Snw141292  *
1214520Snw141292  * There is one of these for every query added to a batch object
1224520Snw141292  * (idmap_query_state, see below).
1234520Snw141292  */
1244520Snw141292 typedef struct idmap_q {
1255447Snw141292 	/*
1265447Snw141292 	 * data used for validating search result entries for name->SID
1275731Sbaban 	 * lookups
1285447Snw141292 	 */
1295696Snw141292 	char			*ecanonname;	/* expected canon name */
1305696Snw141292 	char			*edomain;	/* expected domain name */
1315731Sbaban 	int			eunixtype;	/* expected unix type */
1325447Snw141292 	/* results */
1335696Snw141292 	char			**canonname;	/* actual canon name */
1344520Snw141292 	char			**domain;	/* name of domain of object */
1355731Sbaban 	char			**sid;		/* stringified SID */
1365731Sbaban 	rid_t			*rid;		/* RID */
1375731Sbaban 	int			*sid_type;	/* user or group SID? */
1385731Sbaban 	char			**unixname;	/* unixname for name mapping */
1396386Sjp151216 	char			**dn;		/* DN of entry */
1406386Sjp151216 	char			**attr;		/* Attr for name mapping */
1416386Sjp151216 	char			**value;	/* value for name mapping */
1424520Snw141292 	idmap_retcode		*rc;
1435447Snw141292 
1445447Snw141292 	/* lookup state */
1454520Snw141292 	int			msgid;		/* LDAP message ID */
1466531Sjp151216 	/*
1476531Sjp151216 	 * The LDAP search entry result is placed here to be processed
1486531Sjp151216 	 * when the search done result is received.
1496531Sjp151216 	 */
1506531Sjp151216 	LDAPMessage		*search_res;	/* The LDAP search result */
1514520Snw141292 } idmap_q_t;
1524520Snw141292 
1534520Snw141292 /* Batch context structure; typedef is in header file */
1544520Snw141292 struct idmap_query_state {
1554520Snw141292 	idmap_query_state_t	*next;
1564520Snw141292 	int			qcount;		/* how many queries */
1574884Sjp151216 	int			ref_cnt;	/* reference count */
1584884Sjp151216 	pthread_cond_t		cv;		/* Condition wait variable */
1594520Snw141292 	uint32_t		qlastsent;
1604520Snw141292 	uint32_t		qinflight;	/* how many queries in flight */
1614520Snw141292 	uint16_t		qdead;		/* oops, lost LDAP connection */
1624520Snw141292 	ad_host_t		*qadh;		/* LDAP connection */
1634520Snw141292 	uint64_t		qadh_gen;	/* same as qadh->generation */
1645731Sbaban 	const char		*ad_unixuser_attr;
1655731Sbaban 	const char		*ad_unixgroup_attr;
1664520Snw141292 	idmap_q_t		queries[1];	/* array of query results */
1674520Snw141292 };
1684520Snw141292 
1694520Snw141292 /*
1704520Snw141292  * List of query state structs -- needed so we can "route" LDAP results
1714520Snw141292  * to the right context if multiple threads should be using the same
1724520Snw141292  * connection concurrently
1734520Snw141292  */
1744520Snw141292 static idmap_query_state_t	*qstatehead = NULL;
1754520Snw141292 static pthread_mutex_t		qstatelock = PTHREAD_MUTEX_INITIALIZER;
1764520Snw141292 
1774520Snw141292 /*
1784520Snw141292  * List of DSs, needed by the idle connection reaper thread
1794520Snw141292  */
1804520Snw141292 static ad_host_t	*host_head = NULL;
1814644Sbaban static pthread_t	reaperid = 0;
1824520Snw141292 static pthread_mutex_t	adhostlock = PTHREAD_MUTEX_INITIALIZER;
1834520Snw141292 
1844884Sjp151216 
1854884Sjp151216 static void
1864884Sjp151216 idmap_lookup_unlock_batch(idmap_query_state_t **state);
1874884Sjp151216 
1885317Sjp151216 static void
1895317Sjp151216 delete_ds(ad_t *ad, const char *host, int port);
1904884Sjp151216 
1914520Snw141292 /*ARGSUSED*/
1924520Snw141292 static int
1935908Sjp151216 idmap_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts)
1945908Sjp151216 {
1954520Snw141292 	sasl_interact_t	*interact;
1964520Snw141292 
1974520Snw141292 	if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE)
1984520Snw141292 		return (LDAP_PARAM_ERROR);
1994520Snw141292 
2004520Snw141292 	/* There should be no extra arguemnts for SASL/GSSAPI authentication */
2014520Snw141292 	for (interact = prompts; interact->id != SASL_CB_LIST_END;
2025908Sjp151216 	    interact++) {
2034520Snw141292 		interact->result = NULL;
2044520Snw141292 		interact->len = 0;
2054520Snw141292 	}
2064520Snw141292 	return (LDAP_SUCCESS);
2074520Snw141292 }
2084520Snw141292 
2094520Snw141292 
2104520Snw141292 /*
2114520Snw141292  * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other
2125447Snw141292  * attributes (CN, etc...).  We don't need the reverse, for now.
2134520Snw141292  */
2144520Snw141292 static
2154520Snw141292 char *
2164520Snw141292 dn2dns(const char *dn)
2174520Snw141292 {
2184520Snw141292 	char **rdns = NULL;
2194520Snw141292 	char **attrs = NULL;
2204520Snw141292 	char **labels = NULL;
2214520Snw141292 	char *dns = NULL;
2224520Snw141292 	char **rdn, **attr, **label;
2234520Snw141292 	int maxlabels = 5;
2244520Snw141292 	int nlabels = 0;
2254520Snw141292 	int dnslen;
2264520Snw141292 
2274520Snw141292 	/*
2284520Snw141292 	 * There is no reverse of ldap_dns_to_dn() in our libldap, so we
2294520Snw141292 	 * have to do the hard work here for now.
2304520Snw141292 	 */
2314520Snw141292 
2324520Snw141292 	/*
2334520Snw141292 	 * This code is much too liberal: it looks for "dc" attributes
2344520Snw141292 	 * in all RDNs of the DN.  In theory this could cause problems
2354520Snw141292 	 * if people were to use "dc" in nodes other than the root of
2364520Snw141292 	 * the tree, but in practice noone, least of all Active
2374520Snw141292 	 * Directory, does that.
2384520Snw141292 	 *
2394520Snw141292 	 * On the other hand, this code is much too conservative: it
2404520Snw141292 	 * does not make assumptions about ldap_explode_dn(), and _that_
2414520Snw141292 	 * is the true for looking at every attr of every RDN.
2424520Snw141292 	 *
2434520Snw141292 	 * Since we only ever look at dc and those must be DNS labels,
2444520Snw141292 	 * at least until we get around to supporting IDN here we
2454520Snw141292 	 * shouldn't see escaped labels from AD nor from libldap, though
2464520Snw141292 	 * the spec (RFC2253) does allow libldap to escape things that
2474520Snw141292 	 * don't need escaping -- if that should ever happen then
2484520Snw141292 	 * libldap will need a spanking, and we can take care of that.
2494520Snw141292 	 */
2504520Snw141292 
2514520Snw141292 	/* Explode a DN into RDNs */
2524520Snw141292 	if ((rdns = ldap_explode_dn(dn, 0)) == NULL)
2534520Snw141292 		return (NULL);
2544520Snw141292 
2554520Snw141292 	labels = calloc(maxlabels + 1, sizeof (char *));
2564520Snw141292 	label = labels;
2574520Snw141292 
2584520Snw141292 	for (rdn = rdns; *rdn != NULL; rdn++) {
2594520Snw141292 		if (attrs != NULL)
2604520Snw141292 			ldap_value_free(attrs);
2614520Snw141292 
2624520Snw141292 		/* Explode each RDN, look for DC attr, save val as DNS label */
2634520Snw141292 		if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL)
2644520Snw141292 			goto done;
2654520Snw141292 
2664520Snw141292 		for (attr = attrs; *attr != NULL; attr++) {
2674520Snw141292 			if (strncasecmp(*attr, "dc=", 3) != 0)
2684520Snw141292 				continue;
2694520Snw141292 
2704520Snw141292 			/* Found a DNS label */
2714520Snw141292 			labels[nlabels++] = strdup((*attr) + 3);
2724520Snw141292 
2734520Snw141292 			if (nlabels == maxlabels) {
2744520Snw141292 				char **tmp;
2754520Snw141292 				tmp = realloc(labels,
2764520Snw141292 				    sizeof (char *) * (maxlabels + 1));
2774520Snw141292 
2784520Snw141292 				if (tmp == NULL)
2794520Snw141292 					goto done;
2804520Snw141292 
2814520Snw141292 				labels = tmp;
2824520Snw141292 				labels[nlabels] = NULL;
2834520Snw141292 			}
2844520Snw141292 
2854520Snw141292 			/* There should be just one DC= attr per-RDN */
2864520Snw141292 			break;
2874520Snw141292 		}
2884520Snw141292 	}
2894520Snw141292 
2904520Snw141292 	/*
2914520Snw141292 	 * Got all the labels, now join with '.'
2924520Snw141292 	 *
2934520Snw141292 	 * We need room for nlabels - 1 periods ('.'), one nul
2944520Snw141292 	 * terminator, and the strlen() of each label.
2954520Snw141292 	 */
2964520Snw141292 	dnslen = nlabels;
2974520Snw141292 	for (label = labels; *label != NULL; label++)
2984520Snw141292 		dnslen += strlen(*label);
2994520Snw141292 
3004520Snw141292 	if ((dns = malloc(dnslen)) == NULL)
3014520Snw141292 		goto done;
3024520Snw141292 
3034520Snw141292 	*dns = '\0';
3044520Snw141292 
3054520Snw141292 	for (label = labels; *label != NULL; label++) {
3064520Snw141292 		(void) strlcat(dns, *label, dnslen);
3074520Snw141292 		/*
3084520Snw141292 		 * NOTE: the last '.' won't be appended -- there's no room
3094520Snw141292 		 * for it!
3104520Snw141292 		 */
3114520Snw141292 		(void) strlcat(dns, ".", dnslen);
3124520Snw141292 	}
3134520Snw141292 
3144520Snw141292 done:
3154520Snw141292 	if (labels != NULL) {
3164520Snw141292 		for (label = labels; *label != NULL; label++)
3174520Snw141292 			free(*label);
3184520Snw141292 		free(labels);
3194520Snw141292 	}
3204520Snw141292 	if (attrs != NULL)
3214520Snw141292 		ldap_value_free(attrs);
3224520Snw141292 	if (rdns != NULL)
3234520Snw141292 		ldap_value_free(rdns);
3244520Snw141292 
3254520Snw141292 	return (dns);
3264520Snw141292 }
3274520Snw141292 
3284520Snw141292 /*
3294520Snw141292  * Keep connection management simple for now, extend or replace later
3304520Snw141292  * with updated libsldap code.
3314520Snw141292  */
3324520Snw141292 #define	ADREAPERSLEEP	60
3334520Snw141292 #define	ADCONN_TIME	300
3344520Snw141292 
3354520Snw141292 /*
3364520Snw141292  * Idle connection reaping side of connection management
3374520Snw141292  *
3384520Snw141292  * Every minute wake up and look for connections that have been idle for
3394520Snw141292  * five minutes or more and close them.
3404520Snw141292  */
3414520Snw141292 /*ARGSUSED*/
3424520Snw141292 static
3434520Snw141292 void
3444520Snw141292 adreaper(void *arg)
3454520Snw141292 {
3464520Snw141292 	ad_host_t	*adh;
3474520Snw141292 	time_t		now;
3484520Snw141292 	timespec_t	ts;
3494520Snw141292 
3504520Snw141292 	ts.tv_sec = ADREAPERSLEEP;
3514520Snw141292 	ts.tv_nsec = 0;
3524520Snw141292 
3534520Snw141292 	for (;;) {
3544520Snw141292 		/*
3554520Snw141292 		 * nanosleep(3RT) is thead-safe (no SIGALRM) and more
3564520Snw141292 		 * portable than usleep(3C)
3574520Snw141292 		 */
3584520Snw141292 		(void) nanosleep(&ts, NULL);
3594520Snw141292 		(void) pthread_mutex_lock(&adhostlock);
3604520Snw141292 		now = time(NULL);
3614520Snw141292 		for (adh = host_head; adh != NULL; adh = adh->next) {
3624520Snw141292 			(void) pthread_mutex_lock(&adh->lock);
3634520Snw141292 			if (adh->ref == 0 && adh->idletime != 0 &&
3644520Snw141292 			    adh->idletime + ADCONN_TIME < now) {
3654520Snw141292 				if (adh->ld) {
3664520Snw141292 					(void) ldap_unbind(adh->ld);
3674520Snw141292 					adh->ld = NULL;
3684520Snw141292 					adh->idletime = 0;
3694520Snw141292 					adh->ref = 0;
3704520Snw141292 				}
3714520Snw141292 			}
3724520Snw141292 			(void) pthread_mutex_unlock(&adh->lock);
3734520Snw141292 		}
3744520Snw141292 		(void) pthread_mutex_unlock(&adhostlock);
3754520Snw141292 	}
3764520Snw141292 }
3774520Snw141292 
3784520Snw141292 int
3794520Snw141292 idmap_ad_alloc(ad_t **new_ad, const char *default_domain,
3804520Snw141292 		idmap_ad_partition_t part)
3814520Snw141292 {
3824520Snw141292 	ad_t *ad;
3834520Snw141292 
3844520Snw141292 	*new_ad = NULL;
3854520Snw141292 
3864520Snw141292 	if ((default_domain == NULL || *default_domain == '\0') &&
3874520Snw141292 	    part != IDMAP_AD_GLOBAL_CATALOG)
3884520Snw141292 		return (-1);
3894520Snw141292 
3904520Snw141292 	if ((ad = calloc(1, sizeof (ad_t))) == NULL)
3914520Snw141292 		return (-1);
3924520Snw141292 
3934520Snw141292 	ad->ref = 1;
3944520Snw141292 	ad->partition = part;
3954520Snw141292 
3964520Snw141292 	if (default_domain == NULL)
3974520Snw141292 		default_domain = "";
3984520Snw141292 
3994520Snw141292 	if ((ad->dflt_w2k_dom = strdup(default_domain)) == NULL)
4004520Snw141292 		goto err;
4014520Snw141292 
4024520Snw141292 	if (pthread_mutex_init(&ad->lock, NULL) != 0)
4034520Snw141292 		goto err;
4044520Snw141292 
4054520Snw141292 	*new_ad = ad;
4064520Snw141292 
4074520Snw141292 	return (0);
4084520Snw141292 err:
4094520Snw141292 	if (ad->dflt_w2k_dom != NULL)
4104520Snw141292 		free(ad->dflt_w2k_dom);
4114520Snw141292 	free(ad);
4124520Snw141292 	return (-1);
4134520Snw141292 }
4144520Snw141292 
4154520Snw141292 
4164520Snw141292 void
4174520Snw141292 idmap_ad_free(ad_t **ad)
4184520Snw141292 {
4194520Snw141292 	ad_host_t *p;
4205317Sjp151216 	ad_host_t *prev;
4214520Snw141292 
4224520Snw141292 	if (ad == NULL || *ad == NULL)
4234520Snw141292 		return;
4244520Snw141292 
4254520Snw141292 	(void) pthread_mutex_lock(&(*ad)->lock);
4264520Snw141292 
4274520Snw141292 	if (atomic_dec_32_nv(&(*ad)->ref) > 0) {
4284520Snw141292 		(void) pthread_mutex_unlock(&(*ad)->lock);
4294520Snw141292 		*ad = NULL;
4304520Snw141292 		return;
4314520Snw141292 	}
4324520Snw141292 
4335317Sjp151216 	(void) pthread_mutex_lock(&adhostlock);
4345317Sjp151216 	prev = NULL;
4355317Sjp151216 	p = host_head;
4365317Sjp151216 	while (p != NULL) {
4375317Sjp151216 		if (p->owner != (*ad)) {
4385317Sjp151216 			prev = p;
4395317Sjp151216 			p = p->next;
4404520Snw141292 			continue;
4415317Sjp151216 		} else {
4425317Sjp151216 			delete_ds((*ad), p->host, p->port);
4435317Sjp151216 			if (prev == NULL)
4445317Sjp151216 				p = host_head;
4455317Sjp151216 			else
4465317Sjp151216 				p = prev->next;
4475317Sjp151216 		}
4484520Snw141292 	}
4495317Sjp151216 	(void) pthread_mutex_unlock(&adhostlock);
4504520Snw141292 
4514520Snw141292 	(void) pthread_mutex_unlock(&(*ad)->lock);
4524520Snw141292 	(void) pthread_mutex_destroy(&(*ad)->lock);
4534520Snw141292 
4545447Snw141292 	free((*ad)->dflt_w2k_dom);
4554520Snw141292 	free(*ad);
4564520Snw141292 
4574520Snw141292 	*ad = NULL;
4584520Snw141292 }
4594520Snw141292 
4605317Sjp151216 
4614520Snw141292 static
4624520Snw141292 int
4635968Snw141292 idmap_open_conn(ad_host_t *adh, int timeoutsecs)
4644520Snw141292 {
4655317Sjp151216 	int zero = 0;
4665317Sjp151216 	int ldversion, rc;
4675968Snw141292 	int timeoutms = timeoutsecs * 1000;
4685317Sjp151216 
4695317Sjp151216 	if (adh == NULL)
4705317Sjp151216 		return (0);
4714520Snw141292 
4725317Sjp151216 	(void) pthread_mutex_lock(&adh->lock);
4735317Sjp151216 
4745317Sjp151216 	if (!adh->dead && adh->ld != NULL)
4755317Sjp151216 		/* done! */
4765317Sjp151216 		goto out;
4775317Sjp151216 
4785317Sjp151216 	if (adh->ld != NULL) {
4794520Snw141292 		(void) ldap_unbind(adh->ld);
4804520Snw141292 		adh->ld = NULL;
4814520Snw141292 	}
4826531Sjp151216 	adh->num_requests = 0;
4834520Snw141292 
4845317Sjp151216 	atomic_inc_64(&adh->generation);
4854520Snw141292 
4865317Sjp151216 	/* Open and bind an LDAP connection */
4875317Sjp151216 	adh->ld = ldap_init(adh->host, adh->port);
4885317Sjp151216 	if (adh->ld == NULL) {
4895317Sjp151216 		idmapdlog(LOG_INFO, "ldap_init() to server "
4905317Sjp151216 		    "%s port %d failed. (%s)", adh->host,
4915317Sjp151216 		    adh->port, strerror(errno));
4925317Sjp151216 		goto out;
4935317Sjp151216 	}
4945317Sjp151216 	ldversion = LDAP_VERSION3;
4955317Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion);
4965317Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
4975317Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero);
4985317Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero);
4995317Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
5005317Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
5015317Sjp151216 	rc = ldap_sasl_interactive_bind_s(adh->ld, "" /* binddn */,
5025317Sjp151216 	    adh->saslmech, NULL, NULL, adh->saslflags, &idmap_saslcallback,
5035317Sjp151216 	    NULL);
5044520Snw141292 
5055317Sjp151216 	if (rc != LDAP_SUCCESS) {
5065317Sjp151216 		(void) ldap_unbind(adh->ld);
5075317Sjp151216 		adh->ld = NULL;
5085317Sjp151216 		idmapdlog(LOG_INFO, "ldap_sasl_interactive_bind_s() to server "
5095317Sjp151216 		    "%s port %d failed. (%s)", adh->host, adh->port,
5105317Sjp151216 		    ldap_err2string(rc));
5114520Snw141292 	}
5124520Snw141292 
5135317Sjp151216 	idmapdlog(LOG_DEBUG, "Using global catalog server %s:%d",
5145317Sjp151216 	    adh->host, adh->port);
5154520Snw141292 
5165317Sjp151216 out:
5175317Sjp151216 	if (adh->ld != NULL) {
5185317Sjp151216 		atomic_inc_32(&adh->ref);
5195317Sjp151216 		adh->idletime = time(NULL);
5205317Sjp151216 		adh->dead = 0;
5215317Sjp151216 		(void) pthread_mutex_unlock(&adh->lock);
5225317Sjp151216 		return (1);
5235317Sjp151216 	}
5245317Sjp151216 
5255317Sjp151216 	(void) pthread_mutex_unlock(&adh->lock);
5265317Sjp151216 	return (0);
5274520Snw141292 }
5284520Snw141292 
5294520Snw141292 
5304520Snw141292 /*
5314520Snw141292  * Connection management: find an open connection or open one
5324520Snw141292  */
5334520Snw141292 static
5344520Snw141292 ad_host_t *
5355317Sjp151216 idmap_get_conn(ad_t *ad)
5364520Snw141292 {
5374520Snw141292 	ad_host_t	*adh = NULL;
5385968Snw141292 	int		tries;
5395968Snw141292 	int		dscount = 0;
5405968Snw141292 	int		timeoutsecs = IDMAPD_LDAP_OPEN_TIMEOUT;
5414520Snw141292 
5425317Sjp151216 retry:
5434520Snw141292 	(void) pthread_mutex_lock(&adhostlock);
5444520Snw141292 
5455968Snw141292 	if (host_head == NULL) {
5465968Snw141292 		(void) pthread_mutex_unlock(&adhostlock);
5475317Sjp151216 		goto out;
5485968Snw141292 	}
5495317Sjp151216 
5505968Snw141292 	if (dscount == 0) {
5515968Snw141292 		/*
5525968Snw141292 		 * First try: count the number of DSes.
5535968Snw141292 		 *
5545968Snw141292 		 * Integer overflow is not an issue -- we can't have so many
5555968Snw141292 		 * DSes because they won't fit even DNS over TCP, and SMF
5565968Snw141292 		 * shouldn't let you set so many.
5575968Snw141292 		 */
5585968Snw141292 		for (adh = host_head, tries = 0; adh != NULL; adh = adh->next) {
5595968Snw141292 			if (adh->owner == ad)
5605968Snw141292 				dscount++;
5615968Snw141292 		}
5625968Snw141292 
5635968Snw141292 		if (dscount == 0) {
5645968Snw141292 			(void) pthread_mutex_unlock(&adhostlock);
5655968Snw141292 			goto out;
5665968Snw141292 		}
5675968Snw141292 
5685968Snw141292 		tries = dscount * 3;	/* three tries per-ds */
5695968Snw141292 
5705968Snw141292 		/*
5715968Snw141292 		 * Begin round-robin at the next DS in the list after the last
5725968Snw141292 		 * one that we had a connection to, else start with the first
5735968Snw141292 		 * DS in the list.
5745968Snw141292 		 */
5755968Snw141292 		adh = ad->last_adh;
5764520Snw141292 	}
5774520Snw141292 
5785317Sjp151216 	/*
5795968Snw141292 	 * Round-robin -- pick the next one on the list; if the list
5805968Snw141292 	 * changes on us, no big deal, we'll just potentially go
5815968Snw141292 	 * around the wrong number of times.
5825317Sjp151216 	 */
5835968Snw141292 	for (;;) {
5846017Snw141292 		if (adh != NULL && adh->ld != NULL && !adh->dead)
5856017Snw141292 			break;
5865968Snw141292 		if (adh == NULL || (adh = adh->next) == NULL)
5875968Snw141292 			adh = host_head;
5885968Snw141292 		if (adh->owner == ad)
5895317Sjp151216 			break;
5905317Sjp151216 	}
5915317Sjp151216 
5925968Snw141292 	ad->last_adh = adh;
5935968Snw141292 	(void) pthread_mutex_unlock(&adhostlock);
5945968Snw141292 
5955968Snw141292 
5965968Snw141292 	/* Found suitable DS, open it if not already opened */
5975968Snw141292 	if (idmap_open_conn(adh, timeoutsecs))
5985968Snw141292 		return (adh);
5995317Sjp151216 
6005968Snw141292 	tries--;
6015968Snw141292 
6025968Snw141292 	if ((tries % dscount) == 0)
6035968Snw141292 		timeoutsecs *= 2;
6045968Snw141292 
6055968Snw141292 	if (tries > 0)
6065968Snw141292 		goto retry;
6074520Snw141292 
6085317Sjp151216 out:
6095968Snw141292 	idmapdlog(LOG_NOTICE, "Couldn't open an LDAP connection to any global "
6105317Sjp151216 	    "catalog server!");
6115317Sjp151216 
6125317Sjp151216 	return (NULL);
6134520Snw141292 }
6144520Snw141292 
6154520Snw141292 static
6164520Snw141292 void
6174520Snw141292 idmap_release_conn(ad_host_t *adh)
6184520Snw141292 {
6194520Snw141292 	(void) pthread_mutex_lock(&adh->lock);
6204520Snw141292 	if (atomic_dec_32_nv(&adh->ref) == 0)
6214520Snw141292 		adh->idletime = time(NULL);
6224520Snw141292 	(void) pthread_mutex_unlock(&adh->lock);
6234520Snw141292 }
6244520Snw141292 
6254520Snw141292 /*
6264520Snw141292  * Take ad_host_config_t information, create a ad_host_t,
6274520Snw141292  * populate it and add it to the list of hosts.
6284520Snw141292  */
6294520Snw141292 
6304520Snw141292 int
6314520Snw141292 idmap_add_ds(ad_t *ad, const char *host, int port)
6324520Snw141292 {
6334520Snw141292 	ad_host_t	*p;
6344520Snw141292 	ad_host_t	*new = NULL;
6354520Snw141292 	int		ret = -1;
6364520Snw141292 
6374520Snw141292 	if (port == 0)
6384520Snw141292 		port = (int)ad->partition;
6394520Snw141292 
6404520Snw141292 	(void) pthread_mutex_lock(&adhostlock);
6414520Snw141292 	for (p = host_head; p != NULL; p = p->next) {
6424520Snw141292 		if (p->owner != ad)
6434520Snw141292 			continue;
6444520Snw141292 
6454520Snw141292 		if (strcmp(host, p->host) == 0 && p->port == port) {
6464520Snw141292 			/* already added */
6475317Sjp151216 			ret = 0;
6484520Snw141292 			goto err;
6494520Snw141292 		}
6504520Snw141292 	}
6514520Snw141292 
6524520Snw141292 	/* add new entry */
6534520Snw141292 	new = (ad_host_t *)calloc(1, sizeof (ad_host_t));
6544520Snw141292 	if (new == NULL)
6554520Snw141292 		goto err;
6564520Snw141292 	new->owner = ad;
6574520Snw141292 	new->port = port;
6584520Snw141292 	new->dead = 0;
6596531Sjp151216 	new->max_requests = 80;
6606531Sjp151216 	new->num_requests = 0;
6614520Snw141292 	if ((new->host = strdup(host)) == NULL)
6624520Snw141292 		goto err;
6634520Snw141292 
6644520Snw141292 	/* default to SASL GSSAPI only for now */
6654520Snw141292 	new->saslflags = LDAP_SASL_INTERACTIVE;
6664520Snw141292 	new->saslmech = "GSSAPI";
6674520Snw141292 
6684520Snw141292 	if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) {
6694520Snw141292 		free(new->host);
6704520Snw141292 		new->host = NULL;
6714520Snw141292 		errno = ret;
6724520Snw141292 		ret = -1;
6734520Snw141292 		goto err;
6744520Snw141292 	}
6754520Snw141292 
6764520Snw141292 	/* link in */
6774520Snw141292 	new->next = host_head;
6784520Snw141292 	host_head = new;
6794520Snw141292 
6804520Snw141292 	/* Start reaper if it doesn't exist */
6814520Snw141292 	if (reaperid == 0)
6824520Snw141292 		(void) pthread_create(&reaperid, NULL,
6834520Snw141292 		    (void *(*)(void *))adreaper, (void *)NULL);
6844520Snw141292 
6854520Snw141292 err:
6864520Snw141292 	(void) pthread_mutex_unlock(&adhostlock);
6874520Snw141292 
6884520Snw141292 	if (ret != 0 && new != NULL) {
6894520Snw141292 		if (new->host != NULL) {
6904520Snw141292 			(void) pthread_mutex_destroy(&new->lock);
6914520Snw141292 			free(new->host);
6924520Snw141292 		}
6934520Snw141292 		free(new);
6944520Snw141292 	}
6954520Snw141292 
6964520Snw141292 	return (ret);
6974520Snw141292 }
6984520Snw141292 
6994520Snw141292 /*
7005317Sjp151216  * Free a DS configuration.
7015317Sjp151216  * Caller must lock the adhostlock mutex
7024520Snw141292  */
7035317Sjp151216 static void
7045317Sjp151216 delete_ds(ad_t *ad, const char *host, int port)
7054520Snw141292 {
7064520Snw141292 	ad_host_t	**p, *q;
7074520Snw141292 
7084520Snw141292 	for (p = &host_head; *p != NULL; p = &((*p)->next)) {
7094520Snw141292 		if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 ||
7104520Snw141292 		    (*p)->port != port)
7114520Snw141292 			continue;
7124520Snw141292 		/* found */
7135317Sjp151216 		if ((*p)->ref > 0)
7144520Snw141292 			break;	/* still in use */
7154520Snw141292 
7164520Snw141292 		q = *p;
7174520Snw141292 		*p = (*p)->next;
7184520Snw141292 
7194520Snw141292 		(void) pthread_mutex_destroy(&q->lock);
7204520Snw141292 
7214520Snw141292 		if (q->ld)
7224520Snw141292 			(void) ldap_unbind(q->ld);
7234520Snw141292 		if (q->host)
7244520Snw141292 			free(q->host);
7254520Snw141292 		free(q);
7264520Snw141292 		break;
7274520Snw141292 	}
7285317Sjp151216 
7294520Snw141292 }
7304520Snw141292 
7315317Sjp151216 
7324520Snw141292 /*
7334520Snw141292  * Convert a binary SID in a BerValue to a sid_t
7344520Snw141292  */
7354520Snw141292 static
7364520Snw141292 int
7374520Snw141292 idmap_getsid(BerValue *bval, sid_t *sidp)
7384520Snw141292 {
7394520Snw141292 	int		i, j;
7404520Snw141292 	uchar_t		*v;
7414520Snw141292 	uint32_t	a;
7424520Snw141292 
7434520Snw141292 	/*
7444520Snw141292 	 * The binary format of a SID is as follows:
7454520Snw141292 	 *
7464520Snw141292 	 * byte #0: version, always 0x01
7474520Snw141292 	 * byte #1: RID count, always <= 0x0f
7484520Snw141292 	 * bytes #2-#7: SID authority, big-endian 48-bit unsigned int
7494520Snw141292 	 *
7504520Snw141292 	 * followed by RID count RIDs, each a little-endian, unsigned
7514520Snw141292 	 * 32-bit int.
7524520Snw141292 	 */
7534520Snw141292 	/*
7544520Snw141292 	 * Sanity checks: must have at least one RID, version must be
7554520Snw141292 	 * 0x01, and the length must be 8 + rid count * 4
7564520Snw141292 	 */
7574520Snw141292 	if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 &&
7584520Snw141292 	    bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) {
7594520Snw141292 		v = (uchar_t *)bval->bv_val;
7604520Snw141292 		sidp->version = v[0];
7614520Snw141292 		sidp->sub_authority_count = v[1];
7624520Snw141292 		sidp->authority =
7634520Snw141292 		    /* big endian -- so start from the left */
7644520Snw141292 		    ((u_longlong_t)v[2] << 40) |
7654520Snw141292 		    ((u_longlong_t)v[3] << 32) |
7664520Snw141292 		    ((u_longlong_t)v[4] << 24) |
7674520Snw141292 		    ((u_longlong_t)v[5] << 16) |
7684520Snw141292 		    ((u_longlong_t)v[6] << 8) |
7694520Snw141292 		    (u_longlong_t)v[7];
7704520Snw141292 		for (i = 0; i < sidp->sub_authority_count; i++) {
7714520Snw141292 			j = 8 + (i * 4);
7724520Snw141292 			/* little endian -- so start from the right */
7734520Snw141292 			a = (v[j + 3] << 24) | (v[j + 2] << 16) |
7744520Snw141292 			    (v[j + 1] << 8) | (v[j]);
7754520Snw141292 			sidp->sub_authorities[i] = a;
7764520Snw141292 		}
7774520Snw141292 		return (0);
7784520Snw141292 	}
7794520Snw141292 	return (-1);
7804520Snw141292 }
7814520Snw141292 
7824520Snw141292 /*
7834520Snw141292  * Convert a sid_t to S-1-...
7844520Snw141292  */
7854520Snw141292 static
7864520Snw141292 char *
7874520Snw141292 idmap_sid2txt(sid_t *sidp)
7884520Snw141292 {
7894520Snw141292 	int	rlen, i, len;
7904520Snw141292 	char	*str, *cp;
7914520Snw141292 
7924520Snw141292 	if (sidp->version != 1)
7934520Snw141292 		return (NULL);
7944520Snw141292 
7954520Snw141292 	len = sizeof ("S-1-") - 1;
7964520Snw141292 
7974520Snw141292 	/*
7984520Snw141292 	 * We could optimize like so, but, why?
7994520Snw141292 	 *	if (sidp->authority < 10)
8004520Snw141292 	 *		len += 2;
8014520Snw141292 	 *	else if (sidp->authority < 100)
8024520Snw141292 	 *		len += 3;
8034520Snw141292 	 *	else
8044520Snw141292 	 *		len += snprintf(NULL, 0"%llu", sidp->authority);
8054520Snw141292 	 */
8064520Snw141292 	len += snprintf(NULL, 0, "%llu", sidp->authority);
8074520Snw141292 
8084520Snw141292 	/* Max length of a uint32_t printed out in ASCII is 10 bytes */
8094520Snw141292 	len += 1 + (sidp->sub_authority_count + 1) * 10;
8104520Snw141292 
8114520Snw141292 	if ((cp = str = malloc(len)) == NULL)
8124520Snw141292 		return (NULL);
8134520Snw141292 
8144520Snw141292 	rlen = snprintf(str, len, "S-1-%llu", sidp->authority);
8154520Snw141292 
8164520Snw141292 	cp += rlen;
8174520Snw141292 	len -= rlen;
8184520Snw141292 
8194520Snw141292 	for (i = 0; i < sidp->sub_authority_count; i++) {
8204520Snw141292 		assert(len > 0);
8214520Snw141292 		rlen = snprintf(cp, len, "-%u", sidp->sub_authorities[i]);
8224520Snw141292 		cp += rlen;
8234520Snw141292 		len -= rlen;
8244520Snw141292 		assert(len >= 0);
8254520Snw141292 	}
8264520Snw141292 
8274520Snw141292 	return (str);
8284520Snw141292 }
8294520Snw141292 
8304520Snw141292 /*
8314520Snw141292  * Convert a sid_t to on-the-wire encoding
8324520Snw141292  */
8334520Snw141292 static
8344520Snw141292 int
8354520Snw141292 idmap_sid2binsid(sid_t *sid, uchar_t *binsid, int binsidlen)
8364520Snw141292 {
8374520Snw141292 	uchar_t		*p;
8384520Snw141292 	int		i;
8394520Snw141292 	uint64_t	a;
8404520Snw141292 	uint32_t	r;
8414520Snw141292 
8424520Snw141292 	if (sid->version != 1 ||
8434520Snw141292 	    binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4))
8444520Snw141292 		return (-1);
8454520Snw141292 
8464520Snw141292 	p = binsid;
8474520Snw141292 	*p++ = 0x01;		/* version */
8484520Snw141292 	/* sub authority count */
8494520Snw141292 	*p++ = sid->sub_authority_count;
8504520Snw141292 	/* Authority */
8514520Snw141292 	a = sid->authority;
8524520Snw141292 	/* big-endian -- start from left */
8534520Snw141292 	*p++ = (a >> 40) & 0xFF;
8544520Snw141292 	*p++ = (a >> 32) & 0xFF;
8554520Snw141292 	*p++ = (a >> 24) & 0xFF;
8564520Snw141292 	*p++ = (a >> 16) & 0xFF;
8574520Snw141292 	*p++ = (a >> 8) & 0xFF;
8584520Snw141292 	*p++ = a & 0xFF;
8594520Snw141292 
8604520Snw141292 	/* sub-authorities */
8614520Snw141292 	for (i = 0; i < sid->sub_authority_count; i++) {
8624520Snw141292 		r = sid->sub_authorities[i];
8634520Snw141292 		/* little-endian -- start from right */
8644520Snw141292 		*p++ = (r & 0x000000FF);
8654520Snw141292 		*p++ = (r & 0x0000FF00) >> 8;
8664520Snw141292 		*p++ = (r & 0x00FF0000) >> 16;
8674520Snw141292 		*p++ = (r & 0xFF000000) >> 24;
8684520Snw141292 	}
8694520Snw141292 
8704520Snw141292 	return (0);
8714520Snw141292 }
8724520Snw141292 
8734520Snw141292 /*
8744520Snw141292  * Convert a stringified SID (S-1-...) into a hex-encoded version of the
8754520Snw141292  * on-the-wire encoding, but with each pair of hex digits pre-pended
8764520Snw141292  * with a '\', so we can pass this to libldap.
8774520Snw141292  */
8784520Snw141292 static
8794520Snw141292 int
8804520Snw141292 idmap_txtsid2hexbinsid(const char *txt, const rid_t *rid,
8814520Snw141292 	char *hexbinsid, int hexbinsidlen)
8824520Snw141292 {
8834520Snw141292 	sid_t		sid = { 0 };
8844520Snw141292 	int		i, j;
8854520Snw141292 	const char	*cp;
8864520Snw141292 	char		*ecp;
8874520Snw141292 	u_longlong_t	a;
8884520Snw141292 	unsigned long	r;
8894520Snw141292 	uchar_t		*binsid, b, hb;
8904520Snw141292 
8914520Snw141292 	/* Only version 1 SIDs please */
8924520Snw141292 	if (strncmp(txt, "S-1-", strlen("S-1-")) != 0)
8934520Snw141292 		return (-1);
8944520Snw141292 
8954520Snw141292 	if (strlen(txt) < (strlen("S-1-") + 1))
8964520Snw141292 		return (-1);
8974520Snw141292 
8984520Snw141292 	/* count '-'s */
8994520Snw141292 	for (j = 0, cp = strchr(txt, '-');
9004520Snw141292 	    cp != NULL && *cp != '\0';
9014520Snw141292 	    j++, cp = strchr(cp + 1, '-')) {
9024520Snw141292 		/* can't end on a '-' */
9034520Snw141292 		if (*(cp + 1) == '\0')
9044520Snw141292 			return (-1);
9054520Snw141292 	}
9064520Snw141292 
9074864Sbaban 	/* Adjust count for version and authority */
9084864Sbaban 	j -= 2;
9094864Sbaban 
9104864Sbaban 	/* we know the version number and RID count */
9114864Sbaban 	sid.version = 1;
9124864Sbaban 	sid.sub_authority_count = (rid != NULL) ? j + 1 : j;
9134864Sbaban 
9144520Snw141292 	/* must have at least one RID, but not too many */
9154864Sbaban 	if (sid.sub_authority_count < 1 ||
9164864Sbaban 	    sid.sub_authority_count > SID_MAX_SUB_AUTHORITIES)
9174520Snw141292 		return (-1);
9184520Snw141292 
9194520Snw141292 	/* check that we only have digits and '-' */
9204520Snw141292 	if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1))
9214520Snw141292 		return (-1);
9224520Snw141292 
9234520Snw141292 	cp = txt + strlen("S-1-");
9244520Snw141292 
9254520Snw141292 	/* 64-bit safe parsing of unsigned 48-bit authority value */
9264520Snw141292 	errno = 0;
9274520Snw141292 	a = strtoull(cp, &ecp, 10);
9284520Snw141292 
9294520Snw141292 	/* errors parsing the authority or too many bits */
9304520Snw141292 	if (cp == ecp || (a == 0 && errno == EINVAL) ||
9314520Snw141292 	    (a == ULLONG_MAX && errno == ERANGE) ||
9324520Snw141292 	    (a & 0x0000ffffffffffffULL) != a)
9334520Snw141292 		return (-1);
9344520Snw141292 
9354520Snw141292 	cp = ecp;
9364520Snw141292 
9374520Snw141292 	sid.authority = (uint64_t)a;
9384520Snw141292 
9394864Sbaban 	for (i = 0; i < j; i++) {
9404520Snw141292 		if (*cp++ != '-')
9414520Snw141292 			return (-1);
9424520Snw141292 		/* 64-bit safe parsing of unsigned 32-bit RID */
9434520Snw141292 		errno = 0;
9444520Snw141292 		r = strtoul(cp, &ecp, 10);
9454520Snw141292 		/* errors parsing the RID or too many bits */
9464520Snw141292 		if (cp == ecp || (r == 0 && errno == EINVAL) ||
9474520Snw141292 		    (r == ULONG_MAX && errno == ERANGE) ||
9484520Snw141292 		    (r & 0xffffffffUL) != r)
9494520Snw141292 			return (-1);
9504520Snw141292 		sid.sub_authorities[i] = (uint32_t)r;
9514520Snw141292 		cp = ecp;
9524520Snw141292 	}
9534520Snw141292 
9544520Snw141292 	/* check that all of the string SID has been consumed */
9554520Snw141292 	if (*cp != '\0')
9564520Snw141292 		return (-1);
9574520Snw141292 
9584864Sbaban 	if (rid != NULL)
9594864Sbaban 		sid.sub_authorities[j] = *rid;
9604520Snw141292 
9614520Snw141292 	j = 1 + 1 + 6 + sid.sub_authority_count * 4;
9624520Snw141292 
9634520Snw141292 	if (hexbinsidlen < (j * 3))
9644520Snw141292 		return (-2);
9654520Snw141292 
9664520Snw141292 	/* binary encode the SID */
9674520Snw141292 	binsid = (uchar_t *)alloca(j);
9684520Snw141292 	(void) idmap_sid2binsid(&sid, binsid, j);
9694520Snw141292 
9704520Snw141292 	/* hex encode, with a backslash before each byte */
9714520Snw141292 	for (ecp = hexbinsid, i = 0; i < j; i++) {
9724520Snw141292 		b = binsid[i];
9734520Snw141292 		*ecp++ = '\\';
9744520Snw141292 		hb = (b >> 4) & 0xF;
9754520Snw141292 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
9764520Snw141292 		hb = b & 0xF;
9774520Snw141292 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
9784520Snw141292 	}
9794520Snw141292 	*ecp = '\0';
9804520Snw141292 
9814520Snw141292 	return (0);
9824520Snw141292 }
9834520Snw141292 
9844520Snw141292 static
9854520Snw141292 char *
9864520Snw141292 convert_bval2sid(BerValue *bval, rid_t *rid)
9874520Snw141292 {
9884520Snw141292 	sid_t	sid;
9894520Snw141292 
9904520Snw141292 	if (idmap_getsid(bval, &sid) < 0)
9914520Snw141292 		return (NULL);
9924520Snw141292 
9934520Snw141292 	/*
9944520Snw141292 	 * If desired and if the SID is what should be a domain/computer
9954520Snw141292 	 * user or group SID (i.e., S-1-5-w-x-y-z-<user/group RID>) then
9964520Snw141292 	 * save the last RID and truncate the SID
9974520Snw141292 	 */
9984520Snw141292 	if (rid != NULL && sid.authority == 5 && sid.sub_authority_count == 5)
9994520Snw141292 		*rid = sid.sub_authorities[--sid.sub_authority_count];
10004520Snw141292 	return (idmap_sid2txt(&sid));
10014520Snw141292 }
10024520Snw141292 
10034520Snw141292 
10044520Snw141292 idmap_retcode
10054520Snw141292 idmap_lookup_batch_start(ad_t *ad, int nqueries, idmap_query_state_t **state)
10064520Snw141292 {
10074520Snw141292 	idmap_query_state_t *new_state;
10084520Snw141292 	ad_host_t	*adh = NULL;
10094520Snw141292 
10104520Snw141292 	*state = NULL;
10114520Snw141292 
10126097Snw141292 	if (ad == NULL)
10135731Sbaban 		return (IDMAP_ERR_INTERNAL);
10144520Snw141292 
10154520Snw141292 	adh = idmap_get_conn(ad);
10164520Snw141292 	if (adh == NULL)
10175968Snw141292 		return (IDMAP_ERR_RETRIABLE_NET_ERR);
10184520Snw141292 
10194520Snw141292 	new_state = calloc(1, sizeof (idmap_query_state_t) +
10204520Snw141292 	    (nqueries - 1) * sizeof (idmap_q_t));
10214520Snw141292 
10224520Snw141292 	if (new_state == NULL)
10234520Snw141292 		return (IDMAP_ERR_MEMORY);
10244520Snw141292 
10254884Sjp151216 	new_state->ref_cnt = 1;
10264520Snw141292 	new_state->qadh = adh;
10274520Snw141292 	new_state->qcount = nqueries;
10284520Snw141292 	new_state->qadh_gen = adh->generation;
10294520Snw141292 	/* should be -1, but the atomic routines want unsigned */
10304520Snw141292 	new_state->qlastsent = 0;
10314884Sjp151216 	(void) pthread_cond_init(&new_state->cv, NULL);
10324520Snw141292 
10334520Snw141292 	(void) pthread_mutex_lock(&qstatelock);
10344520Snw141292 	new_state->next = qstatehead;
10354520Snw141292 	qstatehead = new_state;
10364520Snw141292 	(void) pthread_mutex_unlock(&qstatelock);
10374520Snw141292 
10384520Snw141292 	*state = new_state;
10394520Snw141292 
10404520Snw141292 	return (IDMAP_SUCCESS);
10414520Snw141292 }
10424520Snw141292 
10434520Snw141292 /*
10445731Sbaban  * Set unixuser_attr and unixgroup_attr for AD-based name mapping
10455731Sbaban  */
10465731Sbaban void
10475731Sbaban idmap_lookup_batch_set_unixattr(idmap_query_state_t *state,
10485908Sjp151216 		const char *unixuser_attr, const char *unixgroup_attr)
10495908Sjp151216 {
10505731Sbaban 	state->ad_unixuser_attr = unixuser_attr;
10515731Sbaban 	state->ad_unixgroup_attr = unixgroup_attr;
10525731Sbaban }
10535731Sbaban 
10545731Sbaban /*
10554520Snw141292  * Find the idmap_query_state_t to which a given LDAP result msgid on a
10564884Sjp151216  * given connection belongs. This routine increaments the reference count
10574884Sjp151216  * so that the object can not be freed. idmap_lookup_unlock_batch()
10584884Sjp151216  * must be called to decreament the reference count.
10594520Snw141292  */
10604520Snw141292 static
10614520Snw141292 int
10624520Snw141292 idmap_msgid2query(ad_host_t *adh, int msgid,
10634520Snw141292 	idmap_query_state_t **state, int *qid)
10644520Snw141292 {
10656531Sjp151216 	idmap_query_state_t	*p;
10666531Sjp151216 	int			i;
10676531Sjp151216 	int			ret;
10684520Snw141292 
10694520Snw141292 	(void) pthread_mutex_lock(&qstatelock);
10704520Snw141292 	for (p = qstatehead; p != NULL; p = p->next) {
10714520Snw141292 		if (p->qadh != adh || adh->generation != p->qadh_gen)
10724520Snw141292 			continue;
10734520Snw141292 		for (i = 0; i < p->qcount; i++) {
10744520Snw141292 			if ((p->queries[i]).msgid == msgid) {
10756531Sjp151216 				if (!p->qdead) {
10766531Sjp151216 					p->ref_cnt++;
10776531Sjp151216 					*state = p;
10786531Sjp151216 					*qid = i;
10796531Sjp151216 					ret = 1;
10806531Sjp151216 				} else
10816531Sjp151216 					ret = 0;
10824520Snw141292 				(void) pthread_mutex_unlock(&qstatelock);
10836531Sjp151216 				return (ret);
10844520Snw141292 			}
10854520Snw141292 		}
10864520Snw141292 	}
10874520Snw141292 	(void) pthread_mutex_unlock(&qstatelock);
10884520Snw141292 	return (0);
10894520Snw141292 }
10904520Snw141292 
10914520Snw141292 /*
10926531Sjp151216  * Put the the search result onto the correct idmap_q_t given the LDAP result
10936531Sjp151216  * msgid
10946531Sjp151216  * Returns:	0 success
10956531Sjp151216  *		-1 already has a search result
10966531Sjp151216  *		-2 cant find message id
10976531Sjp151216  */
10986531Sjp151216 static
10996531Sjp151216 int
11006531Sjp151216 idmap_quesearchresbymsgid(ad_host_t *adh, int msgid, LDAPMessage *search_res)
11016531Sjp151216 {
11026531Sjp151216 	idmap_query_state_t	*p;
11036531Sjp151216 	int			i;
11046531Sjp151216 	int			res;
11056531Sjp151216 
11066531Sjp151216 	(void) pthread_mutex_lock(&qstatelock);
11076531Sjp151216 	for (p = qstatehead; p != NULL; p = p->next) {
11086531Sjp151216 		if (p->qadh != adh || adh->generation != p->qadh_gen)
11096531Sjp151216 			continue;
11106531Sjp151216 		for (i = 0; i < p->qcount; i++) {
11116531Sjp151216 			if ((p->queries[i]).msgid == msgid) {
11126531Sjp151216 				if (p->queries[i].search_res == NULL) {
11136531Sjp151216 					if (!p->qdead) {
11146531Sjp151216 						p->queries[i].search_res =
11156531Sjp151216 						    search_res;
11166531Sjp151216 						res = 0;
11176531Sjp151216 					} else
11186531Sjp151216 						res = -2;
11196531Sjp151216 				} else
11206531Sjp151216 					res = -1;
11216531Sjp151216 				(void) pthread_mutex_unlock(&qstatelock);
11226531Sjp151216 				return (res);
11236531Sjp151216 			}
11246531Sjp151216 		}
11256531Sjp151216 	}
11266531Sjp151216 	(void) pthread_mutex_unlock(&qstatelock);
11276531Sjp151216 	return (-2);
11286531Sjp151216 }
11296531Sjp151216 
11306531Sjp151216 
11316531Sjp151216 /*
11325696Snw141292  * Take parsed attribute values from a search result entry and check if
11335696Snw141292  * it is the result that was desired and, if so, set the result fields
11345696Snw141292  * of the given idmap_q_t.
11355696Snw141292  *
11365731Sbaban  * Frees the unused char * values.
11374520Snw141292  */
11384520Snw141292 static
11395696Snw141292 void
11406386Sjp151216 idmap_setqresults(idmap_q_t *q, char *san, char *dn, const char *attr,
11416386Sjp151216 	char *sid, rid_t rid, int sid_type, char *unixname)
11424520Snw141292 {
11435696Snw141292 	char *domain;
11445731Sbaban 	int err1, err2;
11455696Snw141292 
11465696Snw141292 	assert(dn != NULL);
11475696Snw141292 
11485696Snw141292 	if ((domain = dn2dns(dn)) == NULL)
11495696Snw141292 		goto out;
11505696Snw141292 
11515731Sbaban 	if (q->ecanonname != NULL && san != NULL) {
11525731Sbaban 		/* Check that this is the canonname that we were looking for */
11535696Snw141292 		if (u8_strcmp(q->ecanonname, san, 0,
11545696Snw141292 		    U8_STRCMP_CI_LOWER, /* no normalization, for now */
11555731Sbaban 		    U8_UNICODE_LATEST, &err1) != 0 || err1 != 0)
11565731Sbaban 			goto out;
11575731Sbaban 	}
11585731Sbaban 
11595731Sbaban 	if (q->edomain != NULL) {
11605731Sbaban 		/* Check that this is the domain that we were looking for */
11615731Sbaban 		if (u8_strcmp(q->edomain, domain, 0, U8_STRCMP_CI_LOWER,
11625696Snw141292 		    U8_UNICODE_LATEST, &err2) != 0 || err2 != 0)
11635696Snw141292 			goto out;
11645696Snw141292 	}
11655696Snw141292 
11666386Sjp151216 	/* Copy the DN and attr and value */
11676386Sjp151216 	if (q->dn != NULL)
11686386Sjp151216 		*q->dn = strdup(dn);
11696386Sjp151216 
11706386Sjp151216 	if (q->attr != NULL && attr != NULL)
11716386Sjp151216 		*q->attr = strdup(attr);
11726386Sjp151216 
11736386Sjp151216 	if (q->value != NULL && unixname != NULL)
11746386Sjp151216 		*q->value = strdup(unixname);
11756386Sjp151216 
11765731Sbaban 	/* Set results */
11775731Sbaban 	if (q->sid) {
11785731Sbaban 		*q->sid = sid;
11795731Sbaban 		sid = NULL;
11805731Sbaban 	}
11815731Sbaban 	if (q->rid)
11825731Sbaban 		*q->rid = rid;
11835731Sbaban 	if (q->sid_type)
11845731Sbaban 		*q->sid_type = sid_type;
11855731Sbaban 	if (q->unixname) {
11865731Sbaban 		*q->unixname = unixname;
11875731Sbaban 		unixname = NULL;
11885731Sbaban 	}
11895731Sbaban 	if (q->domain != NULL) {
11905731Sbaban 		*q->domain = domain;
11915731Sbaban 		domain = NULL;
11925731Sbaban 	}
11935731Sbaban 	if (q->canonname != NULL) {
1194*6616Sdm199847 		/*
1195*6616Sdm199847 		 * The caller may be replacing the given winname by its
1196*6616Sdm199847 		 * canonical name and therefore free any old name before
1197*6616Sdm199847 		 * overwriting the field by the canonical name.
1198*6616Sdm199847 		 */
1199*6616Sdm199847 		free(*q->canonname);
12005731Sbaban 		*q->canonname = san;
12015731Sbaban 		san = NULL;
12025731Sbaban 	}
12035731Sbaban 
12045731Sbaban 	/* Always have q->rc; idmap_extract_object() asserts this */
12055696Snw141292 	*q->rc = IDMAP_SUCCESS;
12065696Snw141292 
12075696Snw141292 out:
12085696Snw141292 	/* Free unused attribute values */
12095696Snw141292 	free(san);
12105696Snw141292 	free(sid);
12115696Snw141292 	free(domain);
12125731Sbaban 	free(unixname);
12134520Snw141292 }
12144520Snw141292 
12154520Snw141292 /*
12165696Snw141292  * The following three functions extract objectSid, sAMAccountName and
12175696Snw141292  * objectClass attribute values and, in the case of objectSid and
12185696Snw141292  * objectClass, parse them.
12195696Snw141292  *
12205696Snw141292  * idmap_setqresults() takes care of dealing with the result entry's DN.
12215696Snw141292  */
12225696Snw141292 
12235696Snw141292 /*
12245696Snw141292  * Return a NUL-terminated stringified SID from the value of an
12255696Snw141292  * objectSid attribute and put the last RID in *rid.
12264520Snw141292  */
12274520Snw141292 static
12285696Snw141292 char *
12295696Snw141292 idmap_bv_objsid2sidstr(BerValue **bvalues, rid_t *rid)
12304520Snw141292 {
12315696Snw141292 	char *sid;
12325696Snw141292 
12335696Snw141292 	if (bvalues == NULL)
12345696Snw141292 		return (NULL);
12355696Snw141292 	/* objectSid is single valued */
12365696Snw141292 	if ((sid = convert_bval2sid(bvalues[0], rid)) == NULL)
12375696Snw141292 		return (NULL);
12385696Snw141292 	return (sid);
12395696Snw141292 }
12405696Snw141292 
12415696Snw141292 /*
12425696Snw141292  * Return a NUL-terminated string from the value of a sAMAccountName
12435731Sbaban  * or unixname attribute.
12445696Snw141292  */
12455696Snw141292 static
12465696Snw141292 char *
12475731Sbaban idmap_bv_name2str(BerValue **bvalues)
12485696Snw141292 {
12495696Snw141292 	char *s;
12504520Snw141292 
12514520Snw141292 	if (bvalues == NULL || bvalues[0] == NULL ||
12524520Snw141292 	    bvalues[0]->bv_val == NULL)
12535696Snw141292 		return (NULL);
12545447Snw141292 
12555696Snw141292 	if ((s = malloc(bvalues[0]->bv_len + 1)) == NULL)
12565696Snw141292 		return (NULL);
12575447Snw141292 
12585696Snw141292 	(void) snprintf(s, bvalues[0]->bv_len + 1, "%.*s", bvalues[0]->bv_len,
12595696Snw141292 	    bvalues[0]->bv_val);
12604520Snw141292 
12615696Snw141292 	return (s);
12624520Snw141292 }
12634520Snw141292 
12644520Snw141292 
12654520Snw141292 #define	BVAL_CASEEQ(bv, str) \
12664520Snw141292 		(((*(bv))->bv_len == (sizeof (str) - 1)) && \
12674520Snw141292 		    strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0)
12684520Snw141292 
12694520Snw141292 /*
12705696Snw141292  * Extract the class of the result entry.  Returns 1 on success, 0 on
12715696Snw141292  * failure.
12724520Snw141292  */
12734520Snw141292 static
12745447Snw141292 int
12755696Snw141292 idmap_bv_objclass2sidtype(BerValue **bvalues, int *sid_type)
12764520Snw141292 {
12774520Snw141292 	BerValue	**cbval;
12784520Snw141292 
12795696Snw141292 	*sid_type = _IDMAP_T_OTHER;
12804520Snw141292 	if (bvalues == NULL)
12815447Snw141292 		return (0);
12824520Snw141292 
12835696Snw141292 	/*
12845696Snw141292 	 * We iterate over all the values because computer is a
12855696Snw141292 	 * sub-class of user.
12865696Snw141292 	 */
12874520Snw141292 	for (cbval = bvalues; *cbval != NULL; cbval++) {
12884520Snw141292 		if (BVAL_CASEEQ(cbval, "Computer")) {
12895696Snw141292 			*sid_type = _IDMAP_T_COMPUTER;
12905696Snw141292 			break;
12914520Snw141292 		} else if (BVAL_CASEEQ(cbval, "Group")) {
12925696Snw141292 			*sid_type = _IDMAP_T_GROUP;
12935696Snw141292 			break;
12944520Snw141292 		} else if (BVAL_CASEEQ(cbval, "USER")) {
12955696Snw141292 			*sid_type = _IDMAP_T_USER;
12965696Snw141292 			/* Continue looping -- this may be a computer yet */
12975696Snw141292 		}
12985696Snw141292 		/*
12995696Snw141292 		 * "else if (*sid_type = _IDMAP_T_USER)" then this is a
13005696Snw141292 		 * new sub-class of user -- what to do with it??
13015696Snw141292 		 */
13024520Snw141292 	}
13035447Snw141292 
13045447Snw141292 	return (1);
13054520Snw141292 }
13064520Snw141292 
13074520Snw141292 /*
13084520Snw141292  * Handle a given search result entry
13094520Snw141292  */
13104520Snw141292 static
13114520Snw141292 void
13124520Snw141292 idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res)
13134520Snw141292 {
13144520Snw141292 	BerElement		*ber = NULL;
13154520Snw141292 	BerValue		**bvalues;
13164520Snw141292 	ad_host_t		*adh;
13174520Snw141292 	idmap_q_t		*q;
13185696Snw141292 	char			*attr;
13195731Sbaban 	const char		*unixuser_attr = NULL;
13205731Sbaban 	const char		*unixgroup_attr = NULL;
13215731Sbaban 	char			*unixuser = NULL;
13225731Sbaban 	char			*unixgroup = NULL;
13235696Snw141292 	char			*dn = NULL;
13245696Snw141292 	char			*san = NULL;
13255696Snw141292 	char			*sid = NULL;
13265696Snw141292 	rid_t			rid = 0;
13275731Sbaban 	int			sid_type = _IDMAP_T_UNDEF;
13285447Snw141292 	int			has_class, has_san, has_sid;
13295731Sbaban 	int			has_unixuser, has_unixgroup;
13306531Sjp151216 	int			num;
13314520Snw141292 
13324520Snw141292 	adh = state->qadh;
13334520Snw141292 
13344520Snw141292 	(void) pthread_mutex_lock(&adh->lock);
13354520Snw141292 
13365447Snw141292 	q = &(state->queries[qid]);
13375447Snw141292 
13385731Sbaban 	assert(q->rc != NULL);
13395731Sbaban 
13406531Sjp151216 	if (adh->dead || (dn = ldap_get_dn(adh->ld, res)) == NULL) {
13416531Sjp151216 		num = adh->num_requests;
13424520Snw141292 		(void) pthread_mutex_unlock(&adh->lock);
13436531Sjp151216 		idmapdlog(LOG_DEBUG,
13446531Sjp151216 		    "AD error decoding search result - %d queued requests",
13456531Sjp151216 		    num);
13464520Snw141292 		return;
13474520Snw141292 	}
13484520Snw141292 
13495447Snw141292 	assert(q->domain == NULL || *q->domain == NULL);
13504520Snw141292 
13515731Sbaban 	/*
13525731Sbaban 	 * If the caller has requested unixname then determine the
13535731Sbaban 	 * AD attribute name that will have the unixname.
13545731Sbaban 	 */
13555731Sbaban 	if (q->unixname != NULL) {
13565731Sbaban 		if (q->eunixtype == _IDMAP_T_USER)
13575731Sbaban 			unixuser_attr = state->ad_unixuser_attr;
13585731Sbaban 		else if (q->eunixtype == _IDMAP_T_GROUP)
13595731Sbaban 			unixgroup_attr = state->ad_unixgroup_attr;
13605731Sbaban 		else if (q->eunixtype == _IDMAP_T_UNDEF) {
13615731Sbaban 			/*
13625731Sbaban 			 * This is the case where we don't know
13635731Sbaban 			 * before hand whether we need unixuser
13645731Sbaban 			 * or unixgroup. This will be determined
13655731Sbaban 			 * by the "sid_type" (i.e whether the given
13665731Sbaban 			 * winname is user or group). If sid_type
13675731Sbaban 			 * turns out to be user we will return
13685731Sbaban 			 * unixuser (if found) and if it is a group
13695731Sbaban 			 * we will return unixgroup (if found). We
13705731Sbaban 			 * lookup for both ad_unixuser_attr and
13715731Sbaban 			 * ad_unixgroup_attr and discard one of them
13725731Sbaban 			 * after we know the "sidtype". This
13735731Sbaban 			 * supports the following type of lookups.
13745731Sbaban 			 *
13755731Sbaban 			 * Example:
13765731Sbaban 			 *   $idmap show -c winname:foo
13775731Sbaban 			 * In the above example, idmap will
13785731Sbaban 			 * return uid if winname is winuser
13795731Sbaban 			 * and gid if winname is wingroup.
13805731Sbaban 			 */
13815731Sbaban 			unixuser_attr = state->ad_unixuser_attr;
13825731Sbaban 			unixgroup_attr = state->ad_unixgroup_attr;
13835731Sbaban 		}
13845731Sbaban 	}
13855731Sbaban 
13865731Sbaban 	has_class = has_san = has_sid = has_unixuser = has_unixgroup = 0;
13874520Snw141292 	for (attr = ldap_first_attribute(adh->ld, res, &ber); attr != NULL;
13884520Snw141292 	    attr = ldap_next_attribute(adh->ld, res, ber)) {
13894520Snw141292 		bvalues = NULL;	/* for memory management below */
13904520Snw141292 
13914520Snw141292 		/*
13924520Snw141292 		 * If this is an attribute we are looking for and
13934520Snw141292 		 * haven't seen it yet, parse it
13944520Snw141292 		 */
13955731Sbaban 		if (q->sid != NULL && !has_sid &&
13965447Snw141292 		    strcasecmp(attr, OBJSID) == 0) {
13974520Snw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
13985696Snw141292 			sid = idmap_bv_objsid2sidstr(bvalues, &rid);
13995696Snw141292 			has_sid = (sid != NULL);
14005447Snw141292 		} else if (!has_san && strcasecmp(attr, SAN) == 0) {
14014520Snw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
14025731Sbaban 			san = idmap_bv_name2str(bvalues);
14035696Snw141292 			has_san = (san != NULL);
14045447Snw141292 		} else if (!has_class && strcasecmp(attr, OBJCLASS) == 0) {
14054520Snw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
14065696Snw141292 			has_class = idmap_bv_objclass2sidtype(bvalues,
14075696Snw141292 			    &sid_type);
14085731Sbaban 			if (has_class && q->unixname != NULL &&
14095731Sbaban 			    q->eunixtype == _IDMAP_T_UNDEF) {
14105731Sbaban 				/*
14115731Sbaban 				 * This is the case where we didn't
14125731Sbaban 				 * know whether we wanted unixuser or
14135731Sbaban 				 * unixgroup as described above.
14145731Sbaban 				 * Now since we know the "sid_type"
14155731Sbaban 				 * we discard the unwanted value
14165731Sbaban 				 * if it was retrieved before we
14175731Sbaban 				 * got here.
14185731Sbaban 				 */
14195731Sbaban 				if (sid_type == _IDMAP_T_USER) {
14205731Sbaban 					free(unixgroup);
14215731Sbaban 					unixgroup_attr = unixgroup = NULL;
14225731Sbaban 				} else if (sid_type == _IDMAP_T_GROUP) {
14235731Sbaban 					free(unixuser);
14245731Sbaban 					unixuser_attr = unixuser = NULL;
14255731Sbaban 				} else {
14265731Sbaban 					free(unixuser);
14275731Sbaban 					free(unixgroup);
14285731Sbaban 					unixuser_attr = unixuser = NULL;
14295731Sbaban 					unixgroup_attr = unixgroup = NULL;
14305731Sbaban 				}
14315731Sbaban 			}
14325731Sbaban 		} else if (!has_unixuser && unixuser_attr != NULL &&
14335731Sbaban 		    strcasecmp(attr, unixuser_attr) == 0) {
14345731Sbaban 			bvalues = ldap_get_values_len(adh->ld, res, attr);
14355731Sbaban 			unixuser = idmap_bv_name2str(bvalues);
14365731Sbaban 			has_unixuser = (unixuser != NULL);
14376386Sjp151216 
14385731Sbaban 		} else if (!has_unixgroup && unixgroup_attr != NULL &&
14395731Sbaban 		    strcasecmp(attr, unixgroup_attr) == 0) {
14405731Sbaban 			bvalues = ldap_get_values_len(adh->ld, res, attr);
14415731Sbaban 			unixgroup = idmap_bv_name2str(bvalues);
14425731Sbaban 			has_unixgroup = (unixgroup != NULL);
14434520Snw141292 		}
14444520Snw141292 
14454520Snw141292 		if (bvalues != NULL)
14464520Snw141292 			ldap_value_free_len(bvalues);
14474520Snw141292 		ldap_memfree(attr);
14484520Snw141292 
14495696Snw141292 		if (has_class && has_san &&
14505731Sbaban 		    (q->sid == NULL || has_sid) &&
14515731Sbaban 		    (unixuser_attr == NULL || has_unixuser) &&
14525731Sbaban 		    (unixgroup_attr == NULL || has_unixgroup)) {
14535731Sbaban 			/* Got what we need */
14545447Snw141292 			break;
14555696Snw141292 		}
14564520Snw141292 	}
14574520Snw141292 
14586531Sjp151216 	(void) pthread_mutex_unlock(&adh->lock);
14596531Sjp151216 
14605731Sbaban 	if (!has_class) {
14615731Sbaban 		/*
14625731Sbaban 		 * Didn't find objectclass. Something's wrong with our
14635731Sbaban 		 * AD data.
14645731Sbaban 		 */
14655731Sbaban 		free(san);
14665731Sbaban 		free(sid);
14675731Sbaban 		free(unixuser);
14685731Sbaban 		free(unixgroup);
14695731Sbaban 	} else {
14705731Sbaban 		/*
14715731Sbaban 		 * Either we got what we needed and came out of the loop
14725731Sbaban 		 * early OR we completed the loop in which case we didn't
14735731Sbaban 		 * find some attributes that we were looking for. In either
14745731Sbaban 		 * case set the result with what we got.
14755731Sbaban 		 */
14766386Sjp151216 		idmap_setqresults(q, san, dn,
14776386Sjp151216 		    (unixuser != NULL) ? unixuser_attr : unixgroup_attr,
14786386Sjp151216 		    sid, rid, sid_type,
14795731Sbaban 		    (unixuser != NULL) ? unixuser : unixgroup);
14805731Sbaban 	}
14815731Sbaban 
14824520Snw141292 	if (ber != NULL)
14834520Snw141292 		ber_free(ber, 0);
14844520Snw141292 
14854520Snw141292 	ldap_memfree(dn);
14864520Snw141292 }
14874520Snw141292 
14884520Snw141292 /*
14894520Snw141292  * Try to get a result; if there is one, find the corresponding
14904520Snw141292  * idmap_q_t and process the result.
14916531Sjp151216  *
14926531Sjp151216  * Returns:	0 success
14936531Sjp151216  *		-1 error
14946531Sjp151216  *		-2 queue empty
14954520Snw141292  */
14964520Snw141292 static
14974520Snw141292 int
14984520Snw141292 idmap_get_adobject_batch(ad_host_t *adh, struct timeval *timeout)
14994520Snw141292 {
15004520Snw141292 	idmap_query_state_t	*query_state;
15014520Snw141292 	LDAPMessage		*res = NULL;
15024520Snw141292 	int			rc, ret, msgid, qid;
15036531Sjp151216 	idmap_q_t		*que;
15046531Sjp151216 	int			num;
15054520Snw141292 
15064520Snw141292 	(void) pthread_mutex_lock(&adh->lock);
15076531Sjp151216 	if (adh->dead || adh->num_requests == 0) {
15086531Sjp151216 		if (adh->dead)
15096531Sjp151216 			ret = -1;
15106531Sjp151216 		else
15116531Sjp151216 			ret = -2;
15124520Snw141292 		(void) pthread_mutex_unlock(&adh->lock);
15136531Sjp151216 		return (ret);
15144520Snw141292 	}
15154520Snw141292 
15164520Snw141292 	/* Get one result */
15174520Snw141292 	rc = ldap_result(adh->ld, LDAP_RES_ANY, 0,
15184520Snw141292 	    timeout, &res);
15195968Snw141292 	if ((timeout != NULL && timeout->tv_sec > 0 && rc == LDAP_SUCCESS) ||
15205968Snw141292 	    rc < 0)
15214520Snw141292 		adh->dead = 1;
15226531Sjp151216 
15236531Sjp151216 	if (rc == LDAP_RES_SEARCH_RESULT && adh->num_requests > 0)
15246531Sjp151216 		adh->num_requests--;
15254520Snw141292 
15266531Sjp151216 	if (adh->dead) {
15276531Sjp151216 		num = adh->num_requests;
15286531Sjp151216 		(void) pthread_mutex_unlock(&adh->lock);
15296531Sjp151216 		idmapdlog(LOG_DEBUG,
15306531Sjp151216 		    "AD ldap_result error - %d queued requests", num);
15314520Snw141292 		return (-1);
15326531Sjp151216 	}
15334520Snw141292 	switch (rc) {
15344520Snw141292 	case LDAP_RES_SEARCH_RESULT:
15356531Sjp151216 		/* We should have the LDAP replies for some search... */
15364520Snw141292 		msgid = ldap_msgid(res);
15376531Sjp151216 		if (idmap_msgid2query(adh, msgid, &query_state, &qid)) {
15386531Sjp151216 			(void) pthread_mutex_unlock(&adh->lock);
15396531Sjp151216 			que = &(query_state->queries[qid]);
15406531Sjp151216 			if (que->search_res != NULL) {
15416531Sjp151216 				idmap_extract_object(query_state, qid,
15426531Sjp151216 				    que->search_res);
15436531Sjp151216 				(void) ldap_msgfree(que->search_res);
15446531Sjp151216 				que->search_res = NULL;
15456531Sjp151216 			} else
15466531Sjp151216 				*que->rc = IDMAP_ERR_NOTFOUND;
15474520Snw141292 			/* ...so we can decrement qinflight */
15484520Snw141292 			atomic_dec_32(&query_state->qinflight);
15494884Sjp151216 			idmap_lookup_unlock_batch(&query_state);
15506531Sjp151216 		} else {
15516531Sjp151216 			num = adh->num_requests;
15526531Sjp151216 			(void) pthread_mutex_unlock(&adh->lock);
15536531Sjp151216 			idmapdlog(LOG_DEBUG,
15546531Sjp151216 			    "AD cannot find message ID  - %d queued requests",
15556531Sjp151216 			    num);
15564520Snw141292 		}
15574520Snw141292 		(void) ldap_msgfree(res);
15584520Snw141292 		ret = 0;
15594520Snw141292 		break;
15606531Sjp151216 
15614520Snw141292 	case LDAP_RES_SEARCH_REFERENCE:
15624520Snw141292 		/*
15634520Snw141292 		 * We have no need for these at the moment.  Eventually,
15644520Snw141292 		 * when we query things that we can't expect to find in
15654520Snw141292 		 * the Global Catalog then we'll need to learn to follow
15664520Snw141292 		 * references.
15674520Snw141292 		 */
15686531Sjp151216 		(void) pthread_mutex_unlock(&adh->lock);
15694520Snw141292 		(void) ldap_msgfree(res);
15704520Snw141292 		ret = 0;
15714520Snw141292 		break;
15726531Sjp151216 
15734520Snw141292 	case LDAP_RES_SEARCH_ENTRY:
15746531Sjp151216 		/* Got a result - queue it */
15754520Snw141292 		msgid = ldap_msgid(res);
15766531Sjp151216 		rc = idmap_quesearchresbymsgid(adh, msgid, res);
15776531Sjp151216 		num = adh->num_requests;
15786531Sjp151216 		(void) pthread_mutex_unlock(&adh->lock);
15796531Sjp151216 		if (rc == -1) {
15806531Sjp151216 			idmapdlog(LOG_DEBUG,
15816531Sjp151216 			    "AD already has search result - %d queued requests",
15826531Sjp151216 			    num);
15836531Sjp151216 			(void) ldap_msgfree(res);
15846531Sjp151216 		} else if (rc == -2) {
15856531Sjp151216 			idmapdlog(LOG_DEBUG,
15866531Sjp151216 			    "AD cannot queue by message ID  "
15876531Sjp151216 			    "- %d queued requests", num);
15886531Sjp151216 			(void) ldap_msgfree(res);
15894520Snw141292 		}
15904520Snw141292 		ret = 0;
15914520Snw141292 		break;
15926531Sjp151216 
15934520Snw141292 	default:
15944520Snw141292 		/* timeout or error; treat the same */
15956531Sjp151216 		(void) pthread_mutex_unlock(&adh->lock);
15964520Snw141292 		ret = -1;
15974520Snw141292 		break;
15984520Snw141292 	}
15994520Snw141292 
16004520Snw141292 	return (ret);
16014520Snw141292 }
16024520Snw141292 
16034884Sjp151216 /*
16044884Sjp151216  * This routine decreament the reference count of the
16054884Sjp151216  * idmap_query_state_t
16064884Sjp151216  */
16074884Sjp151216 static void
16084884Sjp151216 idmap_lookup_unlock_batch(idmap_query_state_t **state)
16094884Sjp151216 {
16104884Sjp151216 	/*
16114884Sjp151216 	 * Decrement reference count with qstatelock locked
16124884Sjp151216 	 */
16134884Sjp151216 	(void) pthread_mutex_lock(&qstatelock);
16144884Sjp151216 	(*state)->ref_cnt--;
16154884Sjp151216 	/*
16164884Sjp151216 	 * If there are no references wakup the allocating thread
16174884Sjp151216 	 */
16186531Sjp151216 	if ((*state)->ref_cnt <= 1)
16194884Sjp151216 		(void) pthread_cond_signal(&(*state)->cv);
16204884Sjp151216 	(void) pthread_mutex_unlock(&qstatelock);
16214884Sjp151216 	*state = NULL;
16224884Sjp151216 }
16234884Sjp151216 
16245447Snw141292 static
16255447Snw141292 void
16265447Snw141292 idmap_cleanup_batch(idmap_query_state_t *batch)
16275447Snw141292 {
16285447Snw141292 	int i;
16295447Snw141292 
16305447Snw141292 	for (i = 0; i < batch->qcount; i++) {
16315696Snw141292 		if (batch->queries[i].ecanonname != NULL)
16325696Snw141292 			free(batch->queries[i].ecanonname);
16335696Snw141292 		batch->queries[i].ecanonname = NULL;
16345696Snw141292 		if (batch->queries[i].edomain != NULL)
16355696Snw141292 			free(batch->queries[i].edomain);
16365696Snw141292 		batch->queries[i].edomain = NULL;
16375447Snw141292 	}
16385447Snw141292 }
16395447Snw141292 
16404884Sjp151216 /*
16414884Sjp151216  * This routine frees the idmap_query_state_t structure
16424884Sjp151216  * If the reference count is greater than 1 it waits
16434884Sjp151216  * for the other threads to finish using it.
16444884Sjp151216  */
16454520Snw141292 void
16464884Sjp151216 idmap_lookup_release_batch(idmap_query_state_t **state)
16474520Snw141292 {
16484520Snw141292 	idmap_query_state_t **p;
16494520Snw141292 
16504884Sjp151216 	/*
16516531Sjp151216 	 * Set state to dead to stop further operations.
16526531Sjp151216 	 * Wait for reference count with qstatelock locked
16536531Sjp151216 	 * to get to one.
16544884Sjp151216 	 */
16554884Sjp151216 	(void) pthread_mutex_lock(&qstatelock);
16566531Sjp151216 	(*state)->qdead = 1;
16576531Sjp151216 	while ((*state)->ref_cnt > 1) {
16584884Sjp151216 		(void) pthread_cond_wait(&(*state)->cv, &qstatelock);
16594884Sjp151216 	}
16604520Snw141292 
16614520Snw141292 	/* Remove this state struct from the list of state structs */
16624520Snw141292 	for (p = &qstatehead; *p != NULL; p = &(*p)->next) {
16634520Snw141292 		if (*p == (*state)) {
16644520Snw141292 			*p = (*state)->next;
16654520Snw141292 			break;
16664520Snw141292 		}
16674520Snw141292 	}
16686531Sjp151216 	(void) pthread_mutex_unlock(&qstatelock);
16695447Snw141292 
16705447Snw141292 	idmap_cleanup_batch(*state);
16715447Snw141292 
16724884Sjp151216 	(void) pthread_cond_destroy(&(*state)->cv);
16734884Sjp151216 
16744884Sjp151216 	idmap_release_conn((*state)->qadh);
16754884Sjp151216 
16764520Snw141292 	free(*state);
16774520Snw141292 	*state = NULL;
16784520Snw141292 }
16794520Snw141292 
16806531Sjp151216 
16816531Sjp151216 /*
16826531Sjp151216  * This routine waits for other threads using the
16836531Sjp151216  * idmap_query_state_t structure to finish.
16846531Sjp151216  * If the reference count is greater than 1 it waits
16856531Sjp151216  * for the other threads to finish using it.
16866531Sjp151216  */
16876531Sjp151216 static
16886531Sjp151216 void
16896531Sjp151216 idmap_lookup_wait_batch(idmap_query_state_t *state)
16906531Sjp151216 {
16916531Sjp151216 	/*
16926531Sjp151216 	 * Set state to dead to stop further operation.
16936531Sjp151216 	 * stating.
16946531Sjp151216 	 * Wait for reference count to get to one
16956531Sjp151216 	 * with qstatelock locked.
16966531Sjp151216 	 */
16976531Sjp151216 	(void) pthread_mutex_lock(&qstatelock);
16986531Sjp151216 	state->qdead = 1;
16996531Sjp151216 	while (state->ref_cnt > 1) {
17006531Sjp151216 		(void) pthread_cond_wait(&state->cv, &qstatelock);
17016531Sjp151216 	}
17026531Sjp151216 	(void) pthread_mutex_unlock(&qstatelock);
17036531Sjp151216 }
17046531Sjp151216 
17056531Sjp151216 
17064520Snw141292 idmap_retcode
17075968Snw141292 idmap_lookup_batch_end(idmap_query_state_t **state)
17084520Snw141292 {
17094520Snw141292 	int		    rc = LDAP_SUCCESS;
17104520Snw141292 	idmap_retcode	    retcode = IDMAP_SUCCESS;
17115968Snw141292 	struct timeval	    timeout;
17124520Snw141292 
17135968Snw141292 	timeout.tv_sec = IDMAPD_SEARCH_TIMEOUT;
17145968Snw141292 	timeout.tv_usec = 0;
17154520Snw141292 
17164520Snw141292 	/* Process results until done or until timeout, if given */
17174520Snw141292 	while ((*state)->qinflight > 0) {
17184520Snw141292 		if ((rc = idmap_get_adobject_batch((*state)->qadh,
17195968Snw141292 		    &timeout)) != 0)
17204520Snw141292 			break;
17214520Snw141292 	}
17226531Sjp151216 	(*state)->qdead = 1;
17236531Sjp151216 	/* Wait for other threads proceesing search result to finish */
17246531Sjp151216 	idmap_lookup_wait_batch(*state);
17254520Snw141292 
17266531Sjp151216 	if (rc == -1 || (*state)->qinflight != 0)
17274520Snw141292 		retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
17284520Snw141292 
17294884Sjp151216 	idmap_lookup_release_batch(state);
17304520Snw141292 
17314520Snw141292 	return (retcode);
17324520Snw141292 }
17334520Snw141292 
17344520Snw141292 /*
17354520Snw141292  * Send one prepared search, queue up msgid, process what results are
17364520Snw141292  * available
17374520Snw141292  */
17384520Snw141292 static
17394520Snw141292 idmap_retcode
17406386Sjp151216 idmap_batch_add1(idmap_query_state_t *state, const char *filter,
17416386Sjp151216 	char *ecanonname, char *edomain, int eunixtype,
17426386Sjp151216 	char **dn, char **attr, char **value,
17436386Sjp151216 	char **canonname, char **dname,
17446386Sjp151216 	char **sid, rid_t *rid, int *sid_type, char **unixname,
17456386Sjp151216 	idmap_retcode *rc)
17464520Snw141292 {
17474520Snw141292 	idmap_retcode	retcode = IDMAP_SUCCESS;
17485731Sbaban 	int		lrc, qid, i;
17496531Sjp151216 	int		num;
17506531Sjp151216 	int		dead;
17514520Snw141292 	struct timeval	tv;
17524520Snw141292 	idmap_q_t	*q;
17535696Snw141292 	static char	*attrs[] = {
17545696Snw141292 		SAN,
17555696Snw141292 		OBJSID,
17565696Snw141292 		OBJCLASS,
17575731Sbaban 		NULL,	/* placeholder for unixname attr */
17585731Sbaban 		NULL,	/* placeholder for unixname attr */
17595696Snw141292 		NULL
17605696Snw141292 	};
17614520Snw141292 
17624520Snw141292 	qid = atomic_inc_32_nv(&state->qlastsent) - 1;
17634520Snw141292 
17644520Snw141292 	q = &(state->queries[qid]);
17654520Snw141292 
17665696Snw141292 	/*
17675696Snw141292 	 * Remember the expected canonname so we can check the results
17685696Snw141292 	 * agains it
17695696Snw141292 	 */
17705696Snw141292 	q->ecanonname = ecanonname;
17715696Snw141292 	q->edomain = edomain;
17725731Sbaban 	q->eunixtype = eunixtype;
17735447Snw141292 
17744520Snw141292 	/* Remember where to put the results */
17755696Snw141292 	q->canonname = canonname;
17765731Sbaban 	q->sid = sid;
17774520Snw141292 	q->domain = dname;
17784520Snw141292 	q->rid = rid;
17794520Snw141292 	q->sid_type = sid_type;
17804520Snw141292 	q->rc = rc;
17815731Sbaban 	q->unixname = unixname;
17826386Sjp151216 	q->dn = dn;
17836386Sjp151216 	q->attr = attr;
17846386Sjp151216 	q->value = value;
17855731Sbaban 
17865731Sbaban 	/* Add unixuser/unixgroup attribute names to the attrs list */
17875731Sbaban 	if (unixname != NULL) {
17885731Sbaban 		i = 3;
17895731Sbaban 		if (eunixtype != _IDMAP_T_GROUP &&
17905731Sbaban 		    state->ad_unixuser_attr != NULL)
17915731Sbaban 			attrs[i++] = (char *)state->ad_unixuser_attr;
17925731Sbaban 		if (eunixtype != _IDMAP_T_USER &&
17935731Sbaban 		    state->ad_unixgroup_attr != NULL)
17945731Sbaban 			attrs[i] = (char *)state->ad_unixgroup_attr;
17955731Sbaban 	}
17964520Snw141292 
17974520Snw141292 	/*
17984520Snw141292 	 * Provide sane defaults for the results in case we never hear
17994520Snw141292 	 * back from the DS before closing the connection.
18005447Snw141292 	 *
18015447Snw141292 	 * In particular we default the result to indicate a retriable
18025447Snw141292 	 * error.  The first complete matching result entry will cause
18035447Snw141292 	 * this to be set to IDMAP_SUCCESS, and the end of the results
18045447Snw141292 	 * for this search will cause this to indicate "not found" if no
18055447Snw141292 	 * result entries arrived or no complete ones matched the lookup
18065447Snw141292 	 * we were doing.
18074520Snw141292 	 */
18084520Snw141292 	*rc = IDMAP_ERR_RETRIABLE_NET_ERR;
18095731Sbaban 	if (sid_type != NULL)
18105731Sbaban 		*sid_type = _IDMAP_T_OTHER;
18115731Sbaban 	if (sid != NULL)
18125731Sbaban 		*sid = NULL;
18134520Snw141292 	if (dname != NULL)
18144520Snw141292 		*dname = NULL;
18154520Snw141292 	if (rid != NULL)
18164520Snw141292 		*rid = 0;
18176386Sjp151216 	if (dn != NULL)
18186386Sjp151216 		*dn = NULL;
18196386Sjp151216 	if (attr != NULL)
18206386Sjp151216 		*attr = NULL;
18216386Sjp151216 	if (value != NULL)
18226386Sjp151216 		*value = NULL;
18234520Snw141292 
18246531Sjp151216 	/* Check the number of queued requests first */
18256531Sjp151216 	tv.tv_sec = IDMAPD_SEARCH_TIMEOUT;
18266531Sjp151216 	tv.tv_usec = 0;
18276531Sjp151216 	while (!state->qadh->dead &&
18286531Sjp151216 	    state->qadh->num_requests > state->qadh->max_requests) {
18296531Sjp151216 		if (idmap_get_adobject_batch(state->qadh, &tv) != 0)
18306531Sjp151216 			break;
18316531Sjp151216 	}
18326531Sjp151216 
1833*6616Sdm199847 	/*
1834*6616Sdm199847 	 * Don't set *canonname to NULL because it may be pointing to the
1835*6616Sdm199847 	 * given winname. Later on if we get a canonical name from AD the
1836*6616Sdm199847 	 * old name if any will be freed before assigning the new name.
1837*6616Sdm199847 	 */
1838*6616Sdm199847 
18394520Snw141292 	/* Send this lookup, don't wait for a result here */
18406531Sjp151216 	lrc = LDAP_SUCCESS;
18414520Snw141292 	(void) pthread_mutex_lock(&state->qadh->lock);
18424520Snw141292 
18434520Snw141292 	if (!state->qadh->dead) {
18444520Snw141292 		state->qadh->idletime = time(NULL);
18455447Snw141292 		lrc = ldap_search_ext(state->qadh->ld, "",
18465696Snw141292 		    LDAP_SCOPE_SUBTREE, filter, attrs, 0, NULL, NULL,
18474520Snw141292 		    NULL, -1, &q->msgid);
18486531Sjp151216 
18496531Sjp151216 		if (lrc == LDAP_SUCCESS) {
18506531Sjp151216 			state->qadh->num_requests++;
18516531Sjp151216 		} else if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE ||
18524520Snw141292 		    lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN ||
18534520Snw141292 		    lrc == LDAP_UNWILLING_TO_PERFORM) {
18544520Snw141292 			retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
18554520Snw141292 			state->qadh->dead = 1;
18566531Sjp151216 		} else {
18574520Snw141292 			retcode = IDMAP_ERR_OTHER;
18584520Snw141292 			state->qadh->dead = 1;
18594520Snw141292 		}
18604520Snw141292 	}
18616531Sjp151216 	dead = state->qadh->dead;
18626531Sjp151216 	num = state->qadh->num_requests;
18634520Snw141292 	(void) pthread_mutex_unlock(&state->qadh->lock);
18644520Snw141292 
18656531Sjp151216 	if (dead) {
18666531Sjp151216 		if (lrc != LDAP_SUCCESS)
18676531Sjp151216 			idmapdlog(LOG_DEBUG,
18686531Sjp151216 			    "AD ldap_search_ext error (%s) "
18696531Sjp151216 			    "- %d queued requests",
18706531Sjp151216 			    ldap_err2string(lrc), num);
18714520Snw141292 		return (retcode);
18726531Sjp151216 	}
18734520Snw141292 
18744520Snw141292 	atomic_inc_32(&state->qinflight);
18754520Snw141292 
18764520Snw141292 	/*
18774520Snw141292 	 * Reap as many requests as we can _without_ waiting
18784520Snw141292 	 *
18794520Snw141292 	 * We do this to prevent any possible TCP socket buffer
18804520Snw141292 	 * starvation deadlocks.
18814520Snw141292 	 */
18824520Snw141292 	(void) memset(&tv, 0, sizeof (tv));
18834520Snw141292 	while (idmap_get_adobject_batch(state->qadh, &tv) == 0)
18844520Snw141292 		;
18854520Snw141292 
18864520Snw141292 	return (IDMAP_SUCCESS);
18874520Snw141292 }
18884520Snw141292 
18894520Snw141292 idmap_retcode
18904520Snw141292 idmap_name2sid_batch_add1(idmap_query_state_t *state,
18915731Sbaban 	const char *name, const char *dname, int eunixtype,
18926386Sjp151216 	char **dn, char **attr, char **value,
18936386Sjp151216 	char **canonname, char **sid, rid_t *rid,
18946386Sjp151216 	int *sid_type, char **unixname, idmap_retcode *rc)
18954520Snw141292 {
18964520Snw141292 	idmap_retcode	retcode;
18975447Snw141292 	int		len, samAcctNameLen;
1898*6616Sdm199847 	char		*filter = NULL, *s_name;
18995696Snw141292 	char		*ecanonname, *edomain; /* expected canonname */
19004520Snw141292 
19014520Snw141292 	/*
19025447Snw141292 	 * Strategy: search the global catalog for user/group by
19035447Snw141292 	 * sAMAccountName = user/groupname with "" as the base DN and by
19045447Snw141292 	 * userPrincipalName = user/groupname@domain.  The result
19055447Snw141292 	 * entries will be checked to conform to the name and domain
19065447Snw141292 	 * name given here.  The DN, sAMAccountName, userPrincipalName,
19075447Snw141292 	 * objectSid and objectClass of the result entries are all we
19085447Snw141292 	 * need to figure out which entries match the lookup, the SID of
19095447Snw141292 	 * the user/group and whether it is a user or a group.
19104520Snw141292 	 */
19114520Snw141292 
19124520Snw141292 	/*
19135447Snw141292 	 * We need the name and the domain name separately and as
19145447Snw141292 	 * name@domain.  We also allow the domain to be provided
19155447Snw141292 	 * separately.
19164520Snw141292 	 */
19175232Snw141292 	samAcctNameLen = strlen(name);
19185447Snw141292 
19195696Snw141292 	if ((ecanonname = strdup(name)) == NULL)
19205696Snw141292 		return (IDMAP_ERR_MEMORY);
19214520Snw141292 
19225447Snw141292 	if (dname == NULL || *dname == '\0') {
19235696Snw141292 		if ((dname = strchr(name, '@')) != NULL) {
19245696Snw141292 			/* 'name' is qualified with a domain name */
19255696Snw141292 			if ((edomain = strdup(dname + 1)) == NULL) {
19265696Snw141292 				free(ecanonname);
19275696Snw141292 				return (IDMAP_ERR_MEMORY);
19285696Snw141292 			}
19295696Snw141292 			*strchr(ecanonname, '@') = '\0';
19305696Snw141292 		} else {
19316097Snw141292 			/*
19326097Snw141292 			 * 'name' not qualified and dname not given
19336097Snw141292 			 *
19346097Snw141292 			 * Note: ad->dflt_w2k_dom cannot be NULL - see
19356097Snw141292 			 * idmap_ad_alloc()
19366097Snw141292 			 */
19376097Snw141292 			if (*state->qadh->owner->dflt_w2k_dom == '\0') {
19385696Snw141292 				free(ecanonname);
19395696Snw141292 				return (IDMAP_ERR_DOMAIN);
19405696Snw141292 			}
19415696Snw141292 			edomain = strdup(state->qadh->owner->dflt_w2k_dom);
19425696Snw141292 			if (edomain == NULL) {
19435696Snw141292 				free(ecanonname);
19445696Snw141292 				return (IDMAP_ERR_MEMORY);
19455696Snw141292 			}
19465696Snw141292 		}
19475696Snw141292 	} else {
19485696Snw141292 		if ((edomain = strdup(dname)) == NULL) {
19495696Snw141292 			free(ecanonname);
19505447Snw141292 			return (IDMAP_ERR_MEMORY);
19515696Snw141292 		}
19525447Snw141292 	}
19534520Snw141292 
1954*6616Sdm199847 	s_name = sanitize_for_ldap_filter(name);
1955*6616Sdm199847 	if (s_name == NULL) {
1956*6616Sdm199847 		free(ecanonname);
1957*6616Sdm199847 		free(edomain);
1958*6616Sdm199847 		return (IDMAP_ERR_MEMORY);
1959*6616Sdm199847 	}
1960*6616Sdm199847 
19614520Snw141292 	/* Assemble filter */
1962*6616Sdm199847 	len = snprintf(NULL, 0, SANFILTER, samAcctNameLen, s_name) + 1;
19635447Snw141292 	if ((filter = (char *)malloc(len)) == NULL) {
19645696Snw141292 		free(ecanonname);
1965*6616Sdm199847 		free(edomain);
1966*6616Sdm199847 		if (s_name != name)
1967*6616Sdm199847 			free(s_name);
19684520Snw141292 		return (IDMAP_ERR_MEMORY);
19694520Snw141292 	}
1970*6616Sdm199847 	(void) snprintf(filter, len, SANFILTER, samAcctNameLen, s_name);
1971*6616Sdm199847 	if (s_name != name)
1972*6616Sdm199847 		free(s_name);
19734520Snw141292 
19745696Snw141292 	retcode = idmap_batch_add1(state, filter, ecanonname, edomain,
19756386Sjp151216 	    eunixtype, dn, attr, value, canonname, NULL, sid, rid, sid_type,
19766386Sjp151216 	    unixname, rc);
19774520Snw141292 
19784520Snw141292 	free(filter);
19794520Snw141292 
19804520Snw141292 	return (retcode);
19814520Snw141292 }
19824520Snw141292 
19834520Snw141292 idmap_retcode
19844520Snw141292 idmap_sid2name_batch_add1(idmap_query_state_t *state,
19855731Sbaban 	const char *sid, const rid_t *rid, int eunixtype,
19866386Sjp151216 	char **dn, char **attr, char **value,
19876386Sjp151216 	char **name, char **dname, int *sid_type,
19886386Sjp151216 	char **unixname, idmap_retcode *rc)
19894520Snw141292 {
19904520Snw141292 	idmap_retcode	retcode;
19914520Snw141292 	int		flen, ret;
19924520Snw141292 	char		*filter = NULL;
19934520Snw141292 	char		cbinsid[MAXHEXBINSID + 1];
19944520Snw141292 
19954520Snw141292 	/*
19964520Snw141292 	 * Strategy: search [the global catalog] for user/group by
19974520Snw141292 	 * objectSid = SID with empty base DN.  The DN, sAMAccountName
19984520Snw141292 	 * and objectClass of the result are all we need to figure out
19994520Snw141292 	 * the name of the SID and whether it is a user, a group or a
20004520Snw141292 	 * computer.
20014520Snw141292 	 */
20024520Snw141292 
20034520Snw141292 	ret = idmap_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid));
20044520Snw141292 	if (ret != 0)
20054520Snw141292 		return (IDMAP_ERR_SID);
20064520Snw141292 
20074520Snw141292 	/* Assemble filter */
20085447Snw141292 	flen = snprintf(NULL, 0, OBJSIDFILTER, cbinsid) + 1;
20094520Snw141292 	if ((filter = (char *)malloc(flen)) == NULL)
20104520Snw141292 		return (IDMAP_ERR_MEMORY);
20115447Snw141292 	(void) snprintf(filter, flen, OBJSIDFILTER, cbinsid);
20124520Snw141292 
20135731Sbaban 	retcode = idmap_batch_add1(state, filter, NULL, NULL, eunixtype,
20146386Sjp151216 	    dn, attr, value, name, dname, NULL, NULL, sid_type, unixname, rc);
20154520Snw141292 
20164520Snw141292 	free(filter);
20174520Snw141292 
20184520Snw141292 	return (retcode);
20194520Snw141292 }
20205731Sbaban 
20215731Sbaban idmap_retcode
20225731Sbaban idmap_unixname2sid_batch_add1(idmap_query_state_t *state,
20235731Sbaban 	const char *unixname, int is_user, int is_wuser,
20246386Sjp151216 	char **dn, char **attr, char **value,
20256386Sjp151216 	char **sid, rid_t *rid, char **name,
20266386Sjp151216 	char **dname, int *sid_type, idmap_retcode *rc)
20275731Sbaban {
20285731Sbaban 	idmap_retcode	retcode;
20295731Sbaban 	int		len, ulen;
2030*6616Sdm199847 	char		*filter = NULL, *s_unixname;
20315731Sbaban 	const char	*attrname = NULL;
20325731Sbaban 
20335731Sbaban 	/* Get unixuser or unixgroup AD attribute name */
20345731Sbaban 	attrname = (is_user) ?
20355731Sbaban 	    state->ad_unixuser_attr : state->ad_unixgroup_attr;
20365731Sbaban 	if (attrname == NULL)
20375731Sbaban 		return (IDMAP_ERR_NOTFOUND);
20385731Sbaban 
2039*6616Sdm199847 	s_unixname = sanitize_for_ldap_filter(unixname);
2040*6616Sdm199847 	if (s_unixname == NULL)
2041*6616Sdm199847 		return (IDMAP_ERR_MEMORY);
2042*6616Sdm199847 
20435731Sbaban 	/*  Assemble filter */
20445731Sbaban 	ulen = strlen(unixname);
20455731Sbaban 	len = snprintf(NULL, 0, "(&(objectclass=%s)(%s=%.*s))",
2046*6616Sdm199847 	    is_wuser ? "user" : "group", attrname, ulen, s_unixname) + 1;
2047*6616Sdm199847 	if ((filter = (char *)malloc(len)) == NULL) {
2048*6616Sdm199847 		if (s_unixname != unixname)
2049*6616Sdm199847 			free(s_unixname);
20505731Sbaban 		return (IDMAP_ERR_MEMORY);
2051*6616Sdm199847 	}
20525731Sbaban 	(void) snprintf(filter, len, "(&(objectclass=%s)(%s=%.*s))",
2053*6616Sdm199847 	    is_wuser ? "user" : "group", attrname, ulen, s_unixname);
2054*6616Sdm199847 	if (s_unixname != unixname)
2055*6616Sdm199847 		free(s_unixname);
20565731Sbaban 
20575731Sbaban 	retcode = idmap_batch_add1(state, filter, NULL, NULL,
20586386Sjp151216 	    _IDMAP_T_UNDEF, dn, NULL, NULL, name, dname, sid, rid, sid_type,
20596386Sjp151216 	    NULL, rc);
20606386Sjp151216 
20616386Sjp151216 	if (retcode == IDMAP_SUCCESS && attr != NULL) {
20626386Sjp151216 		if ((*attr = strdup(attrname)) == NULL)
20636386Sjp151216 			retcode = IDMAP_ERR_MEMORY;
20646386Sjp151216 	}
20656386Sjp151216 
20666386Sjp151216 	if (retcode == IDMAP_SUCCESS && value != NULL) {
20676386Sjp151216 		if (ulen > 0) {
20686386Sjp151216 			if ((*value = strdup(unixname)) == NULL)
20696386Sjp151216 				retcode = IDMAP_ERR_MEMORY;
20706386Sjp151216 		}
20716386Sjp151216 		else
20726386Sjp151216 			*value = NULL;
20736386Sjp151216 	}
20745731Sbaban 
20755731Sbaban 	free(filter);
20765731Sbaban 
20775731Sbaban 	return (retcode);
20785731Sbaban }
2079