StartTLS (ITS#3507) + chain overlay fixes and improvements

This commit is contained in:
Pierangelo Masarati 2005-01-24 09:38:11 +00:00
parent ad2a19d8eb
commit c6b6d2a5ec
21 changed files with 459 additions and 275 deletions

View file

@ -46,14 +46,14 @@ ldap_back_add(
int isupdate;
int do_retry = 1;
LDAPControl **ctrls = NULL;
int rc = LDAP_SUCCESS;
rs->sr_err = LDAP_SUCCESS;
Debug( LDAP_DEBUG_ARGS, "==> ldap_back_add(\"%s\")\n",
op->o_req_dn.bv_val, 0, 0 );
lc = ldap_back_getconn( op, rs );
if ( !lc || !ldap_back_dobind( lc, op, rs ) ) {
rc = -1;
lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
goto cleanup;
}
@ -89,20 +89,19 @@ ldap_back_add(
attrs[ i ] = NULL;
ctrls = op->o_ctrls;
rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls );
if ( rc != LDAP_SUCCESS ) {
rs->sr_err = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls );
if ( rs->sr_err != LDAP_SUCCESS ) {
send_ldap_result( op, rs );
rc = -1;
goto cleanup;
}
retry:
rs->sr_err = ldap_add_ext( lc->lc_ld, op->o_req_dn.bv_val, attrs,
ctrls, NULL, &msgid );
rc = ldap_back_op_result( lc, op, rs, msgid, 1 );
rs->sr_err = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
do_retry = 0;
if ( ldap_back_retry( lc, op, rs ) ) {
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
goto retry;
}
}
@ -118,8 +117,8 @@ cleanup:
}
Debug( LDAP_DEBUG_ARGS, "<== ldap_back_add(\"%s\"): %d\n",
op->o_req_dn.bv_val, rc, 0 );
op->o_req_dn.bv_val, rs->sr_err, 0 );
return rc;
return rs->sr_err;
}

View file

@ -90,12 +90,23 @@ struct ldapinfo {
/* end of ID assert stuff */
ldap_pvt_thread_mutex_t conn_mutex;
int savecred;
unsigned flags;
#define LDAP_BACK_F_NONE 0x00U
#define LDAP_BACK_F_SAVECRED 0x01U
#define LDAP_BACK_F_USE_TLS 0x02U
#define LDAP_BACK_F_TLS_CRITICAL ( 0x04U | LDAP_BACK_F_USE_TLS )
Avlnode *conntree;
int rwm_started;
};
typedef enum ldap_back_send_t {
LDAP_BACK_DONTSEND = 0x00,
LDAP_BACK_SENDOK = 0x01,
LDAP_BACK_SENDERR = 0x02,
LDAP_BACK_SENDRESULT = (LDAP_BACK_SENDOK|LDAP_BACK_SENDERR)
} ldap_back_send_t;
LDAP_END_DECL
#include "proto-ldap.h"

View file

