dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libadutils / common / adutils.c
blobde364c9fc809828fc066c303e91be744cf558923
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
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"
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;
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
52 static pthread_mutex_t qstatelock = PTHREAD_MUTEX_INITIALIZER;
53 static adutils_query_state_t *qstatehead = NULL;
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);
59 int ad_debug[AD_DEBUG_MAX+1] = {0};
61 typedef struct binary_attrs {
62 const char *name;
63 char *(*ber2str)(BerValue *bvalues);
64 } binary_attrs_t;
66 static binary_attrs_t binattrs[] = {
67 {"objectSID", adutils_sid_ber2str},
68 {NULL, NULL}
72 adutils_logger logger = syslog;
75 void
76 adutils_set_logger(adutils_logger funct)
78 logger = funct;
83 * Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com"
85 static
86 char *
87 adutils_dns2dn(const char *dns)
89 int num_parts;
91 return (ldap_dns_to_dn((char *)dns, &num_parts));
96 * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other
97 * attributes (CN, etc...).
99 char *
100 adutils_dn2dns(const char *dn)
102 return (DN_to_DNS(dn));
107 * Convert a binary SID in a BerValue to a adutils_sid_t
110 adutils_getsid(BerValue *bval, adutils_sid_t *sidp)
112 int i, j;
113 uchar_t *v;
114 uint32_t a;
117 * The binary format of a SID is as follows:
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
123 * followed by RID count RIDs, each a little-endian, unsigned
124 * 32-bit int.
127 * Sanity checks: must have at least one RID, version must be
128 * 0x01, and the length must be 8 + rid count * 4
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;
150 return (0);
152 return (-1);
156 * Convert a adutils_sid_t to S-1-...
158 char *
159 adutils_sid2txt(adutils_sid_t *sidp)
161 int rlen, i, len;
162 char *str, *cp;
164 if (sidp->version != 1)
165 return (NULL);
167 len = sizeof ("S-1-") - 1;
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);
178 len += snprintf(NULL, 0, "%llu", sidp->authority);
180 /* Max length of a uint32_t printed out in ASCII is 10 bytes */
181 len += 1 + (sidp->sub_authority_count + 1) * 10;
183 if ((cp = str = malloc(len)) == NULL)
184 return (NULL);
186 rlen = snprintf(str, len, "S-1-%llu", sidp->authority);
188 cp += rlen;
189 len -= rlen;
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);
199 return (str);
203 * Convert a adutils_sid_t to on-the-wire encoding
205 static
207 sid2binsid(adutils_sid_t *sid, uchar_t *binsid, int binsidlen)
209 uchar_t *p;
210 int i;
211 uint64_t a;
212 uint32_t r;
214 if (sid->version != 1 ||
215 binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4))
216 return (-1);
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;
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;
242 return (0);
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.
251 adutils_txtsid2hexbinsid(const char *txt, const uint32_t *rid,
252 char *hexbinsid, int hexbinsidlen)
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;
262 /* Only version 1 SIDs please */
263 if (strncmp(txt, "S-1-", strlen("S-1-")) != 0)
264 return (-1);
266 if (strlen(txt) < (strlen("S-1-") + 1))
267 return (-1);
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);
278 /* Adjust count for version and authority */
279 j -= 2;
281 /* we know the version number and RID count */
282 sid.version = 1;
283 sid.sub_authority_count = (rid != NULL) ? j + 1 : j;
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);
290 /* check that we only have digits and '-' */
291 if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1))
292 return (-1);
294 cp = txt + strlen("S-1-");
296 /* 64-bit safe parsing of unsigned 48-bit authority value */
297 errno = 0;
298 a = strtoull(cp, &ecp, 10);
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);
306 cp = ecp;
308 sid.authority = (uint64_t)a;
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;
325 /* check that all of the string SID has been consumed */
326 if (*cp != '\0')
327 return (-1);
329 if (rid != NULL)
330 sid.sub_authorities[j] = *rid;
332 j = 1 + 1 + 6 + sid.sub_authority_count * 4;
334 if (hexbinsidlen < (j * 3))
335 return (-2);
337 /* binary encode the SID */
338 binsid = (uchar_t *)alloca(j);
339 (void) sid2binsid(&sid, binsid, j);
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');
350 *ecp = '\0';
352 return (0);
355 static
356 char *
357 convert_bval2sid(BerValue *bval, uint32_t *rid)
359 adutils_sid_t sid;
361 if (adutils_getsid(bval, &sid) < 0)
362 return (NULL);
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
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));
376 * Return a NUL-terminated stringified SID from the value of an
377 * objectSid attribute and put the last RID in *rid.
379 char *
380 adutils_bv_objsid2sidstr(BerValue *bval, uint32_t *rid)
382 char *sid;
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);
392 static
393 char *
394 adutils_sid_ber2str(BerValue *bval)
396 return (adutils_bv_objsid2sidstr(bval, NULL));
401 * Extract an int from the Ber value
402 * Return B_TRUE if a valid integer was found, B_FALSE if not.
404 boolean_t
405 adutils_bv_uint(BerValue *bval, unsigned int *result)
407 char buf[40]; /* big enough for any int */
408 unsigned int tmp;
409 char *p;
411 *result = 0; /* for error cases */
413 if (bval == NULL || bval->bv_val == NULL)
414 return (B_FALSE);
415 if (bval->bv_len >= sizeof (buf))
416 return (B_FALSE);
418 (void) memcpy(buf, bval->bv_val, bval->bv_len);
419 buf[bval->bv_len] = '\0';
421 tmp = strtoul(buf, &p, 10);
423 /* Junk after the number? */
424 if (*p != '\0')
425 return (B_FALSE);
427 *result = tmp;
429 return (B_TRUE);
432 /* Return a NUL-terminated string from the Ber value */
433 char *
434 adutils_bv_str(BerValue *bval)
436 char *s;
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);
447 /*ARGSUSED*/
449 saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts)
451 sasl_interact_t *interact;
453 if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE)
454 return (LDAP_PARAM_ERROR);
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;
462 return (LDAP_SUCCESS);
466 #define ADCONN_TIME 300
469 * Idle connection reaping side of connection management
471 void
472 adutils_reap_idle_connections()
474 adutils_host_t *adh;
475 time_t now;
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;
490 (void) pthread_mutex_unlock(&adh->lock);
492 (void) pthread_mutex_unlock(&adhostlock);
496 adutils_rc
497 adutils_ad_alloc(adutils_ad_t **new_ad, const char *domain_name,
498 adutils_ad_partition_t part)
500 adutils_ad_t *ad;
502 *new_ad = NULL;
504 if ((ad = calloc(1, sizeof (*ad))) == NULL)
505 return (ADUTILS_ERR_MEMORY);
506 ad->ref = 1;
507 ad->partition = part;
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');
514 ad->basedn = adutils_dns2dn(domain_name);
515 } else {
516 assert(domain_name == NULL);
517 ad->basedn = strdup("");
519 if (ad->basedn == NULL)
520 goto err;
522 if (pthread_mutex_init(&ad->lock, NULL) != 0)
523 goto err;
524 *new_ad = ad;
525 return (ADUTILS_SUCCESS);
527 err:
528 free(ad->basedn);
529 free(ad);
530 return (ADUTILS_ERR_MEMORY);
533 void
534 adutils_ad_free(adutils_ad_t **ad)
536 adutils_host_t *p;
537 adutils_host_t *prev;
539 if (ad == NULL || *ad == NULL)
540 return;
542 (void) pthread_mutex_lock(&(*ad)->lock);
544 if (atomic_dec_32_nv(&(*ad)->ref) > 0) {
545 (void) pthread_mutex_unlock(&(*ad)->lock);
546 *ad = NULL;
547 return;
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;
566 (void) pthread_mutex_unlock(&adhostlock);
568 (void) pthread_mutex_unlock(&(*ad)->lock);
569 (void) pthread_mutex_destroy(&(*ad)->lock);
571 free((*ad)->known_domains);
572 free((*ad)->basedn);
573 free(*ad);
575 *ad = NULL;
578 static
580 open_conn(adutils_host_t *adh, int timeoutsecs)
582 int zero = 0;
583 int ldversion, rc;
584 int timeoutms = timeoutsecs * 1000;
586 if (adh == NULL)
587 return (0);
589 (void) pthread_mutex_lock(&adh->lock);
591 if (!adh->dead && adh->ld != NULL)
592 /* done! */
593 goto out;
595 if (adh->ld != NULL) {
596 (void) ldap_unbind(adh->ld);
597 adh->ld = NULL;
599 adh->num_requests = 0;
601 atomic_inc_64(&adh->generation);
603 /* Open and bind an LDAP connection */
604 adh->ld = ldap_init(adh->host, adh->port);
605 if (adh->ld == NULL) {
606 logger(LOG_INFO, "ldap_init() to server "
607 "%s port %d failed. (%s)", adh->host,
608 adh->port, strerror(errno));
609 goto out;
611 ldversion = LDAP_VERSION3;
612 (void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion);
613 (void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
614 (void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero);
615 (void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero);
616 (void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
617 (void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
619 rc = adutils_set_thread_functions(adh->ld);
620 if (rc != LDAP_SUCCESS) {
621 /* Error has already been logged */
622 (void) ldap_unbind(adh->ld);
623 adh->ld = NULL;
624 goto out;
627 rc = ldap_sasl_interactive_bind_s(adh->ld, "" /* binddn */,
628 adh->saslmech, NULL, NULL, adh->saslflags, &saslcallback,
629 NULL);
631 if (rc != LDAP_SUCCESS) {
632 (void) ldap_unbind(adh->ld);
633 adh->ld = NULL;
634 logger(LOG_INFO, "ldap_sasl_interactive_bind_s() to server "
635 "%s port %d failed. (%s)", adh->host, adh->port,
636 ldap_err2string(rc));
637 goto out;
640 logger(LOG_DEBUG, "Using server %s:%d",
641 adh->host, adh->port);
643 out:
644 if (adh->ld != NULL) {
645 atomic_inc_32(&adh->ref);
646 adh->idletime = time(NULL);
647 adh->dead = 0;
648 (void) pthread_mutex_unlock(&adh->lock);
649 return (1);
652 (void) pthread_mutex_unlock(&adh->lock);
653 return (0);
658 * Connection management: find an open connection or open one
660 static
661 adutils_host_t *
662 get_conn(adutils_ad_t *ad)
664 adutils_host_t *adh = NULL;
665 int tries;
666 int dscount = 0;
667 int timeoutsecs = ADUTILS_LDAP_OPEN_TIMEOUT;
669 retry:
670 (void) pthread_mutex_lock(&adhostlock);
672 if (host_head == NULL) {
673 (void) pthread_mutex_unlock(&adhostlock);
674 goto out;
677 if (dscount == 0) {
679 * First try: count the number of DSes.
681 * Integer overflow is not an issue -- we can't have so many
682 * DSes because they won't fit even DNS over TCP, and SMF
683 * shouldn't let you set so many.
685 for (adh = host_head, tries = 0; adh != NULL; adh = adh->next) {
686 if (adh->owner == ad)
687 dscount++;
690 if (dscount == 0) {
691 (void) pthread_mutex_unlock(&adhostlock);
692 goto out;
695 tries = dscount * 3; /* three tries per-ds */
698 * Begin round-robin at the next DS in the list after the last
699 * one that we had a connection to, else start with the first
700 * DS in the list.
702 adh = ad->last_adh;
706 * Round-robin -- pick the next one on the list; if the list
707 * changes on us, no big deal, we'll just potentially go
708 * around the wrong number of times.
710 for (;;) {
711 if (adh != NULL && adh->owner == ad && adh->ld != NULL &&
712 !adh->dead)
713 break;
714 if (adh == NULL || (adh = adh->next) == NULL)
715 adh = host_head;
716 if (adh->owner == ad)
717 break;
720 ad->last_adh = adh;
721 (void) pthread_mutex_unlock(&adhostlock);
723 /* Found suitable DS, open it if not already opened */
724 if (open_conn(adh, timeoutsecs))
725 return (adh);
727 tries--;
728 if ((tries % dscount) == 0)
729 timeoutsecs *= 2;
730 if (tries > 0)
731 goto retry;
733 out:
734 logger(LOG_NOTICE, "Couldn't open an LDAP connection to any global "
735 "catalog server!");
736 return (NULL);
739 static
740 void
741 release_conn(adutils_host_t *adh)
743 int delete = 0;
745 (void) pthread_mutex_lock(&adh->lock);
746 if (atomic_dec_32_nv(&adh->ref) == 0) {
747 if (adh->owner == NULL)
748 delete = 1;
749 adh->idletime = time(NULL);
751 (void) pthread_mutex_unlock(&adh->lock);
753 /* Free this host if its owner no longer exists. */
754 if (delete) {
755 (void) pthread_mutex_lock(&adhostlock);
756 delete_ds(NULL, adh->host, adh->port);
757 (void) pthread_mutex_unlock(&adhostlock);
762 * Create a adutils_host_t, populate it and add it to the list of hosts.
764 adutils_rc
765 adutils_add_ds(adutils_ad_t *ad, const char *host, int port)
767 adutils_host_t *p;
768 adutils_host_t *new = NULL;
769 int ret;
770 adutils_rc rc;
772 (void) pthread_mutex_lock(&adhostlock);
773 for (p = host_head; p != NULL; p = p->next) {
774 if (p->owner != ad)
775 continue;
777 if (strcmp(host, p->host) == 0 && p->port == port) {
778 /* already added */
779 rc = ADUTILS_SUCCESS;
780 goto err;
784 rc = ADUTILS_ERR_MEMORY;
786 /* add new entry */
787 new = (adutils_host_t *)calloc(1, sizeof (*new));
788 if (new == NULL)
789 goto err;
790 new->owner = ad;
791 new->port = port;
792 new->dead = 0;
793 new->max_requests = 80;
794 new->num_requests = 0;
795 if ((new->host = strdup(host)) == NULL)
796 goto err;
797 new->saslflags = LDAP_SASL_INTERACTIVE;
798 new->saslmech = "GSSAPI";
800 if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) {
801 free(new->host);
802 new->host = NULL;
803 errno = ret;
804 rc = ADUTILS_ERR_INTERNAL;
805 goto err;
808 /* link in */
809 rc = ADUTILS_SUCCESS;
810 new->next = host_head;
811 host_head = new;
813 err:
814 (void) pthread_mutex_unlock(&adhostlock);
816 if (rc != 0 && new != NULL) {
817 if (new->host != NULL) {
818 (void) pthread_mutex_destroy(&new->lock);
819 free(new->host);
821 free(new);
824 return (rc);
828 * Free a DS configuration.
829 * Caller must lock the adhostlock mutex
831 static
832 void
833 delete_ds(adutils_ad_t *ad, const char *host, int port)
835 adutils_host_t **p, *q;
837 for (p = &host_head; *p != NULL; p = &((*p)->next)) {
838 if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 ||
839 (*p)->port != port)
840 continue;
841 /* found */
843 (void) pthread_mutex_lock(&((*p)->lock));
844 if ((*p)->ref > 0) {
846 * Still in use. Set its owner to NULL so
847 * that it can be freed when its ref count
848 * becomes 0.
850 (*p)->owner = NULL;
851 (void) pthread_mutex_unlock(&((*p)->lock));
852 break;
854 (void) pthread_mutex_unlock(&((*p)->lock));
856 q = *p;
857 *p = (*p)->next;
859 (void) pthread_mutex_destroy(&q->lock);
861 if (q->ld)
862 (void) ldap_unbind(q->ld);
863 free(q->host);
864 free(q);
865 break;
870 * Add known domain name and domain SID to AD configuration.
873 adutils_rc
874 adutils_add_domain(adutils_ad_t *ad, const char *domain, const char *sid)
876 struct known_domain *new;
877 int num = ad->num_known_domains;
879 ad->num_known_domains++;
880 new = reallocarray(ad->known_domains, ad->num_known_domains,
881 sizeof (struct known_domain));
882 if (new != NULL) {
883 ad->known_domains = new;
884 (void) strlcpy(ad->known_domains[num].name, domain,
885 sizeof (ad->known_domains[num].name));
886 (void) strlcpy(ad->known_domains[num].sid, sid,
887 sizeof (ad->known_domains[num].sid));
888 return (ADUTILS_SUCCESS);
889 } else {
890 if (ad->known_domains != NULL) {
891 free(ad->known_domains);
892 ad->known_domains = NULL;
894 ad->num_known_domains = 0;
895 return (ADUTILS_ERR_MEMORY);
901 * Check that this AD supports this domain.
902 * If there are no known domains assume that the
903 * domain is supported by this AD.
905 * Returns 1 if this domain is supported by this AD
906 * else returns 0;
910 adutils_lookup_check_domain(adutils_query_state_t *qs, const char *domain)
912 adutils_ad_t *ad = qs->qadh->owner;
913 int i;
915 for (i = 0; i < ad->num_known_domains; i++) {
916 if (domain_eq(domain, ad->known_domains[i].name))
917 return (1);
920 return ((i == 0) ? 1 : 0);
925 * Check that this AD supports the SID prefix.
926 * The SID prefix should match the domain SID.
927 * If there are no known domains assume that the
928 * SID prefix is supported by this AD.
930 * Returns 1 if this sid prefix is supported by this AD
931 * else returns 0;
935 adutils_lookup_check_sid_prefix(adutils_query_state_t *qs, const char *sid)
937 adutils_ad_t *ad = qs->qadh->owner;
938 int i;
941 for (i = 0; i < ad->num_known_domains; i++) {
942 if (strcmp(sid, ad->known_domains[i].sid) == 0)
943 return (1);
946 return ((i == 0) ? 1 : 0);
950 adutils_rc
951 adutils_lookup_batch_start(adutils_ad_t *ad, int nqueries,
952 adutils_ldap_res_search_cb ldap_res_search_cb,
953 void *ldap_res_search_argp,
954 adutils_query_state_t **state)
956 adutils_query_state_t *new_state;
957 adutils_host_t *adh = NULL;
959 if (ad == NULL)
960 return (ADUTILS_ERR_INTERNAL);
962 *state = NULL;
963 adh = get_conn(ad);
964 if (adh == NULL)
965 return (ADUTILS_ERR_RETRIABLE_NET_ERR);
967 new_state = calloc(1, sizeof (adutils_query_state_t) +
968 (nqueries - 1) * sizeof (adutils_q_t));
969 if (new_state == NULL)
970 return (ADUTILS_ERR_MEMORY);
972 new_state->ref_cnt = 1;
973 new_state->qadh = adh;
974 new_state->qsize = nqueries;
975 new_state->qadh_gen = adh->generation;
976 new_state->qcount = 0;
977 new_state->ldap_res_search_cb = ldap_res_search_cb;
978 new_state->ldap_res_search_argp = ldap_res_search_argp;
979 (void) pthread_cond_init(&new_state->cv, NULL);
981 (void) pthread_mutex_lock(&qstatelock);
982 new_state->next = qstatehead;
983 qstatehead = new_state;
984 (void) pthread_mutex_unlock(&qstatelock);
985 *state = new_state;
987 return (ADUTILS_SUCCESS);
991 * Find the adutils_query_state_t to which a given LDAP result msgid on a
992 * given connection belongs. This routine increaments the reference count
993 * so that the object can not be freed. adutils_lookup_batch_unlock()
994 * must be called to decreament the reference count.
996 static
998 msgid2query(adutils_host_t *adh, int msgid,
999 adutils_query_state_t **state, int *qid)
1001 adutils_query_state_t *p;
1002 int i;
1003 int ret;
1005 (void) pthread_mutex_lock(&qstatelock);
1006 for (p = qstatehead; p != NULL; p = p->next) {
1007 if (p->qadh != adh || adh->generation != p->qadh_gen)
1008 continue;
1009 for (i = 0; i < p->qcount; i++) {
1010 if ((p->queries[i]).msgid == msgid) {
1011 if (!p->qdead) {
1012 p->ref_cnt++;
1013 *state = p;
1014 *qid = i;
1015 ret = 1;
1016 } else
1017 ret = 0;
1018 (void) pthread_mutex_unlock(&qstatelock);
1019 return (ret);
1023 (void) pthread_mutex_unlock(&qstatelock);
1024 return (0);
1027 static
1029 check_for_binary_attrs(const char *attr)
1031 int i;
1032 for (i = 0; binattrs[i].name != NULL; i++) {
1033 if (strcasecmp(binattrs[i].name, attr) == 0)
1034 return (i);
1036 return (-1);
1039 static
1040 void
1041 free_entry(adutils_entry_t *entry)
1043 int i, j;
1044 adutils_attr_t *ap;
1046 if (entry == NULL)
1047 return;
1048 if (entry->attr_nvpairs == NULL) {
1049 free(entry);
1050 return;
1052 for (i = 0; i < entry->num_nvpairs; i++) {
1053 ap = &entry->attr_nvpairs[i];
1054 if (ap->attr_name == NULL) {
1055 ldap_value_free(ap->attr_values);
1056 continue;
1058 if (check_for_binary_attrs(ap->attr_name) >= 0) {
1059 free(ap->attr_name);
1060 if (ap->attr_values == NULL)
1061 continue;
1062 for (j = 0; j < ap->num_values; j++)
1063 free(ap->attr_values[j]);
1064 free(ap->attr_values);
1065 } else if (strcasecmp(ap->attr_name, "dn") == 0) {
1066 free(ap->attr_name);
1067 ldap_memfree(ap->attr_values[0]);
1068 free(ap->attr_values);
1069 } else {
1070 free(ap->attr_name);
1071 ldap_value_free(ap->attr_values);
1074 free(entry->attr_nvpairs);
1075 free(entry);
1078 void
1079 adutils_freeresult(adutils_result_t **result)
1081 adutils_entry_t *e, *next;
1083 if (result == NULL || *result == NULL)
1084 return;
1085 if ((*result)->entries == NULL) {
1086 free(*result);
1087 *result = NULL;
1088 return;
1090 for (e = (*result)->entries; e != NULL; e = next) {
1091 next = e->next;
1092 free_entry(e);
1094 free(*result);
1095 *result = NULL;
1098 const adutils_entry_t *
1099 adutils_getfirstentry(adutils_result_t *result)
1101 if (result != NULL)
1102 return (result->entries);
1103 return (NULL);
1107 char **
1108 adutils_getattr(const adutils_entry_t *entry, const char *attrname)
1110 int i;
1111 adutils_attr_t *ap;
1113 if (entry == NULL || entry->attr_nvpairs == NULL)
1114 return (NULL);
1115 for (i = 0; i < entry->num_nvpairs; i++) {
1116 ap = &entry->attr_nvpairs[i];
1117 if (ap->attr_name != NULL &&
1118 strcasecmp(ap->attr_name, attrname) == 0)
1119 return (ap->attr_values);
1121 return (NULL);
1126 * Queue LDAP result for the given query.
1128 * Return values:
1129 * 0 success
1130 * -1 ignore result
1131 * -2 error
1133 static
1135 make_entry(adutils_q_t *q, adutils_host_t *adh, LDAPMessage *search_res,
1136 adutils_entry_t **entry)
1138 BerElement *ber = NULL;
1139 BerValue **bvalues = NULL;
1140 char **strvalues;
1141 char *attr = NULL, *dn = NULL, *domain = NULL;
1142 adutils_entry_t *ep;
1143 adutils_attr_t *ap;
1144 int i, j, b, ret = -2;
1146 *entry = NULL;
1148 /* Check that this is the domain that we were looking for */
1149 if ((dn = ldap_get_dn(adh->ld, search_res)) == NULL)
1150 return (-2);
1151 if ((domain = adutils_dn2dns(dn)) == NULL) {
1152 ldap_memfree(dn);
1153 return (-2);
1155 if (q->edomain != NULL) {
1156 if (!domain_eq(q->edomain, domain)) {
1157 ldap_memfree(dn);
1158 free(domain);
1159 return (-1);
1162 free(domain);
1164 /* Allocate memory for the entry */
1165 if ((ep = calloc(1, sizeof (*ep))) == NULL)
1166 goto out;
1168 /* For 'dn' */
1169 ep->num_nvpairs = 1;
1171 /* Count the number of name-value pairs for this entry */
1172 for (attr = ldap_first_attribute(adh->ld, search_res, &ber);
1173 attr != NULL;
1174 attr = ldap_next_attribute(adh->ld, search_res, ber)) {
1175 ep->num_nvpairs++;
1176 ldap_memfree(attr);
1178 ber_free(ber, 0);
1179 ber = NULL;
1181 /* Allocate array for the attribute name-value pairs */
1182 ep->attr_nvpairs = calloc(ep->num_nvpairs, sizeof (*ep->attr_nvpairs));
1183 if (ep->attr_nvpairs == NULL) {
1184 ep->num_nvpairs = 0;
1185 goto out;
1188 /* For dn */
1189 ap = &ep->attr_nvpairs[0];
1190 if ((ap->attr_name = strdup("dn")) == NULL)
1191 goto out;
1192 ap->num_values = 1;
1193 ap->attr_values = calloc(ap->num_values, sizeof (*ap->attr_values));
1194 if (ap->attr_values == NULL) {
1195 ap->num_values = 0;
1196 goto out;
1198 ap->attr_values[0] = dn;
1199 dn = NULL;
1201 for (attr = ldap_first_attribute(adh->ld, search_res, &ber), i = 1;
1202 attr != NULL;
1203 ldap_memfree(attr), i++,
1204 attr = ldap_next_attribute(adh->ld, search_res, ber)) {
1205 ap = &ep->attr_nvpairs[i];
1206 if ((ap->attr_name = strdup(attr)) == NULL)
1207 goto out;
1209 if ((b = check_for_binary_attrs(attr)) >= 0) {
1210 bvalues =
1211 ldap_get_values_len(adh->ld, search_res, attr);
1212 if (bvalues == NULL)
1213 continue;
1214 ap->num_values = ldap_count_values_len(bvalues);
1215 if (ap->num_values == 0) {
1216 ldap_value_free_len(bvalues);
1217 bvalues = NULL;
1218 continue;
1220 ap->attr_values = calloc(ap->num_values,
1221 sizeof (*ap->attr_values));
1222 if (ap->attr_values == NULL) {
1223 ap->num_values = 0;
1224 goto out;
1226 for (j = 0; j < ap->num_values; j++) {
1227 ap->attr_values[j] =
1228 binattrs[b].ber2str(bvalues[j]);
1229 if (ap->attr_values[j] == NULL)
1230 goto out;
1232 ldap_value_free_len(bvalues);
1233 bvalues = NULL;
1234 continue;
1237 strvalues = ldap_get_values(adh->ld, search_res, attr);
1238 if (strvalues == NULL)
1239 continue;
1240 ap->num_values = ldap_count_values(strvalues);
1241 if (ap->num_values == 0) {
1242 ldap_value_free(strvalues);
1243 continue;
1245 ap->attr_values = strvalues;
1248 ret = 0;
1249 out:
1250 ldap_memfree(attr);
1251 ldap_memfree(dn);
1252 ber_free(ber, 0);
1253 ldap_value_free_len(bvalues);
1254 if (ret < 0)
1255 free_entry(ep);
1256 else
1257 *entry = ep;
1258 return (ret);
1262 * Put the search result onto the given adutils_q_t.
1263 * Returns: 0 success
1264 * < 0 error
1266 static
1268 add_entry(adutils_host_t *adh, adutils_q_t *q, LDAPMessage *search_res)
1270 int ret = -1;
1271 adutils_entry_t *entry = NULL;
1272 adutils_result_t *res;
1274 ret = make_entry(q, adh, search_res, &entry);
1275 if (ret < -1) {
1276 *q->rc = ADUTILS_ERR_MEMORY;
1277 goto out;
1278 } else if (ret == -1) {
1279 /* ignore result */
1280 goto out;
1282 if (*q->result == NULL) {
1283 res = calloc(1, sizeof (*res));
1284 if (res == NULL) {
1285 *q->rc = ADUTILS_ERR_MEMORY;
1286 goto out;
1288 res->num_entries = 1;
1289 res->entries = entry;
1290 *q->result = res;
1291 } else {
1292 res = *q->result;
1293 entry->next = res->entries;
1294 res->entries = entry;
1295 res->num_entries++;
1297 *q->rc = ADUTILS_SUCCESS;
1298 entry = NULL;
1299 ret = 0;
1301 out:
1302 free_entry(entry);
1303 return (ret);
1307 * Try to get a result; if there is one, find the corresponding
1308 * adutils_q_t and process the result.
1310 * Returns: 0 success
1311 * -1 error
1313 static
1315 get_adobject_batch(adutils_host_t *adh, struct timeval *timeout)
1317 adutils_query_state_t *query_state;
1318 LDAPMessage *res = NULL;
1319 int rc, ret, msgid, qid;
1320 adutils_q_t *que;
1321 int num;
1323 (void) pthread_mutex_lock(&adh->lock);
1324 if (adh->dead || adh->num_requests == 0) {
1325 ret = (adh->dead) ? -1 : -2;
1326 (void) pthread_mutex_unlock(&adh->lock);
1327 return (ret);
1330 /* Get one result */
1331 rc = ldap_result(adh->ld, LDAP_RES_ANY, 0, timeout, &res);
1332 if ((timeout != NULL && timeout->tv_sec > 0 && rc == LDAP_SUCCESS) ||
1333 rc < 0)
1334 adh->dead = 1;
1336 if (rc == LDAP_RES_SEARCH_RESULT && adh->num_requests > 0)
1337 adh->num_requests--;
1338 if (adh->dead) {
1339 num = adh->num_requests;
1340 (void) pthread_mutex_unlock(&adh->lock);
1341 logger(LOG_DEBUG,
1342 "AD ldap_result error - %d queued requests", num);
1343 return (-1);
1346 switch (rc) {
1347 case LDAP_RES_SEARCH_RESULT:
1348 msgid = ldap_msgid(res);
1349 if (msgid2query(adh, msgid, &query_state, &qid)) {
1350 if (query_state->ldap_res_search_cb != NULL) {
1352 * We use the caller-provided callback
1353 * to process the result.
1355 query_state->ldap_res_search_cb(
1356 adh->ld, &res, rc, qid,
1357 query_state->ldap_res_search_argp);
1358 (void) pthread_mutex_unlock(&adh->lock);
1359 } else {
1361 * No callback. We fallback to our
1362 * default behaviour. All the entries
1363 * gotten from this search have been
1364 * added to the result list during
1365 * LDAP_RES_SEARCH_ENTRY (see below).
1366 * Here we set the return status to
1367 * notfound if the result is still empty.
1369 (void) pthread_mutex_unlock(&adh->lock);
1370 que = &(query_state->queries[qid]);
1371 if (*que->result == NULL)
1372 *que->rc = ADUTILS_ERR_NOTFOUND;
1374 atomic_dec_32(&query_state->qinflight);
1375 adutils_lookup_batch_unlock(&query_state);
1376 } else {
1377 num = adh->num_requests;
1378 (void) pthread_mutex_unlock(&adh->lock);
1379 logger(LOG_DEBUG,
1380 "AD cannot find message ID (%d) "
1381 "- %d queued requests",
1382 msgid, num);
1384 (void) ldap_msgfree(res);
1385 ret = 0;
1386 break;
1388 case LDAP_RES_SEARCH_ENTRY:
1389 msgid = ldap_msgid(res);
1390 if (msgid2query(adh, msgid, &query_state, &qid)) {
1391 if (query_state->ldap_res_search_cb != NULL) {
1393 * We use the caller-provided callback
1394 * to process the entry.
1396 query_state->ldap_res_search_cb(
1397 adh->ld, &res, rc, qid,
1398 query_state->ldap_res_search_argp);
1399 (void) pthread_mutex_unlock(&adh->lock);
1400 } else {
1402 * No callback. We fallback to our
1403 * default behaviour. This entry
1404 * will be added to the result list.
1406 que = &(query_state->queries[qid]);
1407 rc = add_entry(adh, que, res);
1408 (void) pthread_mutex_unlock(&adh->lock);
1409 if (rc < 0) {
1410 logger(LOG_DEBUG,
1411 "Failed to queue entry by "
1412 "message ID (%d) "
1413 "- %d queued requests",
1414 msgid, num);
1417 adutils_lookup_batch_unlock(&query_state);
1418 } else {
1419 num = adh->num_requests;
1420 (void) pthread_mutex_unlock(&adh->lock);
1421 logger(LOG_DEBUG,
1422 "AD cannot find message ID (%d) "
1423 "- %d queued requests",
1424 msgid, num);
1426 (void) ldap_msgfree(res);
1427 ret = 0;
1428 break;
1430 case LDAP_RES_SEARCH_REFERENCE:
1432 * We have no need for these at the moment. Eventually,
1433 * when we query things that we can't expect to find in
1434 * the Global Catalog then we'll need to learn to follow
1435 * references.
1437 (void) pthread_mutex_unlock(&adh->lock);
1438 (void) ldap_msgfree(res);
1439 ret = 0;
1440 break;
1442 default:
1443 /* timeout or error; treat the same */
1444 (void) pthread_mutex_unlock(&adh->lock);
1445 ret = -1;
1446 break;
1449 return (ret);
1453 * This routine decreament the reference count of the
1454 * adutils_query_state_t
1456 static void
1457 adutils_lookup_batch_unlock(adutils_query_state_t **state)
1460 * Decrement reference count with qstatelock locked
1462 (void) pthread_mutex_lock(&qstatelock);
1463 (*state)->ref_cnt--;
1465 * If there are no references wakup the allocating thread
1467 if ((*state)->ref_cnt <= 1)
1468 (void) pthread_cond_signal(&(*state)->cv);
1469 (void) pthread_mutex_unlock(&qstatelock);
1470 *state = NULL;
1474 * This routine frees the adutils_query_state_t structure
1475 * If the reference count is greater than 1 it waits
1476 * for the other threads to finish using it.
1478 void
1479 adutils_lookup_batch_release(adutils_query_state_t **state)
1481 adutils_query_state_t **p;
1482 int i;
1484 if (state == NULL || *state == NULL)
1485 return;
1488 * Set state to dead to stop further operations.
1489 * Wait for reference count with qstatelock locked
1490 * to get to one.
1492 (void) pthread_mutex_lock(&qstatelock);
1493 (*state)->qdead = 1;
1494 while ((*state)->ref_cnt > 1) {
1495 (void) pthread_cond_wait(&(*state)->cv, &qstatelock);
1498 /* Remove this state struct from the list of state structs */
1499 for (p = &qstatehead; *p != NULL; p = &(*p)->next) {
1500 if (*p == (*state)) {
1501 *p = (*state)->next;
1502 break;
1505 (void) pthread_mutex_unlock(&qstatelock);
1506 (void) pthread_cond_destroy(&(*state)->cv);
1507 release_conn((*state)->qadh);
1509 /* Clear results for queries that failed */
1510 for (i = 0; i < (*state)->qcount; i++) {
1511 if (*(*state)->queries[i].rc != ADUTILS_SUCCESS) {
1512 adutils_freeresult((*state)->queries[i].result);
1515 free(*state);
1516 *state = NULL;
1521 * This routine waits for other threads using the
1522 * adutils_query_state_t structure to finish.
1523 * If the reference count is greater than 1 it waits
1524 * for the other threads to finish using it.
1526 static
1527 void
1528 adutils_lookup_batch_wait(adutils_query_state_t *state)
1531 * Set state to dead to stop further operation.
1532 * stating.
1533 * Wait for reference count to get to one
1534 * with qstatelock locked.
1536 (void) pthread_mutex_lock(&qstatelock);
1537 state->qdead = 1;
1538 while (state->ref_cnt > 1) {
1539 (void) pthread_cond_wait(&state->cv, &qstatelock);
1541 (void) pthread_mutex_unlock(&qstatelock);
1545 * Process active queries in the AD lookup batch and then finalize the
1546 * result.
1548 adutils_rc
1549 adutils_lookup_batch_end(adutils_query_state_t **state)
1551 int rc = LDAP_SUCCESS;
1552 adutils_rc ad_rc = ADUTILS_SUCCESS;
1553 struct timeval tv;
1555 tv.tv_sec = ADUTILS_SEARCH_TIMEOUT;
1556 tv.tv_usec = 0;
1558 /* Process results until done or until timeout, if given */
1559 while ((*state)->qinflight > 0) {
1560 if ((rc = get_adobject_batch((*state)->qadh,
1561 &tv)) != 0)
1562 break;
1564 (*state)->qdead = 1;
1565 /* Wait for other threads processing search result to finish */
1566 adutils_lookup_batch_wait(*state);
1567 if (rc == -1 || (*state)->qinflight != 0)
1568 ad_rc = ADUTILS_ERR_RETRIABLE_NET_ERR;
1569 adutils_lookup_batch_release(state);
1570 return (ad_rc);
1574 * Send one prepared search, queue up msgid, process what results are
1575 * available
1577 adutils_rc
1578 adutils_lookup_batch_add(adutils_query_state_t *state,
1579 const char *filter, const char * const *attrs, const char *edomain,
1580 adutils_result_t **result, adutils_rc *rc)
1582 adutils_rc retcode = ADUTILS_SUCCESS;
1583 int lrc, qid;
1584 int num;
1585 int dead;
1586 struct timeval tv;
1587 adutils_q_t *q;
1589 qid = atomic_inc_32_nv(&state->qcount) - 1;
1590 q = &(state->queries[qid]);
1592 assert(qid < state->qsize);
1595 * Remember the expected domain so we can check the results
1596 * against it
1598 q->edomain = edomain;
1600 /* Remember where to put the results */
1601 q->result = result;
1602 q->rc = rc;
1605 * Provide sane defaults for the results in case we never hear
1606 * back from the DS before closing the connection.
1608 *rc = ADUTILS_ERR_RETRIABLE_NET_ERR;
1609 if (result != NULL)
1610 *result = NULL;
1612 /* Check the number of queued requests first */
1613 tv.tv_sec = ADUTILS_SEARCH_TIMEOUT;
1614 tv.tv_usec = 0;
1615 while (!state->qadh->dead &&
1616 state->qadh->num_requests > state->qadh->max_requests) {
1617 if (get_adobject_batch(state->qadh, &tv) != 0)
1618 break;
1621 /* Send this lookup, don't wait for a result here */
1622 lrc = LDAP_SUCCESS;
1623 (void) pthread_mutex_lock(&state->qadh->lock);
1625 if (!state->qadh->dead) {
1626 state->qadh->idletime = time(NULL);
1628 lrc = ldap_search_ext(state->qadh->ld,
1629 state->qadh->owner->basedn,
1630 LDAP_SCOPE_SUBTREE, filter, (char **)attrs,
1631 0, NULL, NULL, NULL, -1, &q->msgid);
1633 if (lrc == LDAP_SUCCESS) {
1634 state->qadh->num_requests++;
1635 } else if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE ||
1636 lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN ||
1637 lrc == LDAP_UNWILLING_TO_PERFORM) {
1638 retcode = ADUTILS_ERR_RETRIABLE_NET_ERR;
1639 state->qadh->dead = 1;
1640 } else {
1641 retcode = ADUTILS_ERR_OTHER;
1642 state->qadh->dead = 1;
1645 dead = state->qadh->dead;
1646 num = state->qadh->num_requests;
1647 (void) pthread_mutex_unlock(&state->qadh->lock);
1649 if (dead) {
1650 if (lrc != LDAP_SUCCESS)
1651 logger(LOG_DEBUG,
1652 "AD ldap_search_ext error (%s) "
1653 "- %d queued requests",
1654 ldap_err2string(lrc), num);
1655 return (retcode);
1658 atomic_inc_32(&state->qinflight);
1661 * Reap as many requests as we can _without_ waiting to prevent
1662 * any possible TCP socket buffer starvation deadlocks.
1664 (void) memset(&tv, 0, sizeof (tv));
1665 while (get_adobject_batch(state->qadh, &tv) == 0)
1668 return (ADUTILS_SUCCESS);
1672 * Single AD lookup request implemented on top of the batch API.
1674 adutils_rc
1675 adutils_lookup(adutils_ad_t *ad, const char *filter, const char **attrs,
1676 const char *domain, adutils_result_t **result)
1678 adutils_rc rc, brc;
1679 adutils_query_state_t *qs;
1681 rc = adutils_lookup_batch_start(ad, 1, NULL, NULL, &qs);
1682 if (rc != ADUTILS_SUCCESS)
1683 return (rc);
1685 rc = adutils_lookup_batch_add(qs, filter, attrs, domain, result, &brc);
1686 if (rc != ADUTILS_SUCCESS) {
1687 adutils_lookup_batch_release(&qs);
1688 return (rc);
1691 rc = adutils_lookup_batch_end(&qs);
1692 if (rc != ADUTILS_SUCCESS)
1693 return (rc);
1694 return (brc);
1697 boolean_t
1698 domain_eq(const char *a, const char *b)
1700 int err;
1702 return (u8_strcmp(a, b, 0, U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err)
1703 == 0 && err == 0);
1706 void
1707 adutils_set_debug(enum ad_debug item, int value)
1709 if (item >= 0 && item <= AD_DEBUG_MAX)
1710 ad_debug[item] = value;