xref: /illumos-gate/usr/src/lib/libadutils/common/adutils.c (revision 6e375c8351497b82ffa4f33cbf61d712999b4605)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <alloca.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <lber.h>
30 #include <sasl/sasl.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <synch.h>
34 #include <atomic.h>
35 #include <errno.h>
36 #include <assert.h>
37 #include <limits.h>
38 #include <sys/u8_textprep.h>
39 #include <sys/varargs.h>
40 #include "libadutils.h"
41 #include "adutils_impl.h"
42 
43 /* List of DSs, needed by the idle connection reaper thread */
44 static pthread_mutex_t	adhostlock = PTHREAD_MUTEX_INITIALIZER;
45 static adutils_host_t	*host_head = NULL;
46 
47 /*
48  * List of query state structs -- needed so we can "route" LDAP results
49  * to the right context if multiple threads should be using the same
50  * connection concurrently
51  */
52 static pthread_mutex_t		qstatelock = PTHREAD_MUTEX_INITIALIZER;
53 static adutils_query_state_t	*qstatehead = NULL;
54 
55 static char *adutils_sid_ber2str(BerValue *bvalues);
56 static void adutils_lookup_batch_unlock(adutils_query_state_t **state);
57 static void delete_ds(adutils_ad_t *ad, const char *host, int port);
58 
59 typedef struct binary_attrs {
60 	const char	*name;
61 	char		*(*ber2str)(BerValue *bvalues);
62 } binary_attrs_t;
63 
64 static binary_attrs_t binattrs[] = {
65 	{"objectSID", adutils_sid_ber2str},
66 	{NULL, NULL}
67 };
68 
69 
70 void
71 adutils_set_log(int pri, bool_t syslog, bool_t degraded)
72 {
73 	idmap_log_stderr(pri);
74 	idmap_log_syslog(syslog);
75 	idmap_log_degraded(degraded);
76 }
77 
78 
79 /*
80  * Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com"
81  */
82 static
83 char *
84 adutils_dns2dn(const char *dns)
85 {
86 	int	nameparts;
87 	return (ldap_dns_to_dn((char *)dns, &nameparts));
88 }
89 
90 
91 /*
92  * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other
93  * attributes (CN, etc...).
94  */
95 char *
96 adutils_dn2dns(const char *dn)
97 {
98 	char **rdns = NULL;
99 	char **attrs = NULL;
100 	char **labels = NULL;
101 	char *dns = NULL;
102 	char **rdn, **attr, **label;
103 	int maxlabels = 5;
104 	int nlabels = 0;
105 	int dnslen;
106 
107 	/*
108 	 * There is no reverse of ldap_dns_to_dn() in our libldap, so we
109 	 * have to do the hard work here for now.
110 	 */
111 
112 	/*
113 	 * This code is much too liberal: it looks for "dc" attributes
114 	 * in all RDNs of the DN.  In theory this could cause problems
115 	 * if people were to use "dc" in nodes other than the root of
116 	 * the tree, but in practice noone, least of all Active
117 	 * Directory, does that.
118 	 *
119 	 * On the other hand, this code is much too conservative: it
120 	 * does not make assumptions about ldap_explode_dn(), and _that_
121 	 * is the true for looking at every attr of every RDN.
122 	 *
123 	 * Since we only ever look at dc and those must be DNS labels,
124 	 * at least until we get around to supporting IDN here we
125 	 * shouldn't see escaped labels from AD nor from libldap, though
126 	 * the spec (RFC2253) does allow libldap to escape things that
127 	 * don't need escaping -- if that should ever happen then
128 	 * libldap will need a spanking, and we can take care of that.
129 	 */
130 
131 	/* Explode a DN into RDNs */
132 	if ((rdns = ldap_explode_dn(dn, 0)) == NULL)
133 		return (NULL);
134 
135 	labels = calloc(maxlabels + 1, sizeof (char *));
136 	label = labels;
137 
138 	for (rdn = rdns; *rdn != NULL; rdn++) {
139 		if (attrs != NULL)
140 			ldap_value_free(attrs);
141 
142 		/* Explode each RDN, look for DC attr, save val as DNS label */
143 		if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL)
144 			goto done;
145 
146 		for (attr = attrs; *attr != NULL; attr++) {
147 			if (strncasecmp(*attr, "dc=", 3) != 0)
148 				continue;
149 
150 			/* Found a DNS label */
151 			labels[nlabels++] = strdup((*attr) + 3);
152 
153 			if (nlabels == maxlabels) {
154 				char **tmp;
155 				tmp = realloc(labels,
156 				    sizeof (char *) * (maxlabels + 1));
157 
158 				if (tmp == NULL)
159 					goto done;
160 
161 				labels = tmp;
162 				labels[nlabels] = NULL;
163 			}
164 
165 			/* There should be just one DC= attr per-RDN */
166 			break;
167 		}
168 	}
169 
170 	/*
171 	 * Got all the labels, now join with '.'
172 	 *
173 	 * We need room for nlabels - 1 periods ('.'), one nul
174 	 * terminator, and the strlen() of each label.
175 	 */
176 	dnslen = nlabels;
177 	for (label = labels; *label != NULL; label++)
178 		dnslen += strlen(*label);
179 
180 	if ((dns = malloc(dnslen)) == NULL)
181 		goto done;
182 
183 	*dns = '\0';
184 
185 	for (label = labels; *label != NULL; label++) {
186 		(void) strlcat(dns, *label, dnslen);
187 		/*
188 		 * NOTE: the last '.' won't be appended -- there's no room
189 		 * for it!
190 		 */
191 		(void) strlcat(dns, ".", dnslen);
192 	}
193 
194 done:
195 	if (labels != NULL) {
196 		for (label = labels; *label != NULL; label++)
197 			free(*label);
198 		free(labels);
199 	}
200 	if (attrs != NULL)
201 		ldap_value_free(attrs);
202 	if (rdns != NULL)
203 		ldap_value_free(rdns);
204 
205 	return (dns);
206 }
207 
208 /*
209  * Convert a binary SID in a BerValue to a adutils_sid_t
210  */
211 static
212 int
213 getsid(BerValue *bval, adutils_sid_t *sidp)
214 {
215 	int		i, j;
216 	uchar_t		*v;
217 	uint32_t	a;
218 
219 	/*
220 	 * The binary format of a SID is as follows:
221 	 *
222 	 * byte #0: version, always 0x01
223 	 * byte #1: RID count, always <= 0x0f
224 	 * bytes #2-#7: SID authority, big-endian 48-bit unsigned int
225 	 *
226 	 * followed by RID count RIDs, each a little-endian, unsigned
227 	 * 32-bit int.
228 	 */
229 	/*
230 	 * Sanity checks: must have at least one RID, version must be
231 	 * 0x01, and the length must be 8 + rid count * 4
232 	 */
233 	if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 &&
234 	    bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) {
235 		v = (uchar_t *)bval->bv_val;
236 		sidp->version = v[0];
237 		sidp->sub_authority_count = v[1];
238 		sidp->authority =
239 		    /* big endian -- so start from the left */
240 		    ((u_longlong_t)v[2] << 40) |
241 		    ((u_longlong_t)v[3] << 32) |
242 		    ((u_longlong_t)v[4] << 24) |
243 		    ((u_longlong_t)v[5] << 16) |
244 		    ((u_longlong_t)v[6] << 8) |
245 		    (u_longlong_t)v[7];
246 		for (i = 0; i < sidp->sub_authority_count; i++) {
247 			j = 8 + (i * 4);
248 			/* little endian -- so start from the right */
249 			a = (v[j + 3] << 24) | (v[j + 2] << 16) |
250 			    (v[j + 1] << 8) | (v[j]);
251 			sidp->sub_authorities[i] = a;
252 		}
253 		return (0);
254 	}
255 	return (-1);
256 }
257 
258 /*
259  * Convert a adutils_sid_t to S-1-...
260  */
261 static
262 char *
263 sid2txt(adutils_sid_t *sidp)
264 {
265 	int	rlen, i, len;
266 	char	*str, *cp;
267 
268 	if (sidp->version != 1)
269 		return (NULL);
270 
271 	len = sizeof ("S-1-") - 1;
272 
273 	/*
274 	 * We could optimize like so, but, why?
275 	 *	if (sidp->authority < 10)
276 	 *		len += 2;
277 	 *	else if (sidp->authority < 100)
278 	 *		len += 3;
279 	 *	else
280 	 *		len += snprintf(NULL, 0"%llu", sidp->authority);
281 	 */
282 	len += snprintf(NULL, 0, "%llu", sidp->authority);
283 
284 	/* Max length of a uint32_t printed out in ASCII is 10 bytes */
285 	len += 1 + (sidp->sub_authority_count + 1) * 10;
286 
287 	if ((cp = str = malloc(len)) == NULL)
288 		return (NULL);
289 
290 	rlen = snprintf(str, len, "S-1-%llu", sidp->authority);
291 
292 	cp += rlen;
293 	len -= rlen;
294 
295 	for (i = 0; i < sidp->sub_authority_count; i++) {
296 		assert(len > 0);
297 		rlen = snprintf(cp, len, "-%u", sidp->sub_authorities[i]);
298 		cp += rlen;
299 		len -= rlen;
300 		assert(len >= 0);
301 	}
302 
303 	return (str);
304 }
305 
306 /*
307  * Convert a adutils_sid_t to on-the-wire encoding
308  */
309 static
310 int
311 sid2binsid(adutils_sid_t *sid, uchar_t *binsid, int binsidlen)
312 {
313 	uchar_t		*p;
314 	int		i;
315 	uint64_t	a;
316 	uint32_t	r;
317 
318 	if (sid->version != 1 ||
319 	    binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4))
320 		return (-1);
321 
322 	p = binsid;
323 	*p++ = 0x01;		/* version */
324 	/* sub authority count */
325 	*p++ = sid->sub_authority_count;
326 	/* Authority */
327 	a = sid->authority;
328 	/* big-endian -- start from left */
329 	*p++ = (a >> 40) & 0xFF;
330 	*p++ = (a >> 32) & 0xFF;
331 	*p++ = (a >> 24) & 0xFF;
332 	*p++ = (a >> 16) & 0xFF;
333 	*p++ = (a >> 8) & 0xFF;
334 	*p++ = a & 0xFF;
335 
336 	/* sub-authorities */
337 	for (i = 0; i < sid->sub_authority_count; i++) {
338 		r = sid->sub_authorities[i];
339 		/* little-endian -- start from right */
340 		*p++ = (r & 0x000000FF);
341 		*p++ = (r & 0x0000FF00) >> 8;
342 		*p++ = (r & 0x00FF0000) >> 16;
343 		*p++ = (r & 0xFF000000) >> 24;
344 	}
345 
346 	return (0);
347 }
348 
349 /*
350  * Convert a stringified SID (S-1-...) into a hex-encoded version of the
351  * on-the-wire encoding, but with each pair of hex digits pre-pended
352  * with a '\', so we can pass this to libldap.
353  */
354 int
355 adutils_txtsid2hexbinsid(const char *txt, const uint32_t *rid,
356 	char *hexbinsid, int hexbinsidlen)
357 {
358 	adutils_sid_t	sid = { 0 };
359 	int		i, j;
360 	const char	*cp;
361 	char		*ecp;
362 	u_longlong_t	a;
363 	unsigned long	r;
364 	uchar_t		*binsid, b, hb;
365 
366 	/* Only version 1 SIDs please */
367 	if (strncmp(txt, "S-1-", strlen("S-1-")) != 0)
368 		return (-1);
369 
370 	if (strlen(txt) < (strlen("S-1-") + 1))
371 		return (-1);
372 
373 	/* count '-'s */
374 	for (j = 0, cp = strchr(txt, '-');
375 	    cp != NULL && *cp != '\0';
376 	    j++, cp = strchr(cp + 1, '-')) {
377 		/* can't end on a '-' */
378 		if (*(cp + 1) == '\0')
379 			return (-1);
380 	}
381 
382 	/* Adjust count for version and authority */
383 	j -= 2;
384 
385 	/* we know the version number and RID count */
386 	sid.version = 1;
387 	sid.sub_authority_count = (rid != NULL) ? j + 1 : j;
388 
389 	/* must have at least one RID, but not too many */
390 	if (sid.sub_authority_count < 1 ||
391 	    sid.sub_authority_count > ADUTILS_SID_MAX_SUB_AUTHORITIES)
392 		return (-1);
393 
394 	/* check that we only have digits and '-' */
395 	if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1))
396 		return (-1);
397 
398 	cp = txt + strlen("S-1-");
399 
400 	/* 64-bit safe parsing of unsigned 48-bit authority value */
401 	errno = 0;
402 	a = strtoull(cp, &ecp, 10);
403 
404 	/* errors parsing the authority or too many bits */
405 	if (cp == ecp || (a == 0 && errno == EINVAL) ||
406 	    (a == ULLONG_MAX && errno == ERANGE) ||
407 	    (a & 0x0000ffffffffffffULL) != a)
408 		return (-1);
409 
410 	cp = ecp;
411 
412 	sid.authority = (uint64_t)a;
413 
414 	for (i = 0; i < j; i++) {
415 		if (*cp++ != '-')
416 			return (-1);
417 		/* 64-bit safe parsing of unsigned 32-bit RID */
418 		errno = 0;
419 		r = strtoul(cp, &ecp, 10);
420 		/* errors parsing the RID or too many bits */
421 		if (cp == ecp || (r == 0 && errno == EINVAL) ||
422 		    (r == ULONG_MAX && errno == ERANGE) ||
423 		    (r & 0xffffffffUL) != r)
424 			return (-1);
425 		sid.sub_authorities[i] = (uint32_t)r;
426 		cp = ecp;
427 	}
428 
429 	/* check that all of the string SID has been consumed */
430 	if (*cp != '\0')
431 		return (-1);
432 
433 	if (rid != NULL)
434 		sid.sub_authorities[j] = *rid;
435 
436 	j = 1 + 1 + 6 + sid.sub_authority_count * 4;
437 
438 	if (hexbinsidlen < (j * 3))
439 		return (-2);
440 
441 	/* binary encode the SID */
442 	binsid = (uchar_t *)alloca(j);
443 	(void) sid2binsid(&sid, binsid, j);
444 
445 	/* hex encode, with a backslash before each byte */
446 	for (ecp = hexbinsid, i = 0; i < j; i++) {
447 		b = binsid[i];
448 		*ecp++ = '\\';
449 		hb = (b >> 4) & 0xF;
450 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
451 		hb = b & 0xF;
452 		*ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A');
453 	}
454 	*ecp = '\0';
455 
456 	return (0);
457 }
458 
459 static
460 char *
461 convert_bval2sid(BerValue *bval, uint32_t *rid)
462 {
463 	adutils_sid_t	sid;
464 
465 	if (getsid(bval, &sid) < 0)
466 		return (NULL);
467 
468 	/*
469 	 * If desired and if the SID is what should be a domain/computer
470 	 * user or group SID (i.e., S-1-5-w-x-y-z-<user/group RID>) then
471 	 * save the last RID and truncate the SID
472 	 */
473 	if (rid != NULL && sid.authority == 5 && sid.sub_authority_count == 5)
474 		*rid = sid.sub_authorities[--sid.sub_authority_count];
475 	return (sid2txt(&sid));
476 }
477 
478 
479 /*
480  * Return a NUL-terminated stringified SID from the value of an
481  * objectSid attribute and put the last RID in *rid.
482  */
483 char *
484 adutils_bv_objsid2sidstr(BerValue *bval, uint32_t *rid)
485 {
486 	char *sid;
487 
488 	if (bval == NULL)
489 		return (NULL);
490 	/* objectSid is single valued */
491 	if ((sid = convert_bval2sid(bval, rid)) == NULL)
492 		return (NULL);
493 	return (sid);
494 }
495 
496 static
497 char *
498 adutils_sid_ber2str(BerValue *bval)
499 {
500 	return (adutils_bv_objsid2sidstr(bval, NULL));
501 }
502 
503 
504 /* Return a NUL-terminated string from the Ber value */
505 char *
506 adutils_bv_name2str(BerValue *bval)
507 {
508 	char *s;
509 
510 	if (bval == NULL || bval->bv_val == NULL)
511 		return (NULL);
512 	if ((s = malloc(bval->bv_len + 1)) == NULL)
513 		return (NULL);
514 	(void) snprintf(s, bval->bv_len + 1, "%.*s", bval->bv_len,
515 	    bval->bv_val);
516 	return (s);
517 }
518 
519 /*ARGSUSED*/
520 static
521 int
522 saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts)
523 {
524 	sasl_interact_t	*interact;
525 
526 	if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE)
527 		return (LDAP_PARAM_ERROR);
528 
529 	/* There should be no extra arguemnts for SASL/GSSAPI authentication */
530 	for (interact = prompts; interact->id != SASL_CB_LIST_END;
531 	    interact++) {
532 		interact->result = NULL;
533 		interact->len = 0;
534 	}
535 	return (LDAP_SUCCESS);
536 }
537 
538 
539 #define	ADCONN_TIME	300
540 
541 /*
542  * Idle connection reaping side of connection management
543  */
544 void
545 adutils_reap_idle_connections()
546 {
547 	adutils_host_t	*adh;
548 	time_t		now;
549 
550 	(void) pthread_mutex_lock(&adhostlock);
551 	now = time(NULL);
552 	for (adh = host_head; adh != NULL; adh = adh->next) {
553 		(void) pthread_mutex_lock(&adh->lock);
554 		if (adh->ref == 0 && adh->idletime != 0 &&
555 		    adh->idletime + ADCONN_TIME < now) {
556 			if (adh->ld) {
557 				(void) ldap_unbind(adh->ld);
558 				adh->ld = NULL;
559 				adh->idletime = 0;
560 				adh->ref = 0;
561 			}
562 		}
563 		(void) pthread_mutex_unlock(&adh->lock);
564 	}
565 	(void) pthread_mutex_unlock(&adhostlock);
566 }
567 
568 
569 adutils_rc
570 adutils_ad_alloc(adutils_ad_t **new_ad, const char *default_domain,
571 	adutils_ad_partition_t part)
572 {
573 	adutils_ad_t *ad;
574 
575 	*new_ad = NULL;
576 
577 	if ((default_domain == NULL || *default_domain == '\0') &&
578 	    part != ADUTILS_AD_GLOBAL_CATALOG)
579 		return (ADUTILS_ERR_DOMAIN);
580 	if ((ad = calloc(1, sizeof (*ad))) == NULL)
581 		return (ADUTILS_ERR_MEMORY);
582 	ad->ref = 1;
583 	ad->partition = part;
584 	if (default_domain == NULL)
585 		default_domain = "";
586 	if ((ad->dflt_w2k_dom = strdup(default_domain)) == NULL)
587 		goto err;
588 	if (pthread_mutex_init(&ad->lock, NULL) != 0)
589 		goto err;
590 	*new_ad = ad;
591 	return (ADUTILS_SUCCESS);
592 
593 err:
594 	if (ad->dflt_w2k_dom != NULL)
595 		free(ad->dflt_w2k_dom);
596 	free(ad);
597 	return (ADUTILS_ERR_MEMORY);
598 }
599 
600 void
601 adutils_ad_free(adutils_ad_t **ad)
602 {
603 	adutils_host_t *p;
604 	adutils_host_t *prev;
605 
606 	if (ad == NULL || *ad == NULL)
607 		return;
608 
609 	(void) pthread_mutex_lock(&(*ad)->lock);
610 
611 	if (atomic_dec_32_nv(&(*ad)->ref) > 0) {
612 		(void) pthread_mutex_unlock(&(*ad)->lock);
613 		*ad = NULL;
614 		return;
615 	}
616 
617 	(void) pthread_mutex_lock(&adhostlock);
618 	prev = NULL;
619 	p = host_head;
620 	while (p != NULL) {
621 		if (p->owner != (*ad)) {
622 			prev = p;
623 			p = p->next;
624 			continue;
625 		} else {
626 			delete_ds((*ad), p->host, p->port);
627 			if (prev == NULL)
628 				p = host_head;
629 			else
630 				p = prev->next;
631 		}
632 	}
633 	(void) pthread_mutex_unlock(&adhostlock);
634 
635 	(void) pthread_mutex_unlock(&(*ad)->lock);
636 	(void) pthread_mutex_destroy(&(*ad)->lock);
637 
638 	if ((*ad)->known_domains)
639 		free((*ad)->known_domains);
640 	free((*ad)->dflt_w2k_dom);
641 	free(*ad);
642 
643 	*ad = NULL;
644 }
645 
646 static
647 int
648 open_conn(adutils_host_t *adh, int timeoutsecs)
649 {
650 	int zero = 0;
651 	int ldversion, rc;
652 	int timeoutms = timeoutsecs * 1000;
653 
654 	if (adh == NULL)
655 		return (0);
656 
657 	(void) pthread_mutex_lock(&adh->lock);
658 
659 	if (!adh->dead && adh->ld != NULL)
660 		/* done! */
661 		goto out;
662 
663 	if (adh->ld != NULL) {
664 		(void) ldap_unbind(adh->ld);
665 		adh->ld = NULL;
666 	}
667 	adh->num_requests = 0;
668 
669 	atomic_inc_64(&adh->generation);
670 
671 	/* Open and bind an LDAP connection */
672 	adh->ld = ldap_init(adh->host, adh->port);
673 	if (adh->ld == NULL) {
674 		idmapdlog(LOG_INFO, "ldap_init() to server "
675 		    "%s port %d failed. (%s)", adh->host,
676 		    adh->port, strerror(errno));
677 		goto out;
678 	}
679 	ldversion = LDAP_VERSION3;
680 	(void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion);
681 	(void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
682 	(void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero);
683 	(void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero);
684 	(void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
685 	(void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
686 	rc = ldap_sasl_interactive_bind_s(adh->ld, "" /* binddn */,
687 	    adh->saslmech, NULL, NULL, adh->saslflags, &saslcallback,
688 	    NULL);
689 
690 	if (rc != LDAP_SUCCESS) {
691 		(void) ldap_unbind(adh->ld);
692 		adh->ld = NULL;
693 		idmapdlog(LOG_INFO, "ldap_sasl_interactive_bind_s() to server "
694 		    "%s port %d failed. (%s)", adh->host, adh->port,
695 		    ldap_err2string(rc));
696 	}
697 
698 	idmapdlog(LOG_DEBUG, "Using global catalog server %s:%d",
699 	    adh->host, adh->port);
700 
701 out:
702 	if (adh->ld != NULL) {
703 		atomic_inc_32(&adh->ref);
704 		adh->idletime = time(NULL);
705 		adh->dead = 0;
706 		(void) pthread_mutex_unlock(&adh->lock);
707 		return (1);
708 	}
709 
710 	(void) pthread_mutex_unlock(&adh->lock);
711 	return (0);
712 }
713 
714 
715 /*
716  * Connection management: find an open connection or open one
717  */
718 static
719 adutils_host_t *
720 get_conn(adutils_ad_t *ad)
721 {
722 	adutils_host_t	*adh = NULL;
723 	int		tries;
724 	int		dscount = 0;
725 	int		timeoutsecs = ADUTILS_LDAP_OPEN_TIMEOUT;
726 
727 retry:
728 	(void) pthread_mutex_lock(&adhostlock);
729 
730 	if (host_head == NULL) {
731 		(void) pthread_mutex_unlock(&adhostlock);
732 		goto out;
733 	}
734 
735 	if (dscount == 0) {
736 		/*
737 		 * First try: count the number of DSes.
738 		 *
739 		 * Integer overflow is not an issue -- we can't have so many
740 		 * DSes because they won't fit even DNS over TCP, and SMF
741 		 * shouldn't let you set so many.
742 		 */
743 		for (adh = host_head, tries = 0; adh != NULL; adh = adh->next) {
744 			if (adh->owner == ad)
745 				dscount++;
746 		}
747 
748 		if (dscount == 0) {
749 			(void) pthread_mutex_unlock(&adhostlock);
750 			goto out;
751 		}
752 
753 		tries = dscount * 3;	/* three tries per-ds */
754 
755 		/*
756 		 * Begin round-robin at the next DS in the list after the last
757 		 * one that we had a connection to, else start with the first
758 		 * DS in the list.
759 		 */
760 		adh = ad->last_adh;
761 	}
762 
763 	/*
764 	 * Round-robin -- pick the next one on the list; if the list
765 	 * changes on us, no big deal, we'll just potentially go
766 	 * around the wrong number of times.
767 	 */
768 	for (;;) {
769 		if (adh != NULL && adh->owner == ad && adh->ld != NULL &&
770 		    !adh->dead)
771 			break;
772 		if (adh == NULL || (adh = adh->next) == NULL)
773 			adh = host_head;
774 		if (adh->owner == ad)
775 			break;
776 	}
777 
778 	ad->last_adh = adh;
779 	(void) pthread_mutex_unlock(&adhostlock);
780 
781 	/* Found suitable DS, open it if not already opened */
782 	if (open_conn(adh, timeoutsecs))
783 		return (adh);
784 
785 	tries--;
786 	if ((tries % dscount) == 0)
787 		timeoutsecs *= 2;
788 	if (tries > 0)
789 		goto retry;
790 
791 out:
792 	idmapdlog(LOG_NOTICE, "Couldn't open an LDAP connection to any global "
793 	    "catalog server!");
794 	return (NULL);
795 }
796 
797 static
798 void
799 release_conn(adutils_host_t *adh)
800 {
801 	int delete = 0;
802 
803 	(void) pthread_mutex_lock(&adh->lock);
804 	if (atomic_dec_32_nv(&adh->ref) == 0) {
805 		if (adh->owner == NULL)
806 			delete = 1;
807 		adh->idletime = time(NULL);
808 	}
809 	(void) pthread_mutex_unlock(&adh->lock);
810 
811 	/* Free this host if its owner no longer exists. */
812 	if (delete) {
813 		(void) pthread_mutex_lock(&adhostlock);
814 		delete_ds(NULL, adh->host, adh->port);
815 		(void) pthread_mutex_unlock(&adhostlock);
816 	}
817 }
818 
819 /*
820  * Create a adutils_host_t, populate it and add it to the list of hosts.
821  */
822 adutils_rc
823 adutils_add_ds(adutils_ad_t *ad, const char *host, int port)
824 {
825 	adutils_host_t	*p;
826 	adutils_host_t	*new = NULL;
827 	int		ret;
828 	adutils_rc	rc;
829 
830 	(void) pthread_mutex_lock(&adhostlock);
831 	for (p = host_head; p != NULL; p = p->next) {
832 		if (p->owner != ad)
833 			continue;
834 
835 		if (strcmp(host, p->host) == 0 && p->port == port) {
836 			/* already added */
837 			rc = ADUTILS_SUCCESS;
838 			goto err;
839 		}
840 	}
841 
842 	rc = ADUTILS_ERR_MEMORY;
843 
844 	/* add new entry */
845 	new = (adutils_host_t *)calloc(1, sizeof (*new));
846 	if (new == NULL)
847 		goto err;
848 	new->owner = ad;
849 	new->port = port;
850 	new->dead = 0;
851 	new->max_requests = 80;
852 	new->num_requests = 0;
853 	if ((new->host = strdup(host)) == NULL)
854 		goto err;
855 	new->saslflags = LDAP_SASL_INTERACTIVE;
856 	new->saslmech = "GSSAPI";
857 
858 	if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) {
859 		free(new->host);
860 		new->host = NULL;
861 		errno = ret;
862 		rc = ADUTILS_ERR_INTERNAL;
863 		goto err;
864 	}
865 
866 	/* link in */
867 	rc = ADUTILS_SUCCESS;
868 	new->next = host_head;
869 	host_head = new;
870 
871 err:
872 	(void) pthread_mutex_unlock(&adhostlock);
873 
874 	if (rc != 0 && new != NULL) {
875 		if (new->host != NULL) {
876 			(void) pthread_mutex_destroy(&new->lock);
877 			free(new->host);
878 		}
879 		free(new);
880 	}
881 
882 	return (rc);
883 }
884 
885 /*
886  * Free a DS configuration.
887  * Caller must lock the adhostlock mutex
888  */
889 static
890 void
891 delete_ds(adutils_ad_t *ad, const char *host, int port)
892 {
893 	adutils_host_t	**p, *q;
894 
895 	for (p = &host_head; *p != NULL; p = &((*p)->next)) {
896 		if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 ||
897 		    (*p)->port != port)
898 			continue;
899 		/* found */
900 
901 		(void) pthread_mutex_lock(&((*p)->lock));
902 		if ((*p)->ref > 0) {
903 			/*
904 			 * Still in use. Set its owner to NULL so
905 			 * that it can be freed when its ref count
906 			 * becomes 0.
907 			 */
908 			(*p)->owner = NULL;
909 			(void) pthread_mutex_unlock(&((*p)->lock));
910 			break;
911 		}
912 		(void) pthread_mutex_unlock(&((*p)->lock));
913 
914 		q = *p;
915 		*p = (*p)->next;
916 
917 		(void) pthread_mutex_destroy(&q->lock);
918 
919 		if (q->ld)
920 			(void) ldap_unbind(q->ld);
921 		if (q->host)
922 			free(q->host);
923 		free(q);
924 		break;
925 	}
926 
927 }
928 /*
929  * Add known domain name and domain SID to AD configuration.
930  */
931 
932 adutils_rc
933 adutils_add_domain(adutils_ad_t *ad, const char *domain, const char *sid)
934 {
935 	struct known_domain *new;
936 	int num = ad->num_known_domains;
937 
938 	ad->num_known_domains++;
939 	new = realloc(ad->known_domains,
940 	    sizeof (struct known_domain) * ad->num_known_domains);
941 	if (new != NULL) {
942 		ad->known_domains = new;
943 		(void) strlcpy(ad->known_domains[num].name, domain,
944 		    sizeof (ad->known_domains[num].name));
945 		(void) strlcpy(ad->known_domains[num].sid, sid,
946 		    sizeof (ad->known_domains[num].sid));
947 		return (ADUTILS_SUCCESS);
948 	} else {
949 		if (ad->known_domains != NULL) {
950 			free(ad->known_domains);
951 			ad->known_domains = NULL;
952 		}
953 		ad->num_known_domains = 0;
954 		return (ADUTILS_ERR_MEMORY);
955 	}
956 }
957 
958 
959 /*
960  * Check that this AD supports this domain.
961  * If there are no known domains assume that the
962  * domain is supported by this AD.
963  *
964  * Returns 1 if this domain is supported by this AD
965  * else returns 0;
966  */
967 
968 int
969 adutils_lookup_check_domain(adutils_query_state_t *qs, const char *domain)
970 {
971 	adutils_ad_t *ad = qs->qadh->owner;
972 	int i, err;
973 
974 	for (i = 0; i < ad->num_known_domains; i++) {
975 		if (u8_strcmp(domain, ad->known_domains[i].name, 0,
976 		    U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) == 0 &&
977 		    err == 0)
978 			return (1);
979 	}
980 
981 	return ((i == 0) ? 1 : 0);
982 }
983 
984 
985 /*
986  * Check that this AD supports the SID prefix.
987  * The SID prefix should match the domain SID.
988  * If there are no known domains assume that the
989  * SID prefix is supported by this AD.
990  *
991  * Returns 1 if this sid prefix is supported by this AD
992  * else returns 0;
993  */
994 
995 int
996 adutils_lookup_check_sid_prefix(adutils_query_state_t *qs, const char *sid)
997 {
998 	adutils_ad_t *ad = qs->qadh->owner;
999 	int i;
1000 
1001 
1002 	for (i = 0; i < ad->num_known_domains; i++) {
1003 		if (strcmp(sid, ad->known_domains[i].sid) == 0)
1004 			return (1);
1005 	}
1006 
1007 	return ((i == 0) ? 1 : 0);
1008 }
1009 
1010 
1011 adutils_rc
1012 adutils_lookup_batch_start(adutils_ad_t *ad, int nqueries,
1013 	adutils_ldap_res_search_cb ldap_res_search_cb,
1014 	void *ldap_res_search_argp,
1015 	adutils_query_state_t **state)
1016 {
1017 	adutils_query_state_t	*new_state;
1018 	adutils_host_t		*adh = NULL;
1019 
1020 	if (ad == NULL)
1021 		return (ADUTILS_ERR_INTERNAL);
1022 
1023 	*state = NULL;
1024 	adh = get_conn(ad);
1025 	if (adh == NULL)
1026 		return (ADUTILS_ERR_RETRIABLE_NET_ERR);
1027 
1028 	new_state = calloc(1, sizeof (adutils_query_state_t) +
1029 	    (nqueries - 1) * sizeof (adutils_q_t));
1030 	if (new_state == NULL)
1031 		return (ADUTILS_ERR_MEMORY);
1032 
1033 	/*
1034 	 * Save default domain from the ad object so that we don't
1035 	 * have to access the 'ad' object later.
1036 	 */
1037 	new_state->default_domain = strdup(adh->owner->dflt_w2k_dom);
1038 	if (new_state->default_domain == NULL) {
1039 		free(new_state);
1040 		return (ADUTILS_ERR_MEMORY);
1041 	}
1042 
1043 	if (ad->partition == ADUTILS_AD_DATA)
1044 		new_state->basedn = adutils_dns2dn(new_state->default_domain);
1045 	else
1046 		new_state->basedn = strdup("");
1047 	if (new_state->basedn == NULL) {
1048 		free(new_state->default_domain);
1049 		free(new_state);
1050 		return (ADUTILS_ERR_MEMORY);
1051 	}
1052 
1053 	new_state->ref_cnt = 1;
1054 	new_state->qadh = adh;
1055 	new_state->qsize = nqueries;
1056 	new_state->qadh_gen = adh->generation;
1057 	new_state->qcount = 0;
1058 	new_state->ldap_res_search_cb = ldap_res_search_cb;
1059 	new_state->ldap_res_search_argp = ldap_res_search_argp;
1060 	(void) pthread_cond_init(&new_state->cv, NULL);
1061 
1062 	(void) pthread_mutex_lock(&qstatelock);
1063 	new_state->next = qstatehead;
1064 	qstatehead = new_state;
1065 	(void) pthread_mutex_unlock(&qstatelock);
1066 	*state = new_state;
1067 
1068 	return (ADUTILS_SUCCESS);
1069 }
1070 
1071 /*
1072  * Find the adutils_query_state_t to which a given LDAP result msgid on a
1073  * given connection belongs. This routine increaments the reference count
1074  * so that the object can not be freed. adutils_lookup_batch_unlock()
1075  * must be called to decreament the reference count.
1076  */
1077 static
1078 int
1079 msgid2query(adutils_host_t *adh, int msgid,
1080 	adutils_query_state_t **state, int *qid)
1081 {
1082 	adutils_query_state_t	*p;
1083 	int			i;
1084 	int			ret;
1085 
1086 	(void) pthread_mutex_lock(&qstatelock);
1087 	for (p = qstatehead; p != NULL; p = p->next) {
1088 		if (p->qadh != adh || adh->generation != p->qadh_gen)
1089 			continue;
1090 		for (i = 0; i < p->qcount; i++) {
1091 			if ((p->queries[i]).msgid == msgid) {
1092 				if (!p->qdead) {
1093 					p->ref_cnt++;
1094 					*state = p;
1095 					*qid = i;
1096 					ret = 1;
1097 				} else
1098 					ret = 0;
1099 				(void) pthread_mutex_unlock(&qstatelock);
1100 				return (ret);
1101 			}
1102 		}
1103 	}
1104 	(void) pthread_mutex_unlock(&qstatelock);
1105 	return (0);
1106 }
1107 
1108 static
1109 int
1110 check_for_binary_attrs(const char *attr)
1111 {
1112 	int i;
1113 	for (i = 0; binattrs[i].name != NULL; i++) {
1114 		if (strcasecmp(binattrs[i].name, attr) == 0)
1115 			return (i);
1116 	}
1117 	return (-1);
1118 }
1119 
1120 static
1121 void
1122 free_entry(adutils_entry_t *entry)
1123 {
1124 	int		i, j;
1125 	adutils_attr_t	*ap;
1126 
1127 	if (entry == NULL)
1128 		return;
1129 	if (entry->attr_nvpairs == NULL) {
1130 		free(entry);
1131 		return;
1132 	}
1133 	for (i = 0; i < entry->num_nvpairs; i++) {
1134 		ap = &entry->attr_nvpairs[i];
1135 		if (ap->attr_name == NULL) {
1136 			ldap_value_free(ap->attr_values);
1137 			continue;
1138 		}
1139 		if (check_for_binary_attrs(ap->attr_name) >= 0) {
1140 			free(ap->attr_name);
1141 			if (ap->attr_values == NULL)
1142 				continue;
1143 			for (j = 0; j < ap->num_values; j++)
1144 				free(ap->attr_values[j]);
1145 			free(ap->attr_values);
1146 		} else if (strcasecmp(ap->attr_name, "dn") == 0) {
1147 			free(ap->attr_name);
1148 			ldap_memfree(ap->attr_values[0]);
1149 			free(ap->attr_values);
1150 		} else {
1151 			free(ap->attr_name);
1152 			ldap_value_free(ap->attr_values);
1153 		}
1154 	}
1155 	free(entry->attr_nvpairs);
1156 	free(entry);
1157 }
1158 
1159 void
1160 adutils_freeresult(adutils_result_t **result)
1161 {
1162 	adutils_entry_t	*e, *next;
1163 
1164 	if (result == NULL || *result == NULL)
1165 		return;
1166 	if ((*result)->entries == NULL) {
1167 		free(*result);
1168 		*result = NULL;
1169 		return;
1170 	}
1171 	for (e = (*result)->entries; e != NULL; e = next) {
1172 		next = e->next;
1173 		free_entry(e);
1174 	}
1175 	free(*result);
1176 	*result = NULL;
1177 }
1178 
1179 const adutils_entry_t *
1180 adutils_getfirstentry(adutils_result_t *result)
1181 {
1182 	if (result != NULL)
1183 		return (result->entries);
1184 	return (NULL);
1185 }
1186 
1187 
1188 char **
1189 adutils_getattr(const adutils_entry_t *entry, const char *attrname)
1190 {
1191 	int		i;
1192 	adutils_attr_t	*ap;
1193 
1194 	if (entry == NULL || entry->attr_nvpairs == NULL)
1195 		return (NULL);
1196 	for (i = 0; i < entry->num_nvpairs; i++) {
1197 		ap = &entry->attr_nvpairs[i];
1198 		if (ap->attr_name != NULL &&
1199 		    strcasecmp(ap->attr_name, attrname) == 0)
1200 			return (ap->attr_values);
1201 	}
1202 	return (NULL);
1203 }
1204 
1205 
1206 /*
1207  * Queue LDAP result for the given query.
1208  *
1209  * Return values:
1210  *  0 success
1211  * -1 ignore result
1212  * -2 error
1213  */
1214 static
1215 int
1216 make_entry(adutils_q_t *q, adutils_host_t *adh, LDAPMessage *search_res,
1217 	adutils_entry_t **entry)
1218 {
1219 	BerElement	*ber = NULL;
1220 	BerValue	**bvalues = NULL;
1221 	char		**strvalues;
1222 	char		*attr = NULL, *dn = NULL, *domain = NULL;
1223 	adutils_entry_t	*ep;
1224 	adutils_attr_t	*ap;
1225 	int		i, j, b, err = 0, ret = -2;
1226 
1227 	*entry = NULL;
1228 
1229 	/* Check that this is the domain that we were looking for */
1230 	if ((dn = ldap_get_dn(adh->ld, search_res)) == NULL)
1231 		return (-2);
1232 	if ((domain = adutils_dn2dns(dn)) == NULL) {
1233 		ldap_memfree(dn);
1234 		return (-2);
1235 	}
1236 	if (q->edomain != NULL) {
1237 		if (u8_strcmp(q->edomain, domain, 0, U8_STRCMP_CI_LOWER,
1238 		    U8_UNICODE_LATEST, &err) != 0 || err != 0) {
1239 			ldap_memfree(dn);
1240 			free(domain);
1241 			return (-1);
1242 		}
1243 	}
1244 	free(domain);
1245 
1246 	/* Allocate memory for the entry */
1247 	if ((ep = calloc(1, sizeof (*ep))) == NULL)
1248 		goto out;
1249 
1250 	/* For 'dn' */
1251 	ep->num_nvpairs = 1;
1252 
1253 	/* Count the number of name-value pairs for this entry */
1254 	for (attr = ldap_first_attribute(adh->ld, search_res, &ber);
1255 	    attr != NULL;
1256 	    attr = ldap_next_attribute(adh->ld, search_res, ber)) {
1257 		ep->num_nvpairs++;
1258 		ldap_memfree(attr);
1259 	}
1260 	ber_free(ber, 0);
1261 	ber = NULL;
1262 
1263 	/* Allocate array for the attribute name-value pairs */
1264 	ep->attr_nvpairs = calloc(ep->num_nvpairs, sizeof (*ep->attr_nvpairs));
1265 	if (ep->attr_nvpairs == NULL) {
1266 		ep->num_nvpairs = 0;
1267 		goto out;
1268 	}
1269 
1270 	/* For dn */
1271 	ap = &ep->attr_nvpairs[0];
1272 	if ((ap->attr_name = strdup("dn")) == NULL)
1273 		goto out;
1274 	ap->num_values = 1;
1275 	ap->attr_values = calloc(ap->num_values, sizeof (*ap->attr_values));
1276 	if (ap->attr_values == NULL) {
1277 		ap->num_values = 0;
1278 		goto out;
1279 	}
1280 	ap->attr_values[0] = dn;
1281 	dn = NULL;
1282 
1283 	for (attr = ldap_first_attribute(adh->ld, search_res, &ber), i = 1;
1284 	    attr != NULL;
1285 	    ldap_memfree(attr), i++,
1286 	    attr = ldap_next_attribute(adh->ld, search_res, ber)) {
1287 		ap = &ep->attr_nvpairs[i];
1288 		if ((ap->attr_name = strdup(attr)) == NULL)
1289 			goto out;
1290 
1291 		if ((b = check_for_binary_attrs(attr)) >= 0) {
1292 			bvalues =
1293 			    ldap_get_values_len(adh->ld, search_res, attr);
1294 			if (bvalues == NULL)
1295 				continue;
1296 			ap->num_values = ldap_count_values_len(bvalues);
1297 			if (ap->num_values == 0) {
1298 				ldap_value_free_len(bvalues);
1299 				bvalues = NULL;
1300 				continue;
1301 			}
1302 			ap->attr_values = calloc(ap->num_values,
1303 			    sizeof (*ap->attr_values));
1304 			if (ap->attr_values == NULL) {
1305 				ap->num_values = 0;
1306 				goto out;
1307 			}
1308 			for (j = 0; j < ap->num_values; j++) {
1309 				ap->attr_values[j] =
1310 				    binattrs[b].ber2str(bvalues[j]);
1311 				if (ap->attr_values[j] == NULL)
1312 					goto out;
1313 			}
1314 			ldap_value_free_len(bvalues);
1315 			bvalues = NULL;
1316 			continue;
1317 		}
1318 
1319 		strvalues = ldap_get_values(adh->ld, search_res, attr);
1320 		if (strvalues == NULL)
1321 			continue;
1322 		ap->num_values = ldap_count_values(strvalues);
1323 		if (ap->num_values == 0) {
1324 			ldap_value_free(strvalues);
1325 			continue;
1326 		}
1327 		ap->attr_values = strvalues;
1328 	}
1329 
1330 	ret = 0;
1331 out:
1332 	ldap_memfree(attr);
1333 	ldap_memfree(dn);
1334 	ber_free(ber, 0);
1335 	ldap_value_free_len(bvalues);
1336 	if (ret < 0)
1337 		free_entry(ep);
1338 	else
1339 		*entry = ep;
1340 	return (ret);
1341 }
1342 
1343 /*
1344  * Put the search result onto the given adutils_q_t.
1345  * Returns:	  0 success
1346  *		< 0 error
1347  */
1348 static
1349 int
1350 add_entry(adutils_host_t *adh, adutils_q_t *q, LDAPMessage *search_res)
1351 {
1352 	int			ret = -1;
1353 	adutils_entry_t		*entry = NULL;
1354 	adutils_result_t	*res;
1355 
1356 	ret = make_entry(q, adh, search_res, &entry);
1357 	if (ret < -1) {
1358 		*q->rc = ADUTILS_ERR_MEMORY;
1359 		goto out;
1360 	} else if (ret == -1) {
1361 		/* ignore result */
1362 		goto out;
1363 	}
1364 	if (*q->result == NULL) {
1365 		res = calloc(1, sizeof (*res));
1366 		if (res == NULL) {
1367 			*q->rc = ADUTILS_ERR_MEMORY;
1368 			goto out;
1369 		}
1370 		res->num_entries = 1;
1371 		res->entries = entry;
1372 		*q->result = res;
1373 	} else {
1374 		res = *q->result;
1375 		entry->next = res->entries;
1376 		res->entries = entry;
1377 		res->num_entries++;
1378 	}
1379 	*q->rc = ADUTILS_SUCCESS;
1380 	entry = NULL;
1381 	ret = 0;
1382 
1383 out:
1384 	free_entry(entry);
1385 	return (ret);
1386 }
1387 
1388 /*
1389  * Try to get a result; if there is one, find the corresponding
1390  * adutils_q_t and process the result.
1391  *
1392  * Returns:	0 success
1393  *		-1 error
1394  */
1395 static
1396 int
1397 get_adobject_batch(adutils_host_t *adh, struct timeval *timeout)
1398 {
1399 	adutils_query_state_t	*query_state;
1400 	LDAPMessage		*res = NULL;
1401 	int			rc, ret, msgid, qid;
1402 	adutils_q_t		*que;
1403 	int			num;
1404 
1405 	(void) pthread_mutex_lock(&adh->lock);
1406 	if (adh->dead || adh->num_requests == 0) {
1407 		ret = (adh->dead) ? -1 : -2;
1408 		(void) pthread_mutex_unlock(&adh->lock);
1409 		return (ret);
1410 	}
1411 
1412 	/* Get one result */
1413 	rc = ldap_result(adh->ld, LDAP_RES_ANY, 0, timeout, &res);
1414 	if ((timeout != NULL && timeout->tv_sec > 0 && rc == LDAP_SUCCESS) ||
1415 	    rc < 0)
1416 		adh->dead = 1;
1417 
1418 	if (rc == LDAP_RES_SEARCH_RESULT && adh->num_requests > 0)
1419 		adh->num_requests--;
1420 	if (adh->dead) {
1421 		num = adh->num_requests;
1422 		(void) pthread_mutex_unlock(&adh->lock);
1423 		idmapdlog(LOG_DEBUG,
1424 		    "AD ldap_result error - %d queued requests", num);
1425 		return (-1);
1426 	}
1427 
1428 	switch (rc) {
1429 	case LDAP_RES_SEARCH_RESULT:
1430 		msgid = ldap_msgid(res);
1431 		if (msgid2query(adh, msgid, &query_state, &qid)) {
1432 			if (query_state->ldap_res_search_cb != NULL) {
1433 				/*
1434 				 * We use the caller-provided callback
1435 				 * to process the result.
1436 				 */
1437 				query_state->ldap_res_search_cb(
1438 				    adh->ld, &res, rc, qid,
1439 				    query_state->ldap_res_search_argp);
1440 				(void) pthread_mutex_unlock(&adh->lock);
1441 			} else {
1442 				/*
1443 				 * No callback. We fallback to our
1444 				 * default behaviour. All the entries
1445 				 * gotten from this search have been
1446 				 * added to the result list during
1447 				 * LDAP_RES_SEARCH_ENTRY (see below).
1448 				 * Here we set the return status to
1449 				 * notfound if the result is still empty.
1450 				 */
1451 				(void) pthread_mutex_unlock(&adh->lock);
1452 				que = &(query_state->queries[qid]);
1453 				if (*que->result == NULL)
1454 					*que->rc = ADUTILS_ERR_NOTFOUND;
1455 			}
1456 			atomic_dec_32(&query_state->qinflight);
1457 			adutils_lookup_batch_unlock(&query_state);
1458 		} else {
1459 			num = adh->num_requests;
1460 			(void) pthread_mutex_unlock(&adh->lock);
1461 			idmapdlog(LOG_DEBUG,
1462 			    "AD cannot find message ID (%d) "
1463 			    "- %d queued requests",
1464 			    msgid, num);
1465 		}
1466 		(void) ldap_msgfree(res);
1467 		ret = 0;
1468 		break;
1469 
1470 	case LDAP_RES_SEARCH_ENTRY:
1471 		msgid = ldap_msgid(res);
1472 		if (msgid2query(adh, msgid, &query_state, &qid)) {
1473 			if (query_state->ldap_res_search_cb != NULL) {
1474 				/*
1475 				 * We use the caller-provided callback
1476 				 * to process the entry.
1477 				 */
1478 				query_state->ldap_res_search_cb(
1479 				    adh->ld, &res, rc, qid,
1480 				    query_state->ldap_res_search_argp);
1481 				(void) pthread_mutex_unlock(&adh->lock);
1482 			} else {
1483 				/*
1484 				 * No callback. We fallback to our
1485 				 * default behaviour. This entry
1486 				 * will be added to the result list.
1487 				 */
1488 				que = &(query_state->queries[qid]);
1489 				rc = add_entry(adh, que, res);
1490 				(void) pthread_mutex_unlock(&adh->lock);
1491 				if (rc < 0) {
1492 					idmapdlog(LOG_DEBUG,
1493 					    "Failed to queue entry by "
1494 					    "message ID (%d) "
1495 					    "- %d queued requests",
1496 					    msgid, num);
1497 				}
1498 			}
1499 			adutils_lookup_batch_unlock(&query_state);
1500 		} else {
1501 			num = adh->num_requests;
1502 			(void) pthread_mutex_unlock(&adh->lock);
1503 			idmapdlog(LOG_DEBUG,
1504 			    "AD cannot find message ID (%d) "
1505 			    "- %d queued requests",
1506 			    msgid, num);
1507 		}
1508 		(void) ldap_msgfree(res);
1509 		ret = 0;
1510 		break;
1511 
1512 	case LDAP_RES_SEARCH_REFERENCE:
1513 		/*
1514 		 * We have no need for these at the moment.  Eventually,
1515 		 * when we query things that we can't expect to find in
1516 		 * the Global Catalog then we'll need to learn to follow
1517 		 * references.
1518 		 */
1519 		(void) pthread_mutex_unlock(&adh->lock);
1520 		(void) ldap_msgfree(res);
1521 		ret = 0;
1522 		break;
1523 
1524 	default:
1525 		/* timeout or error; treat the same */
1526 		(void) pthread_mutex_unlock(&adh->lock);
1527 		ret = -1;
1528 		break;
1529 	}
1530 
1531 	return (ret);
1532 }
1533 
1534 /*
1535  * This routine decreament the reference count of the
1536  * adutils_query_state_t
1537  */
1538 static void
1539 adutils_lookup_batch_unlock(adutils_query_state_t **state)
1540 {
1541 	/*
1542 	 * Decrement reference count with qstatelock locked
1543 	 */
1544 	(void) pthread_mutex_lock(&qstatelock);
1545 	(*state)->ref_cnt--;
1546 	/*
1547 	 * If there are no references wakup the allocating thread
1548 	 */
1549 	if ((*state)->ref_cnt <= 1)
1550 		(void) pthread_cond_signal(&(*state)->cv);
1551 	(void) pthread_mutex_unlock(&qstatelock);
1552 	*state = NULL;
1553 }
1554 
1555 /*
1556  * This routine frees the adutils_query_state_t structure
1557  * If the reference count is greater than 1 it waits
1558  * for the other threads to finish using it.
1559  */
1560 void
1561 adutils_lookup_batch_release(adutils_query_state_t **state)
1562 {
1563 	adutils_query_state_t **p;
1564 	int			i;
1565 
1566 	if (state == NULL || *state == NULL)
1567 		return;
1568 
1569 	/*
1570 	 * Set state to dead to stop further operations.
1571 	 * Wait for reference count with qstatelock locked
1572 	 * to get to one.
1573 	 */
1574 	(void) pthread_mutex_lock(&qstatelock);
1575 	(*state)->qdead = 1;
1576 	while ((*state)->ref_cnt > 1) {
1577 		(void) pthread_cond_wait(&(*state)->cv, &qstatelock);
1578 	}
1579 
1580 	/* Remove this state struct from the list of state structs */
1581 	for (p = &qstatehead; *p != NULL; p = &(*p)->next) {
1582 		if (*p == (*state)) {
1583 			*p = (*state)->next;
1584 			break;
1585 		}
1586 	}
1587 	(void) pthread_mutex_unlock(&qstatelock);
1588 	(void) pthread_cond_destroy(&(*state)->cv);
1589 	release_conn((*state)->qadh);
1590 
1591 	/* Clear results for queries that failed */
1592 	for (i = 0; i < (*state)->qcount; i++) {
1593 		if (*(*state)->queries[i].rc != ADUTILS_SUCCESS) {
1594 			adutils_freeresult((*state)->queries[i].result);
1595 		}
1596 	}
1597 	free((*state)->default_domain);
1598 	free((*state)->basedn);
1599 	free(*state);
1600 	*state = NULL;
1601 }
1602 
1603 
1604 /*
1605  * This routine waits for other threads using the
1606  * adutils_query_state_t structure to finish.
1607  * If the reference count is greater than 1 it waits
1608  * for the other threads to finish using it.
1609  */
1610 static
1611 void
1612 adutils_lookup_batch_wait(adutils_query_state_t *state)
1613 {
1614 	/*
1615 	 * Set state to dead to stop further operation.
1616 	 * stating.
1617 	 * Wait for reference count to get to one
1618 	 * with qstatelock locked.
1619 	 */
1620 	(void) pthread_mutex_lock(&qstatelock);
1621 	state->qdead = 1;
1622 	while (state->ref_cnt > 1) {
1623 		(void) pthread_cond_wait(&state->cv, &qstatelock);
1624 	}
1625 	(void) pthread_mutex_unlock(&qstatelock);
1626 }
1627 
1628 /*
1629  * Process active queries in the AD lookup batch and then finalize the
1630  * result.
1631  */
1632 adutils_rc
1633 adutils_lookup_batch_end(adutils_query_state_t **state)
1634 {
1635 	int		    rc = LDAP_SUCCESS;
1636 	adutils_rc	    ad_rc = ADUTILS_SUCCESS;
1637 	struct timeval	    tv;
1638 
1639 	tv.tv_sec = ADUTILS_SEARCH_TIMEOUT;
1640 	tv.tv_usec = 0;
1641 
1642 	/* Process results until done or until timeout, if given */
1643 	while ((*state)->qinflight > 0) {
1644 		if ((rc = get_adobject_batch((*state)->qadh,
1645 		    &tv)) != 0)
1646 			break;
1647 	}
1648 	(*state)->qdead = 1;
1649 	/* Wait for other threads processing search result to finish */
1650 	adutils_lookup_batch_wait(*state);
1651 	if (rc == -1 || (*state)->qinflight != 0)
1652 		ad_rc = ADUTILS_ERR_RETRIABLE_NET_ERR;
1653 	adutils_lookup_batch_release(state);
1654 	return (ad_rc);
1655 }
1656 
1657 const char *
1658 adutils_lookup_batch_getdefdomain(adutils_query_state_t *state)
1659 {
1660 	return (state->default_domain);
1661 }
1662 
1663 /*
1664  * Send one prepared search, queue up msgid, process what results are
1665  * available
1666  */
1667 adutils_rc
1668 adutils_lookup_batch_add(adutils_query_state_t *state,
1669 	const char *filter, const char **attrs, const char *edomain,
1670 	adutils_result_t **result, adutils_rc *rc)
1671 {
1672 	adutils_rc	retcode = ADUTILS_SUCCESS;
1673 	int		lrc, qid;
1674 	int		num;
1675 	int		dead;
1676 	struct timeval	tv;
1677 	adutils_q_t	*q;
1678 
1679 	qid = atomic_inc_32_nv(&state->qcount) - 1;
1680 	q = &(state->queries[qid]);
1681 
1682 	assert(qid < state->qsize);
1683 
1684 	/*
1685 	 * Remember the expected domain so we can check the results
1686 	 * against it
1687 	 */
1688 	q->edomain = edomain;
1689 
1690 	/* Remember where to put the results */
1691 	q->result = result;
1692 	q->rc = rc;
1693 
1694 	/*
1695 	 * Provide sane defaults for the results in case we never hear
1696 	 * back from the DS before closing the connection.
1697 	 */
1698 	*rc = ADUTILS_ERR_RETRIABLE_NET_ERR;
1699 	if (result != NULL)
1700 		*result = NULL;
1701 
1702 	/* Check the number of queued requests first */
1703 	tv.tv_sec = ADUTILS_SEARCH_TIMEOUT;
1704 	tv.tv_usec = 0;
1705 	while (!state->qadh->dead &&
1706 	    state->qadh->num_requests > state->qadh->max_requests) {
1707 		if (get_adobject_batch(state->qadh, &tv) != 0)
1708 			break;
1709 	}
1710 
1711 	/* Send this lookup, don't wait for a result here */
1712 	lrc = LDAP_SUCCESS;
1713 	(void) pthread_mutex_lock(&state->qadh->lock);
1714 
1715 	if (!state->qadh->dead) {
1716 		state->qadh->idletime = time(NULL);
1717 		lrc = ldap_search_ext(state->qadh->ld, state->basedn,
1718 		    LDAP_SCOPE_SUBTREE, filter, (char **)attrs,
1719 		    0, NULL, NULL, NULL, -1, &q->msgid);
1720 
1721 		if (lrc == LDAP_SUCCESS) {
1722 			state->qadh->num_requests++;
1723 		} else if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE ||
1724 		    lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN ||
1725 		    lrc == LDAP_UNWILLING_TO_PERFORM) {
1726 			retcode = ADUTILS_ERR_RETRIABLE_NET_ERR;
1727 			state->qadh->dead = 1;
1728 		} else {
1729 			retcode = ADUTILS_ERR_OTHER;
1730 			state->qadh->dead = 1;
1731 		}
1732 	}
1733 	dead = state->qadh->dead;
1734 	num = state->qadh->num_requests;
1735 	(void) pthread_mutex_unlock(&state->qadh->lock);
1736 
1737 	if (dead) {
1738 		if (lrc != LDAP_SUCCESS)
1739 			idmapdlog(LOG_DEBUG,
1740 			    "AD ldap_search_ext error (%s) "
1741 			    "- %d queued requests",
1742 			    ldap_err2string(lrc), num);
1743 		return (retcode);
1744 	}
1745 
1746 	atomic_inc_32(&state->qinflight);
1747 
1748 	/*
1749 	 * Reap as many requests as we can _without_ waiting to prevent
1750 	 * any possible TCP socket buffer starvation deadlocks.
1751 	 */
1752 	(void) memset(&tv, 0, sizeof (tv));
1753 	while (get_adobject_batch(state->qadh, &tv) == 0)
1754 		;
1755 
1756 	return (ADUTILS_SUCCESS);
1757 }
1758 
1759 /*
1760  * Single AD lookup request implemented on top of the batch API.
1761  */
1762 adutils_rc
1763 adutils_lookup(adutils_ad_t *ad, const char *filter, const char **attrs,
1764 		const char *domain, adutils_result_t **result)
1765 {
1766 	adutils_rc		rc, brc;
1767 	adutils_query_state_t	*qs;
1768 
1769 	rc = adutils_lookup_batch_start(ad, 1, NULL, NULL, &qs);
1770 	if (rc != ADUTILS_SUCCESS)
1771 		return (rc);
1772 
1773 	rc = adutils_lookup_batch_add(qs, filter, attrs, domain, result, &brc);
1774 	if (rc != ADUTILS_SUCCESS) {
1775 		adutils_lookup_batch_release(&qs);
1776 		return (rc);
1777 	}
1778 
1779 	rc = adutils_lookup_batch_end(&qs);
1780 	if (rc != ADUTILS_SUCCESS)
1781 		return (rc);
1782 	return (brc);
1783 }
1784