@ -16,7 +16,7 @@
* <http://www.OpenLDAP.org/license.html>.
*/
/* ACKNOWLEDGEMENTS:
* This work was initially developed by the Howard Chu for inclusion
* This work was initially developed by Howard Chu for inclusion
* in OpenLDAP Software and subsequently enhanced by Pierangelo
* Masarati.
*/
@ -41,6 +41,9 @@ static LDAP_REBIND_PROC ldap_back_rebind;
static int
ldap_back_proxy_authz_bind( struct ldapconn *lc, Operation *op, SlapReply *rs );
static int
ldap_back_prepare_conn( struct ldapconn **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
int
ldap_back_bind( Operation *op, SlapReply *rs )
{
@ -50,9 +53,9 @@ ldap_back_bind( Operation *op, SlapReply *rs )
int rc = 0;
ber_int_t msgid;
lc = ldap_back_getconn( op, rs );
lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
if ( !lc ) {
return( -1 );
return rs->sr_err;
}
if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
@ -65,7 +68,7 @@ ldap_back_bind( Operation *op, SlapReply *rs )
rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val,
LDAP_SASL_SIMPLE,
&op->orb_cred, op->o_ctrls, NULL, &msgid );
rc = ldap_back_op_result( lc, op, rs, msgid, 1 );
rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
if ( rc == LDAP_SUCCESS ) {
/* If defined, proxyAuthz will be used also when
@ -84,7 +87,7 @@ ldap_back_bind( Operation *op, SlapReply *rs )
lc->lc_bound = 1;
ber_dupbv( &lc->lc_bound_ndn, &op->o_req_ndn );
if ( li->savecred ) {
if ( li->flags & LDAP_BACK_F_SAVECRED ) {
if ( !BER_BVISNULL( &lc->lc_cred ) ) {
memset( lc->lc_cred.bv_val, 0,
lc->lc_cred.bv_len );
@ -219,12 +222,76 @@ ldap_back_freeconn( Operation *op, struct ldapconn *lc )
return 0;
}
static int
ldap_back_prepare_conn( struct ldapconn **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
{
struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
int vers = op->o_protocol;
LDAP *ld = NULL;
assert( lcp != NULL );
rs->sr_err = ldap_initialize( &ld, li->url );
if ( rs->sr_err != LDAP_SUCCESS ) {
goto error_return;
}
/* Set LDAP version. This will always succeed: If the client
* bound with a particular version, then so can we.
*/
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&vers );
/* Set LDAP version. This will always succeed: If the client
* bound with a particular version, then so can we.
*/
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
(const void *)&vers );
/* FIXME: configurable? */
ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON );
if ( ( li->flags & LDAP_BACK_F_USE_TLS )
&& !ldap_is_ldaps_url( li->url )
&& ( rs->sr_err = ldap_start_tls_s( ld, NULL, NULL ) ) != LDAP_SUCCESS )
{
/* if StartTLS is requested, only attempt it if the URL
* is not "ldaps://"; this may occur not only in case
* of misconfiguration, but also when used in the chain
* overlay, where the "uri" can be parsed out of a referral */
if ( rs->sr_err == LDAP_SERVER_DOWN
|| ( li->flags & LDAP_BACK_F_TLS_CRITICAL ) )
{
ldap_unbind_ext_s( ld, NULL, NULL );
goto error_return;
}
}
if ( *lcp == NULL ) {
*lcp = (struct ldapconn *)ch_malloc( sizeof( struct ldapconn ) );
memset( *lcp, 0, sizeof( struct ldapconn ) );
}
(*lcp)->lc_ld = ld;
error_return:;
if ( rs->sr_err != LDAP_SUCCESS ) {
rs->sr_err = slap_map_api2result( rs );
if ( sendok & LDAP_BACK_SENDERR ) {
if ( rs->sr_text == NULL ) {
rs->sr_text = "ldap_initialize() failed";
}
send_ldap_result( op, rs );
rs->sr_text = NULL;
}
}
return rs->sr_err;
}
struct ldapconn *
ldap_back_getconn( Operation *op, SlapReply *rs )
ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok )
{
struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
struct ldapconn *lc, lc_curr;
LDAP *ld;
int is_priv = 0;
/* Searches for a ldapconn in the avl tree */
@ -258,31 +325,12 @@ ldap_back_getconn( Operation *op, SlapReply *rs )
/* Looks like we didn't get a bind. Open a new session... */
if ( !lc ) {
int vers = op->o_protocol;
rs->sr_err = ldap_initialize( &ld, li->url );
if ( rs->sr_err != LDAP_SUCCESS ) {
rs->sr_err = slap_map_api2result( rs );
if ( rs->sr_text == NULL ) {
rs->sr_text = "ldap_initialize() failed";
}
if ( op->o_conn ) {
send_ldap_result( op, rs );
}
rs->sr_text = NULL;
return( NULL );
/* lc here must be NULL */
if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
return NULL;
}
/* Set LDAP version. This will always succeed: If the client
* bound with a particular version, then so can we.
*/
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
(const void *)&vers );
/* FIXME: configurable? */
ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON );
lc = (struct ldapconn *)ch_malloc( sizeof( struct ldapconn ) );
lc->lc_conn = lc_curr.lc_conn;
lc->lc_ld = ld;
ber_dupbv( &lc->lc_local_ndn, &lc_curr.lc_local_ndn );
ldap_pvt_thread_mutex_init( &lc->lc_mutex );
@ -320,18 +368,18 @@ ldap_back_getconn( Operation *op, SlapReply *rs )
/* Err could be -1 in case a duplicate ldapconn is inserted */
if ( rs->sr_err != 0 ) {
ldap_back_conn_free( lc );
if ( op->o_conn ) {
if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
send_ldap_error( op, rs, LDAP_OTHER,
"internal server error" );
}
return( NULL );
return NULL;
}
} else {
Debug( LDAP_DEBUG_TRACE,
"=>ldap_back_getconn: conn %p fetched\n", (void *) lc, 0, 0 );
}
return( lc );
return lc;
}
/*
@ -341,12 +389,19 @@ ldap_back_getconn( Operation *op, SlapReply *rs )
* it from all the callers, and I made the function return the flag, so
* it can be used to simplify the check.
*/
int
ldap_back_dobind( struct ldapconn *lc, Operation *op, SlapReply *rs )
static int
ldap_back_dobind_int(
struct ldapconn *lc,
Operation *op,
SlapReply *rs,
ldap_back_send_t sendok,
int retries )
{
int rc;
ber_int_t msgid;
assert( retries >= 0 );
ldap_pvt_thread_mutex_lock( &lc->lc_mutex );
if ( !lc->lc_bound ) {
/*
@ -373,12 +428,33 @@ ldap_back_dobind( struct ldapconn *lc, Operation *op, SlapReply *rs )
goto done;
}
retry:;
rs->sr_err = ldap_sasl_bind( lc->lc_ld,
lc->lc_bound_ndn.bv_val,
LDAP_SASL_SIMPLE, &lc->lc_cred,
NULL, NULL, &msgid );
rc = ldap_back_op_result( lc, op, rs, msgid, 0 );
if ( rs->sr_err == LDAP_SERVER_DOWN ) {
if ( retries > 0 ) {
ldap_unbind_ext_s( lc->lc_ld, NULL, NULL );
lc->lc_ld = NULL;
/* lc here must be the regular lc, reset and ready for init */
if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
return 0;
}
retries--;
goto retry;
}
ldap_back_freeconn( op, lc );
rs->sr_err = slap_map_api2result( rs );
return 0;
}
rc = ldap_back_op_result( lc, op, rs, msgid, sendok );
if ( rc == LDAP_SUCCESS ) {
lc->lc_bound = 1;
}
@ -390,6 +466,12 @@ done:;
return rc;
}
int
ldap_back_dobind( struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
{
return ldap_back_dobind_int( lc, op, rs, sendok, 1 );
}
/*
* ldap_back_rebind
*
@ -400,7 +482,9 @@ static int
ldap_back_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
ber_int_t msgid, void *params )
{
struct ldapconn *lc = params;
struct ldapconn *lc = (struct ldapconn *)params;
/* FIXME: add checks on the URL/identity? */
return ldap_sasl_bind_s( ld, lc->lc_bound_ndn.bv_val,
LDAP_SASL_SIMPLE, &lc->lc_cred, NULL, NULL, NULL );
@ -408,11 +492,11 @@ ldap_back_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
int
ldap_back_op_result(
struct ldapconn *lc,
Operation *op,
SlapReply *rs,
ber_int_t msgid,
int sendok )
struct ldapconn *lc,
Operation *op,
SlapReply *rs,
ber_int_t msgid,
ldap_back_send_t sendok )
{
char *match = NULL;
LDAPMessage *res = NULL;
@ -474,7 +558,10 @@ retry:;
rs->sr_matched = match;
}
}
if ( op->o_conn && ( sendok || rs->sr_err != LDAP_SUCCESS ) ) {
if ( op->o_conn &&
( ( sendok & LDAP_BACK_SENDOK )
|| ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) )
{
send_ldap_result( op, rs );
}
if ( match ) {
@ -493,37 +580,20 @@ retry:;
/* return true if bound, false if failed */
int
ldap_back_retry( struct ldapconn *lc, Operation *op, SlapReply *rs )
ldap_back_retry( struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
{
struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
int vers = op->o_protocol;
LDAP *ld;
ldap_pvt_thread_mutex_lock( &lc->lc_mutex );
ldap_unbind_ext_s( lc->lc_ld, NULL, NULL );
lc->lc_ld = NULL;
lc->lc_bound = 0;
rs->sr_err = ldap_initialize( &ld, li->url );
if ( rs->sr_err != LDAP_SUCCESS ) {
rs->sr_err = slap_map_api2result( rs );
if ( rs->sr_text == NULL ) {
rs->sr_text = "ldap_initialize() failed";
}
if ( op->o_conn ) {
send_ldap_result( op, rs );
}
rs->sr_text = NULL;
/* lc here must be the regular lc, reset and ready for init */
if ( ldap_back_prepare_conn( &lc, op, rs, sendok ) != LDAP_SUCCESS ) {
return 0;
}
/* Set LDAP version. This will always succeed: If the client
* bound with a particular version, then so can we.
*/
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&vers );
/* FIXME: configurable? */
ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON );
lc->lc_ld = ld;
ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
return ldap_back_dobind( lc, op, rs );
return ldap_back_dobind_int( lc, op, rs, sendok, 0 );
}
static int
@ -695,7 +765,7 @@ ldap_back_proxy_authz_bind( struct ldapconn *lc, Operation *op, SlapReply *rs )
goto done;
}
rc = ldap_back_op_result( lc, op, rs, msgid, 0 );
rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDERR );
if ( rc == LDAP_SUCCESS ) {
lc->lc_bound = 1;
}

