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