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