View file

@ -31,18 +31,11 @@
static BackendInfo *lback;
#if 0
static int
ldap_chain_chk_referrals( Operation *op, SlapReply *rs )
{
return LDAP_SUCCESS;
}
#endif
static int
ldap_chain_operational( Operation *op, SlapReply *rs )
{
/* trap entries generated by back-ldap.
/* Trap entries generated by back-ldap.
*
* FIXME: we need a better way to recognize them; a cleaner
* solution would be to be able to intercept the response
* of be_operational(), so that we can divert only those
@ -57,8 +50,11 @@ ldap_chain_operational( Operation *op, SlapReply *rs )
return SLAP_CB_CONTINUE;
}
/*
* Search specific response that strips entryDN from entries
*/
static int
ldap_chain_cb_response( Operation *op, SlapReply *rs )
ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
{
assert( op->o_tag == LDAP_REQ_SEARCH );
@ -82,83 +78,159 @@ ldap_chain_cb_response( Operation *op, SlapReply *rs )
}
return SLAP_CB_CONTINUE;
} else if ( rs->sr_type == REP_RESULT ) {
/* back-ldap tried to send result */
op->o_callback->sc_private = (void *)(1);
}
return 0;
}
/*
* Dummy response that simply traces if back-ldap tried to send
* anything to the client
*/
static int
ldap_chain_cb_response( Operation *op, SlapReply *rs )
{
if ( rs->sr_type == REP_RESULT ) {
op->o_callback->sc_private = (void *)(1);
} else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
{
/* strip the entryDN attribute, but keep returning results */
(void)ldap_chain_cb_search_response( op, rs );
}
return SLAP_CB_CONTINUE;
}
static int
ldap_chain_op(
Operation *op,
SlapReply *rs,
int ( *op_f )( Operation *op, SlapReply *rs ),
BerVarray ref )
{
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
int rc;
if ( lip->url != NULL ) {
op->o_bd->be_private = on->on_bi.bi_private;
return ( *op_f )( op, rs );
}
li = *lip;
op->o_bd->be_private = &li;
/* if we parse the URI then by no means
* we can cache stuff or reuse connections,
* because in back-ldap there's no caching
* based on the URI value, which is supposed
* to be set once for all (correct?) */
op->o_do_not_cache = 1;
for ( ; !BER_BVISNULL( ref ); ref++ ) {
LDAPURLDesc *srv;
char *save_dn;
/* We're setting the URI of the first referral;
* what if there are more?
Document: draft-ietf-ldapbis-protocol-27.txt
4.1.10. Referral
...
If the client wishes to progress the operation, it MUST follow the
referral by contacting one of the supported services. If multiple
URIs are present, the client assumes that any supported URI may be
used to progress the operation.
* so we actually need to follow exactly one,
* and we can assume any is fine.
*/
/* parse reference and use
* proto://[host][:port]/ only */
rc = ldap_url_parse_ext( ref->bv_val, &srv );
if ( rc != LDAP_URL_SUCCESS ) {
/* try next */
rc = LDAP_OTHER;
continue;
}
/* remove DN essentially because later on
* ldap_initialize() will parse the URL
* as a comma-separated URL list */
save_dn = srv->lud_dn;
srv->lud_dn = "";
srv->lud_scope = LDAP_SCOPE_DEFAULT;
li.url = ldap_url_desc2str( srv );
srv->lud_dn = save_dn;
ldap_free_urldesc( srv );
if ( li.url == NULL ) {
/* try next */
rc = LDAP_OTHER;
continue;
}
rc = ( *op_f )( op, rs );
ldap_memfree( li.url );
li.url = NULL;
if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
break;
}
}
return rc;
}
static int
ldap_chain_response( Operation *op, SlapReply *rs )
{
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
void *private = op->o_bd->be_private;
slap_callback *sc = op->o_callback;
slap_callback *sc = op->o_callback,
sc2 = { 0 };
int rc = 0;
int cache = op->o_do_not_cache;
char *authzid = NULL;
BerVarray ref;
struct berval ndn = op->o_ndn;
struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF )
if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
return SLAP_CB_CONTINUE;
}
/*
* TODO: add checks on who/when chain operations; e.g.:
* a) what identities are authorized
* b) what request DN (e.g. only chain requests rooted at <DN>)
* c) what referral URIs
* d) what protocol scheme (e.g. only ldaps://)
* e) what ssf
*/
ref = rs->sr_ref;
rs->sr_ref = NULL;
op->o_callback = NULL;
if ( lip->url == NULL ) {
/* if we parse the URI then by no means
* we can cache stuff or reuse connections,
* because in back-ldap there's no caching
* based on the URI value, which is supposed
* to be set once for all (correct?) */
op->o_do_not_cache = 1;
/* FIXME: we're setting the URI of the first referral;
* what if there are more? Is this something we should
* worry about? */
li = *lip;
op->o_bd->be_private = &li;
if ( rs->sr_type != REP_SEARCHREF ) {
LDAPURLDesc *srv;
char *save_dn;
/* parse reference and use
* proto://[host][:port]/ only */
rc = ldap_url_parse_ext( ref[0].bv_val, &srv );
if ( rc != LDAP_URL_SUCCESS ) {
/* error */
return 1;
}
/* remove DN essentially because later on
* ldap_initialize() will parse the URL
* as a comma-separated URL list */
save_dn = srv->lud_dn;
srv->lud_dn = "";
srv->lud_scope = LDAP_SCOPE_DEFAULT;
li.url = ldap_url_desc2str( srv );
srv->lud_dn = save_dn;
ldap_free_urldesc( srv );
if ( li.url == NULL ) {
/* error */
return 1;
}
}
} else {
op->o_bd->be_private = on->on_bi.bi_private;
}
/* we need this to know if back-ldap returned any result */
sc2.sc_response = ldap_chain_cb_response;
op->o_callback = &sc2;
/* Chaining can be performed by a privileged user on behalf
* of normal users, using the ProxyAuthz control, by exploiting
* the identity assertion feature of back-ldap; see idassert-*
* directives in slapd-ldap(5).
*
* FIXME: the idassert-authcDN is one, will it be fine regardless
* of the URI we obtain from the referral?
*/
switch ( op->o_tag ) {
@ -166,10 +238,10 @@ ldap_chain_response( Operation *op, SlapReply *rs )
struct berval rndn = op->o_req_ndn;
Connection *conn = op->o_conn;
/* FIXME: can we really get a referral for binds? */
op->o_req_ndn = slap_empty_bv;
op->o_conn = NULL;
rc = lback->bi_op_bind( op, rs );
rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
op->o_req_ndn = rndn;
op->o_conn = conn;
}
@ -182,7 +254,7 @@ ldap_chain_response( Operation *op, SlapReply *rs )
char textbuf[ SLAP_TEXT_BUFLEN ];
size_t textlen = sizeof( textbuf );
/* global overlay; create entry */
/* global overlay: create entry */
/* NOTE: this is a hack to use the chain overlay
* as global. I expect to be able to remove this
* soon by using slap_mods2entry() earlier in
@ -197,7 +269,7 @@ ldap_chain_response( Operation *op, SlapReply *rs )
break;
}
}
rc = lback->bi_op_add( op, rs );
rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
if ( cleanup_attrs ) {
attrs_free( op->ora_e->e_attrs );
op->ora_e->e_attrs = NULL;
@ -205,32 +277,38 @@ ldap_chain_response( Operation *op, SlapReply *rs )
break;
}
case LDAP_REQ_DELETE:
rc = lback->bi_op_delete( op, rs );
rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
break;
case LDAP_REQ_MODRDN:
rc = lback->bi_op_modrdn( op, rs );
rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
break;
case LDAP_REQ_MODIFY:
rc = lback->bi_op_modify( op, rs );
rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
break;
case LDAP_REQ_COMPARE:
rc = lback->bi_op_compare( op, rs );
rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
break;
case LDAP_REQ_SEARCH:
if ( rs->sr_type == REP_SEARCHREF ) {
struct berval *curr = ref,
odn = op->o_req_dn,
ondn = op->o_req_ndn;
slap_callback sc2 = { 0 };
int tmprc = 0;
ber_len_t refcnt = 0;
BerVarray newref = NULL;
sc2.sc_response = ldap_chain_cb_response;
op->o_callback = &sc2;
rs->sr_type = REP_SEARCH;
sc2.sc_response = ldap_chain_cb_search_response;
li = *lip;
li.url = NULL;
op->o_bd->be_private = &li;
/* if we parse the URI then by no means
* we can cache stuff or reuse connections,
* because in back-ldap there's no caching
* based on the URI value, which is supposed
* to be set once for all (correct?) */
op->o_do_not_cache = 1;
/* copy the private info because we need to modify it */
for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
LDAPURLDesc *srv;
@ -238,11 +316,11 @@ ldap_chain_response( Operation *op, SlapReply *rs )
/* parse reference and use
* proto://[host][:port]/ only */
tmprc = ldap_url_parse_ext( curr[0].bv_val, &srv );
if ( tmprc != LDAP_URL_SUCCESS ) {
/* error */
rc = 1;
goto end_of_searchref;
rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
if ( rc != LDAP_URL_SUCCESS ) {
/* try next */
rs->sr_err = LDAP_OTHER;
continue;
}
/* remove DN essentially because later on
@ -263,15 +341,15 @@ ldap_chain_response( Operation *op, SlapReply *rs )
ldap_free_urldesc( srv );
if ( li.url == NULL ) {
/* error */
rc = 1;
goto end_of_searchref;
/* try next */
rs->sr_err = LDAP_OTHER;
continue;
}
/* FIXME: should we also copy filter and scope?
* according to RFC3296, no */
tmprc = lback->bi_op_search( op, rs );
rc = lback->bi_op_search( op, rs );
ldap_memfree( li.url );
li.url = NULL;
@ -281,76 +359,51 @@ ldap_chain_response( Operation *op, SlapReply *rs )
op->o_tmpfree( op->o_req_ndn.bv_val,
op->o_tmpmemctx );
if ( tmprc ) {
/* error */
rc = 1;
goto end_of_searchref;
}
if ( rs->sr_err != LDAP_SUCCESS ) {
/* if search was not successful,
* at least return the referral! */
/* FIXME: assumes referrals
* are always created via
* referral_rewrite() and freed via
* ber_bvarray_free( rs->sr_ref ) */
newref = ch_realloc( newref, sizeof( struct berval ) * (refcnt + 2) );
ber_dupbv( &newref[ refcnt ], &curr[ 0 ] );
refcnt++;
BER_BVZERO( &newref[ refcnt ] );
if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
break;
}
}
end_of_searchref:;
op->o_req_dn = odn;
op->o_req_ndn = ondn;
rs->sr_type = REP_SEARCHREF;
rs->sr_entry = NULL;
/* if the error was bad, it was already returned
* by back-ldap; destroy the referrals left;
* otherwise, let the frontend return them. */
if ( newref ) {
if ( rc == 0 ) {
rc = SLAP_CB_CONTINUE;
if ( ref != default_referral ) {
ber_bvarray_free( ref );
}
ref = newref;
} else {
ber_bvarray_free( newref );
}
if ( rc != LDAP_SUCCESS ) {
/* couldn't chase any of the referrals */
rc = SLAP_CB_CONTINUE;
}
} else {
rc = lback->bi_op_search( op, rs );
rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
}
break;
case LDAP_REQ_EXTENDED:
rc = lback->bi_extended( op, rs );
rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
/* FIXME: ldap_back_extended() by design
* doesn't send result; frontend is expected
* to send it... */
if ( rc != SLAPD_ABANDON ) {
send_ldap_extended( op, rs );
rc = LDAP_SUCCESS;
}
break;
default:
rc = SLAP_CB_CONTINUE;
break;
}
if ( sc2.sc_private == NULL ) {
op->o_callback = NULL;
rc = rs->sr_err = slap_map_api2result( rs );
send_ldap_result( op, rs );
}
op->o_do_not_cache = cache;
op->o_bd->be_private = private;
op->o_callback = sc;
op->o_ndn = ndn;
if ( authzid ) {
op->o_tmpfree( authzid, op->o_tmpmemctx );
}
rs->sr_ref = ref;
if ( lip->url == NULL && li.url != NULL ) {
ldap_memfree( li.url );
}
return rc;
}
@ -388,9 +441,9 @@ ldap_chain_db_init(
BackendDB *be
)
{
slap_overinst *on = (slap_overinst *) be->bd_info;
void *private = be->be_private;
int rc;
slap_overinst *on = (slap_overinst *)be->bd_info;
int rc;
BackendDB bd = *be;
if ( lback == NULL ) {
lback = backend_info( "ldap" );
@ -400,10 +453,9 @@ ldap_chain_db_init(
}
}
be->be_private = NULL;
rc = lback->bi_db_init( be );
on->on_bi.bi_private = be->be_private;
be->be_private = private;
bd.be_private = NULL;
rc = lback->bi_db_init( &bd );
on->on_bi.bi_private = bd.be_private;
return rc;
}
@ -447,10 +499,6 @@ chain_init( void )
ldapchain.on_response = ldap_chain_response;
#if 0
ldapchain.on_bi.bi_chk_referrals = ldap_chain_chk_referrals;
#endif
return overlay_register( &ldapchain );
}

