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]
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
29 #include <sasl/sasl.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
{
63 char *(*ber2str
)(BerValue
*bvalues
);
66 static binary_attrs_t binattrs
[] = {
67 {"objectSID", adutils_sid_ber2str
},
72 adutils_logger logger
= syslog
;
76 adutils_set_logger(adutils_logger funct
)
83 * Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com"
87 adutils_dns2dn(const char *dns
)
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...).
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
)
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
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];
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) |
143 for (i
= 0; i
< sidp
->sub_authority_count
; i
++) {
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
;
156 * Convert a adutils_sid_t to S-1-...
159 adutils_sid2txt(adutils_sid_t
*sidp
)
164 if (sidp
->version
!= 1)
167 len
= sizeof ("S-1-") - 1;
170 * We could optimize like so, but, why?
171 * if (sidp->authority < 10)
173 * else if (sidp->authority < 100)
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
)
186 rlen
= snprintf(str
, len
, "S-1-%llu", sidp
->authority
);
191 for (i
= 0; i
< sidp
->sub_authority_count
; i
++) {
193 rlen
= snprintf(cp
, len
, "-%u", sidp
->sub_authorities
[i
]);
203 * Convert a adutils_sid_t to on-the-wire encoding
207 sid2binsid(adutils_sid_t
*sid
, uchar_t
*binsid
, int binsidlen
)
214 if (sid
->version
!= 1 ||
215 binsidlen
!= (1 + 1 + 6 + sid
->sub_authority_count
* 4))
219 *p
++ = 0x01; /* version */
220 /* sub authority count */
221 *p
++ = sid
->sub_authority_count
;
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;
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;
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 };
260 uchar_t
*binsid
, b
, hb
;
262 /* Only version 1 SIDs please */
263 if (strncmp(txt
, "S-1-", strlen("S-1-")) != 0)
266 if (strlen(txt
) < (strlen("S-1-") + 1))
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')
278 /* Adjust count for version and authority */
281 /* we know the version number and RID count */
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
)
290 /* check that we only have digits and '-' */
291 if (strspn(txt
+ 1, "0123456789-") < (strlen(txt
) - 1))
294 cp
= txt
+ strlen("S-1-");
296 /* 64-bit safe parsing of unsigned 48-bit authority value */
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
)
308 sid
.authority
= (uint64_t)a
;
310 for (i
= 0; i
< j
; i
++) {
313 /* 64-bit safe parsing of unsigned 32-bit RID */
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
)
321 sid
.sub_authorities
[i
] = (uint32_t)r
;
325 /* check that all of the string SID has been consumed */
330 sid
.sub_authorities
[j
] = *rid
;
332 j
= 1 + 1 + 6 + sid
.sub_authority_count
* 4;
334 if (hexbinsidlen
< (j
* 3))
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
++) {
346 *ecp
++ = (hb
<= 0x9 ? hb
+ '0' : hb
- 10 + 'A');
348 *ecp
++ = (hb
<= 0x9 ? hb
+ '0' : hb
- 10 + 'A');
357 convert_bval2sid(BerValue
*bval
, uint32_t *rid
)
361 if (adutils_getsid(bval
, &sid
) < 0)
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.
380 adutils_bv_objsid2sidstr(BerValue
*bval
, uint32_t *rid
)
386 /* objectSid is single valued */
387 if ((sid
= convert_bval2sid(bval
, rid
)) == NULL
)
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.
405 adutils_bv_uint(BerValue
*bval
, unsigned int *result
)
407 char buf
[40]; /* big enough for any int */
411 *result
= 0; /* for error cases */
413 if (bval
== NULL
|| bval
->bv_val
== NULL
)
415 if (bval
->bv_len
>= sizeof (buf
))
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? */
432 /* Return a NUL-terminated string from the Ber value */
434 adutils_bv_str(BerValue
*bval
)
438 if (bval
== NULL
|| bval
->bv_val
== NULL
)
440 if ((s
= malloc(bval
->bv_len
+ 1)) == NULL
)
442 (void) snprintf(s
, bval
->bv_len
+ 1, "%.*s", bval
->bv_len
,
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
;
459 interact
->result
= NULL
;
462 return (LDAP_SUCCESS
);
466 #define ADCONN_TIME 300
469 * Idle connection reaping side of connection management
472 adutils_reap_idle_connections()
477 (void) pthread_mutex_lock(&adhostlock
);
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
) {
484 (void) ldap_unbind(adh
->ld
);
490 (void) pthread_mutex_unlock(&adh
->lock
);
492 (void) pthread_mutex_unlock(&adhostlock
);
497 adutils_ad_alloc(adutils_ad_t
**new_ad
, const char *domain_name
,
498 adutils_ad_partition_t part
)
504 if ((ad
= calloc(1, sizeof (*ad
))) == NULL
)
505 return (ADUTILS_ERR_MEMORY
);
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
);
516 assert(domain_name
== NULL
);
517 ad
->basedn
= strdup("");
519 if (ad
->basedn
== NULL
)
522 if (pthread_mutex_init(&ad
->lock
, NULL
) != 0)
525 return (ADUTILS_SUCCESS
);
530 return (ADUTILS_ERR_MEMORY
);
534 adutils_ad_free(adutils_ad_t
**ad
)
537 adutils_host_t
*prev
;
539 if (ad
== NULL
|| *ad
== NULL
)
542 (void) pthread_mutex_lock(&(*ad
)->lock
);
544 if (atomic_dec_32_nv(&(*ad
)->ref
) > 0) {
545 (void) pthread_mutex_unlock(&(*ad
)->lock
);
550 (void) pthread_mutex_lock(&adhostlock
);
554 if (p
->owner
!= (*ad
)) {
559 delete_ds((*ad
), p
->host
, p
->port
);
566 (void) pthread_mutex_unlock(&adhostlock
);
568 (void) pthread_mutex_unlock(&(*ad
)->lock
);
569 (void) pthread_mutex_destroy(&(*ad
)->lock
);
571 if ((*ad
)->known_domains
)
572 free((*ad
)->known_domains
);
581 open_conn(adutils_host_t
*adh
, int timeoutsecs
)
585 int timeoutms
= timeoutsecs
* 1000;
590 (void) pthread_mutex_lock(&adh
->lock
);
592 if (!adh
->dead
&& adh
->ld
!= NULL
)
596 if (adh
->ld
!= NULL
) {
597 (void) ldap_unbind(adh
->ld
);
600 adh
->num_requests
= 0;
602 atomic_inc_64(&adh
->generation
);
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
));
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
);
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
);
628 rc
= ldap_sasl_interactive_bind_s(adh
->ld
, "" /* binddn */,
629 adh
->saslmech
, NULL
, NULL
, adh
->saslflags
, &saslcallback
,
632 if (rc
!= LDAP_SUCCESS
) {
633 (void) ldap_unbind(adh
->ld
);
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
));
641 logger(LOG_DEBUG
, "Using server %s:%d",
642 adh
->host
, adh
->port
);
645 if (adh
->ld
!= NULL
) {
646 atomic_inc_32(&adh
->ref
);
647 adh
->idletime
= time(NULL
);
649 (void) pthread_mutex_unlock(&adh
->lock
);
653 (void) pthread_mutex_unlock(&adh
->lock
);
659 * Connection management: find an open connection or open one
663 get_conn(adutils_ad_t
*ad
)
665 adutils_host_t
*adh
= NULL
;
668 int timeoutsecs
= ADUTILS_LDAP_OPEN_TIMEOUT
;
671 (void) pthread_mutex_lock(&adhostlock
);
673 if (host_head
== NULL
) {
674 (void) pthread_mutex_unlock(&adhostlock
);
680 * First try: count the number of DSes.
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.
686 for (adh
= host_head
, tries
= 0; adh
!= NULL
; adh
= adh
->next
) {
687 if (adh
->owner
== ad
)
692 (void) pthread_mutex_unlock(&adhostlock
);
696 tries
= dscount
* 3; /* three tries per-ds */
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
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.
712 if (adh
!= NULL
&& adh
->owner
== ad
&& adh
->ld
!= NULL
&&
715 if (adh
== NULL
|| (adh
= adh
->next
) == NULL
)
717 if (adh
->owner
== ad
)
722 (void) pthread_mutex_unlock(&adhostlock
);
724 /* Found suitable DS, open it if not already opened */
725 if (open_conn(adh
, timeoutsecs
))
729 if ((tries
% dscount
) == 0)
735 logger(LOG_NOTICE
, "Couldn't open an LDAP connection to any global "
742 release_conn(adutils_host_t
*adh
)
746 (void) pthread_mutex_lock(&adh
->lock
);
747 if (atomic_dec_32_nv(&adh
->ref
) == 0) {
748 if (adh
->owner
== NULL
)
750 adh
->idletime
= time(NULL
);
752 (void) pthread_mutex_unlock(&adh
->lock
);
754 /* Free this host if its owner no longer exists. */
756 (void) pthread_mutex_lock(&adhostlock
);
757 delete_ds(NULL
, adh
->host
, adh
->port
);
758 (void) pthread_mutex_unlock(&adhostlock
);
763 * Create a adutils_host_t, populate it and add it to the list of hosts.
766 adutils_add_ds(adutils_ad_t
*ad
, const char *host
, int port
)
769 adutils_host_t
*new = NULL
;
773 (void) pthread_mutex_lock(&adhostlock
);
774 for (p
= host_head
; p
!= NULL
; p
= p
->next
) {
778 if (strcmp(host
, p
->host
) == 0 && p
->port
== port
) {
780 rc
= ADUTILS_SUCCESS
;
785 rc
= ADUTILS_ERR_MEMORY
;
788 new = (adutils_host_t
*)calloc(1, sizeof (*new));
794 new->max_requests
= 80;
795 new->num_requests
= 0;
796 if ((new->host
= strdup(host
)) == NULL
)
798 new->saslflags
= LDAP_SASL_INTERACTIVE
;
799 new->saslmech
= "GSSAPI";
801 if ((ret
= pthread_mutex_init(&new->lock
, NULL
)) != 0) {
805 rc
= ADUTILS_ERR_INTERNAL
;
810 rc
= ADUTILS_SUCCESS
;
811 new->next
= host_head
;
815 (void) pthread_mutex_unlock(&adhostlock
);
817 if (rc
!= 0 && new != NULL
) {
818 if (new->host
!= NULL
) {
819 (void) pthread_mutex_destroy(&new->lock
);
829 * Free a DS configuration.
830 * Caller must lock the adhostlock mutex
834 delete_ds(adutils_ad_t
*ad
, const char *host
, int port
)
836 adutils_host_t
**p
, *q
;
838 for (p
= &host_head
; *p
!= NULL
; p
= &((*p
)->next
)) {
839 if ((*p
)->owner
!= ad
|| strcmp(host
, (*p
)->host
) != 0 ||
844 (void) pthread_mutex_lock(&((*p
)->lock
));
847 * Still in use. Set its owner to NULL so
848 * that it can be freed when its ref count
852 (void) pthread_mutex_unlock(&((*p
)->lock
));
855 (void) pthread_mutex_unlock(&((*p
)->lock
));
860 (void) pthread_mutex_destroy(&q
->lock
);
863 (void) ldap_unbind(q
->ld
);
872 * Add known domain name and domain SID to AD configuration.
876 adutils_add_domain(adutils_ad_t
*ad
, const char *domain
, const char *sid
)
878 struct known_domain
*new;
879 int num
= ad
->num_known_domains
;
881 ad
->num_known_domains
++;
882 new = realloc(ad
->known_domains
,
883 sizeof (struct known_domain
) * ad
->num_known_domains
);
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
);
892 if (ad
->known_domains
!= NULL
) {
893 free(ad
->known_domains
);
894 ad
->known_domains
= NULL
;
896 ad
->num_known_domains
= 0;
897 return (ADUTILS_ERR_MEMORY
);
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.
907 * Returns 1 if this domain is supported by this AD
912 adutils_lookup_check_domain(adutils_query_state_t
*qs
, const char *domain
)
914 adutils_ad_t
*ad
= qs
->qadh
->owner
;
917 for (i
= 0; i
< ad
->num_known_domains
; i
++) {
918 if (domain_eq(domain
, ad
->known_domains
[i
].name
))
922 return ((i
== 0) ? 1 : 0);
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.
932 * Returns 1 if this sid prefix is supported by this AD
937 adutils_lookup_check_sid_prefix(adutils_query_state_t
*qs
, const char *sid
)
939 adutils_ad_t
*ad
= qs
->qadh
->owner
;
943 for (i
= 0; i
< ad
->num_known_domains
; i
++) {
944 if (strcmp(sid
, ad
->known_domains
[i
].sid
) == 0)
948 return ((i
== 0) ? 1 : 0);
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
)
958 adutils_query_state_t
*new_state
;
959 adutils_host_t
*adh
= NULL
;
962 return (ADUTILS_ERR_INTERNAL
);
967 return (ADUTILS_ERR_RETRIABLE_NET_ERR
);
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
);
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
);
983 (void) pthread_mutex_lock(&qstatelock
);
984 new_state
->next
= qstatehead
;
985 qstatehead
= new_state
;
986 (void) pthread_mutex_unlock(&qstatelock
);
989 return (ADUTILS_SUCCESS
);
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.
1000 msgid2query(adutils_host_t
*adh
, int msgid
,
1001 adutils_query_state_t
**state
, int *qid
)
1003 adutils_query_state_t
*p
;
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
)
1011 for (i
= 0; i
< p
->qcount
; i
++) {
1012 if ((p
->queries
[i
]).msgid
== msgid
) {
1020 (void) pthread_mutex_unlock(&qstatelock
);
1025 (void) pthread_mutex_unlock(&qstatelock
);
1031 check_for_binary_attrs(const char *attr
)
1034 for (i
= 0; binattrs
[i
].name
!= NULL
; i
++) {
1035 if (strcasecmp(binattrs
[i
].name
, attr
) == 0)
1043 free_entry(adutils_entry_t
*entry
)
1050 if (entry
->attr_nvpairs
== NULL
) {
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
);
1060 if (check_for_binary_attrs(ap
->attr_name
) >= 0) {
1061 free(ap
->attr_name
);
1062 if (ap
->attr_values
== NULL
)
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
);
1072 free(ap
->attr_name
);
1073 ldap_value_free(ap
->attr_values
);
1076 free(entry
->attr_nvpairs
);
1081 adutils_freeresult(adutils_result_t
**result
)
1083 adutils_entry_t
*e
, *next
;
1085 if (result
== NULL
|| *result
== NULL
)
1087 if ((*result
)->entries
== NULL
) {
1092 for (e
= (*result
)->entries
; e
!= NULL
; e
= next
) {
1100 const adutils_entry_t
*
1101 adutils_getfirstentry(adutils_result_t
*result
)
1104 return (result
->entries
);
1110 adutils_getattr(const adutils_entry_t
*entry
, const char *attrname
)
1115 if (entry
== NULL
|| entry
->attr_nvpairs
== 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
);
1128 * Queue LDAP result for the given query.
1137 make_entry(adutils_q_t
*q
, adutils_host_t
*adh
, LDAPMessage
*search_res
,
1138 adutils_entry_t
**entry
)
1140 BerElement
*ber
= NULL
;
1141 BerValue
**bvalues
= NULL
;
1143 char *attr
= NULL
, *dn
= NULL
, *domain
= NULL
;
1144 adutils_entry_t
*ep
;
1146 int i
, j
, b
, ret
= -2;
1150 /* Check that this is the domain that we were looking for */
1151 if ((dn
= ldap_get_dn(adh
->ld
, search_res
)) == NULL
)
1153 if ((domain
= adutils_dn2dns(dn
)) == NULL
) {
1157 if (q
->edomain
!= NULL
) {
1158 if (!domain_eq(q
->edomain
, domain
)) {
1166 /* Allocate memory for the entry */
1167 if ((ep
= calloc(1, sizeof (*ep
))) == NULL
)
1171 ep
->num_nvpairs
= 1;
1173 /* Count the number of name-value pairs for this entry */
1174 for (attr
= ldap_first_attribute(adh
->ld
, search_res
, &ber
);
1176 attr
= ldap_next_attribute(adh
->ld
, search_res
, ber
)) {
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;
1191 ap
= &ep
->attr_nvpairs
[0];
1192 if ((ap
->attr_name
= strdup("dn")) == NULL
)
1195 ap
->attr_values
= calloc(ap
->num_values
, sizeof (*ap
->attr_values
));
1196 if (ap
->attr_values
== NULL
) {
1200 ap
->attr_values
[0] = dn
;
1203 for (attr
= ldap_first_attribute(adh
->ld
, search_res
, &ber
), i
= 1;
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
)
1211 if ((b
= check_for_binary_attrs(attr
)) >= 0) {
1213 ldap_get_values_len(adh
->ld
, search_res
, attr
);
1214 if (bvalues
== NULL
)
1216 ap
->num_values
= ldap_count_values_len(bvalues
);
1217 if (ap
->num_values
== 0) {
1218 ldap_value_free_len(bvalues
);
1222 ap
->attr_values
= calloc(ap
->num_values
,
1223 sizeof (*ap
->attr_values
));
1224 if (ap
->attr_values
== NULL
) {
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
)
1234 ldap_value_free_len(bvalues
);
1239 strvalues
= ldap_get_values(adh
->ld
, search_res
, attr
);
1240 if (strvalues
== NULL
)
1242 ap
->num_values
= ldap_count_values(strvalues
);
1243 if (ap
->num_values
== 0) {
1244 ldap_value_free(strvalues
);
1247 ap
->attr_values
= strvalues
;
1255 ldap_value_free_len(bvalues
);
1264 * Put the search result onto the given adutils_q_t.
1265 * Returns: 0 success
1270 add_entry(adutils_host_t
*adh
, adutils_q_t
*q
, LDAPMessage
*search_res
)
1273 adutils_entry_t
*entry
= NULL
;
1274 adutils_result_t
*res
;
1276 ret
= make_entry(q
, adh
, search_res
, &entry
);
1278 *q
->rc
= ADUTILS_ERR_MEMORY
;
1280 } else if (ret
== -1) {
1284 if (*q
->result
== NULL
) {
1285 res
= calloc(1, sizeof (*res
));
1287 *q
->rc
= ADUTILS_ERR_MEMORY
;
1290 res
->num_entries
= 1;
1291 res
->entries
= entry
;
1295 entry
->next
= res
->entries
;
1296 res
->entries
= entry
;
1299 *q
->rc
= ADUTILS_SUCCESS
;
1309 * Try to get a result; if there is one, find the corresponding
1310 * adutils_q_t and process the result.
1312 * Returns: 0 success
1317 get_adobject_batch(adutils_host_t
*adh
, struct timeval
*timeout
)
1319 adutils_query_state_t
*query_state
;
1320 LDAPMessage
*res
= NULL
;
1321 int rc
, ret
, msgid
, qid
;
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
);
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
) ||
1338 if (rc
== LDAP_RES_SEARCH_RESULT
&& adh
->num_requests
> 0)
1339 adh
->num_requests
--;
1341 num
= adh
->num_requests
;
1342 (void) pthread_mutex_unlock(&adh
->lock
);
1344 "AD ldap_result error - %d queued requests", num
);
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
) {
1354 * We use the caller-provided callback
1355 * to process the result.
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
);
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.
1371 (void) pthread_mutex_unlock(&adh
->lock
);
1372 que
= &(query_state
->queries
[qid
]);
1373 if (*que
->result
== NULL
)
1374 *que
->rc
= ADUTILS_ERR_NOTFOUND
;
1376 atomic_dec_32(&query_state
->qinflight
);
1377 adutils_lookup_batch_unlock(&query_state
);
1379 num
= adh
->num_requests
;
1380 (void) pthread_mutex_unlock(&adh
->lock
);
1382 "AD cannot find message ID (%d) "
1383 "- %d queued requests",
1386 (void) ldap_msgfree(res
);
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
) {
1395 * We use the caller-provided callback
1396 * to process the entry.
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
);
1404 * No callback. We fallback to our
1405 * default behaviour. This entry
1406 * will be added to the result list.
1408 que
= &(query_state
->queries
[qid
]);
1409 rc
= add_entry(adh
, que
, res
);
1410 (void) pthread_mutex_unlock(&adh
->lock
);
1413 "Failed to queue entry by "
1415 "- %d queued requests",
1419 adutils_lookup_batch_unlock(&query_state
);
1421 num
= adh
->num_requests
;
1422 (void) pthread_mutex_unlock(&adh
->lock
);
1424 "AD cannot find message ID (%d) "
1425 "- %d queued requests",
1428 (void) ldap_msgfree(res
);
1432 case LDAP_RES_SEARCH_REFERENCE
:
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
1439 (void) pthread_mutex_unlock(&adh
->lock
);
1440 (void) ldap_msgfree(res
);
1445 /* timeout or error; treat the same */
1446 (void) pthread_mutex_unlock(&adh
->lock
);
1455 * This routine decreament the reference count of the
1456 * adutils_query_state_t
1459 adutils_lookup_batch_unlock(adutils_query_state_t
**state
)
1462 * Decrement reference count with qstatelock locked
1464 (void) pthread_mutex_lock(&qstatelock
);
1465 (*state
)->ref_cnt
--;
1467 * If there are no references wakup the allocating thread
1469 if ((*state
)->ref_cnt
<= 1)
1470 (void) pthread_cond_signal(&(*state
)->cv
);
1471 (void) pthread_mutex_unlock(&qstatelock
);
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.
1481 adutils_lookup_batch_release(adutils_query_state_t
**state
)
1483 adutils_query_state_t
**p
;
1486 if (state
== NULL
|| *state
== NULL
)
1490 * Set state to dead to stop further operations.
1491 * Wait for reference count with qstatelock locked
1494 (void) pthread_mutex_lock(&qstatelock
);
1495 (*state
)->qdead
= 1;
1496 while ((*state
)->ref_cnt
> 1) {
1497 (void) pthread_cond_wait(&(*state
)->cv
, &qstatelock
);
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
;
1507 (void) pthread_mutex_unlock(&qstatelock
);
1508 (void) pthread_cond_destroy(&(*state
)->cv
);
1509 release_conn((*state
)->qadh
);
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
);
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.
1530 adutils_lookup_batch_wait(adutils_query_state_t
*state
)
1533 * Set state to dead to stop further operation.
1535 * Wait for reference count to get to one
1536 * with qstatelock locked.
1538 (void) pthread_mutex_lock(&qstatelock
);
1540 while (state
->ref_cnt
> 1) {
1541 (void) pthread_cond_wait(&state
->cv
, &qstatelock
);
1543 (void) pthread_mutex_unlock(&qstatelock
);
1547 * Process active queries in the AD lookup batch and then finalize the
1551 adutils_lookup_batch_end(adutils_query_state_t
**state
)
1553 int rc
= LDAP_SUCCESS
;
1554 adutils_rc ad_rc
= ADUTILS_SUCCESS
;
1557 tv
.tv_sec
= ADUTILS_SEARCH_TIMEOUT
;
1560 /* Process results until done or until timeout, if given */
1561 while ((*state
)->qinflight
> 0) {
1562 if ((rc
= get_adobject_batch((*state
)->qadh
,
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
);
1576 * Send one prepared search, queue up msgid, process what results are
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
)
1584 adutils_rc retcode
= ADUTILS_SUCCESS
;
1591 qid
= atomic_inc_32_nv(&state
->qcount
) - 1;
1592 q
= &(state
->queries
[qid
]);
1594 assert(qid
< state
->qsize
);
1597 * Remember the expected domain so we can check the results
1600 q
->edomain
= edomain
;
1602 /* Remember where to put the results */
1607 * Provide sane defaults for the results in case we never hear
1608 * back from the DS before closing the connection.
1610 *rc
= ADUTILS_ERR_RETRIABLE_NET_ERR
;
1614 /* Check the number of queued requests first */
1615 tv
.tv_sec
= ADUTILS_SEARCH_TIMEOUT
;
1617 while (!state
->qadh
->dead
&&
1618 state
->qadh
->num_requests
> state
->qadh
->max_requests
) {
1619 if (get_adobject_batch(state
->qadh
, &tv
) != 0)
1623 /* Send this lookup, don't wait for a result here */
1625 (void) pthread_mutex_lock(&state
->qadh
->lock
);
1627 if (!state
->qadh
->dead
) {
1628 state
->qadh
->idletime
= time(NULL
);
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
);
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;
1643 retcode
= ADUTILS_ERR_OTHER
;
1644 state
->qadh
->dead
= 1;
1647 dead
= state
->qadh
->dead
;
1648 num
= state
->qadh
->num_requests
;
1649 (void) pthread_mutex_unlock(&state
->qadh
->lock
);
1652 if (lrc
!= LDAP_SUCCESS
)
1654 "AD ldap_search_ext error (%s) "
1655 "- %d queued requests",
1656 ldap_err2string(lrc
), num
);
1660 atomic_inc_32(&state
->qinflight
);
1663 * Reap as many requests as we can _without_ waiting to prevent
1664 * any possible TCP socket buffer starvation deadlocks.
1666 (void) memset(&tv
, 0, sizeof (tv
));
1667 while (get_adobject_batch(state
->qadh
, &tv
) == 0)
1670 return (ADUTILS_SUCCESS
);
1674 * Single AD lookup request implemented on top of the batch API.
1677 adutils_lookup(adutils_ad_t
*ad
, const char *filter
, const char **attrs
,
1678 const char *domain
, adutils_result_t
**result
)
1681 adutils_query_state_t
*qs
;
1683 rc
= adutils_lookup_batch_start(ad
, 1, NULL
, NULL
, &qs
);
1684 if (rc
!= ADUTILS_SUCCESS
)
1687 rc
= adutils_lookup_batch_add(qs
, filter
, attrs
, domain
, result
, &brc
);
1688 if (rc
!= ADUTILS_SUCCESS
) {
1689 adutils_lookup_batch_release(&qs
);
1693 rc
= adutils_lookup_batch_end(&qs
);
1694 if (rc
!= ADUTILS_SUCCESS
)
1700 domain_eq(const char *a
, const char *b
)
1704 return (u8_strcmp(a
, b
, 0, U8_STRCMP_CI_LOWER
, U8_UNICODE_LATEST
, &err
)
1709 adutils_set_debug(enum ad_debug item
, int value
)
1711 if (item
>= 0 && item
<= AD_DEBUG_MAX
)
1712 ad_debug
[item
] = value
;