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 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_f, rs->sr_ref, lb->lb_depth );
322 /* back-ldap tried to send result */
323 lb->lb_status = LDAP_CH_RES;
330 * Dummy response that simply traces if back-ldap tried to send
331 * anything to the client
334 ldap_chain_cb_response( Operation *op, SlapReply *rs )
336 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
338 /* if in error, don't proceed any further */
339 if ( lb->lb_status == LDAP_CH_ERR ) {
343 if ( rs->sr_type == REP_RESULT ) {
345 switch ( rs->sr_err ) {
346 case LDAP_COMPARE_TRUE:
347 case LDAP_COMPARE_FALSE:
348 if ( op->o_tag != LDAP_REQ_COMPARE ) {
354 lb->lb_status = LDAP_CH_RES;
358 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
359 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
363 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
364 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
365 switch ( get_continuationBehavior( op ) ) {
366 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
367 lb->lb_status = LDAP_CH_ERR;
368 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
374 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
381 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
383 /* strip the entryDN attribute, but keep returning results */
384 (void)ldap_chain_cb_search_response( op, rs );
387 return SLAP_CB_CONTINUE;
398 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
399 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
400 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
401 struct berval odn = op->o_req_dn,
402 ondn = op->o_req_ndn;
403 ldapinfo_t li = { 0 }, *lip = NULL;
404 struct berval bvuri[ 2 ] = { { 0 } };
406 /* NOTE: returned if ref is empty... */
410 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
411 LDAPControl **ctrls = NULL;
413 (void)chaining_control_add( lc, op, &ctrls );
414 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
418 for ( ; !BER_BVISNULL( ref ); ref++ ) {
419 SlapReply rs2 = { 0 };
420 LDAPURLDesc *srv = NULL;
421 req_search_s save_oq_search = op->oq_search,
422 tmp_oq_search = { 0 };
423 struct berval dn = BER_BVNULL,
430 /* We're setting the URI of the first referral;
431 * what if there are more?
437 If the client wishes to progress the operation, it MUST follow the
438 referral by contacting one of the supported services. If multiple
439 URIs are present, the client assumes that any supported URI may be
440 used to progress the operation.
442 * so we actually need to follow exactly one,
443 * and we can assume any is fine.
446 /* parse reference and use
447 * proto://[host][:port]/ only */
448 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
449 if ( rc != LDAP_URL_SUCCESS ) {
450 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
451 op->o_log_prefix, ref->bv_val, 0 );
458 if ( op->o_tag == LDAP_REQ_SEARCH ) {
459 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
460 /* RFC 4511: if scope is present, use it */
461 tmp_oq_search.rs_scope = srv->lud_scope;
464 /* RFC 4511: if scope is absent, use original */
465 tmp_oq_search.rs_scope = op->ors_scope;
470 srv->lud_scope = LDAP_SCOPE_DEFAULT;
471 dn.bv_val = srv->lud_dn;
472 filter = srv->lud_filter;
475 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
476 if ( srv->lud_dn == NULL ) {
481 ber_str2bv( srv->lud_dn, 0, 0, &dn );
482 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
483 if ( rc == LDAP_SUCCESS ) {
484 /* remove DN essentially because later on
485 * ldap_initialize() will parse the URL
486 * as a comma-separated URL list */
493 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
495 if ( srv->lud_filter != NULL
496 && srv->lud_filter[0] != '\0'
497 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
499 /* RFC 4511: if filter is present, use it;
500 * otherwise, use original */
501 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
502 if ( tmp_oq_search.rs_filter != NULL ) {
503 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
506 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
507 op->o_log_prefix, ref->bv_val, srv->lud_filter );
512 srv->lud_filter = NULL;
514 if ( rc == LDAP_SUCCESS ) {
515 li.li_uri = ldap_url_desc2str( srv );
518 srv->lud_dn = dn.bv_val;
519 srv->lud_filter = filter;
520 ldap_free_urldesc( srv );
522 if ( rc != LDAP_SUCCESS ) {
528 if ( li.li_uri == NULL ) {
529 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
530 op->o_log_prefix, ref->bv_val, 0 );
534 goto further_cleanup;
537 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
538 op->o_log_prefix, ref->bv_val, li.li_uri );
543 if ( op->o_tag == LDAP_REQ_SEARCH ) {
544 op->ors_scope = tmp_oq_search.rs_scope;
545 if ( tmp_oq_search.rs_filter != NULL ) {
546 op->ors_filter = tmp_oq_search.rs_filter;
547 op->ors_filterstr = tmp_oq_search.rs_filterstr;
551 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
553 /* Searches for a ldapinfo in the avl tree */
554 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
555 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
556 (caddr_t)&li, ldap_chain_uri_cmp );
557 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
560 op->o_bd->be_private = (void *)lip;
562 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
563 op->o_log_prefix, ref->bv_val, li.li_uri );
566 rc = ldap_chain_db_init_one( op->o_bd );
568 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
569 op->o_log_prefix, ref->bv_val, li.li_uri );
572 lip = (ldapinfo_t *)op->o_bd->be_private;
573 lip->li_uri = li.li_uri;
574 lip->li_bvuri = bvuri;
575 rc = ldap_chain_db_open_one( op->o_bd );
577 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
578 op->o_log_prefix, ref->bv_val, li.li_uri );
580 lip->li_bvuri = NULL;
581 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
585 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
586 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
587 if ( avl_insert( &lc->lc_lai.lai_tree,
588 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
590 /* someone just inserted another;
591 * don't bother, use this and then
595 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
601 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
602 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
606 lb->lb_depth = depth + 1;
608 rc = op_f( op, &rs2 );
610 /* note the first error */
611 if ( first_rc == -1 ) {
616 ldap_memfree( li.li_uri );
621 lip->li_bvuri = NULL;
622 (void)ldap_chain_db_close_one( op->o_bd );
623 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
628 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
629 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
632 if ( op->o_tag == LDAP_REQ_SEARCH ) {
633 if ( tmp_oq_search.rs_filter != NULL ) {
634 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
637 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
638 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
641 op->oq_search = save_oq_search;
644 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
653 op->o_req_ndn = ondn;
655 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
656 (void)chaining_control_remove( op, &ctrls );
657 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
659 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
674 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
675 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
676 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
677 ldapinfo_t li = { 0 }, *lip = NULL;
678 struct berval bvuri[ 2 ] = { { 0 } };
680 struct berval odn = op->o_req_dn,
681 ondn = op->o_req_ndn;
682 Entry *save_entry = rs->sr_entry;
683 slap_mask_t save_flags = rs->sr_flags;
688 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
689 LDAPControl **ctrls = NULL;
691 (void)chaining_control_add( lc, op, &ctrls );
692 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
694 assert( rs->sr_type == REP_SEARCHREF );
696 rs->sr_type = REP_SEARCH;
698 /* if we parse the URI then by no means
699 * we can cache stuff or reuse connections,
700 * because in back-ldap there's no caching
701 * based on the URI value, which is supposed
702 * to be set once for all (correct?) */
704 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
705 SlapReply rs2 = { REP_RESULT };
707 req_search_s save_oq_search = op->oq_search,
708 tmp_oq_search = { 0 };
716 /* parse reference and use
717 * proto://[host][:port]/ only */
718 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
719 if ( rc != LDAP_URL_SUCCESS ) {
720 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
721 op->o_log_prefix, ref->bv_val, 0 );
724 rs->sr_err = LDAP_OTHER;
728 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
729 /* RFC 4511: if scope is present, use it */
730 tmp_oq_search.rs_scope = srv->lud_scope;
733 /* RFC 4511: if scope is absent, use original */
734 /* Section 4.5.3: if scope is onelevel, use base */
735 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
736 tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
738 tmp_oq_search.rs_scope = op->ors_scope;
742 srv->lud_scope = LDAP_SCOPE_DEFAULT;
743 dn.bv_val = srv->lud_dn;
744 filter = srv->lud_filter;
747 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
748 if ( srv->lud_dn == NULL ) {
752 if ( save_entry != NULL ) {
753 /* use the "right" DN, if available */
754 pdn = save_entry->e_name;
755 ndn = save_entry->e_nname;
756 } /* else leave the original req DN in place, if any RFC 4511 */
759 /* RFC 4511: if DN is present, use it */
760 ber_str2bv( srv->lud_dn, 0, 0, &dn );
761 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
762 if ( rc == LDAP_SUCCESS ) {
763 /* remove DN essentially because later on
764 * ldap_initialize() will parse the URL
765 * as a comma-separated URL list */
772 if ( rc == LDAP_SUCCESS ) {
774 if ( srv->lud_filter != NULL
775 && srv->lud_filter[0] != '\0'
776 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
778 /* RFC 4511: if filter is present, use it;
779 * otherwise, use original */
780 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
781 if ( tmp_oq_search.rs_filter != NULL ) {
782 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
785 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
786 op->o_log_prefix, ref->bv_val, srv->lud_filter );
791 srv->lud_filter = NULL;
793 if ( rc == LDAP_SUCCESS ) {
794 li.li_uri = ldap_url_desc2str( srv );
797 srv->lud_dn = dn.bv_val;
798 srv->lud_filter = filter;
799 ldap_free_urldesc( srv );
801 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
802 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
803 op->o_log_prefix, ref->bv_val, 0 );
807 goto further_cleanup;
810 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
811 op->o_log_prefix, ref->bv_val, li.li_uri );
815 op->ors_scope = tmp_oq_search.rs_scope;
816 if ( tmp_oq_search.rs_filter != NULL ) {
817 op->ors_filter = tmp_oq_search.rs_filter;
818 op->ors_filterstr = tmp_oq_search.rs_filterstr;
821 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
823 /* Searches for a ldapinfo in the avl tree */
824 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
825 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
826 (caddr_t)&li, ldap_chain_uri_cmp );
827 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
830 op->o_bd->be_private = (void *)lip;
832 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
833 op->o_log_prefix, ref->bv_val, li.li_uri );
836 /* if none is found, create a temporary... */
837 rc = ldap_chain_db_init_one( op->o_bd );
839 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
840 op->o_log_prefix, ref->bv_val, li.li_uri );
843 lip = (ldapinfo_t *)op->o_bd->be_private;
844 lip->li_uri = li.li_uri;
845 lip->li_bvuri = bvuri;
846 rc = ldap_chain_db_open_one( op->o_bd );
848 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
849 op->o_log_prefix, ref->bv_val, li.li_uri );
851 lip->li_bvuri = NULL;
852 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
856 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
857 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
858 if ( avl_insert( &lc->lc_lai.lai_tree,
859 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
861 /* someone just inserted another;
862 * don't bother, use this and then
866 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
872 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
873 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
876 lb->lb_op_f = lback->bi_op_search;
877 lb->lb_depth = depth + 1;
879 /* FIXME: should we also copy filter and scope?
880 * according to RFC3296, no */
881 rc = lback->bi_op_search( op, &rs2 );
882 if ( first_rc == -1 ) {
887 ldap_memfree( li.li_uri );
892 lip->li_bvuri = NULL;
893 (void)ldap_chain_db_close_one( op->o_bd );
894 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
899 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
900 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
904 op->o_req_ndn = ondn;
906 if ( tmp_oq_search.rs_filter != NULL ) {
907 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
910 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
911 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
914 op->oq_search = save_oq_search;
916 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
924 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
925 (void)chaining_control_remove( op, &ctrls );
926 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
929 op->o_req_ndn = ondn;
930 rs->sr_type = REP_SEARCHREF;
931 rs->sr_entry = save_entry;
932 rs->sr_flags = save_flags;
934 if ( rc != LDAP_SUCCESS ) {
935 /* couldn't chase any of the referrals */
936 if ( first_rc != -1 ) {
940 rc = SLAP_CB_CONTINUE;
948 ldap_chain_response( Operation *op, SlapReply *rs )
950 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
951 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
952 BackendDB db, *bd = op->o_bd;
953 ldap_chain_cb_t lb = { 0 };
954 slap_callback *sc = op->o_callback,
957 const char *text = NULL;
960 struct berval ndn = op->o_ndn;
962 int sr_err = rs->sr_err;
963 slap_reply_t sr_type = rs->sr_type;
964 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
965 slap_mask_t chain_mask = 0;
966 ber_len_t chain_shift = 0;
967 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
969 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
970 return SLAP_CB_CONTINUE;
973 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
974 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
975 switch ( get_resolveBehavior( op ) ) {
976 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
977 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
978 return SLAP_CB_CONTINUE;
981 chain_mask = SLAP_CH_RESOLVE_MASK;
982 chain_shift = SLAP_CH_RESOLVE_SHIFT;
986 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
987 switch ( get_continuationBehavior( op ) ) {
988 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
989 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
990 return SLAP_CB_CONTINUE;
993 chain_mask = SLAP_CH_CONTINUATION_MASK;
994 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
998 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1001 * TODO: add checks on who/when chain operations; e.g.:
1002 * a) what identities are authorized
1003 * b) what request DN (e.g. only chain requests rooted at <DN>)
1004 * c) what referral URIs
1005 * d) what protocol scheme (e.g. only ldaps://)
1010 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1015 matched = rs->sr_matched;
1016 rs->sr_matched = NULL;
1020 /* we need this to know if back-ldap returned any result */
1022 sc2.sc_next = sc->sc_next;
1023 sc2.sc_private = &lb;
1024 sc2.sc_response = ldap_chain_cb_response;
1025 op->o_callback = &sc2;
1027 /* Chaining can be performed by a privileged user on behalf
1028 * of normal users, using the ProxyAuthz control, by exploiting
1029 * the identity assertion feature of back-ldap; see idassert-*
1030 * directives in slapd-ldap(5).
1032 * FIXME: the idassert-authcDN is one, will it be fine regardless
1033 * of the URI we obtain from the referral?
1036 switch ( op->o_tag ) {
1037 case LDAP_REQ_BIND: {
1038 struct berval rndn = op->o_req_ndn;
1039 Connection *conn = op->o_conn;
1041 /* FIXME: can we really get a referral for binds? */
1042 op->o_req_ndn = slap_empty_bv;
1044 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1045 op->o_req_ndn = rndn;
1051 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1054 case LDAP_REQ_DELETE:
1055 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1058 case LDAP_REQ_MODRDN:
1059 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1062 case LDAP_REQ_MODIFY:
1063 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1066 case LDAP_REQ_COMPARE:
1067 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
1068 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1073 case LDAP_REQ_SEARCH:
1074 if ( rs->sr_type == REP_SEARCHREF ) {
1075 sc2.sc_response = ldap_chain_cb_search_response;
1076 rc = ldap_chain_search( op, rs, ref, 0 );
1079 /* we might get here before any database actually
1080 * performed a search; in those cases, we need
1081 * to check limits, to make sure safe defaults
1083 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1084 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1087 rc = SLAP_CB_CONTINUE;
1092 case LDAP_REQ_EXTENDED:
1093 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
1094 /* FIXME: ldap_back_extended() by design
1095 * doesn't send result; frontend is expected
1097 /* FIXME: what about chaining? */
1098 if ( rc != SLAPD_ABANDON ) {
1100 send_ldap_extended( op, rs );
1103 lb.lb_status = LDAP_CH_RES;
1107 rc = SLAP_CB_CONTINUE;
1117 sr_err = rs->sr_err;
1118 /* slapd-ldap sent response */
1119 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1120 /* FIXME: should we send response? */
1121 Debug( LDAP_DEBUG_ANY,
1122 "%s: ldap_chain_response: "
1123 "overlay should have sent result.\n",
1124 op->o_log_prefix, 0, 0 );
1129 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1130 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1134 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1135 case LDAP_CHAINING_REQUIRED:
1137 op->o_callback = NULL;
1138 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1139 "operation cannot be completed without chaining" );
1143 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1144 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1145 sr_err = rs->sr_err = rc;
1146 rs->sr_type = sr_type;
1149 rc = SLAP_CB_CONTINUE;
1150 rs->sr_err = sr_err;
1151 rs->sr_type = sr_type;
1153 rs->sr_matched = matched;
1156 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1159 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1162 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1163 /* give the remaining callbacks a chance */
1164 op->o_callback = sc->sc_next;
1165 rc = rs->sr_err = slap_map_api2result( rs );
1166 send_ldap_result( op, rs );
1170 rs->sr_err = sr_err;
1171 rs->sr_type = sr_type;
1173 rs->sr_matched = matched;
1176 op->o_callback = sc;
1182 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1184 ldap_chain_parse_ctrl(
1187 LDAPControl *ctrl );
1190 str2chain( const char *s )
1192 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1193 return LDAP_CHAINING_PREFERRED;
1195 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1196 return LDAP_CHAINING_REQUIRED;
1198 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1199 return LDAP_REFERRALS_PREFERRED;
1201 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1202 return LDAP_REFERRALS_REQUIRED;
1207 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1222 static ConfigDriver chain_cf_gen;
1223 static ConfigCfAdd chain_cfadd;
1224 static ConfigLDAPadd chain_ldadd;
1225 #ifdef SLAP_CONFIG_DELETE
1226 static ConfigLDAPdel chain_lddel;
1229 static ConfigTable chaincfg[] = {
1230 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1231 { "chain-chaining", "args",
1232 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1233 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1234 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1235 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1236 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1237 { "chain-cache-uri", "TRUE/FALSE",
1238 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1239 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1240 "DESC 'Enables caching of URIs not present in configuration' "
1241 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1242 { "chain-max-depth", "args",
1243 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1244 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1245 "DESC 'max referral depth' "
1246 "SYNTAX OMsInteger "
1247 "EQUALITY integerMatch "
1248 "SINGLE-VALUE )", NULL, NULL },
1249 { "chain-return-error", "TRUE/FALSE",
1250 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1251 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1252 "DESC 'Errors are returned instead of the original referral' "
1253 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1254 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1257 static ConfigOCs chainocs[] = {
1258 { "( OLcfgOvOc:3.1 "
1259 "NAME 'olcChainConfig' "
1260 "DESC 'Chain configuration' "
1261 "SUP olcOverlayConfig "
1263 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1264 "olcChainingBehavior $ "
1265 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1266 "olcChainCacheURI $ "
1267 "olcChainMaxReferralDepth $ "
1268 "olcChainReturnError "
1270 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1271 { "( OLcfgOvOc:3.2 "
1272 "NAME 'olcChainDatabase' "
1273 "DESC 'Chain remote server configuration' "
1275 Cft_Misc, olcDatabaseDummy, chain_ldadd
1276 #ifdef SLAP_CONFIG_DELETE
1284 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1291 AttributeDescription *ad = NULL;
1297 if ( p->ce_type != Cft_Overlay
1299 || p->ce_bi->bi_cf_ocs != chainocs )
1301 return LDAP_CONSTRAINT_VIOLATION;
1304 on = (slap_overinst *)p->ce_bi;
1305 lc = (ldap_chain_t *)on->on_bi.bi_private;
1307 assert( ca->be == NULL );
1308 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1310 ca->be->bd_info = (BackendInfo *)on;
1312 rc = slap_str2ad( "olcDbURI", &ad, &text );
1313 assert( rc == LDAP_SUCCESS );
1315 at = attr_find( e->e_attrs, ad );
1317 if ( lc->lc_common_li == NULL && at != NULL ) {
1318 /* FIXME: we should generate an empty default entry
1319 * if none is supplied */
1320 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1321 "first underlying database \"%s\" "
1322 "cannot contain attribute \"%s\".\n",
1323 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1324 rc = LDAP_CONSTRAINT_VIOLATION;
1329 if ( lc->lc_common_li != NULL && at == NULL ) {
1330 /* FIXME: we should generate an empty default entry
1331 * if none is supplied */
1332 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1333 "subsequent underlying database \"%s\" "
1334 "must contain attribute \"%s\".\n",
1335 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1336 rc = LDAP_CONSTRAINT_VIOLATION;
1340 if ( lc->lc_common_li == NULL ) {
1341 rc = ldap_chain_db_init_common( ca->be );
1344 li = ca->be->be_private;
1345 lc->lc_common_li = lc->lc_cfg_li = li;
1348 rc = ldap_chain_db_init_one( ca->be );
1352 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1353 "unable to init %sunderlying database \"%s\".\n",
1354 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1355 return LDAP_CONSTRAINT_VIOLATION;
1358 li = ca->be->be_private;
1361 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1362 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1363 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1364 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1366 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1367 "database \"%s\" insert failed.\n",
1368 e->e_name.bv_val, 0, 0 );
1369 rc = LDAP_CONSTRAINT_VIOLATION;
1374 ca->ca_private = on;
1377 if ( rc != LDAP_SUCCESS ) {
1378 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1386 typedef struct ldap_chain_cfadd_apply_t {
1392 } ldap_chain_cfadd_apply_t;
1395 ldap_chain_cfadd_apply( void *datum, void *arg )
1397 ldapinfo_t *li = (ldapinfo_t *)datum;
1398 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1402 /* FIXME: should not hardcode "olcDatabase" here */
1403 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1404 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1405 bv.bv_val = lca->ca->cr_msg;
1407 lca->ca->be->be_private = (void *)li;
1408 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1409 &bv, lback->bi_cf_ocs, &chainocs[1] );
1417 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1419 CfEntryInfo *pe = p->e_private;
1420 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1421 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1422 void *priv = (void *)ca->be->be_private;
1424 if ( lback->bi_cf_ocs ) {
1425 ldap_chain_cfadd_apply_t lca = { 0 };
1433 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1435 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1436 &lca, 1, AVL_INORDER );
1438 ca->be->be_private = priv;
1444 #ifdef SLAP_CONFIG_DELETE
1446 chain_lddel( CfEntryInfo *ce, Operation *op )
1448 CfEntryInfo *pe = ce->ce_parent;
1449 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1450 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1451 ldapinfo_t *li = (ldapinfo_t *) ce->ce_be->be_private;
1453 if ( li != lc->lc_common_li ) {
1454 if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
1455 Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. "
1456 "\"%s\" not found.\n", li->li_uri, 0, 0 );
1459 } else if ( lc->lc_lai.lai_tree ) {
1460 Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
1461 "LDAP database when other databases are still present.\n", 0, 0, 0 );
1464 lc->lc_common_li = NULL;
1467 ce->ce_be->bd_info = lback;
1469 if ( ce->ce_be->bd_info->bi_db_close ) {
1470 ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
1472 if ( ce->ce_be->bd_info->bi_db_destroy ) {
1473 ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
1479 return LDAP_SUCCESS;
1481 #endif /* SLAP_CONFIG_DELETE */
1483 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1484 static slap_verbmasks chaining_mode[] = {
1485 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1486 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1487 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1488 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1491 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1494 chain_cf_gen( ConfigArgs *c )
1496 slap_overinst *on = (slap_overinst *)c->bi;
1497 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1501 if ( c->op == SLAP_CONFIG_EMIT ) {
1503 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1505 struct berval resolve = BER_BVNULL,
1506 continuation = BER_BVNULL;
1508 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1512 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1513 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1515 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1517 + STRLENOF( "continuation=" ) + continuation.bv_len;
1518 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1519 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1520 "resolve=%s continuation=%s",
1521 resolve.bv_val, continuation.bv_val );
1523 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1524 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1525 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1526 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1527 " critical", STRLENOF( " critical" ) + 1 );
1528 c->value_bv.bv_len += STRLENOF( " critical" );
1533 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1536 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1540 c->value_int = lc->lc_max_depth;
1544 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1553 } else if ( c->op == LDAP_MOD_DELETE ) {
1559 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1567 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1578 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1579 char **argv = c->argv;
1581 BerElementBuffer berbuf;
1582 BerElement *ber = (BerElement *)&berbuf;
1586 Operation op = { 0 };
1587 SlapReply rs = { 0 };
1589 lc->lc_chaining_ctrlflag = 0;
1591 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1592 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1593 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1594 if ( resolve == -1 ) {
1595 Debug( LDAP_DEBUG_ANY, "%s: "
1596 "illegal <resolve> value %s "
1597 "in \"chain-chaining>\".\n",
1598 c->log, argv[ 0 ], 0 );
1602 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1603 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1604 if ( continuation == -1 ) {
1605 Debug( LDAP_DEBUG_ANY, "%s: "
1606 "illegal <continuation> value %s "
1607 "in \"chain-chaining\".\n",
1608 c->log, argv[ 0 ], 0 );
1612 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1616 Debug( LDAP_DEBUG_ANY, "%s: "
1617 "unknown option in \"chain-chaining\".\n",
1623 if ( resolve != -1 || continuation != -1 ) {
1626 if ( resolve == -1 ) {
1628 resolve = SLAP_CHAINING_DEFAULT;
1631 ber_init2( ber, NULL, LBER_USE_DER );
1633 err = ber_printf( ber, "{e" /* } */, resolve );
1636 Debug( LDAP_DEBUG_ANY, "%s: "
1637 "chaining behavior control encoding error!\n",
1642 if ( continuation > -1 ) {
1643 err = ber_printf( ber, "e", continuation );
1646 Debug( LDAP_DEBUG_ANY, "%s: "
1647 "chaining behavior control encoding error!\n",
1653 err = ber_printf( ber, /* { */ "N}" );
1656 Debug( LDAP_DEBUG_ANY, "%s: "
1657 "chaining behavior control encoding error!\n",
1662 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1663 exit( EXIT_FAILURE );
1667 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1670 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1671 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1673 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1675 Debug( LDAP_DEBUG_ANY, "%s: "
1676 "unable to parse chaining control%s%s.\n",
1677 c->log, rs.sr_text ? ": " : "",
1678 rs.sr_text ? rs.sr_text : "" );
1682 lc->lc_chaining_ctrlflag = op.o_chaining;
1684 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1687 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1688 Debug( LDAP_DEBUG_ANY, "%s: "
1689 "\"chaining\" control unsupported (ignored).\n",
1691 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1695 if ( c->value_int ) {
1696 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1698 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1703 if ( c->value_int < 0 ) {
1704 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1705 "<%s> invalid max referral depth %d",
1706 c->argv[0], c->value_int );
1707 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1708 c->log, c->cr_msg, 0 );
1712 lc->lc_max_depth = c->value_int;
1715 if ( c->value_int ) {
1716 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1718 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1734 slap_overinst *on = (slap_overinst *)be->bd_info;
1735 ldap_chain_t *lc = NULL;
1737 if ( lback == NULL ) {
1738 lback = backend_info( "ldap" );
1740 if ( lback == NULL ) {
1745 lc = ch_malloc( sizeof( ldap_chain_t ) );
1749 memset( lc, 0, sizeof( ldap_chain_t ) );
1750 lc->lc_max_depth = 1;
1751 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1753 on->on_bi.bi_private = (void *)lc;
1759 ldap_chain_db_config(
1766 slap_overinst *on = (slap_overinst *)be->bd_info;
1767 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1769 int rc = SLAP_CONF_UNKNOWN;
1771 if ( lc->lc_common_li == NULL ) {
1773 ldap_chain_db_init_common( &db );
1774 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1777 /* Something for the chain database? */
1778 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1779 char *save_argv0 = argv[ 0 ];
1781 static char *allowed_argv[] = {
1782 /* special: put URI here, so in the meanwhile
1783 * it detects whether a new URI is being provided */
1789 /* FIXME: maybe rebind-as-user should be allowed
1790 * only within known URIs... */
1797 int which_argv = -1;
1799 argv[ 0 ] += STRLENOF( "chain-" );
1801 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1802 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1807 if ( allowed_argv[ which_argv ] == NULL ) {
1810 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1811 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1812 "\"%s\" only allowed within a URI directive.\n.",
1813 fname, lineno, argv[ 0 ] );
1818 if ( which_argv == 0 ) {
1819 rc = ldap_chain_db_init_one( &db );
1821 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1822 "underlying slapd-ldap initialization failed.\n.",
1826 lc->lc_cfg_li = db.be_private;
1829 /* TODO: add checks on what other slapd-ldap(5) args
1830 * should be put in the template; this is not quite
1831 * harmful, because attributes that shouldn't don't
1832 * get actually used, but the user should at least
1837 db.be_private = (void *)lc->lc_cfg_li;
1838 db.be_cf_ocs = lback->bi_cf_ocs;
1840 rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1842 argv[ 0 ] = save_argv0;
1844 if ( which_argv == 0 ) {
1848 db.be_private = (void *)lc->lc_cfg_li;
1849 ldap_chain_db_destroy_one( &db, NULL );
1850 lc->lc_cfg_li = NULL;
1852 if ( lc->lc_cfg_li->li_bvuri == NULL
1853 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1854 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1856 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1857 "no URI list allowed in slapo-chain.\n",
1860 goto private_destroy;
1863 if ( avl_insert( &lc->lc_lai.lai_tree,
1864 (caddr_t)lc->lc_cfg_li,
1865 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1867 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1868 "duplicate URI in slapo-chain.\n",
1871 goto private_destroy;
1888 typedef struct ldap_chain_db_apply_t {
1891 } ldap_chain_db_apply_t;
1894 ldap_chain_db_apply( void *datum, void *arg )
1896 ldapinfo_t *li = (ldapinfo_t *)datum;
1897 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1899 lca->be->be_private = (void *)li;
1901 return lca->func( lca->be, NULL );
1910 slap_overinst *on = (slap_overinst *)be->bd_info;
1911 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1916 BI_db_func *func = (&lback->bi_db_open)[ which ];
1918 if ( func != NULL && lc->lc_common_li != NULL ) {
1922 db.be_private = lc->lc_common_li;
1924 rc = func( &db, NULL );
1930 if ( lc->lc_lai.lai_tree != NULL ) {
1931 ldap_chain_db_apply_t lca;
1936 rc = avl_apply( lc->lc_lai.lai_tree,
1937 ldap_chain_db_apply, (void *)&lca,
1938 1, AVL_INORDER ) != AVL_NOMORE;
1951 slap_overinst *on = (slap_overinst *) be->bd_info;
1952 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1953 slap_mask_t monitoring;
1956 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1957 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1961 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1963 if ( lc->lc_common_li == NULL ) {
1964 void *be_private = be->be_private;
1965 ldap_chain_db_init_common( be );
1966 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1967 be->be_private = be_private;
1970 /* filter out and restore monitoring */
1971 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1972 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1973 rc = ldap_chain_db_func( be, db_open );
1974 SLAP_DBFLAGS( be ) |= monitoring;
1980 ldap_chain_db_close(
1984 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1985 #ifdef SLAP_CONFIG_DELETE
1986 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1987 #endif /* SLAP_CONFIG_DELETE */
1988 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1989 return ldap_chain_db_func( be, db_close );
1993 ldap_chain_db_destroy(
1997 slap_overinst *on = (slap_overinst *) be->bd_info;
1998 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2002 rc = ldap_chain_db_func( be, db_destroy );
2005 avl_free( lc->lc_lai.lai_tree, NULL );
2006 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
2014 * inits one instance of the slapd-ldap backend, and stores
2015 * the private info in be_private of the arg
2018 ldap_chain_db_init_common(
2021 BackendInfo *bi = be->bd_info;
2025 be->bd_info = lback;
2026 be->be_private = NULL;
2027 rc = lback->bi_db_init( be, NULL );
2031 li = (ldapinfo_t *)be->be_private;
2032 li->li_urllist_f = NULL;
2033 li->li_urllist_p = NULL;
2041 * inits one instance of the slapd-ldap backend, stores
2042 * the private info in be_private of the arg and fills
2043 * selected fields with data from the template.
2045 * NOTE: add checks about the other fields of the template,
2046 * which are ignored and SHOULD NOT be configured by the user.
2049 ldap_chain_db_init_one(
2052 slap_overinst *on = (slap_overinst *)be->bd_info;
2053 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2055 BackendInfo *bi = be->bd_info;
2060 be->bd_info = lback;
2061 be->be_private = NULL;
2062 t = lback->bi_db_init( be, NULL );
2066 li = (ldapinfo_t *)be->be_private;
2067 li->li_urllist_f = NULL;
2068 li->li_urllist_p = NULL;
2070 /* copy common data */
2071 li->li_nretries = lc->lc_common_li->li_nretries;
2072 li->li_flags = lc->lc_common_li->li_flags;
2073 li->li_version = lc->lc_common_li->li_version;
2074 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2075 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2083 ldap_chain_db_open_one(
2086 if ( SLAP_DBMONITORING( be ) ) {
2087 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
2089 if ( li->li_uri == NULL ) {
2090 ber_str2bv( "cn=Common Connections", 0, 1,
2091 &li->li_monitor_info.lmi_conn_rdn );
2092 ber_str2bv( "cn=Operations on Common Connections", 0, 1,
2093 &li->li_monitor_info.lmi_conn_rdn );
2098 li->li_monitor_info.lmi_conn_rdn.bv_len
2099 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2100 ptr = li->li_monitor_info.lmi_conn_rdn.bv_val
2101 = ch_malloc( li->li_monitor_info.lmi_conn_rdn.bv_len + 1 );
2102 ptr = lutil_strcopy( ptr, "cn=" );
2103 ptr = lutil_strcopy( ptr, li->li_uri );
2106 li->li_monitor_info.lmi_ops_rdn.bv_len
2107 = STRLENOF( "cn=Operations on " ) + strlen( li->li_uri );
2108 ptr = li->li_monitor_info.lmi_ops_rdn.bv_val
2109 = ch_malloc( li->li_monitor_info.lmi_ops_rdn.bv_len + 1 );
2110 ptr = lutil_strcopy( ptr, "cn=Operations on " );
2111 ptr = lutil_strcopy( ptr, li->li_uri );
2116 return lback->bi_db_open( be, NULL );
2119 typedef struct ldap_chain_conn_apply_t {
2122 } ldap_chain_conn_apply_t;
2125 ldap_chain_conn_apply( void *datum, void *arg )
2127 ldapinfo_t *li = (ldapinfo_t *)datum;
2128 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2130 lca->be->be_private = (void *)li;
2132 return lback->bi_connection_destroy( lca->be, lca->conn );
2136 ldap_chain_connection_destroy(
2141 slap_overinst *on = (slap_overinst *) be->bd_info;
2142 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2143 void *private = be->be_private;
2144 ldap_chain_conn_apply_t lca;
2147 be->be_private = NULL;
2150 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2151 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2152 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2153 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2154 be->be_private = private;
2159 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2161 ldap_chain_parse_ctrl(
2171 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2172 rs->sr_text = "Chaining behavior control specified multiple times";
2173 return LDAP_PROTOCOL_ERROR;
2176 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2177 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2178 return LDAP_PROTOCOL_ERROR;
2181 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2182 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2187 /* Parse the control value
2188 * ChainingBehavior ::= SEQUENCE {
2189 * resolveBehavior Behavior OPTIONAL,
2190 * continuationBehavior Behavior OPTIONAL }
2192 * Behavior :: = ENUMERATED {
2193 * chainingPreferred (0),
2194 * chainingRequired (1),
2195 * referralsPreferred (2),
2196 * referralsRequired (3) }
2199 ber = ber_init( &ctrl->ldctl_value );
2201 rs->sr_text = "internal error";
2205 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2206 /* FIXME: since the whole SEQUENCE is optional,
2207 * should we accept no enumerations at all? */
2208 if ( tag != LBER_ENUMERATED ) {
2209 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2210 return LDAP_PROTOCOL_ERROR;
2213 switch ( behavior ) {
2214 case LDAP_CHAINING_PREFERRED:
2215 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2218 case LDAP_CHAINING_REQUIRED:
2219 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2222 case LDAP_REFERRALS_PREFERRED:
2223 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2226 case LDAP_REFERRALS_REQUIRED:
2227 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2231 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2232 return LDAP_PROTOCOL_ERROR;
2235 tag = ber_peek_tag( ber, &len );
2236 if ( tag == LBER_ENUMERATED ) {
2237 tag = ber_scanf( ber, "e", &behavior );
2238 if ( tag == LBER_ERROR ) {
2239 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2240 return LDAP_PROTOCOL_ERROR;
2244 if ( tag == LBER_DEFAULT ) {
2245 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2248 switch ( behavior ) {
2249 case LDAP_CHAINING_PREFERRED:
2250 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2253 case LDAP_CHAINING_REQUIRED:
2254 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2257 case LDAP_REFERRALS_PREFERRED:
2258 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2261 case LDAP_REFERRALS_REQUIRED:
2262 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2266 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2267 return LDAP_PROTOCOL_ERROR;
2271 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2272 rs->sr_text = "Chaining behavior control: decoding error";
2273 return LDAP_PROTOCOL_ERROR;
2276 (void) ber_free( ber, 1 );
2279 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2280 ? SLAP_CONTROL_CRITICAL
2281 : SLAP_CONTROL_NONCRITICAL );
2283 return LDAP_SUCCESS;
2285 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2288 chain_initialize( void )
2292 /* Make sure we don't exceed the bits reserved for userland */
2293 config_check_userland( CH_LAST );
2295 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2296 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2297 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2298 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2299 if ( rc != LDAP_SUCCESS ) {
2300 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2301 "unable to register chaining behavior control: %d.\n",
2305 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2307 ldapchain.on_bi.bi_type = "chain";
2308 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2309 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2310 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2311 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2312 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2314 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2316 ldapchain.on_response = ldap_chain_response;
2318 ldapchain.on_bi.bi_cf_ocs = chainocs;
2320 rc = config_register_schema( chaincfg, chainocs );
2325 return overlay_register( &ldapchain );