1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2013 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;
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 assert( op->o_ctrls != NULL );
200 assert( op->o_ctrls[ 0 ] != NULL );
205 op->o_ctrls = oldctrls;
212 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
215 ldap_chain_uri_cmp( const void *c1, const void *c2 )
217 const ldapinfo_t *li1 = (const ldapinfo_t *)c1;
218 const ldapinfo_t *li2 = (const ldapinfo_t *)c2;
220 assert( li1->li_bvuri != NULL );
221 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
222 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
224 assert( li2->li_bvuri != NULL );
225 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
226 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
228 return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
232 ldap_chain_uri_dup( void *c1, void *c2 )
234 ldapinfo_t *li1 = (ldapinfo_t *)c1;
235 ldapinfo_t *li2 = (ldapinfo_t *)c2;
237 assert( li1->li_bvuri != NULL );
238 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
239 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
241 assert( li2->li_bvuri != NULL );
242 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
243 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
245 if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
253 * Search specific response that strips entryDN from entries
256 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
258 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
260 assert( op->o_tag == LDAP_REQ_SEARCH );
262 /* if in error, don't proceed any further */
263 if ( lb->lb_status == LDAP_CH_ERR ) {
267 if ( rs->sr_type == REP_SEARCH ) {
268 Attribute **ap = &rs->sr_entry->e_attrs;
270 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
271 /* will be generated later by frontend
272 * (a cleaner solution would be that
273 * the frontend checks if it already exists */
274 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
281 /* there SHOULD be one only! */
286 /* tell the frontend not to add generated
287 * operational attributes */
288 rs->sr_flags |= REP_NO_OPERATIONALS;
290 return SLAP_CB_CONTINUE;
292 } else if ( rs->sr_type == REP_SEARCHREF ) {
293 /* if we get it here, it means the library was unable
294 * to chase the referral... */
295 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
296 rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
299 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
300 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
301 switch ( get_continuationBehavior( op ) ) {
302 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
303 lb->lb_status = LDAP_CH_ERR;
304 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
310 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
311 return SLAP_CB_CONTINUE;
313 } else if ( rs->sr_type == REP_RESULT ) {
314 if ( rs->sr_err == LDAP_REFERRAL
315 && lb->lb_depth < lb->lb_lc->lc_max_depth
316 && rs->sr_ref != NULL )
318 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
321 /* back-ldap tried to send result */
322 lb->lb_status = LDAP_CH_RES;
329 * Dummy response that simply traces if back-ldap tried to send
330 * anything to the client
333 ldap_chain_cb_response( Operation *op, SlapReply *rs )
335 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
337 /* if in error, don't proceed any further */
338 if ( lb->lb_status == LDAP_CH_ERR ) {
342 if ( rs->sr_type == REP_RESULT ) {
344 switch ( rs->sr_err ) {
345 case LDAP_COMPARE_TRUE:
346 case LDAP_COMPARE_FALSE:
347 if ( op->o_tag != LDAP_REQ_COMPARE ) {
353 lb->lb_status = LDAP_CH_RES;
357 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
358 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
362 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
363 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
364 switch ( get_continuationBehavior( op ) ) {
365 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
366 lb->lb_status = LDAP_CH_ERR;
367 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
373 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
380 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
382 /* strip the entryDN attribute, but keep returning results */
383 (void)ldap_chain_cb_search_response( op, rs );
386 return SLAP_CB_CONTINUE;
397 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
398 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
399 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
400 struct berval odn = op->o_req_dn,
401 ondn = op->o_req_ndn;
402 ldapinfo_t li = { 0 }, *lip = NULL;
403 struct berval bvuri[ 2 ] = { { 0 } };
405 /* NOTE: returned if ref is empty... */
409 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
410 LDAPControl **ctrls = NULL;
412 (void)chaining_control_add( lc, op, &ctrls );
413 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
417 for ( ; !BER_BVISNULL( ref ); ref++ ) {
418 SlapReply rs2 = { 0 };
419 LDAPURLDesc *srv = NULL;
420 req_search_s save_oq_search = op->oq_search,
421 tmp_oq_search = { 0 };
422 struct berval dn = BER_BVNULL,
429 /* We're setting the URI of the first referral;
430 * what if there are more?
436 If the client wishes to progress the operation, it MUST follow the
437 referral by contacting one of the supported services. If multiple
438 URIs are present, the client assumes that any supported URI may be
439 used to progress the operation.
441 * so we actually need to follow exactly one,
442 * and we can assume any is fine.
445 /* parse reference and use
446 * proto://[host][:port]/ only */
447 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
448 if ( rc != LDAP_URL_SUCCESS ) {
449 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
450 op->o_log_prefix, ref->bv_val, 0 );
457 if ( op->o_tag == LDAP_REQ_SEARCH ) {
458 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
459 /* RFC 4511: if scope is present, use it */
460 tmp_oq_search.rs_scope = srv->lud_scope;
463 /* RFC 4511: if scope is absent, use original */
464 tmp_oq_search.rs_scope = op->ors_scope;
469 srv->lud_scope = LDAP_SCOPE_DEFAULT;
470 dn.bv_val = srv->lud_dn;
471 filter = srv->lud_filter;
474 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
475 if ( srv->lud_dn == NULL ) {
480 ber_str2bv( srv->lud_dn, 0, 0, &dn );
481 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
482 if ( rc == LDAP_SUCCESS ) {
483 /* remove DN essentially because later on
484 * ldap_initialize() will parse the URL
485 * as a comma-separated URL list */
492 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
494 if ( srv->lud_filter != NULL
495 && srv->lud_filter[0] != '\0'
496 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
498 /* RFC 4511: if filter is present, use it;
499 * otherwise, use original */
500 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
501 if ( tmp_oq_search.rs_filter != NULL ) {
502 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
505 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
506 op->o_log_prefix, ref->bv_val, srv->lud_filter );
511 srv->lud_filter = NULL;
513 if ( rc == LDAP_SUCCESS ) {
514 li.li_uri = ldap_url_desc2str( srv );
517 srv->lud_dn = dn.bv_val;
518 srv->lud_filter = filter;
519 ldap_free_urldesc( srv );
521 if ( rc != LDAP_SUCCESS ) {
527 if ( li.li_uri == NULL ) {
528 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
529 op->o_log_prefix, ref->bv_val, 0 );
533 goto further_cleanup;
536 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
537 op->o_log_prefix, ref->bv_val, li.li_uri );
542 if ( op->o_tag == LDAP_REQ_SEARCH ) {
543 op->ors_scope = tmp_oq_search.rs_scope;
544 if ( tmp_oq_search.rs_filter != NULL ) {
545 op->ors_filter = tmp_oq_search.rs_filter;
546 op->ors_filterstr = tmp_oq_search.rs_filterstr;
550 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
552 /* Searches for a ldapinfo in the avl tree */
553 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
554 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
555 (caddr_t)&li, ldap_chain_uri_cmp );
556 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
559 op->o_bd->be_private = (void *)lip;
561 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
562 op->o_log_prefix, ref->bv_val, li.li_uri );
565 rc = ldap_chain_db_init_one( op->o_bd );
567 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
568 op->o_log_prefix, ref->bv_val, li.li_uri );
571 lip = (ldapinfo_t *)op->o_bd->be_private;
572 lip->li_uri = li.li_uri;
573 lip->li_bvuri = bvuri;
574 rc = ldap_chain_db_open_one( op->o_bd );
576 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
577 op->o_log_prefix, ref->bv_val, li.li_uri );
579 lip->li_bvuri = NULL;
580 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
584 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
585 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
586 if ( avl_insert( &lc->lc_lai.lai_tree,
587 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
589 /* someone just inserted another;
590 * don't bother, use this and then
594 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
600 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
601 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
605 lb->lb_depth = depth + 1;
607 rc = op_f( op, &rs2 );
609 /* note the first error */
610 if ( first_rc == -1 ) {
615 ldap_memfree( li.li_uri );
620 lip->li_bvuri = NULL;
621 (void)ldap_chain_db_close_one( op->o_bd );
622 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
627 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
628 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
631 if ( op->o_tag == LDAP_REQ_SEARCH ) {
632 if ( tmp_oq_search.rs_filter != NULL ) {
633 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
636 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
637 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
640 op->oq_search = save_oq_search;
643 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
652 op->o_req_ndn = ondn;
654 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
655 (void)chaining_control_remove( op, &ctrls );
656 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
658 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
673 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
674 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
675 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
676 ldapinfo_t li = { 0 }, *lip = NULL;
677 struct berval bvuri[ 2 ] = { { 0 } };
679 struct berval odn = op->o_req_dn,
680 ondn = op->o_req_ndn;
681 Entry *save_entry = rs->sr_entry;
682 slap_mask_t save_flags = rs->sr_flags;
687 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
688 LDAPControl **ctrls = NULL;
690 (void)chaining_control_add( lc, op, &ctrls );
691 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
693 assert( rs->sr_type == REP_SEARCHREF );
695 rs->sr_type = REP_SEARCH;
697 /* if we parse the URI then by no means
698 * we can cache stuff or reuse connections,
699 * because in back-ldap there's no caching
700 * based on the URI value, which is supposed
701 * to be set once for all (correct?) */
703 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
704 SlapReply rs2 = { REP_RESULT };
706 req_search_s save_oq_search = op->oq_search,
707 tmp_oq_search = { 0 };
715 /* parse reference and use
716 * proto://[host][:port]/ only */
717 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
718 if ( rc != LDAP_URL_SUCCESS ) {
719 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
720 op->o_log_prefix, ref->bv_val, 0 );
723 rs->sr_err = LDAP_OTHER;
727 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
728 /* RFC 4511: if scope is present, use it */
729 tmp_oq_search.rs_scope = srv->lud_scope;
732 /* RFC 4511: if scope is absent, use original */
733 /* Section 4.5.3: if scope is onelevel, use base */
734 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
735 tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
737 tmp_oq_search.rs_scope = op->ors_scope;
741 srv->lud_scope = LDAP_SCOPE_DEFAULT;
742 dn.bv_val = srv->lud_dn;
743 filter = srv->lud_filter;
746 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
747 if ( srv->lud_dn == NULL ) {
751 if ( save_entry != NULL ) {
752 /* use the "right" DN, if available */
753 pdn = save_entry->e_name;
754 ndn = save_entry->e_nname;
755 } /* else leave the original req DN in place, if any RFC 4511 */
758 /* RFC 4511: if DN is present, use it */
759 ber_str2bv( srv->lud_dn, 0, 0, &dn );
760 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
761 if ( rc == LDAP_SUCCESS ) {
762 /* remove DN essentially because later on
763 * ldap_initialize() will parse the URL
764 * as a comma-separated URL list */
771 if ( rc == LDAP_SUCCESS ) {
773 if ( srv->lud_filter != NULL
774 && srv->lud_filter[0] != '\0'
775 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
777 /* RFC 4511: if filter is present, use it;
778 * otherwise, use original */
779 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
780 if ( tmp_oq_search.rs_filter != NULL ) {
781 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
784 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
785 op->o_log_prefix, ref->bv_val, srv->lud_filter );
790 srv->lud_filter = NULL;
792 if ( rc == LDAP_SUCCESS ) {
793 li.li_uri = ldap_url_desc2str( srv );
796 srv->lud_dn = dn.bv_val;
797 srv->lud_filter = filter;
798 ldap_free_urldesc( srv );
800 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
801 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
802 op->o_log_prefix, ref->bv_val, 0 );
806 goto further_cleanup;
809 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
810 op->o_log_prefix, ref->bv_val, li.li_uri );
814 op->ors_scope = tmp_oq_search.rs_scope;
815 if ( tmp_oq_search.rs_filter != NULL ) {
816 op->ors_filter = tmp_oq_search.rs_filter;
817 op->ors_filterstr = tmp_oq_search.rs_filterstr;
820 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
822 /* Searches for a ldapinfo in the avl tree */
823 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
824 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
825 (caddr_t)&li, ldap_chain_uri_cmp );
826 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
829 op->o_bd->be_private = (void *)lip;
831 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
832 op->o_log_prefix, ref->bv_val, li.li_uri );
835 /* if none is found, create a temporary... */
836 rc = ldap_chain_db_init_one( op->o_bd );
838 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
839 op->o_log_prefix, ref->bv_val, li.li_uri );
842 lip = (ldapinfo_t *)op->o_bd->be_private;
843 lip->li_uri = li.li_uri;
844 lip->li_bvuri = bvuri;
845 rc = ldap_chain_db_open_one( op->o_bd );
847 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
848 op->o_log_prefix, ref->bv_val, li.li_uri );
850 lip->li_bvuri = NULL;
851 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
855 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
856 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
857 if ( avl_insert( &lc->lc_lai.lai_tree,
858 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
860 /* someone just inserted another;
861 * don't bother, use this and then
865 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
871 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
872 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
875 lb->lb_op_f = lback->bi_op_search;
876 lb->lb_depth = depth + 1;
878 /* FIXME: should we also copy filter and scope?
879 * according to RFC3296, no */
880 rc = lback->bi_op_search( op, &rs2 );
881 if ( first_rc == -1 ) {
886 ldap_memfree( li.li_uri );
891 lip->li_bvuri = NULL;
892 (void)ldap_chain_db_close_one( op->o_bd );
893 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
898 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
899 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
903 op->o_req_ndn = ondn;
905 if ( tmp_oq_search.rs_filter != NULL ) {
906 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
909 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
910 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
913 op->oq_search = save_oq_search;
915 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
923 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
924 (void)chaining_control_remove( op, &ctrls );
925 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
928 op->o_req_ndn = ondn;
929 rs->sr_type = REP_SEARCHREF;
930 rs->sr_entry = save_entry;
931 rs->sr_flags = save_flags;
933 if ( rc != LDAP_SUCCESS ) {
934 /* couldn't chase any of the referrals */
935 if ( first_rc != -1 ) {
939 rc = SLAP_CB_CONTINUE;
947 ldap_chain_response( Operation *op, SlapReply *rs )
949 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
950 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
951 BackendDB db, *bd = op->o_bd;
952 ldap_chain_cb_t lb = { 0 };
953 slap_callback *sc = op->o_callback,
956 const char *text = NULL;
959 struct berval ndn = op->o_ndn;
961 int sr_err = rs->sr_err;
962 slap_reply_t sr_type = rs->sr_type;
963 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
964 slap_mask_t chain_mask = 0;
965 ber_len_t chain_shift = 0;
966 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
968 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
969 return SLAP_CB_CONTINUE;
972 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
973 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
974 switch ( get_resolveBehavior( op ) ) {
975 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
976 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
977 return SLAP_CB_CONTINUE;
980 chain_mask = SLAP_CH_RESOLVE_MASK;
981 chain_shift = SLAP_CH_RESOLVE_SHIFT;
985 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
986 switch ( get_continuationBehavior( op ) ) {
987 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
988 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
989 return SLAP_CB_CONTINUE;
992 chain_mask = SLAP_CH_CONTINUATION_MASK;
993 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
997 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1000 * TODO: add checks on who/when chain operations; e.g.:
1001 * a) what identities are authorized
1002 * b) what request DN (e.g. only chain requests rooted at <DN>)
1003 * c) what referral URIs
1004 * d) what protocol scheme (e.g. only ldaps://)
1009 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1014 matched = rs->sr_matched;
1015 rs->sr_matched = NULL;
1019 /* we need this to know if back-ldap returned any result */
1021 sc2.sc_next = sc->sc_next;
1022 sc2.sc_private = &lb;
1023 sc2.sc_response = ldap_chain_cb_response;
1024 op->o_callback = &sc2;
1026 /* Chaining can be performed by a privileged user on behalf
1027 * of normal users, using the ProxyAuthz control, by exploiting
1028 * the identity assertion feature of back-ldap; see idassert-*
1029 * directives in slapd-ldap(5).
1031 * FIXME: the idassert-authcDN is one, will it be fine regardless
1032 * of the URI we obtain from the referral?
1035 switch ( op->o_tag ) {
1036 case LDAP_REQ_BIND: {
1037 struct berval rndn = op->o_req_ndn;
1038 Connection *conn = op->o_conn;
1040 /* FIXME: can we really get a referral for binds? */
1041 op->o_req_ndn = slap_empty_bv;
1043 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1044 op->o_req_ndn = rndn;
1050 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1053 case LDAP_REQ_DELETE:
1054 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1057 case LDAP_REQ_MODRDN:
1058 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1061 case LDAP_REQ_MODIFY:
1062 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1065 case LDAP_REQ_COMPARE:
1066 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
1067 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1072 case LDAP_REQ_SEARCH:
1073 if ( rs->sr_type == REP_SEARCHREF ) {
1074 sc2.sc_response = ldap_chain_cb_search_response;
1075 rc = ldap_chain_search( op, rs, ref, 0 );
1078 /* we might get here before any database actually
1079 * performed a search; in those cases, we need
1080 * to check limits, to make sure safe defaults
1082 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1083 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1086 rc = SLAP_CB_CONTINUE;
1091 case LDAP_REQ_EXTENDED:
1092 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
1093 /* FIXME: ldap_back_extended() by design
1094 * doesn't send result; frontend is expected
1096 /* FIXME: what about chaining? */
1097 if ( rc != SLAPD_ABANDON ) {
1099 send_ldap_extended( op, rs );
1102 lb.lb_status = LDAP_CH_RES;
1106 rc = SLAP_CB_CONTINUE;
1116 sr_err = rs->sr_err;
1117 /* slapd-ldap sent response */
1118 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1119 /* FIXME: should we send response? */
1120 Debug( LDAP_DEBUG_ANY,
1121 "%s: ldap_chain_response: "
1122 "overlay should have sent result.\n",
1123 op->o_log_prefix, 0, 0 );
1128 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1129 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1133 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1134 case LDAP_CHAINING_REQUIRED:
1136 op->o_callback = NULL;
1137 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1138 "operation cannot be completed without chaining" );
1142 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1143 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1144 sr_err = rs->sr_err = rc;
1145 rs->sr_type = sr_type;
1148 rc = SLAP_CB_CONTINUE;
1149 rs->sr_err = sr_err;
1150 rs->sr_type = sr_type;
1152 rs->sr_matched = matched;
1155 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1158 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1161 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1162 /* give the remaining callbacks a chance */
1163 op->o_callback = sc->sc_next;
1164 rc = rs->sr_err = slap_map_api2result( rs );
1165 send_ldap_result( op, rs );
1169 rs->sr_err = sr_err;
1170 rs->sr_type = sr_type;
1172 rs->sr_matched = matched;
1175 op->o_callback = sc;
1181 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1183 ldap_chain_parse_ctrl(
1186 LDAPControl *ctrl );
1189 str2chain( const char *s )
1191 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1192 return LDAP_CHAINING_PREFERRED;
1194 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1195 return LDAP_CHAINING_REQUIRED;
1197 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1198 return LDAP_REFERRALS_PREFERRED;
1200 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1201 return LDAP_REFERRALS_REQUIRED;
1206 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1221 static ConfigDriver chain_cf_gen;
1222 static ConfigCfAdd chain_cfadd;
1223 static ConfigLDAPadd chain_ldadd;
1224 #ifdef SLAP_CONFIG_DELETE
1225 static ConfigLDAPdel chain_lddel;
1228 static ConfigTable chaincfg[] = {
1229 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1230 { "chain-chaining", "args",
1231 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1232 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1233 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1234 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1235 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1236 { "chain-cache-uri", "TRUE/FALSE",
1237 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1238 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1239 "DESC 'Enables caching of URIs not present in configuration' "
1240 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1241 { "chain-max-depth", "args",
1242 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1243 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1244 "DESC 'max referral depth' "
1245 "SYNTAX OMsInteger "
1246 "EQUALITY integerMatch "
1247 "SINGLE-VALUE )", NULL, NULL },
1248 { "chain-return-error", "TRUE/FALSE",
1249 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1250 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1251 "DESC 'Errors are returned instead of the original referral' "
1252 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1253 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1256 static ConfigOCs chainocs[] = {
1257 { "( OLcfgOvOc:3.1 "
1258 "NAME 'olcChainConfig' "
1259 "DESC 'Chain configuration' "
1260 "SUP olcOverlayConfig "
1262 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1263 "olcChainingBehavior $ "
1264 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1265 "olcChainCacheURI $ "
1266 "olcChainMaxReferralDepth $ "
1267 "olcChainReturnError "
1269 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1270 { "( OLcfgOvOc:3.2 "
1271 "NAME 'olcChainDatabase' "
1272 "DESC 'Chain remote server configuration' "
1274 Cft_Misc, olcDatabaseDummy, chain_ldadd
1275 #ifdef SLAP_CONFIG_DELETE
1283 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1290 AttributeDescription *ad = NULL;
1296 if ( p->ce_type != Cft_Overlay
1298 || p->ce_bi->bi_cf_ocs != chainocs )
1300 return LDAP_CONSTRAINT_VIOLATION;
1303 on = (slap_overinst *)p->ce_bi;
1304 lc = (ldap_chain_t *)on->on_bi.bi_private;
1306 assert( ca->be == NULL );
1307 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1309 ca->be->bd_info = (BackendInfo *)on;
1311 rc = slap_str2ad( "olcDbURI", &ad, &text );
1312 assert( rc == LDAP_SUCCESS );
1314 at = attr_find( e->e_attrs, ad );
1316 if ( lc->lc_common_li == NULL && at != NULL ) {
1317 /* FIXME: we should generate an empty default entry
1318 * if none is supplied */
1319 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1320 "first underlying database \"%s\" "
1321 "cannot contain attribute \"%s\".\n",
1322 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1323 rc = LDAP_CONSTRAINT_VIOLATION;
1328 if ( lc->lc_common_li != NULL && at == NULL ) {
1329 /* FIXME: we should generate an empty default entry
1330 * if none is supplied */
1331 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1332 "subsequent underlying database \"%s\" "
1333 "must contain attribute \"%s\".\n",
1334 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1335 rc = LDAP_CONSTRAINT_VIOLATION;
1339 if ( lc->lc_common_li == NULL ) {
1340 rc = ldap_chain_db_init_common( ca->be );
1343 li = ca->be->be_private;
1344 lc->lc_common_li = lc->lc_cfg_li = li;
1347 rc = ldap_chain_db_init_one( ca->be );
1351 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1352 "unable to init %sunderlying database \"%s\".\n",
1353 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1354 return LDAP_CONSTRAINT_VIOLATION;
1357 li = ca->be->be_private;
1360 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1361 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1362 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1363 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1365 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1366 "database \"%s\" insert failed.\n",
1367 e->e_name.bv_val, 0, 0 );
1368 rc = LDAP_CONSTRAINT_VIOLATION;
1373 ca->ca_private = on;
1376 if ( rc != LDAP_SUCCESS ) {
1377 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1385 typedef struct ldap_chain_cfadd_apply_t {
1391 } ldap_chain_cfadd_apply_t;
1394 ldap_chain_cfadd_apply( void *datum, void *arg )
1396 ldapinfo_t *li = (ldapinfo_t *)datum;
1397 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1401 /* FIXME: should not hardcode "olcDatabase" here */
1402 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1403 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1404 bv.bv_val = lca->ca->cr_msg;
1406 lca->ca->be->be_private = (void *)li;
1407 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1408 &bv, lback->bi_cf_ocs, &chainocs[1] );
1416 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1418 CfEntryInfo *pe = p->e_private;
1419 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1420 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1421 void *priv = (void *)ca->be->be_private;
1423 if ( lback->bi_cf_ocs ) {
1424 ldap_chain_cfadd_apply_t lca = { 0 };
1432 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1434 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1435 &lca, 1, AVL_INORDER );
1437 ca->be->be_private = priv;
1443 #ifdef SLAP_CONFIG_DELETE
1445 chain_lddel( CfEntryInfo *ce, Operation *op )
1447 CfEntryInfo *pe = ce->ce_parent;
1448 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1449 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1450 ldapinfo_t *li = (ldapinfo_t *) ce->ce_be->be_private;
1452 if ( li != lc->lc_common_li ) {
1453 if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
1454 Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. "
1455 "\"%s\" not found.\n", li->li_uri, 0, 0 );
1458 } else if ( lc->lc_lai.lai_tree ) {
1459 Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
1460 "LDAP database when other databases are still present.\n", 0, 0, 0 );
1463 lc->lc_common_li = NULL;
1466 ce->ce_be->bd_info = lback;
1468 if ( ce->ce_be->bd_info->bi_db_close ) {
1469 ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
1471 if ( ce->ce_be->bd_info->bi_db_destroy ) {
1472 ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
1478 return LDAP_SUCCESS;
1480 #endif /* SLAP_CONFIG_DELETE */
1482 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1483 static slap_verbmasks chaining_mode[] = {
1484 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1485 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1486 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1487 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1490 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1493 chain_cf_gen( ConfigArgs *c )
1495 slap_overinst *on = (slap_overinst *)c->bi;
1496 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1500 if ( c->op == SLAP_CONFIG_EMIT ) {
1502 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1504 struct berval resolve = BER_BVNULL,
1505 continuation = BER_BVNULL;
1507 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1511 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1512 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1514 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1516 + STRLENOF( "continuation=" ) + continuation.bv_len;
1517 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1518 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1519 "resolve=%s continuation=%s",
1520 resolve.bv_val, continuation.bv_val );
1522 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1523 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1524 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1525 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1526 " critical", STRLENOF( " critical" ) + 1 );
1527 c->value_bv.bv_len += STRLENOF( " critical" );
1532 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1535 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1539 c->value_int = lc->lc_max_depth;
1543 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1552 } else if ( c->op == LDAP_MOD_DELETE ) {
1558 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1566 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1577 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1578 char **argv = c->argv;
1580 BerElementBuffer berbuf;
1581 BerElement *ber = (BerElement *)&berbuf;
1585 Operation op = { 0 };
1586 SlapReply rs = { 0 };
1588 lc->lc_chaining_ctrlflag = 0;
1590 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1591 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1592 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1593 if ( resolve == -1 ) {
1594 Debug( LDAP_DEBUG_ANY, "%s: "
1595 "illegal <resolve> value %s "
1596 "in \"chain-chaining>\".\n",
1597 c->log, argv[ 0 ], 0 );
1601 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1602 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1603 if ( continuation == -1 ) {
1604 Debug( LDAP_DEBUG_ANY, "%s: "
1605 "illegal <continuation> value %s "
1606 "in \"chain-chaining\".\n",
1607 c->log, argv[ 0 ], 0 );
1611 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1615 Debug( LDAP_DEBUG_ANY, "%s: "
1616 "unknown option in \"chain-chaining\".\n",
1622 if ( resolve != -1 || continuation != -1 ) {
1625 if ( resolve == -1 ) {
1627 resolve = SLAP_CHAINING_DEFAULT;
1630 ber_init2( ber, NULL, LBER_USE_DER );
1632 err = ber_printf( ber, "{e" /* } */, resolve );
1635 Debug( LDAP_DEBUG_ANY, "%s: "
1636 "chaining behavior control encoding error!\n",
1641 if ( continuation > -1 ) {
1642 err = ber_printf( ber, "e", continuation );
1645 Debug( LDAP_DEBUG_ANY, "%s: "
1646 "chaining behavior control encoding error!\n",
1652 err = ber_printf( ber, /* { */ "N}" );
1655 Debug( LDAP_DEBUG_ANY, "%s: "
1656 "chaining behavior control encoding error!\n",
1661 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1662 exit( EXIT_FAILURE );
1666 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1669 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1670 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1672 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1674 Debug( LDAP_DEBUG_ANY, "%s: "
1675 "unable to parse chaining control%s%s.\n",
1676 c->log, rs.sr_text ? ": " : "",
1677 rs.sr_text ? rs.sr_text : "" );
1681 lc->lc_chaining_ctrlflag = op.o_chaining;
1683 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1686 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1687 Debug( LDAP_DEBUG_ANY, "%s: "
1688 "\"chaining\" control unsupported (ignored).\n",
1690 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1694 if ( c->value_int ) {
1695 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1697 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1702 if ( c->value_int < 0 ) {
1703 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1704 "<%s> invalid max referral depth %d",
1705 c->argv[0], c->value_int );
1706 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1707 c->log, c->cr_msg, 0 );
1711 lc->lc_max_depth = c->value_int;
1714 if ( c->value_int ) {
1715 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1717 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1733 slap_overinst *on = (slap_overinst *)be->bd_info;
1734 ldap_chain_t *lc = NULL;
1736 if ( lback == NULL ) {
1737 lback = backend_info( "ldap" );
1739 if ( lback == NULL ) {
1744 lc = ch_malloc( sizeof( ldap_chain_t ) );
1748 memset( lc, 0, sizeof( ldap_chain_t ) );
1749 lc->lc_max_depth = 1;
1750 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1752 on->on_bi.bi_private = (void *)lc;
1758 ldap_chain_db_config(
1765 slap_overinst *on = (slap_overinst *)be->bd_info;
1766 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1768 int rc = SLAP_CONF_UNKNOWN;
1770 if ( lc->lc_common_li == NULL ) {
1772 ldap_chain_db_init_common( &db );
1773 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1776 /* Something for the chain database? */
1777 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1778 char *save_argv0 = argv[ 0 ];
1780 static char *allowed_argv[] = {
1781 /* special: put URI here, so in the meanwhile
1782 * it detects whether a new URI is being provided */
1788 /* FIXME: maybe rebind-as-user should be allowed
1789 * only within known URIs... */
1796 int which_argv = -1;
1798 argv[ 0 ] += STRLENOF( "chain-" );
1800 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1801 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1806 if ( allowed_argv[ which_argv ] == NULL ) {
1809 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1810 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1811 "\"%s\" only allowed within a URI directive.\n.",
1812 fname, lineno, argv[ 0 ] );
1817 if ( which_argv == 0 ) {
1818 rc = ldap_chain_db_init_one( &db );
1820 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1821 "underlying slapd-ldap initialization failed.\n.",
1825 lc->lc_cfg_li = db.be_private;
1828 /* TODO: add checks on what other slapd-ldap(5) args
1829 * should be put in the template; this is not quite
1830 * harmful, because attributes that shouldn't don't
1831 * get actually used, but the user should at least
1836 db.be_private = (void *)lc->lc_cfg_li;
1837 db.be_cf_ocs = lback->bi_cf_ocs;
1839 rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1841 argv[ 0 ] = save_argv0;
1843 if ( which_argv == 0 ) {
1847 db.be_private = (void *)lc->lc_cfg_li;
1848 ldap_chain_db_destroy_one( &db, NULL );
1849 lc->lc_cfg_li = NULL;
1851 if ( lc->lc_cfg_li->li_bvuri == NULL
1852 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1853 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1855 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1856 "no URI list allowed in slapo-chain.\n",
1859 goto private_destroy;
1862 if ( avl_insert( &lc->lc_lai.lai_tree,
1863 (caddr_t)lc->lc_cfg_li,
1864 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1866 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1867 "duplicate URI in slapo-chain.\n",
1870 goto private_destroy;
1887 typedef struct ldap_chain_db_apply_t {
1890 } ldap_chain_db_apply_t;
1893 ldap_chain_db_apply( void *datum, void *arg )
1895 ldapinfo_t *li = (ldapinfo_t *)datum;
1896 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1898 lca->be->be_private = (void *)li;
1900 return lca->func( lca->be, NULL );
1909 slap_overinst *on = (slap_overinst *)be->bd_info;
1910 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1915 BI_db_func *func = (&lback->bi_db_open)[ which ];
1917 if ( func != NULL && lc->lc_common_li != NULL ) {
1921 db.be_private = lc->lc_common_li;
1923 rc = func( &db, NULL );
1929 if ( lc->lc_lai.lai_tree != NULL ) {
1930 ldap_chain_db_apply_t lca;
1935 rc = avl_apply( lc->lc_lai.lai_tree,
1936 ldap_chain_db_apply, (void *)&lca,
1937 1, AVL_INORDER ) != AVL_NOMORE;
1950 slap_overinst *on = (slap_overinst *) be->bd_info;
1951 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1952 slap_mask_t monitoring;
1955 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1956 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1960 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1962 if ( lc->lc_common_li == NULL ) {
1963 void *be_private = be->be_private;
1964 ldap_chain_db_init_common( be );
1965 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1966 be->be_private = be_private;
1969 /* filter out and restore monitoring */
1970 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1971 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1972 rc = ldap_chain_db_func( be, db_open );
1973 SLAP_DBFLAGS( be ) |= monitoring;
1979 ldap_chain_db_close(
1983 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1984 #ifdef SLAP_CONFIG_DELETE
1985 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1986 #endif /* SLAP_CONFIG_DELETE */
1987 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1988 return ldap_chain_db_func( be, db_close );
1992 ldap_chain_db_destroy(
1996 slap_overinst *on = (slap_overinst *) be->bd_info;
1997 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2001 rc = ldap_chain_db_func( be, db_destroy );
2004 avl_free( lc->lc_lai.lai_tree, NULL );
2005 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
2013 * inits one instance of the slapd-ldap backend, and stores
2014 * the private info in be_private of the arg
2017 ldap_chain_db_init_common(
2020 BackendInfo *bi = be->bd_info;
2024 be->bd_info = lback;
2025 be->be_private = NULL;
2026 rc = lback->bi_db_init( be, NULL );
2030 li = (ldapinfo_t *)be->be_private;
2031 li->li_urllist_f = NULL;
2032 li->li_urllist_p = NULL;
2040 * inits one instance of the slapd-ldap backend, stores
2041 * the private info in be_private of the arg and fills
2042 * selected fields with data from the template.
2044 * NOTE: add checks about the other fields of the template,
2045 * which are ignored and SHOULD NOT be configured by the user.
2048 ldap_chain_db_init_one(
2051 slap_overinst *on = (slap_overinst *)be->bd_info;
2052 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2054 BackendInfo *bi = be->bd_info;
2059 be->bd_info = lback;
2060 be->be_private = NULL;
2061 t = lback->bi_db_init( be, NULL );
2065 li = (ldapinfo_t *)be->be_private;
2066 li->li_urllist_f = NULL;
2067 li->li_urllist_p = NULL;
2069 /* copy common data */
2070 li->li_nretries = lc->lc_common_li->li_nretries;
2071 li->li_flags = lc->lc_common_li->li_flags;
2072 li->li_version = lc->lc_common_li->li_version;
2073 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2074 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2082 ldap_chain_db_open_one(
2085 if ( SLAP_DBMONITORING( be ) ) {
2086 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
2088 if ( li->li_uri == NULL ) {
2089 ber_str2bv( "cn=Common Connections", 0, 1,
2090 &li->li_monitor_info.lmi_conn_rdn );
2091 ber_str2bv( "cn=Operations on Common Connections", 0, 1,
2092 &li->li_monitor_info.lmi_conn_rdn );
2097 li->li_monitor_info.lmi_conn_rdn.bv_len
2098 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2099 ptr = li->li_monitor_info.lmi_conn_rdn.bv_val
2100 = ch_malloc( li->li_monitor_info.lmi_conn_rdn.bv_len + 1 );
2101 ptr = lutil_strcopy( ptr, "cn=" );
2102 ptr = lutil_strcopy( ptr, li->li_uri );
2105 li->li_monitor_info.lmi_ops_rdn.bv_len
2106 = STRLENOF( "cn=Operations on " ) + strlen( li->li_uri );
2107 ptr = li->li_monitor_info.lmi_ops_rdn.bv_val
2108 = ch_malloc( li->li_monitor_info.lmi_ops_rdn.bv_len + 1 );
2109 ptr = lutil_strcopy( ptr, "cn=Operations on " );
2110 ptr = lutil_strcopy( ptr, li->li_uri );
2115 return lback->bi_db_open( be, NULL );
2118 typedef struct ldap_chain_conn_apply_t {
2121 } ldap_chain_conn_apply_t;
2124 ldap_chain_conn_apply( void *datum, void *arg )
2126 ldapinfo_t *li = (ldapinfo_t *)datum;
2127 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2129 lca->be->be_private = (void *)li;
2131 return lback->bi_connection_destroy( lca->be, lca->conn );
2135 ldap_chain_connection_destroy(
2140 slap_overinst *on = (slap_overinst *) be->bd_info;
2141 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2142 void *private = be->be_private;
2143 ldap_chain_conn_apply_t lca;
2146 be->be_private = NULL;
2149 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2150 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2151 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2152 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2153 be->be_private = private;
2158 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2160 ldap_chain_parse_ctrl(
2170 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2171 rs->sr_text = "Chaining behavior control specified multiple times";
2172 return LDAP_PROTOCOL_ERROR;
2175 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2176 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2177 return LDAP_PROTOCOL_ERROR;
2180 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2181 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2186 /* Parse the control value
2187 * ChainingBehavior ::= SEQUENCE {
2188 * resolveBehavior Behavior OPTIONAL,
2189 * continuationBehavior Behavior OPTIONAL }
2191 * Behavior :: = ENUMERATED {
2192 * chainingPreferred (0),
2193 * chainingRequired (1),
2194 * referralsPreferred (2),
2195 * referralsRequired (3) }
2198 ber = ber_init( &ctrl->ldctl_value );
2200 rs->sr_text = "internal error";
2204 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2205 /* FIXME: since the whole SEQUENCE is optional,
2206 * should we accept no enumerations at all? */
2207 if ( tag != LBER_ENUMERATED ) {
2208 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2209 return LDAP_PROTOCOL_ERROR;
2212 switch ( behavior ) {
2213 case LDAP_CHAINING_PREFERRED:
2214 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2217 case LDAP_CHAINING_REQUIRED:
2218 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2221 case LDAP_REFERRALS_PREFERRED:
2222 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2225 case LDAP_REFERRALS_REQUIRED:
2226 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2230 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2231 return LDAP_PROTOCOL_ERROR;
2234 tag = ber_peek_tag( ber, &len );
2235 if ( tag == LBER_ENUMERATED ) {
2236 tag = ber_scanf( ber, "e", &behavior );
2237 if ( tag == LBER_ERROR ) {
2238 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2239 return LDAP_PROTOCOL_ERROR;
2243 if ( tag == LBER_DEFAULT ) {
2244 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2247 switch ( behavior ) {
2248 case LDAP_CHAINING_PREFERRED:
2249 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2252 case LDAP_CHAINING_REQUIRED:
2253 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2256 case LDAP_REFERRALS_PREFERRED:
2257 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2260 case LDAP_REFERRALS_REQUIRED:
2261 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2265 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2266 return LDAP_PROTOCOL_ERROR;
2270 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2271 rs->sr_text = "Chaining behavior control: decoding error";
2272 return LDAP_PROTOCOL_ERROR;
2275 (void) ber_free( ber, 1 );
2278 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2279 ? SLAP_CONTROL_CRITICAL
2280 : SLAP_CONTROL_NONCRITICAL );
2282 return LDAP_SUCCESS;
2284 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2287 chain_initialize( void )
2291 /* Make sure we don't exceed the bits reserved for userland */
2292 config_check_userland( CH_LAST );
2294 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2295 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2296 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2297 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2298 if ( rc != LDAP_SUCCESS ) {
2299 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2300 "unable to register chaining behavior control: %d.\n",
2304 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2306 ldapchain.on_bi.bi_type = "chain";
2307 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2308 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2309 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2310 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2311 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2313 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2315 ldapchain.on_response = ldap_chain_response;
2317 ldapchain.on_bi.bi_cf_ocs = chainocs;
2319 rc = config_register_schema( chaincfg, chainocs );
2324 return overlay_register( &ldapchain );