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