1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2017 The OpenLDAP Foundation.
6 * Portions Copyright 2003 Howard Chu.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
18 * This work was initially developed by the Howard Chu for inclusion
19 * in OpenLDAP Software.
20 * This work was subsequently modified by Pierangelo Masarati.
27 #include <ac/string.h>
28 #include <ac/socket.h>
32 #include "back-ldap.h"
35 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
36 #define SLAP_CHAINING_DEFAULT LDAP_CHAINING_PREFERRED
37 #define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT
38 #define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT)
39 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
40 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
41 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
42 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
43 #define SLAP_CH_RESOLVE_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
44 #define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2)
45 #define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT)
46 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
47 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
48 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
49 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
50 #define SLAP_CH_CONTINUATION_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
52 #define o_chaining o_ctrlflag[sc_chainingBehavior]
53 #define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK)
54 #define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
55 #define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
56 #define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
58 static int sc_chainingBehavior;
59 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
65 } ldap_chain_status_t;
67 static BackendInfo *lback;
69 typedef struct ldap_chain_t {
71 * A "template" ldapinfo_t gets all common configuration items;
72 * then, for each configured URI, an entry is created in the tree;
73 * all the specific configuration items get in the current URI
76 * Then, for each referral, extract the URI and lookup the
77 * related structure. If configured to do so, allow URIs
78 * not found in the structure to create a temporary one
79 * that chains anonymously; maybe it can also be added to
80 * the tree? Should be all configurable.
83 /* "common" configuration info (anything occurring before an "uri") */
84 ldapinfo_t *lc_common_li;
86 /* current configuration info */
87 ldapinfo_t *lc_cfg_li;
89 /* tree of configured[/generated?] "uri" info */
90 ldap_avl_info_t lc_lai;
92 /* max depth in nested referrals chaining */
96 #define LDAP_CHAIN_F_NONE (0x00U)
97 #define LDAP_CHAIN_F_CHAINING (0x01U)
98 #define LDAP_CHAIN_F_CACHE_URI (0x02U)
99 #define LDAP_CHAIN_F_RETURN_ERR (0x04U)
101 #define LDAP_CHAIN_ISSET(lc, f) ( ( (lc)->lc_flags & (f) ) == (f) )
102 #define LDAP_CHAIN_CHAINING( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
103 #define LDAP_CHAIN_CACHE_URI( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
104 #define LDAP_CHAIN_RETURN_ERR( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
106 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
107 LDAPControl lc_chaining_ctrl;
108 char lc_chaining_ctrlflag;
109 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
112 static int ldap_chain_db_init_common( BackendDB *be );
113 static int ldap_chain_db_init_one( BackendDB *be );
114 static int ldap_chain_db_open_one( BackendDB *be );
115 #define ldap_chain_db_close_one(be) (0)
116 #define ldap_chain_db_destroy_one(be, rs) (lback)->bi_db_destroy( (be), (rs) )
118 typedef struct ldap_chain_cb_t {
119 ldap_chain_status_t lb_status;
121 slap_operation_t lb_op_type;
129 slap_operation_t op_type,
140 static slap_overinst ldapchain;
142 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
144 chaining_control_add(
147 LDAPControl ***oldctrlsp )
149 LDAPControl **ctrls = NULL;
152 *oldctrlsp = op->o_ctrls;
154 /* default chaining control not defined */
155 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
159 /* already present */
160 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
164 /* FIXME: check other incompatibilities */
166 /* add to other controls */
168 for ( c = 0; op->o_ctrls[ c ]; c++ )
172 ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
173 ctrls[ 0 ] = &lc->lc_chaining_ctrl;
175 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
176 ctrls[ c + 1 ] = op->o_ctrls[ c ];
179 ctrls[ c + 1 ] = NULL;
183 op->o_chaining = lc->lc_chaining_ctrlflag;
189 chaining_control_remove(
191 LDAPControl ***oldctrlsp )
193 LDAPControl **oldctrls = *oldctrlsp;
195 /* we assume that the first control is the chaining control
196 * added by the chain overlay, so it's the only one we explicitly
198 if ( op->o_ctrls != oldctrls ) {
199 if ( op->o_ctrls != NULL ) {
200 assert( op->o_ctrls[ 0 ] != NULL );
206 op->o_ctrls = oldctrls;
213 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
216 ldap_chain_uri_cmp( const void *c1, const void *c2 )
218 const ldapinfo_t *li1 = (const ldapinfo_t *)c1;
219 const ldapinfo_t *li2 = (const ldapinfo_t *)c2;
221 assert( li1->li_bvuri != NULL );
222 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
223 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
225 assert( li2->li_bvuri != NULL );
226 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
227 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
229 return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
233 ldap_chain_uri_dup( void *c1, void *c2 )
235 ldapinfo_t *li1 = (ldapinfo_t *)c1;
236 ldapinfo_t *li2 = (ldapinfo_t *)c2;
238 assert( li1->li_bvuri != NULL );
239 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
240 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
242 assert( li2->li_bvuri != NULL );
243 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
244 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
246 if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
254 * Search specific response that strips entryDN from entries
257 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
259 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
261 assert( op->o_tag == LDAP_REQ_SEARCH );
263 /* if in error, don't proceed any further */
264 if ( lb->lb_status == LDAP_CH_ERR ) {
268 if ( rs->sr_type == REP_SEARCH ) {
269 Attribute **ap = &rs->sr_entry->e_attrs;
271 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
272 /* will be generated later by frontend
273 * (a cleaner solution would be that
274 * the frontend checks if it already exists */
275 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
282 /* there SHOULD be one only! */
287 /* tell the frontend not to add generated
288 * operational attributes */
289 rs->sr_flags |= REP_NO_OPERATIONALS;
291 return SLAP_CB_CONTINUE;
293 } else if ( rs->sr_type == REP_SEARCHREF ) {
294 /* if we get it here, it means the library was unable
295 * to chase the referral... */
296 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
297 rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
300 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
301 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
302 switch ( get_continuationBehavior( op ) ) {
303 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
304 lb->lb_status = LDAP_CH_ERR;
305 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
311 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
312 return SLAP_CB_CONTINUE;
314 } else if ( rs->sr_type == REP_RESULT ) {
315 if ( rs->sr_err == LDAP_REFERRAL
316 && lb->lb_depth < lb->lb_lc->lc_max_depth
317 && rs->sr_ref != NULL )
319 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_type,
320 rs->sr_ref, lb->lb_depth );
323 /* back-ldap tried to send result */
324 lb->lb_status = LDAP_CH_RES;
325 /* don't let other callbacks run, this isn't
326 * the real result for this op.
328 op->o_callback->sc_next = NULL;
335 * Dummy response that simply traces if back-ldap tried to send
336 * anything to the client
339 ldap_chain_cb_response( Operation *op, SlapReply *rs )
341 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
343 /* if in error, don't proceed any further */
344 if ( lb->lb_status == LDAP_CH_ERR ) {
348 if ( rs->sr_type == REP_RESULT ) {
350 switch ( rs->sr_err ) {
351 case LDAP_COMPARE_TRUE:
352 case LDAP_COMPARE_FALSE:
353 if ( op->o_tag != LDAP_REQ_COMPARE ) {
359 lb->lb_status = LDAP_CH_RES;
363 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
364 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_type,
365 rs->sr_ref, lb->lb_depth );
369 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
370 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
371 switch ( get_continuationBehavior( op ) ) {
372 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
373 lb->lb_status = LDAP_CH_ERR;
374 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
380 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
387 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
389 /* strip the entryDN attribute, but keep returning results */
390 (void)ldap_chain_cb_search_response( op, rs );
393 return SLAP_CB_CONTINUE;
400 slap_operation_t op_type,
404 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
405 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
406 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
407 struct berval odn = op->o_req_dn,
408 ondn = op->o_req_ndn;
409 ldapinfo_t li = { 0 }, *lip = NULL;
410 struct berval bvuri[ 2 ] = { { 0 } };
412 /* NOTE: returned if ref is empty... */
416 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
417 LDAPControl **ctrls = NULL;
419 (void)chaining_control_add( lc, op, &ctrls );
420 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
424 for ( ; !BER_BVISNULL( ref ); ref++ ) {
425 SlapReply rs2 = { 0 };
426 LDAPURLDesc *srv = NULL;
427 req_search_s save_oq_search = op->oq_search,
428 tmp_oq_search = { 0 };
429 struct berval dn = BER_BVNULL,
436 /* We're setting the URI of the first referral;
437 * what if there are more?
443 If the client wishes to progress the operation, it MUST follow the
444 referral by contacting one of the supported services. If multiple
445 URIs are present, the client assumes that any supported URI may be
446 used to progress the operation.
448 * so we actually need to follow exactly one,
449 * and we can assume any is fine.
452 /* parse reference and use
453 * proto://[host][:port]/ only */
454 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
455 if ( rc != LDAP_URL_SUCCESS ) {
456 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
457 op->o_log_prefix, ref->bv_val, 0 );
464 if ( op->o_tag == LDAP_REQ_SEARCH ) {
465 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
466 /* RFC 4511: if scope is present, use it */
467 tmp_oq_search.rs_scope = srv->lud_scope;
470 /* RFC 4511: if scope is absent, use original */
471 tmp_oq_search.rs_scope = op->ors_scope;
476 srv->lud_scope = LDAP_SCOPE_DEFAULT;
477 dn.bv_val = srv->lud_dn;
478 filter = srv->lud_filter;
481 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
482 if ( srv->lud_dn == NULL ) {
487 ber_str2bv( srv->lud_dn, 0, 0, &dn );
488 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
489 if ( rc == LDAP_SUCCESS ) {
490 /* remove DN essentially because later on
491 * ldap_initialize() will parse the URL
492 * as a comma-separated URL list */
499 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
501 if ( srv->lud_filter != NULL
502 && srv->lud_filter[0] != '\0'
503 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
505 /* RFC 4511: if filter is present, use it;
506 * otherwise, use original */
507 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
508 if ( tmp_oq_search.rs_filter != NULL ) {
509 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
512 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
513 op->o_log_prefix, ref->bv_val, srv->lud_filter );
518 srv->lud_filter = NULL;
520 if ( rc == LDAP_SUCCESS ) {
521 li.li_uri = ldap_url_desc2str( srv );
524 srv->lud_dn = dn.bv_val;
525 srv->lud_filter = filter;
526 ldap_free_urldesc( srv );
528 if ( rc != LDAP_SUCCESS ) {
534 if ( li.li_uri == NULL ) {
535 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
536 op->o_log_prefix, ref->bv_val, 0 );
540 goto further_cleanup;
543 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
544 op->o_log_prefix, ref->bv_val, li.li_uri );
549 if ( op->o_tag == LDAP_REQ_SEARCH ) {
550 op->ors_scope = tmp_oq_search.rs_scope;
551 if ( tmp_oq_search.rs_filter != NULL ) {
552 op->ors_filter = tmp_oq_search.rs_filter;
553 op->ors_filterstr = tmp_oq_search.rs_filterstr;
557 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
559 /* Searches for a ldapinfo in the avl tree */
560 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
561 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
562 (caddr_t)&li, ldap_chain_uri_cmp );
563 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
566 op->o_bd->be_private = (void *)lip;
568 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
569 op->o_log_prefix, ref->bv_val, li.li_uri );
572 rc = ldap_chain_db_init_one( op->o_bd );
574 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
575 op->o_log_prefix, ref->bv_val, li.li_uri );
578 lip = (ldapinfo_t *)op->o_bd->be_private;
579 lip->li_uri = li.li_uri;
580 lip->li_bvuri = bvuri;
581 rc = ldap_chain_db_open_one( op->o_bd );
583 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
584 op->o_log_prefix, ref->bv_val, li.li_uri );
586 lip->li_bvuri = NULL;
587 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
591 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
592 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
593 if ( avl_insert( &lc->lc_lai.lai_tree,
594 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
596 /* someone just inserted another;
597 * don't bother, use this and then
601 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
607 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
608 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
611 lb->lb_op_type = op_type;
612 lb->lb_depth = depth + 1;
614 rc = (&lback->bi_op_bind)[ op_type ]( op, &rs2 );
616 /* note the first error */
617 if ( first_rc == -1 ) {
622 ldap_memfree( li.li_uri );
627 lip->li_bvuri = NULL;
628 (void)ldap_chain_db_close_one( op->o_bd );
629 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
633 if ( op->o_req_dn.bv_val == pdn.bv_val ) {
635 op->o_req_ndn = ondn;
639 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
640 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
643 if ( op->o_tag == LDAP_REQ_SEARCH ) {
644 if ( tmp_oq_search.rs_filter != NULL ) {
645 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
648 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
649 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
652 op->oq_search = save_oq_search;
655 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
663 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
664 (void)chaining_control_remove( op, &ctrls );
665 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
667 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
682 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
683 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
684 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
685 ldapinfo_t li = { 0 }, *lip = NULL;
686 struct berval bvuri[ 2 ] = { { 0 } };
688 struct berval odn = op->o_req_dn,
689 ondn = op->o_req_ndn;
690 Entry *save_entry = rs->sr_entry;
691 slap_mask_t save_flags = rs->sr_flags;
696 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
697 LDAPControl **ctrls = NULL;
699 (void)chaining_control_add( lc, op, &ctrls );
700 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
702 assert( rs->sr_type == REP_SEARCHREF );
704 rs->sr_type = REP_SEARCH;
706 /* if we parse the URI then by no means
707 * we can cache stuff or reuse connections,
708 * because in back-ldap there's no caching
709 * based on the URI value, which is supposed
710 * to be set once for all (correct?) */
712 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
713 SlapReply rs2 = { REP_RESULT };
715 req_search_s save_oq_search = op->oq_search,
716 tmp_oq_search = { 0 };
724 /* parse reference and use
725 * proto://[host][:port]/ only */
726 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
727 if ( rc != LDAP_URL_SUCCESS ) {
728 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
729 op->o_log_prefix, ref->bv_val, 0 );
732 rs->sr_err = LDAP_OTHER;
736 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
737 /* RFC 4511: if scope is present, use it */
738 tmp_oq_search.rs_scope = srv->lud_scope;
741 /* RFC 4511: if scope is absent, use original */
742 /* Section 4.5.3: if scope is onelevel, use base */
743 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
744 tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
746 tmp_oq_search.rs_scope = op->ors_scope;
750 srv->lud_scope = LDAP_SCOPE_DEFAULT;
751 dn.bv_val = srv->lud_dn;
752 filter = srv->lud_filter;
755 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
756 if ( srv->lud_dn == NULL ) {
760 if ( save_entry != NULL ) {
761 /* use the "right" DN, if available */
762 pdn = save_entry->e_name;
763 ndn = save_entry->e_nname;
764 } /* else leave the original req DN in place, if any RFC 4511 */
767 /* RFC 4511: if DN is present, use it */
768 ber_str2bv( srv->lud_dn, 0, 0, &dn );
769 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
770 if ( rc == LDAP_SUCCESS ) {
771 /* remove DN essentially because later on
772 * ldap_initialize() will parse the URL
773 * as a comma-separated URL list */
780 if ( rc == LDAP_SUCCESS ) {
782 if ( srv->lud_filter != NULL
783 && srv->lud_filter[0] != '\0'
784 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
786 /* RFC 4511: if filter is present, use it;
787 * otherwise, use original */
788 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
789 if ( tmp_oq_search.rs_filter != NULL ) {
790 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
793 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
794 op->o_log_prefix, ref->bv_val, srv->lud_filter );
799 srv->lud_filter = NULL;
801 if ( rc == LDAP_SUCCESS ) {
802 li.li_uri = ldap_url_desc2str( srv );
805 srv->lud_dn = dn.bv_val;
806 srv->lud_filter = filter;
807 ldap_free_urldesc( srv );
809 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
810 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
811 op->o_log_prefix, ref->bv_val, 0 );
815 goto further_cleanup;
818 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
819 op->o_log_prefix, ref->bv_val, li.li_uri );
823 op->ors_scope = tmp_oq_search.rs_scope;
824 if ( tmp_oq_search.rs_filter != NULL ) {
825 op->ors_filter = tmp_oq_search.rs_filter;
826 op->ors_filterstr = tmp_oq_search.rs_filterstr;
829 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
831 /* Searches for a ldapinfo in the avl tree */
832 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
833 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
834 (caddr_t)&li, ldap_chain_uri_cmp );
835 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
838 op->o_bd->be_private = (void *)lip;
840 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
841 op->o_log_prefix, ref->bv_val, li.li_uri );
844 /* if none is found, create a temporary... */
845 rc = ldap_chain_db_init_one( op->o_bd );
847 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
848 op->o_log_prefix, ref->bv_val, li.li_uri );
851 lip = (ldapinfo_t *)op->o_bd->be_private;
852 lip->li_uri = li.li_uri;
853 lip->li_bvuri = bvuri;
854 rc = ldap_chain_db_open_one( op->o_bd );
856 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
857 op->o_log_prefix, ref->bv_val, li.li_uri );
859 lip->li_bvuri = NULL;
860 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
864 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
865 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
866 if ( avl_insert( &lc->lc_lai.lai_tree,
867 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
869 /* someone just inserted another;
870 * don't bother, use this and then
874 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
880 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
881 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
884 lb->lb_op_type = op_search;
885 lb->lb_depth = depth + 1;
887 /* FIXME: should we also copy filter and scope?
888 * according to RFC3296, no */
889 rc = lback->bi_op_search( op, &rs2 );
890 if ( first_rc == -1 ) {
895 ldap_memfree( li.li_uri );
900 lip->li_bvuri = NULL;
901 (void)ldap_chain_db_close_one( op->o_bd );
902 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
906 if ( op->o_req_dn.bv_val == pdn.bv_val ) {
908 op->o_req_ndn = ondn;
912 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
913 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
916 if ( tmp_oq_search.rs_filter != NULL ) {
917 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
920 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
921 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
924 op->oq_search = save_oq_search;
926 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
934 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
935 (void)chaining_control_remove( op, &ctrls );
936 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
938 rs->sr_type = REP_SEARCHREF;
939 rs->sr_entry = save_entry;
940 rs->sr_flags = save_flags;
942 if ( rc != LDAP_SUCCESS ) {
943 /* couldn't chase any of the referrals */
944 if ( first_rc != -1 ) {
948 rc = SLAP_CB_CONTINUE;
956 ldap_chain_response( Operation *op, SlapReply *rs )
958 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
959 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
960 BackendDB db, *bd = op->o_bd;
961 ldap_chain_cb_t lb = { 0 };
962 slap_callback *sc = op->o_callback,
965 const char *text = NULL;
968 struct berval ndn = op->o_ndn;
970 int sr_err = rs->sr_err;
971 slap_reply_t sr_type = rs->sr_type;
972 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
973 slap_mask_t chain_mask = 0;
974 ber_len_t chain_shift = 0;
975 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
977 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
978 return SLAP_CB_CONTINUE;
981 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
982 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
983 switch ( get_resolveBehavior( op ) ) {
984 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
985 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
986 return SLAP_CB_CONTINUE;
989 chain_mask = SLAP_CH_RESOLVE_MASK;
990 chain_shift = SLAP_CH_RESOLVE_SHIFT;
994 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
995 switch ( get_continuationBehavior( op ) ) {
996 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
997 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
998 return SLAP_CB_CONTINUE;
1001 chain_mask = SLAP_CH_CONTINUATION_MASK;
1002 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
1006 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1009 * TODO: add checks on who/when chain operations; e.g.:
1010 * a) what identities are authorized
1011 * b) what request DN (e.g. only chain requests rooted at <DN>)
1012 * c) what referral URIs
1013 * d) what protocol scheme (e.g. only ldaps://)
1018 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1023 matched = rs->sr_matched;
1024 rs->sr_matched = NULL;
1028 /* we need this to know if back-ldap returned any result */
1030 sc2.sc_next = sc->sc_next;
1031 sc2.sc_private = &lb;
1032 sc2.sc_response = ldap_chain_cb_response;
1033 op->o_callback = &sc2;
1035 /* Chaining can be performed by a privileged user on behalf
1036 * of normal users, using the ProxyAuthz control, by exploiting
1037 * the identity assertion feature of back-ldap; see idassert-*
1038 * directives in slapd-ldap(5).
1040 * FIXME: the idassert-authcDN is one, will it be fine regardless
1041 * of the URI we obtain from the referral?
1044 switch ( op->o_tag ) {
1045 case LDAP_REQ_BIND: {
1046 struct berval rndn = op->o_req_ndn;
1047 Connection *conn = op->o_conn;
1049 /* FIXME: can we really get a referral for binds? */
1050 op->o_req_ndn = slap_empty_bv;
1052 rc = ldap_chain_op( op, rs, op_bind, ref, 0 );
1053 op->o_req_ndn = rndn;
1059 rc = ldap_chain_op( op, rs, op_add, ref, 0 );
1062 case LDAP_REQ_DELETE:
1063 rc = ldap_chain_op( op, rs, op_delete, ref, 0 );
1066 case LDAP_REQ_MODRDN:
1067 rc = ldap_chain_op( op, rs, op_modrdn, ref, 0 );
1070 case LDAP_REQ_MODIFY:
1071 rc = ldap_chain_op( op, rs, op_modify, ref, 0 );
1074 case LDAP_REQ_COMPARE:
1075 rc = ldap_chain_op( op, rs, op_compare, ref, 0 );
1076 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1081 case LDAP_REQ_SEARCH:
1082 if ( rs->sr_type == REP_SEARCHREF ) {
1083 sc2.sc_response = ldap_chain_cb_search_response;
1084 rc = ldap_chain_search( op, rs, ref, 0 );
1087 /* we might get here before any database actually
1088 * performed a search; in those cases, we need
1089 * to check limits, to make sure safe defaults
1091 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1092 rc = ldap_chain_op( op, rs, op_search, ref, 0 );
1094 rc = SLAP_CB_CONTINUE;
1099 case LDAP_REQ_EXTENDED:
1100 rc = ldap_chain_op( op, rs, op_extended, ref, 0 );
1101 /* FIXME: ldap_back_extended() by design
1102 * doesn't send result; frontend is expected
1104 /* FIXME: what about chaining? */
1105 if ( rc != SLAPD_ABANDON ) {
1107 send_ldap_extended( op, rs );
1110 lb.lb_status = LDAP_CH_RES;
1114 rc = SLAP_CB_CONTINUE;
1124 sr_err = rs->sr_err;
1125 /* slapd-ldap sent response */
1126 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1127 /* FIXME: should we send response? */
1128 Debug( LDAP_DEBUG_ANY,
1129 "%s: ldap_chain_response: "
1130 "overlay should have sent result.\n",
1131 op->o_log_prefix, 0, 0 );
1136 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1137 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1141 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1142 case LDAP_CHAINING_REQUIRED:
1144 op->o_callback = NULL;
1145 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1146 "operation cannot be completed without chaining" );
1150 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1151 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1152 sr_err = rs->sr_err = rc;
1153 rs->sr_type = sr_type;
1156 rc = SLAP_CB_CONTINUE;
1157 rs->sr_err = sr_err;
1158 rs->sr_type = sr_type;
1160 rs->sr_matched = matched;
1163 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1166 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1169 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1170 /* give the remaining callbacks a chance */
1171 op->o_callback = sc->sc_next;
1172 rc = rs->sr_err = slap_map_api2result( rs );
1173 send_ldap_result( op, rs );
1177 rs->sr_err = sr_err;
1178 rs->sr_type = sr_type;
1180 rs->sr_matched = matched;
1183 op->o_callback = sc;
1189 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1191 ldap_chain_parse_ctrl(
1194 LDAPControl *ctrl );
1197 str2chain( const char *s )
1199 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1200 return LDAP_CHAINING_PREFERRED;
1202 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1203 return LDAP_CHAINING_REQUIRED;
1205 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1206 return LDAP_REFERRALS_PREFERRED;
1208 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1209 return LDAP_REFERRALS_REQUIRED;
1214 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1229 static ConfigDriver chain_cf_gen;
1230 static ConfigCfAdd chain_cfadd;
1231 static ConfigLDAPadd chain_ldadd;
1232 #ifdef SLAP_CONFIG_DELETE
1233 static ConfigLDAPdel chain_lddel;
1236 static ConfigTable chaincfg[] = {
1237 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1238 { "chain-chaining", "args",
1239 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1240 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1241 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1242 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1243 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1244 { "chain-cache-uri", "TRUE/FALSE",
1245 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1246 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1247 "DESC 'Enables caching of URIs not present in configuration' "
1248 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1249 { "chain-max-depth", "args",
1250 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1251 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1252 "DESC 'max referral depth' "
1253 "SYNTAX OMsInteger "
1254 "EQUALITY integerMatch "
1255 "SINGLE-VALUE )", NULL, NULL },
1256 { "chain-return-error", "TRUE/FALSE",
1257 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1258 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1259 "DESC 'Errors are returned instead of the original referral' "
1260 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1261 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1264 static ConfigOCs chainocs[] = {
1265 { "( OLcfgOvOc:3.1 "
1266 "NAME 'olcChainConfig' "
1267 "DESC 'Chain configuration' "
1268 "SUP olcOverlayConfig "
1270 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1271 "olcChainingBehavior $ "
1272 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1273 "olcChainCacheURI $ "
1274 "olcChainMaxReferralDepth $ "
1275 "olcChainReturnError "
1277 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1278 { "( OLcfgOvOc:3.2 "
1279 "NAME 'olcChainDatabase' "
1280 "DESC 'Chain remote server configuration' "
1282 Cft_Misc, olcDatabaseDummy, chain_ldadd
1283 #ifdef SLAP_CONFIG_DELETE
1291 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1298 AttributeDescription *ad = NULL;
1304 if ( p->ce_type != Cft_Overlay
1306 || p->ce_bi->bi_cf_ocs != chainocs )
1308 return LDAP_CONSTRAINT_VIOLATION;
1311 on = (slap_overinst *)p->ce_bi;
1312 lc = (ldap_chain_t *)on->on_bi.bi_private;
1314 assert( ca->be == NULL );
1315 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1317 ca->be->bd_info = (BackendInfo *)on;
1319 rc = slap_str2ad( "olcDbURI", &ad, &text );
1320 assert( rc == LDAP_SUCCESS );
1322 at = attr_find( e->e_attrs, ad );
1324 if ( lc->lc_common_li == NULL && at != NULL ) {
1325 /* FIXME: we should generate an empty default entry
1326 * if none is supplied */
1327 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1328 "first underlying database \"%s\" "
1329 "cannot contain attribute \"%s\".\n",
1330 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1331 rc = LDAP_CONSTRAINT_VIOLATION;
1336 if ( lc->lc_common_li != NULL && at == NULL ) {
1337 /* FIXME: we should generate an empty default entry
1338 * if none is supplied */
1339 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1340 "subsequent underlying database \"%s\" "
1341 "must contain attribute \"%s\".\n",
1342 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1343 rc = LDAP_CONSTRAINT_VIOLATION;
1347 if ( lc->lc_common_li == NULL ) {
1348 rc = ldap_chain_db_init_common( ca->be );
1351 li = ca->be->be_private;
1352 lc->lc_common_li = lc->lc_cfg_li = li;
1355 rc = ldap_chain_db_init_one( ca->be );
1359 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1360 "unable to init %sunderlying database \"%s\".\n",
1361 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1362 return LDAP_CONSTRAINT_VIOLATION;
1365 li = ca->be->be_private;
1368 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1369 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1370 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1371 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1373 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1374 "database \"%s\" insert failed.\n",
1375 e->e_name.bv_val, 0, 0 );
1376 rc = LDAP_CONSTRAINT_VIOLATION;
1381 ca->ca_private = on;
1384 if ( rc != LDAP_SUCCESS ) {
1385 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1393 typedef struct ldap_chain_cfadd_apply_t {
1399 } ldap_chain_cfadd_apply_t;
1402 ldap_chain_cfadd_apply( void *datum, void *arg )
1404 ldapinfo_t *li = (ldapinfo_t *)datum;
1405 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1409 /* FIXME: should not hardcode "olcDatabase" here */
1410 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1411 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1412 bv.bv_val = lca->ca->cr_msg;
1414 lca->ca->be->be_private = (void *)li;
1415 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1416 &bv, lback->bi_cf_ocs, &chainocs[1] );
1424 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1426 CfEntryInfo *pe = p->e_private;
1427 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1428 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1429 void *priv = (void *)ca->be->be_private;
1431 if ( lback->bi_cf_ocs ) {
1432 ldap_chain_cfadd_apply_t lca = { 0 };
1440 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1442 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1443 &lca, 1, AVL_INORDER );
1445 ca->be->be_private = priv;
1451 #ifdef SLAP_CONFIG_DELETE
1453 chain_lddel( CfEntryInfo *ce, Operation *op )
1455 CfEntryInfo *pe = ce->ce_parent;
1456 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1457 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1458 ldapinfo_t *li = (ldapinfo_t *) ce->ce_be->be_private;
1460 if ( li != lc->lc_common_li ) {
1461 if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
1462 Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. "
1463 "\"%s\" not found.\n", li->li_uri, 0, 0 );
1466 } else if ( lc->lc_lai.lai_tree ) {
1467 Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
1468 "LDAP database when other databases are still present.\n", 0, 0, 0 );
1471 lc->lc_common_li = NULL;
1474 ce->ce_be->bd_info = lback;
1476 if ( ce->ce_be->bd_info->bi_db_close ) {
1477 ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
1479 if ( ce->ce_be->bd_info->bi_db_destroy ) {
1480 ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
1486 return LDAP_SUCCESS;
1488 #endif /* SLAP_CONFIG_DELETE */
1490 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1491 static slap_verbmasks chaining_mode[] = {
1492 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1493 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1494 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1495 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1498 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1501 chain_cf_gen( ConfigArgs *c )
1503 slap_overinst *on = (slap_overinst *)c->bi;
1504 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1508 if ( c->op == SLAP_CONFIG_EMIT ) {
1510 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1512 struct berval resolve = BER_BVNULL,
1513 continuation = BER_BVNULL;
1515 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1519 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1520 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1522 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1524 + STRLENOF( "continuation=" ) + continuation.bv_len;
1525 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1526 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1527 "resolve=%s continuation=%s",
1528 resolve.bv_val, continuation.bv_val );
1530 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1531 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1532 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1533 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1534 " critical", STRLENOF( " critical" ) + 1 );
1535 c->value_bv.bv_len += STRLENOF( " critical" );
1540 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1543 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1547 c->value_int = lc->lc_max_depth;
1551 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1560 } else if ( c->op == LDAP_MOD_DELETE ) {
1566 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1574 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1585 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1586 char **argv = c->argv;
1588 BerElementBuffer berbuf;
1589 BerElement *ber = (BerElement *)&berbuf;
1593 Operation op = { 0 };
1594 SlapReply rs = { 0 };
1596 lc->lc_chaining_ctrlflag = 0;
1598 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1599 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1600 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1601 if ( resolve == -1 ) {
1602 Debug( LDAP_DEBUG_ANY, "%s: "
1603 "illegal <resolve> value %s "
1604 "in \"chain-chaining>\".\n",
1605 c->log, argv[ 0 ], 0 );
1609 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1610 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1611 if ( continuation == -1 ) {
1612 Debug( LDAP_DEBUG_ANY, "%s: "
1613 "illegal <continuation> value %s "
1614 "in \"chain-chaining\".\n",
1615 c->log, argv[ 0 ], 0 );
1619 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1623 Debug( LDAP_DEBUG_ANY, "%s: "
1624 "unknown option in \"chain-chaining\".\n",
1630 if ( resolve != -1 || continuation != -1 ) {
1633 if ( resolve == -1 ) {
1635 resolve = SLAP_CHAINING_DEFAULT;
1638 ber_init2( ber, NULL, LBER_USE_DER );
1640 err = ber_printf( ber, "{e" /* } */, resolve );
1643 Debug( LDAP_DEBUG_ANY, "%s: "
1644 "chaining behavior control encoding error!\n",
1649 if ( continuation > -1 ) {
1650 err = ber_printf( ber, "e", continuation );
1653 Debug( LDAP_DEBUG_ANY, "%s: "
1654 "chaining behavior control encoding error!\n",
1660 err = ber_printf( ber, /* { */ "N}" );
1663 Debug( LDAP_DEBUG_ANY, "%s: "
1664 "chaining behavior control encoding error!\n",
1669 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1670 exit( EXIT_FAILURE );
1674 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1677 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1678 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1680 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1682 Debug( LDAP_DEBUG_ANY, "%s: "
1683 "unable to parse chaining control%s%s.\n",
1684 c->log, rs.sr_text ? ": " : "",
1685 rs.sr_text ? rs.sr_text : "" );
1689 lc->lc_chaining_ctrlflag = op.o_chaining;
1691 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1694 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1695 Debug( LDAP_DEBUG_ANY, "%s: "
1696 "\"chaining\" control unsupported (ignored).\n",
1698 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1702 if ( c->value_int ) {
1703 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1705 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1710 if ( c->value_int < 0 ) {
1711 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1712 "<%s> invalid max referral depth %d",
1713 c->argv[0], c->value_int );
1714 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1715 c->log, c->cr_msg, 0 );
1719 lc->lc_max_depth = c->value_int;
1722 if ( c->value_int ) {
1723 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1725 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1741 slap_overinst *on = (slap_overinst *)be->bd_info;
1742 ldap_chain_t *lc = NULL;
1744 if ( lback == NULL ) {
1745 lback = backend_info( "ldap" );
1747 if ( lback == NULL ) {
1752 lc = ch_malloc( sizeof( ldap_chain_t ) );
1756 memset( lc, 0, sizeof( ldap_chain_t ) );
1757 lc->lc_max_depth = 1;
1758 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1760 on->on_bi.bi_private = (void *)lc;
1766 ldap_chain_db_config(
1773 slap_overinst *on = (slap_overinst *)be->bd_info;
1774 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1776 int rc = SLAP_CONF_UNKNOWN;
1778 if ( lc->lc_common_li == NULL ) {
1780 ldap_chain_db_init_common( &db );
1781 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1784 /* Something for the chain database? */
1785 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1786 char *save_argv0 = argv[ 0 ];
1788 static char *allowed_argv[] = {
1789 /* special: put URI here, so in the meanwhile
1790 * it detects whether a new URI is being provided */
1796 /* FIXME: maybe rebind-as-user should be allowed
1797 * only within known URIs... */
1804 int which_argv = -1;
1806 argv[ 0 ] += STRLENOF( "chain-" );
1808 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1809 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1814 if ( allowed_argv[ which_argv ] == NULL ) {
1817 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1818 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1819 "\"%s\" only allowed within a URI directive.\n.",
1820 fname, lineno, argv[ 0 ] );
1825 if ( which_argv == 0 ) {
1826 rc = ldap_chain_db_init_one( &db );
1828 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1829 "underlying slapd-ldap initialization failed.\n.",
1833 lc->lc_cfg_li = db.be_private;
1836 /* TODO: add checks on what other slapd-ldap(5) args
1837 * should be put in the template; this is not quite
1838 * harmful, because attributes that shouldn't don't
1839 * get actually used, but the user should at least
1844 db.be_private = (void *)lc->lc_cfg_li;
1845 db.be_cf_ocs = lback->bi_cf_ocs;
1847 rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1849 argv[ 0 ] = save_argv0;
1851 if ( which_argv == 0 ) {
1855 db.be_private = (void *)lc->lc_cfg_li;
1856 ldap_chain_db_destroy_one( &db, NULL );
1857 lc->lc_cfg_li = NULL;
1859 if ( lc->lc_cfg_li->li_bvuri == NULL
1860 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1861 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1863 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1864 "no URI list allowed in slapo-chain.\n",
1867 goto private_destroy;
1870 if ( avl_insert( &lc->lc_lai.lai_tree,
1871 (caddr_t)lc->lc_cfg_li,
1872 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1874 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1875 "duplicate URI in slapo-chain.\n",
1878 goto private_destroy;
1895 typedef struct ldap_chain_db_apply_t {
1898 } ldap_chain_db_apply_t;
1901 ldap_chain_db_apply( void *datum, void *arg )
1903 ldapinfo_t *li = (ldapinfo_t *)datum;
1904 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1906 lca->be->be_private = (void *)li;
1908 return lca->func( lca->be, NULL );
1917 slap_overinst *on = (slap_overinst *)be->bd_info;
1918 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1923 BI_db_func *func = (&lback->bi_db_open)[ which ];
1925 if ( func != NULL && lc->lc_common_li != NULL ) {
1929 db.be_private = lc->lc_common_li;
1931 rc = func( &db, NULL );
1937 if ( lc->lc_lai.lai_tree != NULL ) {
1938 ldap_chain_db_apply_t lca;
1943 rc = avl_apply( lc->lc_lai.lai_tree,
1944 ldap_chain_db_apply, (void *)&lca,
1945 1, AVL_INORDER ) != AVL_NOMORE;
1958 slap_overinst *on = (slap_overinst *) be->bd_info;
1959 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1960 slap_mask_t monitoring;
1963 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1964 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1968 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1970 if ( lc->lc_common_li == NULL ) {
1971 void *be_private = be->be_private;
1972 ldap_chain_db_init_common( be );
1973 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1974 be->be_private = be_private;
1977 /* filter out and restore monitoring */
1978 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1979 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1980 rc = ldap_chain_db_func( be, db_open );
1981 SLAP_DBFLAGS( be ) |= monitoring;
1987 ldap_chain_db_close(
1991 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1992 #ifdef SLAP_CONFIG_DELETE
1993 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1994 #endif /* SLAP_CONFIG_DELETE */
1995 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1996 return ldap_chain_db_func( be, db_close );
2000 ldap_chain_db_destroy(
2004 slap_overinst *on = (slap_overinst *) be->bd_info;
2005 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2009 rc = ldap_chain_db_func( be, db_destroy );
2012 avl_free( lc->lc_lai.lai_tree, NULL );
2013 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
2021 * inits one instance of the slapd-ldap backend, and stores
2022 * the private info in be_private of the arg
2025 ldap_chain_db_init_common(
2028 BackendInfo *bi = be->bd_info;
2032 be->bd_info = lback;
2033 be->be_private = NULL;
2034 rc = lback->bi_db_init( be, NULL );
2038 li = (ldapinfo_t *)be->be_private;
2039 li->li_urllist_f = NULL;
2040 li->li_urllist_p = NULL;
2048 * inits one instance of the slapd-ldap backend, stores
2049 * the private info in be_private of the arg and fills
2050 * selected fields with data from the template.
2052 * NOTE: add checks about the other fields of the template,
2053 * which are ignored and SHOULD NOT be configured by the user.
2056 ldap_chain_db_init_one(
2059 slap_overinst *on = (slap_overinst *)be->bd_info;
2060 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2062 BackendInfo *bi = be->bd_info;
2067 be->bd_info = lback;
2068 be->be_private = NULL;
2069 t = lback->bi_db_init( be, NULL );
2073 li = (ldapinfo_t *)be->be_private;
2074 li->li_urllist_f = NULL;
2075 li->li_urllist_p = NULL;
2077 /* copy common data */
2078 li->li_nretries = lc->lc_common_li->li_nretries;
2079 li->li_flags = lc->lc_common_li->li_flags;
2080 li->li_version = lc->lc_common_li->li_version;
2081 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2082 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2090 ldap_chain_db_open_one(
2093 if ( SLAP_DBMONITORING( be ) ) {
2094 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
2096 if ( li->li_uri == NULL ) {
2097 ber_str2bv( "cn=Common Connections", 0, 1,
2098 &li->li_monitor_info.lmi_conn_rdn );
2099 ber_str2bv( "cn=Operations on Common Connections", 0, 1,
2100 &li->li_monitor_info.lmi_conn_rdn );
2105 li->li_monitor_info.lmi_conn_rdn.bv_len
2106 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2107 ptr = li->li_monitor_info.lmi_conn_rdn.bv_val
2108 = ch_malloc( li->li_monitor_info.lmi_conn_rdn.bv_len + 1 );
2109 ptr = lutil_strcopy( ptr, "cn=" );
2110 ptr = lutil_strcopy( ptr, li->li_uri );
2113 li->li_monitor_info.lmi_ops_rdn.bv_len
2114 = STRLENOF( "cn=Operations on " ) + strlen( li->li_uri );
2115 ptr = li->li_monitor_info.lmi_ops_rdn.bv_val
2116 = ch_malloc( li->li_monitor_info.lmi_ops_rdn.bv_len + 1 );
2117 ptr = lutil_strcopy( ptr, "cn=Operations on " );
2118 ptr = lutil_strcopy( ptr, li->li_uri );
2123 return lback->bi_db_open( be, NULL );
2126 typedef struct ldap_chain_conn_apply_t {
2129 } ldap_chain_conn_apply_t;
2132 ldap_chain_conn_apply( void *datum, void *arg )
2134 ldapinfo_t *li = (ldapinfo_t *)datum;
2135 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2137 lca->be->be_private = (void *)li;
2139 return lback->bi_connection_destroy( lca->be, lca->conn );
2143 ldap_chain_connection_destroy(
2148 slap_overinst *on = (slap_overinst *) be->bd_info;
2149 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2150 void *private = be->be_private;
2151 ldap_chain_conn_apply_t lca;
2154 be->be_private = NULL;
2157 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2158 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2159 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2160 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2161 be->be_private = private;
2166 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2168 ldap_chain_parse_ctrl(
2178 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2179 rs->sr_text = "Chaining behavior control specified multiple times";
2180 return LDAP_PROTOCOL_ERROR;
2183 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2184 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2185 return LDAP_PROTOCOL_ERROR;
2188 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2189 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2194 /* Parse the control value
2195 * ChainingBehavior ::= SEQUENCE {
2196 * resolveBehavior Behavior OPTIONAL,
2197 * continuationBehavior Behavior OPTIONAL }
2199 * Behavior :: = ENUMERATED {
2200 * chainingPreferred (0),
2201 * chainingRequired (1),
2202 * referralsPreferred (2),
2203 * referralsRequired (3) }
2206 ber = ber_init( &ctrl->ldctl_value );
2208 rs->sr_text = "internal error";
2212 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2213 /* FIXME: since the whole SEQUENCE is optional,
2214 * should we accept no enumerations at all? */
2215 if ( tag != LBER_ENUMERATED ) {
2216 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2217 return LDAP_PROTOCOL_ERROR;
2220 switch ( behavior ) {
2221 case LDAP_CHAINING_PREFERRED:
2222 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2225 case LDAP_CHAINING_REQUIRED:
2226 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2229 case LDAP_REFERRALS_PREFERRED:
2230 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2233 case LDAP_REFERRALS_REQUIRED:
2234 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2238 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2239 return LDAP_PROTOCOL_ERROR;
2242 tag = ber_peek_tag( ber, &len );
2243 if ( tag == LBER_ENUMERATED ) {
2244 tag = ber_scanf( ber, "e", &behavior );
2245 if ( tag == LBER_ERROR ) {
2246 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2247 return LDAP_PROTOCOL_ERROR;
2251 if ( tag == LBER_DEFAULT ) {
2252 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2255 switch ( behavior ) {
2256 case LDAP_CHAINING_PREFERRED:
2257 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2260 case LDAP_CHAINING_REQUIRED:
2261 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2264 case LDAP_REFERRALS_PREFERRED:
2265 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2268 case LDAP_REFERRALS_REQUIRED:
2269 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2273 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2274 return LDAP_PROTOCOL_ERROR;
2278 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2279 rs->sr_text = "Chaining behavior control: decoding error";
2280 return LDAP_PROTOCOL_ERROR;
2283 (void) ber_free( ber, 1 );
2286 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2287 ? SLAP_CONTROL_CRITICAL
2288 : SLAP_CONTROL_NONCRITICAL );
2290 return LDAP_SUCCESS;
2292 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2295 chain_initialize( void )
2299 /* Make sure we don't exceed the bits reserved for userland */
2300 config_check_userland( CH_LAST );
2302 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2303 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2304 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2305 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2306 if ( rc != LDAP_SUCCESS ) {
2307 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2308 "unable to register chaining behavior control: %d.\n",
2312 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2314 ldapchain.on_bi.bi_type = "chain";
2315 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2316 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2317 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2318 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2319 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2321 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2323 ldapchain.on_response = ldap_chain_response;
2325 ldapchain.on_bi.bi_cf_ocs = chainocs;
2327 rc = config_register_schema( chaincfg, chainocs );
2332 return overlay_register( &ldapchain );