Back to index

openldap  2.4.31
chain.c
Go to the documentation of this file.
00001 /* chain.c - chain LDAP operations */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2003-2012 The OpenLDAP Foundation.
00006  * Portions Copyright 2003 Howard Chu.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * This work was initially developed by the Howard Chu for inclusion
00019  * in OpenLDAP Software.
00020  * This work was subsequently modified by Pierangelo Masarati.
00021  */
00022 
00023 #include "portable.h"
00024 
00025 #include <stdio.h>
00026 
00027 #include <ac/string.h>
00028 #include <ac/socket.h>
00029 
00030 #include "lutil.h"
00031 #include "slap.h"
00032 #include "back-ldap.h"
00033 #include "config.h"
00034 
00035 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
00036 #define SLAP_CHAINING_DEFAULT                           LDAP_CHAINING_PREFERRED
00037 #define SLAP_CH_RESOLVE_SHIFT                           SLAP_CONTROL_SHIFT
00038 #define SLAP_CH_RESOLVE_MASK                            (0x3 << SLAP_CH_RESOLVE_SHIFT)
00039 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED              (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
00040 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED        (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
00041 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED             (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
00042 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED              (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
00043 #define SLAP_CH_RESOLVE_DEFAULT                         (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
00044 #define       SLAP_CH_CONTINUATION_SHIFT                (SLAP_CH_RESOLVE_SHIFT + 2)
00045 #define SLAP_CH_CONTINUATION_MASK                (0x3 << SLAP_CH_CONTINUATION_SHIFT)
00046 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED         (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
00047 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED          (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
00048 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
00049 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED         (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
00050 #define SLAP_CH_CONTINUATION_DEFAULT                    (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
00051 
00052 #define o_chaining                 o_ctrlflag[sc_chainingBehavior]
00053 #define get_chaining(op)           ((op)->o_chaining & SLAP_CONTROL_MASK)
00054 #define get_chainingBehavior(op)   ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
00055 #define get_resolveBehavior(op)           ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
00056 #define get_continuationBehavior(op)      ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
00057 
00058 static int           sc_chainingBehavior;
00059 #endif /*  LDAP_CONTROL_X_CHAINING_BEHAVIOR */
00060 
00061 typedef enum {
00062        LDAP_CH_NONE = 0,
00063        LDAP_CH_RES,
00064        LDAP_CH_ERR
00065 } ldap_chain_status_t;
00066 
00067 static BackendInfo   *lback;
00068 
00069 typedef struct ldap_chain_t {
00070        /*
00071         * A "template" ldapinfo_t gets all common configuration items;
00072         * then, for each configured URI, an entry is created in the tree;
00073         * all the specific configuration items get in the current URI 
00074         * structure.
00075         *
00076         * Then, for each referral, extract the URI and lookup the
00077         * related structure.  If configured to do so, allow URIs
00078         * not found in the structure to create a temporary one
00079         * that chains anonymously; maybe it can also be added to 
00080         * the tree?  Should be all configurable.
00081         */
00082 
00083        /* "common" configuration info (anything occurring before an "uri") */
00084        ldapinfo_t           *lc_common_li;
00085 
00086        /* current configuration info */
00087        ldapinfo_t           *lc_cfg_li;
00088 
00089        /* tree of configured[/generated?] "uri" info */
00090        ldap_avl_info_t             lc_lai;
00091 
00092        /* max depth in nested referrals chaining */
00093        int                  lc_max_depth;
00094 
00095        unsigned             lc_flags;
00096 #define LDAP_CHAIN_F_NONE          (0x00U)
00097 #define       LDAP_CHAIN_F_CHAINING              (0x01U)
00098 #define       LDAP_CHAIN_F_CACHE_URI             (0x02U)
00099 #define       LDAP_CHAIN_F_RETURN_ERR            (0x04U)
00100 
00101 #define LDAP_CHAIN_ISSET(lc, f)           ( ( (lc)->lc_flags & (f) ) == (f) )
00102 #define       LDAP_CHAIN_CHAINING( lc )   LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
00103 #define       LDAP_CHAIN_CACHE_URI( lc )  LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
00104 #define       LDAP_CHAIN_RETURN_ERR( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
00105 
00106 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
00107        LDAPControl          lc_chaining_ctrl;
00108        char                 lc_chaining_ctrlflag;
00109 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
00110 } ldap_chain_t;
00111 
00112 static int ldap_chain_db_init_common( BackendDB  *be );
00113 static int ldap_chain_db_init_one( BackendDB *be );
00114 static int ldap_chain_db_open_one( BackendDB *be );
00115 #define       ldap_chain_db_close_one(be) (0)
00116 #define       ldap_chain_db_destroy_one(be, rs)  (lback)->bi_db_destroy( (be), (rs) )
00117 
00118 typedef struct ldap_chain_cb_t {
00119        ldap_chain_status_t  lb_status;
00120        ldap_chain_t         *lb_lc;
00121        BI_op_func           *lb_op_f;
00122        int                  lb_depth;
00123 } ldap_chain_cb_t;
00124 
00125 static int
00126 ldap_chain_op(
00127        Operation     *op,
00128        SlapReply     *rs,
00129        BI_op_func    *op_f,
00130        BerVarray     ref,
00131        int           depth );
00132 
00133 static int
00134 ldap_chain_search(
00135        Operation     *op,
00136        SlapReply     *rs,
00137        BerVarray     ref,
00138        int           depth );
00139 
00140 static slap_overinst ldapchain;
00141 
00142 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
00143 static int
00144 chaining_control_add(
00145               ldap_chain_t  *lc,
00146               Operation     *op, 
00147               LDAPControl   ***oldctrlsp )
00148 {
00149        LDAPControl   **ctrls = NULL;
00150        int           c = 0;
00151 
00152        *oldctrlsp = op->o_ctrls;
00153 
00154        /* default chaining control not defined */
00155        if ( !LDAP_CHAIN_CHAINING( lc ) ) {
00156               return 0;
00157        }
00158 
00159        /* already present */
00160        if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
00161               return 0;
00162        }
00163 
00164        /* FIXME: check other incompatibilities */
00165 
00166        /* add to other controls */
00167        if ( op->o_ctrls ) {
00168               for ( c = 0; op->o_ctrls[ c ]; c++ )
00169                      /* count them */ ;
00170        }
00171 
00172        ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
00173        ctrls[ 0 ] = &lc->lc_chaining_ctrl;
00174        if ( op->o_ctrls ) {
00175               for ( c = 0; op->o_ctrls[ c ]; c++ ) {
00176                      ctrls[ c + 1 ] = op->o_ctrls[ c ];
00177               }
00178        }
00179        ctrls[ c + 1 ] = NULL;
00180 
00181        op->o_ctrls = ctrls;
00182 
00183        op->o_chaining = lc->lc_chaining_ctrlflag;
00184 
00185        return 0;
00186 }
00187 
00188 static int
00189 chaining_control_remove(
00190               Operation     *op, 
00191               LDAPControl   ***oldctrlsp )
00192 {
00193        LDAPControl   **oldctrls = *oldctrlsp;
00194 
00195        /* we assume that the first control is the chaining control
00196         * added by the chain overlay, so it's the only one we explicitly 
00197         * free */
00198        if ( op->o_ctrls != oldctrls ) {
00199               assert( op->o_ctrls != NULL );
00200               assert( op->o_ctrls[ 0 ] != NULL );
00201 
00202               free( op->o_ctrls );
00203 
00204               op->o_chaining = 0;
00205               op->o_ctrls = oldctrls;
00206        } 
00207 
00208        *oldctrlsp = NULL;
00209 
00210        return 0;
00211 }
00212 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
00213 
00214 static int
00215 ldap_chain_uri_cmp( const void *c1, const void *c2 )
00216 {
00217        const ldapinfo_t     *li1 = (const ldapinfo_t *)c1;
00218        const ldapinfo_t     *li2 = (const ldapinfo_t *)c2;
00219 
00220        assert( li1->li_bvuri != NULL );
00221        assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
00222        assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
00223 
00224        assert( li2->li_bvuri != NULL );
00225        assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
00226        assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
00227 
00228        return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
00229 }
00230 
00231 static int
00232 ldap_chain_uri_dup( void *c1, void *c2 )
00233 {
00234        ldapinfo_t    *li1 = (ldapinfo_t *)c1;
00235        ldapinfo_t    *li2 = (ldapinfo_t *)c2;
00236 
00237        assert( li1->li_bvuri != NULL );
00238        assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
00239        assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
00240 
00241        assert( li2->li_bvuri != NULL );
00242        assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
00243        assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
00244 
00245        if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
00246               return -1;
00247        }
00248 
00249        return 0;
00250 }
00251 
00252 /*
00253  * Search specific response that strips entryDN from entries
00254  */
00255 static int
00256 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
00257 {
00258        ldap_chain_cb_t      *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
00259 
00260        assert( op->o_tag == LDAP_REQ_SEARCH );
00261 
00262        /* if in error, don't proceed any further */
00263        if ( lb->lb_status == LDAP_CH_ERR ) {
00264               return 0;
00265        }
00266 
00267        if ( rs->sr_type == REP_SEARCH ) {
00268               Attribute     **ap = &rs->sr_entry->e_attrs;
00269 
00270               for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
00271                      /* will be generated later by frontend
00272                       * (a cleaner solution would be that
00273                       * the frontend checks if it already exists */
00274                      if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
00275                      {
00276                             Attribute *a = *ap;
00277 
00278                             *ap = (*ap)->a_next;
00279                             attr_free( a );
00280 
00281                             /* there SHOULD be one only! */
00282                             break;
00283                      }
00284               }
00285 
00286               /* tell the frontend not to add generated
00287                * operational attributes */
00288               rs->sr_flags |= REP_NO_OPERATIONALS;
00289               
00290               return SLAP_CB_CONTINUE;
00291 
00292        } else if ( rs->sr_type == REP_SEARCHREF ) {
00293               /* if we get it here, it means the library was unable
00294                * to chase the referral... */
00295               if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
00296                      rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
00297               }
00298 
00299 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
00300               if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
00301                      switch ( get_continuationBehavior( op ) ) {
00302                      case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
00303                             lb->lb_status = LDAP_CH_ERR;
00304                             return rs->sr_err = LDAP_X_CANNOT_CHAIN;
00305 
00306                      default:
00307                             break;
00308                      }
00309               }
00310 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
00311               return SLAP_CB_CONTINUE;
00312 
00313        } else if ( rs->sr_type == REP_RESULT ) {
00314               if ( rs->sr_err == LDAP_REFERRAL
00315                      && lb->lb_depth < lb->lb_lc->lc_max_depth
00316                      && rs->sr_ref != NULL )
00317               {
00318                      rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
00319               }
00320 
00321               /* back-ldap tried to send result */
00322               lb->lb_status = LDAP_CH_RES;
00323        }
00324 
00325        return 0;
00326 }
00327 
00328 /*
00329  * Dummy response that simply traces if back-ldap tried to send 
00330  * anything to the client
00331  */
00332 static int
00333 ldap_chain_cb_response( Operation *op, SlapReply *rs )
00334 {
00335        ldap_chain_cb_t      *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
00336 
00337        /* if in error, don't proceed any further */
00338        if ( lb->lb_status == LDAP_CH_ERR ) {
00339               return 0;
00340        }
00341 
00342        if ( rs->sr_type == REP_RESULT ) {
00343 retry:;
00344               switch ( rs->sr_err ) {
00345               case LDAP_COMPARE_TRUE:
00346               case LDAP_COMPARE_FALSE:
00347                      if ( op->o_tag != LDAP_REQ_COMPARE ) {
00348                             return rs->sr_err;
00349                      }
00350                      /* fallthru */
00351 
00352               case LDAP_SUCCESS:
00353                      lb->lb_status = LDAP_CH_RES;
00354                      break;
00355 
00356               case LDAP_REFERRAL:
00357                      if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
00358                             rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
00359                             goto retry;
00360                      }
00361 
00362 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
00363                      if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
00364                             switch ( get_continuationBehavior( op ) ) {
00365                             case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
00366                                    lb->lb_status = LDAP_CH_ERR;
00367                                    return rs->sr_err = LDAP_X_CANNOT_CHAIN;
00368 
00369                             default:
00370                                    break;
00371                             }
00372                      }
00373 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
00374                      break;
00375 
00376               default:
00377                      return rs->sr_err;
00378               }
00379 
00380        } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
00381        {
00382               /* strip the entryDN attribute, but keep returning results */
00383               (void)ldap_chain_cb_search_response( op, rs );
00384        }
00385 
00386        return SLAP_CB_CONTINUE;
00387 }
00388 
00389 static int
00390 ldap_chain_op(
00391        Operation     *op,
00392        SlapReply     *rs,
00393        BI_op_func    *op_f,
00394        BerVarray     ref,
00395        int           depth )
00396 {
00397        slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
00398        ldap_chain_cb_t      *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
00399        ldap_chain_t  *lc = (ldap_chain_t *)on->on_bi.bi_private;
00400        struct berval odn = op->o_req_dn,
00401                      ondn = op->o_req_ndn;
00402        ldapinfo_t    li = { 0 }, *lip = NULL;
00403        struct berval bvuri[ 2 ] = { { 0 } };
00404 
00405        /* NOTE: returned if ref is empty... */
00406        int           rc = LDAP_OTHER,
00407                      first_rc;
00408 
00409 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
00410        LDAPControl   **ctrls = NULL;
00411        
00412        (void)chaining_control_add( lc, op, &ctrls );
00413 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
00414 
00415        li.li_bvuri = bvuri;
00416        first_rc = -1;
00417        for ( ; !BER_BVISNULL( ref ); ref++ ) {
00418               SlapReply     rs2 = { 0 };
00419               LDAPURLDesc   *srv = NULL;
00420               req_search_s  save_oq_search = op->oq_search,
00421                             tmp_oq_search = { 0 };
00422               struct berval dn = BER_BVNULL,
00423                             pdn = odn,
00424                             ndn = ondn;
00425               char          *filter = NULL;
00426               int           temporary = 0;
00427               int           free_dn = 0;
00428                      
00429               /* We're setting the URI of the first referral;
00430                * what if there are more?
00431 
00432 Document: RFC 4511
00433 
00434 4.1.10. Referral 
00435    ...
00436    If the client wishes to progress the operation, it MUST follow the 
00437    referral by contacting one of the supported services. If multiple 
00438    URIs are present, the client assumes that any supported URI may be 
00439    used to progress the operation. 
00440 
00441                * so we actually need to follow exactly one,
00442                * and we can assume any is fine.
00443                */
00444        
00445               /* parse reference and use 
00446                * proto://[host][:port]/ only */
00447               rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
00448               if ( rc != LDAP_URL_SUCCESS ) {
00449                      Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
00450                             op->o_log_prefix, ref->bv_val, 0 );
00451 
00452                      /* try next */
00453                      rc = LDAP_OTHER;
00454                      continue;
00455               }
00456 
00457               if ( op->o_tag == LDAP_REQ_SEARCH ) {
00458                      if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
00459                             /* RFC 4511: if scope is present, use it */
00460                             tmp_oq_search.rs_scope = srv->lud_scope;
00461 
00462                      } else {
00463                             /* RFC 4511: if scope is absent, use original */
00464                             tmp_oq_search.rs_scope = op->ors_scope;
00465                      }
00466               }
00467 
00468               rc = LDAP_SUCCESS;
00469               srv->lud_scope = LDAP_SCOPE_DEFAULT;
00470               dn.bv_val = srv->lud_dn;
00471               filter = srv->lud_filter;
00472 
00473               /* normalize DN */
00474               if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
00475                      if ( srv->lud_dn == NULL ) {
00476                             srv->lud_dn = "";
00477                      }
00478 
00479               } else {
00480                      ber_str2bv( srv->lud_dn, 0, 0, &dn );
00481                      rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
00482                      if ( rc == LDAP_SUCCESS ) {
00483                             /* remove DN essentially because later on 
00484                              * ldap_initialize() will parse the URL 
00485                              * as a comma-separated URL list */
00486                             srv->lud_dn = "";
00487                             free_dn = 1;
00488                      }
00489               }
00490 
00491               /* prepare filter */
00492               if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
00493                      /* filter */
00494                      if ( srv->lud_filter != NULL
00495                             && srv->lud_filter[0] != '\0'
00496                             && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
00497                      {
00498                             /* RFC 4511: if filter is present, use it;
00499                              * otherwise, use original */
00500                             tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
00501                             if ( tmp_oq_search.rs_filter != NULL ) {
00502                                    filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
00503 
00504                             } else {
00505                                    Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
00506                                           op->o_log_prefix, ref->bv_val, srv->lud_filter );
00507                                    rc = LDAP_OTHER;
00508                             }
00509                      }
00510               }
00511               srv->lud_filter = NULL;
00512 
00513               if ( rc == LDAP_SUCCESS ) {
00514                      li.li_uri = ldap_url_desc2str( srv );
00515               }
00516 
00517               srv->lud_dn = dn.bv_val;
00518               srv->lud_filter = filter;
00519               ldap_free_urldesc( srv );
00520 
00521               if ( rc != LDAP_SUCCESS ) {
00522                      /* try next */
00523                      rc = LDAP_OTHER;
00524                      continue;
00525               }
00526 
00527               if ( li.li_uri == NULL ) {
00528                      Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
00529                             op->o_log_prefix, ref->bv_val, 0 );
00530 
00531                      /* try next */
00532                      rc = LDAP_OTHER;
00533                      goto further_cleanup;
00534               }
00535 
00536               Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
00537                      op->o_log_prefix, ref->bv_val, li.li_uri );
00538 
00539               op->o_req_dn = pdn;
00540               op->o_req_ndn = ndn;
00541 
00542               if ( op->o_tag == LDAP_REQ_SEARCH ) {
00543                      op->ors_scope = tmp_oq_search.rs_scope;
00544                      if ( tmp_oq_search.rs_filter != NULL ) {
00545                             op->ors_filter = tmp_oq_search.rs_filter;
00546                             op->ors_filterstr = tmp_oq_search.rs_filterstr;
00547                      }
00548               }
00549 
00550               ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
00551 
00552               /* Searches for a ldapinfo in the avl tree */
00553               ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
00554               lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 
00555                      (caddr_t)&li, ldap_chain_uri_cmp );
00556               ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
00557 
00558               if ( lip != NULL ) {
00559                      op->o_bd->be_private = (void *)lip;
00560 
00561                      Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
00562                             op->o_log_prefix, ref->bv_val, li.li_uri );
00563 
00564               } else {
00565                      rc = ldap_chain_db_init_one( op->o_bd );
00566                      if ( rc != 0 ) {
00567                             Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
00568                                    op->o_log_prefix, ref->bv_val, li.li_uri );
00569                             goto cleanup;
00570                      }
00571                      lip = (ldapinfo_t *)op->o_bd->be_private;
00572                      lip->li_uri = li.li_uri;
00573                      lip->li_bvuri = bvuri;
00574                      rc = ldap_chain_db_open_one( op->o_bd );
00575                      if ( rc != 0 ) {
00576                             Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
00577                                    op->o_log_prefix, ref->bv_val, li.li_uri );
00578                             lip->li_uri = NULL;
00579                             lip->li_bvuri = NULL;
00580                             (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
00581                             goto cleanup;
00582                      }
00583 
00584                      if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
00585                             ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
00586                             if ( avl_insert( &lc->lc_lai.lai_tree,
00587                                    (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
00588                             {
00589                                    /* someone just inserted another;
00590                                     * don't bother, use this and then
00591                                     * just free it */
00592                                    temporary = 1;
00593                             }
00594                             ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
00595 
00596                      } else {
00597                             temporary = 1;
00598                      }
00599 
00600                      Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
00601                             op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
00602               }
00603 
00604               lb->lb_op_f = op_f;
00605               lb->lb_depth = depth + 1;
00606 
00607               rc = op_f( op, &rs2 );
00608 
00609               /* note the first error */
00610               if ( first_rc == -1 ) {
00611                      first_rc = rc;
00612               }
00613 
00614 cleanup:;
00615               ldap_memfree( li.li_uri );
00616               li.li_uri = NULL;
00617 
00618               if ( temporary ) {
00619                      lip->li_uri = NULL;
00620                      lip->li_bvuri = NULL;
00621                      (void)ldap_chain_db_close_one( op->o_bd );
00622                      (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
00623               }
00624 
00625 further_cleanup:;
00626               if ( free_dn ) {
00627                      op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
00628                      op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
00629               }
00630        
00631               if ( op->o_tag == LDAP_REQ_SEARCH ) {     
00632                      if ( tmp_oq_search.rs_filter != NULL ) {
00633                             filter_free_x( op, tmp_oq_search.rs_filter, 1 );
00634                      }
00635 
00636                      if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
00637                             slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
00638                      }
00639 
00640                      op->oq_search = save_oq_search;
00641               }
00642 
00643               if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
00644                      *rs = rs2;
00645                      break;
00646               }
00647 
00648               rc = rs2.sr_err;
00649        }
00650 
00651        op->o_req_dn = odn;
00652        op->o_req_ndn = ondn;
00653 
00654 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
00655        (void)chaining_control_remove( op, &ctrls );
00656 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
00657 
00658        if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
00659               rc = first_rc;
00660        }
00661 
00662        return rc;
00663 }
00664 
00665 static int
00666 ldap_chain_search(
00667        Operation     *op,
00668        SlapReply     *rs,
00669        BerVarray     ref,
00670        int           depth )
00671 
00672 {
00673        slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
00674        ldap_chain_cb_t      *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
00675        ldap_chain_t  *lc = (ldap_chain_t *)on->on_bi.bi_private;
00676        ldapinfo_t    li = { 0 }, *lip = NULL;
00677        struct berval bvuri[ 2 ] = { { 0 } };
00678 
00679        struct berval odn = op->o_req_dn,
00680                      ondn = op->o_req_ndn;
00681        Entry         *save_entry = rs->sr_entry;
00682        slap_mask_t   save_flags = rs->sr_flags;
00683 
00684        int           rc = LDAP_OTHER,
00685                      first_rc = -1;
00686 
00687 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
00688        LDAPControl   **ctrls = NULL;
00689        
00690        (void)chaining_control_add( lc, op, &ctrls );
00691 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
00692 
00693        assert( rs->sr_type == REP_SEARCHREF );
00694 
00695        rs->sr_type = REP_SEARCH;
00696 
00697        /* if we parse the URI then by no means 
00698         * we can cache stuff or reuse connections, 
00699         * because in back-ldap there's no caching
00700         * based on the URI value, which is supposed
00701         * to be set once for all (correct?) */
00702        li.li_bvuri = bvuri;
00703        for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
00704               SlapReply     rs2 = { REP_RESULT };
00705               LDAPURLDesc   *srv;
00706               req_search_s  save_oq_search = op->oq_search,
00707                             tmp_oq_search = { 0 };
00708               struct berval dn,
00709                             pdn = op->o_req_dn,
00710                             ndn = op->o_req_ndn;
00711               char          *filter = NULL;
00712               int           temporary = 0;
00713               int           free_dn = 0;
00714 
00715               /* parse reference and use
00716                * proto://[host][:port]/ only */
00717               rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
00718               if ( rc != LDAP_URL_SUCCESS ) {
00719                      Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
00720                             op->o_log_prefix, ref->bv_val, 0 );
00721 
00722                      /* try next */
00723                      rs->sr_err = LDAP_OTHER;
00724                      continue;
00725               }
00726 
00727               if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
00728                      /* RFC 4511: if scope is present, use it */
00729                      tmp_oq_search.rs_scope = srv->lud_scope;
00730 
00731               } else {
00732                      /* RFC 4511: if scope is absent, use original */
00733                      /* Section 4.5.3: if scope is onelevel, use base */
00734                      if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
00735                             tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
00736                      else
00737                             tmp_oq_search.rs_scope = op->ors_scope;
00738               }
00739 
00740               rc = LDAP_SUCCESS;
00741               srv->lud_scope = LDAP_SCOPE_DEFAULT;
00742               dn.bv_val = srv->lud_dn;
00743               filter = srv->lud_filter;
00744 
00745               /* normalize DN */
00746               if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
00747                      if ( srv->lud_dn == NULL ) {
00748                             srv->lud_dn = "";
00749                      }
00750 
00751                      if ( save_entry != NULL ) {
00752                             /* use the "right" DN, if available */
00753                             pdn = save_entry->e_name;
00754                             ndn = save_entry->e_nname;
00755                      } /* else leave the original req DN in place, if any RFC 4511 */
00756                      
00757               } else {
00758                      /* RFC 4511: if DN is present, use it */
00759                      ber_str2bv( srv->lud_dn, 0, 0, &dn );
00760                      rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
00761                      if ( rc == LDAP_SUCCESS ) {
00762                             /* remove DN essentially because later on 
00763                              * ldap_initialize() will parse the URL 
00764                              * as a comma-separated URL list */
00765                             srv->lud_dn = "";
00766                             free_dn = 1;
00767                      }
00768               }
00769 
00770               /* prepare filter */
00771               if ( rc == LDAP_SUCCESS ) {
00772                      /* filter */
00773                      if ( srv->lud_filter != NULL
00774                             && srv->lud_filter[0] != '\0'
00775                             && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
00776                      {
00777                             /* RFC 4511: if filter is present, use it;
00778                              * otherwise, use original */
00779                             tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
00780                             if ( tmp_oq_search.rs_filter != NULL ) {
00781                                    filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
00782 
00783                             } else {
00784                                    Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
00785                                           op->o_log_prefix, ref->bv_val, srv->lud_filter );
00786                                    rc = LDAP_OTHER;
00787                             }
00788                      }
00789               }
00790               srv->lud_filter = NULL;
00791 
00792               if ( rc == LDAP_SUCCESS ) {
00793                      li.li_uri = ldap_url_desc2str( srv );
00794               }
00795 
00796               srv->lud_dn = dn.bv_val;
00797               srv->lud_filter = filter;
00798               ldap_free_urldesc( srv );
00799 
00800               if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
00801                      Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
00802                             op->o_log_prefix, ref->bv_val, 0 );
00803 
00804                      /* try next */
00805                      rc = LDAP_OTHER;
00806                      goto further_cleanup;
00807               }
00808 
00809               Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
00810                      op->o_log_prefix, ref->bv_val, li.li_uri );
00811 
00812               op->o_req_dn = pdn;
00813               op->o_req_ndn = ndn;
00814               op->ors_scope = tmp_oq_search.rs_scope;
00815               if ( tmp_oq_search.rs_filter != NULL ) {
00816                      op->ors_filter = tmp_oq_search.rs_filter;
00817                      op->ors_filterstr = tmp_oq_search.rs_filterstr;
00818               }
00819 
00820               ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
00821 
00822               /* Searches for a ldapinfo in the avl tree */
00823               ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
00824               lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 
00825                      (caddr_t)&li, ldap_chain_uri_cmp );
00826               ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
00827 
00828               if ( lip != NULL ) {
00829                      op->o_bd->be_private = (void *)lip;
00830 
00831                      Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
00832                             op->o_log_prefix, ref->bv_val, li.li_uri );
00833 
00834               } else {
00835                      /* if none is found, create a temporary... */
00836                      rc = ldap_chain_db_init_one( op->o_bd );
00837                      if ( rc != 0 ) {
00838                             Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
00839                                    op->o_log_prefix, ref->bv_val, li.li_uri );
00840                             goto cleanup;
00841                      }
00842                      lip = (ldapinfo_t *)op->o_bd->be_private;
00843                      lip->li_uri = li.li_uri;
00844                      lip->li_bvuri = bvuri;
00845                      rc = ldap_chain_db_open_one( op->o_bd );
00846                      if ( rc != 0 ) {
00847                             Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
00848                                    op->o_log_prefix, ref->bv_val, li.li_uri );
00849                             lip->li_uri = NULL;
00850                             lip->li_bvuri = NULL;
00851                             (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
00852                             goto cleanup;
00853                      }
00854 
00855                      if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
00856                             ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
00857                             if ( avl_insert( &lc->lc_lai.lai_tree,
00858                                    (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
00859                             {
00860                                    /* someone just inserted another;
00861                                     * don't bother, use this and then
00862                                     * just free it */
00863                                    temporary = 1;
00864                             }
00865                             ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
00866 
00867                      } else {
00868                             temporary = 1;
00869                      }
00870 
00871                      Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
00872                             op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
00873               }
00874 
00875               lb->lb_op_f = lback->bi_op_search;
00876               lb->lb_depth = depth + 1;
00877 
00878               /* FIXME: should we also copy filter and scope?
00879                * according to RFC3296, no */
00880               rc = lback->bi_op_search( op, &rs2 );
00881               if ( first_rc == -1 ) {
00882                      first_rc = rc;
00883               }
00884 
00885 cleanup:;
00886               ldap_memfree( li.li_uri );
00887               li.li_uri = NULL;
00888 
00889               if ( temporary ) {
00890                      lip->li_uri = NULL;
00891                      lip->li_bvuri = NULL;
00892                      (void)ldap_chain_db_close_one( op->o_bd );
00893                      (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
00894               }
00895               
00896 further_cleanup:;
00897               if ( free_dn ) {
00898                      op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
00899                      op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
00900               }
00901 
00902               op->o_req_dn = odn;
00903               op->o_req_ndn = ondn;
00904 
00905               if ( tmp_oq_search.rs_filter != NULL ) {
00906                      filter_free_x( op, tmp_oq_search.rs_filter, 1 );
00907               }
00908 
00909               if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
00910                      slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
00911               }
00912 
00913               op->oq_search = save_oq_search;
00914               
00915               if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
00916                      *rs = rs2;
00917                      break;
00918               }
00919 
00920               rc = rs2.sr_err;
00921        }
00922 
00923 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
00924        (void)chaining_control_remove( op, &ctrls );
00925 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
00926 
00927        op->o_req_dn = odn;
00928        op->o_req_ndn = ondn;
00929        rs->sr_type = REP_SEARCHREF;
00930        rs->sr_entry = save_entry;
00931        rs->sr_flags = save_flags;
00932 
00933        if ( rc != LDAP_SUCCESS ) {
00934               /* couldn't chase any of the referrals */
00935               if ( first_rc != -1 ) {
00936                      rc = first_rc;
00937 
00938               } else {
00939                      rc = SLAP_CB_CONTINUE;
00940               }
00941        }
00942 
00943        return rc;
00944 }
00945 
00946 static int
00947 ldap_chain_response( Operation *op, SlapReply *rs )
00948 {
00949        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00950        ldap_chain_t  *lc = (ldap_chain_t *)on->on_bi.bi_private;
00951        BackendDB     db, *bd = op->o_bd;
00952        ldap_chain_cb_t      lb = { 0 };
00953        slap_callback *sc = op->o_callback,
00954                      sc2 = { 0 };
00955        int           rc = 0;
00956        const char    *text = NULL;
00957        const char    *matched;
00958        BerVarray     ref;
00959        struct berval ndn = op->o_ndn;
00960 
00961        int           sr_err = rs->sr_err;
00962        slap_reply_t  sr_type = rs->sr_type;
00963 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
00964        slap_mask_t   chain_mask = 0;
00965        ber_len_t     chain_shift = 0;
00966 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
00967 
00968        if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
00969               return SLAP_CB_CONTINUE;
00970        }
00971 
00972 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
00973        if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
00974               switch ( get_resolveBehavior( op ) ) {
00975               case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
00976               case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
00977                      return SLAP_CB_CONTINUE;
00978 
00979               default:
00980                      chain_mask = SLAP_CH_RESOLVE_MASK;
00981                      chain_shift = SLAP_CH_RESOLVE_SHIFT;
00982                      break;
00983               }
00984 
00985        } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
00986               switch ( get_continuationBehavior( op ) ) {
00987               case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
00988               case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
00989                      return SLAP_CB_CONTINUE;
00990 
00991               default:
00992                      chain_mask = SLAP_CH_CONTINUATION_MASK;
00993                      chain_shift = SLAP_CH_CONTINUATION_SHIFT;
00994                      break;
00995               }
00996        }
00997 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
00998 
00999        /*
01000         * TODO: add checks on who/when chain operations; e.g.:
01001         *   a) what identities are authorized
01002         *   b) what request DN (e.g. only chain requests rooted at <DN>)
01003         *   c) what referral URIs
01004         *   d) what protocol scheme (e.g. only ldaps://)
01005         *   e) what ssf
01006         */
01007 
01008        db = *op->o_bd;
01009        SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
01010        op->o_bd = &db;
01011 
01012        text = rs->sr_text;
01013        rs->sr_text = NULL;
01014        matched = rs->sr_matched;
01015        rs->sr_matched = NULL;
01016        ref = rs->sr_ref;
01017        rs->sr_ref = NULL;
01018 
01019        /* we need this to know if back-ldap returned any result */
01020        lb.lb_lc = lc;
01021        sc2.sc_next = sc->sc_next;
01022        sc2.sc_private = &lb;
01023        sc2.sc_response = ldap_chain_cb_response;
01024        op->o_callback = &sc2;
01025 
01026        /* Chaining can be performed by a privileged user on behalf
01027         * of normal users, using the ProxyAuthz control, by exploiting
01028         * the identity assertion feature of back-ldap; see idassert-*
01029         * directives in slapd-ldap(5).
01030         *
01031         * FIXME: the idassert-authcDN is one, will it be fine regardless
01032         * of the URI we obtain from the referral?
01033         */
01034 
01035        switch ( op->o_tag ) {
01036        case LDAP_REQ_BIND: {
01037               struct berval rndn = op->o_req_ndn;
01038               Connection    *conn = op->o_conn;
01039 
01040               /* FIXME: can we really get a referral for binds? */
01041               op->o_req_ndn = slap_empty_bv;
01042               op->o_conn = NULL;
01043               rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
01044               op->o_req_ndn = rndn;
01045               op->o_conn = conn;
01046               }
01047               break;
01048 
01049        case LDAP_REQ_ADD:
01050               rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
01051               break;
01052 
01053        case LDAP_REQ_DELETE:
01054               rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
01055               break;
01056 
01057        case LDAP_REQ_MODRDN:
01058               rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
01059               break;
01060 
01061        case LDAP_REQ_MODIFY:
01062               rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
01063               break;
01064 
01065        case LDAP_REQ_COMPARE:
01066               rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
01067               if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
01068                      rc = LDAP_SUCCESS;
01069               }
01070               break;
01071 
01072        case LDAP_REQ_SEARCH:
01073               if ( rs->sr_type == REP_SEARCHREF ) {
01074                      sc2.sc_response = ldap_chain_cb_search_response;
01075                      rc = ldap_chain_search( op, rs, ref, 0 );
01076                      
01077               } else {
01078                      /* we might get here before any database actually 
01079                       * performed a search; in those cases, we need
01080                       * to check limits, to make sure safe defaults
01081                       * are in place */
01082                      if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
01083                             rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
01084 
01085                      } else {
01086                             rc = SLAP_CB_CONTINUE;
01087                      }
01088               }
01089               break;
01090 
01091        case LDAP_REQ_EXTENDED:
01092               rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
01093               /* FIXME: ldap_back_extended() by design 
01094                * doesn't send result; frontend is expected
01095                * to send it... */
01096               /* FIXME: what about chaining? */
01097               if ( rc != SLAPD_ABANDON ) {
01098                      rs->sr_err = rc;
01099                      send_ldap_extended( op, rs );
01100                      rc = LDAP_SUCCESS;
01101               }
01102               lb.lb_status = LDAP_CH_RES;
01103               break;
01104 
01105        default:
01106               rc = SLAP_CB_CONTINUE;
01107               break;
01108        }
01109 
01110        switch ( rc ) {
01111        case SLAPD_ABANDON:
01112               goto dont_chain;
01113 
01114        case LDAP_SUCCESS:
01115        case LDAP_REFERRAL:
01116               sr_err = rs->sr_err;
01117               /* slapd-ldap sent response */
01118               if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
01119                      /* FIXME: should we send response? */
01120                      Debug( LDAP_DEBUG_ANY,
01121                             "%s: ldap_chain_response: "
01122                             "overlay should have sent result.\n",
01123                             op->o_log_prefix, 0, 0 );
01124               }
01125               break;
01126 
01127        default:
01128 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
01129               if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
01130                      goto cannot_chain;
01131               }
01132 
01133               switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
01134               case LDAP_CHAINING_REQUIRED:
01135 cannot_chain:;
01136                      op->o_callback = NULL;
01137                      send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
01138                             "operation cannot be completed without chaining" );
01139                      goto dont_chain;
01140 
01141               default:
01142 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
01143                      if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
01144                             sr_err = rs->sr_err = rc;
01145                             rs->sr_type = sr_type;
01146 
01147                      } else {
01148                             rc = SLAP_CB_CONTINUE;
01149                             rs->sr_err = sr_err;
01150                             rs->sr_type = sr_type;
01151                             rs->sr_text = text;
01152                             rs->sr_matched = matched;
01153                             rs->sr_ref = ref;
01154                      }
01155 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
01156                      break;
01157               }
01158 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
01159        }
01160 
01161        if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
01162               /* give the remaining callbacks a chance */
01163               op->o_callback = sc->sc_next;
01164               rc = rs->sr_err = slap_map_api2result( rs );
01165               send_ldap_result( op, rs );
01166        }
01167 
01168 dont_chain:;
01169        rs->sr_err = sr_err;
01170        rs->sr_type = sr_type;
01171        rs->sr_text = text;
01172        rs->sr_matched = matched;
01173        rs->sr_ref = ref;
01174        op->o_bd = bd;
01175        op->o_callback = sc;
01176        op->o_ndn = ndn;
01177 
01178        return rc;
01179 }
01180 
01181 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
01182 static int
01183 ldap_chain_parse_ctrl(
01184        Operation     *op,
01185        SlapReply     *rs,
01186        LDAPControl   *ctrl );
01187 
01188 static int
01189 str2chain( const char *s )
01190 {
01191        if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
01192               return LDAP_CHAINING_PREFERRED;
01193               
01194        } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
01195               return LDAP_CHAINING_REQUIRED;
01196 
01197        } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
01198               return LDAP_REFERRALS_PREFERRED;
01199               
01200        } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
01201               return LDAP_REFERRALS_REQUIRED;
01202        }
01203 
01204        return -1;
01205 }
01206 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
01207 
01208 /*
01209  * configuration...
01210  */
01211 
01212 enum {
01213        CH_CHAINING = 1,
01214        CH_CACHE_URI,
01215        CH_MAX_DEPTH,
01216        CH_RETURN_ERR,
01217 
01218        CH_LAST
01219 };
01220 
01221 static ConfigDriver chain_cf_gen;
01222 static ConfigCfAdd chain_cfadd;
01223 static ConfigLDAPadd chain_ldadd;
01224 #ifdef SLAP_CONFIG_DELETE
01225 static ConfigLDAPdel chain_lddel;
01226 #endif
01227 
01228 static ConfigTable chaincfg[] = {
01229 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
01230        { "chain-chaining", "args",
01231               2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
01232               "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
01233                      "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
01234                      "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
01235 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
01236        { "chain-cache-uri", "TRUE/FALSE",
01237               2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
01238               "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
01239                      "DESC 'Enables caching of URIs not present in configuration' "
01240                      "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
01241        { "chain-max-depth", "args",
01242               2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
01243               "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
01244                      "DESC 'max referral depth' "
01245                      "SYNTAX OMsInteger "
01246                      "EQUALITY integerMatch "
01247                      "SINGLE-VALUE )", NULL, NULL },
01248        { "chain-return-error", "TRUE/FALSE",
01249               2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
01250               "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
01251                      "DESC 'Errors are returned instead of the original referral' "
01252                      "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
01253        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
01254 };
01255 
01256 static ConfigOCs chainocs[] = {
01257        { "( OLcfgOvOc:3.1 "
01258               "NAME 'olcChainConfig' "
01259               "DESC 'Chain configuration' "
01260               "SUP olcOverlayConfig "
01261               "MAY ( "
01262 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
01263                      "olcChainingBehavior $ "
01264 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
01265                      "olcChainCacheURI $ "
01266                      "olcChainMaxReferralDepth $ "
01267                      "olcChainReturnError "
01268                      ") )",
01269               Cft_Overlay, chaincfg, NULL, chain_cfadd },
01270        { "( OLcfgOvOc:3.2 "
01271               "NAME 'olcChainDatabase' "
01272               "DESC 'Chain remote server configuration' "
01273               "AUXILIARY )",
01274               Cft_Misc, olcDatabaseDummy, chain_ldadd
01275 #ifdef SLAP_CONFIG_DELETE
01276               , NULL, chain_lddel
01277 #endif
01278        },
01279        { NULL, 0, NULL }
01280 };
01281 
01282 static int
01283 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
01284 {
01285        slap_overinst        *on;
01286        ldap_chain_t         *lc;
01287 
01288        ldapinfo_t           *li;
01289 
01290        AttributeDescription *ad = NULL;
01291        Attribute            *at;
01292        const char           *text;
01293 
01294        int                  rc;
01295 
01296        if ( p->ce_type != Cft_Overlay
01297               || !p->ce_bi
01298               || p->ce_bi->bi_cf_ocs != chainocs )
01299        {
01300               return LDAP_CONSTRAINT_VIOLATION;
01301        }
01302 
01303        on = (slap_overinst *)p->ce_bi;
01304        lc = (ldap_chain_t *)on->on_bi.bi_private;
01305 
01306        assert( ca->be == NULL );
01307        ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
01308 
01309        ca->be->bd_info = (BackendInfo *)on;
01310 
01311        rc = slap_str2ad( "olcDbURI", &ad, &text );
01312        assert( rc == LDAP_SUCCESS );
01313 
01314        at = attr_find( e->e_attrs, ad );
01315 #if 0
01316        if ( lc->lc_common_li == NULL && at != NULL ) {
01317               /* FIXME: we should generate an empty default entry
01318                * if none is supplied */
01319               Debug( LDAP_DEBUG_ANY, "slapd-chain: "
01320                      "first underlying database \"%s\" "
01321                      "cannot contain attribute \"%s\".\n",
01322                      e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
01323               rc = LDAP_CONSTRAINT_VIOLATION;
01324               goto done;
01325 
01326        } else
01327 #endif
01328        if ( lc->lc_common_li != NULL && at == NULL ) {
01329               /* FIXME: we should generate an empty default entry
01330                * if none is supplied */
01331               Debug( LDAP_DEBUG_ANY, "slapd-chain: "
01332                      "subsequent underlying database \"%s\" "
01333                      "must contain attribute \"%s\".\n",
01334                      e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
01335               rc = LDAP_CONSTRAINT_VIOLATION;
01336               goto done;
01337        }
01338 
01339        if ( lc->lc_common_li == NULL ) {
01340               rc = ldap_chain_db_init_common( ca->be );
01341 
01342        } else {
01343               rc = ldap_chain_db_init_one( ca->be );
01344        }
01345 
01346        if ( rc != 0 ) {
01347               Debug( LDAP_DEBUG_ANY, "slapd-chain: "
01348                      "unable to init %sunderlying database \"%s\".\n",
01349                      lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
01350               return LDAP_CONSTRAINT_VIOLATION;
01351        }
01352 
01353        li = ca->be->be_private;
01354 
01355        if ( lc->lc_common_li == NULL ) {
01356               lc->lc_common_li = li;
01357 
01358        } else {
01359               li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
01360               value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
01361               if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
01362                      ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
01363               {
01364                      Debug( LDAP_DEBUG_ANY, "slapd-chain: "
01365                             "database \"%s\" insert failed.\n",
01366                             e->e_name.bv_val, 0, 0 );
01367                      rc = LDAP_CONSTRAINT_VIOLATION;
01368                      goto done;
01369               }
01370        }
01371 
01372        ca->ca_private = on;
01373 
01374 done:;
01375        if ( rc != LDAP_SUCCESS ) {
01376               (void)ldap_chain_db_destroy_one( ca->be, NULL );
01377               ch_free( ca->be );
01378               ca->be = NULL;
01379        }
01380 
01381        return rc;
01382 }
01383 
01384 typedef struct ldap_chain_cfadd_apply_t {
01385        Operation     *op;
01386        SlapReply     *rs;
01387        Entry         *p;
01388        ConfigArgs    *ca;
01389        int           count;
01390 } ldap_chain_cfadd_apply_t;
01391 
01392 static int
01393 ldap_chain_cfadd_apply( void *datum, void *arg )
01394 {
01395        ldapinfo_t                  *li = (ldapinfo_t *)datum;
01396        ldap_chain_cfadd_apply_t    *lca = (ldap_chain_cfadd_apply_t *)arg;
01397 
01398        struct berval               bv;
01399 
01400        /* FIXME: should not hardcode "olcDatabase" here */
01401        bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
01402               "olcDatabase={%d}%s", lca->count, lback->bi_type );
01403        bv.bv_val = lca->ca->cr_msg;
01404 
01405        lca->ca->be->be_private = (void *)li;
01406        config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
01407               &bv, lback->bi_cf_ocs, &chainocs[1] );
01408 
01409        lca->count++;
01410 
01411        return 0;
01412 }
01413 
01414 static int
01415 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
01416 {
01417        CfEntryInfo   *pe = p->e_private;
01418        slap_overinst *on = (slap_overinst *)pe->ce_bi;
01419        ldap_chain_t  *lc = (ldap_chain_t *)on->on_bi.bi_private;
01420        void          *priv = (void *)ca->be->be_private;
01421 
01422        if ( lback->bi_cf_ocs ) {
01423               ldap_chain_cfadd_apply_t    lca = { 0 };
01424 
01425               lca.op = op;
01426               lca.rs = rs;
01427               lca.p = p;
01428               lca.ca = ca;
01429               lca.count = 0;
01430 
01431               (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
01432 
01433               (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
01434                      &lca, 1, AVL_INORDER );
01435 
01436               ca->be->be_private = priv;
01437        }
01438 
01439        return 0;
01440 }
01441 
01442 #ifdef SLAP_CONFIG_DELETE
01443 static int
01444 chain_lddel( CfEntryInfo *ce, Operation *op )
01445 {
01446        CfEntryInfo   *pe = ce->ce_parent;
01447        slap_overinst *on = (slap_overinst *)pe->ce_bi;
01448        ldap_chain_t  *lc = (ldap_chain_t *)on->on_bi.bi_private;
01449        ldapinfo_t    *li = (ldapinfo_t *) ce->ce_be->be_private;
01450 
01451        if ( li != lc->lc_common_li ) {
01452               if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
01453                      Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. "
01454                             "\"%s\" not found.\n", li->li_uri, 0, 0 );
01455                      return -1;
01456               }
01457        } else if ( lc->lc_lai.lai_tree ) {
01458               Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
01459                      "LDAP database when other databases are still present.\n", 0, 0, 0 );
01460               return -1;
01461        } else {
01462               lc->lc_common_li = NULL;
01463        }
01464 
01465        ce->ce_be->bd_info = lback;
01466 
01467        if ( ce->ce_be->bd_info->bi_db_close ) {
01468               ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
01469        }
01470        if ( ce->ce_be->bd_info->bi_db_destroy ) {
01471               ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
01472        }
01473 
01474        ch_free(ce->ce_be);
01475        ce->ce_be = NULL;
01476 
01477        return LDAP_SUCCESS;
01478 }
01479 #endif /* SLAP_CONFIG_DELETE */
01480 
01481 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
01482 static slap_verbmasks chaining_mode[] = {
01483        { BER_BVC("referralsRequired"),           LDAP_REFERRALS_REQUIRED },
01484        { BER_BVC("referralsPreferred"),   LDAP_REFERRALS_PREFERRED },
01485        { BER_BVC("chainingRequired"),            LDAP_CHAINING_REQUIRED },
01486        { BER_BVC("chainingPreferred"),           LDAP_CHAINING_PREFERRED },
01487        { BER_BVNULL,                      0 }
01488 };
01489 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
01490 
01491 static int
01492 chain_cf_gen( ConfigArgs *c )
01493 {
01494        slap_overinst *on = (slap_overinst *)c->bi;
01495        ldap_chain_t  *lc = (ldap_chain_t *)on->on_bi.bi_private;
01496 
01497        int           rc = 0;
01498 
01499        if ( c->op == SLAP_CONFIG_EMIT ) {
01500               switch( c->type ) {
01501 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
01502               case CH_CHAINING: {
01503                      struct berval resolve = BER_BVNULL,
01504                                    continuation = BER_BVNULL;
01505 
01506                      if ( !LDAP_CHAIN_CHAINING( lc ) ) {
01507                             return 1;
01508                      }
01509 
01510                      enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
01511                      enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
01512 
01513                      c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
01514                             + STRLENOF( " " )
01515                             + STRLENOF( "continuation=" ) + continuation.bv_len;
01516                      c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
01517                      snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
01518                             "resolve=%s continuation=%s",
01519                             resolve.bv_val, continuation.bv_val );
01520 
01521                      if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
01522                             c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
01523                                    c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
01524                             AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
01525                                    " critical", STRLENOF( " critical" ) + 1 );
01526                             c->value_bv.bv_len += STRLENOF( " critical" );
01527                      }
01528 
01529                      break;
01530               }
01531 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
01532 
01533               case CH_CACHE_URI:
01534                      c->value_int = LDAP_CHAIN_CACHE_URI( lc );
01535                      break;
01536 
01537               case CH_MAX_DEPTH:
01538                      c->value_int = lc->lc_max_depth;
01539                      break;
01540 
01541               case CH_RETURN_ERR:
01542                      c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
01543                      break;
01544 
01545               default:
01546                      assert( 0 );
01547                      rc = 1;
01548               }
01549               return rc;
01550 
01551        } else if ( c->op == LDAP_MOD_DELETE ) {
01552               switch( c->type ) {
01553               case CH_CHAINING:
01554                      return 1;
01555 
01556               case CH_CACHE_URI:
01557                      lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
01558                      break;
01559 
01560               case CH_MAX_DEPTH:
01561                      c->value_int = 0;
01562                      break;
01563 
01564               case CH_RETURN_ERR:
01565                      lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
01566                      break;
01567 
01568               default:
01569                      return 1;
01570               }
01571               return rc;
01572        }
01573 
01574        switch( c->type ) {
01575        case CH_CHAINING: {
01576 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
01577               char                 **argv = c->argv;
01578               int                  argc = c->argc;
01579               BerElementBuffer     berbuf;
01580               BerElement           *ber = (BerElement *)&berbuf;
01581               int                  resolve = -1,
01582                                    continuation = -1,
01583                                    iscritical = 0;
01584               Operation            op = { 0 };
01585               SlapReply            rs = { 0 };
01586 
01587               lc->lc_chaining_ctrlflag = 0;
01588 
01589               for ( argc--, argv++; argc > 0; argc--, argv++ ) {
01590                      if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
01591                             resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
01592                             if ( resolve == -1 ) {
01593                                    Debug( LDAP_DEBUG_ANY, "%s: "
01594                                           "illegal <resolve> value %s "
01595                                           "in \"chain-chaining>\".\n",
01596                                           c->log, argv[ 0 ], 0 );
01597                                    return 1;
01598                             }
01599 
01600                      } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
01601                             continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
01602                             if ( continuation == -1 ) {
01603                                    Debug( LDAP_DEBUG_ANY, "%s: "
01604                                           "illegal <continuation> value %s "
01605                                           "in \"chain-chaining\".\n",
01606                                           c->log, argv[ 0 ], 0 );
01607                                    return 1;
01608                             }
01609 
01610                      } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
01611                             iscritical = 1;
01612 
01613                      } else {
01614                             Debug( LDAP_DEBUG_ANY, "%s: "
01615                                    "unknown option in \"chain-chaining\".\n",
01616                                    c->log, 0, 0 );
01617                             return 1;
01618                      }
01619               }
01620 
01621               if ( resolve != -1 || continuation != -1 ) {
01622                      int    err;
01623 
01624                      if ( resolve == -1 ) {
01625                             /* default */
01626                             resolve = SLAP_CHAINING_DEFAULT;
01627                      }
01628 
01629                      ber_init2( ber, NULL, LBER_USE_DER );
01630 
01631                      err = ber_printf( ber, "{e" /* } */, resolve );
01632                      if ( err == -1 ) {
01633                             ber_free( ber, 1 );
01634                             Debug( LDAP_DEBUG_ANY, "%s: "
01635                                    "chaining behavior control encoding error!\n",
01636                                    c->log, 0, 0 );
01637                             return 1;
01638                      }
01639 
01640                      if ( continuation > -1 ) {
01641                             err = ber_printf( ber, "e", continuation );
01642                             if ( err == -1 ) {
01643                                    ber_free( ber, 1 );
01644                                    Debug( LDAP_DEBUG_ANY, "%s: "
01645                                           "chaining behavior control encoding error!\n",
01646                                           c->log, 0, 0 );
01647                                    return 1;
01648                             }
01649                      }
01650 
01651                      err = ber_printf( ber, /* { */ "N}" );
01652                      if ( err == -1 ) {
01653                             ber_free( ber, 1 );
01654                             Debug( LDAP_DEBUG_ANY, "%s: "
01655                                    "chaining behavior control encoding error!\n",
01656                                    c->log, 0, 0 );
01657                             return 1;
01658                      }
01659 
01660                      if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
01661                             exit( EXIT_FAILURE );
01662                      }
01663 
01664               } else {
01665                      BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
01666               }
01667 
01668               lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
01669               lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
01670 
01671               if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
01672               {
01673                      Debug( LDAP_DEBUG_ANY, "%s: "
01674                             "unable to parse chaining control%s%s.\n",
01675                             c->log, rs.sr_text ? ": " : "",
01676                             rs.sr_text ? rs.sr_text : "" );
01677                      return 1;
01678               }
01679 
01680               lc->lc_chaining_ctrlflag = op.o_chaining;
01681 
01682               lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
01683 
01684               rc = 0;
01685 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
01686               Debug( LDAP_DEBUG_ANY, "%s: "
01687                      "\"chaining\" control unsupported (ignored).\n",
01688                      c->log, 0, 0 );
01689 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
01690               } break;
01691 
01692        case CH_CACHE_URI:
01693               if ( c->value_int ) {
01694                      lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
01695               } else {
01696                      lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
01697               }
01698               break;
01699 
01700        case CH_MAX_DEPTH:
01701               if ( c->value_int < 0 ) {
01702                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
01703                             "<%s> invalid max referral depth %d",
01704                             c->argv[0], c->value_int );
01705                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01706                             c->log, c->cr_msg, 0 );
01707                      rc = 1;
01708                      break;
01709               }
01710               lc->lc_max_depth = c->value_int;
01711 
01712        case CH_RETURN_ERR:
01713               if ( c->value_int ) {
01714                      lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
01715               } else {
01716                      lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
01717               }
01718               break;
01719 
01720        default:
01721               assert( 0 );
01722               return 1;
01723        }
01724        return rc;
01725 }
01726 
01727 static int
01728 ldap_chain_db_init(
01729        BackendDB *be,
01730        ConfigReply *cr )
01731 {
01732        slap_overinst *on = (slap_overinst *)be->bd_info;
01733        ldap_chain_t  *lc = NULL;
01734 
01735        if ( lback == NULL ) {
01736               lback = backend_info( "ldap" );
01737 
01738               if ( lback == NULL ) {
01739                      return 1;
01740               }
01741        }
01742 
01743        lc = ch_malloc( sizeof( ldap_chain_t ) );
01744        if ( lc == NULL ) {
01745               return 1;
01746        }
01747        memset( lc, 0, sizeof( ldap_chain_t ) );
01748        lc->lc_max_depth = 1;
01749        ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
01750 
01751        on->on_bi.bi_private = (void *)lc;
01752 
01753        return 0;
01754 }
01755 
01756 static int
01757 ldap_chain_db_config(
01758        BackendDB     *be,
01759        const char    *fname,
01760        int           lineno,
01761        int           argc,
01762        char          **argv )
01763 {
01764        slap_overinst *on = (slap_overinst *)be->bd_info;
01765        ldap_chain_t  *lc = (ldap_chain_t *)on->on_bi.bi_private;
01766 
01767        int           rc = SLAP_CONF_UNKNOWN;
01768 
01769        if ( lc->lc_common_li == NULL ) {
01770               BackendDB db = *be;
01771               ldap_chain_db_init_common( &db );
01772               lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
01773        }
01774 
01775        /* Something for the chain database? */
01776        if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
01777               char          *save_argv0 = argv[ 0 ];
01778               BackendDB     db = *be;
01779               static char   *allowed_argv[] = {
01780                      /* special: put URI here, so in the meanwhile
01781                       * it detects whether a new URI is being provided */
01782                      "uri",
01783                      "nretries",
01784                      "timeout",
01785                      /* flags */
01786                      "tls",
01787                      /* FIXME: maybe rebind-as-user should be allowed
01788                       * only within known URIs... */
01789                      "rebind-as-user",
01790                      "chase-referrals",
01791                      "t-f-support",
01792                      "proxy-whoami",
01793                      NULL
01794               };
01795               int           which_argv = -1;
01796 
01797               argv[ 0 ] += STRLENOF( "chain-" );
01798 
01799               for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
01800                      if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
01801                             break;
01802                      }
01803               }
01804 
01805               if ( allowed_argv[ which_argv ] == NULL ) {
01806                      which_argv = -1;
01807 
01808                      if ( lc->lc_cfg_li == lc->lc_common_li ) {
01809                             Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01810                                    "\"%s\" only allowed within a URI directive.\n.",
01811                                    fname, lineno, argv[ 0 ] );
01812                             return 1;
01813                      }
01814               }
01815 
01816               if ( which_argv == 0 ) {
01817                      rc = ldap_chain_db_init_one( &db );
01818                      if ( rc != 0 ) {
01819                             Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01820                                    "underlying slapd-ldap initialization failed.\n.",
01821                                    fname, lineno, 0 );
01822                             return 1;
01823                      }
01824                      lc->lc_cfg_li = db.be_private;
01825               }
01826 
01827               /* TODO: add checks on what other slapd-ldap(5) args
01828                * should be put in the template; this is not quite
01829                * harmful, because attributes that shouldn't don't
01830                * get actually used, but the user should at least
01831                * be warned.
01832                */
01833 
01834               db.bd_info = lback;
01835               db.be_private = (void *)lc->lc_cfg_li;
01836               db.be_cf_ocs = lback->bi_cf_ocs;
01837 
01838               rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
01839 
01840               argv[ 0 ] = save_argv0;
01841 
01842               if ( which_argv == 0 ) {
01843 private_destroy:;
01844                      if ( rc != 0 ) {
01845                             db.bd_info = lback;
01846                             db.be_private = (void *)lc->lc_cfg_li;
01847                             ldap_chain_db_destroy_one( &db, NULL );
01848                             lc->lc_cfg_li = NULL;
01849                      } else {
01850                             if ( lc->lc_cfg_li->li_bvuri == NULL
01851                                    || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
01852                                    || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
01853                             {
01854                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01855                                           "no URI list allowed in slapo-chain.\n",
01856                                           fname, lineno, 0 );
01857                                    rc = 1;
01858                                    goto private_destroy;
01859                             }
01860 
01861                             if ( avl_insert( &lc->lc_lai.lai_tree,
01862                                    (caddr_t)lc->lc_cfg_li,
01863                                    ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
01864                             {
01865                                    Debug( LDAP_DEBUG_ANY, "%s: line %d: "
01866                                           "duplicate URI in slapo-chain.\n",
01867                                           fname, lineno, 0 );
01868                                    rc = 1;
01869                                    goto private_destroy;
01870                             }
01871                      }
01872               }
01873        }
01874 
01875        return rc;
01876 }
01877 
01878 enum db_which {
01879        db_open = 0,
01880        db_close,
01881        db_destroy,
01882 
01883        db_last
01884 };
01885 
01886 typedef struct ldap_chain_db_apply_t {
01887        BackendDB     *be;
01888        BI_db_func    *func;
01889 } ldap_chain_db_apply_t;
01890 
01891 static int
01892 ldap_chain_db_apply( void *datum, void *arg )
01893 {
01894        ldapinfo_t           *li = (ldapinfo_t *)datum;
01895        ldap_chain_db_apply_t       *lca = (ldap_chain_db_apply_t *)arg;
01896 
01897        lca->be->be_private = (void *)li;
01898 
01899        return lca->func( lca->be, NULL );
01900 }
01901 
01902 static int
01903 ldap_chain_db_func(
01904        BackendDB *be,
01905        enum db_which which
01906 )
01907 {
01908        slap_overinst *on = (slap_overinst *)be->bd_info;
01909        ldap_chain_t  *lc = (ldap_chain_t *)on->on_bi.bi_private;
01910 
01911        int           rc = 0;
01912 
01913        if ( lc ) {
01914               BI_db_func    *func = (&lback->bi_db_open)[ which ];
01915 
01916               if ( func != NULL && lc->lc_common_li != NULL ) {
01917                      BackendDB            db = *be;
01918 
01919                      db.bd_info = lback;
01920                      db.be_private = lc->lc_common_li;
01921 
01922                      rc = func( &db, NULL );
01923 
01924                      if ( rc != 0 ) {
01925                             return rc;
01926                      }
01927 
01928                      if ( lc->lc_lai.lai_tree != NULL ) {
01929                             ldap_chain_db_apply_t       lca;
01930 
01931                             lca.be = &db;
01932                             lca.func = func;
01933 
01934                             rc = avl_apply( lc->lc_lai.lai_tree,
01935                                    ldap_chain_db_apply, (void *)&lca,
01936                                    1, AVL_INORDER ) != AVL_NOMORE;
01937                      }
01938               }
01939        }
01940 
01941        return rc;
01942 }
01943 
01944 static int
01945 ldap_chain_db_open(
01946        BackendDB     *be,
01947        ConfigReply   *cr )
01948 {
01949        slap_overinst *on = (slap_overinst *) be->bd_info;
01950        ldap_chain_t  *lc = (ldap_chain_t *)on->on_bi.bi_private;
01951        slap_mask_t   monitoring;
01952        int           rc = 0;
01953 
01954 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
01955        rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
01956        if ( rc != 0 ) {
01957               return rc;
01958        }
01959 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
01960 
01961        if ( lc->lc_common_li == NULL ) {
01962               void   *be_private = be->be_private;
01963               ldap_chain_db_init_common( be );
01964               lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
01965               be->be_private = be_private;
01966        }
01967 
01968        /* filter out and restore monitoring */
01969        monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
01970        SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
01971        rc = ldap_chain_db_func( be, db_open );
01972        SLAP_DBFLAGS( be ) |= monitoring;
01973 
01974        return rc;
01975 }
01976 
01977 static int
01978 ldap_chain_db_close(
01979        BackendDB     *be,
01980        ConfigReply   *cr )
01981 {
01982 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
01983 #ifdef SLAP_CONFIG_DELETE
01984        overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
01985 #endif /* SLAP_CONFIG_DELETE */
01986 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
01987        return ldap_chain_db_func( be, db_close );
01988 }
01989 
01990 static int
01991 ldap_chain_db_destroy(
01992        BackendDB     *be,
01993        ConfigReply   *cr )
01994 {
01995        slap_overinst *on = (slap_overinst *) be->bd_info;
01996        ldap_chain_t  *lc = (ldap_chain_t *)on->on_bi.bi_private;
01997 
01998        int           rc;
01999 
02000        rc = ldap_chain_db_func( be, db_destroy );
02001 
02002        if ( lc ) {
02003               avl_free( lc->lc_lai.lai_tree, NULL );
02004               ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
02005               ch_free( lc );
02006        }
02007 
02008        return rc;
02009 }
02010 
02011 /*
02012  * inits one instance of the slapd-ldap backend, and stores
02013  * the private info in be_private of the arg
02014  */
02015 static int
02016 ldap_chain_db_init_common(
02017        BackendDB     *be )
02018 {
02019        BackendInfo   *bi = be->bd_info;
02020        ldapinfo_t    *li;
02021        int           rc;
02022 
02023        be->bd_info = lback;
02024        be->be_private = NULL;
02025        rc = lback->bi_db_init( be, NULL );
02026        if ( rc != 0 ) {
02027               return rc;
02028        }
02029        li = (ldapinfo_t *)be->be_private;
02030        li->li_urllist_f = NULL;
02031        li->li_urllist_p = NULL;
02032 
02033        be->bd_info = bi;
02034 
02035        return 0;
02036 }
02037 
02038 /*
02039  * inits one instance of the slapd-ldap backend, stores
02040  * the private info in be_private of the arg and fills
02041  * selected fields with data from the template.
02042  *
02043  * NOTE: add checks about the other fields of the template,
02044  * which are ignored and SHOULD NOT be configured by the user.
02045  */
02046 static int
02047 ldap_chain_db_init_one(
02048        BackendDB     *be )
02049 {
02050        slap_overinst *on = (slap_overinst *)be->bd_info;
02051        ldap_chain_t  *lc = (ldap_chain_t *)on->on_bi.bi_private;
02052 
02053        BackendInfo   *bi = be->bd_info;
02054        ldapinfo_t    *li;
02055 
02056        slap_op_t     t;
02057 
02058        be->bd_info = lback;
02059        be->be_private = NULL;
02060        t = lback->bi_db_init( be, NULL );
02061        if ( t != 0 ) {
02062               return t;
02063        }
02064        li = (ldapinfo_t *)be->be_private;
02065        li->li_urllist_f = NULL;
02066        li->li_urllist_p = NULL;
02067 
02068        /* copy common data */
02069        li->li_nretries = lc->lc_common_li->li_nretries;
02070        li->li_flags = lc->lc_common_li->li_flags;
02071        li->li_version = lc->lc_common_li->li_version;
02072        for ( t = 0; t < SLAP_OP_LAST; t++ ) {
02073               li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
02074        }
02075        be->bd_info = bi;
02076 
02077        return 0;
02078 }
02079 
02080 static int
02081 ldap_chain_db_open_one(
02082        BackendDB     *be )
02083 {
02084        if ( SLAP_DBMONITORING( be ) ) {
02085               ldapinfo_t    *li = (ldapinfo_t *)be->be_private;
02086 
02087               if ( li->li_uri == NULL ) {
02088                      ber_str2bv( "cn=Common Connections", 0, 1,
02089                             &li->li_monitor_info.lmi_rdn );
02090 
02091               } else {
02092                      char          *ptr;
02093 
02094                      li->li_monitor_info.lmi_rdn.bv_len
02095                             = STRLENOF( "cn=" ) + strlen( li->li_uri );
02096                      ptr = li->li_monitor_info.lmi_rdn.bv_val
02097                             = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
02098                      ptr = lutil_strcopy( ptr, "cn=" );
02099                      ptr = lutil_strcopy( ptr, li->li_uri );
02100                      ptr[ 0 ] = '\0';
02101               }
02102        }
02103 
02104        return lback->bi_db_open( be, NULL );
02105 }
02106 
02107 typedef struct ldap_chain_conn_apply_t {
02108        BackendDB     *be;
02109        Connection    *conn;
02110 } ldap_chain_conn_apply_t;
02111 
02112 static int
02113 ldap_chain_conn_apply( void *datum, void *arg )
02114 {
02115        ldapinfo_t           *li = (ldapinfo_t *)datum;
02116        ldap_chain_conn_apply_t     *lca = (ldap_chain_conn_apply_t *)arg;
02117 
02118        lca->be->be_private = (void *)li;
02119 
02120        return lback->bi_connection_destroy( lca->be, lca->conn );
02121 }
02122 
02123 static int
02124 ldap_chain_connection_destroy(
02125        BackendDB *be,
02126        Connection *conn
02127 )
02128 {
02129        slap_overinst        *on = (slap_overinst *) be->bd_info;
02130        ldap_chain_t         *lc = (ldap_chain_t *)on->on_bi.bi_private;
02131        void                 *private = be->be_private;
02132        ldap_chain_conn_apply_t     lca;
02133        int                  rc;
02134 
02135        be->be_private = NULL;
02136        lca.be = be;
02137        lca.conn = conn;
02138        ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
02139        rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
02140               (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
02141        ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
02142        be->be_private = private;
02143 
02144        return rc;
02145 }
02146 
02147 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
02148 static int
02149 ldap_chain_parse_ctrl(
02150        Operation     *op,
02151        SlapReply     *rs,
02152        LDAPControl   *ctrl )
02153 {
02154        ber_tag_t     tag;
02155        BerElement    *ber;
02156        ber_int_t     mode,
02157                      behavior;
02158 
02159        if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
02160               rs->sr_text = "Chaining behavior control specified multiple times";
02161               return LDAP_PROTOCOL_ERROR;
02162        }
02163 
02164        if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
02165               rs->sr_text = "Chaining behavior control specified with pagedResults control";
02166               return LDAP_PROTOCOL_ERROR;
02167        }
02168 
02169        if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
02170               mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
02171 
02172        } else {
02173               ber_len_t     len;
02174 
02175               /* Parse the control value
02176                *      ChainingBehavior ::= SEQUENCE { 
02177                *           resolveBehavior         Behavior OPTIONAL, 
02178                *           continuationBehavior    Behavior OPTIONAL } 
02179                *                             
02180                *      Behavior :: = ENUMERATED { 
02181                *           chainingPreferred       (0), 
02182                *           chainingRequired        (1), 
02183                *           referralsPreferred      (2), 
02184                *           referralsRequired       (3) } 
02185                */
02186 
02187               ber = ber_init( &ctrl->ldctl_value );
02188               if( ber == NULL ) {
02189                      rs->sr_text = "internal error";
02190                      return LDAP_OTHER;
02191               }
02192 
02193               tag = ber_scanf( ber, "{e" /* } */, &behavior );
02194               /* FIXME: since the whole SEQUENCE is optional,
02195                * should we accept no enumerations at all? */
02196               if ( tag != LBER_ENUMERATED ) {
02197                      rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
02198                      return LDAP_PROTOCOL_ERROR;
02199               }
02200 
02201               switch ( behavior ) {
02202               case LDAP_CHAINING_PREFERRED:
02203                      mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
02204                      break;
02205 
02206               case LDAP_CHAINING_REQUIRED:
02207                      mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
02208                      break;
02209 
02210               case LDAP_REFERRALS_PREFERRED:
02211                      mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
02212                      break;
02213 
02214               case LDAP_REFERRALS_REQUIRED:
02215                      mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
02216                      break;
02217 
02218               default:
02219                      rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
02220                      return LDAP_PROTOCOL_ERROR;
02221               }
02222 
02223               tag = ber_peek_tag( ber, &len );
02224               if ( tag == LBER_ENUMERATED ) {
02225                      tag = ber_scanf( ber, "e", &behavior );
02226                      if ( tag == LBER_ERROR ) {
02227                             rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
02228                             return LDAP_PROTOCOL_ERROR;
02229                      }
02230               }
02231 
02232               if ( tag == LBER_DEFAULT ) {
02233                      mode |= SLAP_CH_CONTINUATION_DEFAULT;
02234 
02235               } else {
02236                      switch ( behavior ) {
02237                      case LDAP_CHAINING_PREFERRED:
02238                             mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
02239                             break;
02240 
02241                      case LDAP_CHAINING_REQUIRED:
02242                             mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
02243                             break;
02244 
02245                      case LDAP_REFERRALS_PREFERRED:
02246                             mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
02247                             break;
02248 
02249                      case LDAP_REFERRALS_REQUIRED:
02250                             mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
02251                             break;
02252 
02253                      default:
02254                             rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
02255                             return LDAP_PROTOCOL_ERROR;
02256                      }
02257               }
02258 
02259               if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
02260                      rs->sr_text = "Chaining behavior control: decoding error";
02261                      return LDAP_PROTOCOL_ERROR;
02262               }
02263 
02264               (void) ber_free( ber, 1 );
02265        }
02266 
02267        op->o_chaining = mode | ( ctrl->ldctl_iscritical
02268                      ? SLAP_CONTROL_CRITICAL
02269                      : SLAP_CONTROL_NONCRITICAL );
02270 
02271        return LDAP_SUCCESS;
02272 }
02273 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
02274 
02275 int
02276 chain_initialize( void )
02277 {
02278        int rc;
02279 
02280        /* Make sure we don't exceed the bits reserved for userland */
02281        config_check_userland( CH_LAST );
02282 
02283 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
02284        rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
02285                      /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
02286                      ldap_chain_parse_ctrl, &sc_chainingBehavior );
02287        if ( rc != LDAP_SUCCESS ) {
02288               Debug( LDAP_DEBUG_ANY, "slapd-chain: "
02289                      "unable to register chaining behavior control: %d.\n",
02290                      rc, 0, 0 );
02291               return rc;
02292        }
02293 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
02294 
02295        ldapchain.on_bi.bi_type = "chain";
02296        ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
02297        ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
02298        ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
02299        ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
02300        ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
02301 
02302        ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
02303 
02304        ldapchain.on_response = ldap_chain_response;
02305 
02306        ldapchain.on_bi.bi_cf_ocs = chainocs;
02307 
02308        rc = config_register_schema( chaincfg, chainocs );
02309        if ( rc ) {
02310               return rc;
02311        }
02312 
02313        return overlay_register( &ldapchain );
02314 }
02315