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 free((*ad
)->known_domains
);
580 open_conn(adutils_host_t
*adh
, int timeoutsecs
)
584 int timeoutms
= timeoutsecs
* 1000;
589 (void) pthread_mutex_lock(&adh
->lock
);
591 if (!adh
->dead
&& adh
->ld
!= NULL
)
595 if (adh
->ld
!= NULL
) {
596 (void) ldap_unbind(adh
->ld
);
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
));
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
);
627 rc
= ldap_sasl_interactive_bind_s(adh
->ld
, "" /* binddn */,
628 adh
->saslmech
, NULL
, NULL
, adh
->saslflags
, &saslcallback
,
631 if (rc
!= LDAP_SUCCESS
) {
632 (void) ldap_unbind(adh
->ld
);
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
));
640 logger(LOG_DEBUG
, "Using server %s:%d",
641 adh
->host
, adh
->port
);
644 if (adh
->ld
!= NULL
) {
645 atomic_inc_32(&adh
->ref
);
646 adh
->idletime
= time(NULL
);
648 (void) pthread_mutex_unlock(&adh
->lock
);
652 (void) pthread_mutex_unlock(&adh
->lock
);
658 * Connection management: find an open connection or open one
662 get_conn(adutils_ad_t
*ad
)
664 adutils_host_t
*adh
= NULL
;
667 int timeoutsecs
= ADUTILS_LDAP_OPEN_TIMEOUT
;
670 (void) pthread_mutex_lock(&adhostlock
);
672 if (host_head
== NULL
) {
673 (void) pthread_mutex_unlock(&adhostlock
);
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
)
691 (void) pthread_mutex_unlock(&adhostlock
);
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
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.
711 if (adh
!= NULL
&& adh
->owner
== ad
&& adh
->ld
!= NULL
&&
714 if (adh
== NULL
|| (adh
= adh
->next
) == NULL
)
716 if (adh
->owner
== ad
)
721 (void) pthread_mutex_unlock(&adhostlock
);
723 /* Found suitable DS, open it if not already opened */
724 if (open_conn(adh
, timeoutsecs
))
728 if ((tries
% dscount
) == 0)
734 logger(LOG_NOTICE
, "Couldn't open an LDAP connection to any global "
741 release_conn(adutils_host_t
*adh
)
745 (void) pthread_mutex_lock(&adh
->lock
);
746 if (atomic_dec_32_nv(&adh
->ref
) == 0) {
747 if (adh
->owner
== NULL
)
749 adh
->idletime
= time(NULL
);
751 (void) pthread_mutex_unlock(&adh
->lock
);
753 /* Free this host if its owner no longer exists. */
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.
765 adutils_add_ds(adutils_ad_t
*ad
, const char *host
, int port
)
768 adutils_host_t
*new = NULL
;
772 (void) pthread_mutex_lock(&adhostlock
);
773 for (p
= host_head
; p
!= NULL
; p
= p
->next
) {
777 if (strcmp(host
, p
->host
) == 0 && p
->port
== port
) {
779 rc
= ADUTILS_SUCCESS
;
784 rc
= ADUTILS_ERR_MEMORY
;
787 new = (adutils_host_t
*)calloc(1, sizeof (*new));
793 new->max_requests
= 80;
794 new->num_requests
= 0;
795 if ((new->host
= strdup(host
)) == NULL
)
797 new->saslflags
= LDAP_SASL_INTERACTIVE
;
798 new->saslmech
= "GSSAPI";
800 if ((ret
= pthread_mutex_init(&new->lock
, NULL
)) != 0) {
804 rc
= ADUTILS_ERR_INTERNAL
;
809 rc
= ADUTILS_SUCCESS
;
810 new->next
= host_head
;
814 (void) pthread_mutex_unlock(&adhostlock
);
816 if (rc
!= 0 && new != NULL
) {
817 if (new->host
!= NULL
) {
818 (void) pthread_mutex_destroy(&new->lock
);
828 * Free a DS configuration.
829 * Caller must lock the adhostlock mutex
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 ||
843 (void) pthread_mutex_lock(&((*p
)->lock
));
846 * Still in use. Set its owner to NULL so
847 * that it can be freed when its ref count
851 (void) pthread_mutex_unlock(&((*p
)->lock
));
854 (void) pthread_mutex_unlock(&((*p
)->lock
));
859 (void) pthread_mutex_destroy(&q
->lock
);
862 (void) ldap_unbind(q
->ld
);
870 * Add known domain name and domain SID to AD configuration.
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
));
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
);
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
910 adutils_lookup_check_domain(adutils_query_state_t
*qs
, const char *domain
)
912 adutils_ad_t
*ad
= qs
->qadh
->owner
;
915 for (i
= 0; i
< ad
->num_known_domains
; i
++) {
916 if (domain_eq(domain
, ad
->known_domains
[i
].name
))
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
935 adutils_lookup_check_sid_prefix(adutils_query_state_t
*qs
, const char *sid
)
937 adutils_ad_t
*ad
= qs
->qadh
->owner
;
941 for (i
= 0; i
< ad
->num_known_domains
; i
++) {
942 if (strcmp(sid
, ad
->known_domains
[i
].sid
) == 0)
946 return ((i
== 0) ? 1 : 0);
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
;
960 return (ADUTILS_ERR_INTERNAL
);
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
);
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.
998 msgid2query(adutils_host_t
*adh
, int msgid
,
999 adutils_query_state_t
**state
, int *qid
)
1001 adutils_query_state_t
*p
;
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
)
1009 for (i
= 0; i
< p
->qcount
; i
++) {
1010 if ((p
->queries
[i
]).msgid
== msgid
) {
1018 (void) pthread_mutex_unlock(&qstatelock
);
1023 (void) pthread_mutex_unlock(&qstatelock
);
1029 check_for_binary_attrs(const char *attr
)
1032 for (i
= 0; binattrs
[i
].name
!= NULL
; i
++) {
1033 if (strcasecmp(binattrs
[i
].name
, attr
) == 0)
1041 free_entry(adutils_entry_t
*entry
)
1048 if (entry
->attr_nvpairs
== NULL
) {
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
);
1058 if (check_for_binary_attrs(ap
->attr_name
) >= 0) {
1059 free(ap
->attr_name
);
1060 if (ap
->attr_values
== NULL
)
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
);
1070 free(ap
->attr_name
);
1071 ldap_value_free(ap
->attr_values
);
1074 free(entry
->attr_nvpairs
);
1079 adutils_freeresult(adutils_result_t
**result
)
1081 adutils_entry_t
*e
, *next
;
1083 if (result
== NULL
|| *result
== NULL
)
1085 if ((*result
)->entries
== NULL
) {
1090 for (e
= (*result
)->entries
; e
!= NULL
; e
= next
) {
1098 const adutils_entry_t
*
1099 adutils_getfirstentry(adutils_result_t
*result
)
1102 return (result
->entries
);
1108 adutils_getattr(const adutils_entry_t
*entry
, const char *attrname
)
1113 if (entry
== NULL
|| entry
->attr_nvpairs
== 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
);
1126 * Queue LDAP result for the given query.
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
;
1141 char *attr
= NULL
, *dn
= NULL
, *domain
= NULL
;
1142 adutils_entry_t
*ep
;
1144 int i
, j
, b
, ret
= -2;
1148 /* Check that this is the domain that we were looking for */
1149 if ((dn
= ldap_get_dn(adh
->ld
, search_res
)) == NULL
)
1151 if ((domain
= adutils_dn2dns(dn
)) == NULL
) {
1155 if (q
->edomain
!= NULL
) {
1156 if (!domain_eq(q
->edomain
, domain
)) {
1164 /* Allocate memory for the entry */
1165 if ((ep
= calloc(1, sizeof (*ep
))) == NULL
)
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
);
1174 attr
= ldap_next_attribute(adh
->ld
, search_res
, ber
)) {
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;
1189 ap
= &ep
->attr_nvpairs
[0];
1190 if ((ap
->attr_name
= strdup("dn")) == NULL
)
1193 ap
->attr_values
= calloc(ap
->num_values
, sizeof (*ap
->attr_values
));
1194 if (ap
->attr_values
== NULL
) {
1198 ap
->attr_values
[0] = dn
;
1201 for (attr
= ldap_first_attribute(adh
->ld
, search_res
, &ber
), i
= 1;
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
)
1209 if ((b
= check_for_binary_attrs(attr
)) >= 0) {
1211 ldap_get_values_len(adh
->ld
, search_res
, attr
);
1212 if (bvalues
== NULL
)
1214 ap
->num_values
= ldap_count_values_len(bvalues
);
1215 if (ap
->num_values
== 0) {
1216 ldap_value_free_len(bvalues
);
1220 ap
->attr_values
= calloc(ap
->num_values
,
1221 sizeof (*ap
->attr_values
));
1222 if (ap
->attr_values
== NULL
) {
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
)
1232 ldap_value_free_len(bvalues
);
1237 strvalues
= ldap_get_values(adh
->ld
, search_res
, attr
);
1238 if (strvalues
== NULL
)
1240 ap
->num_values
= ldap_count_values(strvalues
);
1241 if (ap
->num_values
== 0) {
1242 ldap_value_free(strvalues
);
1245 ap
->attr_values
= strvalues
;
1253 ldap_value_free_len(bvalues
);
1262 * Put the search result onto the given adutils_q_t.
1263 * Returns: 0 success
1268 add_entry(adutils_host_t
*adh
, adutils_q_t
*q
, LDAPMessage
*search_res
)
1271 adutils_entry_t
*entry
= NULL
;
1272 adutils_result_t
*res
;
1274 ret
= make_entry(q
, adh
, search_res
, &entry
);
1276 *q
->rc
= ADUTILS_ERR_MEMORY
;
1278 } else if (ret
== -1) {
1282 if (*q
->result
== NULL
) {
1283 res
= calloc(1, sizeof (*res
));
1285 *q
->rc
= ADUTILS_ERR_MEMORY
;
1288 res
->num_entries
= 1;
1289 res
->entries
= entry
;
1293 entry
->next
= res
->entries
;
1294 res
->entries
= entry
;
1297 *q
->rc
= ADUTILS_SUCCESS
;
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
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
;
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
);
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
) ||
1336 if (rc
== LDAP_RES_SEARCH_RESULT
&& adh
->num_requests
> 0)
1337 adh
->num_requests
--;
1339 num
= adh
->num_requests
;
1340 (void) pthread_mutex_unlock(&adh
->lock
);
1342 "AD ldap_result error - %d queued requests", num
);
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
);
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
);
1377 num
= adh
->num_requests
;
1378 (void) pthread_mutex_unlock(&adh
->lock
);
1380 "AD cannot find message ID (%d) "
1381 "- %d queued requests",
1384 (void) ldap_msgfree(res
);
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
);
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
);
1411 "Failed to queue entry by "
1413 "- %d queued requests",
1417 adutils_lookup_batch_unlock(&query_state
);
1419 num
= adh
->num_requests
;
1420 (void) pthread_mutex_unlock(&adh
->lock
);
1422 "AD cannot find message ID (%d) "
1423 "- %d queued requests",
1426 (void) ldap_msgfree(res
);
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
1437 (void) pthread_mutex_unlock(&adh
->lock
);
1438 (void) ldap_msgfree(res
);
1443 /* timeout or error; treat the same */
1444 (void) pthread_mutex_unlock(&adh
->lock
);
1453 * This routine decreament the reference count of the
1454 * adutils_query_state_t
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
);
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.
1479 adutils_lookup_batch_release(adutils_query_state_t
**state
)
1481 adutils_query_state_t
**p
;
1484 if (state
== NULL
|| *state
== NULL
)
1488 * Set state to dead to stop further operations.
1489 * Wait for reference count with qstatelock locked
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
;
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
);
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.
1528 adutils_lookup_batch_wait(adutils_query_state_t
*state
)
1531 * Set state to dead to stop further operation.
1533 * Wait for reference count to get to one
1534 * with qstatelock locked.
1536 (void) pthread_mutex_lock(&qstatelock
);
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
1549 adutils_lookup_batch_end(adutils_query_state_t
**state
)
1551 int rc
= LDAP_SUCCESS
;
1552 adutils_rc ad_rc
= ADUTILS_SUCCESS
;
1555 tv
.tv_sec
= ADUTILS_SEARCH_TIMEOUT
;
1558 /* Process results until done or until timeout, if given */
1559 while ((*state
)->qinflight
> 0) {
1560 if ((rc
= get_adobject_batch((*state
)->qadh
,
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
);
1574 * Send one prepared search, queue up msgid, process what results are
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
;
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
1598 q
->edomain
= edomain
;
1600 /* Remember where to put the results */
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
;
1612 /* Check the number of queued requests first */
1613 tv
.tv_sec
= ADUTILS_SEARCH_TIMEOUT
;
1615 while (!state
->qadh
->dead
&&
1616 state
->qadh
->num_requests
> state
->qadh
->max_requests
) {
1617 if (get_adobject_batch(state
->qadh
, &tv
) != 0)
1621 /* Send this lookup, don't wait for a result here */
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;
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
);
1650 if (lrc
!= LDAP_SUCCESS
)
1652 "AD ldap_search_ext error (%s) "
1653 "- %d queued requests",
1654 ldap_err2string(lrc
), num
);
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.
1675 adutils_lookup(adutils_ad_t
*ad
, const char *filter
, const char **attrs
,
1676 const char *domain
, adutils_result_t
**result
)
1679 adutils_query_state_t
*qs
;
1681 rc
= adutils_lookup_batch_start(ad
, 1, NULL
, NULL
, &qs
);
1682 if (rc
!= ADUTILS_SUCCESS
)
1685 rc
= adutils_lookup_batch_add(qs
, filter
, attrs
, domain
, result
, &brc
);
1686 if (rc
!= ADUTILS_SUCCESS
) {
1687 adutils_lookup_batch_release(&qs
);
1691 rc
= adutils_lookup_batch_end(&qs
);
1692 if (rc
!= ADUTILS_SUCCESS
)
1698 domain_eq(const char *a
, const char *b
)
1702 return (u8_strcmp(a
, b
, 0, U8_STRCMP_CI_LOWER
, U8_UNICODE_LATEST
, &err
)
1707 adutils_set_debug(enum ad_debug item
, int value
)
1709 if (item
>= 0 && item
<= AD_DEBUG_MAX
)
1710 ad_debug
[item
] = value
;