xref: /titanic_51/usr/src/cmd/idmap/idmapd/adutils.c (revision 479ac37569625bae44ffb80071d4bc865fc710ed)
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 /*
234edd44c5Sjp151216  * Copyright 2008 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>
50cd37da74Snw141292 #include <sys/u8_textprep.h>
51*479ac375Sdm199847 #include "nldaputils.h"
52c5c4113dSnw141292 #include "idmapd.h"
53c5c4113dSnw141292 
54c5c4113dSnw141292 /*
55c5c4113dSnw141292  * Internal data structures for this code
56c5c4113dSnw141292  */
57c5c4113dSnw141292 
58c5c4113dSnw141292 /* Attribute names and filter format strings */
59e3c2d6aaSnw141292 #define	SAN		"sAMAccountName"
60e3c2d6aaSnw141292 #define	OBJSID		"objectSid"
61e3c2d6aaSnw141292 #define	OBJCLASS	"objectClass"
62c5c4113dSnw141292 #define	SANFILTER	"(sAMAccountName=%.*s)"
63e3c2d6aaSnw141292 #define	OBJSIDFILTER	"(objectSid=%s)"
64c5c4113dSnw141292 
65c5c4113dSnw141292 /*
66c5c4113dSnw141292  * This should really be in some <sys/sid.h> file or so; we have a
67c5c4113dSnw141292  * private version of sid_t, and so must other components of ON until we
68c5c4113dSnw141292  * rationalize this.
69c5c4113dSnw141292  */
70c5c4113dSnw141292 typedef struct sid {
71c5c4113dSnw141292 	uchar_t		version;
72c5c4113dSnw141292 	uchar_t		sub_authority_count;
73c5c4113dSnw141292 	uint64_t	authority;  /* really, 48-bits */
74c5c4113dSnw141292 	rid_t		sub_authorities[SID_MAX_SUB_AUTHORITIES];
75c5c4113dSnw141292 } sid_t;
76c5c4113dSnw141292 
77c5c4113dSnw141292 /* A single DS */
78c5c4113dSnw141292 typedef struct ad_host {
79c5c4113dSnw141292 	struct ad_host		*next;
80c5c4113dSnw141292 	ad_t			*owner;		/* ad_t to which this belongs */
81c5c4113dSnw141292 	pthread_mutex_t		lock;
82c5c4113dSnw141292 	LDAP			*ld;		/* LDAP connection */
83c5c4113dSnw141292 	uint32_t		ref;		/* ref count */
84c5c4113dSnw141292 	time_t			idletime;	/* time since last activity */
85c5c4113dSnw141292 	int			dead;		/* error on LDAP connection */
86c5c4113dSnw141292 	/*
87c5c4113dSnw141292 	 * Used to distinguish between different instances of LDAP
88c5c4113dSnw141292 	 * connections to this same DS.  We need this so we never mix up
89c5c4113dSnw141292 	 * results for a given msgID from one connection with those of
90c5c4113dSnw141292 	 * another earlier connection where two batch state structures
91c5c4113dSnw141292 	 * share this ad_host object but used different LDAP connections
92c5c4113dSnw141292 	 * to send their LDAP searches.
93c5c4113dSnw141292 	 */
94c5c4113dSnw141292 	uint64_t		generation;
95c5c4113dSnw141292 
96c5c4113dSnw141292 	/* LDAP DS info */
97c5c4113dSnw141292 	char			*host;
98c5c4113dSnw141292 	int			port;
99c5c4113dSnw141292 
100c5c4113dSnw141292 	/* hardwired to SASL GSSAPI only for now */
101c5c4113dSnw141292 	char			*saslmech;
102c5c4113dSnw141292 	unsigned		saslflags;
103bcced03bSjp151216 
104bcced03bSjp151216 	/* Number of outstanding search requests */
105bcced03bSjp151216 	uint32_t		max_requests;
106bcced03bSjp151216 	uint32_t		num_requests;
107c5c4113dSnw141292 } ad_host_t;
108c5c4113dSnw141292 
109c5c4113dSnw141292 /* A set of DSs for a given AD partition; ad_t typedef comes from  adutil.h */
110c5c4113dSnw141292 struct ad {
111c5c4113dSnw141292 	char			*dflt_w2k_dom;	/* used to qualify bare names */
112c5c4113dSnw141292 	pthread_mutex_t		lock;
113c5c4113dSnw141292 	uint32_t		ref;
114c8e26105Sjp151216 	ad_host_t		*last_adh;
115c5c4113dSnw141292 	idmap_ad_partition_t	partition;	/* Data or global catalog? */
116c5c4113dSnw141292 };
117c5c4113dSnw141292 
118c5c4113dSnw141292 /*
119c5c4113dSnw141292  * A place to put the results of a batched (async) query
120c5c4113dSnw141292  *
121c5c4113dSnw141292  * There is one of these for every query added to a batch object
122c5c4113dSnw141292  * (idmap_query_state, see below).
123c5c4113dSnw141292  */
124c5c4113dSnw141292 typedef struct idmap_q {
125e3c2d6aaSnw141292 	/*
126e3c2d6aaSnw141292 	 * data used for validating search result entries for name->SID
127e8c27ec8Sbaban 	 * lookups
128e3c2d6aaSnw141292 	 */
129cd37da74Snw141292 	char			*ecanonname;	/* expected canon name */
130cd37da74Snw141292 	char			*edomain;	/* expected domain name */
131e8c27ec8Sbaban 	int			eunixtype;	/* expected unix type */
132e3c2d6aaSnw141292 	/* results */
133cd37da74Snw141292 	char			**canonname;	/* actual canon name */
134c5c4113dSnw141292 	char			**domain;	/* name of domain of object */
135e8c27ec8Sbaban 	char			**sid;		/* stringified SID */
136e8c27ec8Sbaban 	rid_t			*rid;		/* RID */
137e8c27ec8Sbaban 	int			*sid_type;	/* user or group SID? */
138e8c27ec8Sbaban 	char			**unixname;	/* unixname for name mapping */
13948258c6bSjp151216 	char			**dn;		/* DN of entry */
14048258c6bSjp151216 	char			**attr;		/* Attr for name mapping */
14148258c6bSjp151216 	char			**value;	/* value for name mapping */
142c5c4113dSnw141292 	idmap_retcode		*rc;
143e3c2d6aaSnw141292 
144e3c2d6aaSnw141292 	/* lookup state */
145c5c4113dSnw141292 	int			msgid;		/* LDAP message ID */
146bcced03bSjp151216 	/*
147bcced03bSjp151216 	 * The LDAP search entry result is placed here to be processed
148bcced03bSjp151216 	 * when the search done result is received.
149bcced03bSjp151216 	 */
150bcced03bSjp151216 	LDAPMessage		*search_res;	/* The LDAP search result */
151c5c4113dSnw141292 } idmap_q_t;
152c5c4113dSnw141292 
153c5c4113dSnw141292 /* Batch context structure; typedef is in header file */
154c5c4113dSnw141292 struct idmap_query_state {
155c5c4113dSnw141292 	idmap_query_state_t	*next;
156c5c4113dSnw141292 	int			qcount;		/* how many queries */
15784decf41Sjp151216 	int			ref_cnt;	/* reference count */
15884decf41Sjp151216 	pthread_cond_t		cv;		/* Condition wait variable */
159c5c4113dSnw141292 	uint32_t		qlastsent;
160c5c4113dSnw141292 	uint32_t		qinflight;	/* how many queries in flight */
161c5c4113dSnw141292 	uint16_t		qdead;		/* oops, lost LDAP connection */
162c5c4113dSnw141292 	ad_host_t		*qadh;		/* LDAP connection */
163c5c4113dSnw141292 	uint64_t		qadh_gen;	/* same as qadh->generation */
164e8c27ec8Sbaban 	const char		*ad_unixuser_attr;
165e8c27ec8Sbaban 	const char		*ad_unixgroup_attr;
166c5c4113dSnw141292 	idmap_q_t		queries[1];	/* array of query results */
167c5c4113dSnw141292 };
168c5c4113dSnw141292 
169c5c4113dSnw141292 /*
170c5c4113dSnw141292  * List of query state structs -- needed so we can "route" LDAP results
171c5c4113dSnw141292  * to the right context if multiple threads should be using the same
172c5c4113dSnw141292  * connection concurrently
173c5c4113dSnw141292  */
174c5c4113dSnw141292 static idmap_query_state_t	*qstatehead = NULL;
175c5c4113dSnw141292 static pthread_mutex_t		qstatelock = PTHREAD_MUTEX_INITIALIZER;
176c5c4113dSnw141292 
177c5c4113dSnw141292 /*
178c5c4113dSnw141292  * List of DSs, needed by the idle connection reaper thread
179c5c4113dSnw141292  */
180c5c4113dSnw141292 static ad_host_t	*host_head = NULL;
181651c0131Sbaban static pthread_t	reaperid = 0;
182c5c4113dSnw141292 static pthread_mutex_t	adhostlock = PTHREAD_MUTEX_INITIALIZER;
183c5c4113dSnw141292 
18484decf41Sjp151216 
18584decf41Sjp151216 static void
18684decf41Sjp151216 idmap_lookup_unlock_batch(idmap_query_state_t **state);
18784decf41Sjp151216 
188c8e26105Sjp151216 static void
189c8e26105Sjp151216 delete_ds(ad_t *ad, const char *host, int port);
19084decf41Sjp151216 
191c5c4113dSnw141292 /*ARGSUSED*/
192c5c4113dSnw141292 static int
1934edd44c5Sjp151216 idmap_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts)
1944edd44c5Sjp151216 {
195c5c4113dSnw141292 	sasl_interact_t	*interact;
196c5c4113dSnw141292 
197c5c4113dSnw141292 	if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE)
198c5c4113dSnw141292 		return (LDAP_PARAM_ERROR);
199c5c4113dSnw141292 
200c5c4113dSnw141292 	/* There should be no extra arguemnts for SASL/GSSAPI authentication */
201c5c4113dSnw141292 	for (interact = prompts; interact->id != SASL_CB_LIST_END;
202c5c4113dSnw141292 	    interact++) {
203c5c4113dSnw141292 		interact->result = NULL;
204c5c4113dSnw141292 		interact->len = 0;
205c5c4113dSnw141292 	}
206c5c4113dSnw141292 	return (LDAP_SUCCESS);
207c5c4113dSnw141292 }
208c5c4113dSnw141292 
209c5c4113dSnw141292 
210c5c4113dSnw141292 /*
211c5c4113dSnw141292  * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other
212e3c2d6aaSnw141292  * attributes (CN, etc...).  We don't need the reverse, for now.
213c5c4113dSnw141292  */
214c5c4113dSnw141292 static
215c5c4113dSnw141292 char *
216c5c4113dSnw141292 dn2dns(const char *dn)
217c5c4113dSnw141292 {
218c5c4113dSnw141292 	char **rdns = NULL;
219c5c4113dSnw141292 	char **attrs = NULL;
220c5c4113dSnw141292 	char **labels = NULL;
221c5c4113dSnw141292 	char *dns = NULL;
222c5c4113dSnw141292 	char **rdn, **attr, **label;
223c5c4113dSnw141292 	int maxlabels = 5;
224c5c4113dSnw141292 	int nlabels = 0;
225c5c4113dSnw141292 	int dnslen;
226c5c4113dSnw141292 
227c5c4113dSnw141292 	/*
228c5c4113dSnw141292 	 * There is no reverse of ldap_dns_to_dn() in our libldap, so we
229c5c4113dSnw141292 	 * have to do the hard work here for now.
230c5c4113dSnw141292 	 */
231c5c4113dSnw141292 
232c5c4113dSnw141292 	/*
233c5c4113dSnw141292 	 * This code is much too liberal: it looks for "dc" attributes
234c5c4113dSnw141292 	 * in all RDNs of the DN.  In theory this could cause problems
235c5c4113dSnw141292 	 * if people were to use "dc" in nodes other than the root of
236c5c4113dSnw141292 	 * the tree, but in practice noone, least of all Active
237c5c4113dSnw141292 	 * Directory, does that.
238c5c4113dSnw141292 	 *
239c5c4113dSnw141292 	 * On the other hand, this code is much too conservative: it
240c5c4113dSnw141292 	 * does not make assumptions about ldap_explode_dn(), and _that_
241c5c4113dSnw141292 	 * is the true for looking at every attr of every RDN.
242c5c4113dSnw141292 	 *
243c5c4113dSnw141292 	 * Since we only ever look at dc and those must be DNS labels,
244c5c4113dSnw141292 	 * at least until we get around to supporting IDN here we
245c5c4113dSnw141292 	 * shouldn't see escaped labels from AD nor from libldap, though
246c5c4113dSnw141292 	 * the spec (RFC2253) does allow libldap to escape things that
247c5c4113dSnw141292 	 * don't need escaping -- if that should ever happen then
248c5c4113dSnw141292 	 * libldap will need a spanking, and we can take care of that.
249c5c4113dSnw141292 	 */
250c5c4113dSnw141292 
251c5c4113dSnw141292 	/* Explode a DN into RDNs */
252c5c4113dSnw141292 	if ((rdns = ldap_explode_dn(dn, 0)) == NULL)
253c5c4113dSnw141292 		return (NULL);
254c5c4113dSnw141292 
255c5c4113dSnw141292 	labels = calloc(maxlabels + 1, sizeof (char *));
256c5c4113dSnw141292 	label = labels;
257c5c4113dSnw141292 
258c5c4113dSnw141292 	for (rdn = rdns; *rdn != NULL; rdn++) {
259c5c4113dSnw141292 		if (attrs != NULL)
260c5c4113dSnw141292 			ldap_value_free(attrs);
261c5c4113dSnw141292 
262c5c4113dSnw141292 		/* Explode each RDN, look for DC attr, save val as DNS label */
263c5c4113dSnw141292 		if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL)
264c5c4113dSnw141292 			goto done;
265c5c4113dSnw141292 
266c5c4113dSnw141292 		for (attr = attrs; *attr != NULL; attr++) {
267c5c4113dSnw141292 			if (strncasecmp(*attr, "dc=", 3) != 0)
268c5c4113dSnw141292 				continue;
269c5c4113dSnw141292 
270c5c4113dSnw141292 			/* Found a DNS label */
271c5c4113dSnw141292 			labels[nlabels++] = strdup((*attr) + 3);
272c5c4113dSnw141292 
273c5c4113dSnw141292 			if (nlabels == maxlabels) {
274c5c4113dSnw141292 				char **tmp;
275c5c4113dSnw141292 				tmp = realloc(labels,
276c5c4113dSnw141292 				    sizeof (char *) * (maxlabels + 1));
277c5c4113dSnw141292 
278c5c4113dSnw141292 				if (tmp == NULL)
279c5c4113dSnw141292 					goto done;
280c5c4113dSnw141292 
281c5c4113dSnw141292 				labels = tmp;
282c5c4113dSnw141292 				labels[nlabels] = NULL;
283c5c4113dSnw141292 			}
284c5c4113dSnw141292 
285c5c4113dSnw141292 			/* There should be just one DC= attr per-RDN */
286c5c4113dSnw141292 			break;
287c5c4113dSnw141292 		}
288c5c4113dSnw141292 	}
289c5c4113dSnw141292 
290c5c4113dSnw141292 	/*
291c5c4113dSnw141292 	 * Got all the labels, now join with '.'
292c5c4113dSnw141292 	 *
293c5c4113dSnw141292 	 * We need room for nlabels - 1 periods ('.'), one nul
294c5c4113dSnw141292 	 * terminator, and the strlen() of each label.
295c5c4113dSnw141292 	 */
296c5c4113dSnw141292 	dnslen = nlabels;
297c5c4113dSnw141292 	for (label = labels; *label != NULL; label++)
298c5c4113dSnw141292 		dnslen += strlen(*label);
299c5c4113dSnw141292 
300c5c4113dSnw141292 	if ((dns = malloc(dnslen)) == NULL)
301c5c4113dSnw141292 		goto done;
302c5c4113dSnw141292 
303c5c4113dSnw141292 	*dns = '\0';
304c5c4113dSnw141292 
305c5c4113dSnw141292 	for (label = labels; *label != NULL; label++) {
306c5c4113dSnw141292 		(void) strlcat(dns, *label, dnslen);
307c5c4113dSnw141292 		/*
308c5c4113dSnw141292 		 * NOTE: the last '.' won't be appended -- there's no room
309c5c4113dSnw141292 		 * for it!
310c5c4113dSnw141292 		 */
311c5c4113dSnw141292 		(void) strlcat(dns, ".", dnslen);
312c5c4113dSnw141292 	}
313c5c4113dSnw141292 
314c5c4113dSnw141292 done:
315c5c4113dSnw141292 	if (labels != NULL) {
316c5c4113dSnw141292 		for (label = labels; *label != NULL; label++)
317c5c4113dSnw141292 			free(*label);
318c5c4113dSnw141292 		free(labels);
319c5c4113dSnw141292 	}
320c5c4113dSnw141292 	if (attrs != NULL)
321c5c4113dSnw141292 		ldap_value_free(attrs);
322c5c4113dSnw141292 	if (rdns != NULL)
323c5c4113dSnw141292 		ldap_value_free(rdns);
324c5c4113dSnw141292 
325c5c4113dSnw141292 	return (dns);
326c5c4113dSnw141292 }
327c5c4113dSnw141292 
328c5c4113dSnw141292 /*
329c5c4113dSnw141292  * Keep connection management simple for now, extend or replace later
330c5c4113dSnw141292  * with updated libsldap code.
331c5c4113dSnw141292  */
332c5c4113dSnw141292 #define	ADREAPERSLEEP	60
333c5c4113dSnw141292 #define	ADCONN_TIME	300
334c5c4113dSnw141292 
335c5c4113dSnw141292 /*
336c5c4113dSnw141292  * Idle connection reaping side of connection management
337c5c4113dSnw141292  *
338c5c4113dSnw141292  * Every minute wake up and look for connections that have been idle for
339c5c4113dSnw141292  * five minutes or more and close them.
340c5c4113dSnw141292  */
341c5c4113dSnw141292 /*ARGSUSED*/
342c5c4113dSnw141292 static
343c5c4113dSnw141292 void
344c5c4113dSnw141292 adreaper(void *arg)
345c5c4113dSnw141292 {
346c5c4113dSnw141292 	ad_host_t	*adh;
347c5c4113dSnw141292 	time_t		now;
348c5c4113dSnw141292 	timespec_t	ts;
349c5c4113dSnw141292 
350c5c4113dSnw141292 	ts.tv_sec = ADREAPERSLEEP;
351c5c4113dSnw141292 	ts.tv_nsec = 0;
352c5c4113dSnw141292 
353c5c4113dSnw141292 	for (;;) {
354c5c4113dSnw141292 		/*
355c5c4113dSnw141292 		 * nanosleep(3RT) is thead-safe (no SIGALRM) and more
356c5c4113dSnw141292 		 * portable than usleep(3C)
357c5c4113dSnw141292 		 */
358c5c4113dSnw141292 		(void) nanosleep(&ts, NULL);
359c5c4113dSnw141292 		(void) pthread_mutex_lock(&adhostlock);
360c5c4113dSnw141292 		now = time(NULL);
361c5c4113dSnw141292 		for (adh = host_head; adh != NULL; adh = adh->next) {
362c5c4113dSnw141292 			(void) pthread_mutex_lock(&adh->lock);
363c5c4113dSnw141292 			if (adh->ref == 0 && adh->idletime != 0 &&
364c5c4113dSnw141292 			    adh->idletime + ADCONN_TIME < now) {
365c5c4113dSnw141292 				if (adh->ld) {
366c5c4113dSnw141292 					(void) ldap_unbind(adh->ld);
367c5c4113dSnw141292 					adh->ld = NULL;
368c5c4113dSnw141292 					adh->idletime = 0;
369c5c4113dSnw141292 					adh->ref = 0;
370c5c4113dSnw141292 				}
371c5c4113dSnw141292 			}
372c5c4113dSnw141292 			(void) pthread_mutex_unlock(&adh->lock);
373c5c4113dSnw141292 		}
374c5c4113dSnw141292 		(void) pthread_mutex_unlock(&adhostlock);
375c5c4113dSnw141292 	}
376c5c4113dSnw141292 }
377c5c4113dSnw141292 
378c5c4113dSnw141292 int
379c5c4113dSnw141292 idmap_ad_alloc(ad_t **new_ad, const char *default_domain,
380c5c4113dSnw141292 		idmap_ad_partition_t part)
381c5c4113dSnw141292 {
382c5c4113dSnw141292 	ad_t *ad;
383c5c4113dSnw141292 
384c5c4113dSnw141292 	*new_ad = NULL;
385c5c4113dSnw141292 
386c5c4113dSnw141292 	if ((default_domain == NULL || *default_domain == '\0') &&
387c5c4113dSnw141292 	    part != IDMAP_AD_GLOBAL_CATALOG)
388c5c4113dSnw141292 		return (-1);
389c5c4113dSnw141292 
390c5c4113dSnw141292 	if ((ad = calloc(1, sizeof (ad_t))) == NULL)
391c5c4113dSnw141292 		return (-1);
392c5c4113dSnw141292 
393c5c4113dSnw141292 	ad->ref = 1;
394c5c4113dSnw141292 	ad->partition = part;
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 (pthread_mutex_init(&ad->lock, NULL) != 0)
403c5c4113dSnw141292 		goto err;
404c5c4113dSnw141292 
405c5c4113dSnw141292 	*new_ad = ad;
406c5c4113dSnw141292 
407c5c4113dSnw141292 	return (0);
408c5c4113dSnw141292 err:
409c5c4113dSnw141292 	if (ad->dflt_w2k_dom != NULL)
410c5c4113dSnw141292 		free(ad->dflt_w2k_dom);
411c5c4113dSnw141292 	free(ad);
412c5c4113dSnw141292 	return (-1);
413c5c4113dSnw141292 }
414c5c4113dSnw141292 
415c5c4113dSnw141292 
416c5c4113dSnw141292 void
417c5c4113dSnw141292 idmap_ad_free(ad_t **ad)
418c5c4113dSnw141292 {
419c5c4113dSnw141292 	ad_host_t *p;
420c8e26105Sjp151216 	ad_host_t *prev;
421c5c4113dSnw141292 
422c5c4113dSnw141292 	if (ad == NULL || *ad == NULL)
423c5c4113dSnw141292 		return;
424c5c4113dSnw141292 
425c5c4113dSnw141292 	(void) pthread_mutex_lock(&(*ad)->lock);
426c5c4113dSnw141292 
427c5c4113dSnw141292 	if (atomic_dec_32_nv(&(*ad)->ref) > 0) {
428c5c4113dSnw141292 		(void) pthread_mutex_unlock(&(*ad)->lock);
429c5c4113dSnw141292 		*ad = NULL;
430c5c4113dSnw141292 		return;
431c5c4113dSnw141292 	}
432c5c4113dSnw141292 
433c8e26105Sjp151216 	(void) pthread_mutex_lock(&adhostlock);
434c8e26105Sjp151216 	prev = NULL;
435c8e26105Sjp151216 	p = host_head;
436c8e26105Sjp151216 	while (p != NULL) {
437c8e26105Sjp151216 		if (p->owner != (*ad)) {
438c8e26105Sjp151216 			prev = p;
439c8e26105Sjp151216 			p = p->next;
440c5c4113dSnw141292 			continue;
441c8e26105Sjp151216 		} else {
442c8e26105Sjp151216 			delete_ds((*ad), p->host, p->port);
443c8e26105Sjp151216 			if (prev == NULL)
444c8e26105Sjp151216 				p = host_head;
445c8e26105Sjp151216 			else
446c8e26105Sjp151216 				p = prev->next;
447c5c4113dSnw141292 		}
448c8e26105Sjp151216 	}
449c8e26105Sjp151216 	(void) pthread_mutex_unlock(&adhostlock);
450c5c4113dSnw141292 
451c5c4113dSnw141292 	(void) pthread_mutex_unlock(&(*ad)->lock);
452c5c4113dSnw141292 	(void) pthread_mutex_destroy(&(*ad)->lock);
453c5c4113dSnw141292 
454e3c2d6aaSnw141292 	free((*ad)->dflt_w2k_dom);
455c5c4113dSnw141292 	free(*ad);
456c5c4113dSnw141292 
457c5c4113dSnw141292 	*ad = NULL;
458c5c4113dSnw141292 }
459c5c4113dSnw141292 
460c8e26105Sjp151216 
461c5c4113dSnw141292 static
462c5c4113dSnw141292 int
4630dcc7149Snw141292 idmap_open_conn(ad_host_t *adh, int timeoutsecs)
464c5c4113dSnw141292 {
465c5c4113dSnw141292 	int zero = 0;
466c8e26105Sjp151216 	int ldversion, rc;
4670dcc7149Snw141292 	int timeoutms = timeoutsecs * 1000;
468c8e26105Sjp151216 
469c8e26105Sjp151216 	if (adh == NULL)
470c8e26105Sjp151216 		return (0);
471c8e26105Sjp151216 
472c8e26105Sjp151216 	(void) pthread_mutex_lock(&adh->lock);
473c8e26105Sjp151216 
474c8e26105Sjp151216 	if (!adh->dead && adh->ld != NULL)
475c8e26105Sjp151216 		/* done! */
476c8e26105Sjp151216 		goto out;
477c8e26105Sjp151216 
478c8e26105Sjp151216 	if (adh->ld != NULL) {
479c8e26105Sjp151216 		(void) ldap_unbind(adh->ld);
480c8e26105Sjp151216 		adh->ld = NULL;
481c8e26105Sjp151216 	}
482bcced03bSjp151216 	adh->num_requests = 0;
483c5c4113dSnw141292 
484c5c4113dSnw141292 	atomic_inc_64(&adh->generation);
485c8e26105Sjp151216 
486c8e26105Sjp151216 	/* Open and bind an LDAP connection */
487c5c4113dSnw141292 	adh->ld = ldap_init(adh->host, adh->port);
488c8e26105Sjp151216 	if (adh->ld == NULL) {
489c8e26105Sjp151216 		idmapdlog(LOG_INFO, "ldap_init() to server "
490c8e26105Sjp151216 		    "%s port %d failed. (%s)", adh->host,
491c8e26105Sjp151216 		    adh->port, strerror(errno));
492c8e26105Sjp151216 		goto out;
493c8e26105Sjp151216 	}
494c5c4113dSnw141292 	ldversion = LDAP_VERSION3;
495c8e26105Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion);
496c8e26105Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
497c5c4113dSnw141292 	(void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero);
498c5c4113dSnw141292 	(void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero);
499c8e26105Sjp151216 	(void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
500c5c4113dSnw141292 	(void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
501c8e26105Sjp151216 	rc = ldap_sasl_interactive_bind_s(adh->ld, "" /* binddn */,
502c8e26105Sjp151216 	    adh->saslmech, NULL, NULL, adh->saslflags, &idmap_saslcallback,
503c8e26105Sjp151216 	    NULL);
504c5c4113dSnw141292 
505c5c4113dSnw141292 	if (rc != LDAP_SUCCESS) {
506c8e26105Sjp151216 		(void) ldap_unbind(adh->ld);
507c8e26105Sjp151216 		adh->ld = NULL;
508c8e26105Sjp151216 		idmapdlog(LOG_INFO, "ldap_sasl_interactive_bind_s() to server "
509c8e26105Sjp151216 		    "%s port %d failed. (%s)", adh->host, adh->port,
510c8e26105Sjp151216 		    ldap_err2string(rc));
511c5c4113dSnw141292 	}
512c5c4113dSnw141292 
513c8e26105Sjp151216 	idmapdlog(LOG_DEBUG, "Using global catalog server %s:%d",
514c8e26105Sjp151216 	    adh->host, adh->port);
515c8e26105Sjp151216 
516c8e26105Sjp151216 out:
517c8e26105Sjp151216 	if (adh->ld != NULL) {
518c8e26105Sjp151216 		atomic_inc_32(&adh->ref);
519c5c4113dSnw141292 		adh->idletime = time(NULL);
520c8e26105Sjp151216 		adh->dead = 0;
521c8e26105Sjp151216 		(void) pthread_mutex_unlock(&adh->lock);
522c8e26105Sjp151216 		return (1);
523c8e26105Sjp151216 	}
524c5c4113dSnw141292 
525c8e26105Sjp151216 	(void) pthread_mutex_unlock(&adh->lock);
526c8e26105Sjp151216 	return (0);
527c5c4113dSnw141292 }
528c5c4113dSnw141292 
529c5c4113dSnw141292 
530c5c4113dSnw141292 /*
531c5c4113dSnw141292  * Connection management: find an open connection or open one
532c5c4113dSnw141292  */
533c5c4113dSnw141292 static
534c5c4113dSnw141292 ad_host_t *
535c8e26105Sjp151216 idmap_get_conn(ad_t *ad)
536c5c4113dSnw141292 {
537c5c4113dSnw141292 	ad_host_t	*adh = NULL;
5380dcc7149Snw141292 	int		tries;
5390dcc7149Snw141292 	int		dscount = 0;
5400dcc7149Snw141292 	int		timeoutsecs = IDMAPD_LDAP_OPEN_TIMEOUT;
541c5c4113dSnw141292 
542c8e26105Sjp151216 retry:
543c5c4113dSnw141292 	(void) pthread_mutex_lock(&adhostlock);
544c5c4113dSnw141292 
5450dcc7149Snw141292 	if (host_head == NULL) {
5460dcc7149Snw141292 		(void) pthread_mutex_unlock(&adhostlock);
547c8e26105Sjp151216 		goto out;
5480dcc7149Snw141292 	}
549c8e26105Sjp151216 
5500dcc7149Snw141292 	if (dscount == 0) {
5510dcc7149Snw141292 		/*
5520dcc7149Snw141292 		 * First try: count the number of DSes.
5530dcc7149Snw141292 		 *
5540dcc7149Snw141292 		 * Integer overflow is not an issue -- we can't have so many
5550dcc7149Snw141292 		 * DSes because they won't fit even DNS over TCP, and SMF
5560dcc7149Snw141292 		 * shouldn't let you set so many.
5570dcc7149Snw141292 		 */
5580dcc7149Snw141292 		for (adh = host_head, tries = 0; adh != NULL; adh = adh->next) {
5590dcc7149Snw141292 			if (adh->owner == ad)
5600dcc7149Snw141292 				dscount++;
5610dcc7149Snw141292 		}
5620dcc7149Snw141292 
5630dcc7149Snw141292 		if (dscount == 0) {
5640dcc7149Snw141292 			(void) pthread_mutex_unlock(&adhostlock);
5650dcc7149Snw141292 			goto out;
5660dcc7149Snw141292 		}
5670dcc7149Snw141292 
5680dcc7149Snw141292 		tries = dscount * 3;	/* three tries per-ds */
5690dcc7149Snw141292 
5700dcc7149Snw141292 		/*
5710dcc7149Snw141292 		 * Begin round-robin at the next DS in the list after the last
5720dcc7149Snw141292 		 * one that we had a connection to, else start with the first
5730dcc7149Snw141292 		 * DS in the list.
5740dcc7149Snw141292 		 */
5750dcc7149Snw141292 		adh = ad->last_adh;
576c8e26105Sjp151216 	}
577c8e26105Sjp151216 
578c5c4113dSnw141292 	/*
5790dcc7149Snw141292 	 * Round-robin -- pick the next one on the list; if the list
5800dcc7149Snw141292 	 * changes on us, no big deal, we'll just potentially go
5810dcc7149Snw141292 	 * around the wrong number of times.
582c5c4113dSnw141292 	 */
5830dcc7149Snw141292 	for (;;) {
58471590c90Snw141292 		if (adh != NULL && adh->ld != NULL && !adh->dead)
58571590c90Snw141292 			break;
5860dcc7149Snw141292 		if (adh == NULL || (adh = adh->next) == NULL)
5870dcc7149Snw141292 			adh = host_head;
5880dcc7149Snw141292 		if (adh->owner == ad)
589c5c4113dSnw141292 			break;
590c5c4113dSnw141292 	}
591c8e26105Sjp151216 
592c8e26105Sjp151216 	ad->last_adh = adh;
593c5c4113dSnw141292 	(void) pthread_mutex_unlock(&adhostlock);
594c5c4113dSnw141292 
5950dcc7149Snw141292 
596c8e26105Sjp151216 	/* Found suitable DS, open it if not already opened */
5970dcc7149Snw141292 	if (idmap_open_conn(adh, timeoutsecs))
598c5c4113dSnw141292 		return (adh);
599c8e26105Sjp151216 
6000dcc7149Snw141292 	tries--;
6010dcc7149Snw141292 
6020dcc7149Snw141292 	if ((tries % dscount) == 0)
6030dcc7149Snw141292 		timeoutsecs *= 2;
6040dcc7149Snw141292 
6050dcc7149Snw141292 	if (tries > 0)
606c8e26105Sjp151216 		goto retry;
607c8e26105Sjp151216 
6080dcc7149Snw141292 out:
6090dcc7149Snw141292 	idmapdlog(LOG_NOTICE, "Couldn't open an LDAP connection to any global "
610c8e26105Sjp151216 	    "catalog server!");
611c8e26105Sjp151216 
612c8e26105Sjp151216 	return (NULL);
613c5c4113dSnw141292 }
614c5c4113dSnw141292 
615c5c4113dSnw141292 static
616c5c4113dSnw141292 void
617c5c4113dSnw141292 idmap_release_conn(ad_host_t *adh)
618c5c4113dSnw141292 {
619c5c4113dSnw141292 	(void) pthread_mutex_lock(&adh->lock);
620c5c4113dSnw141292 	if (atomic_dec_32_nv(&adh->ref) == 0)
621c5c4113dSnw141292 		adh->idletime = time(NULL);
622c5c4113dSnw141292 	(void) pthread_mutex_unlock(&adh->lock);
623c5c4113dSnw141292 }
624c5c4113dSnw141292 
625c5c4113dSnw141292 /*
626c5c4113dSnw141292  * Take ad_host_config_t information, create a ad_host_t,
627c5c4113dSnw141292  * populate it and add it to the list of hosts.
628c5c4113dSnw141292  */
629c5c4113dSnw141292 
630c5c4113dSnw141292 int
631c5c4113dSnw141292 idmap_add_ds(ad_t *ad, const char *host, int port)
632c5c4113dSnw141292 {
633c5c4113dSnw141292 	ad_host_t	*p;
634c5c4113dSnw141292 	ad_host_t	*new = NULL;
635c5c4113dSnw141292 	int		ret = -1;
636c5c4113dSnw141292 
637c5c4113dSnw141292 	if (port == 0)
638c5c4113dSnw141292 		port = (int)ad->partition;
639c5c4113dSnw141292 
640c5c4113dSnw141292 	(void) pthread_mutex_lock(&adhostlock);
641c5c4113dSnw141292 	for (p = host_head; p != NULL; p = p->next) {
642c5c4113dSnw141292 		if (p->owner != ad)
643c5c4113dSnw141292 			continue;
644c5c4113dSnw141292 
645c5c4113dSnw141292 		if (strcmp(host, p->host) == 0 && p->port == port) {
646c5c4113dSnw141292 			/* already added */
647c8e26105Sjp151216 			ret = 0;
648c5c4113dSnw141292 			goto err;
649c5c4113dSnw141292 		}
650c5c4113dSnw141292 	}
651c5c4113dSnw141292 
652c5c4113dSnw141292 	/* add new entry */
653c5c4113dSnw141292 	new = (ad_host_t *)calloc(1, sizeof (ad_host_t));
654c5c4113dSnw141292 	if (new == NULL)
655c5c4113dSnw141292 		goto err;
656c5c4113dSnw141292 	new->owner = ad;
657c5c4113dSnw141292 	new->port = port;
658c5c4113dSnw141292 	new->dead = 0;
659bcced03bSjp151216 	new->max_requests = 80;
660bcced03bSjp151216 	new->num_requests = 0;
661c5c4113dSnw141292 	if ((new->host = strdup(host)) == NULL)
662c5c4113dSnw141292 		goto err;
663c5c4113dSnw141292 
664c5c4113dSnw141292 	/* default to SASL GSSAPI only for now */
665c5c4113dSnw141292 	new->saslflags = LDAP_SASL_INTERACTIVE;
666c5c4113dSnw141292 	new->saslmech = "GSSAPI";
667c5c4113dSnw141292 
668c5c4113dSnw141292 	if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) {
669c5c4113dSnw141292 		free(new->host);
670c5c4113dSnw141292 		new->host = NULL;
671c5c4113dSnw141292 		errno = ret;
672c5c4113dSnw141292 		ret = -1;
673c5c4113dSnw141292 		goto err;
674c5c4113dSnw141292 	}
675c5c4113dSnw141292 
676c5c4113dSnw141292 	/* link in */
677c5c4113dSnw141292 	new->next = host_head;
678c5c4113dSnw141292 	host_head = new;
679c5c4113dSnw141292 
680c5c4113dSnw141292 	/* Start reaper if it doesn't exist */
681c5c4113dSnw141292 	if (reaperid == 0)
682c5c4113dSnw141292 		(void) pthread_create(&reaperid, NULL,
683c5c4113dSnw141292 		    (void *(*)(void *))adreaper, (void *)NULL);
684c5c4113dSnw141292 
685c5c4113dSnw141292 err:
686c5c4113dSnw141292 	(void) pthread_mutex_unlock(&adhostlock);
687c5c4113dSnw141292 
688c5c4113dSnw141292 	if (ret != 0 && new != NULL) {
689c5c4113dSnw141292 		if (new->host != NULL) {
690c5c4113dSnw141292 			(void) pthread_mutex_destroy(&new->lock);
691c5c4113dSnw141292 			free(new->host);
692c5c4113dSnw141292 		}
693c5c4113dSnw141292 		free(new);
694c5c4113dSnw141292 	}
695c5c4113dSnw141292 
696c5c4113dSnw141292 	return (ret);
697c5c4113dSnw141292 }
698c5c4113dSnw141292 
699c5c4113dSnw141292 /*
700c8e26105Sjp151216  * Free a DS configuration.
701c8e26105Sjp151216  * Caller must lock the adhostlock mutex
702c5c4113dSnw141292  */
703c8e26105Sjp151216 static void
704c8e26105Sjp151216 delete_ds(ad_t *ad, const char *host, int port)
705c5c4113dSnw141292 {
706c5c4113dSnw141292 	ad_host_t	**p, *q;
707c5c4113dSnw141292 
708c5c4113dSnw141292 	for (p = &host_head; *p != NULL; p = &((*p)->next)) {
709c5c4113dSnw141292 		if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 ||
710c5c4113dSnw141292 		    (*p)->port != port)
711c5c4113dSnw141292 			continue;
712c5c4113dSnw141292 		/* found */
713c8e26105Sjp151216 		if ((*p)->ref > 0)
714c5c4113dSnw141292 			break;	/* still in use */
715c5c4113dSnw141292 
716c5c4113dSnw141292 		q = *p;
717c5c4113dSnw141292 		*p = (*p)->next;
718c5c4113dSnw141292 
719c5c4113dSnw141292 		(void) pthread_mutex_destroy(&q->lock);
720c5c4113dSnw141292 
721c5c4113dSnw141292 		if (q->ld)
722c5c4113dSnw141292 			(void) ldap_unbind(q->ld);
723c5c4113dSnw141292 		if (q->host)
724c5c4113dSnw141292 			free(q->host);
725c5c4113dSnw141292 		free(q);
726c5c4113dSnw141292 		break;
727c5c4113dSnw141292 	}
728c8e26105Sjp151216 
729c5c4113dSnw141292 }
730c5c4113dSnw141292 
731c8e26105Sjp151216 
732c5c4113dSnw141292 /*
733c5c4113dSnw141292  * Convert a binary SID in a BerValue to a sid_t
734c5c4113dSnw141292  */
735c5c4113dSnw141292 static
736c5c4113dSnw141292 int
737c5c4113dSnw141292 idmap_getsid(BerValue *bval, sid_t *sidp)
738c5c4113dSnw141292 {
739c5c4113dSnw141292 	int		i, j;
740c5c4113dSnw141292 	uchar_t		*v;
741c5c4113dSnw141292 	uint32_t	a;
742c5c4113dSnw141292 
743c5c4113dSnw141292 	/*
744c5c4113dSnw141292 	 * The binary format of a SID is as follows:
745c5c4113dSnw141292 	 *
746c5c4113dSnw141292 	 * byte #0: version, always 0x01
747c5c4113dSnw141292 	 * byte #1: RID count, always <= 0x0f
748c5c4113dSnw141292 	 * bytes #2-#7: SID authority, big-endian 48-bit unsigned int
749c5c4113dSnw141292 	 *
750c5c4113dSnw141292 	 * followed by RID count RIDs, each a little-endian, unsigned
751c5c4113dSnw141292 	 * 32-bit int.
752c5c4113dSnw141292 	 */
753c5c4113dSnw141292 	/*
754c5c4113dSnw141292 	 * Sanity checks: must have at least one RID, version must be
755c5c4113dSnw141292 	 * 0x01, and the length must be 8 + rid count * 4
756c5c4113dSnw141292 	 */
757c5c4113dSnw141292 	if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 &&
758c5c4113dSnw141292 	    bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) {
759c5c4113dSnw141292 		v = (uchar_t *)bval->bv_val;
760c5c4113dSnw141292 		sidp->version = v[0];
761c5c4113dSnw141292 		sidp->sub_authority_count = v[1];
762c5c4113dSnw141292 		sidp->authority =
763c5c4113dSnw141292 		    /* big endian -- so start from the left */
764c5c4113dSnw141292 		    ((u_longlong_t)v[2] << 40) |
765c5c4113dSnw141292 		    ((u_longlong_t)v[3] << 32) |
766c5c4113dSnw141292 		    ((u_longlong_t)v[4] << 24) |
767c5c4113dSnw141292 		    ((u_longlong_t)v[5] << 16) |
768c5c4113dSnw141292 		    ((u_longlong_t)v[6] << 8) |
769c5c4113dSnw141292 		    (u_longlong_t)v[7];
770c5c4113dSnw141292 		for (i = 0; i < sidp->sub_authority_count; i++) {
771c5c4113dSnw141292 			j = 8 + (i * 4);
772c5c4113dSnw141292 			/* little endian -- so start from the right */
773c5c4113dSnw141292 			a = (v[j + 3] << 24) | (v[j + 2] << 16) |
774c5c4113dSnw141292 			    (v[j + 1] << 8) | (v[j]);
775c5c4113dSnw141292 			sidp->sub_authorities[i] = a;
776c5c4113dSnw141292 		}
777c5c4113dSnw141292 		return (0);
778c5c4113dSnw141292 	}
779c5c4113dSnw141292 	return (-1);
780c5c4113dSnw141292 }
781c5c4113dSnw141292 
782c5c4113dSnw141292 /*
783c5c4113dSnw141292  * Convert a sid_t to S-1-...
784c5c4113dSnw141292  */
785c5c4113dSnw141292 static
786c5c4113dSnw141292 char *
787c5c4113dSnw141292 idmap_sid2txt(sid_t *sidp)
788c5c4113dSnw141292 {
789c5c4113dSnw141292 	int	rlen, i, len;
790c5c4113dSnw141292 	char	*str, *cp;
791c5c4113dSnw141292 
792c5c4113dSnw141292 	if (sidp->version != 1)
793c5c4113dSnw141292 		return (NULL);
794c5c4113dSnw141292 
795c5c4113dSnw141292 	len = sizeof ("S-1-") - 1;
796c5c4113dSnw141292 
797c5c4113dSnw141292 	/*
798c5c4113dSnw141292 	 * We could optimize like so, but, why?
799c5c4113dSnw141292 	 *	if (sidp->authority < 10)
800c5c4113dSnw141292 	 *		len += 2;
801c5c4113dSnw141292 	 *	else if (sidp->authority < 100)
802c5c4113dSnw141292 	 *		len += 3;
803c5c4113dSnw141292 	 *	else
804c5c4113dSnw141292 	 *		len += snprintf(NULL, 0"%llu", sidp->authority);
805c5c4113dSnw141292 	 */
806c5c4113dSnw141292 	len += snprintf(NULL, 0, "%llu", sidp->authority);
807c5c4113dSnw141292 
808c5c4113dSnw141292 	/* Max length of a uint32_t printed out in ASCII is 10 bytes */
809c5c4113dSnw141292 	len += 1 + (sidp->sub_authority_count + 1) * 10;
810c5c4113dSnw141292 
811c5c4113dSnw141292 	if ((cp = str = malloc(len)) == NULL)
812c5c4113dSnw141292 		return (NULL);
813c5c4113dSnw141292 
814c5c4113dSnw141292 	rlen = snprintf(str, len, "S-1-%llu", sidp->authority);
815c5c4113dSnw141292 
816c5c4113dSnw141292 	cp += rlen;
817c5c4113dSnw141292 	len -= rlen;
818c5c4113dSnw141292 
819c5c4113dSnw141292 	for (i = 0; i < sidp->sub_authority_count; i++) {
820c5c4113dSnw141292 		assert(len > 0);
821c5c4113dSnw141292 		rlen = snprintf(cp, len, "-%u", sidp->sub_authorities[i]);
822c5c4113dSnw141292 		cp += rlen;
823c5c4113dSnw141292 		len -= rlen;
824c5c4113dSnw141292 		assert(len >= 0);
825c5c4113dSnw141292 	}
826c5c4113dSnw141292 
827c5c4113dSnw141292 	return (str);
828c5c4113dSnw141292 }
829c5c4113dSnw141292 
830c5c4113dSnw141292 /*
831c5c4113dSnw141292  * Convert a sid_t to on-the-wire encoding
832c5c4113dSnw141292  */
833c5c4113dSnw141292 static
834c5c4113dSnw141292 int
835c5c4113dSnw141292 idmap_sid2binsid(sid_t *sid, uchar_t *binsid, int binsidlen)
836c5c4113dSnw141292 {
837c5c4113dSnw141292 	uchar_t		*p;
838c5c4113dSnw141292 	int		i;
839c5c4113dSnw141292 	uint64_t	a;
840c5c4113dSnw141292 	uint32_t	r;
841c5c4113dSnw141292 
842c5c4113dSnw141292 	if (sid->version != 1 ||
843c5c4113dSnw141292 	    binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4))
844c5c4113dSnw141292 		return (-1);
845c5c4113dSnw141292 
846c5c4113dSnw141292 	p = binsid;
847c5c4113dSnw141292 	*p++ = 0x01;		/* version */
848c5c4113dSnw141292 	/* sub authority count */
849c5c4113dSnw141292 	*p++ = sid->sub_authority_count;
850c5c4113dSnw141292 	/* Authority */
851c5c4113dSnw141292 	a = sid->authority;
852c5c4113dSnw141292 	/* big-endian -- start from left */
853c5c4113dSnw141292 	*p++ = (a >> 40) & 0xFF;
854c5c4113dSnw141292 	*p++ = (a >> 32) & 0xFF;
855c5c4113dSnw141292 	*p++ = (a >> 24) & 0xFF;
856c5c4113dSnw141292 	*p++ = (a >> 16) & 0xFF;
857c5c4113dSnw141292 	*p++ = (a >> 8) & 0xFF;
858c5c4113dSnw141292 	*p++ = a & 0xFF;
859c5c4113dSnw141292 
860c5c4113dSnw141292 	/* sub-authorities */
861c5c4113dSnw141292 	for (i = 0; i < sid->sub_authority_count; i++) {
862c5c4113dSnw141292 		r = sid->sub_authorities[i];
863c5c4113dSnw141292 		/* little-endian -- start from right */
864c5c4113dSnw141292 		*p++ = (r & 0x000000FF);
865c5c4113dSnw141292 		*p++ = (r & 0x0000FF00) >> 8;
866c5c4113dSnw141292 		*p++ = (r & 0x00FF0000) >> 16;
867c5c4113dSnw141292 		*p++ = (r & 0xFF000000) >> 24;
868c5c4113dSnw141292 	}
869c5c4113dSnw141292 
870c5c4113dSnw141292 	return (0);
871c5c4113dSnw141292 }
872c5c4113dSnw141292 
873c5c4113dSnw141292 /*
874c5c4113dSnw141292  * Convert a stringified SID (S-1-...) into a hex-encoded version of the
875c5c4113dSnw141292  * on-the-wire encoding, but with each pair of hex digits pre-pended
876c5c4113dSnw141292  * with a '\', so we can pass this to libldap.
877c5c4113dSnw141292  */
878c5c4113dSnw141292 static
879c5c4113dSnw141292 int
880c5c4113dSnw141292 idmap_txtsid2hexbinsid(const char *txt, const rid_t *rid,
881c5c4113dSnw141292 	char *hexbinsid, int hexbinsidlen)
882c5c4113dSnw141292 {
883c5c4113dSnw141292 	sid_t		sid = { 0 };
884c5c4113dSnw141292 	int		i, j;
885c5c4113dSnw141292 	const char	*cp;
886c5c4113dSnw141292 	char		*ecp;
887c5c4113dSnw141292 	u_longlong_t	a;
888c5c4113dSnw141292 	unsigned long	r;
889c5c4113dSnw141292 	uchar_t		*binsid, b, hb;
890c5c4113dSnw141292 
891c5c4113dSnw141292 	/* Only version 1 SIDs please */
892c5c4113dSnw141292 	if (strncmp(txt, "S-1-", strlen("S-1-")) != 0)
893c5c4113dSnw141292 		return (-1);
894c5c4113dSnw141292 
895c5c4113dSnw141292 	if (strlen(txt) < (strlen("S-1-") + 1))
896c5c4113dSnw141292 		return (-1);
897c5c4113dSnw141292 
898c5c4113dSnw141292 	/* count '-'s */
899c5c4113dSnw141292 	for (j = 0, cp = strchr(txt, '-');
900c5c4113dSnw141292 	    cp != NULL && *cp != '\0';
901c5c4113dSnw141292 	    j++, cp = strchr(cp + 1, '-')) {
902c5c4113dSnw141292 		/* can't end on a '-' */
903c5c4113dSnw141292 		if (*(cp + 1) == '\0')
904c5c4113dSnw141292 			return (-1);
905c5c4113dSnw141292 	}
906c5c4113dSnw141292 
90762c60062Sbaban 	/* Adjust count for version and authority */
90862c60062Sbaban 	j -= 2;
90962c60062Sbaban 
91062c60062Sbaban 	/* we know the version number and RID count */
91162c60062Sbaban 	sid.version = 1;
91262c60062Sbaban 	sid.sub_authority_count = (rid != NULL) ? j + 1 : j;
91362c60062Sbaban 
914c5c4113dSnw141292 	/* must have at least one RID, but not too many */
91562c60062Sbaban 	if (sid.sub_authority_count < 1 ||
91662c60062Sbaban 	    sid.sub_authority_count > SID_MAX_SUB_AUTHORITIES)
917c5c4113dSnw141292 		return (-1);
918c5c4113dSnw141292 
919c5c4113dSnw141292 	/* check that we only have digits and '-' */
920c5c4113dSnw141292 	if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1))
921c5c4113dSnw141292 		return (-1);
922c5c4113dSnw141292 
923c5c4113dSnw141292 	cp = txt + strlen("S-1-");
924c5c4113dSnw141292 
925c5c4113dSnw141292 	/* 64-bit safe parsing of unsigned 48-bit authority value */
926c5c4113dSnw141292 	errno = 0;
927c5c4113dSnw141292 	a = strtoull(cp, &ecp, 10);
928c5c4113dSnw141292 
929c5c4113dSnw141292 	/* errors parsing the authority or too many bits */
930c5c4113dSnw141292 	if (cp == ecp || (a == 0 && errno == EINVAL) ||
931c5c4113dSnw141292 	    (a == ULLONG_MAX && errno == ERANGE) ||
932c5c4113dSnw141292 	    (a & 0x0000ffffffffffffULL) != a)
933c5c4113dSnw141292 		return (-1);
934c5c4113dSnw141292 
935c5c4113dSnw141292 	cp = ecp;
936c5c4113dSnw141292 
937c5c4113dSnw141292 	sid.authority = (uint64_t)a;
938c5c4113dSnw141292 
93962c60062Sbaban 	for (i = 0; i < j; i++) {
940c5c4113dSnw141292 		if (*cp++ != '-')
941c5c4113dSnw141292 			return (-1);
942c5c4113dSnw141292 		/* 64-bit safe parsing of unsigned 32-bit RID */
943c5c4113dSnw141292 		errno = 0;
944c5c4113dSnw141292 		r = strtoul(cp, &ecp, 10);
945c5c4113dSnw141292 		/* errors parsing the RID or too many bits */
946c5c4113dSnw141292 		if (cp == ecp || (r == 0 && errno == EINVAL) ||
947c5c4113dSnw141292 		    (r == ULONG_MAX && errno == ERANGE) ||
948c5c4113dSnw141292 		    (r & 0xffffffffUL) != r)
949c5c4113dSnw141292 			return (-1);
950c5c4113dSnw141292 		sid.sub_authorities[i] = (uint32_t)r;
951c5c4113dSnw141292 		cp = ecp;
952c5c4113dSnw141292 	}
953c5c4113dSnw141292 
954c5c4113dSnw141292 	/* check that all of the string SID has been consumed */
955c5c4113dSnw141292 	if (*cp != '\0')
956c5c4113dSnw141292 		return (-1);
957c5c4113dSnw141292 
95862c60062Sbaban 	if (rid != NULL)
95962c60062Sbaban 		sid.sub_authorities[j] = *rid;
960c5c4113dSnw141292 
961c5c4113dSnw141292 	j = 1 + 1 + 6 + sid.sub_authority_count * 4;
962c5c4113dSnw141292 
963c5c4113dSnw141292 	if (hexbinsidlen < (j * 3))
964c5c4113dSnw141292 		return (-2);
965c5c4113dSnw141292 
966c5c4113dSnw141292 	/* binary encode the SID */
967c5c4113dSnw141292 	binsid = (uchar_t *)alloca(j);
968c5c4113dSnw141292 	(void) idmap_sid2binsid(&sid, binsid, j);
969c5c4113dSnw141292 
970c5c4113dSnw141292 	/* hex encode, with a backslash before each byte */
971c5c4113dSnw141292 	for (ecp = hexbinsid, i = 0; i < j; i++) {
972c5c4113dSnw141292 		b = binsid[i];
973c5c4113dSnw141292 		*ecp++ = '\\';
974c5c4113dSnw141292 		hb = (b >> 4) & 0xF;
975c5c4113dSnw141292 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
976c5c4113dSnw141292 		hb = b & 0xF;
977c5c4113dSnw141292 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
978c5c4113dSnw141292 	}
979c5c4113dSnw141292 	*ecp = '\0';
980c5c4113dSnw141292 
981c5c4113dSnw141292 	return (0);
982c5c4113dSnw141292 }
983c5c4113dSnw141292 
984c5c4113dSnw141292 static
985c5c4113dSnw141292 char *
986c5c4113dSnw141292 convert_bval2sid(BerValue *bval, rid_t *rid)
987c5c4113dSnw141292 {
988c5c4113dSnw141292 	sid_t	sid;
989c5c4113dSnw141292 
990c5c4113dSnw141292 	if (idmap_getsid(bval, &sid) < 0)
991c5c4113dSnw141292 		return (NULL);
992c5c4113dSnw141292 
993c5c4113dSnw141292 	/*
994c5c4113dSnw141292 	 * If desired and if the SID is what should be a domain/computer
995c5c4113dSnw141292 	 * user or group SID (i.e., S-1-5-w-x-y-z-<user/group RID>) then
996c5c4113dSnw141292 	 * save the last RID and truncate the SID
997c5c4113dSnw141292 	 */
998c5c4113dSnw141292 	if (rid != NULL && sid.authority == 5 && sid.sub_authority_count == 5)
999c5c4113dSnw141292 		*rid = sid.sub_authorities[--sid.sub_authority_count];
1000c5c4113dSnw141292 	return (idmap_sid2txt(&sid));
1001c5c4113dSnw141292 }
1002c5c4113dSnw141292 
1003c5c4113dSnw141292 
1004c5c4113dSnw141292 idmap_retcode
1005c5c4113dSnw141292 idmap_lookup_batch_start(ad_t *ad, int nqueries, idmap_query_state_t **state)
1006c5c4113dSnw141292 {
1007c5c4113dSnw141292 	idmap_query_state_t *new_state;
1008c5c4113dSnw141292 	ad_host_t	*adh = NULL;
1009c5c4113dSnw141292 
1010c5c4113dSnw141292 	*state = NULL;
1011c5c4113dSnw141292 
1012349d5d8fSnw141292 	if (ad == NULL)
1013e8c27ec8Sbaban 		return (IDMAP_ERR_INTERNAL);
1014c5c4113dSnw141292 
1015c5c4113dSnw141292 	adh = idmap_get_conn(ad);
1016c5c4113dSnw141292 	if (adh == NULL)
10170dcc7149Snw141292 		return (IDMAP_ERR_RETRIABLE_NET_ERR);
1018c5c4113dSnw141292 
1019c5c4113dSnw141292 	new_state = calloc(1, sizeof (idmap_query_state_t) +
1020c5c4113dSnw141292 	    (nqueries - 1) * sizeof (idmap_q_t));
1021c5c4113dSnw141292 
1022c5c4113dSnw141292 	if (new_state == NULL)
1023c5c4113dSnw141292 		return (IDMAP_ERR_MEMORY);
1024c5c4113dSnw141292 
102584decf41Sjp151216 	new_state->ref_cnt = 1;
1026c5c4113dSnw141292 	new_state->qadh = adh;
1027c5c4113dSnw141292 	new_state->qcount = nqueries;
1028c5c4113dSnw141292 	new_state->qadh_gen = adh->generation;
1029c5c4113dSnw141292 	/* should be -1, but the atomic routines want unsigned */
1030c5c4113dSnw141292 	new_state->qlastsent = 0;
103184decf41Sjp151216 	(void) pthread_cond_init(&new_state->cv, NULL);
1032c5c4113dSnw141292 
1033c5c4113dSnw141292 	(void) pthread_mutex_lock(&qstatelock);
1034c5c4113dSnw141292 	new_state->next = qstatehead;
1035c5c4113dSnw141292 	qstatehead = new_state;
1036c5c4113dSnw141292 	(void) pthread_mutex_unlock(&qstatelock);
1037c5c4113dSnw141292 
1038c5c4113dSnw141292 	*state = new_state;
1039c5c4113dSnw141292 
1040c5c4113dSnw141292 	return (IDMAP_SUCCESS);
1041c5c4113dSnw141292 }
1042c5c4113dSnw141292 
1043c5c4113dSnw141292 /*
1044e8c27ec8Sbaban  * Set unixuser_attr and unixgroup_attr for AD-based name mapping
1045e8c27ec8Sbaban  */
1046e8c27ec8Sbaban void
1047e8c27ec8Sbaban idmap_lookup_batch_set_unixattr(idmap_query_state_t *state,
10484edd44c5Sjp151216 		const char *unixuser_attr, const char *unixgroup_attr)
10494edd44c5Sjp151216 {
1050e8c27ec8Sbaban 	state->ad_unixuser_attr = unixuser_attr;
1051e8c27ec8Sbaban 	state->ad_unixgroup_attr = unixgroup_attr;
1052e8c27ec8Sbaban }
1053e8c27ec8Sbaban 
1054e8c27ec8Sbaban /*
1055c5c4113dSnw141292  * Find the idmap_query_state_t to which a given LDAP result msgid on a
105684decf41Sjp151216  * given connection belongs. This routine increaments the reference count
105784decf41Sjp151216  * so that the object can not be freed. idmap_lookup_unlock_batch()
105884decf41Sjp151216  * must be called to decreament the reference count.
1059c5c4113dSnw141292  */
1060c5c4113dSnw141292 static
1061c5c4113dSnw141292 int
1062c5c4113dSnw141292 idmap_msgid2query(ad_host_t *adh, int msgid,
1063c5c4113dSnw141292 	idmap_query_state_t **state, int *qid)
1064c5c4113dSnw141292 {
1065c5c4113dSnw141292 	idmap_query_state_t	*p;
1066c5c4113dSnw141292 	int			i;
1067bcced03bSjp151216 	int			ret;
1068c5c4113dSnw141292 
1069c5c4113dSnw141292 	(void) pthread_mutex_lock(&qstatelock);
1070c5c4113dSnw141292 	for (p = qstatehead; p != NULL; p = p->next) {
1071c5c4113dSnw141292 		if (p->qadh != adh || adh->generation != p->qadh_gen)
1072c5c4113dSnw141292 			continue;
1073c5c4113dSnw141292 		for (i = 0; i < p->qcount; i++) {
1074c5c4113dSnw141292 			if ((p->queries[i]).msgid == msgid) {
1075bcced03bSjp151216 				if (!p->qdead) {
107684decf41Sjp151216 					p->ref_cnt++;
1077c5c4113dSnw141292 					*state = p;
1078c5c4113dSnw141292 					*qid = i;
1079bcced03bSjp151216 					ret = 1;
1080bcced03bSjp151216 				} else
1081bcced03bSjp151216 					ret = 0;
1082c5c4113dSnw141292 				(void) pthread_mutex_unlock(&qstatelock);
1083bcced03bSjp151216 				return (ret);
1084c5c4113dSnw141292 			}
1085c5c4113dSnw141292 		}
1086c5c4113dSnw141292 	}
1087c5c4113dSnw141292 	(void) pthread_mutex_unlock(&qstatelock);
1088c5c4113dSnw141292 	return (0);
1089c5c4113dSnw141292 }
1090c5c4113dSnw141292 
1091c5c4113dSnw141292 /*
1092bcced03bSjp151216  * Put the the search result onto the correct idmap_q_t given the LDAP result
1093bcced03bSjp151216  * msgid
1094bcced03bSjp151216  * Returns:	0 success
1095bcced03bSjp151216  *		-1 already has a search result
1096bcced03bSjp151216  *		-2 cant find message id
1097bcced03bSjp151216  */
1098bcced03bSjp151216 static
1099bcced03bSjp151216 int
1100bcced03bSjp151216 idmap_quesearchresbymsgid(ad_host_t *adh, int msgid, LDAPMessage *search_res)
1101bcced03bSjp151216 {
1102bcced03bSjp151216 	idmap_query_state_t	*p;
1103bcced03bSjp151216 	int			i;
1104bcced03bSjp151216 	int			res;
1105bcced03bSjp151216 
1106bcced03bSjp151216 	(void) pthread_mutex_lock(&qstatelock);
1107bcced03bSjp151216 	for (p = qstatehead; p != NULL; p = p->next) {
1108bcced03bSjp151216 		if (p->qadh != adh || adh->generation != p->qadh_gen)
1109bcced03bSjp151216 			continue;
1110bcced03bSjp151216 		for (i = 0; i < p->qcount; i++) {
1111bcced03bSjp151216 			if ((p->queries[i]).msgid == msgid) {
1112bcced03bSjp151216 				if (p->queries[i].search_res == NULL) {
1113bcced03bSjp151216 					if (!p->qdead) {
1114bcced03bSjp151216 						p->queries[i].search_res =
1115bcced03bSjp151216 						    search_res;
1116bcced03bSjp151216 						res = 0;
1117bcced03bSjp151216 					} else
1118bcced03bSjp151216 						res = -2;
1119bcced03bSjp151216 				} else
1120bcced03bSjp151216 					res = -1;
1121bcced03bSjp151216 				(void) pthread_mutex_unlock(&qstatelock);
1122bcced03bSjp151216 				return (res);
1123bcced03bSjp151216 			}
1124bcced03bSjp151216 		}
1125bcced03bSjp151216 	}
1126bcced03bSjp151216 	(void) pthread_mutex_unlock(&qstatelock);
1127bcced03bSjp151216 	return (-2);
1128bcced03bSjp151216 }
1129bcced03bSjp151216 
1130bcced03bSjp151216 
1131bcced03bSjp151216 /*
1132cd37da74Snw141292  * Take parsed attribute values from a search result entry and check if
1133cd37da74Snw141292  * it is the result that was desired and, if so, set the result fields
1134cd37da74Snw141292  * of the given idmap_q_t.
1135cd37da74Snw141292  *
1136e8c27ec8Sbaban  * Frees the unused char * values.
1137c5c4113dSnw141292  */
1138c5c4113dSnw141292 static
1139cd37da74Snw141292 void
114048258c6bSjp151216 idmap_setqresults(idmap_q_t *q, char *san, char *dn, const char *attr,
114148258c6bSjp151216 	char *sid, rid_t rid, int sid_type, char *unixname)
1142c5c4113dSnw141292 {
1143cd37da74Snw141292 	char *domain;
1144e8c27ec8Sbaban 	int err1, err2;
1145cd37da74Snw141292 
1146cd37da74Snw141292 	assert(dn != NULL);
1147cd37da74Snw141292 
1148cd37da74Snw141292 	if ((domain = dn2dns(dn)) == NULL)
1149cd37da74Snw141292 		goto out;
1150cd37da74Snw141292 
1151e8c27ec8Sbaban 	if (q->ecanonname != NULL && san != NULL) {
1152e8c27ec8Sbaban 		/* Check that this is the canonname that we were looking for */
1153cd37da74Snw141292 		if (u8_strcmp(q->ecanonname, san, 0,
1154cd37da74Snw141292 		    U8_STRCMP_CI_LOWER, /* no normalization, for now */
1155e8c27ec8Sbaban 		    U8_UNICODE_LATEST, &err1) != 0 || err1 != 0)
1156e8c27ec8Sbaban 			goto out;
1157e8c27ec8Sbaban 	}
1158e8c27ec8Sbaban 
1159e8c27ec8Sbaban 	if (q->edomain != NULL) {
1160e8c27ec8Sbaban 		/* Check that this is the domain that we were looking for */
1161e8c27ec8Sbaban 		if (u8_strcmp(q->edomain, domain, 0, U8_STRCMP_CI_LOWER,
1162cd37da74Snw141292 		    U8_UNICODE_LATEST, &err2) != 0 || err2 != 0)
1163cd37da74Snw141292 			goto out;
1164e8c27ec8Sbaban 	}
1165cd37da74Snw141292 
116648258c6bSjp151216 	/* Copy the DN and attr and value */
116748258c6bSjp151216 	if (q->dn != NULL)
116848258c6bSjp151216 		*q->dn = strdup(dn);
116948258c6bSjp151216 
117048258c6bSjp151216 	if (q->attr != NULL && attr != NULL)
117148258c6bSjp151216 		*q->attr = strdup(attr);
117248258c6bSjp151216 
117348258c6bSjp151216 	if (q->value != NULL && unixname != NULL)
117448258c6bSjp151216 		*q->value = strdup(unixname);
117548258c6bSjp151216 
1176e8c27ec8Sbaban 	/* Set results */
1177e8c27ec8Sbaban 	if (q->sid) {
1178e8c27ec8Sbaban 		*q->sid = sid;
1179cd37da74Snw141292 		sid = NULL;
1180e8c27ec8Sbaban 	}
1181e8c27ec8Sbaban 	if (q->rid)
1182e8c27ec8Sbaban 		*q->rid = rid;
1183e8c27ec8Sbaban 	if (q->sid_type)
1184cd37da74Snw141292 		*q->sid_type = sid_type;
1185e8c27ec8Sbaban 	if (q->unixname) {
1186e8c27ec8Sbaban 		*q->unixname = unixname;
1187e8c27ec8Sbaban 		unixname = NULL;
1188e8c27ec8Sbaban 	}
1189cd37da74Snw141292 	if (q->domain != NULL) {
1190cd37da74Snw141292 		*q->domain = domain;
1191cd37da74Snw141292 		domain = NULL;
1192e8c27ec8Sbaban 	}
1193e8c27ec8Sbaban 	if (q->canonname != NULL) {
1194*479ac375Sdm199847 		/*
1195*479ac375Sdm199847 		 * The caller may be replacing the given winname by its
1196*479ac375Sdm199847 		 * canonical name and therefore free any old name before
1197*479ac375Sdm199847 		 * overwriting the field by the canonical name.
1198*479ac375Sdm199847 		 */
1199*479ac375Sdm199847 		free(*q->canonname);
1200e8c27ec8Sbaban 		*q->canonname = san;
1201cd37da74Snw141292 		san = NULL;
1202cd37da74Snw141292 	}
1203cd37da74Snw141292 
1204e8c27ec8Sbaban 	/* Always have q->rc; idmap_extract_object() asserts this */
1205cd37da74Snw141292 	*q->rc = IDMAP_SUCCESS;
1206cd37da74Snw141292 
1207cd37da74Snw141292 out:
1208cd37da74Snw141292 	/* Free unused attribute values */
1209cd37da74Snw141292 	free(san);
1210cd37da74Snw141292 	free(sid);
1211cd37da74Snw141292 	free(domain);
1212e8c27ec8Sbaban 	free(unixname);
1213c5c4113dSnw141292 }
1214c5c4113dSnw141292 
1215c5c4113dSnw141292 /*
1216cd37da74Snw141292  * The following three functions extract objectSid, sAMAccountName and
1217cd37da74Snw141292  * objectClass attribute values and, in the case of objectSid and
1218cd37da74Snw141292  * objectClass, parse them.
1219cd37da74Snw141292  *
1220cd37da74Snw141292  * idmap_setqresults() takes care of dealing with the result entry's DN.
1221cd37da74Snw141292  */
1222cd37da74Snw141292 
1223cd37da74Snw141292 /*
1224cd37da74Snw141292  * Return a NUL-terminated stringified SID from the value of an
1225cd37da74Snw141292  * objectSid attribute and put the last RID in *rid.
1226c5c4113dSnw141292  */
1227c5c4113dSnw141292 static
1228cd37da74Snw141292 char *
1229cd37da74Snw141292 idmap_bv_objsid2sidstr(BerValue **bvalues, rid_t *rid)
1230c5c4113dSnw141292 {
1231cd37da74Snw141292 	char *sid;
1232cd37da74Snw141292 
1233cd37da74Snw141292 	if (bvalues == NULL)
1234cd37da74Snw141292 		return (NULL);
1235cd37da74Snw141292 	/* objectSid is single valued */
1236cd37da74Snw141292 	if ((sid = convert_bval2sid(bvalues[0], rid)) == NULL)
1237cd37da74Snw141292 		return (NULL);
1238cd37da74Snw141292 	return (sid);
1239cd37da74Snw141292 }
1240cd37da74Snw141292 
1241cd37da74Snw141292 /*
1242cd37da74Snw141292  * Return a NUL-terminated string from the value of a sAMAccountName
1243e8c27ec8Sbaban  * or unixname attribute.
1244cd37da74Snw141292  */
1245cd37da74Snw141292 static
1246cd37da74Snw141292 char *
1247e8c27ec8Sbaban idmap_bv_name2str(BerValue **bvalues)
1248cd37da74Snw141292 {
1249cd37da74Snw141292 	char *s;
1250c5c4113dSnw141292 
1251c5c4113dSnw141292 	if (bvalues == NULL || bvalues[0] == NULL ||
1252c5c4113dSnw141292 	    bvalues[0]->bv_val == NULL)
1253cd37da74Snw141292 		return (NULL);
1254e3c2d6aaSnw141292 
1255cd37da74Snw141292 	if ((s = malloc(bvalues[0]->bv_len + 1)) == NULL)
1256cd37da74Snw141292 		return (NULL);
1257e3c2d6aaSnw141292 
1258cd37da74Snw141292 	(void) snprintf(s, bvalues[0]->bv_len + 1, "%.*s", bvalues[0]->bv_len,
1259cd37da74Snw141292 	    bvalues[0]->bv_val);
1260e3c2d6aaSnw141292 
1261cd37da74Snw141292 	return (s);
1262c5c4113dSnw141292 }
1263c5c4113dSnw141292 
1264c5c4113dSnw141292 
1265c5c4113dSnw141292 #define	BVAL_CASEEQ(bv, str) \
1266c5c4113dSnw141292 		(((*(bv))->bv_len == (sizeof (str) - 1)) && \
1267c5c4113dSnw141292 		    strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0)
1268c5c4113dSnw141292 
1269c5c4113dSnw141292 /*
1270cd37da74Snw141292  * Extract the class of the result entry.  Returns 1 on success, 0 on
1271cd37da74Snw141292  * failure.
1272c5c4113dSnw141292  */
1273c5c4113dSnw141292 static
1274e3c2d6aaSnw141292 int
1275cd37da74Snw141292 idmap_bv_objclass2sidtype(BerValue **bvalues, int *sid_type)
1276c5c4113dSnw141292 {
1277c5c4113dSnw141292 	BerValue	**cbval;
1278c5c4113dSnw141292 
1279cd37da74Snw141292 	*sid_type = _IDMAP_T_OTHER;
1280c5c4113dSnw141292 	if (bvalues == NULL)
1281e3c2d6aaSnw141292 		return (0);
1282c5c4113dSnw141292 
1283cd37da74Snw141292 	/*
1284cd37da74Snw141292 	 * We iterate over all the values because computer is a
1285cd37da74Snw141292 	 * sub-class of user.
1286cd37da74Snw141292 	 */
1287c5c4113dSnw141292 	for (cbval = bvalues; *cbval != NULL; cbval++) {
1288c5c4113dSnw141292 		if (BVAL_CASEEQ(cbval, "Computer")) {
1289cd37da74Snw141292 			*sid_type = _IDMAP_T_COMPUTER;
1290cd37da74Snw141292 			break;
1291c5c4113dSnw141292 		} else if (BVAL_CASEEQ(cbval, "Group")) {
1292cd37da74Snw141292 			*sid_type = _IDMAP_T_GROUP;
1293cd37da74Snw141292 			break;
1294c5c4113dSnw141292 		} else if (BVAL_CASEEQ(cbval, "USER")) {
1295cd37da74Snw141292 			*sid_type = _IDMAP_T_USER;
1296cd37da74Snw141292 			/* Continue looping -- this may be a computer yet */
1297cd37da74Snw141292 		}
1298cd37da74Snw141292 		/*
1299cd37da74Snw141292 		 * "else if (*sid_type = _IDMAP_T_USER)" then this is a
1300cd37da74Snw141292 		 * new sub-class of user -- what to do with it??
1301cd37da74Snw141292 		 */
1302c5c4113dSnw141292 	}
1303e3c2d6aaSnw141292 
1304e3c2d6aaSnw141292 	return (1);
1305c5c4113dSnw141292 }
1306c5c4113dSnw141292 
1307c5c4113dSnw141292 /*
1308c5c4113dSnw141292  * Handle a given search result entry
1309c5c4113dSnw141292  */
1310c5c4113dSnw141292 static
1311c5c4113dSnw141292 void
1312c5c4113dSnw141292 idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res)
1313c5c4113dSnw141292 {
1314c5c4113dSnw141292 	BerElement		*ber = NULL;
1315c5c4113dSnw141292 	BerValue		**bvalues;
1316c5c4113dSnw141292 	ad_host_t		*adh;
1317c5c4113dSnw141292 	idmap_q_t		*q;
1318cd37da74Snw141292 	char			*attr;
1319e8c27ec8Sbaban 	const char		*unixuser_attr = NULL;
1320e8c27ec8Sbaban 	const char		*unixgroup_attr = NULL;
1321e8c27ec8Sbaban 	char			*unixuser = NULL;
1322e8c27ec8Sbaban 	char			*unixgroup = NULL;
1323cd37da74Snw141292 	char			*dn = NULL;
1324cd37da74Snw141292 	char			*san = NULL;
1325cd37da74Snw141292 	char			*sid = NULL;
1326cd37da74Snw141292 	rid_t			rid = 0;
1327e8c27ec8Sbaban 	int			sid_type = _IDMAP_T_UNDEF;
1328e3c2d6aaSnw141292 	int			has_class, has_san, has_sid;
1329e8c27ec8Sbaban 	int			has_unixuser, has_unixgroup;
1330bcced03bSjp151216 	int			num;
1331c5c4113dSnw141292 
1332c5c4113dSnw141292 	adh = state->qadh;
1333c5c4113dSnw141292 
1334c5c4113dSnw141292 	(void) pthread_mutex_lock(&adh->lock);
1335c5c4113dSnw141292 
1336e3c2d6aaSnw141292 	q = &(state->queries[qid]);
1337e3c2d6aaSnw141292 
1338e8c27ec8Sbaban 	assert(q->rc != NULL);
1339e8c27ec8Sbaban 
1340bcced03bSjp151216 	if (adh->dead || (dn = ldap_get_dn(adh->ld, res)) == NULL) {
1341bcced03bSjp151216 		num = adh->num_requests;
1342c5c4113dSnw141292 		(void) pthread_mutex_unlock(&adh->lock);
1343bcced03bSjp151216 		idmapdlog(LOG_DEBUG,
1344bcced03bSjp151216 		    "AD error decoding search result - %d queued requests",
1345bcced03bSjp151216 		    num);
1346c5c4113dSnw141292 		return;
1347c5c4113dSnw141292 	}
1348c5c4113dSnw141292 
1349e3c2d6aaSnw141292 	assert(q->domain == NULL || *q->domain == NULL);
1350c5c4113dSnw141292 
1351e8c27ec8Sbaban 	/*
1352e8c27ec8Sbaban 	 * If the caller has requested unixname then determine the
1353e8c27ec8Sbaban 	 * AD attribute name that will have the unixname.
1354e8c27ec8Sbaban 	 */
1355e8c27ec8Sbaban 	if (q->unixname != NULL) {
1356e8c27ec8Sbaban 		if (q->eunixtype == _IDMAP_T_USER)
1357e8c27ec8Sbaban 			unixuser_attr = state->ad_unixuser_attr;
1358e8c27ec8Sbaban 		else if (q->eunixtype == _IDMAP_T_GROUP)
1359e8c27ec8Sbaban 			unixgroup_attr = state->ad_unixgroup_attr;
1360e8c27ec8Sbaban 		else if (q->eunixtype == _IDMAP_T_UNDEF) {
1361e8c27ec8Sbaban 			/*
1362e8c27ec8Sbaban 			 * This is the case where we don't know
1363e8c27ec8Sbaban 			 * before hand whether we need unixuser
1364e8c27ec8Sbaban 			 * or unixgroup. This will be determined
1365e8c27ec8Sbaban 			 * by the "sid_type" (i.e whether the given
1366e8c27ec8Sbaban 			 * winname is user or group). If sid_type
1367e8c27ec8Sbaban 			 * turns out to be user we will return
1368e8c27ec8Sbaban 			 * unixuser (if found) and if it is a group
1369e8c27ec8Sbaban 			 * we will return unixgroup (if found). We
1370e8c27ec8Sbaban 			 * lookup for both ad_unixuser_attr and
1371e8c27ec8Sbaban 			 * ad_unixgroup_attr and discard one of them
1372e8c27ec8Sbaban 			 * after we know the "sidtype". This
1373e8c27ec8Sbaban 			 * supports the following type of lookups.
1374e8c27ec8Sbaban 			 *
1375e8c27ec8Sbaban 			 * Example:
1376e8c27ec8Sbaban 			 *   $idmap show -c winname:foo
1377e8c27ec8Sbaban 			 * In the above example, idmap will
1378e8c27ec8Sbaban 			 * return uid if winname is winuser
1379e8c27ec8Sbaban 			 * and gid if winname is wingroup.
1380e8c27ec8Sbaban 			 */
1381e8c27ec8Sbaban 			unixuser_attr = state->ad_unixuser_attr;
1382e8c27ec8Sbaban 			unixgroup_attr = state->ad_unixgroup_attr;
1383e8c27ec8Sbaban 		}
1384e8c27ec8Sbaban 	}
1385e8c27ec8Sbaban 
1386e8c27ec8Sbaban 	has_class = has_san = has_sid = has_unixuser = has_unixgroup = 0;
1387c5c4113dSnw141292 	for (attr = ldap_first_attribute(adh->ld, res, &ber); attr != NULL;
1388c5c4113dSnw141292 	    attr = ldap_next_attribute(adh->ld, res, ber)) {
1389c5c4113dSnw141292 		bvalues = NULL;	/* for memory management below */
1390c5c4113dSnw141292 
1391c5c4113dSnw141292 		/*
1392c5c4113dSnw141292 		 * If this is an attribute we are looking for and
1393c5c4113dSnw141292 		 * haven't seen it yet, parse it
1394c5c4113dSnw141292 		 */
1395e8c27ec8Sbaban 		if (q->sid != NULL && !has_sid &&
1396e3c2d6aaSnw141292 		    strcasecmp(attr, OBJSID) == 0) {
1397c5c4113dSnw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
1398cd37da74Snw141292 			sid = idmap_bv_objsid2sidstr(bvalues, &rid);
1399cd37da74Snw141292 			has_sid = (sid != NULL);
1400e3c2d6aaSnw141292 		} else if (!has_san && strcasecmp(attr, SAN) == 0) {
1401c5c4113dSnw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
1402e8c27ec8Sbaban 			san = idmap_bv_name2str(bvalues);
1403cd37da74Snw141292 			has_san = (san != NULL);
1404e3c2d6aaSnw141292 		} else if (!has_class && strcasecmp(attr, OBJCLASS) == 0) {
1405c5c4113dSnw141292 			bvalues = ldap_get_values_len(adh->ld, res, attr);
1406cd37da74Snw141292 			has_class = idmap_bv_objclass2sidtype(bvalues,
1407cd37da74Snw141292 			    &sid_type);
1408e8c27ec8Sbaban 			if (has_class && q->unixname != NULL &&
1409e8c27ec8Sbaban 			    q->eunixtype == _IDMAP_T_UNDEF) {
1410e8c27ec8Sbaban 				/*
1411e8c27ec8Sbaban 				 * This is the case where we didn't
1412e8c27ec8Sbaban 				 * know whether we wanted unixuser or
1413e8c27ec8Sbaban 				 * unixgroup as described above.
1414e8c27ec8Sbaban 				 * Now since we know the "sid_type"
1415e8c27ec8Sbaban 				 * we discard the unwanted value
1416e8c27ec8Sbaban 				 * if it was retrieved before we
1417e8c27ec8Sbaban 				 * got here.
1418e8c27ec8Sbaban 				 */
1419e8c27ec8Sbaban 				if (sid_type == _IDMAP_T_USER) {
1420e8c27ec8Sbaban 					free(unixgroup);
1421e8c27ec8Sbaban 					unixgroup_attr = unixgroup = NULL;
1422e8c27ec8Sbaban 				} else if (sid_type == _IDMAP_T_GROUP) {
1423e8c27ec8Sbaban 					free(unixuser);
1424e8c27ec8Sbaban 					unixuser_attr = unixuser = NULL;
1425e8c27ec8Sbaban 				} else {
1426e8c27ec8Sbaban 					free(unixuser);
1427e8c27ec8Sbaban 					free(unixgroup);
1428e8c27ec8Sbaban 					unixuser_attr = unixuser = NULL;
1429e8c27ec8Sbaban 					unixgroup_attr = unixgroup = NULL;
1430e8c27ec8Sbaban 				}
1431e8c27ec8Sbaban 			}
1432e8c27ec8Sbaban 		} else if (!has_unixuser && unixuser_attr != NULL &&
1433e8c27ec8Sbaban 		    strcasecmp(attr, unixuser_attr) == 0) {
1434e8c27ec8Sbaban 			bvalues = ldap_get_values_len(adh->ld, res, attr);
1435e8c27ec8Sbaban 			unixuser = idmap_bv_name2str(bvalues);
1436e8c27ec8Sbaban 			has_unixuser = (unixuser != NULL);
143748258c6bSjp151216 
1438e8c27ec8Sbaban 		} else if (!has_unixgroup && unixgroup_attr != NULL &&
1439e8c27ec8Sbaban 		    strcasecmp(attr, unixgroup_attr) == 0) {
1440e8c27ec8Sbaban 			bvalues = ldap_get_values_len(adh->ld, res, attr);
1441e8c27ec8Sbaban 			unixgroup = idmap_bv_name2str(bvalues);
1442e8c27ec8Sbaban 			has_unixgroup = (unixgroup != NULL);
1443c5c4113dSnw141292 		}
1444c5c4113dSnw141292 
1445c5c4113dSnw141292 		if (bvalues != NULL)
1446c5c4113dSnw141292 			ldap_value_free_len(bvalues);
1447c5c4113dSnw141292 		ldap_memfree(attr);
1448c5c4113dSnw141292 
1449cd37da74Snw141292 		if (has_class && has_san &&
1450e8c27ec8Sbaban 		    (q->sid == NULL || has_sid) &&
1451e8c27ec8Sbaban 		    (unixuser_attr == NULL || has_unixuser) &&
1452e8c27ec8Sbaban 		    (unixgroup_attr == NULL || has_unixgroup)) {
1453e8c27ec8Sbaban 			/* Got what we need */
1454e3c2d6aaSnw141292 			break;
1455c5c4113dSnw141292 		}
1456e3c2d6aaSnw141292 	}
1457e3c2d6aaSnw141292 
1458bcced03bSjp151216 	(void) pthread_mutex_unlock(&adh->lock);
1459bcced03bSjp151216 
1460e8c27ec8Sbaban 	if (!has_class) {
1461e8c27ec8Sbaban 		/*
1462e8c27ec8Sbaban 		 * Didn't find objectclass. Something's wrong with our
1463e8c27ec8Sbaban 		 * AD data.
1464e8c27ec8Sbaban 		 */
1465e8c27ec8Sbaban 		free(san);
1466e8c27ec8Sbaban 		free(sid);
1467e8c27ec8Sbaban 		free(unixuser);
1468e8c27ec8Sbaban 		free(unixgroup);
1469e8c27ec8Sbaban 	} else {
1470e8c27ec8Sbaban 		/*
1471e8c27ec8Sbaban 		 * Either we got what we needed and came out of the loop
1472e8c27ec8Sbaban 		 * early OR we completed the loop in which case we didn't
1473e8c27ec8Sbaban 		 * find some attributes that we were looking for. In either
1474e8c27ec8Sbaban 		 * case set the result with what we got.
1475e8c27ec8Sbaban 		 */
147648258c6bSjp151216 		idmap_setqresults(q, san, dn,
147748258c6bSjp151216 		    (unixuser != NULL) ? unixuser_attr : unixgroup_attr,
147848258c6bSjp151216 		    sid, rid, sid_type,
1479e8c27ec8Sbaban 		    (unixuser != NULL) ? unixuser : unixgroup);
1480e8c27ec8Sbaban 	}
1481e8c27ec8Sbaban 
1482c5c4113dSnw141292 	if (ber != NULL)
1483c5c4113dSnw141292 		ber_free(ber, 0);
1484c5c4113dSnw141292 
1485c5c4113dSnw141292 	ldap_memfree(dn);
1486c5c4113dSnw141292 }
1487c5c4113dSnw141292 
1488c5c4113dSnw141292 /*
1489c5c4113dSnw141292  * Try to get a result; if there is one, find the corresponding
1490c5c4113dSnw141292  * idmap_q_t and process the result.
1491bcced03bSjp151216  *
1492bcced03bSjp151216  * Returns:	0 success
1493bcced03bSjp151216  *		-1 error
1494bcced03bSjp151216  *		-2 queue empty
1495c5c4113dSnw141292  */
1496c5c4113dSnw141292 static
1497c5c4113dSnw141292 int
1498c5c4113dSnw141292 idmap_get_adobject_batch(ad_host_t *adh, struct timeval *timeout)
1499c5c4113dSnw141292 {
1500c5c4113dSnw141292 	idmap_query_state_t	*query_state;
1501c5c4113dSnw141292 	LDAPMessage		*res = NULL;
1502c5c4113dSnw141292 	int			rc, ret, msgid, qid;
1503bcced03bSjp151216 	idmap_q_t		*que;
1504bcced03bSjp151216 	int			num;
1505c5c4113dSnw141292 
1506c5c4113dSnw141292 	(void) pthread_mutex_lock(&adh->lock);
1507bcced03bSjp151216 	if (adh->dead || adh->num_requests == 0) {
1508bcced03bSjp151216 		if (adh->dead)
1509bcced03bSjp151216 			ret = -1;
1510bcced03bSjp151216 		else
1511bcced03bSjp151216 			ret = -2;
1512c5c4113dSnw141292 		(void) pthread_mutex_unlock(&adh->lock);
1513bcced03bSjp151216 		return (ret);
1514c5c4113dSnw141292 	}
1515c5c4113dSnw141292 
1516c5c4113dSnw141292 	/* Get one result */
1517c5c4113dSnw141292 	rc = ldap_result(adh->ld, LDAP_RES_ANY, 0,
1518c5c4113dSnw141292 	    timeout, &res);
15190dcc7149Snw141292 	if ((timeout != NULL && timeout->tv_sec > 0 && rc == LDAP_SUCCESS) ||
15200dcc7149Snw141292 	    rc < 0)
1521c5c4113dSnw141292 		adh->dead = 1;
1522bcced03bSjp151216 
1523bcced03bSjp151216 	if (rc == LDAP_RES_SEARCH_RESULT && adh->num_requests > 0)
1524bcced03bSjp151216 		adh->num_requests--;
1525bcced03bSjp151216 
1526bcced03bSjp151216 	if (adh->dead) {
1527bcced03bSjp151216 		num = adh->num_requests;
1528c5c4113dSnw141292 		(void) pthread_mutex_unlock(&adh->lock);
1529bcced03bSjp151216 		idmapdlog(LOG_DEBUG,
1530bcced03bSjp151216 		    "AD ldap_result error - %d queued requests", num);
1531c5c4113dSnw141292 		return (-1);
1532bcced03bSjp151216 	}
1533c5c4113dSnw141292 	switch (rc) {
1534c5c4113dSnw141292 	case LDAP_RES_SEARCH_RESULT:
1535bcced03bSjp151216 		/* We should have the LDAP replies for some search... */
1536c5c4113dSnw141292 		msgid = ldap_msgid(res);
1537bcced03bSjp151216 		if (idmap_msgid2query(adh, msgid, &query_state, &qid)) {
1538bcced03bSjp151216 			(void) pthread_mutex_unlock(&adh->lock);
1539bcced03bSjp151216 			que = &(query_state->queries[qid]);
1540bcced03bSjp151216 			if (que->search_res != NULL) {
1541bcced03bSjp151216 				idmap_extract_object(query_state, qid,
1542bcced03bSjp151216 				    que->search_res);
1543bcced03bSjp151216 				(void) ldap_msgfree(que->search_res);
1544bcced03bSjp151216 				que->search_res = NULL;
1545bcced03bSjp151216 			} else
1546bcced03bSjp151216 				*que->rc = IDMAP_ERR_NOTFOUND;
1547c5c4113dSnw141292 			/* ...so we can decrement qinflight */
1548c5c4113dSnw141292 			atomic_dec_32(&query_state->qinflight);
154984decf41Sjp151216 			idmap_lookup_unlock_batch(&query_state);
1550bcced03bSjp151216 		} else {
1551bcced03bSjp151216 			num = adh->num_requests;
1552bcced03bSjp151216 			(void) pthread_mutex_unlock(&adh->lock);
1553bcced03bSjp151216 			idmapdlog(LOG_DEBUG,
1554bcced03bSjp151216 			    "AD cannot find message ID  - %d queued requests",
1555bcced03bSjp151216 			    num);
1556c5c4113dSnw141292 		}
1557c5c4113dSnw141292 		(void) ldap_msgfree(res);
1558c5c4113dSnw141292 		ret = 0;
1559c5c4113dSnw141292 		break;
1560bcced03bSjp151216 
1561c5c4113dSnw141292 	case LDAP_RES_SEARCH_REFERENCE:
1562c5c4113dSnw141292 		/*
1563c5c4113dSnw141292 		 * We have no need for these at the moment.  Eventually,
1564c5c4113dSnw141292 		 * when we query things that we can't expect to find in
1565c5c4113dSnw141292 		 * the Global Catalog then we'll need to learn to follow
1566c5c4113dSnw141292 		 * references.
1567c5c4113dSnw141292 		 */
1568bcced03bSjp151216 		(void) pthread_mutex_unlock(&adh->lock);
1569c5c4113dSnw141292 		(void) ldap_msgfree(res);
1570c5c4113dSnw141292 		ret = 0;
1571c5c4113dSnw141292 		break;
1572bcced03bSjp151216 
1573c5c4113dSnw141292 	case LDAP_RES_SEARCH_ENTRY:
1574bcced03bSjp151216 		/* Got a result - queue it */
1575c5c4113dSnw141292 		msgid = ldap_msgid(res);
1576bcced03bSjp151216 		rc = idmap_quesearchresbymsgid(adh, msgid, res);
1577bcced03bSjp151216 		num = adh->num_requests;
1578bcced03bSjp151216 		(void) pthread_mutex_unlock(&adh->lock);
1579bcced03bSjp151216 		if (rc == -1) {
1580bcced03bSjp151216 			idmapdlog(LOG_DEBUG,
1581bcced03bSjp151216 			    "AD already has search result - %d queued requests",
1582bcced03bSjp151216 			    num);
1583c5c4113dSnw141292 			(void) ldap_msgfree(res);
1584bcced03bSjp151216 		} else if (rc == -2) {
1585bcced03bSjp151216 			idmapdlog(LOG_DEBUG,
1586bcced03bSjp151216 			    "AD cannot queue by message ID  "
1587bcced03bSjp151216 			    "- %d queued requests", num);
1588bcced03bSjp151216 			(void) ldap_msgfree(res);
1589bcced03bSjp151216 		}
1590c5c4113dSnw141292 		ret = 0;
1591c5c4113dSnw141292 		break;
1592bcced03bSjp151216 
1593c5c4113dSnw141292 	default:
1594c5c4113dSnw141292 		/* timeout or error; treat the same */
1595bcced03bSjp151216 		(void) pthread_mutex_unlock(&adh->lock);
1596c5c4113dSnw141292 		ret = -1;
1597c5c4113dSnw141292 		break;
1598c5c4113dSnw141292 	}
1599c5c4113dSnw141292 
1600c5c4113dSnw141292 	return (ret);
1601c5c4113dSnw141292 }
1602c5c4113dSnw141292 
160384decf41Sjp151216 /*
160484decf41Sjp151216  * This routine decreament the reference count of the
160584decf41Sjp151216  * idmap_query_state_t
160684decf41Sjp151216  */
160784decf41Sjp151216 static void
160884decf41Sjp151216 idmap_lookup_unlock_batch(idmap_query_state_t **state)
160984decf41Sjp151216 {
161084decf41Sjp151216 	/*
161184decf41Sjp151216 	 * Decrement reference count with qstatelock locked
161284decf41Sjp151216 	 */
161384decf41Sjp151216 	(void) pthread_mutex_lock(&qstatelock);
161484decf41Sjp151216 	(*state)->ref_cnt--;
161584decf41Sjp151216 	/*
161684decf41Sjp151216 	 * If there are no references wakup the allocating thread
161784decf41Sjp151216 	 */
1618bcced03bSjp151216 	if ((*state)->ref_cnt <= 1)
161984decf41Sjp151216 		(void) pthread_cond_signal(&(*state)->cv);
162084decf41Sjp151216 	(void) pthread_mutex_unlock(&qstatelock);
162184decf41Sjp151216 	*state = NULL;
162284decf41Sjp151216 }
162384decf41Sjp151216 
1624e3c2d6aaSnw141292 static
1625e3c2d6aaSnw141292 void
1626e3c2d6aaSnw141292 idmap_cleanup_batch(idmap_query_state_t *batch)
1627e3c2d6aaSnw141292 {
1628e3c2d6aaSnw141292 	int i;
1629e3c2d6aaSnw141292 
1630e3c2d6aaSnw141292 	for (i = 0; i < batch->qcount; i++) {
1631cd37da74Snw141292 		if (batch->queries[i].ecanonname != NULL)
1632cd37da74Snw141292 			free(batch->queries[i].ecanonname);
1633cd37da74Snw141292 		batch->queries[i].ecanonname = NULL;
1634cd37da74Snw141292 		if (batch->queries[i].edomain != NULL)
1635cd37da74Snw141292 			free(batch->queries[i].edomain);
1636cd37da74Snw141292 		batch->queries[i].edomain = NULL;
1637e3c2d6aaSnw141292 	}
1638e3c2d6aaSnw141292 }
1639e3c2d6aaSnw141292 
164084decf41Sjp151216 /*
164184decf41Sjp151216  * This routine frees the idmap_query_state_t structure
164284decf41Sjp151216  * If the reference count is greater than 1 it waits
164384decf41Sjp151216  * for the other threads to finish using it.
164484decf41Sjp151216  */
1645c5c4113dSnw141292 void
164684decf41Sjp151216 idmap_lookup_release_batch(idmap_query_state_t **state)
1647c5c4113dSnw141292 {
1648c5c4113dSnw141292 	idmap_query_state_t **p;
1649c5c4113dSnw141292 
165084decf41Sjp151216 	/*
1651bcced03bSjp151216 	 * Set state to dead to stop further operations.
1652bcced03bSjp151216 	 * Wait for reference count with qstatelock locked
1653bcced03bSjp151216 	 * to get to one.
165484decf41Sjp151216 	 */
165584decf41Sjp151216 	(void) pthread_mutex_lock(&qstatelock);
1656bcced03bSjp151216 	(*state)->qdead = 1;
1657bcced03bSjp151216 	while ((*state)->ref_cnt > 1) {
165884decf41Sjp151216 		(void) pthread_cond_wait(&(*state)->cv, &qstatelock);
165984decf41Sjp151216 	}
1660c5c4113dSnw141292 
1661c5c4113dSnw141292 	/* Remove this state struct from the list of state structs */
1662c5c4113dSnw141292 	for (p = &qstatehead; *p != NULL; p = &(*p)->next) {
1663c5c4113dSnw141292 		if (*p == (*state)) {
1664c5c4113dSnw141292 			*p = (*state)->next;
1665c5c4113dSnw141292 			break;
1666c5c4113dSnw141292 		}
1667c5c4113dSnw141292 	}
1668bcced03bSjp151216 	(void) pthread_mutex_unlock(&qstatelock);
1669e3c2d6aaSnw141292 
1670e3c2d6aaSnw141292 	idmap_cleanup_batch(*state);
1671e3c2d6aaSnw141292 
167284decf41Sjp151216 	(void) pthread_cond_destroy(&(*state)->cv);
167384decf41Sjp151216 
167484decf41Sjp151216 	idmap_release_conn((*state)->qadh);
167584decf41Sjp151216 
1676c5c4113dSnw141292 	free(*state);
1677c5c4113dSnw141292 	*state = NULL;
1678c5c4113dSnw141292 }
1679c5c4113dSnw141292 
1680bcced03bSjp151216 
1681bcced03bSjp151216 /*
1682bcced03bSjp151216  * This routine waits for other threads using the
1683bcced03bSjp151216  * idmap_query_state_t structure to finish.
1684bcced03bSjp151216  * If the reference count is greater than 1 it waits
1685bcced03bSjp151216  * for the other threads to finish using it.
1686bcced03bSjp151216  */
1687bcced03bSjp151216 static
1688bcced03bSjp151216 void
1689bcced03bSjp151216 idmap_lookup_wait_batch(idmap_query_state_t *state)
1690bcced03bSjp151216 {
1691bcced03bSjp151216 	/*
1692bcced03bSjp151216 	 * Set state to dead to stop further operation.
1693bcced03bSjp151216 	 * stating.
1694bcced03bSjp151216 	 * Wait for reference count to get to one
1695bcced03bSjp151216 	 * with qstatelock locked.
1696bcced03bSjp151216 	 */
1697bcced03bSjp151216 	(void) pthread_mutex_lock(&qstatelock);
1698bcced03bSjp151216 	state->qdead = 1;
1699bcced03bSjp151216 	while (state->ref_cnt > 1) {
1700bcced03bSjp151216 		(void) pthread_cond_wait(&state->cv, &qstatelock);
1701bcced03bSjp151216 	}
1702bcced03bSjp151216 	(void) pthread_mutex_unlock(&qstatelock);
1703bcced03bSjp151216 }
1704bcced03bSjp151216 
1705bcced03bSjp151216 
1706c5c4113dSnw141292 idmap_retcode
17070dcc7149Snw141292 idmap_lookup_batch_end(idmap_query_state_t **state)
1708c5c4113dSnw141292 {
1709c5c4113dSnw141292 	int		    rc = LDAP_SUCCESS;
1710c5c4113dSnw141292 	idmap_retcode	    retcode = IDMAP_SUCCESS;
17110dcc7149Snw141292 	struct timeval	    timeout;
1712c5c4113dSnw141292 
17130dcc7149Snw141292 	timeout.tv_sec = IDMAPD_SEARCH_TIMEOUT;
17140dcc7149Snw141292 	timeout.tv_usec = 0;
1715c5c4113dSnw141292 
1716c5c4113dSnw141292 	/* Process results until done or until timeout, if given */
1717c5c4113dSnw141292 	while ((*state)->qinflight > 0) {
1718c5c4113dSnw141292 		if ((rc = idmap_get_adobject_batch((*state)->qadh,
17190dcc7149Snw141292 		    &timeout)) != 0)
1720c5c4113dSnw141292 			break;
1721c5c4113dSnw141292 	}
1722bcced03bSjp151216 	(*state)->qdead = 1;
1723bcced03bSjp151216 	/* Wait for other threads proceesing search result to finish */
1724bcced03bSjp151216 	idmap_lookup_wait_batch(*state);
1725c5c4113dSnw141292 
1726bcced03bSjp151216 	if (rc == -1 || (*state)->qinflight != 0)
1727c5c4113dSnw141292 		retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
1728c5c4113dSnw141292 
172984decf41Sjp151216 	idmap_lookup_release_batch(state);
1730c5c4113dSnw141292 
1731c5c4113dSnw141292 	return (retcode);
1732c5c4113dSnw141292 }
1733c5c4113dSnw141292 
1734c5c4113dSnw141292 /*
1735c5c4113dSnw141292  * Send one prepared search, queue up msgid, process what results are
1736c5c4113dSnw141292  * available
1737c5c4113dSnw141292  */
1738c5c4113dSnw141292 static
1739c5c4113dSnw141292 idmap_retcode
174048258c6bSjp151216 idmap_batch_add1(idmap_query_state_t *state, const char *filter,
174148258c6bSjp151216 	char *ecanonname, char *edomain, int eunixtype,
174248258c6bSjp151216 	char **dn, char **attr, char **value,
174348258c6bSjp151216 	char **canonname, char **dname,
174448258c6bSjp151216 	char **sid, rid_t *rid, int *sid_type, char **unixname,
174548258c6bSjp151216 	idmap_retcode *rc)
1746c5c4113dSnw141292 {
1747c5c4113dSnw141292 	idmap_retcode	retcode = IDMAP_SUCCESS;
1748e8c27ec8Sbaban 	int		lrc, qid, i;
1749bcced03bSjp151216 	int		num;
1750bcced03bSjp151216 	int		dead;
1751c5c4113dSnw141292 	struct timeval	tv;
1752c5c4113dSnw141292 	idmap_q_t	*q;
1753cd37da74Snw141292 	static char	*attrs[] = {
1754cd37da74Snw141292 		SAN,
1755cd37da74Snw141292 		OBJSID,
1756cd37da74Snw141292 		OBJCLASS,
1757e8c27ec8Sbaban 		NULL,	/* placeholder for unixname attr */
1758e8c27ec8Sbaban 		NULL,	/* placeholder for unixname attr */
1759cd37da74Snw141292 		NULL
1760cd37da74Snw141292 	};
1761c5c4113dSnw141292 
1762c5c4113dSnw141292 	qid = atomic_inc_32_nv(&state->qlastsent) - 1;
1763c5c4113dSnw141292 
1764c5c4113dSnw141292 	q = &(state->queries[qid]);
1765c5c4113dSnw141292 
1766cd37da74Snw141292 	/*
1767cd37da74Snw141292 	 * Remember the expected canonname so we can check the results
1768cd37da74Snw141292 	 * agains it
1769cd37da74Snw141292 	 */
1770cd37da74Snw141292 	q->ecanonname = ecanonname;
1771cd37da74Snw141292 	q->edomain = edomain;
1772e8c27ec8Sbaban 	q->eunixtype = eunixtype;
1773e3c2d6aaSnw141292 
1774c5c4113dSnw141292 	/* Remember where to put the results */
1775cd37da74Snw141292 	q->canonname = canonname;
1776e8c27ec8Sbaban 	q->sid = sid;
1777c5c4113dSnw141292 	q->domain = dname;
1778c5c4113dSnw141292 	q->rid = rid;
1779c5c4113dSnw141292 	q->sid_type = sid_type;
1780c5c4113dSnw141292 	q->rc = rc;
1781e8c27ec8Sbaban 	q->unixname = unixname;
178248258c6bSjp151216 	q->dn = dn;
178348258c6bSjp151216 	q->attr = attr;
178448258c6bSjp151216 	q->value = value;
1785e8c27ec8Sbaban 
1786e8c27ec8Sbaban 	/* Add unixuser/unixgroup attribute names to the attrs list */
1787e8c27ec8Sbaban 	if (unixname != NULL) {
1788e8c27ec8Sbaban 		i = 3;
1789e8c27ec8Sbaban 		if (eunixtype != _IDMAP_T_GROUP &&
1790e8c27ec8Sbaban 		    state->ad_unixuser_attr != NULL)
1791e8c27ec8Sbaban 			attrs[i++] = (char *)state->ad_unixuser_attr;
1792e8c27ec8Sbaban 		if (eunixtype != _IDMAP_T_USER &&
1793e8c27ec8Sbaban 		    state->ad_unixgroup_attr != NULL)
1794e8c27ec8Sbaban 			attrs[i] = (char *)state->ad_unixgroup_attr;
1795e8c27ec8Sbaban 	}
1796c5c4113dSnw141292 
1797c5c4113dSnw141292 	/*
1798c5c4113dSnw141292 	 * Provide sane defaults for the results in case we never hear
1799c5c4113dSnw141292 	 * back from the DS before closing the connection.
1800e3c2d6aaSnw141292 	 *
1801e3c2d6aaSnw141292 	 * In particular we default the result to indicate a retriable
1802e3c2d6aaSnw141292 	 * error.  The first complete matching result entry will cause
1803e3c2d6aaSnw141292 	 * this to be set to IDMAP_SUCCESS, and the end of the results
1804e3c2d6aaSnw141292 	 * for this search will cause this to indicate "not found" if no
1805e3c2d6aaSnw141292 	 * result entries arrived or no complete ones matched the lookup
1806e3c2d6aaSnw141292 	 * we were doing.
1807c5c4113dSnw141292 	 */
1808c5c4113dSnw141292 	*rc = IDMAP_ERR_RETRIABLE_NET_ERR;
1809e8c27ec8Sbaban 	if (sid_type != NULL)
1810c5c4113dSnw141292 		*sid_type = _IDMAP_T_OTHER;
1811e8c27ec8Sbaban 	if (sid != NULL)
1812e8c27ec8Sbaban 		*sid = NULL;
1813c5c4113dSnw141292 	if (dname != NULL)
1814c5c4113dSnw141292 		*dname = NULL;
1815c5c4113dSnw141292 	if (rid != NULL)
1816c5c4113dSnw141292 		*rid = 0;
181748258c6bSjp151216 	if (dn != NULL)
181848258c6bSjp151216 		*dn = NULL;
181948258c6bSjp151216 	if (attr != NULL)
182048258c6bSjp151216 		*attr = NULL;
182148258c6bSjp151216 	if (value != NULL)
182248258c6bSjp151216 		*value = NULL;
1823c5c4113dSnw141292 
1824bcced03bSjp151216 	/* Check the number of queued requests first */
1825bcced03bSjp151216 	tv.tv_sec = IDMAPD_SEARCH_TIMEOUT;
1826bcced03bSjp151216 	tv.tv_usec = 0;
1827bcced03bSjp151216 	while (!state->qadh->dead &&
1828bcced03bSjp151216 	    state->qadh->num_requests > state->qadh->max_requests) {
1829bcced03bSjp151216 		if (idmap_get_adobject_batch(state->qadh, &tv) != 0)
1830bcced03bSjp151216 			break;
1831bcced03bSjp151216 	}
1832bcced03bSjp151216 
1833*479ac375Sdm199847 	/*
1834*479ac375Sdm199847 	 * Don't set *canonname to NULL because it may be pointing to the
1835*479ac375Sdm199847 	 * given winname. Later on if we get a canonical name from AD the
1836*479ac375Sdm199847 	 * old name if any will be freed before assigning the new name.
1837*479ac375Sdm199847 	 */
1838*479ac375Sdm199847 
1839c5c4113dSnw141292 	/* Send this lookup, don't wait for a result here */
1840bcced03bSjp151216 	lrc = LDAP_SUCCESS;
1841c5c4113dSnw141292 	(void) pthread_mutex_lock(&state->qadh->lock);
1842c5c4113dSnw141292 
1843c5c4113dSnw141292 	if (!state->qadh->dead) {
1844c5c4113dSnw141292 		state->qadh->idletime = time(NULL);
1845e3c2d6aaSnw141292 		lrc = ldap_search_ext(state->qadh->ld, "",
1846cd37da74Snw141292 		    LDAP_SCOPE_SUBTREE, filter, attrs, 0, NULL, NULL,
1847c5c4113dSnw141292 		    NULL, -1, &q->msgid);
1848bcced03bSjp151216 
1849bcced03bSjp151216 		if (lrc == LDAP_SUCCESS) {
1850bcced03bSjp151216 			state->qadh->num_requests++;
1851bcced03bSjp151216 		} else if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE ||
1852c5c4113dSnw141292 		    lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN ||
1853c5c4113dSnw141292 		    lrc == LDAP_UNWILLING_TO_PERFORM) {
1854c5c4113dSnw141292 			retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
1855c5c4113dSnw141292 			state->qadh->dead = 1;
1856bcced03bSjp151216 		} else {
1857c5c4113dSnw141292 			retcode = IDMAP_ERR_OTHER;
1858c5c4113dSnw141292 			state->qadh->dead = 1;
1859c5c4113dSnw141292 		}
1860c5c4113dSnw141292 	}
1861bcced03bSjp151216 	dead = state->qadh->dead;
1862bcced03bSjp151216 	num = state->qadh->num_requests;
1863c5c4113dSnw141292 	(void) pthread_mutex_unlock(&state->qadh->lock);
1864c5c4113dSnw141292 
1865bcced03bSjp151216 	if (dead) {
1866bcced03bSjp151216 		if (lrc != LDAP_SUCCESS)
1867bcced03bSjp151216 			idmapdlog(LOG_DEBUG,
1868bcced03bSjp151216 			    "AD ldap_search_ext error (%s) "
1869bcced03bSjp151216 			    "- %d queued requests",
1870bcced03bSjp151216 			    ldap_err2string(lrc), num);
1871c5c4113dSnw141292 		return (retcode);
1872bcced03bSjp151216 	}
1873c5c4113dSnw141292 
1874c5c4113dSnw141292 	atomic_inc_32(&state->qinflight);
1875c5c4113dSnw141292 
1876c5c4113dSnw141292 	/*
1877c5c4113dSnw141292 	 * Reap as many requests as we can _without_ waiting
1878c5c4113dSnw141292 	 *
1879c5c4113dSnw141292 	 * We do this to prevent any possible TCP socket buffer
1880c5c4113dSnw141292 	 * starvation deadlocks.
1881c5c4113dSnw141292 	 */
1882c5c4113dSnw141292 	(void) memset(&tv, 0, sizeof (tv));
1883c5c4113dSnw141292 	while (idmap_get_adobject_batch(state->qadh, &tv) == 0)
1884c5c4113dSnw141292 		;
1885c5c4113dSnw141292 
1886c5c4113dSnw141292 	return (IDMAP_SUCCESS);
1887c5c4113dSnw141292 }
1888c5c4113dSnw141292 
1889c5c4113dSnw141292 idmap_retcode
1890c5c4113dSnw141292 idmap_name2sid_batch_add1(idmap_query_state_t *state,
1891e8c27ec8Sbaban 	const char *name, const char *dname, int eunixtype,
189248258c6bSjp151216 	char **dn, char **attr, char **value,
189348258c6bSjp151216 	char **canonname, char **sid, rid_t *rid,
189448258c6bSjp151216 	int *sid_type, char **unixname, idmap_retcode *rc)
1895c5c4113dSnw141292 {
1896c5c4113dSnw141292 	idmap_retcode	retcode;
1897e3c2d6aaSnw141292 	int		len, samAcctNameLen;
1898*479ac375Sdm199847 	char		*filter = NULL, *s_name;
1899cd37da74Snw141292 	char		*ecanonname, *edomain; /* expected canonname */
1900c5c4113dSnw141292 
1901c5c4113dSnw141292 	/*
1902e3c2d6aaSnw141292 	 * Strategy: search the global catalog for user/group by
1903e3c2d6aaSnw141292 	 * sAMAccountName = user/groupname with "" as the base DN and by
1904e3c2d6aaSnw141292 	 * userPrincipalName = user/groupname@domain.  The result
1905e3c2d6aaSnw141292 	 * entries will be checked to conform to the name and domain
1906e3c2d6aaSnw141292 	 * name given here.  The DN, sAMAccountName, userPrincipalName,
1907e3c2d6aaSnw141292 	 * objectSid and objectClass of the result entries are all we
1908e3c2d6aaSnw141292 	 * need to figure out which entries match the lookup, the SID of
1909e3c2d6aaSnw141292 	 * the user/group and whether it is a user or a group.
1910c5c4113dSnw141292 	 */
1911c5c4113dSnw141292 
1912c5c4113dSnw141292 	/*
1913e3c2d6aaSnw141292 	 * We need the name and the domain name separately and as
1914e3c2d6aaSnw141292 	 * name@domain.  We also allow the domain to be provided
1915e3c2d6aaSnw141292 	 * separately.
1916c5c4113dSnw141292 	 */
1917d3a612caSnw141292 	samAcctNameLen = strlen(name);
1918e3c2d6aaSnw141292 
1919cd37da74Snw141292 	if ((ecanonname = strdup(name)) == NULL)
1920e3c2d6aaSnw141292 		return (IDMAP_ERR_MEMORY);
1921cd37da74Snw141292 
1922cd37da74Snw141292 	if (dname == NULL || *dname == '\0') {
1923cd37da74Snw141292 		if ((dname = strchr(name, '@')) != NULL) {
1924cd37da74Snw141292 			/* 'name' is qualified with a domain name */
1925cd37da74Snw141292 			if ((edomain = strdup(dname + 1)) == NULL) {
1926cd37da74Snw141292 				free(ecanonname);
1927cd37da74Snw141292 				return (IDMAP_ERR_MEMORY);
1928cd37da74Snw141292 			}
1929cd37da74Snw141292 			*strchr(ecanonname, '@') = '\0';
1930c5c4113dSnw141292 		} else {
1931349d5d8fSnw141292 			/*
1932349d5d8fSnw141292 			 * 'name' not qualified and dname not given
1933349d5d8fSnw141292 			 *
1934349d5d8fSnw141292 			 * Note: ad->dflt_w2k_dom cannot be NULL - see
1935349d5d8fSnw141292 			 * idmap_ad_alloc()
1936349d5d8fSnw141292 			 */
1937349d5d8fSnw141292 			if (*state->qadh->owner->dflt_w2k_dom == '\0') {
1938cd37da74Snw141292 				free(ecanonname);
1939e3c2d6aaSnw141292 				return (IDMAP_ERR_DOMAIN);
1940e3c2d6aaSnw141292 			}
1941cd37da74Snw141292 			edomain = strdup(state->qadh->owner->dflt_w2k_dom);
1942cd37da74Snw141292 			if (edomain == NULL) {
1943cd37da74Snw141292 				free(ecanonname);
1944e3c2d6aaSnw141292 				return (IDMAP_ERR_MEMORY);
1945cd37da74Snw141292 			}
1946cd37da74Snw141292 		}
1947cd37da74Snw141292 	} else {
1948cd37da74Snw141292 		if ((edomain = strdup(dname)) == NULL) {
1949cd37da74Snw141292 			free(ecanonname);
1950cd37da74Snw141292 			return (IDMAP_ERR_MEMORY);
1951cd37da74Snw141292 		}
1952e3c2d6aaSnw141292 	}
1953c5c4113dSnw141292 
1954*479ac375Sdm199847 	s_name = sanitize_for_ldap_filter(name);
1955*479ac375Sdm199847 	if (s_name == NULL) {
1956cd37da74Snw141292 		free(ecanonname);
1957*479ac375Sdm199847 		free(edomain);
1958c5c4113dSnw141292 		return (IDMAP_ERR_MEMORY);
1959c5c4113dSnw141292 	}
1960*479ac375Sdm199847 
1961*479ac375Sdm199847 	/* Assemble filter */
1962*479ac375Sdm199847 	len = snprintf(NULL, 0, SANFILTER, samAcctNameLen, s_name) + 1;
1963*479ac375Sdm199847 	if ((filter = (char *)malloc(len)) == NULL) {
1964*479ac375Sdm199847 		free(ecanonname);
1965*479ac375Sdm199847 		free(edomain);
1966*479ac375Sdm199847 		if (s_name != name)
1967*479ac375Sdm199847 			free(s_name);
1968*479ac375Sdm199847 		return (IDMAP_ERR_MEMORY);
1969*479ac375Sdm199847 	}
1970*479ac375Sdm199847 	(void) snprintf(filter, len, SANFILTER, samAcctNameLen, s_name);
1971*479ac375Sdm199847 	if (s_name != name)
1972*479ac375Sdm199847 		free(s_name);
1973c5c4113dSnw141292 
1974cd37da74Snw141292 	retcode = idmap_batch_add1(state, filter, ecanonname, edomain,
197548258c6bSjp151216 	    eunixtype, dn, attr, value, canonname, NULL, sid, rid, sid_type,
197648258c6bSjp151216 	    unixname, rc);
1977c5c4113dSnw141292 
1978c5c4113dSnw141292 	free(filter);
1979c5c4113dSnw141292 
1980c5c4113dSnw141292 	return (retcode);
1981c5c4113dSnw141292 }
1982c5c4113dSnw141292 
1983c5c4113dSnw141292 idmap_retcode
1984c5c4113dSnw141292 idmap_sid2name_batch_add1(idmap_query_state_t *state,
1985e8c27ec8Sbaban 	const char *sid, const rid_t *rid, int eunixtype,
198648258c6bSjp151216 	char **dn, char **attr, char **value,
198748258c6bSjp151216 	char **name, char **dname, int *sid_type,
198848258c6bSjp151216 	char **unixname, idmap_retcode *rc)
1989c5c4113dSnw141292 {
1990c5c4113dSnw141292 	idmap_retcode	retcode;
1991c5c4113dSnw141292 	int		flen, ret;
1992c5c4113dSnw141292 	char		*filter = NULL;
1993c5c4113dSnw141292 	char		cbinsid[MAXHEXBINSID + 1];
1994c5c4113dSnw141292 
1995c5c4113dSnw141292 	/*
1996c5c4113dSnw141292 	 * Strategy: search [the global catalog] for user/group by
1997c5c4113dSnw141292 	 * objectSid = SID with empty base DN.  The DN, sAMAccountName
1998c5c4113dSnw141292 	 * and objectClass of the result are all we need to figure out
1999c5c4113dSnw141292 	 * the name of the SID and whether it is a user, a group or a
2000c5c4113dSnw141292 	 * computer.
2001c5c4113dSnw141292 	 */
2002c5c4113dSnw141292 
2003c5c4113dSnw141292 	ret = idmap_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid));
2004c5c4113dSnw141292 	if (ret != 0)
2005c5c4113dSnw141292 		return (IDMAP_ERR_SID);
2006c5c4113dSnw141292 
2007c5c4113dSnw141292 	/* Assemble filter */
2008e3c2d6aaSnw141292 	flen = snprintf(NULL, 0, OBJSIDFILTER, cbinsid) + 1;
2009c5c4113dSnw141292 	if ((filter = (char *)malloc(flen)) == NULL)
2010c5c4113dSnw141292 		return (IDMAP_ERR_MEMORY);
2011e3c2d6aaSnw141292 	(void) snprintf(filter, flen, OBJSIDFILTER, cbinsid);
2012c5c4113dSnw141292 
2013e8c27ec8Sbaban 	retcode = idmap_batch_add1(state, filter, NULL, NULL, eunixtype,
201448258c6bSjp151216 	    dn, attr, value, name, dname, NULL, NULL, sid_type, unixname, rc);
2015e8c27ec8Sbaban 
2016e8c27ec8Sbaban 	free(filter);
2017e8c27ec8Sbaban 
2018e8c27ec8Sbaban 	return (retcode);
2019e8c27ec8Sbaban }
2020e8c27ec8Sbaban 
2021e8c27ec8Sbaban idmap_retcode
2022e8c27ec8Sbaban idmap_unixname2sid_batch_add1(idmap_query_state_t *state,
2023e8c27ec8Sbaban 	const char *unixname, int is_user, int is_wuser,
202448258c6bSjp151216 	char **dn, char **attr, char **value,
202548258c6bSjp151216 	char **sid, rid_t *rid, char **name,
202648258c6bSjp151216 	char **dname, int *sid_type, idmap_retcode *rc)
2027e8c27ec8Sbaban {
2028e8c27ec8Sbaban 	idmap_retcode	retcode;
2029e8c27ec8Sbaban 	int		len, ulen;
2030*479ac375Sdm199847 	char		*filter = NULL, *s_unixname;
2031e8c27ec8Sbaban 	const char	*attrname = NULL;
2032e8c27ec8Sbaban 
2033e8c27ec8Sbaban 	/* Get unixuser or unixgroup AD attribute name */
2034e8c27ec8Sbaban 	attrname = (is_user) ?
2035e8c27ec8Sbaban 	    state->ad_unixuser_attr : state->ad_unixgroup_attr;
2036e8c27ec8Sbaban 	if (attrname == NULL)
2037e8c27ec8Sbaban 		return (IDMAP_ERR_NOTFOUND);
2038e8c27ec8Sbaban 
2039*479ac375Sdm199847 	s_unixname = sanitize_for_ldap_filter(unixname);
2040*479ac375Sdm199847 	if (s_unixname == NULL)
2041*479ac375Sdm199847 		return (IDMAP_ERR_MEMORY);
2042*479ac375Sdm199847 
2043e8c27ec8Sbaban 	/*  Assemble filter */
2044e8c27ec8Sbaban 	ulen = strlen(unixname);
2045e8c27ec8Sbaban 	len = snprintf(NULL, 0, "(&(objectclass=%s)(%s=%.*s))",
2046*479ac375Sdm199847 	    is_wuser ? "user" : "group", attrname, ulen, s_unixname) + 1;
2047*479ac375Sdm199847 	if ((filter = (char *)malloc(len)) == NULL) {
2048*479ac375Sdm199847 		if (s_unixname != unixname)
2049*479ac375Sdm199847 			free(s_unixname);
2050e8c27ec8Sbaban 		return (IDMAP_ERR_MEMORY);
2051*479ac375Sdm199847 	}
2052e8c27ec8Sbaban 	(void) snprintf(filter, len, "(&(objectclass=%s)(%s=%.*s))",
2053*479ac375Sdm199847 	    is_wuser ? "user" : "group", attrname, ulen, s_unixname);
2054*479ac375Sdm199847 	if (s_unixname != unixname)
2055*479ac375Sdm199847 		free(s_unixname);
2056e8c27ec8Sbaban 
2057e8c27ec8Sbaban 	retcode = idmap_batch_add1(state, filter, NULL, NULL,
205848258c6bSjp151216 	    _IDMAP_T_UNDEF, dn, NULL, NULL, name, dname, sid, rid, sid_type,
205948258c6bSjp151216 	    NULL, rc);
206048258c6bSjp151216 
206148258c6bSjp151216 	if (retcode == IDMAP_SUCCESS && attr != NULL) {
206248258c6bSjp151216 		if ((*attr = strdup(attrname)) == NULL)
206348258c6bSjp151216 			retcode = IDMAP_ERR_MEMORY;
206448258c6bSjp151216 	}
206548258c6bSjp151216 
206648258c6bSjp151216 	if (retcode == IDMAP_SUCCESS && value != NULL) {
206748258c6bSjp151216 		if (ulen > 0) {
206848258c6bSjp151216 			if ((*value = strdup(unixname)) == NULL)
206948258c6bSjp151216 				retcode = IDMAP_ERR_MEMORY;
207048258c6bSjp151216 		}
207148258c6bSjp151216 		else
207248258c6bSjp151216 			*value = NULL;
207348258c6bSjp151216 	}
2074c5c4113dSnw141292 
2075c5c4113dSnw141292 	free(filter);
2076c5c4113dSnw141292 
2077c5c4113dSnw141292 	return (retcode);
2078c5c4113dSnw141292 }
2079