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