xref: /titanic_44/usr/src/lib/libadutils/common/adutils.c (revision 148c5f43199ca0b43fc8e3b643aab11cd66ea327)
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 */
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 int ad_debug[AD_DEBUG_MAX+1] = {0};
60 
61 typedef struct binary_attrs {
62 	const char	*name;
63 	char		*(*ber2str)(BerValue *bvalues);
64 } binary_attrs_t;
65 
66 static binary_attrs_t binattrs[] = {
67 	{"objectSID", adutils_sid_ber2str},
68 	{NULL, NULL}
69 };
70 
71 
72 adutils_logger logger = syslog;
73 
74 
75 void
adutils_set_logger(adutils_logger funct)76 adutils_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  */
85 static
86 char *
adutils_dns2dn(const char * dns)87 adutils_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  */
99 char *
adutils_dn2dns(const char * dn)100 adutils_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  */
109 int
adutils_getsid(BerValue * bval,adutils_sid_t * sidp)110 adutils_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  */
158 char *
adutils_sid2txt(adutils_sid_t * sidp)159 adutils_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  */
205 static
206 int
sid2binsid(adutils_sid_t * sid,uchar_t * binsid,int binsidlen)207 sid2binsid(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  */
250 int
adutils_txtsid2hexbinsid(const char * txt,const uint32_t * rid,char * hexbinsid,int hexbinsidlen)251 adutils_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 
355 static
356 char *
convert_bval2sid(BerValue * bval,uint32_t * rid)357 convert_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  */
379 char *
adutils_bv_objsid2sidstr(BerValue * bval,uint32_t * rid)380 adutils_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 
392 static
393 char *
adutils_sid_ber2str(BerValue * bval)394 adutils_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  */
404 boolean_t
adutils_bv_uint(BerValue * bval,unsigned int * result)405 adutils_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 */
433 char *
adutils_bv_str(BerValue * bval)434 adutils_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*/
448 int
saslcallback(LDAP * ld,unsigned flags,void * defaults,void * prompts)449 saslcallback(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  */
471 void
adutils_reap_idle_connections()472 adutils_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 
496 adutils_rc
adutils_ad_alloc(adutils_ad_t ** new_ad,const char * domain_name,adutils_ad_partition_t part)497 adutils_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 
527 err:
528 	free(ad->basedn);
529 	free(ad);
530 	return (ADUTILS_ERR_MEMORY);
531 }
532 
533 void
adutils_ad_free(adutils_ad_t ** ad)534 adutils_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 
579 static
580 int
open_conn(adutils_host_t * adh,int timeoutsecs)581 open_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 
644 out:
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  */
661 static
662 adutils_host_t *
get_conn(adutils_ad_t * ad)663 get_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 
670 retry:
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 
734 out:
735 	logger(LOG_NOTICE, "Couldn't open an LDAP connection to any global "
736 	    "catalog server!");
737 	return (NULL);
738 }
739 
740 static
741 void
release_conn(adutils_host_t * adh)742 release_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  */
765 adutils_rc
adutils_add_ds(adutils_ad_t * ad,const char * host,int port)766 adutils_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 
814 err:
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  */
832 static
833 void
delete_ds(adutils_ad_t * ad,const char * host,int port)834 delete_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 
875 adutils_rc
adutils_add_domain(adutils_ad_t * ad,const char * domain,const char * sid)876 adutils_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 
911 int
adutils_lookup_check_domain(adutils_query_state_t * qs,const char * domain)912 adutils_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 
936 int
adutils_lookup_check_sid_prefix(adutils_query_state_t * qs,const char * sid)937 adutils_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 
952 adutils_rc
adutils_lookup_batch_start(adutils_ad_t * ad,int nqueries,adutils_ldap_res_search_cb ldap_res_search_cb,void * ldap_res_search_argp,adutils_query_state_t ** state)953 adutils_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  */
998 static
999 int
msgid2query(adutils_host_t * adh,int msgid,adutils_query_state_t ** state,int * qid)1000 msgid2query(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 
1029 static
1030 int
check_for_binary_attrs(const char * attr)1031 check_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 
1041 static
1042 void
free_entry(adutils_entry_t * entry)1043 free_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 
1080 void
adutils_freeresult(adutils_result_t ** result)1081 adutils_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 
1100 const adutils_entry_t *
adutils_getfirstentry(adutils_result_t * result)1101 adutils_getfirstentry(adutils_result_t *result)
1102 {
1103 	if (result != NULL)
1104 		return (result->entries);
1105 	return (NULL);
1106 }
1107 
1108 
1109 char **
adutils_getattr(const adutils_entry_t * entry,const char * attrname)1110 adutils_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  */
1135 static
1136 int
make_entry(adutils_q_t * q,adutils_host_t * adh,LDAPMessage * search_res,adutils_entry_t ** entry)1137 make_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;
1251 out:
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  */
1268 static
1269 int
add_entry(adutils_host_t * adh,adutils_q_t * q,LDAPMessage * search_res)1270 add_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 
1303 out:
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  */
1315 static
1316 int
get_adobject_batch(adutils_host_t * adh,struct timeval * timeout)1317 get_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  */
1458 static void
adutils_lookup_batch_unlock(adutils_query_state_t ** state)1459 adutils_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  */
1480 void
adutils_lookup_batch_release(adutils_query_state_t ** state)1481 adutils_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  */
1528 static
1529 void
adutils_lookup_batch_wait(adutils_query_state_t * state)1530 adutils_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  */
1550 adutils_rc
adutils_lookup_batch_end(adutils_query_state_t ** state)1551 adutils_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  */
1579 adutils_rc
adutils_lookup_batch_add(adutils_query_state_t * state,const char * filter,const char * const * attrs,const char * edomain,adutils_result_t ** result,adutils_rc * rc)1580 adutils_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  */
1676 adutils_rc
adutils_lookup(adutils_ad_t * ad,const char * filter,const char ** attrs,const char * domain,adutils_result_t ** result)1677 adutils_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 
1699 boolean_t
domain_eq(const char * a,const char * b)1700 domain_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 
1708 void
adutils_set_debug(enum ad_debug item,int value)1709 adutils_set_debug(enum ad_debug item, int value)
1710 {
1711 	if (item >= 0 && item <= AD_DEBUG_MAX)
1712 		ad_debug[item] = value;
1713 }
1714