xref: /titanic_50/usr/src/cmd/idmap/idmapd/adutils.c (revision c8e261054d98729a8718903716b9b8a512d8b693)
1c5c4113dSnw141292 /*
2c5c4113dSnw141292  * CDDL HEADER START
3c5c4113dSnw141292  *
4c5c4113dSnw141292  * The contents of this file are subject to the terms of the
5c5c4113dSnw141292  * Common Development and Distribution License (the "License").
6c5c4113dSnw141292  * You may not use this file except in compliance with the License.
7c5c4113dSnw141292  *
8c5c4113dSnw141292  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9c5c4113dSnw141292  * or http://www.opensolaris.org/os/licensing.
10c5c4113dSnw141292  * See the License for the specific language governing permissions
11c5c4113dSnw141292  * and limitations under the License.
12c5c4113dSnw141292  *
13c5c4113dSnw141292  * When distributing Covered Code, include this CDDL HEADER in each
14c5c4113dSnw141292  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15c5c4113dSnw141292  * If applicable, add the following below this CDDL HEADER, with the
16c5c4113dSnw141292  * fields enclosed by brackets "[]" replaced with your own identifying
17c5c4113dSnw141292  * information: Portions Copyright [yyyy] [name of copyright owner]
18c5c4113dSnw141292  *
19c5c4113dSnw141292  * CDDL HEADER END
20c5c4113dSnw141292  */
21c5c4113dSnw141292 
22c5c4113dSnw141292 /*
23c5c4113dSnw141292  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24c5c4113dSnw141292  * Use is subject to license terms.
25c5c4113dSnw141292  */
26c5c4113dSnw141292 
27c5c4113dSnw141292 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28c5c4113dSnw141292 
29c5c4113dSnw141292 /*
30c5c4113dSnw141292  * Processes name2sid & sid2name batched lookups for a given user or
31c5c4113dSnw141292  * computer from an AD Directory server using GSSAPI authentication
32c5c4113dSnw141292  */
33c5c4113dSnw141292 
34c5c4113dSnw141292 #include <stdio.h>
35c5c4113dSnw141292 #include <stdlib.h>
36c5c4113dSnw141292 #include <alloca.h>
37c5c4113dSnw141292 #include <string.h>
38c5c4113dSnw141292 #include <strings.h>
39c5c4113dSnw141292 #include <lber.h>
40c5c4113dSnw141292 #include <ldap.h>
41c5c4113dSnw141292 #include <sasl/sasl.h>
42c5c4113dSnw141292 #include <string.h>
43c5c4113dSnw141292 #include <ctype.h>
44c5c4113dSnw141292 #include <pthread.h>
45c5c4113dSnw141292 #include <synch.h>
46c5c4113dSnw141292 #include <atomic.h>
47c5c4113dSnw141292 #include <errno.h>
48c5c4113dSnw141292 #include <assert.h>
49c5c4113dSnw141292 #include <limits.h>
50c5c4113dSnw141292 #include "idmapd.h"
51c5c4113dSnw141292 
52c5c4113dSnw141292 /*
53c5c4113dSnw141292  * Internal data structures for this code
54c5c4113dSnw141292  */
55c5c4113dSnw141292 
56c5c4113dSnw141292 /* Attribute names and filter format strings */
57c5c4113dSnw141292 #define	OBJECTSID	"objectSid"
58c5c4113dSnw141292 #define	OBJECTSIDFILTER	"(objectSid=%s)"
59c5c4113dSnw141292 #define	SAMACCOUNTNAME	"sAMAccountName"
60c5c4113dSnw141292 #define	SANFILTER	"(sAMAccountName=%.*s)"
61c5c4113dSnw141292 #define	OBJECTCLASS	"objectClass"
62c5c4113dSnw141292 
63c5c4113dSnw141292 /*
64c5c4113dSnw141292  * This should really be in some <sys/sid.h> file or so; we have a
65c5c4113dSnw141292  * private version of sid_t, and so must other components of ON until we
66c5c4113dSnw141292  * rationalize this.
67c5c4113dSnw141292  */
68c5c4113dSnw141292 typedef struct sid {
69c5c4113dSnw141292 	uchar_t		version;
70c5c4113dSnw141292 	uchar_t		sub_authority_count;
71c5c4113dSnw141292 	uint64_t	authority;  /* really, 48-bits */
72c5c4113dSnw141292 	rid_t		sub_authorities[SID_MAX_SUB_AUTHORITIES];
73c5c4113dSnw141292 } sid_t;
74c5c4113dSnw141292 
75c5c4113dSnw141292 /* A single DS */
76c5c4113dSnw141292 typedef struct ad_host {
77c5c4113dSnw141292 	struct ad_host		*next;
78c5c4113dSnw141292 	ad_t			*owner;		/* ad_t to which this belongs */
79c5c4113dSnw141292 	pthread_mutex_t		lock;
80c5c4113dSnw141292 	LDAP			*ld;		/* LDAP connection */
81c5c4113dSnw141292 	uint32_t		ref;		/* ref count */
82c5c4113dSnw141292 	time_t			idletime;	/* time since last activity */
83c5c4113dSnw141292 	int			dead;		/* error on LDAP connection */
84c5c4113dSnw141292 	/*
85c5c4113dSnw141292 	 * Used to distinguish between different instances of LDAP
86c5c4113dSnw141292 	 * connections to this same DS.  We need this so we never mix up
87c5c4113dSnw141292 	 * results for a given msgID from one connection with those of
88c5c4113dSnw141292 	 * another earlier connection where two batch state structures
89c5c4113dSnw141292 	 * share this ad_host object but used different LDAP connections
90c5c4113dSnw141292 	 * to send their LDAP searches.
91c5c4113dSnw141292 	 */
92c5c4113dSnw141292 	uint64_t		generation;
93c5c4113dSnw141292 
94c5c4113dSnw141292 	/* LDAP DS info */
95c5c4113dSnw141292 	char			*host;
96c5c4113dSnw141292 	int			port;
97c5c4113dSnw141292 
98c5c4113dSnw141292 	/* hardwired to SASL GSSAPI only for now */
99c5c4113dSnw141292 	char			*saslmech;
100c5c4113dSnw141292 	unsigned		saslflags;
101c5c4113dSnw141292 } ad_host_t;
102c5c4113dSnw141292 
103c5c4113dSnw141292 /* A set of DSs for a given AD partition; ad_t typedef comes from  adutil.h */
104c5c4113dSnw141292 struct ad {
105c5c4113dSnw141292 	char			*dflt_w2k_dom;	/* used to qualify bare names */
106c5c4113dSnw141292 	char			*basedn;	/* derived from dflt domain */
107c5c4113dSnw141292 	pthread_mutex_t		lock;
108c5c4113dSnw141292 	uint32_t		ref;
109*c8e26105Sjp151216 	ad_host_t		*last_adh;
110c5c4113dSnw141292 	idmap_ad_partition_t	partition;	/* Data or global catalog? */
111c5c4113dSnw141292 };
112c5c4113dSnw141292 
113c5c4113dSnw141292 /*
114c5c4113dSnw141292  * A place to put the results of a batched (async) query
115c5c4113dSnw141292  *
116c5c4113dSnw141292  * There is one of these for every query added to a batch object
117c5c4113dSnw141292  * (idmap_query_state, see below).
118c5c4113dSnw141292  */
119c5c4113dSnw141292 typedef struct idmap_q {
120c5c4113dSnw141292 	char			**result;	/* name or stringified SID */
121c5c4113dSnw141292 	char			**domain;	/* name of domain of object */
122c5c4113dSnw141292 	rid_t			*rid;		/* for n2s, if not NULL */
123c5c4113dSnw141292 	int			*sid_type;	/* if not NULL */
124c5c4113dSnw141292 	idmap_retcode		*rc;
125c5c4113dSnw141292 	int			msgid;		/* LDAP message ID */
126c5c4113dSnw141292 	/*
127c5c4113dSnw141292 	 * Bitfield containing state needed to know when we're done
128c5c4113dSnw141292 	 * processing search results related to this query's LDAP
129c5c4113dSnw141292 	 * searches.  Mostly self-explanatory.
130c5c4113dSnw141292 	 */
131c5c4113dSnw141292 	uint16_t		n2s : 1;	/* name->SID or SID->name? */
132c5c4113dSnw141292 	uint16_t		got_reply : 1;
133c5c4113dSnw141292 	uint16_t		got_results : 1;
134c5c4113dSnw141292 	uint16_t		got_objectSid : 1;
135c5c4113dSnw141292 	uint16_t		got_objectClass : 1;
136c5c4113dSnw141292 	uint16_t		got_samAcctName : 1;
137c5c4113dSnw141292 } idmap_q_t;
138c5c4113dSnw141292 
139c5c4113dSnw141292 /* Batch context structure; typedef is in header file */
140c5c4113dSnw141292 struct idmap_query_state {
141c5c4113dSnw141292 	idmap_query_state_t	*next;
142c5c4113dSnw141292 	int			qcount;		/* how many queries */
14384decf41Sjp151216 	int			ref_cnt;	/* reference count */
14484decf41Sjp151216 	pthread_cond_t		cv;		/* Condition wait variable */
145c5c4113dSnw141292 	uint32_t		qlastsent;
146c5c4113dSnw141292 	uint32_t		qinflight;	/* how many queries in flight */
147c5c4113dSnw141292 	uint16_t		qdead;		/* oops, lost LDAP connection */
148c5c4113dSnw141292 	ad_host_t		*qadh;		/* LDAP connection */
149c5c4113dSnw141292 	uint64_t		qadh_gen;	/* same as qadh->generation */
150c5c4113dSnw141292 	idmap_q_t		queries[1];	/* array of query results */
151c5c4113dSnw141292 };
152c5c4113dSnw141292 
153c5c4113dSnw141292 /*
154c5c4113dSnw141292  * List of query state structs -- needed so we can "route" LDAP results
155c5c4113dSnw141292  * to the right context if multiple threads should be using the same
156c5c4113dSnw141292  * connection concurrently
157c5c4113dSnw141292  */
158c5c4113dSnw141292 static idmap_query_state_t	*qstatehead = NULL;
159c5c4113dSnw141292 static pthread_mutex_t		qstatelock = PTHREAD_MUTEX_INITIALIZER;
160c5c4113dSnw141292 
161c5c4113dSnw141292 /*
162c5c4113dSnw141292  * List of DSs, needed by the idle connection reaper thread
163c5c4113dSnw141292  */
164c5c4113dSnw141292 static ad_host_t	*host_head = NULL;
165651c0131Sbaban static pthread_t	reaperid = 0;
166c5c4113dSnw141292 static pthread_mutex_t	adhostlock = PTHREAD_MUTEX_INITIALIZER;
167c5c4113dSnw141292 
16884decf41Sjp151216 
16984decf41Sjp151216 static void
17084decf41Sjp151216 idmap_lookup_unlock_batch(idmap_query_state_t **state);
17184decf41Sjp151216 
172*c8e26105Sjp151216 static void
173*c8e26105Sjp151216 delete_ds(ad_t *ad, const char *host, int port);
17484decf41Sjp151216 
175c5c4113dSnw141292 /*ARGSUSED*/
176c5c4113dSnw141292 static int
177c5c4113dSnw141292 idmap_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts) {
178c5c4113dSnw141292 	sasl_interact_t	*interact;
179c5c4113dSnw141292 
180c5c4113dSnw141292 	if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE)
181c5c4113dSnw141292 		return (LDAP_PARAM_ERROR);
182c5c4113dSnw141292 
183c5c4113dSnw141292 	/* There should be no extra arguemnts for SASL/GSSAPI authentication */
184c5c4113dSnw141292 	for (interact = prompts; interact->id != SASL_CB_LIST_END;
185c5c4113dSnw141292 			interact++) {
186c5c4113dSnw141292 		interact->result = NULL;
187c5c4113dSnw141292 		interact->len = 0;
188c5c4113dSnw141292 	}
189c5c4113dSnw141292 	return (LDAP_SUCCESS);
190c5c4113dSnw141292 }
191c5c4113dSnw141292 
192c5c4113dSnw141292 /* Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com" */
193c5c4113dSnw141292 static
194c5c4113dSnw141292 char *
195c5c4113dSnw141292 dns2dn(const char *dns)
196c5c4113dSnw141292 {
197c5c4113dSnw141292 	int nameparts;
198c5c4113dSnw141292 
199c5c4113dSnw141292 	/* Sigh, ldap_dns_to_dn()'s first arg should be a const char * */
200c5c4113dSnw141292 	return (ldap_dns_to_dn((char *)dns, &nameparts));
201c5c4113dSnw141292 }
202c5c4113dSnw141292 
203c5c4113dSnw141292 /*
204c5c4113dSnw141292  * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other
205c5c4113dSnw141292  * attributes (CN, etc...)
206c5c4113dSnw141292  */
207c5c4113dSnw141292 static
208c5c4113dSnw141292 char *
209c5c4113dSnw141292 dn2dns(const char *dn)
210c5c4113dSnw141292 {
211c5c4113dSnw141292 	char **rdns = NULL;
212c5c4113dSnw141292 	char **attrs = NULL;
213c5c4113dSnw141292 	char **labels = NULL;
214c5c4113dSnw141292 	char *dns = NULL;
215c5c4113dSnw141292 	char **rdn, **attr, **label;
216c5c4113dSnw141292 	int maxlabels = 5;
217c5c4113dSnw141292 	int nlabels = 0;
218c5c4113dSnw141292 	int dnslen;
219c5c4113dSnw141292 
220c5c4113dSnw141292 	/*
221c5c4113dSnw141292 	 * There is no reverse of ldap_dns_to_dn() in our libldap, so we
222c5c4113dSnw141292 	 * have to do the hard work here for now.
223c5c4113dSnw141292 	 */
224c5c4113dSnw141292 
225c5c4113dSnw141292 	/*
226c5c4113dSnw141292 	 * This code is much too liberal: it looks for "dc" attributes
227c5c4113dSnw141292 	 * in all RDNs of the DN.  In theory this could cause problems
228c5c4113dSnw141292 	 * if people were to use "dc" in nodes other than the root of
229c5c4113dSnw141292 	 * the tree, but in practice noone, least of all Active
230c5c4113dSnw141292 	 * Directory, does that.
231c5c4113dSnw141292 	 *
232c5c4113dSnw141292 	 * On the other hand, this code is much too conservative: it
233c5c4113dSnw141292 	 * does not make assumptions about ldap_explode_dn(), and _that_
234c5c4113dSnw141292 	 * is the true for looking at every attr of every RDN.
235c5c4113dSnw141292 	 *
236c5c4113dSnw141292 	 * Since we only ever look at dc and those must be DNS labels,
237c5c4113dSnw141292 	 * at least until we get around to supporting IDN here we
238c5c4113dSnw141292 	 * shouldn't see escaped labels from AD nor from libldap, though
239c5c4113dSnw141292 	 * the spec (RFC2253) does allow libldap to escape things that
240c5c4113dSnw141292 	 * don't need escaping -- if that should ever happen then
241c5c4113dSnw141292 	 * libldap will need a spanking, and we can take care of that.
242c5c4113dSnw141292 	 */
243c5c4113dSnw141292 
244c5c4113dSnw141292 	/* Explode a DN into RDNs */
245c5c4113dSnw141292 	if ((rdns = ldap_explode_dn(dn, 0)) == NULL)
246c5c4113dSnw141292 		return (NULL);
247c5c4113dSnw141292 
248c5c4113dSnw141292 	labels = calloc(maxlabels + 1, sizeof (char *));
249c5c4113dSnw141292 	label = labels;
250c5c4113dSnw141292 
251c5c4113dSnw141292 	for (rdn = rdns; *rdn != NULL; rdn++) {
252c5c4113dSnw141292 		if (attrs != NULL)
253c5c4113dSnw141292 			ldap_value_free(attrs);
254c5c4113dSnw141292 
255c5c4113dSnw141292 		/* Explode each RDN, look for DC attr, save val as DNS label */
256c5c4113dSnw141292 		if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL)
257c5c4113dSnw141292 			goto done;
258c5c4113dSnw141292 
259c5c4113dSnw141292 		for (attr = attrs; *attr != NULL; attr++) {
260c5c4113dSnw141292 			if (strncasecmp(*attr, "dc=", 3) != 0)
261c5c4113dSnw141292 				continue;
262c5c4113dSnw141292 
263c5c4113dSnw141292 			/* Found a DNS label */
264c5c4113dSnw141292 			labels[nlabels++] = strdup((*attr) + 3);
265c5c4113dSnw141292 
266c5c4113dSnw141292 			if (nlabels == maxlabels) {
267c5c4113dSnw141292 				char **tmp;
268c5c4113dSnw141292 				tmp = realloc(labels,
269c5c4113dSnw141292 				    sizeof (char *) * (maxlabels + 1));
270c5c4113dSnw141292 
271c5c4113dSnw141292 				if (tmp == NULL)
272c5c4113dSnw141292 					goto done;
273c5c4113dSnw141292 
274c5c4113dSnw141292 				labels = tmp;
275c5c4113dSnw141292 				labels[nlabels] = NULL;
276c5c4113dSnw141292 			}
277c5c4113dSnw141292 
278c5c4113dSnw141292 			/* There should be just one DC= attr per-RDN */
279c5c4113dSnw141292 			break;
280c5c4113dSnw141292 		}
281c5c4113dSnw141292 	}
282c5c4113dSnw141292 
283c5c4113dSnw141292 	/*
284c5c4113dSnw141292 	 * Got all the labels, now join with '.'
285c5c4113dSnw141292 	 *
286c5c4113dSnw141292 	 * We need room for nlabels - 1 periods ('.'), one nul
287c5c4113dSnw141292 	 * terminator, and the strlen() of each label.
288c5c4113dSnw141292 	 */
289c5c4113dSnw141292 	dnslen = nlabels;
290c5c4113dSnw141292 	for (label = labels; *label != NULL; label++)
291c5c4113dSnw141292 		dnslen += strlen(*label);
292c5c4113dSnw141292 
293c5c4113dSnw141292 	if ((dns = malloc(dnslen)) == NULL)
294c5c4113dSnw141292 		goto done;
295c5c4113dSnw141292 
296c5c4113dSnw141292 	*dns = '\0';
297c5c4113dSnw141292 
298c5c4113dSnw141292 	for (label = labels; *label != NULL; label++) {
299c5c4113dSnw141292 		(void) strlcat(dns, *label, dnslen);
300c5c4113dSnw141292 		/*
301c5c4113dSnw141292 		 * NOTE: the last '.' won't be appended -- there's no room
302c5c4113dSnw141292 		 * for it!
303c5c4113dSnw141292 		 */
304c5c4113dSnw141292 		(void) strlcat(dns, ".", dnslen);
305c5c4113dSnw141292 	}
306c5c4113dSnw141292 
307c5c4113dSnw141292 done:
308c5c4113dSnw141292 	if (labels != NULL) {
309c5c4113dSnw141292 		for (label = labels; *label != NULL; label++)
310c5c4113dSnw141292 			free(*label);
311c5c4113dSnw141292 		free(labels);
312c5c4113dSnw141292 	}
313c5c4113dSnw141292 	if (attrs != NULL)
314c5c4113dSnw141292 		ldap_value_free(attrs);
315c5c4113dSnw141292 	if (rdns != NULL)
316c5c4113dSnw141292 		ldap_value_free(rdns);
317c5c4113dSnw141292 
318c5c4113dSnw141292 	return (dns);
319c5c4113dSnw141292 }
320c5c4113dSnw141292 
321c5c4113dSnw141292 /*
322c5c4113dSnw141292  * Keep connection management simple for now, extend or replace later
323c5c4113dSnw141292  * with updated libsldap code.
324c5c4113dSnw141292  */
325c5c4113dSnw141292 #define	ADREAPERSLEEP	60
326c5c4113dSnw141292 #define	ADCONN_TIME	300
327c5c4113dSnw141292 
328c5c4113dSnw141292 /*
329c5c4113dSnw141292  * Idle connection reaping side of connection management
330c5c4113dSnw141292  *
331c5c4113dSnw141292  * Every minute wake up and look for connections that have been idle for
332c5c4113dSnw141292  * five minutes or more and close them.
333c5c4113dSnw141292  */
334c5c4113dSnw141292 /*ARGSUSED*/
335c5c4113dSnw141292 static
336c5c4113dSnw141292 void
337c5c4113dSnw141292 adreaper(void *arg)
338c5c4113dSnw141292 {
339c5c4113dSnw141292 	ad_host_t	*adh;
340c5c4113dSnw141292 	time_t		now;
341c5c4113dSnw141292 	timespec_t	ts;
342c5c4113dSnw141292 
343c5c4113dSnw141292 	ts.tv_sec = ADREAPERSLEEP;
344c5c4113dSnw141292 	ts.tv_nsec = 0;
345c5c4113dSnw141292 
346c5c4113dSnw141292 	for (;;) {
347c5c4113dSnw141292 		/*
348c5c4113dSnw141292 		 * nanosleep(3RT) is thead-safe (no SIGALRM) and more
349c5c4113dSnw141292 		 * portable than usleep(3C)
350c5c4113dSnw141292 		 */
351c5c4113dSnw141292 		(void) nanosleep(&ts, NULL);
352c5c4113dSnw141292 		(void) pthread_mutex_lock(&adhostlock);
353c5c4113dSnw141292 		now = time(NULL);
354c5c4113dSnw141292 		for (adh = host_head; adh != NULL; adh = adh->next) {
355c5c4113dSnw141292 			(void) pthread_mutex_lock(&adh->lock);
356c5c4113dSnw141292 			if (adh->ref == 0 && adh->idletime != 0 &&
357c5c4113dSnw141292 			    adh->idletime + ADCONN_TIME < now) {
358c5c4113dSnw141292 				if (adh->ld) {
359c5c4113dSnw141292 					(void) ldap_unbind(adh->ld);
360c5c4113dSnw141292 					adh->ld = NULL;
361c5c4113dSnw141292 					adh->idletime = 0;
362c5c4113dSnw141292 					adh->ref = 0;
363c5c4113dSnw141292 				}
364c5c4113dSnw141292 			}
365c5c4113dSnw141292 			(void) pthread_mutex_unlock(&adh->lock);
366c5c4113dSnw141292 		}
367c5c4113dSnw141292 		(void) pthread_mutex_unlock(&adhostlock);
368c5c4113dSnw141292 	}
369c5c4113dSnw141292 }
370c5c4113dSnw141292 
371c5c4113dSnw141292 int
372c5c4113dSnw141292 idmap_ad_alloc(ad_t **new_ad, const char *default_domain,
373c5c4113dSnw141292 		idmap_ad_partition_t part)
374c5c4113dSnw141292 {
375c5c4113dSnw141292 	ad_t *ad;
376c5c4113dSnw141292 
377c5c4113dSnw141292 	*new_ad = NULL;
378c5c4113dSnw141292 
379c5c4113dSnw141292 	if ((default_domain == NULL || *default_domain == '\0') &&
380c5c4113dSnw141292 	    part != IDMAP_AD_GLOBAL_CATALOG)
381c5c4113dSnw141292 		return (-1);
382c5c4113dSnw141292 
383c5c4113dSnw141292 	if ((ad = calloc(1, sizeof (ad_t))) == NULL)
384c5c4113dSnw141292 		return (-1);
385c5c4113dSnw141292 
386c5c4113dSnw141292 	ad->ref = 1;
387c5c4113dSnw141292 	ad->partition = part;
388c5c4113dSnw141292 
389c5c4113dSnw141292 	/*
390c5c4113dSnw141292 	 * If default_domain is NULL, deal, deferring errors until
391c5c4113dSnw141292 	 * idmap_lookup_batch_start() -- this makes it easier on the
392c5c4113dSnw141292 	 * caller, who can simply observe lookups failing as opposed to
393c5c4113dSnw141292 	 * having to conditionalize calls to lookups according to
394c5c4113dSnw141292 	 * whether it has a non-NULL ad_t *.
395c5c4113dSnw141292 	 */
396c5c4113dSnw141292 	if (default_domain == NULL)
397c5c4113dSnw141292 		default_domain = "";
398c5c4113dSnw141292 
399c5c4113dSnw141292 	if ((ad->dflt_w2k_dom = strdup(default_domain)) == NULL)
400c5c4113dSnw141292 		goto err;
401c5c4113dSnw141292 
402c5c4113dSnw141292 	/* If default_domain is empty, deal; see above */
403c5c4113dSnw141292 	if (*default_domain == '\0') {
404c5c4113dSnw141292 		if ((ad->basedn = strdup("")) == NULL)
405c5c4113dSnw141292 			goto err;
406c5c4113dSnw141292 	} else if ((ad->basedn = dns2dn(default_domain)) == NULL) {
407c5c4113dSnw141292 		goto err;
408c5c4113dSnw141292 	}
409c5c4113dSnw141292 
410c5c4113dSnw141292 	if (pthread_mutex_init(&ad->lock, NULL) != 0)
411c5c4113dSnw141292 		goto err;
412c5c4113dSnw141292 
413c5c4113dSnw141292 	*new_ad = ad;
414c5c4113dSnw141292 
415c5c4113dSnw141292 	return (0);
416c5c4113dSnw141292 err:
417c5c4113dSnw141292 	if (ad->dflt_w2k_dom != NULL)
418c5c4113dSnw141292 		free(ad->dflt_w2k_dom);
419c5c4113dSnw141292 	if (ad->basedn != NULL)
420c5c4113dSnw141292 		free(ad->basedn);
421c5c4113dSnw141292 	free(ad);
422c5c4113dSnw141292 	return (-1);
423c5c4113dSnw141292 }
424c5c4113dSnw141292 
425c5c4113dSnw141292 
426c5c4113dSnw141292 void
427c5c4113dSnw141292 idmap_ad_free(ad_t **ad)
428c5c4113dSnw141292 {
429c5c4113dSnw141292 	ad_host_t *p;
430*c8e26105Sjp151216 	ad_host_t *prev;
431c5c4113dSnw141292 
432c5c4113dSnw141292 	if (ad == NULL || *ad == NULL)
433c5c4113dSnw141292 		return;
434c5c4113dSnw141292 
435c5c4113dSnw141292 	(void) pthread_mutex_lock(&(*ad)->lock);
436c5c4113dSnw141292 
437c5c4113dSnw141292 	if (atomic_dec_32_nv(&(*ad)->ref) > 0) {
438c5c4113dSnw141292 		(void) pthread_mutex_unlock(&(*ad)->lock);
439c5c4113dSnw141292 		*ad = NULL;
440c5c4113dSnw141292 		return;
441c5c4113dSnw141292 	}
442c5c4113dSnw141292 
443*c8e26105Sjp151216 	(void) pthread_mutex_lock(&adhostlock);
444*c8e26105Sjp151216 	prev = NULL;
445*c8e26105Sjp151216 	p = host_head;
446*c8e26105Sjp151216 	while (p != NULL) {
447*c8e26105Sjp151216 		if (p->owner != (*ad)) {
448*c8e26105Sjp151216 			prev = p;
449*c8e26105Sjp151216 			p = p->next;
450c5c4113dSnw141292 			continue;
451*c8e26105Sjp151216 		} else {
452*c8e26105Sjp151216 			delete_ds((*ad), p->host, p->port);
453*c8e26105Sjp151216 			if (prev == NULL)
454*c8e26105Sjp151216 				p = host_head;
455*c8e26105Sjp151216 			else
456*c8e26105Sjp151216 				p = prev->next;
457c5c4113dSnw141292 		}
458*c8e26105Sjp151216 	}
459*c8e26105Sjp151216 	(void) pthread_mutex_unlock(&adhostlock);
460c5c4113dSnw141292 
461c5c4113dSnw141292 	free((*ad)->basedn);
462c5c4113dSnw141292 
463c5c4113dSnw141292 	(void) pthread_mutex_unlock(&(*ad)->lock);
464c5c4113dSnw141292 	(void) pthread_mutex_destroy(&(*ad)->lock);
465c5c4113dSnw141292 
466c5c4113dSnw141292 	free(*ad);
467c5c4113dSnw141292 
468c5c4113dSnw141292 	*ad = NULL;
469c5c4113dSnw141292 }
470c5c4113dSnw141292 
471*c8e26105Sjp151216 
472c5c4113dSnw141292 static
473c5c4113dSnw141292 int
474c5c4113dSnw141292 idmap_open_conn(ad_host_t *adh)
475c5c4113dSnw141292 {
476c5c4113dSnw141292 	int zero = 0;
477c5c4113dSnw141292 	int timeoutms = 30 * 1000;
478*c8e26105Sjp151216 	int ldversion, rc;
479*c8e26105Sjp151216 
480*c8e26105Sjp151216 	if (adh == NULL)
481*c8e26105Sjp151216 		return (0);
482*c8e26105Sjp151216 
483*c8e26105Sjp151216 	(void) pthread_mutex_lock(&adh->lock);
484*c8e26105Sjp151216 
485*c8e26105Sjp151216 	if (!adh->dead && adh->ld != NULL)
486*c8e26105Sjp151216 		/* done! */
487*c8e26105Sjp151216 		goto out;
488*c8e26105Sjp151216 
489*c8e26105Sjp151216 	if (adh->ld != NULL) {
490*c8e26105Sjp151216 		(void) ldap_unbind(adh->ld);
491*c8e26105Sjp151216 		adh->ld = NULL;
492*c8e26105Sjp151216 	}
493c5c4113dSnw141292 
494c5c4113dSnw141292 	atomic_inc_64(&adh->generation);
495*c8e26105Sjp151216 
496*c8e26105Sjp151216 	/* Open and bind an LDAP connection */
497c5c4113dSnw141292 	adh->ld = ldap_init(adh->host, adh->port);
498*c8e26105Sjp151216 	if (adh->ld == NULL) {
499*c8e26105Sjp151216 		idmapdlog(LOG_INFO, "ldap_init() to server "
500*c8e26105Sjp151216 		    "%s port %d failed. (%s)", adh->host,
501*c8e26105Sjp151216 		    adh->port, strerror(errno));
502*c8e26105Sjp151216 		goto out;
503*c8e26105Sjp151216 	}
504c5c4113dSnw141292 	ldversion = LDAP_VERSION3;
505*c8e26105Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion);
506*c8e26105Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
507c5c4113dSnw141292 	(void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero);
508c5c4113dSnw141292 	(void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero);
509*c8e26105Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
510c5c4113dSnw141292 	(void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
511*c8e26105Sjp151216 	rc = ldap_sasl_interactive_bind_s(adh->ld, "" /* binddn */,
512*c8e26105Sjp151216 	    adh->saslmech, NULL, NULL, adh->saslflags, &idmap_saslcallback,
513*c8e26105Sjp151216 	    NULL);
514c5c4113dSnw141292 
515c5c4113dSnw141292 	if (rc != LDAP_SUCCESS) {
516*c8e26105Sjp151216 		(void) ldap_unbind(adh->ld);
517*c8e26105Sjp151216 		adh->ld = NULL;
518*c8e26105Sjp151216 		idmapdlog(LOG_INFO, "ldap_sasl_interactive_bind_s() to server "
519*c8e26105Sjp151216 		    "%s port %d failed. (%s)", adh->host, adh->port,
520*c8e26105Sjp151216 		    ldap_err2string(rc));
521c5c4113dSnw141292 	}
522c5c4113dSnw141292 
523*c8e26105Sjp151216 	idmapdlog(LOG_DEBUG, "Using global catalog server %s:%d",
524*c8e26105Sjp151216 	    adh->host, adh->port);
525*c8e26105Sjp151216 
526*c8e26105Sjp151216 out:
527*c8e26105Sjp151216 	if (adh->ld != NULL) {
528*c8e26105Sjp151216 		atomic_inc_32(&adh->ref);
529c5c4113dSnw141292 		adh->idletime = time(NULL);
530*c8e26105Sjp151216 		adh->dead = 0;
531*c8e26105Sjp151216 		(void) pthread_mutex_unlock(&adh->lock);
532*c8e26105Sjp151216 		return (1);
533*c8e26105Sjp151216 	}
534c5c4113dSnw141292 
535*c8e26105Sjp151216 	(void) pthread_mutex_unlock(&adh->lock);
536*c8e26105Sjp151216 	return (0);
537c5c4113dSnw141292 }
538c5c4113dSnw141292 
539c5c4113dSnw141292 
540c5c4113dSnw141292 /*
541c5c4113dSnw141292  * Connection management: find an open connection or open one
542c5c4113dSnw141292  */
543c5c4113dSnw141292 static
544c5c4113dSnw141292 ad_host_t *
545*c8e26105Sjp151216 idmap_get_conn(ad_t *ad)
546c5c4113dSnw141292 {
547c5c4113dSnw141292 	ad_host_t	*adh = NULL;
548*c8e26105Sjp151216 	ad_host_t	*first_adh, *next_adh;
549*c8e26105Sjp151216 	int		seen_last;
550*c8e26105Sjp151216 	int		tries = -1;
551c5c4113dSnw141292 
552*c8e26105Sjp151216 retry:
553c5c4113dSnw141292 	(void) pthread_mutex_lock(&adhostlock);
554c5c4113dSnw141292 
555*c8e26105Sjp151216 	if (host_head == NULL)
556*c8e26105Sjp151216 		goto out;
557*c8e26105Sjp151216 
558*c8e26105Sjp151216 	/* Try as many DSs as we have, once each; count them once */
559*c8e26105Sjp151216 	if (tries < 0) {
560*c8e26105Sjp151216 		for (adh = host_head, tries = 0; adh != NULL; adh = adh->next)
561*c8e26105Sjp151216 			tries++;
562*c8e26105Sjp151216 	}
563*c8e26105Sjp151216 
564c5c4113dSnw141292 	/*
565*c8e26105Sjp151216 	 * Find a suitable ad_host_t (one associated with this ad_t,
566*c8e26105Sjp151216 	 * preferably one that's already connected and not dead),
567*c8e26105Sjp151216 	 * possibly round-robining through the ad_host_t list.
568*c8e26105Sjp151216 	 *
569*c8e26105Sjp151216 	 * If we can't find a non-dead ad_host_t and we've had one
570*c8e26105Sjp151216 	 * before (ad->last_adh != NULL) then pick the next one or, if
571*c8e26105Sjp151216 	 * there is no next one, the first one (i.e., round-robin).
572*c8e26105Sjp151216 	 *
573*c8e26105Sjp151216 	 * If we've never had one then (ad->last_adh == NULL) then pick
574*c8e26105Sjp151216 	 * the first one.
575*c8e26105Sjp151216 	 *
576*c8e26105Sjp151216 	 * If we ever want to be more clever, such as preferring DSes
577*c8e26105Sjp151216 	 * with better average response times, adjusting for weights
578*c8e26105Sjp151216 	 * from SRV RRs, and so on, then this is the place to do it.
579c5c4113dSnw141292 	 */
580*c8e26105Sjp151216 
581*c8e26105Sjp151216 	for (adh = host_head, first_adh = NULL, next_adh = NULL, seen_last = 0;
582*c8e26105Sjp151216 	    adh != NULL; adh = adh->next) {
583*c8e26105Sjp151216 		if (adh->owner != ad)
584*c8e26105Sjp151216 			continue;
585*c8e26105Sjp151216 		if (first_adh == NULL)
586*c8e26105Sjp151216 			first_adh = adh;
587*c8e26105Sjp151216 		if (adh == ad->last_adh)
588*c8e26105Sjp151216 			seen_last++;
589*c8e26105Sjp151216 		else if (seen_last)
590*c8e26105Sjp151216 			next_adh = adh;
591*c8e26105Sjp151216 
592*c8e26105Sjp151216 		/* First time or current adh is live -> done */
593*c8e26105Sjp151216 		if (ad->last_adh == NULL || (!adh->dead && adh->ld != NULL))
594c5c4113dSnw141292 			break;
595c5c4113dSnw141292 	}
596*c8e26105Sjp151216 
597*c8e26105Sjp151216 	/* Round-robin */
598*c8e26105Sjp151216 	if (adh == NULL)
599*c8e26105Sjp151216 		adh = (next_adh != NULL) ? next_adh : first_adh;
600c5c4113dSnw141292 
601c5c4113dSnw141292 	if (adh != NULL)
602*c8e26105Sjp151216 		ad->last_adh = adh;
603c5c4113dSnw141292 
604*c8e26105Sjp151216 out:
605c5c4113dSnw141292 	(void) pthread_mutex_unlock(&adhostlock);
606c5c4113dSnw141292 
607*c8e26105Sjp151216 	/* Found suitable DS, open it if not already opened */
608*c8e26105Sjp151216 	if (idmap_open_conn(adh))
609c5c4113dSnw141292 		return (adh);
610*c8e26105Sjp151216 
611*c8e26105Sjp151216 	if (tries-- > 0)
612*c8e26105Sjp151216 		goto retry;
613*c8e26105Sjp151216 
614*c8e26105Sjp151216 	idmapdlog(LOG_ERR, "Couldn't open an LDAP connection to any global "
615*c8e26105Sjp151216 	    "catalog server!");
616*c8e26105Sjp151216 
617*c8e26105Sjp151216 	return (NULL);
618c5c4113dSnw141292 }
619c5c4113dSnw141292 
620c5c4113dSnw141292 static
621c5c4113dSnw141292 void
622c5c4113dSnw141292 idmap_release_conn(ad_host_t *adh)
623c5c4113dSnw141292 {
624c5c4113dSnw141292 	(void) pthread_mutex_lock(&adh->lock);
625c5c4113dSnw141292 	if (atomic_dec_32_nv(&adh->ref) == 0)
626c5c4113dSnw141292 		adh->idletime = time(NULL);
627c5c4113dSnw141292 	(void) pthread_mutex_unlock(&adh->lock);
628c5c4113dSnw141292 }
629c5c4113dSnw141292 
630c5c4113dSnw141292 /*
631c5c4113dSnw141292  * Take ad_host_config_t information, create a ad_host_t,
632c5c4113dSnw141292  * populate it and add it to the list of hosts.
633c5c4113dSnw141292  */
634c5c4113dSnw141292 
635c5c4113dSnw141292 int
636c5c4113dSnw141292 idmap_add_ds(ad_t *ad, const char *host, int port)
637c5c4113dSnw141292 {
638c5c4113dSnw141292 	ad_host_t	*p;
639c5c4113dSnw141292 	ad_host_t	*new = NULL;
640c5c4113dSnw141292 	int		ret = -1;
641c5c4113dSnw141292 
642c5c4113dSnw141292 	if (port == 0)
643c5c4113dSnw141292 		port = (int)ad->partition;
644c5c4113dSnw141292 
645c5c4113dSnw141292 	(void) pthread_mutex_lock(&adhostlock);
646c5c4113dSnw141292 	for (p = host_head; p != NULL; p = p->next) {
647c5c4113dSnw141292 		if (p->owner != ad)
648c5c4113dSnw141292 			continue;
649c5c4113dSnw141292 
650c5c4113dSnw141292 		if (strcmp(host, p->host) == 0 && p->port == port) {
651c5c4113dSnw141292 			/* already added */
652*c8e26105Sjp151216 			ret = 0;
653c5c4113dSnw141292 			goto err;
654c5c4113dSnw141292 		}
655c5c4113dSnw141292 	}
656c5c4113dSnw141292 
657c5c4113dSnw141292 	/* add new entry */
658c5c4113dSnw141292 	new = (ad_host_t *)calloc(1, sizeof (ad_host_t));
659c5c4113dSnw141292 	if (new == NULL)
660c5c4113dSnw141292 		goto err;
661c5c4113dSnw141292 	new->owner = ad;
662c5c4113dSnw141292 	new->port = port;
663c5c4113dSnw141292 	new->dead = 0;
664c5c4113dSnw141292 	if ((new->host = strdup(host)) == NULL)
665c5c4113dSnw141292 		goto err;
666c5c4113dSnw141292 
667c5c4113dSnw141292 	/* default to SASL GSSAPI only for now */
668c5c4113dSnw141292 	new->saslflags = LDAP_SASL_INTERACTIVE;
669c5c4113dSnw141292 	new->saslmech = "GSSAPI";
670c5c4113dSnw141292 
671c5c4113dSnw141292 	if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) {
672c5c4113dSnw141292 		free(new->host);
673c5c4113dSnw141292 		new->host = NULL;
674c5c4113dSnw141292 		errno = ret;
675c5c4113dSnw141292 		ret = -1;
676c5c4113dSnw141292 		goto err;
677c5c4113dSnw141292 	}
678c5c4113dSnw141292 
679c5c4113dSnw141292 	/* link in */
680c5c4113dSnw141292 	new->next = host_head;
681c5c4113dSnw141292 	host_head = new;
682c5c4113dSnw141292 
683c5c4113dSnw141292 	/* Start reaper if it doesn't exist */
684c5c4113dSnw141292 	if (reaperid == 0)
685c5c4113dSnw141292 		(void) pthread_create(&reaperid, NULL,
686c5c4113dSnw141292 		    (void *(*)(void *))adreaper, (void *)NULL);
687c5c4113dSnw141292 
688c5c4113dSnw141292 err:
689c5c4113dSnw141292 	(void) pthread_mutex_unlock(&adhostlock);
690c5c4113dSnw141292 
691c5c4113dSnw141292 	if (ret != 0 && new != NULL) {
692c5c4113dSnw141292 		if (new->host != NULL) {
693c5c4113dSnw141292 			(void) pthread_mutex_destroy(&new->lock);
694c5c4113dSnw141292 			free(new->host);
695c5c4113dSnw141292 		}
696c5c4113dSnw141292 		free(new);
697c5c4113dSnw141292 	}
698c5c4113dSnw141292 
699c5c4113dSnw141292 	return (ret);
700c5c4113dSnw141292 }
701c5c4113dSnw141292 
702c5c4113dSnw141292 /*
703*c8e26105Sjp151216  * Free a DS configuration.
704*c8e26105Sjp151216  * Caller must lock the adhostlock mutex
705c5c4113dSnw141292  */
706*c8e26105Sjp151216 static void
707*c8e26105Sjp151216 delete_ds(ad_t *ad, const char *host, int port)
708c5c4113dSnw141292 {
709c5c4113dSnw141292 	ad_host_t	**p, *q;
710c5c4113dSnw141292 
711c5c4113dSnw141292 	for (p = &host_head; *p != NULL; p = &((*p)->next)) {
712c5c4113dSnw141292 		if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 ||
713c5c4113dSnw141292 		    (*p)->port != port)
714c5c4113dSnw141292 			continue;
715c5c4113dSnw141292 		/* found */
716*c8e26105Sjp151216 		if ((*p)->ref > 0)
717c5c4113dSnw141292 			break;	/* still in use */
718c5c4113dSnw141292 
719c5c4113dSnw141292 		q = *p;
720c5c4113dSnw141292 		*p = (*p)->next;
721c5c4113dSnw141292 
722c5c4113dSnw141292 		(void) pthread_mutex_destroy(&q->lock);
723c5c4113dSnw141292 
724c5c4113dSnw141292 		if (q->ld)
725c5c4113dSnw141292 			(void) ldap_unbind(q->ld);
726c5c4113dSnw141292 		if (q->host)
727c5c4113dSnw141292 			free(q->host);
728c5c4113dSnw141292 		free(q);
729c5c4113dSnw141292 		break;
730c5c4113dSnw141292 	}
731*c8e26105Sjp151216 
732c5c4113dSnw141292 }
733c5c4113dSnw141292 
734*c8e26105Sjp151216 
735c5c4113dSnw141292 /*
736c5c4113dSnw141292  * Convert a binary SID in a BerValue to a sid_t
737c5c4113dSnw141292  */
738c5c4113dSnw141292 static
739c5c4113dSnw141292 int
740c5c4113dSnw141292 idmap_getsid(BerValue *bval, sid_t *sidp)
741c5c4113dSnw141292 {
742c5c4113dSnw141292 	int		i, j;
743c5c4113dSnw141292 	uchar_t		*v;
744c5c4113dSnw141292 	uint32_t	a;
745c5c4113dSnw141292 
746c5c4113dSnw141292 	/*
747c5c4113dSnw141292 	 * The binary format of a SID is as follows:
748c5c4113dSnw141292 	 *
749c5c4113dSnw141292 	 * byte #0: version, always 0x01
750c5c4113dSnw141292 	 * byte #1: RID count, always <= 0x0f
751c5c4113dSnw141292 	 * bytes #2-#7: SID authority, big-endian 48-bit unsigned int
752c5c4113dSnw141292 	 *
753c5c4113dSnw141292 	 * followed by RID count RIDs, each a little-endian, unsigned
754c5c4113dSnw141292 	 * 32-bit int.
755c5c4113dSnw141292 	 */
756c5c4113dSnw141292 	/*
757c5c4113dSnw141292 	 * Sanity checks: must have at least one RID, version must be
758c5c4113dSnw141292 	 * 0x01, and the length must be 8 + rid count * 4
759c5c4113dSnw141292 	 */
760c5c4113dSnw141292 	if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 &&
761c5c4113dSnw141292 	    bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) {
762c5c4113dSnw141292 		v = (uchar_t *)bval->bv_val;
763c5c4113dSnw141292 		sidp->version = v[0];
764c5c4113dSnw141292 		sidp->sub_authority_count = v[1];
765c5c4113dSnw141292 		sidp->authority =
766c5c4113dSnw141292 		    /* big endian -- so start from the left */
767c5c4113dSnw141292 		    ((u_longlong_t)v[2] << 40) |
768c5c4113dSnw141292 		    ((u_longlong_t)v[3] << 32) |
769c5c4113dSnw141292 		    ((u_longlong_t)v[4] << 24) |
770c5c4113dSnw141292 		    ((u_longlong_t)v[5] << 16) |
771c5c4113dSnw141292 		    ((u_longlong_t)v[6] << 8) |
772c5c4113dSnw141292 		    (u_longlong_t)v[7];
773c5c4113dSnw141292 		for (i = 0; i < sidp->sub_authority_count; i++) {
774c5c4113dSnw141292 			j = 8 + (i * 4);
775c5c4113dSnw141292 			/* little endian -- so start from the right */
776c5c4113dSnw141292 			a = (v[j + 3] << 24) | (v[j + 2] << 16) |
777c5c4113dSnw141292 			    (v[j + 1] << 8) | (v[j]);
778c5c4113dSnw141292 			sidp->sub_authorities[i] = a;
779c5c4113dSnw141292 		}
780c5c4113dSnw141292 		return (0);
781c5c4113dSnw141292 	}
782c5c4113dSnw141292 	return (-1);
783c5c4113dSnw141292 }
784c5c4113dSnw141292 
785c5c4113dSnw141292 /*
786c5c4113dSnw141292  * Convert a sid_t to S-1-...
787c5c4113dSnw141292  */
788c5c4113dSnw141292 static
789c5c4113dSnw141292 char *
790c5c4113dSnw141292 idmap_sid2txt(sid_t *sidp)
791c5c4113dSnw141292 {
792c5c4113dSnw141292 	int	rlen, i, len;
793c5c4113dSnw141292 	char	*str, *cp;
794c5c4113dSnw141292 
795c5c4113dSnw141292 	if (sidp->version != 1)
796c5c4113dSnw141292 		return (NULL);
797c5c4113dSnw141292 
798c5c4113dSnw141292 	len = sizeof ("S-1-") - 1;
799c5c4113dSnw141292 
800c5c4113dSnw141292 	/*
801c5c4113dSnw141292 	 * We could optimize like so, but, why?
802c5c4113dSnw141292 	 *	if (sidp->authority < 10)
803c5c4113dSnw141292 	 *		len += 2;
804c5c4113dSnw141292 	 *	else if (sidp->authority < 100)
805c5c4113dSnw141292 	 *		len += 3;
806c5c4113dSnw141292 	 *	else
807c5c4113dSnw141292 	 *		len += snprintf(NULL, 0"%llu", sidp->authority);
808c5c4113dSnw141292 	 */
809c5c4113dSnw141292 	len += snprintf(NULL, 0, "%llu", sidp->authority);
810c5c4113dSnw141292 
811c5c4113dSnw141292 	/* Max length of a uint32_t printed out in ASCII is 10 bytes */
812c5c4113dSnw141292 	len += 1 + (sidp->sub_authority_count + 1) * 10;
813c5c4113dSnw141292 
814c5c4113dSnw141292 	if ((cp = str = malloc(len)) == NULL)
815c5c4113dSnw141292 		return (NULL);
816c5c4113dSnw141292 
817c5c4113dSnw141292 	rlen = snprintf(str, len, "S-1-%llu", sidp->authority);
818c5c4113dSnw141292 
819c5c4113dSnw141292 	cp += rlen;
820c5c4113dSnw141292 	len -= rlen;
821c5c4113dSnw141292 
822c5c4113dSnw141292 	for (i = 0; i < sidp->sub_authority_count; i++) {
823c5c4113dSnw141292 		assert(len > 0);
824c5c4113dSnw141292 		rlen = snprintf(cp, len, "-%u", sidp->sub_authorities[i]);
825c5c4113dSnw141292 		cp += rlen;
826c5c4113dSnw141292 		len -= rlen;
827c5c4113dSnw141292 		assert(len >= 0);
828c5c4113dSnw141292 	}
829c5c4113dSnw141292 
830c5c4113dSnw141292 	return (str);
831c5c4113dSnw141292 }
832c5c4113dSnw141292 
833c5c4113dSnw141292 /*
834c5c4113dSnw141292  * Convert a sid_t to on-the-wire encoding
835c5c4113dSnw141292  */
836c5c4113dSnw141292 static
837c5c4113dSnw141292 int
838c5c4113dSnw141292 idmap_sid2binsid(sid_t *sid, uchar_t *binsid, int binsidlen)
839c5c4113dSnw141292 {
840c5c4113dSnw141292 	uchar_t		*p;
841c5c4113dSnw141292 	int		i;
842c5c4113dSnw141292 	uint64_t	a;
843c5c4113dSnw141292 	uint32_t	r;
844c5c4113dSnw141292 
845c5c4113dSnw141292 	if (sid->version != 1 ||
846c5c4113dSnw141292 	    binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4))
847c5c4113dSnw141292 		return (-1);
848c5c4113dSnw141292 
849c5c4113dSnw141292 	p = binsid;
850c5c4113dSnw141292 	*p++ = 0x01;		/* version */
851c5c4113dSnw141292 	/* sub authority count */
852c5c4113dSnw141292 	*p++ = sid->sub_authority_count;
853c5c4113dSnw141292 	/* Authority */
854c5c4113dSnw141292 	a = sid->authority;
855c5c4113dSnw141292 	/* big-endian -- start from left */
856c5c4113dSnw141292 	*p++ = (a >> 40) & 0xFF;
857c5c4113dSnw141292 	*p++ = (a >> 32) & 0xFF;
858c5c4113dSnw141292 	*p++ = (a >> 24) & 0xFF;
859c5c4113dSnw141292 	*p++ = (a >> 16) & 0xFF;
860c5c4113dSnw141292 	*p++ = (a >> 8) & 0xFF;
861c5c4113dSnw141292 	*p++ = a & 0xFF;
862c5c4113dSnw141292 
863c5c4113dSnw141292 	/* sub-authorities */
864c5c4113dSnw141292 	for (i = 0; i < sid->sub_authority_count; i++) {
865c5c4113dSnw141292 		r = sid->sub_authorities[i];
866c5c4113dSnw141292 		/* little-endian -- start from right */
867c5c4113dSnw141292 		*p++ = (r & 0x000000FF);
868c5c4113dSnw141292 		*p++ = (r & 0x0000FF00) >> 8;
869c5c4113dSnw141292 		*p++ = (r & 0x00FF0000) >> 16;
870c5c4113dSnw141292 		*p++ = (r & 0xFF000000) >> 24;
871c5c4113dSnw141292 	}
872c5c4113dSnw141292 
873c5c4113dSnw141292 	return (0);
874c5c4113dSnw141292 }
875c5c4113dSnw141292 
876c5c4113dSnw141292 /*
877c5c4113dSnw141292  * Convert a stringified SID (S-1-...) into a hex-encoded version of the
878c5c4113dSnw141292  * on-the-wire encoding, but with each pair of hex digits pre-pended
879c5c4113dSnw141292  * with a '\', so we can pass this to libldap.
880c5c4113dSnw141292  */
881c5c4113dSnw141292 static
882c5c4113dSnw141292 int
883c5c4113dSnw141292 idmap_txtsid2hexbinsid(const char *txt, const rid_t *rid,
884c5c4113dSnw141292 	char *hexbinsid, int hexbinsidlen)
885c5c4113dSnw141292 {
886c5c4113dSnw141292 	sid_t		sid = { 0 };
887c5c4113dSnw141292 	int		i, j;
888c5c4113dSnw141292 	const char	*cp;
889c5c4113dSnw141292 	char		*ecp;
890c5c4113dSnw141292 	u_longlong_t	a;
891c5c4113dSnw141292 	unsigned long	r;
892c5c4113dSnw141292 	uchar_t		*binsid, b, hb;
893c5c4113dSnw141292 
894c5c4113dSnw141292 	/* Only version 1 SIDs please */
895c5c4113dSnw141292 	if (strncmp(txt, "S-1-", strlen("S-1-")) != 0)
896c5c4113dSnw141292 		return (-1);
897c5c4113dSnw141292 
898c5c4113dSnw141292 	if (strlen(txt) < (strlen("S-1-") + 1))
899c5c4113dSnw141292 		return (-1);
900c5c4113dSnw141292 
901c5c4113dSnw141292 	/* count '-'s */
902c5c4113dSnw141292 	for (j = 0, cp = strchr(txt, '-');
903c5c4113dSnw141292 	    cp != NULL && *cp != '\0';
904c5c4113dSnw141292 	    j++, cp = strchr(cp + 1, '-')) {
905c5c4113dSnw141292 		/* can't end on a '-' */
906c5c4113dSnw141292 		if (*(cp + 1) == '\0')
907c5c4113dSnw141292 			return (-1);
908c5c4113dSnw141292 	}
909c5c4113dSnw141292 
91062c60062Sbaban 	/* Adjust count for version and authority */
91162c60062Sbaban 	j -= 2;
91262c60062Sbaban 
91362c60062Sbaban 	/* we know the version number and RID count */
91462c60062Sbaban 	sid.version = 1;
91562c60062Sbaban 	sid.sub_authority_count = (rid != NULL) ? j + 1 : j;
91662c60062Sbaban 
917c5c4113dSnw141292 	/* must have at least one RID, but not too many */
91862c60062Sbaban 	if (sid.sub_authority_count < 1 ||
91962c60062Sbaban 	    sid.sub_authority_count > SID_MAX_SUB_AUTHORITIES)
920c5c4113dSnw141292 		return (-1);
921c5c4113dSnw141292 
922c5c4113dSnw141292 	/* check that we only have digits and '-' */
923c5c4113dSnw141292 	if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1))
924c5c4113dSnw141292 		return (-1);
925c5c4113dSnw141292 
926c5c4113dSnw141292 	cp = txt + strlen("S-1-");
927c5c4113dSnw141292 
928c5c4113dSnw141292 	/* 64-bit safe parsing of unsigned 48-bit authority value */
929c5c4113dSnw141292 	errno = 0;
930c5c4113dSnw141292 	a = strtoull(cp, &ecp, 10);
931c5c4113dSnw141292 
932c5c4113dSnw141292 	/* errors parsing the authority or too many bits */
933c5c4113dSnw141292 	if (cp == ecp || (a == 0 && errno == EINVAL) ||
934c5c4113dSnw141292 	    (a == ULLONG_MAX && errno == ERANGE) ||
935c5c4113dSnw141292 	    (a & 0x0000ffffffffffffULL) != a)
936c5c4113dSnw141292 		return (-1);
937c5c4113dSnw141292 
938c5c4113dSnw141292 	cp = ecp;
939c5c4113dSnw141292 
940c5c4113dSnw141292 	sid.authority = (uint64_t)a;
941c5c4113dSnw141292 
94262c60062Sbaban 	for (i = 0; i < j; i++) {
943c5c4113dSnw141292 		if (*cp++ != '-')
944c5c4113dSnw141292 			return (-1);
945c5c4113dSnw141292 		/* 64-bit safe parsing of unsigned 32-bit RID */
946c5c4113dSnw141292 		errno = 0;
947c5c4113dSnw141292 		r = strtoul(cp, &ecp, 10);
948c5c4113dSnw141292 		/* errors parsing the RID or too many bits */
949c5c4113dSnw141292 		if (cp == ecp || (r == 0 && errno == EINVAL) ||
950c5c4113dSnw141292 		    (r == ULONG_MAX && errno == ERANGE) ||
951c5c4113dSnw141292 		    (r & 0xffffffffUL) != r)
952c5c4113dSnw141292 			return (-1);
953c5c4113dSnw141292 		sid.sub_authorities[i] = (uint32_t)r;
954c5c4113dSnw141292 		cp = ecp;
955c5c4113dSnw141292 	}
956c5c4113dSnw141292 
957c5c4113dSnw141292 	/* check that all of the string SID has been consumed */
958c5c4113dSnw141292 	if (*cp != '\0')
959c5c4113dSnw141292 		return (-1);
960c5c4113dSnw141292 
96162c60062Sbaban 	if (rid != NULL)
96262c60062Sbaban 		sid.sub_authorities[j] = *rid;
963c5c4113dSnw141292 
964c5c4113dSnw141292 	j = 1 + 1 + 6 + sid.sub_authority_count * 4;
965c5c4113dSnw141292 
966c5c4113dSnw141292 	if (hexbinsidlen < (j * 3))
967c5c4113dSnw141292 		return (-2);
968c5c4113dSnw141292 
969c5c4113dSnw141292 	/* binary encode the SID */
970c5c4113dSnw141292 	binsid = (uchar_t *)alloca(j);
971c5c4113dSnw141292 	(void) idmap_sid2binsid(&sid, binsid, j);
972c5c4113dSnw141292 
973c5c4113dSnw141292 	/* hex encode, with a backslash before each byte */
974c5c4113dSnw141292 	for (ecp = hexbinsid, i = 0; i < j; i++) {
975c5c4113dSnw141292 		b = binsid[i];
976c5c4113dSnw141292 		*ecp++ = '\\';
977c5c4113dSnw141292 		hb = (b >> 4) & 0xF;
978c5c4113dSnw141292 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
979c5c4113dSnw141292 		hb = b & 0xF;
980c5c4113dSnw141292 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
981c5c4113dSnw141292 	}
982c5c4113dSnw141292 	*ecp = '\0';
983c5c4113dSnw141292 
984c5c4113dSnw141292 	return (0);
985c5c4113dSnw141292 }
986c5c4113dSnw141292 
987c5c4113dSnw141292 static
988c5c4113dSnw141292 char *
989c5c4113dSnw141292 convert_bval2sid(BerValue *bval, rid_t *rid)
990c5c4113dSnw141292 {
991c5c4113dSnw141292 	sid_t	sid;
992c5c4113dSnw141292 
993c5c4113dSnw141292 	if (idmap_getsid(bval, &sid) < 0)
994c5c4113dSnw141292 		return (NULL);
995c5c4113dSnw141292 
996c5c4113dSnw141292 	/*
997c5c4113dSnw141292 	 * If desired and if the SID is what should be a domain/computer
998c5c4113dSnw141292 	 * user or group SID (i.e., S-1-5-w-x-y-z-<user/group RID>) then
999c5c4113dSnw141292 	 * save the last RID and truncate the SID
1000c5c4113dSnw141292 	 */
1001c5c4113dSnw141292 	if (rid != NULL && sid.authority == 5 && sid.sub_authority_count == 5)
1002c5c4113dSnw141292 		*rid = sid.sub_authorities[--sid.sub_authority_count];
1003c5c4113dSnw141292 	return (idmap_sid2txt(&sid));
1004c5c4113dSnw141292 }
1005c5c4113dSnw141292 
1006c5c4113dSnw141292 
1007c5c4113dSnw141292 idmap_retcode
1008c5c4113dSnw141292 idmap_lookup_batch_start(ad_t *ad, int nqueries, idmap_query_state_t **state)
1009c5c4113dSnw141292 {
1010c5c4113dSnw141292 	idmap_query_state_t *new_state;
1011c5c4113dSnw141292 	ad_host_t	*adh = NULL;
1012c5c4113dSnw141292 
1013c5c4113dSnw141292 	*state = NULL;
1014c5c4113dSnw141292 
1015c5c4113dSnw141292 	if (*ad->dflt_w2k_dom == '\0')
1016c5c4113dSnw141292 		return (-1);
1017c5c4113dSnw141292 
1018c5c4113dSnw141292 	adh = idmap_get_conn(ad);
1019c5c4113dSnw141292 	if (adh == NULL)
1020c5c4113dSnw141292 		return (IDMAP_ERR_OTHER);
1021c5c4113dSnw141292 
1022c5c4113dSnw141292 	new_state = calloc(1, sizeof (idmap_query_state_t) +
1023c5c4113dSnw141292 	    (nqueries - 1) * sizeof (idmap_q_t));
1024c5c4113dSnw141292 
1025c5c4113dSnw141292 	if (new_state == NULL)
1026c5c4113dSnw141292 		return (IDMAP_ERR_MEMORY);
1027c5c4113dSnw141292 
102884decf41Sjp151216 	new_state->ref_cnt = 1;
1029c5c4113dSnw141292 	new_state->qadh = adh;
1030c5c4113dSnw141292 	new_state->qcount = nqueries;
1031c5c4113dSnw141292 	new_state->qadh_gen = adh->generation;
1032c5c4113dSnw141292 	/* should be -1, but the atomic routines want unsigned */
1033c5c4113dSnw141292 	new_state->qlastsent = 0;
103484decf41Sjp151216 	(void) pthread_cond_init(&new_state->cv, NULL);
1035c5c4113dSnw141292 
1036c5c4113dSnw141292 	(void) pthread_mutex_lock(&qstatelock);
1037c5c4113dSnw141292 	new_state->next = qstatehead;
1038c5c4113dSnw141292 	qstatehead = new_state;
1039c5c4113dSnw141292 	(void) pthread_mutex_unlock(&qstatelock);
1040c5c4113dSnw141292 
1041c5c4113dSnw141292 	*state = new_state;
1042c5c4113dSnw141292 
1043c5c4113dSnw141292 	return (IDMAP_SUCCESS);
1044c5c4113dSnw141292 }
1045c5c4113dSnw141292 
1046c5c4113dSnw141292 /*
1047c5c4113dSnw141292  * Find the idmap_query_state_t to which a given LDAP result msgid on a
104884decf41Sjp151216  * given connection belongs. This routine increaments the reference count
104984decf41Sjp151216  * so that the object can not be freed. idmap_lookup_unlock_batch()
105084decf41Sjp151216  * must be called to decreament the reference count.
1051c5c4113dSnw141292  */
1052c5c4113dSnw141292 static
1053c5c4113dSnw141292 int
1054c5c4113dSnw141292 idmap_msgid2query(ad_host_t *adh, int msgid,
1055c5c4113dSnw141292 	idmap_query_state_t **state, int *qid)
1056c5c4113dSnw141292 {
1057c5c4113dSnw141292 	idmap_query_state_t *p;
1058c5c4113dSnw141292 	int		    i;
1059c5c4113dSnw141292 
1060c5c4113dSnw141292 	(void) pthread_mutex_lock(&qstatelock);
1061c5c4113dSnw141292 	for (p = qstatehead; p != NULL; p = p->next) {
1062c5c4113dSnw141292 		if (p->qadh != adh || adh->generation != p->qadh_gen)
1063c5c4113dSnw141292 			continue;
1064c5c4113dSnw141292 		for (i = 0; i < p->qcount; i++) {
1065c5c4113dSnw141292 			if ((p->queries[i]).msgid == msgid) {
106684decf41Sjp151216 				p->ref_cnt++;
1067c5c4113dSnw141292 				*state = p;
1068c5c4113dSnw141292 				*qid = i;
1069c5c4113dSnw141292 				(void) pthread_mutex_unlock(&qstatelock);
1070c5c4113dSnw141292 				return (1);
1071c5c4113dSnw141292 			}
1072c5c4113dSnw141292 		}
1073c5c4113dSnw141292 	}
1074c5c4113dSnw141292 	(void) pthread_mutex_unlock(&qstatelock);
1075c5c4113dSnw141292 	return (0);
1076c5c4113dSnw141292 }
1077c5c4113dSnw141292 
1078c5c4113dSnw141292 /*
1079c5c4113dSnw141292  * Handle an objectSid attr from a result
1080c5c4113dSnw141292  */
1081c5c4113dSnw141292 static
1082c5c4113dSnw141292 void
1083c5c4113dSnw141292 idmap_bv_objsid2sidstr(BerValue **bvalues, idmap_q_t *q)
1084c5c4113dSnw141292 {
1085c5c4113dSnw141292 	if (bvalues == NULL)
1086c5c4113dSnw141292 		return;
1087c5c4113dSnw141292 	/* objectSid is single valued */
1088c5c4113dSnw141292 	*(q->result) = convert_bval2sid(bvalues[0], q->rid);
1089c5c4113dSnw141292 	q->got_objectSid = 1;
1090c5c4113dSnw141292 }
1091c5c4113dSnw141292 
1092c5c4113dSnw141292 /*
1093c5c4113dSnw141292  * Handle a sAMAccountName attr from a result
1094c5c4113dSnw141292  */
1095c5c4113dSnw141292 static
1096c5c4113dSnw141292 void
1097c5c4113dSnw141292 idmap_bv_samaccountname2name(BerValue **bvalues, idmap_q_t *q, const char *dn)
1098c5c4113dSnw141292 {
1099c5c4113dSnw141292 	char *result, *domain;
1100c5c4113dSnw141292 	int len;
1101c5c4113dSnw141292 
1102c5c4113dSnw141292 	if (bvalues == NULL)
1103c5c4113dSnw141292 		return;
1104c5c4113dSnw141292 
1105c5c4113dSnw141292 	if ((domain = dn2dns(dn)) == NULL)
1106c5c4113dSnw141292 		return;
1107c5c4113dSnw141292 
1108c5c4113dSnw141292 	if (bvalues == NULL || bvalues[0] == NULL ||
1109c5c4113dSnw141292 	    bvalues[0]->bv_val == NULL)
1110c5c4113dSnw141292 		return;
1111c5c4113dSnw141292 
1112c5c4113dSnw141292 	len = bvalues[0]->bv_len + 1;
1113c5c4113dSnw141292 
1114c5c4113dSnw141292 	if (q->domain != NULL)
1115c5c4113dSnw141292 		*(q->domain) = domain;
1116c5c4113dSnw141292 	else
1117c5c4113dSnw141292 		len += strlen(domain) + 1;
1118c5c4113dSnw141292 
1119c5c4113dSnw141292 	if ((result = malloc(len)) == NULL) {
1120c5c4113dSnw141292 		if (q->domain != NULL)
1121c5c4113dSnw141292 			*(q->domain) = NULL;
1122c5c4113dSnw141292 		free(domain);
1123c5c4113dSnw141292 		return;
1124c5c4113dSnw141292 	}
1125c5c4113dSnw141292 
1126c5c4113dSnw141292 	(void) memcpy(result, bvalues[0]->bv_val, (size_t)bvalues[0]->bv_len);
1127c5c4113dSnw141292 	result[bvalues[0]->bv_len] = '\0';
1128c5c4113dSnw141292 
1129c5c4113dSnw141292 	if (q->domain == NULL) {
1130c5c4113dSnw141292 		(void) strlcat(result, "@", len);
1131c5c4113dSnw141292 		(void) strlcat(result, domain, len);
1132c5c4113dSnw141292 		free(domain);
1133c5c4113dSnw141292 	}
1134c5c4113dSnw141292 
1135c5c4113dSnw141292 	*(q->result) = result;
1136c5c4113dSnw141292 	q->got_samAcctName = 1;
1137c5c4113dSnw141292 }
1138c5c4113dSnw141292 
1139c5c4113dSnw141292 
1140c5c4113dSnw141292 #define	BVAL_CASEEQ(bv, str) \
1141c5c4113dSnw141292 		(((*(bv))->bv_len == (sizeof (str) - 1)) && \
1142c5c4113dSnw141292 		    strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0)
1143c5c4113dSnw141292 
1144c5c4113dSnw141292 /*
1145c5c4113dSnw141292  * Handle an objectClass attr from a result
1146c5c4113dSnw141292  */
1147c5c4113dSnw141292 static
1148c5c4113dSnw141292 void
1149c5c4113dSnw141292 idmap_bv_objclass2sidtype(BerValue **bvalues, idmap_q_t *q)
1150c5c4113dSnw141292 {
1151c5c4113dSnw141292 	BerValue	**cbval;
1152c5c4113dSnw141292 
1153c5c4113dSnw141292 	if (bvalues == NULL)
1154c5c4113dSnw141292 		return;
1155c5c4113dSnw141292 
1156c5c4113dSnw141292 	for (cbval = bvalues; *cbval != NULL; cbval++) {
1157c5c4113dSnw141292 		/* don't clobber sid_type */
1158c5c4113dSnw141292 		if (*(q->sid_type) == _IDMAP_T_COMPUTER ||
1159c5c4113dSnw141292 		    *(q->sid_type) == _IDMAP_T_GROUP ||
1160c5c4113dSnw141292 		    *(q->sid_type) == _IDMAP_T_USER)
1161c5c4113dSnw141292 			continue;
1162c5c4113dSnw141292 
1163c5c4113dSnw141292 		if (BVAL_CASEEQ(cbval, "Computer")) {
1164c5c4113dSnw141292 			*(q->sid_type) = _IDMAP_T_COMPUTER;
1165c5c4113dSnw141292 			return;
1166c5c4113dSnw141292 		} else if (BVAL_CASEEQ(cbval, "Group")) {
1167c5c4113dSnw141292 			*(q->sid_type) = _IDMAP_T_GROUP;
1168c5c4113dSnw141292 		} else if (BVAL_CASEEQ(cbval, "USER")) {
1169c5c4113dSnw141292 			*(q->sid_type) = _IDMAP_T_USER;
1170c5c4113dSnw141292 		} else
1171c5c4113dSnw141292 			*(q->sid_type) = _IDMAP_T_OTHER;
1172c5c4113dSnw141292 		q->got_objectClass = 1;
1173c5c4113dSnw141292 	}
1174c5c4113dSnw141292 }
1175c5c4113dSnw141292 
1176c5c4113dSnw141292 /*
1177c5c4113dSnw141292  * Handle a given search result entry
1178c5c4113dSnw141292  */
1179c5c4113dSnw141292 static
1180c5c4113dSnw141292 void
1181c5c4113dSnw141292 idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res)
1182c5c4113dSnw141292 {
1183c5c4113dSnw141292 	char			*dn, *attr;
1184c5c4113dSnw141292 	BerElement		*ber = NULL;
1185c5c4113dSnw141292 	BerValue		**bvalues;
1186c5c4113dSnw141292 	ad_host_t		*adh;
1187c5c4113dSnw141292 	idmap_q_t		*q;
1188c5c4113dSnw141292 	idmap_retcode		orc;
1189c5c4113dSnw141292 
1190c5c4113dSnw141292 	adh = state->qadh;
1191c5c4113dSnw141292 
1192c5c4113dSnw141292 	(void) pthread_mutex_lock(&adh->lock);
1193c5c4113dSnw141292 
1194c5c4113dSnw141292 	if (adh->dead || (dn = ldap_get_dn(adh->ld, res)) == NULL) {
1195c5c4113dSnw141292 		(void) pthread_mutex_unlock(&adh->lock);
1196c5c4113dSnw141292 		return;
1197c5c4113dSnw141292 	}
1198c5c4113dSnw141292 
1199c5c4113dSnw141292 	q = &(state->queries[qid]);
1200c5c4113dSnw141292 
1201c5c4113dSnw141292 	for (attr = ldap_first_attribute(adh->ld, res, &ber); attr != NULL;
1202c5c4113dSnw141292 	    attr = ldap_next_attribute(adh->ld, res, ber)) {
1203c5c4113dSnw141292 		orc = *q->rc;
1204c5c4113dSnw141292 		bvalues = NULL;	/* for memory management below */
1205c5c4113dSnw141292 
1206c5c4113dSnw141292 		/*
1207c5c4113dSnw141292 		 * If this is an attribute we are looking for and
1208c5c4113dSnw141292 		 * haven't seen it yet, parse it
1209c5c4113dSnw141292 		 */
1210c5c4113dSnw141292 		if (orc != IDMAP_SUCCESS && q->n2s && !q->got_objectSid &&
1211c5c4113dSnw141292 		    strcasecmp(attr, OBJECTSID) == 0) {
1212c5c4113dSnw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
1213c5c4113dSnw141292 			idmap_bv_objsid2sidstr(bvalues, q);
1214c5c4113dSnw141292 		} else if (orc != IDMAP_SUCCESS && !q->n2s &&
1215c5c4113dSnw141292 		    !q->got_samAcctName &&
1216c5c4113dSnw141292 		    strcasecmp(attr, SAMACCOUNTNAME) == 0) {
1217c5c4113dSnw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
1218c5c4113dSnw141292 			idmap_bv_samaccountname2name(bvalues, q, dn);
1219c5c4113dSnw141292 		} else if (orc != IDMAP_SUCCESS && !q->got_objectClass &&
1220c5c4113dSnw141292 		    strcasecmp(attr, OBJECTCLASS) == 0) {
1221c5c4113dSnw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
1222c5c4113dSnw141292 			idmap_bv_objclass2sidtype(bvalues, q);
1223c5c4113dSnw141292 		}
1224c5c4113dSnw141292 
1225c5c4113dSnw141292 		if (bvalues != NULL)
1226c5c4113dSnw141292 			ldap_value_free_len(bvalues);
1227c5c4113dSnw141292 		ldap_memfree(attr);
1228c5c4113dSnw141292 
1229c5c4113dSnw141292 		if (q->n2s)
1230c5c4113dSnw141292 			*q->rc = (q->got_objectSid &&
1231c5c4113dSnw141292 			    q->got_objectClass) ?
1232c5c4113dSnw141292 			    IDMAP_SUCCESS : IDMAP_ERR_NORESULT;
1233c5c4113dSnw141292 		else
1234c5c4113dSnw141292 			*q->rc = (q->got_samAcctName &&
1235c5c4113dSnw141292 			    q->got_objectClass) ?
1236c5c4113dSnw141292 			    IDMAP_SUCCESS : IDMAP_ERR_NORESULT;
1237c5c4113dSnw141292 
1238c5c4113dSnw141292 		if (*q->rc == IDMAP_SUCCESS && *q->result == NULL)
1239c5c4113dSnw141292 			*q->rc = IDMAP_ERR_NORESULT;
1240c5c4113dSnw141292 	}
1241c5c4113dSnw141292 	(void) pthread_mutex_unlock(&adh->lock);
1242c5c4113dSnw141292 
1243c5c4113dSnw141292 	/*
1244c5c4113dSnw141292 	 * If there should be multiple partial results for different
1245c5c4113dSnw141292 	 * entities (there should not be, but, if it should happen) then
1246c5c4113dSnw141292 	 * it's possible that they could get mixed up here and we could
1247c5c4113dSnw141292 	 * get bogus results.  We just mark the query's results as
1248c5c4113dSnw141292 	 * toxic (IDMAP_ERR_INTERNAL).
1249c5c4113dSnw141292 	 *
1250c5c4113dSnw141292 	 * Between this and ignoring results when we've already filled
1251c5c4113dSnw141292 	 * out a query's results we should be OK.  The first full reply
1252c5c4113dSnw141292 	 * wins.  In practice we should never get multiple results.
1253c5c4113dSnw141292 	 */
1254c5c4113dSnw141292 	if (orc == IDMAP_ERR_INTERNAL)
1255c5c4113dSnw141292 		*q->rc = IDMAP_ERR_INTERNAL;
1256c5c4113dSnw141292 	else if (*q->rc != IDMAP_SUCCESS)
1257c5c4113dSnw141292 		*q->rc = IDMAP_ERR_INTERNAL;
1258c5c4113dSnw141292 
1259c5c4113dSnw141292 	if (ber != NULL)
1260c5c4113dSnw141292 		ber_free(ber, 0);
1261c5c4113dSnw141292 
1262c5c4113dSnw141292 	ldap_memfree(dn);
1263c5c4113dSnw141292 }
1264c5c4113dSnw141292 
1265c5c4113dSnw141292 /*
1266c5c4113dSnw141292  * Try to get a result; if there is one, find the corresponding
1267c5c4113dSnw141292  * idmap_q_t and process the result.
1268c5c4113dSnw141292  */
1269c5c4113dSnw141292 static
1270c5c4113dSnw141292 int
1271c5c4113dSnw141292 idmap_get_adobject_batch(ad_host_t *adh, struct timeval *timeout)
1272c5c4113dSnw141292 {
1273c5c4113dSnw141292 	idmap_query_state_t	*query_state;
1274c5c4113dSnw141292 	LDAPMessage		*res = NULL;
1275c5c4113dSnw141292 	int			rc, ret, msgid, qid;
1276c5c4113dSnw141292 
1277c5c4113dSnw141292 	(void) pthread_mutex_lock(&adh->lock);
1278c5c4113dSnw141292 	if (adh->dead) {
1279c5c4113dSnw141292 		(void) pthread_mutex_unlock(&adh->lock);
1280c5c4113dSnw141292 		return (-1);
1281c5c4113dSnw141292 	}
1282c5c4113dSnw141292 
1283c5c4113dSnw141292 	/* Get one result */
1284c5c4113dSnw141292 	rc = ldap_result(adh->ld, LDAP_RES_ANY, 0,
1285c5c4113dSnw141292 	    timeout, &res);
1286c5c4113dSnw141292 	if (rc == LDAP_UNAVAILABLE || rc == LDAP_UNWILLING_TO_PERFORM ||
1287c5c4113dSnw141292 	    rc == LDAP_CONNECT_ERROR || rc == LDAP_SERVER_DOWN ||
1288c5c4113dSnw141292 	    rc == LDAP_BUSY)
1289c5c4113dSnw141292 		adh->dead = 1;
1290c5c4113dSnw141292 	(void) pthread_mutex_unlock(&adh->lock);
1291c5c4113dSnw141292 
1292c5c4113dSnw141292 	if (adh->dead)
1293c5c4113dSnw141292 		return (-1);
1294c5c4113dSnw141292 
1295c5c4113dSnw141292 	switch (rc) {
1296c5c4113dSnw141292 	case LDAP_RES_SEARCH_RESULT:
1297c5c4113dSnw141292 		/* We have all the LDAP replies for some search... */
1298c5c4113dSnw141292 		msgid = ldap_msgid(res);
1299c5c4113dSnw141292 		if (idmap_msgid2query(adh, msgid,
1300c5c4113dSnw141292 		    &query_state, &qid)) {
1301c5c4113dSnw141292 			/* ...so we can decrement qinflight */
1302c5c4113dSnw141292 			atomic_dec_32(&query_state->qinflight);
1303c5c4113dSnw141292 			/* we saw at least one reply */
1304c5c4113dSnw141292 			query_state->queries[qid].got_reply = 1;
130584decf41Sjp151216 			idmap_lookup_unlock_batch(&query_state);
1306c5c4113dSnw141292 		}
1307c5c4113dSnw141292 		(void) ldap_msgfree(res);
1308c5c4113dSnw141292 		ret = 0;
1309c5c4113dSnw141292 		break;
1310c5c4113dSnw141292 	case LDAP_RES_SEARCH_REFERENCE:
1311c5c4113dSnw141292 		/*
1312c5c4113dSnw141292 		 * We have no need for these at the moment.  Eventually,
1313c5c4113dSnw141292 		 * when we query things that we can't expect to find in
1314c5c4113dSnw141292 		 * the Global Catalog then we'll need to learn to follow
1315c5c4113dSnw141292 		 * references.
1316c5c4113dSnw141292 		 */
1317c5c4113dSnw141292 		(void) ldap_msgfree(res);
1318c5c4113dSnw141292 		ret = 0;
1319c5c4113dSnw141292 		break;
1320c5c4113dSnw141292 	case LDAP_RES_SEARCH_ENTRY:
1321c5c4113dSnw141292 		/* Got a result */
1322c5c4113dSnw141292 		msgid = ldap_msgid(res);
1323c5c4113dSnw141292 		if (idmap_msgid2query(adh, msgid,
1324c5c4113dSnw141292 		    &query_state, &qid)) {
1325c5c4113dSnw141292 			idmap_extract_object(query_state, qid, res);
1326c5c4113dSnw141292 			/* we saw at least one result */
1327c5c4113dSnw141292 			query_state->queries[qid].got_reply = 1;
1328c5c4113dSnw141292 			query_state->queries[qid].got_results = 1;
132984decf41Sjp151216 			idmap_lookup_unlock_batch(&query_state);
1330c5c4113dSnw141292 		}
1331c5c4113dSnw141292 		(void) ldap_msgfree(res);
1332c5c4113dSnw141292 		ret = 0;
1333c5c4113dSnw141292 		break;
1334c5c4113dSnw141292 	default:
1335c5c4113dSnw141292 		/* timeout or error; treat the same */
1336c5c4113dSnw141292 		ret = -1;
1337c5c4113dSnw141292 		break;
1338c5c4113dSnw141292 	}
1339c5c4113dSnw141292 
1340c5c4113dSnw141292 	return (ret);
1341c5c4113dSnw141292 }
1342c5c4113dSnw141292 
134384decf41Sjp151216 /*
134484decf41Sjp151216  * This routine decreament the reference count of the
134584decf41Sjp151216  * idmap_query_state_t
134684decf41Sjp151216  */
134784decf41Sjp151216 static void
134884decf41Sjp151216 idmap_lookup_unlock_batch(idmap_query_state_t **state)
134984decf41Sjp151216 {
135084decf41Sjp151216 	/*
135184decf41Sjp151216 	 * Decrement reference count with qstatelock locked
135284decf41Sjp151216 	 */
135384decf41Sjp151216 	(void) pthread_mutex_lock(&qstatelock);
135484decf41Sjp151216 	(*state)->ref_cnt--;
135584decf41Sjp151216 	/*
135684decf41Sjp151216 	 * If there are no references wakup the allocating thread
135784decf41Sjp151216 	 */
135884decf41Sjp151216 	if ((*state)->ref_cnt == 0)
135984decf41Sjp151216 		(void) pthread_cond_signal(&(*state)->cv);
136084decf41Sjp151216 	(void) pthread_mutex_unlock(&qstatelock);
136184decf41Sjp151216 	*state = NULL;
136284decf41Sjp151216 }
136384decf41Sjp151216 
136484decf41Sjp151216 /*
136584decf41Sjp151216  * This routine frees the idmap_query_state_t structure
136684decf41Sjp151216  * If the reference count is greater than 1 it waits
136784decf41Sjp151216  * for the other threads to finish using it.
136884decf41Sjp151216  */
1369c5c4113dSnw141292 void
137084decf41Sjp151216 idmap_lookup_release_batch(idmap_query_state_t **state)
1371c5c4113dSnw141292 {
1372c5c4113dSnw141292 	idmap_query_state_t **p;
1373c5c4113dSnw141292 
137484decf41Sjp151216 	/*
137584decf41Sjp151216 	 * Decrement reference count with qstatelock locked
137684decf41Sjp151216 	 * and wait for reference count to get to zero
137784decf41Sjp151216 	 */
137884decf41Sjp151216 	(void) pthread_mutex_lock(&qstatelock);
137984decf41Sjp151216 	(*state)->ref_cnt--;
138084decf41Sjp151216 	while ((*state)->ref_cnt > 0) {
138184decf41Sjp151216 		(void) pthread_cond_wait(&(*state)->cv, &qstatelock);
138284decf41Sjp151216 	}
1383c5c4113dSnw141292 
1384c5c4113dSnw141292 	/* Remove this state struct from the list of state structs */
1385c5c4113dSnw141292 	for (p = &qstatehead; *p != NULL; p = &(*p)->next) {
1386c5c4113dSnw141292 		if (*p == (*state)) {
1387c5c4113dSnw141292 			*p = (*state)->next;
1388c5c4113dSnw141292 			break;
1389c5c4113dSnw141292 		}
1390c5c4113dSnw141292 	}
1391c5c4113dSnw141292 	(void) pthread_mutex_unlock(&qstatelock);
1392c5c4113dSnw141292 
139384decf41Sjp151216 	(void) pthread_cond_destroy(&(*state)->cv);
139484decf41Sjp151216 
139584decf41Sjp151216 	idmap_release_conn((*state)->qadh);
139684decf41Sjp151216 
1397c5c4113dSnw141292 	free(*state);
1398c5c4113dSnw141292 	*state = NULL;
1399c5c4113dSnw141292 }
1400c5c4113dSnw141292 
1401c5c4113dSnw141292 idmap_retcode
1402c5c4113dSnw141292 idmap_lookup_batch_end(idmap_query_state_t **state,
1403c5c4113dSnw141292 	struct timeval *timeout)
1404c5c4113dSnw141292 {
1405c5c4113dSnw141292 	idmap_q_t	    *q;
1406c5c4113dSnw141292 	int		    i;
1407c5c4113dSnw141292 	int		    rc = LDAP_SUCCESS;
1408c5c4113dSnw141292 	idmap_retcode	    retcode = IDMAP_SUCCESS;
1409c5c4113dSnw141292 
1410c5c4113dSnw141292 	(*state)->qdead = 1;
1411c5c4113dSnw141292 
1412c5c4113dSnw141292 	/* Process results until done or until timeout, if given */
1413c5c4113dSnw141292 	while ((*state)->qinflight > 0) {
1414c5c4113dSnw141292 		if ((rc = idmap_get_adobject_batch((*state)->qadh,
1415c5c4113dSnw141292 		    timeout)) != 0)
1416c5c4113dSnw141292 			break;
1417c5c4113dSnw141292 	}
1418c5c4113dSnw141292 
1419c5c4113dSnw141292 	if (rc == LDAP_UNAVAILABLE || rc == LDAP_UNWILLING_TO_PERFORM ||
1420c5c4113dSnw141292 	    rc == LDAP_CONNECT_ERROR || rc == LDAP_SERVER_DOWN ||
1421c5c4113dSnw141292 	    rc == LDAP_BUSY) {
1422c5c4113dSnw141292 		retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
1423c5c4113dSnw141292 		(*state)->qadh->dead = 1;
1424c5c4113dSnw141292 	}
1425c5c4113dSnw141292 
1426c5c4113dSnw141292 	for (i = 0; i < (*state)->qcount; i++) {
1427c5c4113dSnw141292 		q = &((*state)->queries[i]);
1428c5c4113dSnw141292 		if (q->got_reply && !q->got_results) {
1429c5c4113dSnw141292 			if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR)
1430c5c4113dSnw141292 				*q->rc = IDMAP_ERR_RETRIABLE_NET_ERR;
1431c5c4113dSnw141292 			else
1432c5c4113dSnw141292 				*q->rc = IDMAP_ERR_NOTFOUND;
1433c5c4113dSnw141292 		}
1434c5c4113dSnw141292 	}
1435c5c4113dSnw141292 
143684decf41Sjp151216 	idmap_lookup_release_batch(state);
1437c5c4113dSnw141292 
1438c5c4113dSnw141292 	return (retcode);
1439c5c4113dSnw141292 }
1440c5c4113dSnw141292 
1441c5c4113dSnw141292 /*
1442c5c4113dSnw141292  * Send one prepared search, queue up msgid, process what results are
1443c5c4113dSnw141292  * available
1444c5c4113dSnw141292  */
1445c5c4113dSnw141292 static
1446c5c4113dSnw141292 idmap_retcode
1447c5c4113dSnw141292 idmap_batch_add1(idmap_query_state_t *state, int n2s,
1448c5c4113dSnw141292 	const char *filter, const char *basedn,
1449c5c4113dSnw141292 	char **result, char **dname, rid_t *rid, int *sid_type,
1450c5c4113dSnw141292 	idmap_retcode *rc)
1451c5c4113dSnw141292 {
1452c5c4113dSnw141292 	idmap_retcode	retcode = IDMAP_SUCCESS;
1453c5c4113dSnw141292 	int		lrc, qid;
1454c5c4113dSnw141292 	struct timeval	tv;
1455c5c4113dSnw141292 	idmap_q_t	*q;
1456c5c4113dSnw141292 
1457c5c4113dSnw141292 	if (state->qdead) {
1458c5c4113dSnw141292 		*rc = IDMAP_ERR_NORESULT;
1459c5c4113dSnw141292 		return (IDMAP_ERR_RETRIABLE_NET_ERR);
1460c5c4113dSnw141292 	}
1461c5c4113dSnw141292 
1462c5c4113dSnw141292 	qid = atomic_inc_32_nv(&state->qlastsent) - 1;
1463c5c4113dSnw141292 
1464c5c4113dSnw141292 	q = &(state->queries[qid]);
1465c5c4113dSnw141292 
1466c5c4113dSnw141292 	/* Remember where to put the results */
1467c5c4113dSnw141292 	q->result = result;
1468c5c4113dSnw141292 	q->domain = dname;
1469c5c4113dSnw141292 	q->rid = rid;
1470c5c4113dSnw141292 	q->sid_type = sid_type;
1471c5c4113dSnw141292 	q->rc = rc;
1472c5c4113dSnw141292 	q->n2s = n2s ? 1 : 0;
1473c5c4113dSnw141292 	q->got_objectSid = 0;
1474c5c4113dSnw141292 	q->got_objectClass = 0;
1475c5c4113dSnw141292 	q->got_samAcctName = 0;
1476c5c4113dSnw141292 
1477c5c4113dSnw141292 	/*
1478c5c4113dSnw141292 	 * Provide sane defaults for the results in case we never hear
1479c5c4113dSnw141292 	 * back from the DS before closing the connection.
1480c5c4113dSnw141292 	 */
1481c5c4113dSnw141292 	*rc = IDMAP_ERR_RETRIABLE_NET_ERR;
1482c5c4113dSnw141292 	*sid_type = _IDMAP_T_OTHER;
1483c5c4113dSnw141292 	*result = NULL;
1484c5c4113dSnw141292 	if (dname != NULL)
1485c5c4113dSnw141292 		*dname = NULL;
1486c5c4113dSnw141292 	if (rid != NULL)
1487c5c4113dSnw141292 		*rid = 0;
1488c5c4113dSnw141292 
1489c5c4113dSnw141292 	/* Send this lookup, don't wait for a result here */
1490c5c4113dSnw141292 	(void) pthread_mutex_lock(&state->qadh->lock);
1491c5c4113dSnw141292 
1492c5c4113dSnw141292 	if (!state->qadh->dead) {
1493c5c4113dSnw141292 		state->qadh->idletime = time(NULL);
1494c5c4113dSnw141292 		lrc = ldap_search_ext(state->qadh->ld, basedn,
1495c5c4113dSnw141292 		    LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
1496c5c4113dSnw141292 		    NULL, -1, &q->msgid);
1497c5c4113dSnw141292 		if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE ||
1498c5c4113dSnw141292 		    lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN ||
1499c5c4113dSnw141292 		    lrc == LDAP_UNWILLING_TO_PERFORM) {
1500c5c4113dSnw141292 			retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
1501c5c4113dSnw141292 			state->qadh->dead = 1;
1502c5c4113dSnw141292 		} else if (lrc != LDAP_SUCCESS) {
1503c5c4113dSnw141292 			retcode = IDMAP_ERR_OTHER;
1504c5c4113dSnw141292 			state->qadh->dead = 1;
1505c5c4113dSnw141292 		}
1506c5c4113dSnw141292 	}
1507c5c4113dSnw141292 	(void) pthread_mutex_unlock(&state->qadh->lock);
1508c5c4113dSnw141292 
1509c5c4113dSnw141292 	if (state->qadh->dead)
1510c5c4113dSnw141292 		return (retcode);
1511c5c4113dSnw141292 
1512c5c4113dSnw141292 	atomic_inc_32(&state->qinflight);
1513c5c4113dSnw141292 
1514c5c4113dSnw141292 	/*
1515c5c4113dSnw141292 	 * Reap as many requests as we can _without_ waiting
1516c5c4113dSnw141292 	 *
1517c5c4113dSnw141292 	 * We do this to prevent any possible TCP socket buffer
1518c5c4113dSnw141292 	 * starvation deadlocks.
1519c5c4113dSnw141292 	 */
1520c5c4113dSnw141292 	(void) memset(&tv, 0, sizeof (tv));
1521c5c4113dSnw141292 	while (idmap_get_adobject_batch(state->qadh, &tv) == 0)
1522c5c4113dSnw141292 		;
1523c5c4113dSnw141292 
1524c5c4113dSnw141292 	return (IDMAP_SUCCESS);
1525c5c4113dSnw141292 }
1526c5c4113dSnw141292 
1527c5c4113dSnw141292 idmap_retcode
1528c5c4113dSnw141292 idmap_name2sid_batch_add1(idmap_query_state_t *state,
1529c5c4113dSnw141292 	const char *name, const char *dname,
1530c5c4113dSnw141292 	char **sid, rid_t *rid, int *sid_type, idmap_retcode *rc)
1531c5c4113dSnw141292 {
1532c5c4113dSnw141292 	idmap_retcode	retcode;
1533c5c4113dSnw141292 	int		flen, samAcctNameLen;
1534c5c4113dSnw141292 	char		*filter = NULL;
1535c5c4113dSnw141292 	char		*basedn = NULL;
1536c5c4113dSnw141292 	char		*cp;
1537c5c4113dSnw141292 
1538c5c4113dSnw141292 	/*
1539c5c4113dSnw141292 	 * Strategy: search [the global catalog] for user/group by
1540c5c4113dSnw141292 	 * sAMAccountName = user/groupname with base DN derived from the
1541c5c4113dSnw141292 	 * domain name.  The objectSid and objectClass of the result are
1542c5c4113dSnw141292 	 * all we need to figure out the SID of the user/group and
1543c5c4113dSnw141292 	 * whether it is a user or a group.
1544c5c4113dSnw141292 	 */
1545c5c4113dSnw141292 
1546c5c4113dSnw141292 	/*
1547c5c4113dSnw141292 	 * Handle optional domain parameter and default domain
1548c5c4113dSnw141292 	 * semantics.  The get a basedn from the domainname.
1549c5c4113dSnw141292 	 */
1550d3a612caSnw141292 	samAcctNameLen = strlen(name);
1551d3a612caSnw141292 	if (dname == NULL || *dname == '\0') {
1552c5c4113dSnw141292 		/* domain name not given separately */
1553c5c4113dSnw141292 		if ((cp = strchr(name, '@')) == NULL) {
1554c5c4113dSnw141292 			/* nor is the name qualified */
1555c5c4113dSnw141292 			dname = state->qadh->owner->dflt_w2k_dom;
1556c5c4113dSnw141292 			basedn = state->qadh->owner->basedn;
1557c5c4113dSnw141292 		} else {
1558c5c4113dSnw141292 			/* the name is qualified */
1559d3a612caSnw141292 			samAcctNameLen -= strlen(cp);
1560c5c4113dSnw141292 			dname = cp + 1;
1561c5c4113dSnw141292 		}
1562c5c4113dSnw141292 	}
1563c5c4113dSnw141292 
1564c5c4113dSnw141292 	if (basedn == NULL)
1565c5c4113dSnw141292 		basedn = dns2dn(dname);
1566c5c4113dSnw141292 
1567c5c4113dSnw141292 	/* Assemble filter */
1568c5c4113dSnw141292 	flen = snprintf(NULL, 0, SANFILTER, samAcctNameLen, name) + 1;
1569c5c4113dSnw141292 	if ((filter = (char *)malloc(flen)) == NULL) {
1570c5c4113dSnw141292 		if (basedn != state->qadh->owner->basedn)
1571c5c4113dSnw141292 			free(basedn);
1572c5c4113dSnw141292 		return (IDMAP_ERR_MEMORY);
1573c5c4113dSnw141292 	}
1574c5c4113dSnw141292 	(void) snprintf(filter, flen, SANFILTER, samAcctNameLen, name);
1575c5c4113dSnw141292 
1576c5c4113dSnw141292 	retcode = idmap_batch_add1(state, 1, filter, basedn,
1577c5c4113dSnw141292 	    sid, NULL, rid, sid_type, rc);
1578c5c4113dSnw141292 
1579c5c4113dSnw141292 	if (basedn != state->qadh->owner->basedn)
1580c5c4113dSnw141292 		free(basedn);
1581c5c4113dSnw141292 	free(filter);
1582c5c4113dSnw141292 
1583c5c4113dSnw141292 	return (retcode);
1584c5c4113dSnw141292 }
1585c5c4113dSnw141292 
1586c5c4113dSnw141292 idmap_retcode
1587c5c4113dSnw141292 idmap_sid2name_batch_add1(idmap_query_state_t *state,
1588c5c4113dSnw141292 	const char *sid, const rid_t *rid,
1589c5c4113dSnw141292 	char **name, char **dname, int *sid_type, idmap_retcode *rc)
1590c5c4113dSnw141292 {
1591c5c4113dSnw141292 	idmap_retcode	retcode;
1592c5c4113dSnw141292 	int		flen, ret;
1593c5c4113dSnw141292 	char		*filter = NULL;
1594c5c4113dSnw141292 	char		cbinsid[MAXHEXBINSID + 1];
1595c5c4113dSnw141292 
1596c5c4113dSnw141292 	/*
1597c5c4113dSnw141292 	 * Strategy: search [the global catalog] for user/group by
1598c5c4113dSnw141292 	 * objectSid = SID with empty base DN.  The DN, sAMAccountName
1599c5c4113dSnw141292 	 * and objectClass of the result are all we need to figure out
1600c5c4113dSnw141292 	 * the name of the SID and whether it is a user, a group or a
1601c5c4113dSnw141292 	 * computer.
1602c5c4113dSnw141292 	 */
1603c5c4113dSnw141292 
1604c5c4113dSnw141292 	ret = idmap_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid));
1605c5c4113dSnw141292 	if (ret != 0)
1606c5c4113dSnw141292 		return (IDMAP_ERR_SID);
1607c5c4113dSnw141292 
1608c5c4113dSnw141292 	/* Assemble filter */
1609c5c4113dSnw141292 	flen = snprintf(NULL, 0, OBJECTSIDFILTER, cbinsid) + 1;
1610c5c4113dSnw141292 	if ((filter = (char *)malloc(flen)) == NULL)
1611c5c4113dSnw141292 		return (IDMAP_ERR_MEMORY);
1612c5c4113dSnw141292 	(void) snprintf(filter, flen, OBJECTSIDFILTER, cbinsid);
1613c5c4113dSnw141292 
1614c5c4113dSnw141292 	retcode = idmap_batch_add1(state, 0, filter, NULL, name, dname,
1615c5c4113dSnw141292 	    NULL, sid_type, rc);
1616c5c4113dSnw141292 
1617c5c4113dSnw141292 	free(filter);
1618c5c4113dSnw141292 
1619c5c4113dSnw141292 	return (retcode);
1620c5c4113dSnw141292 }
1621