mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-12-21 23:29:34 -05:00
Introduces pinned operations. When SASL bind finishes, we might still have to maintain a link between the client an an upstream for future bind operations if we got a SASL Bind in Progress result code. We zero out the msgids and remember a server-unique identifer on the client and the relevant operation that lets us retrieve that link again. This operation is reclaimed just like anything else when connections drop. Hopefully, this should work for LDAP TXN and VC Exop support with SASL later as well since it allows for many-to-many links to exist.
570 lines
18 KiB
C
570 lines
18 KiB
C
/* $OpenLDAP$ */
|
|
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
|
*
|
|
* Copyright 1998-2020 The OpenLDAP Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted only as authorized by the OpenLDAP
|
|
* Public License.
|
|
*
|
|
* A copy of this license is available in the file LICENSE in the
|
|
* top-level directory of the distribution or, alternatively, at
|
|
* <http://www.OpenLDAP.org/license.html>.
|
|
*/
|
|
|
|
#include "portable.h"
|
|
|
|
#include <ac/socket.h>
|
|
#include <ac/errno.h>
|
|
#include <ac/string.h>
|
|
#include <ac/time.h>
|
|
#include <ac/unistd.h>
|
|
|
|
#include "lutil.h"
|
|
#include "lload.h"
|
|
|
|
/*
|
|
* On entering the function, we've put a reference on both connections and hold
|
|
* upstream's c_io_mutex.
|
|
*/
|
|
static int
|
|
client_bind(
|
|
LloadOperation *op,
|
|
struct berval *binddn,
|
|
ber_tag_t tag,
|
|
struct berval *auth )
|
|
{
|
|
LloadConnection *upstream = op->o_upstream;
|
|
|
|
ber_printf( upstream->c_pendingber, "t{titOtO}", LDAP_TAG_MESSAGE,
|
|
LDAP_TAG_MSGID, op->o_upstream_msgid,
|
|
LDAP_REQ_BIND, &op->o_request,
|
|
LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
|
|
/*
|
|
* On entering the function, we've put a reference on both connections and hold
|
|
* upstream's c_io_mutex.
|
|
*/
|
|
static int
|
|
client_bind_as_vc(
|
|
LloadOperation *op,
|
|
struct berval *binddn,
|
|
ber_tag_t tag,
|
|
struct berval *auth )
|
|
{
|
|
LloadConnection *upstream = op->o_upstream;
|
|
|
|
CONNECTION_LOCK(upstream);
|
|
ber_printf( upstream->c_pendingber, "t{tit{tst{{tOOtOtO}}}}", LDAP_TAG_MESSAGE,
|
|
LDAP_TAG_MSGID, op->o_upstream_msgid,
|
|
LDAP_REQ_EXTENDED,
|
|
LDAP_TAG_EXOP_REQ_OID, LDAP_EXOP_VERIFY_CREDENTIALS,
|
|
LDAP_TAG_EXOP_REQ_VALUE,
|
|
LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, BER_BV_OPTIONAL( &upstream->c_vc_cookie ),
|
|
&binddn, tag, &auth,
|
|
LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
|
|
CONNECTION_UNLOCK(upstream);
|
|
return 0;
|
|
}
|
|
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
|
|
|
|
int
|
|
request_bind( LloadConnection *client, LloadOperation *op )
|
|
{
|
|
LloadConnection *upstream = NULL;
|
|
BerElement *ber, *copy;
|
|
struct berval binddn, auth;
|
|
ber_int_t version;
|
|
ber_tag_t tag;
|
|
unsigned long pin = client->c_pin_id;
|
|
int res, rc = LDAP_SUCCESS;
|
|
|
|
if ( pin ) {
|
|
LloadOperation *pinned_op, needle = {
|
|
.o_client_connid = client->c_connid,
|
|
.o_client_msgid = 0,
|
|
.o_pin_id = client->c_pin_id,
|
|
};
|
|
|
|
Debug( LDAP_DEBUG_CONNS, "request_bind: "
|
|
"client connid=%lu is pinned pin=%lu\n",
|
|
client->c_connid, pin );
|
|
|
|
pinned_op =
|
|
tavl_delete( &client->c_ops, &needle, operation_client_cmp );
|
|
if ( pinned_op ) {
|
|
assert( op->o_tag == pinned_op->o_tag );
|
|
|
|
pinned_op->o_client_msgid = op->o_client_msgid;
|
|
|
|
/* Preserve the new BerElement and its pointers, reclaim the old
|
|
* one in operation_destroy_from_client if it's still there */
|
|
needle.o_ber = pinned_op->o_ber;
|
|
pinned_op->o_ber = op->o_ber;
|
|
op->o_ber = needle.o_ber;
|
|
|
|
pinned_op->o_request = op->o_request;
|
|
pinned_op->o_ctrls = op->o_ctrls;
|
|
|
|
/*
|
|
* pinned_op is accessible from the upstream, protect it since we
|
|
* lose the client lock in operation_destroy_from_client temporarily
|
|
*/
|
|
pinned_op->o_client_refcnt++;
|
|
operation_destroy_from_client( op );
|
|
pinned_op->o_client_refcnt--;
|
|
|
|
op = pinned_op;
|
|
}
|
|
}
|
|
|
|
/* protect the Bind operation */
|
|
op->o_client_refcnt++;
|
|
tavl_delete( &client->c_ops, op, operation_client_cmp );
|
|
|
|
client_reset( client );
|
|
|
|
client->c_state = LLOAD_C_BINDING;
|
|
client->c_type = LLOAD_C_OPEN;
|
|
|
|
if ( (copy = ber_alloc()) == NULL ) {
|
|
goto fail;
|
|
}
|
|
ber_init2( copy, &op->o_request, 0 );
|
|
|
|
tag = ber_get_int( copy, &version );
|
|
if ( tag == LBER_ERROR ) {
|
|
Debug( LDAP_DEBUG_PACKETS, "request_bind: "
|
|
"failed to parse version field\n" );
|
|
goto fail;
|
|
} else if ( version != LDAP_VERSION3 ) {
|
|
operation_send_reject_locked(
|
|
op, LDAP_PROTOCOL_ERROR, "LDAP version unsupported", 1 );
|
|
ber_free( copy, 0 );
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
tag = ber_get_stringbv( copy, &binddn, LBER_BV_NOTERM );
|
|
if ( tag == LBER_ERROR ) {
|
|
Debug( LDAP_DEBUG_PACKETS, "request_bind: "
|
|
"failed to parse bind name field\n" );
|
|
goto fail;
|
|
}
|
|
|
|
tag = ber_skip_element( copy, &auth );
|
|
if ( tag == LDAP_AUTH_SIMPLE ) {
|
|
if ( !BER_BVISNULL( &client->c_auth ) ) {
|
|
ch_free( client->c_auth.bv_val );
|
|
}
|
|
if ( !BER_BVISEMPTY( &binddn ) ) {
|
|
char *ptr;
|
|
client->c_auth.bv_len = STRLENOF("dn:") + binddn.bv_len;
|
|
client->c_auth.bv_val = ch_malloc( client->c_auth.bv_len + 1 );
|
|
|
|
ptr = lutil_strcopy( client->c_auth.bv_val, "dn:" );
|
|
ptr = lutil_strncopy( ptr, binddn.bv_val, binddn.bv_len );
|
|
*ptr = '\0';
|
|
} else {
|
|
BER_BVZERO( &client->c_auth );
|
|
}
|
|
|
|
if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
|
|
ber_memfree( client->c_sasl_bind_mech.bv_val );
|
|
BER_BVZERO( &client->c_sasl_bind_mech );
|
|
}
|
|
} else if ( tag == LDAP_AUTH_SASL ) {
|
|
struct berval mech;
|
|
|
|
ber_init2( copy, &auth, 0 );
|
|
|
|
if ( ber_get_stringbv( copy, &mech, LBER_BV_NOTERM ) == LBER_ERROR ) {
|
|
goto fail;
|
|
}
|
|
if ( ber_bvcmp( &mech, &client->c_sasl_bind_mech ) ) {
|
|
ber_memfree( client->c_sasl_bind_mech.bv_val );
|
|
ber_dupbv( &client->c_sasl_bind_mech, &mech );
|
|
}
|
|
} else {
|
|
goto fail;
|
|
}
|
|
|
|
rc = tavl_insert( &client->c_ops, op, operation_client_cmp, avl_dup_error );
|
|
assert( rc == LDAP_SUCCESS );
|
|
CONNECTION_UNLOCK_INCREF(client);
|
|
|
|
if ( pin ) {
|
|
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
|
upstream = op->o_upstream;
|
|
if ( upstream ) {
|
|
CONNECTION_LOCK(upstream);
|
|
if ( !upstream->c_live ) {
|
|
CONNECTION_UNLOCK(upstream);
|
|
upstream = NULL;
|
|
}
|
|
}
|
|
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
|
}
|
|
|
|
/* If we were pinned but lost the link, don't look for a new upstream, we
|
|
* have to reject the op and clear pin */
|
|
if ( upstream ) {
|
|
CONNECTION_UNLOCK_INCREF(upstream);
|
|
ldap_pvt_thread_mutex_lock( &upstream->c_io_mutex );
|
|
} else if ( !pin ) {
|
|
upstream = backend_select( op, &res );
|
|
} else {
|
|
Debug( LDAP_DEBUG_STATS, "request_bind: "
|
|
"connid=%lu, msgid=%d pinned upstream lost\n",
|
|
op->o_client_connid, op->o_client_msgid );
|
|
operation_send_reject( op, LDAP_UNAVAILABLE,
|
|
"connection to the remote server has been severed", 1 );
|
|
pin = 0;
|
|
goto done;
|
|
}
|
|
|
|
if ( !upstream ) {
|
|
Debug( LDAP_DEBUG_STATS, "request_bind: "
|
|
"connid=%lu, msgid=%d no available connection found\n",
|
|
op->o_client_connid, op->o_client_msgid );
|
|
operation_send_reject( op, res, "no connections available", 1 );
|
|
assert( client->c_pin_id == 0 );
|
|
goto done;
|
|
}
|
|
|
|
ber = upstream->c_pendingber;
|
|
if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
|
|
Debug( LDAP_DEBUG_ANY, "request_bind: "
|
|
"ber_alloc failed\n" );
|
|
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
|
CONNECTION_LOCK_DECREF(upstream);
|
|
CONNECTION_UNLOCK_OR_DESTROY(upstream);
|
|
|
|
CONNECTION_LOCK_DECREF(client);
|
|
goto fail;
|
|
}
|
|
upstream->c_pendingber = ber;
|
|
|
|
CONNECTION_LOCK(upstream);
|
|
if ( pin ) {
|
|
tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
|
|
} else if ( tag == LDAP_AUTH_SASL && !op->o_pin_id ) {
|
|
ldap_pvt_thread_mutex_lock( &lload_pin_mutex );
|
|
pin = op->o_pin_id = lload_next_pin++;
|
|
Debug( LDAP_DEBUG_CONNS, "request_bind: "
|
|
"client connid=%lu allocated pin=%lu linking it to upstream "
|
|
"connid=%lu\n",
|
|
op->o_client_connid, pin, upstream->c_connid );
|
|
ldap_pvt_thread_mutex_unlock( &lload_pin_mutex );
|
|
}
|
|
|
|
op->o_upstream = upstream;
|
|
op->o_upstream_connid = upstream->c_connid;
|
|
op->o_upstream_msgid = upstream->c_next_msgid++;
|
|
|
|
Debug( LDAP_DEBUG_TRACE, "request_bind: "
|
|
"added bind from client connid=%lu to upstream connid=%lu "
|
|
"as msgid=%d\n",
|
|
op->o_client_connid, op->o_upstream_connid, op->o_upstream_msgid );
|
|
if ( tavl_insert( &upstream->c_ops, op, operation_upstream_cmp,
|
|
avl_dup_error ) ) {
|
|
assert(0);
|
|
}
|
|
upstream->c_state = LLOAD_C_BINDING;
|
|
CONNECTION_UNLOCK(upstream);
|
|
|
|
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
|
|
if ( lload_features & LLOAD_FEATURE_VC ) {
|
|
rc = client_bind_as_vc( op, &binddn, tag, &auth );
|
|
} else
|
|
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
|
|
{
|
|
rc = client_bind( op, &binddn, tag, &auth );
|
|
}
|
|
|
|
done:
|
|
if ( rc == LDAP_SUCCESS ) {
|
|
CONNECTION_LOCK(client);
|
|
if ( upstream ) {
|
|
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
|
}
|
|
|
|
client->c_pin_id = pin;
|
|
if ( !--op->o_client_refcnt || !upstream ) {
|
|
operation_destroy_from_client( op );
|
|
if ( client->c_state == LLOAD_C_BINDING ) {
|
|
client->c_state = LLOAD_C_READY;
|
|
client->c_type = LLOAD_C_OPEN;
|
|
client->c_pin_id = 0;
|
|
if ( !BER_BVISNULL( &client->c_auth ) ) {
|
|
ch_free( client->c_auth.bv_val );
|
|
BER_BVZERO( &client->c_auth );
|
|
}
|
|
if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
|
|
ber_memfree( client->c_sasl_bind_mech.bv_val );
|
|
BER_BVZERO( &client->c_sasl_bind_mech );
|
|
}
|
|
}
|
|
}
|
|
CONNECTION_UNLOCK(client);
|
|
|
|
if ( upstream ) {
|
|
connection_write_cb( -1, 0, upstream );
|
|
CONNECTION_LOCK_DECREF(upstream);
|
|
CONNECTION_UNLOCK_OR_DESTROY(upstream);
|
|
}
|
|
CONNECTION_LOCK_DECREF(client);
|
|
} else {
|
|
fail:
|
|
rc = -1;
|
|
|
|
CONNECTION_LOCK_DECREF(client);
|
|
op->o_client_refcnt--;
|
|
operation_destroy_from_client( op );
|
|
client->c_pin_id = 0;
|
|
CONNECTION_DESTROY(client);
|
|
}
|
|
|
|
ber_free( copy, 0 );
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
handle_bind_response(
|
|
LloadConnection *client,
|
|
LloadOperation *op,
|
|
BerElement *ber )
|
|
{
|
|
LloadConnection *upstream = op->o_upstream;
|
|
BerValue response;
|
|
BerElement *copy;
|
|
LloadOperation *removed;
|
|
ber_int_t result;
|
|
ber_tag_t tag;
|
|
int rc = LDAP_SUCCESS;
|
|
|
|
if ( (copy = ber_alloc()) == NULL ) {
|
|
rc = -1;
|
|
goto done;
|
|
}
|
|
|
|
tag = ber_peek_element( ber, &response );
|
|
assert( tag == LDAP_RES_BIND );
|
|
|
|
ber_init2( copy, &response, 0 );
|
|
|
|
tag = ber_get_enum( copy, &result );
|
|
ber_free( copy, 0 );
|
|
|
|
if ( tag == LBER_ERROR ) {
|
|
rc = -1;
|
|
goto done;
|
|
}
|
|
|
|
Debug( LDAP_DEBUG_STATS, "handle_bind_response: "
|
|
"received response for bind request msgid=%d by client "
|
|
"connid=%lu, result=%d\n",
|
|
op->o_client_msgid, op->o_client_connid, result );
|
|
|
|
CONNECTION_LOCK(upstream);
|
|
if ( result != LDAP_SASL_BIND_IN_PROGRESS ) {
|
|
upstream->c_state = LLOAD_C_READY;
|
|
op->o_pin_id = 0;
|
|
} else {
|
|
if ( tavl_delete( &upstream->c_ops, op, operation_upstream_cmp ) ) {
|
|
op->o_upstream_msgid = 0;
|
|
op->o_upstream_refcnt++;
|
|
rc = tavl_insert( &upstream->c_ops, op, operation_upstream_cmp,
|
|
avl_dup_error );
|
|
assert( rc == LDAP_SUCCESS );
|
|
}
|
|
}
|
|
CONNECTION_UNLOCK(upstream);
|
|
|
|
CONNECTION_LOCK(client);
|
|
removed = tavl_delete( &client->c_ops, op, operation_client_cmp );
|
|
assert( !removed || op == removed );
|
|
|
|
if ( client->c_state == LLOAD_C_BINDING ) {
|
|
switch ( result ) {
|
|
case LDAP_SASL_BIND_IN_PROGRESS:
|
|
op->o_client_msgid = 0;
|
|
rc = tavl_insert( &client->c_ops, op, operation_client_cmp,
|
|
avl_dup_error );
|
|
assert( rc == LDAP_SUCCESS );
|
|
break;
|
|
case LDAP_SUCCESS:
|
|
default: {
|
|
op->o_client = NULL;
|
|
client->c_state = LLOAD_C_READY;
|
|
client->c_type = LLOAD_C_OPEN;
|
|
client->c_pin_id = 0;
|
|
if ( result != LDAP_SUCCESS ) {
|
|
ber_memfree( client->c_auth.bv_val );
|
|
BER_BVZERO( &client->c_auth );
|
|
} else if ( !ber_bvstrcasecmp(
|
|
&client->c_auth, &lloadd_identity ) ) {
|
|
client->c_type = LLOAD_C_PRIVILEGED;
|
|
}
|
|
if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
|
|
ber_memfree( client->c_sasl_bind_mech.bv_val );
|
|
BER_BVZERO( &client->c_sasl_bind_mech );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
assert( client->c_state == LLOAD_C_INVALID ||
|
|
client->c_state == LLOAD_C_CLOSING );
|
|
}
|
|
CONNECTION_UNLOCK(client);
|
|
|
|
done:
|
|
if ( rc ) {
|
|
operation_send_reject( op, LDAP_OTHER, "internal error", 1 );
|
|
|
|
ber_free( ber, 1 );
|
|
return LDAP_SUCCESS;
|
|
}
|
|
return forward_final_response( client, op, ber );
|
|
}
|
|
|
|
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
|
|
int
|
|
handle_vc_bind_response(
|
|
LloadConnection *client,
|
|
LloadOperation *op,
|
|
BerElement *ber )
|
|
{
|
|
BerElement *output;
|
|
BerValue matched, diagmsg, creds = BER_BVNULL, controls = BER_BVNULL;
|
|
ber_int_t result;
|
|
ber_tag_t tag;
|
|
ber_len_t len;
|
|
int rc = 0;
|
|
|
|
tag = ber_scanf( ber, "{emm" /* "}" */,
|
|
&result, &matched, &diagmsg );
|
|
if ( tag == LBER_ERROR ) {
|
|
rc = -1;
|
|
goto done;
|
|
}
|
|
|
|
tag = ber_peek_tag( ber, &len );
|
|
if ( result == LDAP_PROTOCOL_ERROR ) {
|
|
LloadConnection *upstream = op->o_upstream;
|
|
LloadBackend *b;
|
|
|
|
CONNECTION_LOCK(upstream);
|
|
b = (LloadBackend *)upstream->c_private;
|
|
Debug( LDAP_DEBUG_ANY, "handle_vc_bind_response: "
|
|
"VC extended operation not supported on backend %s\n",
|
|
b->b_uri.bv_val );
|
|
CONNECTION_UNLOCK(upstream);
|
|
}
|
|
|
|
Debug( LDAP_DEBUG_STATS, "handle_vc_bind_response: "
|
|
"received response for bind request msgid=%d by client "
|
|
"connid=%lu, result=%d\n",
|
|
op->o_client_msgid, op->o_client_connid, result );
|
|
|
|
CONNECTION_LOCK(client);
|
|
|
|
if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) {
|
|
if ( !BER_BVISNULL( &client->c_vc_cookie ) ) {
|
|
ber_memfree( client->c_vc_cookie.bv_val );
|
|
}
|
|
tag = ber_scanf( ber, "o", &client->c_vc_cookie );
|
|
if ( tag == LBER_ERROR ) {
|
|
rc = -1;
|
|
CONNECTION_UNLOCK_INCREF(client);
|
|
goto done;
|
|
}
|
|
tag = ber_peek_tag( ber, &len );
|
|
}
|
|
|
|
if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS ) {
|
|
tag = ber_scanf( ber, "m", &creds );
|
|
if ( tag == LBER_ERROR ) {
|
|
rc = -1;
|
|
CONNECTION_UNLOCK_INCREF(client);
|
|
goto done;
|
|
}
|
|
tag = ber_peek_tag( ber, &len );
|
|
}
|
|
|
|
if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ) {
|
|
tag = ber_scanf( ber, "m", &controls );
|
|
if ( tag == LBER_ERROR ) {
|
|
rc = -1;
|
|
CONNECTION_UNLOCK_INCREF(client);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if ( client->c_state == LLOAD_C_BINDING ) {
|
|
switch ( result ) {
|
|
case LDAP_SASL_BIND_IN_PROGRESS:
|
|
break;
|
|
case LDAP_SUCCESS:
|
|
default: {
|
|
client->c_state = LLOAD_C_READY;
|
|
client->c_type = LLOAD_C_OPEN;
|
|
client->c_pin_id = 0;
|
|
if ( result != LDAP_SUCCESS ) {
|
|
ber_memfree( client->c_auth.bv_val );
|
|
BER_BVZERO( &client->c_auth );
|
|
} else if ( !ber_bvstrcasecmp(
|
|
&client->c_auth, &lloadd_identity ) ) {
|
|
client->c_type = LLOAD_C_PRIVILEGED;
|
|
}
|
|
if ( !BER_BVISNULL( &client->c_vc_cookie ) ) {
|
|
ber_memfree( client->c_vc_cookie.bv_val );
|
|
BER_BVZERO( &client->c_vc_cookie );
|
|
}
|
|
if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
|
|
ber_memfree( client->c_sasl_bind_mech.bv_val );
|
|
BER_BVZERO( &client->c_sasl_bind_mech );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
assert( client->c_state == LLOAD_C_INVALID ||
|
|
client->c_state == LLOAD_C_CLOSING );
|
|
}
|
|
CONNECTION_UNLOCK_INCREF(client);
|
|
|
|
ldap_pvt_thread_mutex_lock( &client->c_io_mutex );
|
|
output = client->c_pendingber;
|
|
if ( output == NULL && (output = ber_alloc()) == NULL ) {
|
|
rc = -1;
|
|
ldap_pvt_thread_mutex_unlock( &client->c_io_mutex );
|
|
goto done;
|
|
}
|
|
client->c_pendingber = output;
|
|
|
|
rc = ber_printf( output, "t{tit{eOOtO}tO}", LDAP_TAG_MESSAGE,
|
|
LDAP_TAG_MSGID, op->o_client_msgid, LDAP_RES_BIND,
|
|
result, &matched, &diagmsg,
|
|
LDAP_TAG_SASL_RES_CREDS, BER_BV_OPTIONAL( &creds ),
|
|
LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &controls ) );
|
|
|
|
ldap_pvt_thread_mutex_unlock( &client->c_io_mutex );
|
|
if ( rc >= 0 ) {
|
|
connection_write_cb( -1, 0, client );
|
|
rc = 0;
|
|
}
|
|
|
|
done:
|
|
CONNECTION_LOCK_DECREF(client);
|
|
operation_destroy_from_client( op );
|
|
CONNECTION_UNLOCK_OR_DESTROY(client);
|
|
ber_free( ber, 1 );
|
|
return rc;
|
|
}
|
|
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
|