View file

@ -42,9 +42,8 @@ ldap_back_compare(
LDAPControl **ctrls = NULL;
int rc = LDAP_SUCCESS;
lc = ldap_back_getconn( op, rs );
if (!lc || !ldap_back_dobind( lc, op, rs ) ) {
rc = -1;
lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
goto cleanup;
}
@ -52,7 +51,6 @@ ldap_back_compare(
rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls );
if ( rc != LDAP_SUCCESS ) {
send_ldap_result( op, rs );
rc = -1;
goto cleanup;
}
@ -61,10 +59,10 @@ retry:
op->orc_ava->aa_desc->ad_cname.bv_val,
&op->orc_ava->aa_value,
ctrls, NULL, &msgid );
rc = ldap_back_op_result( lc, op, rs, msgid, 1 );
if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
if ( rc == LDAP_UNAVAILABLE && do_retry ) {
do_retry = 0;
if ( ldap_back_retry(lc, op, rs ) ) {
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
goto retry;
}
}
@ -72,5 +70,5 @@ retry:
cleanup:
(void)ldap_back_proxy_authz_ctrl_free( op, &ctrls );
return rc;
return rs->sr_err;
}

View file

@ -213,6 +213,27 @@ ldap_back_db_config(
li->url = ch_strdup( argv[ 1 ] );
#endif
/* start tls */
} else if ( strcasecmp( argv[0], "start-tls" ) == 0 ) {
if ( argc != 1 ) {
fprintf( stderr,
"%s: line %d: start-tls takes no arguments\n",
fname, lineno );
return( 1 );
}
li->flags |= LDAP_BACK_F_TLS_CRITICAL;
/* try start tls */
} else if ( strcasecmp( argv[0], "try-start-tls" ) == 0 ) {
if ( argc != 1 ) {
fprintf( stderr,
"%s: line %d: try-start-tls takes no arguments\n",
fname, lineno );
return( 1 );
}
li->flags &= ~LDAP_BACK_F_TLS_CRITICAL;
li->flags |= LDAP_BACK_F_USE_TLS;
/* name to use for ldap_back_group */
} else if ( strcasecmp( argv[0], "acl-authcdn" ) == 0
|| strcasecmp( argv[0], "binddn" ) == 0 )
@ -272,7 +293,7 @@ ldap_back_db_config(
fname, lineno );
return( 1 );
}
li->savecred = 1;
li->flags |= LDAP_BACK_F_SAVECRED;
/* intercept exop_who_am_i? */
} else if ( strcasecmp( argv[0], "proxy-whoami" ) == 0 ) {
@ -360,8 +381,8 @@ ldap_back_exop_whoami(
ctrls[0] = &c;
op2.o_ndn = op->o_conn->c_ndn;
lc = ldap_back_getconn(&op2, rs);
if (!lc || !ldap_back_dobind( lc, op, rs )) {
lc = ldap_back_getconn(&op2, rs, LDAP_BACK_SENDERR);
if (!lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR )) {
return -1;
}
c.ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
@ -379,7 +400,7 @@ retry:
&rs->sr_err);
if ( rs->sr_err == LDAP_SERVER_DOWN && do_retry ) {
do_retry = 0;
if ( ldap_back_retry( lc, op, rs ) )
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) )
goto retry;
}
ldap_back_freeconn( op, lc );

