/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <alloca.h>
#include <string.h>
#include <strings.h>
#include <lber.h>
#include <string.h>
#include <ctype.h>
#include <synch.h>
#include <atomic.h>
#include <errno.h>
#include <assert.h>
#include <limits.h>
#include <syslog.h>
#include <sys/u8_textprep.h>
#include "libadutils.h"
#include "adutils_impl.h"
#include "ldappr.h"
/* List of DSs, needed by the idle connection reaper thread */
/*
* List of query state structs -- needed so we can "route" LDAP results
* to the right context if multiple threads should be using the same
* connection concurrently
*/
typedef struct binary_attrs {
const char *name;
{"objectSID", adutils_sid_ber2str},
};
void
{
}
/*
* Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com"
*/
static
char *
{
int num_parts;
}
/*
* Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other
* attributes (CN, etc...).
*/
char *
{
}
/*
* Convert a binary SID in a BerValue to a adutils_sid_t
*/
int
{
int i, j;
uchar_t *v;
uint32_t a;
/*
* The binary format of a SID is as follows:
*
* byte #0: version, always 0x01
* byte #1: RID count, always <= 0x0f
* bytes #2-#7: SID authority, big-endian 48-bit unsigned int
*
* followed by RID count RIDs, each a little-endian, unsigned
* 32-bit int.
*/
/*
* Sanity checks: must have at least one RID, version must be
* 0x01, and the length must be 8 + rid count * 4
*/
/* big endian -- so start from the left */
(u_longlong_t)v[7];
for (i = 0; i < sidp->sub_authority_count; i++) {
j = 8 + (i * 4);
/* little endian -- so start from the right */
a = (v[j + 3] << 24) | (v[j + 2] << 16) |
(v[j + 1] << 8) | (v[j]);
sidp->sub_authorities[i] = a;
}
return (0);
}
return (-1);
}
/*
* Convert a adutils_sid_t to S-1-...
*/
char *
{
return (NULL);
/*
* We could optimize like so, but, why?
* if (sidp->authority < 10)
* len += 2;
* else if (sidp->authority < 100)
* len += 3;
* else
* len += snprintf(NULL, 0"%llu", sidp->authority);
*/
/* Max length of a uint32_t printed out in ASCII is 10 bytes */
return (NULL);
for (i = 0; i < sidp->sub_authority_count; i++) {
}
return (str);
}
/*
* Convert a adutils_sid_t to on-the-wire encoding
*/
static
int
{
uchar_t *p;
int i;
uint64_t a;
uint32_t r;
return (-1);
p = binsid;
*p++ = 0x01; /* version */
/* sub authority count */
*p++ = sid->sub_authority_count;
/* Authority */
/* big-endian -- start from left */
*p++ = (a >> 40) & 0xFF;
*p++ = (a >> 32) & 0xFF;
*p++ = (a >> 24) & 0xFF;
*p++ = (a >> 16) & 0xFF;
*p++ = (a >> 8) & 0xFF;
*p++ = a & 0xFF;
/* sub-authorities */
for (i = 0; i < sid->sub_authority_count; i++) {
r = sid->sub_authorities[i];
/* little-endian -- start from right */
*p++ = (r & 0x000000FF);
*p++ = (r & 0x0000FF00) >> 8;
*p++ = (r & 0x00FF0000) >> 16;
*p++ = (r & 0xFF000000) >> 24;
}
return (0);
}
/*
* Convert a stringified SID (S-1-...) into a hex-encoded version of the
* on-the-wire encoding, but with each pair of hex digits pre-pended
* with a '\', so we can pass this to libldap.
*/
int
char *hexbinsid, int hexbinsidlen)
{
int i, j;
const char *cp;
char *ecp;
u_longlong_t a;
unsigned long r;
/* Only version 1 SIDs please */
return (-1);
return (-1);
/* count '-'s */
/* can't end on a '-' */
return (-1);
}
/* Adjust count for version and authority */
j -= 2;
/* we know the version number and RID count */
/* must have at least one RID, but not too many */
return (-1);
/* check that we only have digits and '-' */
return (-1);
/* 64-bit safe parsing of unsigned 48-bit authority value */
errno = 0;
/* errors parsing the authority or too many bits */
(a & 0x0000ffffffffffffULL) != a)
return (-1);
for (i = 0; i < j; i++) {
if (*cp++ != '-')
return (-1);
/* 64-bit safe parsing of unsigned 32-bit RID */
errno = 0;
/* errors parsing the RID or too many bits */
(r & 0xffffffffUL) != r)
return (-1);
}
/* check that all of the string SID has been consumed */
if (*cp != '\0')
return (-1);
if (hexbinsidlen < (j * 3))
return (-2);
/* binary encode the SID */
/* hex encode, with a backslash before each byte */
b = binsid[i];
*ecp++ = '\\';
hb = b & 0xF;
}
*ecp = '\0';
return (0);
}
static
char *
{
return (NULL);
/*
* save the last RID and truncate the SID
*/
return (adutils_sid2txt(&sid));
}
/*
* Return a NUL-terminated stringified SID from the value of an
* objectSid attribute and put the last RID in *rid.
*/
char *
{
char *sid;
return (NULL);
/* objectSid is single valued */
return (NULL);
return (sid);
}
static
char *
{
}
/*
* Extract an int from the Ber value
* Return B_TRUE if a valid integer was found, B_FALSE if not.
*/
{
unsigned int tmp;
char *p;
*result = 0; /* for error cases */
return (B_FALSE);
return (B_FALSE);
/* Junk after the number? */
if (*p != '\0')
return (B_FALSE);
return (B_TRUE);
}
/* Return a NUL-terminated string from the Ber value */
char *
{
char *s;
return (NULL);
return (NULL);
return (s);
}
/*ARGSUSED*/
int
{
return (LDAP_PARAM_ERROR);
interact++) {
}
return (LDAP_SUCCESS);
}
/*
* Idle connection reaping side of connection management
*/
void
{
(void) pthread_mutex_lock(&adhostlock);
}
}
}
(void) pthread_mutex_unlock(&adhostlock);
}
{
return (ADUTILS_ERR_MEMORY);
/* domain_name is required iff we are talking directly to a DC */
if (part == ADUTILS_AD_DATA) {
} else {
}
goto err;
goto err;
return (ADUTILS_SUCCESS);
err:
return (ADUTILS_ERR_MEMORY);
}
void
{
adutils_host_t *p;
return;
return;
}
(void) pthread_mutex_lock(&adhostlock);
p = host_head;
while (p != NULL) {
prev = p;
p = p->next;
continue;
} else {
p = host_head;
else
}
}
(void) pthread_mutex_unlock(&adhostlock);
if ((*ad)->known_domains)
}
static
int
{
int zero = 0;
return (0);
/* done! */
goto out;
}
adh->num_requests = 0;
/* Open and bind an LDAP connection */
goto out;
}
if (rc != LDAP_SUCCESS) {
/* Error has already been logged */
goto out;
}
/*
* Enforce I/O timeout to protect SASL bind from hang.
*/
NULL);
if (rc != LDAP_SUCCESS) {
goto out;
}
out:
return (1);
}
return (0);
}
/*
* Connection management: find an open connection or open one
*/
static
{
int tries;
int dscount = 0;
(void) pthread_mutex_lock(&adhostlock);
(void) pthread_mutex_unlock(&adhostlock);
goto out;
}
if (dscount == 0) {
/*
* First try: count the number of DSes.
*
* Integer overflow is not an issue -- we can't have so many
* DSes because they won't fit even DNS over TCP, and SMF
* shouldn't let you set so many.
*/
dscount++;
}
if (dscount == 0) {
(void) pthread_mutex_unlock(&adhostlock);
goto out;
}
/*
* Begin round-robin at the next DS in the list after the last
* one that we had a connection to, else start with the first
* DS in the list.
*/
}
/*
* Round-robin -- pick the next one on the list; if the list
* changes on us, no big deal, we'll just potentially go
* around the wrong number of times.
*/
for (;;) {
break;
break;
}
(void) pthread_mutex_unlock(&adhostlock);
/* Found suitable DS, open it if not already opened */
return (adh);
tries--;
timeoutsecs *= 2;
if (tries > 0)
goto retry;
out:
"catalog server!");
return (NULL);
}
static
void
{
int delete = 0;
delete = 1;
}
/* Free this host if its owner no longer exists. */
if (delete) {
(void) pthread_mutex_lock(&adhostlock);
(void) pthread_mutex_unlock(&adhostlock);
}
}
/*
* Create a adutils_host_t, populate it and add it to the list of hosts.
*/
{
adutils_host_t *p;
int ret;
(void) pthread_mutex_lock(&adhostlock);
continue;
/* already added */
goto err;
}
}
/* add new entry */
goto err;
new->num_requests = 0;
goto err;
goto err;
}
/* link in */
err:
(void) pthread_mutex_unlock(&adhostlock);
}
}
return (rc);
}
/*
* Free a DS configuration.
* Caller must lock the adhostlock mutex
*/
static
void
{
adutils_host_t **p, *q;
continue;
/* found */
(void) pthread_mutex_lock(&((*p)->lock));
if ((*p)->ref > 0) {
/*
* Still in use. Set its owner to NULL so
* that it can be freed when its ref count
* becomes 0.
*/
(void) pthread_mutex_unlock(&((*p)->lock));
break;
}
(void) pthread_mutex_unlock(&((*p)->lock));
q = *p;
*p = (*p)->next;
(void) pthread_mutex_destroy(&q->lock);
if (q->ld)
(void) ldap_unbind(q->ld);
if (q->host)
free(q);
break;
}
}
/*
* Add known domain name and domain SID to AD configuration.
*/
{
ad->num_known_domains++;
return (ADUTILS_SUCCESS);
} else {
}
ad->num_known_domains = 0;
return (ADUTILS_ERR_MEMORY);
}
}
/*
* Check that this AD supports this domain.
* If there are no known domains assume that the
* domain is supported by this AD.
*
* Returns 1 if this domain is supported by this AD
* else returns 0;
*/
int
{
int i;
for (i = 0; i < ad->num_known_domains; i++) {
return (1);
}
return ((i == 0) ? 1 : 0);
}
/*
* Check that this AD supports the SID prefix.
* The SID prefix should match the domain SID.
* If there are no known domains assume that the
* SID prefix is supported by this AD.
*
* Returns 1 if this sid prefix is supported by this AD
* else returns 0;
*/
int
{
int i;
for (i = 0; i < ad->num_known_domains; i++) {
return (1);
}
return ((i == 0) ? 1 : 0);
}
void *ldap_res_search_argp,
{
return (ADUTILS_ERR_INTERNAL);
return (ADUTILS_ERR_RETRIABLE_NET_ERR);
return (ADUTILS_ERR_MEMORY);
(void) pthread_mutex_lock(&qstatelock);
(void) pthread_mutex_unlock(&qstatelock);
return (ADUTILS_SUCCESS);
}
/*
* Find the adutils_query_state_t to which a given LDAP result msgid on a
* given connection belongs. This routine increaments the reference count
* so that the object can not be freed. adutils_lookup_batch_unlock()
* must be called to decreament the reference count.
*/
static
int
{
int i;
int ret;
(void) pthread_mutex_lock(&qstatelock);
continue;
for (i = 0; i < p->qcount; i++) {
if (!p->qdead) {
p->ref_cnt++;
*state = p;
*qid = i;
ret = 1;
} else
ret = 0;
(void) pthread_mutex_unlock(&qstatelock);
return (ret);
}
}
}
(void) pthread_mutex_unlock(&qstatelock);
return (0);
}
static
int
{
int i;
return (i);
}
return (-1);
}
static
void
{
int i, j;
return;
return;
}
for (i = 0; i < entry->num_nvpairs; i++) {
continue;
}
continue;
for (j = 0; j < ap->num_values; j++)
} else {
}
}
}
void
{
return;
return;
}
free_entry(e);
}
}
const adutils_entry_t *
{
return (NULL);
}
char **
{
int i;
return (NULL);
for (i = 0; i < entry->num_nvpairs; i++) {
return (ap->attr_values);
}
return (NULL);
}
/*
* Queue LDAP result for the given query.
*
* Return values:
* 0 success
* -1 ignore result
* -2 error
*/
static
int
{
char **strvalues;
/* Check that this is the domain that we were looking for */
return (-2);
return (-2);
}
return (-1);
}
}
/* Allocate memory for the entry */
goto out;
/* For 'dn' */
/* Count the number of name-value pairs for this entry */
ep->num_nvpairs++;
}
/* Allocate array for the attribute name-value pairs */
ep->num_nvpairs = 0;
goto out;
}
/* For dn */
goto out;
ap->num_values = 0;
goto out;
}
ldap_memfree(attr), i++,
goto out;
if ((b = check_for_binary_attrs(attr)) >= 0) {
bvalues =
continue;
if (ap->num_values == 0) {
continue;
}
sizeof (*ap->attr_values));
ap->num_values = 0;
goto out;
}
for (j = 0; j < ap->num_values; j++) {
ap->attr_values[j] =
goto out;
}
continue;
}
continue;
if (ap->num_values == 0) {
continue;
}
}
ret = 0;
out:
if (ret < 0)
free_entry(ep);
else
return (ret);
}
/*
* Put the search result onto the given adutils_q_t.
* Returns: 0 success
* < 0 error
*/
static
int
{
if (ret < -1) {
*q->rc = ADUTILS_ERR_MEMORY;
goto out;
} else if (ret == -1) {
/* ignore result */
goto out;
}
*q->rc = ADUTILS_ERR_MEMORY;
goto out;
}
} else {
res->num_entries++;
}
*q->rc = ADUTILS_SUCCESS;
ret = 0;
out:
return (ret);
}
/*
* Try to get a result; if there is one, find the corresponding
* adutils_q_t and process the result.
*
* Returns: 0 success
* -1 error
*/
static
int
{
int num;
return (ret);
}
/* Get one result */
rc < 0)
adh->num_requests--;
"AD ldap_result error - %d queued requests", num);
return (-1);
}
switch (rc) {
case LDAP_RES_SEARCH_RESULT:
/*
* We use the caller-provided callback
* to process the result.
*/
} else {
/*
* No callback. We fallback to our
* default behaviour. All the entries
* gotten from this search have been
* added to the result list during
* LDAP_RES_SEARCH_ENTRY (see below).
* Here we set the return status to
* notfound if the result is still empty.
*/
}
} else {
"AD cannot find message ID (%d) "
"- %d queued requests",
}
(void) ldap_msgfree(res);
ret = 0;
break;
case LDAP_RES_SEARCH_ENTRY:
/*
* We use the caller-provided callback
* to process the entry.
*/
} else {
/*
* No callback. We fallback to our
* default behaviour. This entry
* will be added to the result list.
*/
if (rc < 0) {
"Failed to queue entry by "
"message ID (%d) "
"- %d queued requests",
}
}
} else {
"AD cannot find message ID (%d) "
"- %d queued requests",
}
(void) ldap_msgfree(res);
ret = 0;
break;
/*
* We have no need for these at the moment. Eventually,
* when we query things that we can't expect to find in
* the Global Catalog then we'll need to learn to follow
* references.
*/
(void) ldap_msgfree(res);
ret = 0;
break;
default:
/* timeout or error; treat the same */
ret = -1;
break;
}
return (ret);
}
/*
* This routine decreament the reference count of the
* adutils_query_state_t
*/
static void
{
/*
* Decrement reference count with qstatelock locked
*/
(void) pthread_mutex_lock(&qstatelock);
/*
* If there are no references wakup the allocating thread
*/
(void) pthread_mutex_unlock(&qstatelock);
}
/*
* This routine frees the adutils_query_state_t structure
* If the reference count is greater than 1 it waits
* for the other threads to finish using it.
*/
void
{
int i;
return;
/*
* Set state to dead to stop further operations.
* Wait for reference count with qstatelock locked
* to get to one.
*/
(void) pthread_mutex_lock(&qstatelock);
}
/* Remove this state struct from the list of state structs */
if (*p == (*state)) {
break;
}
}
(void) pthread_mutex_unlock(&qstatelock);
/* Clear results for queries that failed */
}
}
}
/*
* This routine waits for other threads using the
* adutils_query_state_t structure to finish.
* If the reference count is greater than 1 it waits
* for the other threads to finish using it.
*/
static
void
{
/*
* Set state to dead to stop further operation.
* stating.
* Wait for reference count to get to one
* with qstatelock locked.
*/
(void) pthread_mutex_lock(&qstatelock);
}
(void) pthread_mutex_unlock(&qstatelock);
}
/*
* Process active queries in the AD lookup batch and then finalize the
* result.
*/
{
/* Process results until done or until timeout, if given */
&tv)) != 0)
break;
}
/* Wait for other threads processing search result to finish */
return (ad_rc);
}
/*
* Send one prepared search, queue up msgid, process what results are
* available
*/
{
int num;
int dead;
adutils_q_t *q;
/*
* Remember the expected domain so we can check the results
* against it
*/
/* Remember where to put the results */
/*
* Provide sane defaults for the results in case we never hear
* back from the DS before closing the connection.
*/
/* Check the number of queued requests first */
break;
}
/* Send this lookup, don't wait for a result here */
lrc = LDAP_SUCCESS;
if (lrc == LDAP_SUCCESS) {
lrc == LDAP_UNWILLING_TO_PERFORM) {
} else {
}
}
if (dead) {
if (lrc != LDAP_SUCCESS)
"AD ldap_search_ext error (%s) "
"- %d queued requests",
return (retcode);
}
/*
* Reap as many requests as we can _without_ waiting to prevent
* any possible TCP socket buffer starvation deadlocks.
*/
;
return (ADUTILS_SUCCESS);
}
/*
* Single AD lookup request implemented on top of the batch API.
*/
{
if (rc != ADUTILS_SUCCESS)
return (rc);
if (rc != ADUTILS_SUCCESS) {
return (rc);
}
if (rc != ADUTILS_SUCCESS)
return (rc);
return (brc);
}
domain_eq(const char *a, const char *b)
{
int err;
== 0 && err == 0);
}
void
{
}