1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2015 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;
324 /* don't let other callbacks run, this isn't
325 * the real result for this op.
327 op->o_callback->sc_next = NULL;
334 * Dummy response that simply traces if back-ldap tried to send
335 * anything to the client
338 ldap_chain_cb_response( Operation *op, SlapReply *rs )
340 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
342 /* if in error, don't proceed any further */
343 if ( lb->lb_status == LDAP_CH_ERR ) {
347 if ( rs->sr_type == REP_RESULT ) {
349 switch ( rs->sr_err ) {
350 case LDAP_COMPARE_TRUE:
351 case LDAP_COMPARE_FALSE:
352 if ( op->o_tag != LDAP_REQ_COMPARE ) {
358 lb->lb_status = LDAP_CH_RES;
362 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
363 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
367 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
368 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
369 switch ( get_continuationBehavior( op ) ) {
370 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
371 lb->lb_status = LDAP_CH_ERR;
372 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
378 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
385 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
387 /* strip the entryDN attribute, but keep returning results */
388 (void)ldap_chain_cb_search_response( op, rs );
391 return SLAP_CB_CONTINUE;
402 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
403 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
404 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
405 struct berval odn = op->o_req_dn,
406 ondn = op->o_req_ndn;
407 ldapinfo_t li = { 0 }, *lip = NULL;
408 struct berval bvuri[ 2 ] = { { 0 } };
410 /* NOTE: returned if ref is empty... */
414 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
415 LDAPControl **ctrls = NULL;
417 (void)chaining_control_add( lc, op, &ctrls );
418 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
422 for ( ; !BER_BVISNULL( ref ); ref++ ) {
423 SlapReply rs2 = { 0 };
424 LDAPURLDesc *srv = NULL;
425 req_search_s save_oq_search = op->oq_search,
426 tmp_oq_search = { 0 };
427 struct berval dn = BER_BVNULL,
434 /* We're setting the URI of the first referral;
435 * what if there are more?
441 If the client wishes to progress the operation, it MUST follow the
442 referral by contacting one of the supported services. If multiple
443 URIs are present, the client assumes that any supported URI may be
444 used to progress the operation.
446 * so we actually need to follow exactly one,
447 * and we can assume any is fine.
450 /* parse reference and use
451 * proto://[host][:port]/ only */
452 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
453 if ( rc != LDAP_URL_SUCCESS ) {
454 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
455 op->o_log_prefix, ref->bv_val, 0 );
462 if ( op->o_tag == LDAP_REQ_SEARCH ) {
463 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
464 /* RFC 4511: if scope is present, use it */
465 tmp_oq_search.rs_scope = srv->lud_scope;
468 /* RFC 4511: if scope is absent, use original */
469 tmp_oq_search.rs_scope = op->ors_scope;
474 srv->lud_scope = LDAP_SCOPE_DEFAULT;
475 dn.bv_val = srv->lud_dn;
476 filter = srv->lud_filter;
479 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
480 if ( srv->lud_dn == NULL ) {
485 ber_str2bv( srv->lud_dn, 0, 0, &dn );
486 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
487 if ( rc == LDAP_SUCCESS ) {
488 /* remove DN essentially because later on
489 * ldap_initialize() will parse the URL
490 * as a comma-separated URL list */
497 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
499 if ( srv->lud_filter != NULL
500 && srv->lud_filter[0] != '\0'
501 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
503 /* RFC 4511: if filter is present, use it;
504 * otherwise, use original */
505 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
506 if ( tmp_oq_search.rs_filter != NULL ) {
507 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
510 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
511 op->o_log_prefix, ref->bv_val, srv->lud_filter );
516 srv->lud_filter = NULL;
518 if ( rc == LDAP_SUCCESS ) {
519 li.li_uri = ldap_url_desc2str( srv );
522 srv->lud_dn = dn.bv_val;
523 srv->lud_filter = filter;
524 ldap_free_urldesc( srv );
526 if ( rc != LDAP_SUCCESS ) {
532 if ( li.li_uri == NULL ) {
533 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
534 op->o_log_prefix, ref->bv_val, 0 );
538 goto further_cleanup;
541 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
542 op->o_log_prefix, ref->bv_val, li.li_uri );
547 if ( op->o_tag == LDAP_REQ_SEARCH ) {
548 op->ors_scope = tmp_oq_search.rs_scope;
549 if ( tmp_oq_search.rs_filter != NULL ) {
550 op->ors_filter = tmp_oq_search.rs_filter;
551 op->ors_filterstr = tmp_oq_search.rs_filterstr;
555 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
557 /* Searches for a ldapinfo in the avl tree */
558 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
559 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
560 (caddr_t)&li, ldap_chain_uri_cmp );
561 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
564 op->o_bd->be_private = (void *)lip;
566 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
567 op->o_log_prefix, ref->bv_val, li.li_uri );
570 rc = ldap_chain_db_init_one( op->o_bd );
572 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
573 op->o_log_prefix, ref->bv_val, li.li_uri );
576 lip = (ldapinfo_t *)op->o_bd->be_private;
577 lip->li_uri = li.li_uri;
578 lip->li_bvuri = bvuri;
579 rc = ldap_chain_db_open_one( op->o_bd );
581 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
582 op->o_log_prefix, ref->bv_val, li.li_uri );
584 lip->li_bvuri = NULL;
585 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
589 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
590 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
591 if ( avl_insert( &lc->lc_lai.lai_tree,
592 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
594 /* someone just inserted another;
595 * don't bother, use this and then
599 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
605 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
606 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
610 lb->lb_depth = depth + 1;
612 rc = op_f( op, &rs2 );
614 /* note the first error */
615 if ( first_rc == -1 ) {
620 ldap_memfree( li.li_uri );
625 lip->li_bvuri = NULL;
626 (void)ldap_chain_db_close_one( op->o_bd );
627 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
631 if ( op->o_req_dn.bv_val == pdn.bv_val ) {
633 op->o_req_ndn = ondn;
637 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
638 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
641 if ( op->o_tag == LDAP_REQ_SEARCH ) {
642 if ( tmp_oq_search.rs_filter != NULL ) {
643 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
646 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
647 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
650 op->oq_search = save_oq_search;
653 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
661 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
662 (void)chaining_control_remove( op, &ctrls );
663 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
665 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
680 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
681 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
682 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
683 ldapinfo_t li = { 0 }, *lip = NULL;
684 struct berval bvuri[ 2 ] = { { 0 } };
686 struct berval odn = op->o_req_dn,
687 ondn = op->o_req_ndn;
688 Entry *save_entry = rs->sr_entry;
689 slap_mask_t save_flags = rs->sr_flags;
694 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
695 LDAPControl **ctrls = NULL;
697 (void)chaining_control_add( lc, op, &ctrls );
698 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
700 assert( rs->sr_type == REP_SEARCHREF );
702 rs->sr_type = REP_SEARCH;
704 /* if we parse the URI then by no means
705 * we can cache stuff or reuse connections,
706 * because in back-ldap there's no caching
707 * based on the URI value, which is supposed
708 * to be set once for all (correct?) */
710 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
711 SlapReply rs2 = { REP_RESULT };
713 req_search_s save_oq_search = op->oq_search,
714 tmp_oq_search = { 0 };
722 /* parse reference and use
723 * proto://[host][:port]/ only */
724 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
725 if ( rc != LDAP_URL_SUCCESS ) {
726 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
727 op->o_log_prefix, ref->bv_val, 0 );
730 rs->sr_err = LDAP_OTHER;
734 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
735 /* RFC 4511: if scope is present, use it */
736 tmp_oq_search.rs_scope = srv->lud_scope;
739 /* RFC 4511: if scope is absent, use original */
740 /* Section 4.5.3: if scope is onelevel, use base */
741 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
742 tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
744 tmp_oq_search.rs_scope = op->ors_scope;
748 srv->lud_scope = LDAP_SCOPE_DEFAULT;
749 dn.bv_val = srv->lud_dn;
750 filter = srv->lud_filter;
753 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
754 if ( srv->lud_dn == NULL ) {
758 if ( save_entry != NULL ) {
759 /* use the "right" DN, if available */
760 pdn = save_entry->e_name;
761 ndn = save_entry->e_nname;
762 } /* else leave the original req DN in place, if any RFC 4511 */
765 /* RFC 4511: if DN is present, use it */
766 ber_str2bv( srv->lud_dn, 0, 0, &dn );
767 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
768 if ( rc == LDAP_SUCCESS ) {
769 /* remove DN essentially because later on
770 * ldap_initialize() will parse the URL
771 * as a comma-separated URL list */
778 if ( rc == LDAP_SUCCESS ) {
780 if ( srv->lud_filter != NULL
781 && srv->lud_filter[0] != '\0'
782 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
784 /* RFC 4511: if filter is present, use it;
785 * otherwise, use original */
786 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
787 if ( tmp_oq_search.rs_filter != NULL ) {
788 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
791 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
792 op->o_log_prefix, ref->bv_val, srv->lud_filter );
797 srv->lud_filter = NULL;
799 if ( rc == LDAP_SUCCESS ) {
800 li.li_uri = ldap_url_desc2str( srv );
803 srv->lud_dn = dn.bv_val;
804 srv->lud_filter = filter;
805 ldap_free_urldesc( srv );
807 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
808 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
809 op->o_log_prefix, ref->bv_val, 0 );
813 goto further_cleanup;
816 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
817 op->o_log_prefix, ref->bv_val, li.li_uri );
821 op->ors_scope = tmp_oq_search.rs_scope;
822 if ( tmp_oq_search.rs_filter != NULL ) {
823 op->ors_filter = tmp_oq_search.rs_filter;
824 op->ors_filterstr = tmp_oq_search.rs_filterstr;
827 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
829 /* Searches for a ldapinfo in the avl tree */
830 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
831 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
832 (caddr_t)&li, ldap_chain_uri_cmp );
833 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
836 op->o_bd->be_private = (void *)lip;
838 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
839 op->o_log_prefix, ref->bv_val, li.li_uri );
842 /* if none is found, create a temporary... */
843 rc = ldap_chain_db_init_one( op->o_bd );
845 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
846 op->o_log_prefix, ref->bv_val, li.li_uri );
849 lip = (ldapinfo_t *)op->o_bd->be_private;
850 lip->li_uri = li.li_uri;
851 lip->li_bvuri = bvuri;
852 rc = ldap_chain_db_open_one( op->o_bd );
854 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
855 op->o_log_prefix, ref->bv_val, li.li_uri );
857 lip->li_bvuri = NULL;
858 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
862 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
863 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
864 if ( avl_insert( &lc->lc_lai.lai_tree,
865 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
867 /* someone just inserted another;
868 * don't bother, use this and then
872 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
878 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
879 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
882 lb->lb_op_f = lback->bi_op_search;
883 lb->lb_depth = depth + 1;
885 /* FIXME: should we also copy filter and scope?
886 * according to RFC3296, no */
887 rc = lback->bi_op_search( op, &rs2 );
888 if ( first_rc == -1 ) {
893 ldap_memfree( li.li_uri );
898 lip->li_bvuri = NULL;
899 (void)ldap_chain_db_close_one( op->o_bd );
900 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
904 if ( op->o_req_dn.bv_val == pdn.bv_val ) {
906 op->o_req_ndn = ondn;
910 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
911 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
914 if ( tmp_oq_search.rs_filter != NULL ) {
915 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
918 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
919 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
922 op->oq_search = save_oq_search;
924 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
932 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
933 (void)chaining_control_remove( op, &ctrls );
934 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
936 rs->sr_type = REP_SEARCHREF;
937 rs->sr_entry = save_entry;
938 rs->sr_flags = save_flags;
940 if ( rc != LDAP_SUCCESS ) {
941 /* couldn't chase any of the referrals */
942 if ( first_rc != -1 ) {
946 rc = SLAP_CB_CONTINUE;
954 ldap_chain_response( Operation *op, SlapReply *rs )
956 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
957 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
958 BackendDB db, *bd = op->o_bd;
959 ldap_chain_cb_t lb = { 0 };
960 slap_callback *sc = op->o_callback,
963 const char *text = NULL;
966 struct berval ndn = op->o_ndn;
968 int sr_err = rs->sr_err;
969 slap_reply_t sr_type = rs->sr_type;
970 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
971 slap_mask_t chain_mask = 0;
972 ber_len_t chain_shift = 0;
973 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
975 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
976 return SLAP_CB_CONTINUE;
979 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
980 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
981 switch ( get_resolveBehavior( op ) ) {
982 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
983 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
984 return SLAP_CB_CONTINUE;
987 chain_mask = SLAP_CH_RESOLVE_MASK;
988 chain_shift = SLAP_CH_RESOLVE_SHIFT;
992 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
993 switch ( get_continuationBehavior( op ) ) {
994 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
995 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
996 return SLAP_CB_CONTINUE;
999 chain_mask = SLAP_CH_CONTINUATION_MASK;
1000 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
1004 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1007 * TODO: add checks on who/when chain operations; e.g.:
1008 * a) what identities are authorized
1009 * b) what request DN (e.g. only chain requests rooted at <DN>)
1010 * c) what referral URIs
1011 * d) what protocol scheme (e.g. only ldaps://)
1016 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1021 matched = rs->sr_matched;
1022 rs->sr_matched = NULL;
1026 /* we need this to know if back-ldap returned any result */
1028 sc2.sc_next = sc->sc_next;
1029 sc2.sc_private = &lb;
1030 sc2.sc_response = ldap_chain_cb_response;
1031 op->o_callback = &sc2;
1033 /* Chaining can be performed by a privileged user on behalf
1034 * of normal users, using the ProxyAuthz control, by exploiting
1035 * the identity assertion feature of back-ldap; see idassert-*
1036 * directives in slapd-ldap(5).
1038 * FIXME: the idassert-authcDN is one, will it be fine regardless
1039 * of the URI we obtain from the referral?
1042 switch ( op->o_tag ) {
1043 case LDAP_REQ_BIND: {
1044 struct berval rndn = op->o_req_ndn;
1045 Connection *conn = op->o_conn;
1047 /* FIXME: can we really get a referral for binds? */
1048 op->o_req_ndn = slap_empty_bv;
1050 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1051 op->o_req_ndn = rndn;
1057 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1060 case LDAP_REQ_DELETE:
1061 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1064 case LDAP_REQ_MODRDN:
1065 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1068 case LDAP_REQ_MODIFY:
1069 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1072 case LDAP_REQ_COMPARE:
1073 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
1074 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1079 case LDAP_REQ_SEARCH:
1080 if ( rs->sr_type == REP_SEARCHREF ) {
1081 sc2.sc_response = ldap_chain_cb_search_response;
1082 rc = ldap_chain_search( op, rs, ref, 0 );
1085 /* we might get here before any database actually
1086 * performed a search; in those cases, we need
1087 * to check limits, to make sure safe defaults
1089 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1090 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1093 rc = SLAP_CB_CONTINUE;
1098 case LDAP_REQ_EXTENDED:
1099 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
1100 /* FIXME: ldap_back_extended() by design
1101 * doesn't send result; frontend is expected
1103 /* FIXME: what about chaining? */
1104 if ( rc != SLAPD_ABANDON ) {
1106 send_ldap_extended( op, rs );
1109 lb.lb_status = LDAP_CH_RES;
1113 rc = SLAP_CB_CONTINUE;
1123 sr_err = rs->sr_err;
1124 /* slapd-ldap sent response */
1125 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1126 /* FIXME: should we send response? */
1127 Debug( LDAP_DEBUG_ANY,
1128 "%s: ldap_chain_response: "
1129 "overlay should have sent result.\n",
1130 op->o_log_prefix, 0, 0 );
1135 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1136 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1140 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1141 case LDAP_CHAINING_REQUIRED:
1143 op->o_callback = NULL;
1144 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1145 "operation cannot be completed without chaining" );
1149 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1150 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1151 sr_err = rs->sr_err = rc;
1152 rs->sr_type = sr_type;
1155 rc = SLAP_CB_CONTINUE;
1156 rs->sr_err = sr_err;
1157 rs->sr_type = sr_type;
1159 rs->sr_matched = matched;
1162 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1165 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1168 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1169 /* give the remaining callbacks a chance */
1170 op->o_callback = sc->sc_next;
1171 rc = rs->sr_err = slap_map_api2result( rs );
1172 send_ldap_result( op, rs );
1176 rs->sr_err = sr_err;
1177 rs->sr_type = sr_type;
1179 rs->sr_matched = matched;
1182 op->o_callback = sc;
1188 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1190 ldap_chain_parse_ctrl(
1193 LDAPControl *ctrl );
1196 str2chain( const char *s )
1198 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1199 return LDAP_CHAINING_PREFERRED;
1201 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1202 return LDAP_CHAINING_REQUIRED;
1204 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1205 return LDAP_REFERRALS_PREFERRED;
1207 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1208 return LDAP_REFERRALS_REQUIRED;
1213 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1228 static ConfigDriver chain_cf_gen;
1229 static ConfigCfAdd chain_cfadd;
1230 static ConfigLDAPadd chain_ldadd;
1231 #ifdef SLAP_CONFIG_DELETE
1232 static ConfigLDAPdel chain_lddel;
1235 static ConfigTable chaincfg[] = {
1236 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1237 { "chain-chaining", "args",
1238 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1239 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1240 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1241 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1242 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1243 { "chain-cache-uri", "TRUE/FALSE",
1244 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1245 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1246 "DESC 'Enables caching of URIs not present in configuration' "
1247 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1248 { "chain-max-depth", "args",
1249 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1250 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1251 "DESC 'max referral depth' "
1252 "SYNTAX OMsInteger "
1253 "EQUALITY integerMatch "
1254 "SINGLE-VALUE )", NULL, NULL },
1255 { "chain-return-error", "TRUE/FALSE",
1256 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1257 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1258 "DESC 'Errors are returned instead of the original referral' "
1259 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1260 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1263 static ConfigOCs chainocs[] = {
1264 { "( OLcfgOvOc:3.1 "
1265 "NAME 'olcChainConfig' "
1266 "DESC 'Chain configuration' "
1267 "SUP olcOverlayConfig "
1269 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1270 "olcChainingBehavior $ "
1271 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1272 "olcChainCacheURI $ "
1273 "olcChainMaxReferralDepth $ "
1274 "olcChainReturnError "
1276 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1277 { "( OLcfgOvOc:3.2 "
1278 "NAME 'olcChainDatabase' "
1279 "DESC 'Chain remote server configuration' "
1281 Cft_Misc, olcDatabaseDummy, chain_ldadd
1282 #ifdef SLAP_CONFIG_DELETE
1290 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1297 AttributeDescription *ad = NULL;
1303 if ( p->ce_type != Cft_Overlay
1305 || p->ce_bi->bi_cf_ocs != chainocs )
1307 return LDAP_CONSTRAINT_VIOLATION;
1310 on = (slap_overinst *)p->ce_bi;
1311 lc = (ldap_chain_t *)on->on_bi.bi_private;
1313 assert( ca->be == NULL );
1314 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1316 ca->be->bd_info = (BackendInfo *)on;
1318 rc = slap_str2ad( "olcDbURI", &ad, &text );
1319 assert( rc == LDAP_SUCCESS );
1321 at = attr_find( e->e_attrs, ad );
1323 if ( lc->lc_common_li == NULL && at != NULL ) {
1324 /* FIXME: we should generate an empty default entry
1325 * if none is supplied */
1326 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1327 "first underlying database \"%s\" "
1328 "cannot contain attribute \"%s\".\n",
1329 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1330 rc = LDAP_CONSTRAINT_VIOLATION;
1335 if ( lc->lc_common_li != NULL && at == NULL ) {
1336 /* FIXME: we should generate an empty default entry
1337 * if none is supplied */
1338 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1339 "subsequent underlying database \"%s\" "
1340 "must contain attribute \"%s\".\n",
1341 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1342 rc = LDAP_CONSTRAINT_VIOLATION;
1346 if ( lc->lc_common_li == NULL ) {
1347 rc = ldap_chain_db_init_common( ca->be );
1350 li = ca->be->be_private;
1351 lc->lc_common_li = lc->lc_cfg_li = li;
1354 rc = ldap_chain_db_init_one( ca->be );
1358 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1359 "unable to init %sunderlying database \"%s\".\n",
1360 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1361 return LDAP_CONSTRAINT_VIOLATION;
1364 li = ca->be->be_private;
1367 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1368 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1369 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1370 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1372 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1373 "database \"%s\" insert failed.\n",
1374 e->e_name.bv_val, 0, 0 );
1375 rc = LDAP_CONSTRAINT_VIOLATION;
1380 ca->ca_private = on;
1383 if ( rc != LDAP_SUCCESS ) {
1384 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1392 typedef struct ldap_chain_cfadd_apply_t {
1398 } ldap_chain_cfadd_apply_t;
1401 ldap_chain_cfadd_apply( void *datum, void *arg )
1403 ldapinfo_t *li = (ldapinfo_t *)datum;
1404 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1408 /* FIXME: should not hardcode "olcDatabase" here */
1409 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1410 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1411 bv.bv_val = lca->ca->cr_msg;
1413 lca->ca->be->be_private = (void *)li;
1414 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1415 &bv, lback->bi_cf_ocs, &chainocs[1] );
1423 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1425 CfEntryInfo *pe = p->e_private;
1426 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1427 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1428 void *priv = (void *)ca->be->be_private;
1430 if ( lback->bi_cf_ocs ) {
1431 ldap_chain_cfadd_apply_t lca = { 0 };
1439 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1441 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1442 &lca, 1, AVL_INORDER );
1444 ca->be->be_private = priv;
1450 #ifdef SLAP_CONFIG_DELETE
1452 chain_lddel( CfEntryInfo *ce, Operation *op )
1454 CfEntryInfo *pe = ce->ce_parent;
1455 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1456 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1457 ldapinfo_t *li = (ldapinfo_t *) ce->ce_be->be_private;
1459 if ( li != lc->lc_common_li ) {
1460 if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
1461 Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. "
1462 "\"%s\" not found.\n", li->li_uri, 0, 0 );
1465 } else if ( lc->lc_lai.lai_tree ) {
1466 Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
1467 "LDAP database when other databases are still present.\n", 0, 0, 0 );
1470 lc->lc_common_li = NULL;
1473 ce->ce_be->bd_info = lback;
1475 if ( ce->ce_be->bd_info->bi_db_close ) {
1476 ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
1478 if ( ce->ce_be->bd_info->bi_db_destroy ) {
1479 ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
1485 return LDAP_SUCCESS;
1487 #endif /* SLAP_CONFIG_DELETE */
1489 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1490 static slap_verbmasks chaining_mode[] = {
1491 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1492 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1493 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1494 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1497 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1500 chain_cf_gen( ConfigArgs *c )
1502 slap_overinst *on = (slap_overinst *)c->bi;
1503 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1507 if ( c->op == SLAP_CONFIG_EMIT ) {
1509 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1511 struct berval resolve = BER_BVNULL,
1512 continuation = BER_BVNULL;
1514 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1518 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1519 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1521 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1523 + STRLENOF( "continuation=" ) + continuation.bv_len;
1524 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1525 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1526 "resolve=%s continuation=%s",
1527 resolve.bv_val, continuation.bv_val );
1529 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1530 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1531 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1532 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1533 " critical", STRLENOF( " critical" ) + 1 );
1534 c->value_bv.bv_len += STRLENOF( " critical" );
1539 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1542 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1546 c->value_int = lc->lc_max_depth;
1550 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1559 } else if ( c->op == LDAP_MOD_DELETE ) {
1565 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1573 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1584 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1585 char **argv = c->argv;
1587 BerElementBuffer berbuf;
1588 BerElement *ber = (BerElement *)&berbuf;
1592 Operation op = { 0 };
1593 SlapReply rs = { 0 };
1595 lc->lc_chaining_ctrlflag = 0;
1597 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1598 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1599 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1600 if ( resolve == -1 ) {
1601 Debug( LDAP_DEBUG_ANY, "%s: "
1602 "illegal <resolve> value %s "
1603 "in \"chain-chaining>\".\n",
1604 c->log, argv[ 0 ], 0 );
1608 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1609 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1610 if ( continuation == -1 ) {
1611 Debug( LDAP_DEBUG_ANY, "%s: "
1612 "illegal <continuation> value %s "
1613 "in \"chain-chaining\".\n",
1614 c->log, argv[ 0 ], 0 );
1618 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1622 Debug( LDAP_DEBUG_ANY, "%s: "
1623 "unknown option in \"chain-chaining\".\n",
1629 if ( resolve != -1 || continuation != -1 ) {
1632 if ( resolve == -1 ) {
1634 resolve = SLAP_CHAINING_DEFAULT;
1637 ber_init2( ber, NULL, LBER_USE_DER );
1639 err = ber_printf( ber, "{e" /* } */, resolve );
1642 Debug( LDAP_DEBUG_ANY, "%s: "
1643 "chaining behavior control encoding error!\n",
1648 if ( continuation > -1 ) {
1649 err = ber_printf( ber, "e", continuation );
1652 Debug( LDAP_DEBUG_ANY, "%s: "
1653 "chaining behavior control encoding error!\n",
1659 err = ber_printf( ber, /* { */ "N}" );
1662 Debug( LDAP_DEBUG_ANY, "%s: "
1663 "chaining behavior control encoding error!\n",
1668 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1669 exit( EXIT_FAILURE );
1673 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1676 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1677 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1679 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1681 Debug( LDAP_DEBUG_ANY, "%s: "
1682 "unable to parse chaining control%s%s.\n",
1683 c->log, rs.sr_text ? ": " : "",
1684 rs.sr_text ? rs.sr_text : "" );
1688 lc->lc_chaining_ctrlflag = op.o_chaining;
1690 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1693 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1694 Debug( LDAP_DEBUG_ANY, "%s: "
1695 "\"chaining\" control unsupported (ignored).\n",
1697 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1701 if ( c->value_int ) {
1702 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1704 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1709 if ( c->value_int < 0 ) {
1710 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1711 "<%s> invalid max referral depth %d",
1712 c->argv[0], c->value_int );
1713 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1714 c->log, c->cr_msg, 0 );
1718 lc->lc_max_depth = c->value_int;
1721 if ( c->value_int ) {
1722 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1724 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1740 slap_overinst *on = (slap_overinst *)be->bd_info;
1741 ldap_chain_t *lc = NULL;
1743 if ( lback == NULL ) {
1744 lback = backend_info( "ldap" );
1746 if ( lback == NULL ) {
1751 lc = ch_malloc( sizeof( ldap_chain_t ) );
1755 memset( lc, 0, sizeof( ldap_chain_t ) );
1756 lc->lc_max_depth = 1;
1757 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1759 on->on_bi.bi_private = (void *)lc;
1765 ldap_chain_db_config(
1772 slap_overinst *on = (slap_overinst *)be->bd_info;
1773 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1775 int rc = SLAP_CONF_UNKNOWN;
1777 if ( lc->lc_common_li == NULL ) {
1779 ldap_chain_db_init_common( &db );
1780 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1783 /* Something for the chain database? */
1784 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1785 char *save_argv0 = argv[ 0 ];
1787 static char *allowed_argv[] = {
1788 /* special: put URI here, so in the meanwhile
1789 * it detects whether a new URI is being provided */
1795 /* FIXME: maybe rebind-as-user should be allowed
1796 * only within known URIs... */
1803 int which_argv = -1;
1805 argv[ 0 ] += STRLENOF( "chain-" );
1807 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1808 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1813 if ( allowed_argv[ which_argv ] == NULL ) {
1816 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1817 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1818 "\"%s\" only allowed within a URI directive.\n.",
1819 fname, lineno, argv[ 0 ] );
1824 if ( which_argv == 0 ) {
1825 rc = ldap_chain_db_init_one( &db );
1827 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1828 "underlying slapd-ldap initialization failed.\n.",
1832 lc->lc_cfg_li = db.be_private;
1835 /* TODO: add checks on what other slapd-ldap(5) args
1836 * should be put in the template; this is not quite
1837 * harmful, because attributes that shouldn't don't
1838 * get actually used, but the user should at least
1843 db.be_private = (void *)lc->lc_cfg_li;
1844 db.be_cf_ocs = lback->bi_cf_ocs;
1846 rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1848 argv[ 0 ] = save_argv0;
1850 if ( which_argv == 0 ) {
1854 db.be_private = (void *)lc->lc_cfg_li;
1855 ldap_chain_db_destroy_one( &db, NULL );
1856 lc->lc_cfg_li = NULL;
1858 if ( lc->lc_cfg_li->li_bvuri == NULL
1859 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1860 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1862 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1863 "no URI list allowed in slapo-chain.\n",
1866 goto private_destroy;
1869 if ( avl_insert( &lc->lc_lai.lai_tree,
1870 (caddr_t)lc->lc_cfg_li,
1871 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1873 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1874 "duplicate URI in slapo-chain.\n",
1877 goto private_destroy;
1894 typedef struct ldap_chain_db_apply_t {
1897 } ldap_chain_db_apply_t;
1900 ldap_chain_db_apply( void *datum, void *arg )
1902 ldapinfo_t *li = (ldapinfo_t *)datum;
1903 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1905 lca->be->be_private = (void *)li;
1907 return lca->func( lca->be, NULL );
1916 slap_overinst *on = (slap_overinst *)be->bd_info;
1917 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1922 BI_db_func *func = (&lback->bi_db_open)[ which ];
1924 if ( func != NULL && lc->lc_common_li != NULL ) {
1928 db.be_private = lc->lc_common_li;
1930 rc = func( &db, NULL );
1936 if ( lc->lc_lai.lai_tree != NULL ) {
1937 ldap_chain_db_apply_t lca;
1942 rc = avl_apply( lc->lc_lai.lai_tree,
1943 ldap_chain_db_apply, (void *)&lca,
1944 1, AVL_INORDER ) != AVL_NOMORE;
1957 slap_overinst *on = (slap_overinst *) be->bd_info;
1958 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1959 slap_mask_t monitoring;
1962 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1963 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1967 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1969 if ( lc->lc_common_li == NULL ) {
1970 void *be_private = be->be_private;
1971 ldap_chain_db_init_common( be );
1972 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1973 be->be_private = be_private;
1976 /* filter out and restore monitoring */
1977 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1978 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1979 rc = ldap_chain_db_func( be, db_open );
1980 SLAP_DBFLAGS( be ) |= monitoring;
1986 ldap_chain_db_close(
1990 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1991 #ifdef SLAP_CONFIG_DELETE
1992 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1993 #endif /* SLAP_CONFIG_DELETE */
1994 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1995 return ldap_chain_db_func( be, db_close );
1999 ldap_chain_db_destroy(
2003 slap_overinst *on = (slap_overinst *) be->bd_info;
2004 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2008 rc = ldap_chain_db_func( be, db_destroy );
2011 avl_free( lc->lc_lai.lai_tree, NULL );
2012 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
2020 * inits one instance of the slapd-ldap backend, and stores
2021 * the private info in be_private of the arg
2024 ldap_chain_db_init_common(
2027 BackendInfo *bi = be->bd_info;
2031 be->bd_info = lback;
2032 be->be_private = NULL;
2033 rc = lback->bi_db_init( be, NULL );
2037 li = (ldapinfo_t *)be->be_private;
2038 li->li_urllist_f = NULL;
2039 li->li_urllist_p = NULL;
2047 * inits one instance of the slapd-ldap backend, stores
2048 * the private info in be_private of the arg and fills
2049 * selected fields with data from the template.
2051 * NOTE: add checks about the other fields of the template,
2052 * which are ignored and SHOULD NOT be configured by the user.
2055 ldap_chain_db_init_one(
2058 slap_overinst *on = (slap_overinst *)be->bd_info;
2059 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2061 BackendInfo *bi = be->bd_info;
2066 be->bd_info = lback;
2067 be->be_private = NULL;
2068 t = lback->bi_db_init( be, NULL );
2072 li = (ldapinfo_t *)be->be_private;
2073 li->li_urllist_f = NULL;
2074 li->li_urllist_p = NULL;
2076 /* copy common data */
2077 li->li_nretries = lc->lc_common_li->li_nretries;
2078 li->li_flags = lc->lc_common_li->li_flags;
2079 li->li_version = lc->lc_common_li->li_version;
2080 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2081 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2089 ldap_chain_db_open_one(
2092 if ( SLAP_DBMONITORING( be ) ) {
2093 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
2095 if ( li->li_uri == NULL ) {
2096 ber_str2bv( "cn=Common Connections", 0, 1,
2097 &li->li_monitor_info.lmi_conn_rdn );
2098 ber_str2bv( "cn=Operations on Common Connections", 0, 1,
2099 &li->li_monitor_info.lmi_conn_rdn );
2104 li->li_monitor_info.lmi_conn_rdn.bv_len
2105 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2106 ptr = li->li_monitor_info.lmi_conn_rdn.bv_val
2107 = ch_malloc( li->li_monitor_info.lmi_conn_rdn.bv_len + 1 );
2108 ptr = lutil_strcopy( ptr, "cn=" );
2109 ptr = lutil_strcopy( ptr, li->li_uri );
2112 li->li_monitor_info.lmi_ops_rdn.bv_len
2113 = STRLENOF( "cn=Operations on " ) + strlen( li->li_uri );
2114 ptr = li->li_monitor_info.lmi_ops_rdn.bv_val
2115 = ch_malloc( li->li_monitor_info.lmi_ops_rdn.bv_len + 1 );
2116 ptr = lutil_strcopy( ptr, "cn=Operations on " );
2117 ptr = lutil_strcopy( ptr, li->li_uri );
2122 return lback->bi_db_open( be, NULL );
2125 typedef struct ldap_chain_conn_apply_t {
2128 } ldap_chain_conn_apply_t;
2131 ldap_chain_conn_apply( void *datum, void *arg )
2133 ldapinfo_t *li = (ldapinfo_t *)datum;
2134 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2136 lca->be->be_private = (void *)li;
2138 return lback->bi_connection_destroy( lca->be, lca->conn );
2142 ldap_chain_connection_destroy(
2147 slap_overinst *on = (slap_overinst *) be->bd_info;
2148 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2149 void *private = be->be_private;
2150 ldap_chain_conn_apply_t lca;
2153 be->be_private = NULL;
2156 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2157 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2158 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2159 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2160 be->be_private = private;
2165 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2167 ldap_chain_parse_ctrl(
2177 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2178 rs->sr_text = "Chaining behavior control specified multiple times";
2179 return LDAP_PROTOCOL_ERROR;
2182 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2183 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2184 return LDAP_PROTOCOL_ERROR;
2187 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2188 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2193 /* Parse the control value
2194 * ChainingBehavior ::= SEQUENCE {
2195 * resolveBehavior Behavior OPTIONAL,
2196 * continuationBehavior Behavior OPTIONAL }
2198 * Behavior :: = ENUMERATED {
2199 * chainingPreferred (0),
2200 * chainingRequired (1),
2201 * referralsPreferred (2),
2202 * referralsRequired (3) }
2205 ber = ber_init( &ctrl->ldctl_value );
2207 rs->sr_text = "internal error";
2211 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2212 /* FIXME: since the whole SEQUENCE is optional,
2213 * should we accept no enumerations at all? */
2214 if ( tag != LBER_ENUMERATED ) {
2215 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2216 return LDAP_PROTOCOL_ERROR;
2219 switch ( behavior ) {
2220 case LDAP_CHAINING_PREFERRED:
2221 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2224 case LDAP_CHAINING_REQUIRED:
2225 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2228 case LDAP_REFERRALS_PREFERRED:
2229 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2232 case LDAP_REFERRALS_REQUIRED:
2233 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2237 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2238 return LDAP_PROTOCOL_ERROR;
2241 tag = ber_peek_tag( ber, &len );
2242 if ( tag == LBER_ENUMERATED ) {
2243 tag = ber_scanf( ber, "e", &behavior );
2244 if ( tag == LBER_ERROR ) {
2245 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2246 return LDAP_PROTOCOL_ERROR;
2250 if ( tag == LBER_DEFAULT ) {
2251 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2254 switch ( behavior ) {
2255 case LDAP_CHAINING_PREFERRED:
2256 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2259 case LDAP_CHAINING_REQUIRED:
2260 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2263 case LDAP_REFERRALS_PREFERRED:
2264 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2267 case LDAP_REFERRALS_REQUIRED:
2268 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2272 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2273 return LDAP_PROTOCOL_ERROR;
2277 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2278 rs->sr_text = "Chaining behavior control: decoding error";
2279 return LDAP_PROTOCOL_ERROR;
2282 (void) ber_free( ber, 1 );
2285 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2286 ? SLAP_CONTROL_CRITICAL
2287 : SLAP_CONTROL_NONCRITICAL );
2289 return LDAP_SUCCESS;
2291 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2294 chain_initialize( void )
2298 /* Make sure we don't exceed the bits reserved for userland */
2299 config_check_userland( CH_LAST );
2301 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2302 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2303 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2304 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2305 if ( rc != LDAP_SUCCESS ) {
2306 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2307 "unable to register chaining behavior control: %d.\n",
2311 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2313 ldapchain.on_bi.bi_type = "chain";
2314 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2315 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2316 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2317 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2318 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2320 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2322 ldapchain.on_response = ldap_chain_response;
2324 ldapchain.on_bi.bi_cf_ocs = chainocs;
2326 rc = config_register_schema( chaincfg, chainocs );
2331 return overlay_register( &ldapchain );