X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;ds=sidebyside;f=servers%2Fslapd%2Fback-ldap%2Fchain.c;h=32783ed0a9765a66baea47b8f39e484290920a43;hb=c6f12694fc959cde780fe83adfa535cce7998b89;hp=47cc33e97534e8f4a12a3cb62c48bb9f48ca4eb3;hpb=672c39024e9ae6c0c23e4581d87bd24a4be6f780;p=openldap diff --git a/servers/slapd/back-ldap/chain.c b/servers/slapd/back-ldap/chain.c index 47cc33e975..32783ed0a9 100644 --- a/servers/slapd/back-ldap/chain.c +++ b/servers/slapd/back-ldap/chain.c @@ -2,7 +2,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 2003-2005 The OpenLDAP Foundation. + * Copyright 2003-2008 The OpenLDAP Foundation. * Portions Copyright 2003 Howard Chu. * All rights reserved. * @@ -17,6 +17,7 @@ /* ACKNOWLEDGEMENTS: * This work was initially developed by the Howard Chu for inclusion * in OpenLDAP Software. + * This work was subsequently modified by Pierangelo Masarati. */ #include "portable.h" @@ -26,9 +27,9 @@ #include #include +#include "lutil.h" #include "slap.h" #include "back-ldap.h" - #include "config.h" #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR @@ -57,17 +58,49 @@ static int sc_chainingBehavior; #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ -#define LDAP_CH_NONE ((void *)(0)) -#define LDAP_CH_RES ((void *)(1)) -#define LDAP_CH_ERR ((void *)(2)) - +typedef enum { + LDAP_CH_NONE = 0, + LDAP_CH_RES, + LDAP_CH_ERR +} ldap_chain_status_t; static BackendInfo *lback; typedef struct ldap_chain_t { - struct ldapinfo *lc_li; + /* + * A "template" ldapinfo_t gets all common configuration items; + * then, for each configured URI, an entry is created in the tree; + * all the specific configuration items get in the current URI + * structure. + * + * Then, for each referral, extract the URI and lookup the + * related structure. If configured to do so, allow URIs + * not found in the structure to create a temporary one + * that chains anonymously; maybe it can also be added to + * the tree? Should be all configurable. + */ + + /* "common" configuration info (anything occurring before an "uri") */ + ldapinfo_t *lc_common_li; + + /* current configuration info */ + ldapinfo_t *lc_cfg_li; + + /* tree of configured[/generated?] "uri" info */ + ldap_avl_info_t lc_lai; + + /* max depth in nested referrals chaining */ + int lc_max_depth; + unsigned lc_flags; -#define LDAP_CHAIN_F_NONE 0x00U -#define LDAP_CHAIN_F_CHAINING 0x01U +#define LDAP_CHAIN_F_NONE (0x00U) +#define LDAP_CHAIN_F_CHAINING (0x01U) +#define LDAP_CHAIN_F_CACHE_URI (0x02U) +#define LDAP_CHAIN_F_RETURN_ERR (0x04U) + +#define LDAP_CHAIN_ISSET(lc, f) ( ( (lc)->lc_flags & (f) ) == (f) ) +#define LDAP_CHAIN_CHAINING( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING ) +#define LDAP_CHAIN_CACHE_URI( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI ) +#define LDAP_CHAIN_RETURN_ERR( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR ) #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR LDAPControl lc_chaining_ctrl; @@ -75,6 +108,36 @@ typedef struct ldap_chain_t { #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ } ldap_chain_t; +static int ldap_chain_db_init_common( BackendDB *be ); +static int ldap_chain_db_init_one( BackendDB *be ); +static int ldap_chain_db_open_one( BackendDB *be ); +#define ldap_chain_db_close_one(be) (0) +#define ldap_chain_db_destroy_one(be, rs) (lback)->bi_db_destroy( (be), (rs) ) + +typedef struct ldap_chain_cb_t { + ldap_chain_status_t lb_status; + ldap_chain_t *lb_lc; + BI_op_func *lb_op_f; + int lb_depth; +} ldap_chain_cb_t; + +static int +ldap_chain_op( + Operation *op, + SlapReply *rs, + BI_op_func *op_f, + BerVarray ref, + int depth ); + +static int +ldap_chain_search( + Operation *op, + SlapReply *rs, + BerVarray ref, + int depth ); + +static slap_overinst ldapchain; + #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR static int chaining_control_add( @@ -88,7 +151,7 @@ chaining_control_add( *oldctrlsp = op->o_ctrls; /* default chaining control not defined */ - if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) { + if ( !LDAP_CHAIN_CHAINING( lc ) ) { return 0; } @@ -132,8 +195,8 @@ chaining_control_remove( * added by the chain overlay, so it's the only one we explicitly * free */ if ( op->o_ctrls != oldctrls ) { - assert( op->o_ctrls ); - assert( op->o_ctrls[ 0 ] ); + assert( op->o_ctrls != NULL ); + assert( op->o_ctrls[ 0 ] != NULL ); free( op->o_ctrls ); @@ -148,22 +211,43 @@ chaining_control_remove( #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ static int -ldap_chain_operational( Operation *op, SlapReply *rs ) +ldap_chain_uri_cmp( const void *c1, const void *c2 ) { - /* 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 - * calls that fail because operational attributes were - * requested for entries that do not belong to the underlying - * database. This fix is likely to intercept also entries - * generated by back-perl and so. */ - if ( rs->sr_entry->e_private == NULL ) { - return 0; - } + const ldapinfo_t *li1 = (const ldapinfo_t *)c1; + const ldapinfo_t *li2 = (const ldapinfo_t *)c2; - return SLAP_CB_CONTINUE; + assert( li1->li_bvuri != NULL ); + assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) ); + assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) ); + + assert( li2->li_bvuri != NULL ); + assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) ); + assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) ); + + /* If local DNs don't match, it is definitely not a match */ + return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ); +} + +static int +ldap_chain_uri_dup( void *c1, void *c2 ) +{ + ldapinfo_t *li1 = (ldapinfo_t *)c1; + ldapinfo_t *li2 = (ldapinfo_t *)c2; + + assert( li1->li_bvuri != NULL ); + assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) ); + assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) ); + + assert( li2->li_bvuri != NULL ); + assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) ); + assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) ); + + /* Cannot have more than one shared session with same DN */ + if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) { + return -1; + } + + return 0; } /* @@ -172,10 +256,12 @@ ldap_chain_operational( Operation *op, SlapReply *rs ) static int ldap_chain_cb_search_response( Operation *op, SlapReply *rs ) { + ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private; + assert( op->o_tag == LDAP_REQ_SEARCH ); /* if in error, don't proceed any further */ - if ( op->o_callback->sc_private == LDAP_CH_ERR ) { + if ( lb->lb_status == LDAP_CH_ERR ) { return 0; } @@ -197,19 +283,26 @@ ldap_chain_cb_search_response( Operation *op, SlapReply *rs ) break; } } + + /* tell the frontend not to add generated + * operational attributes */ + rs->sr_flags |= REP_NO_OPERATIONALS; return SLAP_CB_CONTINUE; } else if ( rs->sr_type == REP_SEARCHREF ) { /* if we get it here, it means the library was unable * to chase the referral... */ + if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) { + rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth ); + } #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) { + if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) { switch ( get_continuationBehavior( op ) ) { case SLAP_CH_RESOLVE_CHAINING_REQUIRED: - op->o_callback->sc_private = LDAP_CH_ERR; - return -1; + lb->lb_status = LDAP_CH_ERR; + return rs->sr_err = LDAP_X_CANNOT_CHAIN; default: break; @@ -219,8 +312,15 @@ ldap_chain_cb_search_response( Operation *op, SlapReply *rs ) return SLAP_CB_CONTINUE; } else if ( rs->sr_type == REP_RESULT ) { + if ( rs->sr_err == LDAP_REFERRAL + && lb->lb_depth < lb->lb_lc->lc_max_depth + && rs->sr_ref != NULL ) + { + rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth ); + } + /* back-ldap tried to send result */ - op->o_callback->sc_private = LDAP_CH_RES; + lb->lb_status = LDAP_CH_RES; } return 0; @@ -233,13 +333,50 @@ ldap_chain_cb_search_response( Operation *op, SlapReply *rs ) static int ldap_chain_cb_response( Operation *op, SlapReply *rs ) { + ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private; + /* if in error, don't proceed any further */ - if ( op->o_callback->sc_private == LDAP_CH_ERR ) { + if ( lb->lb_status == LDAP_CH_ERR ) { return 0; } if ( rs->sr_type == REP_RESULT ) { - op->o_callback->sc_private = LDAP_CH_RES; +retry:; + switch ( rs->sr_err ) { + case LDAP_COMPARE_TRUE: + case LDAP_COMPARE_FALSE: + if ( op->o_tag != LDAP_REQ_COMPARE ) { + return rs->sr_err; + } + /* fallthru */ + + case LDAP_SUCCESS: + lb->lb_status = LDAP_CH_RES; + break; + + case LDAP_REFERRAL: + if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) { + rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth ); + goto retry; + } + +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR + if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) { + switch ( get_continuationBehavior( op ) ) { + case SLAP_CH_RESOLVE_CHAINING_REQUIRED: + lb->lb_status = LDAP_CH_ERR; + return rs->sr_err = LDAP_X_CANNOT_CHAIN; + + default: + break; + } + } +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + break; + + default: + return rs->sr_err; + } } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH ) { @@ -254,15 +391,19 @@ static int ldap_chain_op( Operation *op, SlapReply *rs, - int ( *op_f )( Operation *op, SlapReply *rs ), - BerVarray ref ) + BI_op_func *op_f, + BerVarray ref, + int depth ) { slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; + ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private; ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; - struct ldapinfo li, *lip = lc->lc_li; + ldapinfo_t li = { 0 }, *lip = NULL; + struct berval bvuri[ 2 ] = { { 0 } }; /* NOTE: returned if ref is empty... */ - int rc = LDAP_OTHER; + int rc = LDAP_OTHER, + first_rc; #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR LDAPControl **ctrls = NULL; @@ -270,30 +411,22 @@ ldap_chain_op( (void)chaining_control_add( lc, op, &ctrls ); #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ - if ( lip->url != NULL ) { - op->o_bd->be_private = lip; - rc = ( *op_f )( op, rs ); - goto done; - } - - 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; - + li.li_bvuri = bvuri; + first_rc = -1; for ( ; !BER_BVISNULL( ref ); ref++ ) { - LDAPURLDesc *srv; - char *save_dn; + SlapReply rs2 = { 0 }; + LDAPURLDesc *srv = NULL; + struct berval save_req_dn = op->o_req_dn, + save_req_ndn = op->o_req_ndn, + dn = BER_BVNULL, + pdn = BER_BVNULL, + ndn = BER_BVNULL; + int temporary = 0; /* We're setting the URI of the first referral; * what if there are more? -Document: draft-ietf-ldapbis-protocol-27.txt +Document: RFC 4511 4.1.10. Referral ... @@ -308,44 +441,337 @@ Document: draft-ietf-ldapbis-protocol-27.txt /* parse reference and use * proto://[host][:port]/ only */ - rc = ldap_url_parse_ext( ref->bv_val, &srv ); + rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE ); 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 = ""; + /* normalize DN */ + rc = LDAP_SUCCESS; srv->lud_scope = LDAP_SCOPE_DEFAULT; - li.url = ldap_url_desc2str( srv ); - srv->lud_dn = save_dn; + if ( srv->lud_dn != NULL ) { + ber_str2bv( srv->lud_dn, 0, 0, &dn ); + rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx ); + if ( rc == LDAP_SUCCESS ) { + /* remove DN essentially because later on + * ldap_initialize() will parse the URL + * as a comma-separated URL list */ + srv->lud_dn = ""; + } + + } else { + srv->lud_dn = ""; + } + + li.li_uri = ldap_url_desc2str( srv ); + srv->lud_dn = dn.bv_val; ldap_free_urldesc( srv ); - if ( li.url == NULL ) { + if ( rc != LDAP_SUCCESS ) { /* try next */ rc = LDAP_OTHER; continue; } - rc = ( *op_f )( op, rs ); + if ( li.li_uri == NULL ) { + /* try next */ + rc = LDAP_OTHER; + goto further_cleanup; + } + + op->o_req_dn = pdn; + op->o_req_ndn = ndn; + + ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] ); + + /* Searches for a ldapinfo in the avl tree */ + ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); + lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, + (caddr_t)&li, ldap_chain_uri_cmp ); + ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); - ldap_memfree( li.url ); - li.url = NULL; + if ( lip != NULL ) { + op->o_bd->be_private = (void *)lip; + + } else { + rc = ldap_chain_db_init_one( op->o_bd ); + if ( rc != 0 ) { + goto cleanup; + } + lip = (ldapinfo_t *)op->o_bd->be_private; + lip->li_uri = li.li_uri; + lip->li_bvuri = bvuri; + rc = ldap_chain_db_open_one( op->o_bd ); + if ( rc != 0 ) { + lip->li_uri = NULL; + lip->li_bvuri = NULL; + (void)ldap_chain_db_destroy_one( op->o_bd, NULL); + goto cleanup; + } + + if ( LDAP_CHAIN_CACHE_URI( lc ) ) { + ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); + if ( avl_insert( &lc->lc_lai.lai_tree, + (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) ) + { + /* someone just inserted another; + * don't bother, use this and then + * just free it */ + temporary = 1; + } + ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); + + } else { + temporary = 1; + } + } + + lb->lb_op_f = op_f; + lb->lb_depth = depth + 1; + + rc = op_f( op, &rs2 ); + + /* note the first error */ + if ( first_rc == -1 ) { + first_rc = rc; + } + +cleanup:; + ldap_memfree( li.li_uri ); + li.li_uri = NULL; + + if ( temporary ) { + lip->li_uri = NULL; + lip->li_bvuri = NULL; + (void)ldap_chain_db_close_one( op->o_bd ); + (void)ldap_chain_db_destroy_one( op->o_bd, NULL ); + } + +further_cleanup:; + if ( !BER_BVISNULL( &pdn ) ) { + op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx ); + } + op->o_req_dn = save_req_dn; + + if ( !BER_BVISNULL( &ndn ) ) { + op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); + } + op->o_req_ndn = save_req_ndn; - if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) { + if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) { + *rs = rs2; break; } + + rc = rs2.sr_err; } -done:; #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR (void)chaining_control_remove( op, &ctrls ); #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + if ( rc != LDAP_SUCCESS && first_rc > 0 ) { + rc = first_rc; + } + + return rc; +} + +static int +ldap_chain_search( + Operation *op, + SlapReply *rs, + BerVarray ref, + int depth ) + +{ + slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; + ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private; + ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; + ldapinfo_t li = { 0 }, *lip = NULL; + struct berval bvuri[ 2 ] = { { 0 } }; + + struct berval odn = op->o_req_dn, + ondn = op->o_req_ndn; + slap_response *save_response = op->o_callback->sc_response; + + int rc = LDAP_OTHER, + first_rc = -1; + +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR + LDAPControl **ctrls = NULL; + + (void)chaining_control_add( lc, op, &ctrls ); +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + + rs->sr_type = REP_SEARCH; + + op->o_callback->sc_response = ldap_chain_cb_search_response; + + /* 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?) */ + li.li_bvuri = bvuri; + for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) { + SlapReply rs2 = { 0 }; + LDAPURLDesc *srv; + struct berval save_req_dn = op->o_req_dn, + save_req_ndn = op->o_req_ndn, + dn, + pdn = BER_BVNULL, + ndn = BER_BVNULL; + int temporary = 0; + + /* parse reference and use + * proto://[host][:port]/ only */ + rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE ); + if ( rc != LDAP_URL_SUCCESS ) { + /* try next */ + rs->sr_err = LDAP_OTHER; + continue; + } + + /* normalize DN */ + rc = LDAP_INVALID_SYNTAX; + if ( srv->lud_dn != NULL ) { + ber_str2bv( srv->lud_dn, 0, 0, &dn ); + rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx ); + if ( rc == LDAP_SUCCESS ) { + /* remove DN essentially because later on + * ldap_initialize() will parse the URL + * as a comma-separated URL list */ + srv->lud_dn = ""; + srv->lud_scope = LDAP_SCOPE_DEFAULT; + li.li_uri = ldap_url_desc2str( srv ); + srv->lud_dn = dn.bv_val; + } + } + ldap_free_urldesc( srv ); + + if ( rc != LDAP_SUCCESS ) { + /* try next */ + rc = LDAP_OTHER; + continue; + } + + if ( li.li_uri == NULL ) { + /* try next */ + rc = LDAP_OTHER; + goto further_cleanup; + } + + op->o_req_dn = pdn; + op->o_req_ndn = ndn; + + ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] ); + + /* Searches for a ldapinfo in the avl tree */ + ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); + lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, + (caddr_t)&li, ldap_chain_uri_cmp ); + ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); + + if ( lip != NULL ) { + op->o_bd->be_private = (void *)lip; + + } else { + /* if none is found, create a temporary... */ + rc = ldap_chain_db_init_one( op->o_bd ); + if ( rc != 0 ) { + goto cleanup; + } + lip = (ldapinfo_t *)op->o_bd->be_private; + lip->li_uri = li.li_uri; + lip->li_bvuri = bvuri; + rc = ldap_chain_db_open_one( op->o_bd ); + if ( rc != 0 ) { + lip->li_uri = NULL; + lip->li_bvuri = NULL; + (void)ldap_chain_db_destroy_one( op->o_bd, NULL ); + goto cleanup; + } + + if ( LDAP_CHAIN_CACHE_URI( lc ) ) { + ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); + if ( avl_insert( &lc->lc_lai.lai_tree, + (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) ) + { + /* someone just inserted another; + * don't bother, use this and then + * just free it */ + temporary = 1; + } + ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); + + } else { + temporary = 1; + } + } + + lb->lb_op_f = lback->bi_op_search; + lb->lb_depth = depth + 1; + + /* FIXME: should we also copy filter and scope? + * according to RFC3296, no */ + rc = lback->bi_op_search( op, &rs2 ); + if ( first_rc == -1 ) { + first_rc = rc; + } + +cleanup:; + ldap_memfree( li.li_uri ); + li.li_uri = NULL; + + if ( temporary ) { + lip->li_uri = NULL; + lip->li_bvuri = NULL; + (void)ldap_chain_db_close_one( op->o_bd ); + (void)ldap_chain_db_destroy_one( op->o_bd, NULL ); + } + +further_cleanup:; + if ( !BER_BVISNULL( &pdn ) ) { + op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx ); + } + op->o_req_dn = save_req_dn; + + if ( !BER_BVISNULL( &ndn ) ) { + op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); + } + op->o_req_ndn = save_req_ndn; + + if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) { + *rs = rs2; + break; + } + + rc = rs2.sr_err; + } + +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR + (void)chaining_control_remove( op, &ctrls ); +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + + op->o_req_dn = odn; + op->o_req_ndn = ondn; + op->o_callback->sc_response = save_response; + rs->sr_type = REP_SEARCHREF; + rs->sr_entry = NULL; + + if ( rc != LDAP_SUCCESS ) { + /* couldn't chase any of the referrals */ + if ( first_rc != -1 ) { + rc = first_rc; + + } else { + rc = SLAP_CB_CONTINUE; + } + } + return rc; } @@ -353,20 +779,20 @@ 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; + ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; + BackendDB db, *bd = op->o_bd; + ldap_chain_cb_t lb = { 0 }; slap_callback *sc = op->o_callback, sc2 = { 0 }; int rc = 0; - int cache = op->o_do_not_cache; + const char *text = NULL; + const char *matched; BerVarray ref; struct berval ndn = op->o_ndn; - ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; - struct ldapinfo li, *lip = lc->lc_li; - -#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR int sr_err = rs->sr_err; slap_reply_t sr_type = rs->sr_type; +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR slap_mask_t chain_mask = 0; ber_len_t chain_shift = 0; #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ @@ -411,10 +837,20 @@ ldap_chain_response( Operation *op, SlapReply *rs ) * e) what ssf */ + db = *op->o_bd; + SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING; + op->o_bd = &db; + + text = rs->sr_text; + rs->sr_text = NULL; + matched = rs->sr_matched; + rs->sr_matched = NULL; ref = rs->sr_ref; rs->sr_ref = NULL; /* we need this to know if back-ldap returned any result */ + lb.lb_lc = lc; + sc2.sc_private = &lb; sc2.sc_response = ldap_chain_cb_response; op->o_callback = &sc2; @@ -435,181 +871,91 @@ ldap_chain_response( Operation *op, SlapReply *rs ) /* FIXME: can we really get a referral for binds? */ op->o_req_ndn = slap_empty_bv; op->o_conn = NULL; - rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 ); op->o_req_ndn = rndn; op->o_conn = conn; } break; + case LDAP_REQ_ADD: - { - int cleanup_attrs = 0; - - if ( op->ora_e->e_attrs == NULL ) { - char textbuf[ SLAP_TEXT_BUFLEN ]; - size_t textlen = sizeof( textbuf ); - -#if 0 - /* FIXME: op->o_bd is still set to the BackendDB - * structure of the database that tried to handle - * the operation and actually returned a referral - * ... */ - assert( SLAP_DBFLAGS( op->o_bd ) & SLAP_DBFLAG_GLOBAL_OVERLAY ); -#endif - - /* 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 - * do_add(), adding the operational attrs later - * if required. */ - rs->sr_err = slap_mods2entry( op->ora_modlist, - &op->ora_e, 0, 1, - &rs->sr_text, textbuf, textlen ); - if ( rs->sr_err != LDAP_SUCCESS ) { - send_ldap_result( op, rs ); - rc = 1; - break; - } - } - 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; - } + rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 ); break; - } + case LDAP_REQ_DELETE: - rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 ); break; + case LDAP_REQ_MODRDN: - rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 ); break; + case LDAP_REQ_MODIFY: - rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 ); break; + case LDAP_REQ_COMPARE: - rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref ); + rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 ); + if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) { + rc = LDAP_SUCCESS; + } 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; - -#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - LDAPControl **ctrls = NULL; - - (void)chaining_control_add( lc, op, &ctrls ); -#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ - - rs->sr_type = REP_SEARCH; - - sc2.sc_response = ldap_chain_cb_search_response; - - li = *lip; - li.url = NULL; - op->o_bd->be_private = &li; + rc = ldap_chain_search( op, rs, ref, 0 ); - /* 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; - char *save_dn; - - /* parse reference and use - * proto://[host][:port]/ only */ - 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 - * 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 ); - if ( li.url != NULL ) { - ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn, - op->o_tmpmemctx ); - ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn, - op->o_tmpmemctx ); - } - - srv->lud_dn = save_dn; - ldap_free_urldesc( srv ); - - if ( li.url == NULL ) { - /* try next */ - rs->sr_err = LDAP_OTHER; - continue; - } - - - /* FIXME: should we also copy filter and scope? - * according to RFC3296, no */ - rc = lback->bi_op_search( op, rs ); - - ldap_memfree( li.url ); - li.url = NULL; - - op->o_tmpfree( op->o_req_dn.bv_val, - op->o_tmpmemctx ); - op->o_tmpfree( op->o_req_ndn.bv_val, - op->o_tmpmemctx ); - - if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) { - break; - } - - rc = rs->sr_err; - } - -#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - (void)chaining_control_remove( op, &ctrls ); -#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ - - op->o_req_dn = odn; - op->o_req_ndn = ondn; - rs->sr_type = REP_SEARCHREF; - rs->sr_entry = NULL; + } else { + /* we might get here before any database actually + * performed a search; in those cases, we need + * to check limits, to make sure safe defaults + * are in place */ + if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) { + rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 ); - if ( rc != LDAP_SUCCESS ) { - /* couldn't chase any of the referrals */ + } else { rc = SLAP_CB_CONTINUE; } - - } else { - rc = ldap_chain_op( op, rs, lback->bi_op_search, ref ); } break; + case LDAP_REQ_EXTENDED: - rc = ldap_chain_op( op, rs, lback->bi_extended, ref ); + rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 ); /* FIXME: ldap_back_extended() by design * doesn't send result; frontend is expected * to send it... */ + /* FIXME: what about chaining? */ if ( rc != SLAPD_ABANDON ) { + rs->sr_err = rc; send_ldap_extended( op, rs ); rc = LDAP_SUCCESS; } + lb.lb_status = LDAP_CH_RES; break; + default: rc = SLAP_CB_CONTINUE; break; } + switch ( rc ) { + case SLAPD_ABANDON: + goto dont_chain; + + case LDAP_SUCCESS: + case LDAP_REFERRAL: + /* slapd-ldap sent response */ + if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) { + /* FIXME: should we send response? */ + Debug( LDAP_DEBUG_ANY, + "%s: ldap_chain_response: " + "overlay should have sent result.\n", + op->o_log_prefix, 0, 0 ); + } + break; + + default: #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - if ( rc != LDAP_SUCCESS || sc2.sc_private == LDAP_CH_ERR ) { - if ( rs->sr_err == LDAP_CANNOT_CHAIN ) { + if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) { goto cannot_chain; } @@ -617,33 +963,45 @@ ldap_chain_response( Operation *op, SlapReply *rs ) case LDAP_CHAINING_REQUIRED: cannot_chain:; op->o_callback = NULL; - send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" ); - break; + send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN, + "operation cannot be completed without chaining" ); + goto dont_chain; default: - rc = SLAP_CB_CONTINUE; - rs->sr_err = sr_err; - rs->sr_type = sr_type; +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + if ( LDAP_CHAIN_RETURN_ERR( lc ) ) { + rs->sr_err = rc; + rs->sr_type = sr_type; + + } else { + rc = SLAP_CB_CONTINUE; + rs->sr_err = sr_err; + rs->sr_type = sr_type; + rs->sr_text = text; + rs->sr_matched = matched; + rs->sr_ref = ref; + } +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR break; } - goto dont_chain; - } #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + } - if ( sc2.sc_private == LDAP_CH_NONE ) { + if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) { op->o_callback = NULL; rc = rs->sr_err = slap_map_api2result( rs ); send_ldap_result( op, rs ); } -#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR dont_chain:; -#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ - op->o_do_not_cache = cache; - op->o_bd->be_private = private; + rs->sr_err = sr_err; + rs->sr_type = sr_type; + rs->sr_text = text; + rs->sr_matched = matched; + rs->sr_ref = ref; + op->o_bd = bd; op->o_callback = sc; op->o_ndn = ndn; - rs->sr_ref = ref; return rc; } @@ -675,46 +1033,198 @@ str2chain( const char *s ) } #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ +/* + * configuration... + */ + enum { - PC_CHAINING = 1 + CH_CHAINING = 1, + CH_CACHE_URI, + CH_MAX_DEPTH, + CH_RETURN_ERR, + + CH_LAST +}; + +static ConfigDriver chain_cf_gen; +static ConfigCfAdd chain_cfadd; +static ConfigLDAPadd chain_ldadd; + +static ConfigTable chaincfg[] = { +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR + { "chain-chaining", "args", + 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen, + "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' " + "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' " + "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL }, +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + { "chain-cache-uri", "TRUE/FALSE", + 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen, + "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' " + "DESC 'Enables caching of URIs not present in configuration' " + "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, + { "chain-max-depth", "args", + 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen, + "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' " + "DESC 'max referral depth' " + "SYNTAX OMsInteger " + "EQUALITY integerMatch " + "SINGLE-VALUE )", NULL, NULL }, + { "chain-return-error", "TRUE/FALSE", + 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen, + "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' " + "DESC 'Errors are returned instead of the original referral' " + "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, + { NULL, NULL, 0, 0, 0, ARG_IGNORED } }; -static ConfigDriver chain_cf_gen; -static ConfigLDAPadd chain_ldadd; -static ConfigCfAdd chain_cfadd; +static ConfigOCs chainocs[] = { + { "( OLcfgOvOc:3.1 " + "NAME 'olcChainConfig' " + "DESC 'Chain configuration' " + "SUP olcOverlayConfig " + "MAY ( " +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR + "olcChainingBehavior $ " +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + "olcChainCacheURI $ " + "olcChainMaxReferralDepth $ " + "olcChainReturnError " + ") )", + Cft_Overlay, chaincfg, NULL, chain_cfadd }, + { "( OLcfgOvOc:3.2 " + "NAME 'olcChainDatabase' " + "DESC 'Chain remote server configuration' " + "AUXILIARY )", + Cft_Misc, chaincfg, chain_ldadd }, + { NULL, 0, NULL } +}; + +static int +chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca ) +{ + slap_overinst *on; + ldap_chain_t *lc; + + ldapinfo_t *li; + + AttributeDescription *ad = NULL; + Attribute *at; + const char *text; + + int rc; + + if ( p->ce_type != Cft_Overlay + || !p->ce_bi + || p->ce_bi->bi_cf_ocs != chainocs ) + { + return LDAP_CONSTRAINT_VIOLATION; + } + + on = (slap_overinst *)p->ce_bi; + lc = (ldap_chain_t *)on->on_bi.bi_private; + + assert( ca->be == NULL ); + ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) ); + + ca->be->bd_info = (BackendInfo *)on; + + rc = slap_str2ad( "olcDbURI", &ad, &text ); + assert( rc == LDAP_SUCCESS ); + + at = attr_find( e->e_attrs, ad ); + if ( lc->lc_common_li == NULL && at != NULL ) { + /* FIXME: we should generate an empty default entry + * if none is supplied */ + Debug( LDAP_DEBUG_ANY, "slapd-chain: " + "first underlying database \"%s\" " + "cannot contain attribute \"%s\".\n", + e->e_name.bv_val, ad->ad_cname.bv_val, 0 ); + rc = LDAP_CONSTRAINT_VIOLATION; + goto done; + + } else if ( lc->lc_common_li != NULL && at == NULL ) { + /* FIXME: we should generate an empty default entry + * if none is supplied */ + Debug( LDAP_DEBUG_ANY, "slapd-chain: " + "subsequent underlying database \"%s\" " + "must contain attribute \"%s\".\n", + e->e_name.bv_val, ad->ad_cname.bv_val, 0 ); + rc = LDAP_CONSTRAINT_VIOLATION; + goto done; + } + + if ( lc->lc_common_li == NULL ) { + rc = ldap_chain_db_init_common( ca->be ); + + } else { + rc = ldap_chain_db_init_one( ca->be ); + } + + if ( rc != 0 ) { + Debug( LDAP_DEBUG_ANY, "slapd-chain: " + "unable to init %sunderlying database \"%s\".\n", + lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 ); + return LDAP_CONSTRAINT_VIOLATION; + } + + li = ca->be->be_private; + + if ( lc->lc_common_li == NULL ) { + lc->lc_common_li = li; + + } else { + li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val ); + value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] ); + if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li, + ldap_chain_uri_cmp, ldap_chain_uri_dup ) ) + { + Debug( LDAP_DEBUG_ANY, "slapd-chain: " + "database \"%s\" insert failed.\n", + e->e_name.bv_val, 0, 0 ); + rc = LDAP_CONSTRAINT_VIOLATION; + goto done; + } + } + +done:; + if ( rc != LDAP_SUCCESS ) { + (void)ldap_chain_db_destroy_one( ca->be, NULL ); + ch_free( ca->be ); + ca->be = NULL; + } -static ConfigTable chaincfg[] = { -#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - { "chain-chaining", "args", - 2, 4, 0, ARG_MAGIC|ARG_BERVAL|PC_CHAINING, chain_cf_gen, - "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' " - "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' " - "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL }, -#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ - { NULL, NULL, 0, 0, 0, ARG_IGNORED } -}; + return rc; +} -static ConfigOCs chainocs[] = { - { "( OLcfgOvOc:3.1 " - "NAME 'olcChainConfig' " - "DESC 'Chain configuration' " - "SUP olcOverlayConfig " - "MAY olcChainingBehavior )", Cft_Overlay, chaincfg, NULL, chain_cfadd }, - { "( OLcfgOvOc:3.2 " - "NAME 'olcChainDatabase' " - "DESC 'Chain remote server configuration' " - "AUXILIARY )", Cft_Misc, chaincfg, chain_ldadd }, - { NULL, 0, NULL } -}; +typedef struct ldap_chain_cfadd_apply_t { + Operation *op; + SlapReply *rs; + Entry *p; + ConfigArgs *ca; + int count; +} ldap_chain_cfadd_apply_t; static int -chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca ) +ldap_chain_cfadd_apply( void *datum, void *arg ) { - if ( p->ce_type != Cft_Overlay || !p->ce_bi || - p->ce_bi->bi_cf_ocs != chainocs ) - return LDAP_CONSTRAINT_VIOLATION; + ldapinfo_t *li = (ldapinfo_t *)datum; + ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg; - return LDAP_SUCCESS; + struct berval bv; + + /* FIXME: should not hardcode "olcDatabase" here */ + bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ), + "olcDatabase={%d}%s", lca->count, lback->bi_type ); + bv.bv_val = lca->ca->cr_msg; + + lca->ca->be->be_private = (void *)li; + config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca, + &bv, lback->bi_cf_ocs, &chainocs[1] ); + + lca->count++; + + return 0; } static int @@ -724,22 +1234,28 @@ chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca ) slap_overinst *on = (slap_overinst *)pe->ce_bi; ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; void *priv = (void *)ca->be->be_private; - struct berval bv; - /* FIXME: should not hardcode "olcDatabase" here */ - bv.bv_len = sprintf( ca->msg, "olcDatabase=%s", lback->bi_type ); - bv.bv_val = ca->msg; - - /* We can only create this entry if the database is table-driven */ if ( lback->bi_cf_ocs ) { - ca->be->be_private = (void *)lc->lc_li; - config_build_entry( op, rs, pe, ca, &bv, lback->bi_cf_ocs, &chainocs[1] ); + ldap_chain_cfadd_apply_t lca = { 0 }; + + lca.op = op; + lca.rs = rs; + lca.p = p; + lca.ca = ca; + lca.count = 0; + + (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca ); + + (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply, + &lca, 1, AVL_INORDER ); + ca->be->be_private = priv; } return 0; } +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR static slap_verbmasks chaining_mode[] = { { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED }, { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED }, @@ -747,6 +1263,7 @@ static slap_verbmasks chaining_mode[] = { { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED }, { BER_BVNULL, 0 } }; +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ static int chain_cf_gen( ConfigArgs *c ) @@ -759,11 +1276,11 @@ chain_cf_gen( ConfigArgs *c ) if ( c->op == SLAP_CONFIG_EMIT ) { switch( c->type ) { #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - case PC_CHAINING: { + case CH_CHAINING: { struct berval resolve = BER_BVNULL, continuation = BER_BVNULL; - if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) { + if ( !LDAP_CHAIN_CHAINING( lc ) ) { return 1; } @@ -790,6 +1307,18 @@ chain_cf_gen( ConfigArgs *c ) } #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + case CH_CACHE_URI: + c->value_int = LDAP_CHAIN_CACHE_URI( lc ); + break; + + case CH_MAX_DEPTH: + c->value_int = lc->lc_max_depth; + break; + + case CH_RETURN_ERR: + c->value_int = LDAP_CHAIN_RETURN_ERR( lc ); + break; + default: assert( 0 ); rc = 1; @@ -797,19 +1326,31 @@ chain_cf_gen( ConfigArgs *c ) return rc; } else if ( c->op == LDAP_MOD_DELETE ) { - return 1; /* FIXME */ -#if 0 switch( c->type ) { - case PC_ATTR: - case PC_TEMP: + case CH_CHAINING: + return 1; + + case CH_CACHE_URI: + lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI; + break; + + case CH_MAX_DEPTH: + c->value_int = 0; + break; + + case CH_RETURN_ERR: + lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR; + break; + + default: + return 1; } return rc; -#endif } switch( c->type ) { + case CH_CHAINING: { #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - case PC_CHAINING: { char **argv = c->argv; int argc = c->argc; BerElementBuffer berbuf; @@ -826,20 +1367,20 @@ chain_cf_gen( ConfigArgs *c ) if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) { resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) ); if ( resolve == -1 ) { - fprintf( stderr, "%s line %d: " + Debug( LDAP_DEBUG_ANY, "%s: " "illegal value %s " - "in \"chain-chaining>\"\n", - c->fname, c->lineno, argv[ 0 ] ); + "in \"chain-chaining>\".\n", + c->log, argv[ 0 ], 0 ); return 1; } } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) { continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) ); if ( continuation == -1 ) { - fprintf( stderr, "%s line %d: " + Debug( LDAP_DEBUG_ANY, "%s: " "illegal value %s " - "in \"chain-chaining\"\n", - c->fname, c->lineno, argv[ 0 ] ); + "in \"chain-chaining\".\n", + c->log, argv[ 0 ], 0 ); return 1; } @@ -847,9 +1388,9 @@ chain_cf_gen( ConfigArgs *c ) iscritical = 1; } else { - fprintf( stderr, "%s line %d: " - "unknown option in \"chain-chaining\"\n", - c->fname, c->lineno ); + Debug( LDAP_DEBUG_ANY, "%s: " + "unknown option in \"chain-chaining\".\n", + c->log, 0, 0 ); return 1; } } @@ -867,9 +1408,9 @@ chain_cf_gen( ConfigArgs *c ) err = ber_printf( ber, "{e" /* } */, resolve ); if ( err == -1 ) { ber_free( ber, 1 ); - fprintf( stderr, "%s line %d: " + Debug( LDAP_DEBUG_ANY, "%s: " "chaining behavior control encoding error!\n", - c->fname, c->lineno ); + c->log, 0, 0 ); return 1; } @@ -877,9 +1418,9 @@ chain_cf_gen( ConfigArgs *c ) err = ber_printf( ber, "e", continuation ); if ( err == -1 ) { ber_free( ber, 1 ); - fprintf( stderr, "%s line %d: " + Debug( LDAP_DEBUG_ANY, "%s: " "chaining behavior control encoding error!\n", - c->fname, c->lineno ); + c->log, 0, 0 ); return 1; } } @@ -887,9 +1428,9 @@ chain_cf_gen( ConfigArgs *c ) err = ber_printf( ber, /* { */ "N}" ); if ( err == -1 ) { ber_free( ber, 1 ); - fprintf( stderr, "%s line %d: " + Debug( LDAP_DEBUG_ANY, "%s: " "chaining behavior control encoding error!\n", - c->fname, c->lineno ); + c->log, 0, 0 ); return 1; } @@ -906,10 +1447,9 @@ chain_cf_gen( ConfigArgs *c ) if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS ) { - fprintf( stderr, "%s line %d: " - "unable to parse chaining control%s%s\n", - c->fname, c->lineno, - rs.sr_text ? ": " : "", + Debug( LDAP_DEBUG_ANY, "%s: " + "unable to parse chaining control%s%s.\n", + c->log, rs.sr_text ? ": " : "", rs.sr_text ? rs.sr_text : "" ); return 1; } @@ -919,10 +1459,40 @@ chain_cf_gen( ConfigArgs *c ) lc->lc_flags |= LDAP_CHAIN_F_CHAINING; rc = 0; +#else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + Debug( LDAP_DEBUG_ANY, "%s: " + "\"chaining\" control unsupported (ignored).\n", + c->log, 0, 0 ); +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + } break; + case CH_CACHE_URI: + if ( c->value_int ) { + lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI; + } else { + lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI; + } + break; + + case CH_MAX_DEPTH: + if ( c->value_int < 0 ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "<%s> invalid max referral depth %d", + c->argv[0], c->value_int ); + Debug( LDAP_DEBUG_ANY, "%s: %s.\n", + c->log, c->cr_msg, 0 ); + rc = 1; + break; + } + lc->lc_max_depth = c->value_int; + + case CH_RETURN_ERR: + if ( c->value_int ) { + lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR; + } else { + lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR; + } break; - } -#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ default: assert( 0 ); @@ -931,6 +1501,41 @@ chain_cf_gen( ConfigArgs *c ) return rc; } +static int +ldap_chain_db_init( + BackendDB *be, + ConfigReply *cr ) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + ldap_chain_t *lc = NULL; + + if ( lback == NULL ) { + static BackendInfo lback2; + + lback = backend_info( "ldap" ); + + if ( lback == NULL ) { + return 1; + } + + lback2 = *lback; + lback2.bi_type = ldapchain.on_bi.bi_type; + lback = &lback2; + } + + lc = ch_malloc( sizeof( ldap_chain_t ) ); + if ( lc == NULL ) { + return 1; + } + memset( lc, 0, sizeof( ldap_chain_t ) ); + lc->lc_max_depth = 1; + ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex ); + + on->on_bi.bi_private = (void *)lc; + + return 0; +} + static int ldap_chain_db_config( BackendDB *be, @@ -939,217 +1544,388 @@ ldap_chain_db_config( int argc, char **argv ) { - slap_overinst *on = (slap_overinst *) be->bd_info; + slap_overinst *on = (slap_overinst *)be->bd_info; ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; - char *argv0 = NULL; - int rc; - BackendDB db = *be; + int rc = SLAP_CONF_UNKNOWN; + + if ( lc->lc_common_li == NULL ) { + void *be_private = be->be_private; + ldap_chain_db_init_common( be ); + lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private; + be->be_private = be_private; + } + + /* Something for the chain database? */ if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) { - argv0 = argv[ 0 ]; - argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ]; + char *save_argv0 = argv[ 0 ]; + BackendInfo *bd_info = be->bd_info; + void *be_private = be->be_private; + ConfigOCs *be_cf_ocs = be->be_cf_ocs; + static char *allowed_argv[] = { + /* special: put URI here, so in the meanwhile + * it detects whether a new URI is being provided */ + "uri", + "nretries", + "timeout", + /* flags */ + "tls", + /* FIXME: maybe rebind-as-user should be allowed + * only within known URIs... */ + "rebind-as-user", + "chase-referrals", + "t-f-support", + "proxy-whoami", + NULL + }; + int which_argv = -1; + + argv[ 0 ] += STRLENOF( "chain-" ); + + for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) { + if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) { + break; + } + } -#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR - if ( strcasecmp( argv[ 0 ], "chaining" ) == 0 ) { - char **tmpargv = argv; - BerElementBuffer berbuf; - BerElement *ber = (BerElement *)&berbuf; - int resolve = -1, - continuation = -1, - iscritical = 0; - Operation op = { 0 }; - SlapReply rs = { 0 }; - - lc->lc_chaining_ctrlflag = 0; - - for ( argc--, tmpargv++; argc > 0; argc--, tmpargv++ ) { - if ( strncasecmp( tmpargv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) { - resolve = str2chain( tmpargv[ 0 ] + STRLENOF( "resolve=" ) ); - if ( resolve == -1 ) { - fprintf( stderr, "%s line %d: " - "illegal value %s " - "in \"chain-chaining>\"\n", - fname, lineno, tmpargv[ 0 ] ); - return 1; - } - - } else if ( strncasecmp( tmpargv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) { - continuation = str2chain( tmpargv[ 0 ] + STRLENOF( "continuation=" ) ); - if ( continuation == -1 ) { - fprintf( stderr, "%s line %d: " - "illegal value %s " - "in \"chain-chaining\"\n", - fname, lineno, tmpargv[ 0 ] ); - return 1; - } - - } else if ( strcasecmp( tmpargv[ 0 ], "critical" ) == 0 ) { - iscritical = 1; - - } else { - fprintf( stderr, "%s line %d: " - "unknown option in \"chain-chaining\"\n", - fname, lineno ); - return 1; - } + if ( allowed_argv[ which_argv ] == NULL ) { + which_argv = -1; + + if ( lc->lc_cfg_li == lc->lc_common_li ) { + Debug( LDAP_DEBUG_ANY, "%s: line %d: " + "\"%s\" only allowed within a URI directive.\n.", + fname, lineno, argv[ 0 ] ); + return 1; } + } - if ( resolve != -1 || continuation != -1 ) { - int err; + if ( which_argv == 0 ) { + rc = ldap_chain_db_init_one( be ); + if ( rc != 0 ) { + Debug( LDAP_DEBUG_ANY, "%s: line %d: " + "underlying slapd-ldap initialization failed.\n.", + fname, lineno, 0 ); + return 1; + } + lc->lc_cfg_li = be->be_private; + } - if ( resolve == -1 ) { - /* default */ - resolve = SLAP_CHAINING_DEFAULT; - } + /* TODO: add checks on what other slapd-ldap(5) args + * should be put in the template; this is not quite + * harmful, because attributes that shouldn't don't + * get actually used, but the user should at least + * be warned. + */ - ber_init2( ber, NULL, LBER_USE_DER ); + be->bd_info = lback; + be->be_private = (void *)lc->lc_cfg_li; + be->be_cf_ocs = lback->bi_cf_ocs; - err = ber_printf( ber, "{e" /* } */, resolve ); - if ( err == -1 ) { - ber_free( ber, 1 ); - fprintf( stderr, "%s line %d: " - "chaining behavior control encoding error!\n", - fname, lineno ); - return 1; - } + rc = config_generic_wrapper( be, fname, lineno, argc, argv ); - if ( continuation > -1 ) { - err = ber_printf( ber, "e", continuation ); - if ( err == -1 ) { - ber_free( ber, 1 ); - fprintf( stderr, "%s line %d: " - "chaining behavior control encoding error!\n", - fname, lineno ); - return 1; - } - } + argv[ 0 ] = save_argv0; + be->be_cf_ocs = be_cf_ocs; + be->be_private = be_private; + be->bd_info = bd_info; - err = ber_printf( ber, /* { */ "N}" ); - if ( err == -1 ) { - ber_free( ber, 1 ); - fprintf( stderr, "%s line %d: " - "chaining behavior control encoding error!\n", - fname, lineno ); - return 1; - } + if ( which_argv == 0 ) { +private_destroy:; + if ( rc != 0 ) { + BackendDB db = *be; - if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) { - exit( EXIT_FAILURE ); - } + db.bd_info = lback; + db.be_private = (void *)lc->lc_cfg_li; + ldap_chain_db_destroy_one( &db, NULL ); + lc->lc_cfg_li = NULL; } else { - BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value ); - } - - lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR; - lc->lc_chaining_ctrl.ldctl_iscritical = iscritical; + if ( lc->lc_cfg_li->li_bvuri == NULL + || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] ) + || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) ) + { + Debug( LDAP_DEBUG_ANY, "%s: line %d: " + "no URI list allowed in slapo-chain.\n", + fname, lineno, 0 ); + rc = 1; + goto private_destroy; + } - if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS ) - { - fprintf( stderr, "%s line %d: " - "unable to parse chaining control%s%s\n", - fname, lineno, - rs.sr_text ? ": " : "", - rs.sr_text ? rs.sr_text : "" ); - return 1; + if ( avl_insert( &lc->lc_lai.lai_tree, + (caddr_t)lc->lc_cfg_li, + ldap_chain_uri_cmp, ldap_chain_uri_dup ) ) + { + Debug( LDAP_DEBUG_ANY, "%s: line %d: " + "duplicate URI in slapo-chain.\n", + fname, lineno, 0 ); + rc = 1; + goto private_destroy; + } } + } + } + + return rc; +} - lc->lc_chaining_ctrlflag = op.o_chaining; +enum db_which { + db_open = 0, + db_close, + db_destroy, - lc->lc_flags |= LDAP_CHAIN_F_CHAINING; + db_last +}; - rc = 0; - goto done; - } -#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ - } +typedef struct ldap_chain_db_apply_t { + BackendDB *be; + BI_db_func *func; +} ldap_chain_db_apply_t; - db.be_cf_ocs = lback->bi_cf_ocs; - db.be_private = lc->lc_li; - rc = lback->bi_db_config( &db, fname, lineno, argc, argv ); +static int +ldap_chain_db_apply( void *datum, void *arg ) +{ + ldapinfo_t *li = (ldapinfo_t *)datum; + ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg; -done:; - if ( argv0 ) { - argv[ 0 ] = argv0; - } + lca->be->be_private = (void *)li; - return rc; + return lca->func( lca->be, NULL ); } static int -ldap_chain_db_init( - BackendDB *be ) +ldap_chain_db_func( + BackendDB *be, + enum db_which which +) { slap_overinst *on = (slap_overinst *)be->bd_info; - ldap_chain_t *lc = NULL; - int rc; - BackendDB bd = *be; + ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; - if ( lback == NULL ) { - lback = backend_info( "ldap" ); + int rc = 0; - if ( lback == NULL ) { - return -1; - } - } + if ( lc ) { + BI_db_func *func = (&lback->bi_db_open)[ which ]; - lc = ch_malloc( sizeof( ldap_chain_t ) ); - memset( lc, 0, sizeof( ldap_chain_t ) ); + if ( func != NULL && lc->lc_common_li != NULL ) { + BackendDB db = *be; - bd.be_private = NULL; - rc = lback->bi_db_init( &bd ); - lc->lc_li = (struct ldapinfo *)bd.be_private; - on->on_bi.bi_private = (void *)lc; + db.bd_info = lback; + db.be_private = lc->lc_common_li; + + rc = func( &db, NULL ); + + if ( rc != 0 ) { + return rc; + } + + if ( lc->lc_lai.lai_tree != NULL ) { + ldap_chain_db_apply_t lca; + + lca.be = &db; + lca.func = func; + + rc = avl_apply( lc->lc_lai.lai_tree, + ldap_chain_db_apply, (void *)&lca, + 1, AVL_INORDER ) != AVL_NOMORE; + } + } + } return rc; } static int ldap_chain_db_open( - BackendDB *be -) + BackendDB *be, + ConfigReply *cr ) { - int rc = 0; + slap_overinst *on = (slap_overinst *) be->bd_info; + ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; + slap_mask_t monitoring; + int rc = 0; #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR ); + if ( rc != 0 ) { + return rc; + } #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + if ( lc->lc_common_li == NULL ) { + void *be_private = be->be_private; + ldap_chain_db_init_common( be ); + lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private; + be->be_private = be_private; + } + + /* filter out and restore monitoring */ + monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING ); + SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING; + rc = ldap_chain_db_func( be, db_open ); + SLAP_DBFLAGS( be ) |= monitoring; + return rc; } +static int +ldap_chain_db_close( + BackendDB *be, + ConfigReply *cr ) +{ + return ldap_chain_db_func( be, db_close ); +} + static int ldap_chain_db_destroy( - BackendDB *be -) + BackendDB *be, + ConfigReply *cr ) { slap_overinst *on = (slap_overinst *) be->bd_info; ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; - void *private = be->be_private; + int rc; - be->be_private = (void *)lc->lc_li; - rc = lback->bi_db_destroy( be ); - lc->lc_li = be->be_private; - ch_free( lc ); - on->on_bi.bi_private = NULL; - be->be_private = private; + rc = ldap_chain_db_func( be, db_destroy ); + + if ( lc ) { + avl_free( lc->lc_lai.lai_tree, NULL ); + ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex ); + ch_free( lc ); + } + return rc; } +/* + * inits one instance of the slapd-ldap backend, and stores + * the private info in be_private of the arg + */ +static int +ldap_chain_db_init_common( + BackendDB *be ) +{ + BackendInfo *bi = be->bd_info; + ldapinfo_t *li; + int rc; + + be->bd_info = lback; + be->be_private = NULL; + rc = lback->bi_db_init( be, NULL ); + if ( rc != 0 ) { + return rc; + } + li = (ldapinfo_t *)be->be_private; + li->li_urllist_f = NULL; + li->li_urllist_p = NULL; + + be->bd_info = bi; + + return 0; +} + +/* + * inits one instance of the slapd-ldap backend, stores + * the private info in be_private of the arg and fills + * selected fields with data from the template. + * + * NOTE: add checks about the other fields of the template, + * which are ignored and SHOULD NOT be configured by the user. + */ +static int +ldap_chain_db_init_one( + BackendDB *be ) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; + + BackendInfo *bi = be->bd_info; + ldapinfo_t *li; + + slap_op_t t; + + be->bd_info = lback; + be->be_private = NULL; + t = lback->bi_db_init( be, NULL ); + if ( t != 0 ) { + return t; + } + li = (ldapinfo_t *)be->be_private; + li->li_urllist_f = NULL; + li->li_urllist_p = NULL; + + /* copy common data */ + li->li_nretries = lc->lc_common_li->li_nretries; + li->li_flags = lc->lc_common_li->li_flags; + li->li_version = lc->lc_common_li->li_version; + for ( t = 0; t < SLAP_OP_LAST; t++ ) { + li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ]; + } + be->bd_info = bi; + + return 0; +} + +static int +ldap_chain_db_open_one( + BackendDB *be ) +{ + if ( SLAP_DBMONITORING( be ) ) { + ldapinfo_t *li = (ldapinfo_t *)be->be_private; + + if ( li->li_uri == NULL ) { + ber_str2bv( "cn=Common Connections", 0, 1, + &li->li_monitor_info.lmi_rdn ); + + } else { + char *ptr; + + li->li_monitor_info.lmi_rdn.bv_len + = STRLENOF( "cn=" ) + strlen( li->li_uri ); + ptr = li->li_monitor_info.lmi_rdn.bv_val + = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 ); + ptr = lutil_strcopy( ptr, "cn=" ); + ptr = lutil_strcopy( ptr, li->li_uri ); + ptr[ 0 ] = '\0'; + } + } + + return lback->bi_db_open( be, NULL ); +} + +typedef struct ldap_chain_conn_apply_t { + BackendDB *be; + Connection *conn; +} ldap_chain_conn_apply_t; + +static int +ldap_chain_conn_apply( void *datum, void *arg ) +{ + ldapinfo_t *li = (ldapinfo_t *)datum; + ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg; + + lca->be->be_private = (void *)li; + + return lback->bi_connection_destroy( lca->be, lca->conn ); +} + static int ldap_chain_connection_destroy( BackendDB *be, Connection *conn ) { - slap_overinst *on = (slap_overinst *) be->bd_info; - ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; - void *private = be->be_private; - int rc; - - be->be_private = (void *)lc->lc_li; - rc = lback->bi_connection_destroy( be, conn ); + slap_overinst *on = (slap_overinst *) be->bd_info; + ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; + void *private = be->be_private; + ldap_chain_conn_apply_t lca; + int rc; + + be->be_private = NULL; + lca.be = be; + lca.conn = conn; + ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); + rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply, + (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE; + ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); be->be_private = private; return rc; @@ -1283,40 +2059,33 @@ ldap_chain_parse_ctrl( } #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ -static slap_overinst ldapchain; - int -chain_init( void ) +chain_initialize( void ) { -#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR int rc; + /* Make sure we don't exceed the bits reserved for userland */ + config_check_userland( CH_LAST ); + +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR, /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL, ldap_chain_parse_ctrl, &sc_chainingBehavior ); if ( rc != LDAP_SUCCESS ) { - fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc ); + Debug( LDAP_DEBUG_ANY, "slapd-chain: " + "unable to register chaining behavior control: %d.\n", + rc, 0, 0 ); return rc; } #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ ldapchain.on_bi.bi_type = "chain"; ldapchain.on_bi.bi_db_init = ldap_chain_db_init; - ldapchain.on_bi.bi_db_open = ldap_chain_db_open; ldapchain.on_bi.bi_db_config = ldap_chain_db_config; + ldapchain.on_bi.bi_db_open = ldap_chain_db_open; + ldapchain.on_bi.bi_db_close = ldap_chain_db_close; ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy; - /* ... otherwise the underlying backend's function would be called, - * likely passing an invalid entry; on the contrary, the requested - * operational attributes should have been returned while chasing - * the referrals. This all in all is a bit messy, because part - * of the operational attributes are generated by the backend; - * part by the frontend; back-ldap should receive all the available - * ones from the remote server, but then, on its own, it strips those - * it assumes will be (re)generated by the frontend (e.g. - * subschemaSubentry.) */ - ldapchain.on_bi.bi_operational = ldap_chain_operational; - ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy; ldapchain.on_response = ldap_chain_response;