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