View file

@ -42,9 +42,9 @@ ldap_back_delete(
int do_retry = 1;
int rc = LDAP_SUCCESS;
lc = ldap_back_getconn( op, rs );
lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
if ( !lc || !ldap_back_dobind( lc, op, rs ) ) {
if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
rc = -1;
goto cleanup;
}
@ -60,10 +60,10 @@ ldap_back_delete(
retry:
rs->sr_err = ldap_delete_ext( lc->lc_ld, op->o_req_ndn.bv_val,
ctrls, NULL, &msgid );
rc = ldap_back_op_result( lc, op, rs, msgid, 1 );
rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
if ( rs->sr_err == LDAP_SERVER_DOWN && do_retry ) {
do_retry = 0;
if ( ldap_back_retry (lc, op, rs ) ) {
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
goto retry;
}
}

View file

@ -56,8 +56,8 @@ ldap_back_extended(
* called twice; maybe we could avoid the
* ldap_back_dobind() call inside each extended()
* call ... */
lc = ldap_back_getconn( op, rs );
if ( !lc || !ldap_back_dobind( lc, op, rs ) ) {
lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
return -1;
}
@ -99,8 +99,8 @@ ldap_back_exop_passwd(
int rc, isproxy;
int do_retry = 1;
lc = ldap_back_getconn( op, rs );
if ( !lc || !ldap_back_dobind( lc, op, rs ) ) {
lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
return -1;
}
@ -154,7 +154,7 @@ retry:
rs->sr_err = slap_map_api2result( rs );
if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
do_retry = 0;
if ( ldap_back_retry(lc, op, rs ) ) {
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
goto retry;
}
}

View file

@ -144,10 +144,10 @@ ldap_back_db_open( BackendDB *be )
/* FIXME: disabled because namingContexts doesn't have
* a matching rule, and using an MRA filter doesn't work
* because the normalized assertion is compared to the
* non-normalized value, which in general differ.
* See ITS#3406 */
* non-normalized value, which in general differs from
* the normalized one. See ITS#3406 */
struct berval filter,
base = BER_BVC( "cn=Databases,cn=Monitor" );
base = BER_BVC( "cn=Databases," SLAPD_MONITOR );
struct berval vals[ 2 ];
Attribute a = { 0 };

View file

@ -46,8 +46,8 @@ ldap_back_modify(
int do_retry = 1;
LDAPControl **ctrls = NULL;
lc = ldap_back_getconn( op, rs );
if ( !lc || !ldap_back_dobind( lc, op, rs ) ) {
lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
return -1;
}
@ -106,10 +106,10 @@ ldap_back_modify(
retry:
rs->sr_err = ldap_modify_ext( lc->lc_ld, op->o_req_ndn.bv_val, modv,
ctrls, NULL, &msgid );
rc = ldap_back_op_result( lc, op, rs, msgid, 1 );
rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
do_retry = 0;
if ( ldap_back_retry(lc, op, rs ) ) {
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
goto retry;
}
}

View file

@ -43,8 +43,8 @@ ldap_back_modrdn(
int rc = LDAP_SUCCESS;
char *newSup = NULL;
lc = ldap_back_getconn( op, rs );
if ( !lc || !ldap_back_dobind( lc, op, rs ) ) {
lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
if ( !lc || !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
return( -1 );
}
@ -67,10 +67,10 @@ retry:
rs->sr_err = ldap_rename( lc->lc_ld, op->o_req_ndn.bv_val,
op->orr_newrdn.bv_val, newSup,
op->orr_deleteoldrdn, ctrls, NULL, &msgid );
rc = ldap_back_op_result( lc, op, rs, msgid, 1 );
rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
if ( rs->sr_err == LDAP_SERVER_DOWN && do_retry ) {
do_retry = 0;
if ( ldap_back_retry( lc, op, rs ) ) {
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
goto retry;
}
}

View file

@ -49,12 +49,12 @@ extern BI_connection_destroy ldap_back_conn_destroy;
extern BI_entry_get_rw ldap_back_entry_get;
int ldap_back_freeconn( Operation *op, struct ldapconn *lc );
struct ldapconn *ldap_back_getconn(struct slap_op *op, struct slap_rep *rs);
int ldap_back_dobind(struct ldapconn *lc, Operation *op, SlapReply *rs);
int ldap_back_retry(struct ldapconn *lc, Operation *op, SlapReply *rs);
struct ldapconn *ldap_back_getconn(struct slap_op *op, struct slap_rep *rs, ldap_back_send_t sendok);
int ldap_back_dobind(struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok);
int ldap_back_retry(struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok);
int ldap_back_map_result(SlapReply *rs);
int ldap_back_op_result(struct ldapconn *lc, Operation *op, SlapReply *rs,
ber_int_t msgid, int sendok);
ber_int_t msgid, ldap_back_send_t sendok);
int back_ldap_LTX_init_module(int argc, char *argv[]);
extern int ldap_back_conn_cmp( const void *c1, const void *c2);

View file

@ -60,17 +60,17 @@ ldap_back_search(
int do_retry = 1;
LDAPControl **ctrls = NULL;
lc = ldap_back_getconn( op, rs );
lc = ldap_back_getconn( op, rs, LDAP_BACK_SENDERR );
if ( !lc ) {
return -1;
return rs->sr_err;
}
/*
* FIXME: in case of values return filter, we might want
* to map attrs and maybe rewrite value
*/
if ( !ldap_back_dobind( lc, op, rs ) ) {
return -1;
if ( !ldap_back_dobind( lc, op, rs, LDAP_BACK_SENDERR ) ) {
return rs->sr_err;
}
/* should we check return values? */
@ -120,7 +120,7 @@ retry:
if ( rs->sr_err != LDAP_SUCCESS ) {
fail:;
rc = ldap_back_op_result( lc, op, rs, msgid, 0 );
rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDERR );
if ( freeconn ) {
ldap_back_freeconn( op, lc );
lc = NULL;
@ -249,7 +249,7 @@ fail:;
if ( rc == -1 ) {
if ( do_retry ) {
do_retry = 0;
if ( ldap_back_retry( lc, op, rs ) ) {
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
goto retry;
}
}
@ -500,7 +500,6 @@ ldap_back_entry_get(
*e = NULL;
char *gattr[3];
char *filter = NULL;
Connection *oconn;
SlapReply rs;
int do_retry = 1;
LDAPControl **ctrls = NULL;
@ -508,16 +507,12 @@ ldap_back_entry_get(
/* Tell getconn this is a privileged op */
do_not_cache = op->o_do_not_cache;
op->o_do_not_cache = 1;
lc = ldap_back_getconn( op, &rs );
oconn = op->o_conn;
op->o_conn = NULL;
if ( !lc || !ldap_back_dobind( lc, op, &rs ) ) {
lc = ldap_back_getconn( op, &rs, LDAP_BACK_DONTSEND );
if ( !lc || !ldap_back_dobind( lc, op, &rs, LDAP_BACK_DONTSEND ) ) {
op->o_do_not_cache = do_not_cache;
op->o_conn = oconn;
return 1;
return rs.sr_err;
}
op->o_do_not_cache = do_not_cache;
op->o_conn = oconn;
if ( at ) {
if ( oc && at != slap_schema.si_ad_objectClass ) {
@ -555,7 +550,7 @@ retry:
if ( rc != LDAP_SUCCESS ) {
if ( rc == LDAP_SERVER_DOWN && do_retry ) {
do_retry = 0;
if ( ldap_back_retry( lc, op, &rs ) ) {
if ( ldap_back_retry( lc, op, &rs, LDAP_BACK_DONTSEND ) ) {
goto retry;
}
}

View file

@ -85,15 +85,6 @@ typedef struct dncookie {
#define META_BIND_NRETRIES 3
#define META_BIND_TIMEOUT 1000
int ldap_back_freeconn( Operation *op, struct ldapconn *lc );
struct ldapconn *ldap_back_getconn(struct slap_op *op, struct slap_rep *rs);
int ldap_back_dobind(struct ldapconn *lc, Operation *op, SlapReply *rs);
int ldap_back_retry(struct ldapconn *lc, Operation *op, SlapReply *rs);
int ldap_back_map_result(SlapReply *rs);
int ldap_back_op_result(struct ldapconn *lc, Operation *op, SlapReply *rs,
ber_int_t msgid, int sendok);
int back_ldap_LTX_init_module(int argc, char *argv[]);
int ldap_back_dn_massage(dncookie *dc, struct berval *dn,
struct berval *res);

View file

@ -386,6 +386,10 @@ pager: +1 313 555 7671
facsimileTelephoneNumber: +1 313 555 7762
telephoneNumber: +1 313 555 4177
dn: ou=Other,dc=example,dc=com
objectClass: organizationalUnit
ou: Other
dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
objectClass: extensibleObject

View file

@ -365,6 +365,10 @@ pager: +1 313 555 7671
facsimileTelephoneNumber: +1 313 555 7762
telephoneNumber: +1 313 555 4177
dn: ou=Other,dc=example,dc=com
objectClass: organizationalUnit
ou: Other
dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
objectClass: extensibleObject

4
tests/data/chainref.out Normal file
View file

@ -0,0 +1,4 @@
dn: ou=Other,dc=example,dc=com
objectClass: organizationalUnit
ou: Other

View file

@ -29,6 +29,14 @@ objectclass: extensibleobject
ou: Groups
ref: @URI2@ou=Groups,dc=example,dc=com
dn: ou=Other,dc=example,dc=com
objectclass: referral
objectclass: extensibleobject
ou: Other
# invalid URI first to test failover capabilities (search only)
ref: @URI3@ou=Other,dc=example,dc=com
ref: @URI2@ou=Other,dc=example,dc=com
dn: ou=Alumni Association,ou=People,dc=example,dc=com
objectclass: organizationalUnit
ou: Alumni Association

View file

@ -81,3 +81,8 @@ cn: Dir Man
sn: Manager
description: Manager of the directory
userpassword:: c2VjcmV0
dn: ou=Other,dc=example,dc=com
objectclass: organizationalUnit
ou: Other

View file

@ -235,6 +235,7 @@ LDAPGLUEOUT=$DATADIR/ldapglue.out
LDAPGLUEANONYMOUSOUT=$DATADIR/ldapglueanonymous.out
RELAYOUT=$DATADIR/relay.out
CHAINOUT=$DATADIR/chain.out
CHAINREFOUT=$DATADIR/chainref.out
CHAINMODOUT=$DATADIR/chainmod.out
SQLREAD=$DATADIR/sql-read.out
SQLWRITE=$DATADIR/sql-write.out

View file

@ -110,6 +110,31 @@ for P in $PORT1 $PORT2 ; do
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit 1
fi
echo "Reading the referral entry "ou=Other,$BASEDN" as anonymous on port $P..."
$LDAPSEARCH -h $LOCALHOST -p $P -b "ou=Other,$BASEDN" -S "" \
> $SEARCHOUT 2>&1
RC=$?
if test $RC != 0 ; then
echo "ldapsearch failed ($RC)!"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit $RC
fi
echo "Filtering ldapsearch results..."
. $LDIFFILTER < $SEARCHOUT > $SEARCHFLT
echo "Filtering original ldif used to create database..."
. $LDIFFILTER < $CHAINREFOUT > $LDIFFLT
echo "Comparing filter output..."
$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
if test $? != 0 ; then
echo "comparison failed - chained search didn't succeed"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit 1
fi
done
#