1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2014 The OpenLDAP Foundation.
6 * Portions Copyright 2003 Howard Chu.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
18 * This work was initially developed by the Howard Chu for inclusion
19 * in OpenLDAP Software.
20 * This work was subsequently modified by Pierangelo Masarati.
27 #include <ac/string.h>
28 #include <ac/socket.h>
32 #include "back-ldap.h"
35 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
36 #define SLAP_CHAINING_DEFAULT LDAP_CHAINING_PREFERRED
37 #define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT
38 #define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT)
39 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
40 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
41 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
42 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
43 #define SLAP_CH_RESOLVE_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
44 #define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2)
45 #define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT)
46 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
47 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
48 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
49 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
50 #define SLAP_CH_CONTINUATION_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
52 #define o_chaining o_ctrlflag[sc_chainingBehavior]
53 #define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK)
54 #define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
55 #define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
56 #define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
58 static int sc_chainingBehavior;
59 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
65 } ldap_chain_status_t;
67 static BackendInfo *lback;
69 typedef struct ldap_chain_t {
71 * A "template" ldapinfo_t gets all common configuration items;
72 * then, for each configured URI, an entry is created in the tree;
73 * all the specific configuration items get in the current URI
76 * Then, for each referral, extract the URI and lookup the
77 * related structure. If configured to do so, allow URIs
78 * not found in the structure to create a temporary one
79 * that chains anonymously; maybe it can also be added to
80 * the tree? Should be all configurable.
83 /* "common" configuration info (anything occurring before an "uri") */
84 ldapinfo_t *lc_common_li;
86 /* current configuration info */
87 ldapinfo_t *lc_cfg_li;
89 /* tree of configured[/generated?] "uri" info */
90 ldap_avl_info_t lc_lai;
92 /* max depth in nested referrals chaining */
96 #define LDAP_CHAIN_F_NONE (0x00U)
97 #define LDAP_CHAIN_F_CHAINING (0x01U)
98 #define LDAP_CHAIN_F_CACHE_URI (0x02U)
99 #define LDAP_CHAIN_F_RETURN_ERR (0x04U)
101 #define LDAP_CHAIN_ISSET(lc, f) ( ( (lc)->lc_flags & (f) ) == (f) )
102 #define LDAP_CHAIN_CHAINING( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
103 #define LDAP_CHAIN_CACHE_URI( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
104 #define LDAP_CHAIN_RETURN_ERR( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
106 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
107 LDAPControl lc_chaining_ctrl;
108 char lc_chaining_ctrlflag;
109 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
112 static int ldap_chain_db_init_common( BackendDB *be );
113 static int ldap_chain_db_init_one( BackendDB *be );
114 static int ldap_chain_db_open_one( BackendDB *be );
115 #define ldap_chain_db_close_one(be) (0)
116 #define ldap_chain_db_destroy_one(be, rs) (lback)->bi_db_destroy( (be), (rs) )
118 typedef struct ldap_chain_cb_t {
119 ldap_chain_status_t lb_status;
121 slap_operation_t lb_op_type;
129 slap_operation_t op_type,
140 static slap_overinst ldapchain;
142 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
144 chaining_control_add(
147 LDAPControl ***oldctrlsp )
149 LDAPControl **ctrls = NULL;
152 *oldctrlsp = op->o_ctrls;
154 /* default chaining control not defined */
155 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
159 /* already present */
160 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
164 /* FIXME: check other incompatibilities */
166 /* add to other controls */
168 for ( c = 0; op->o_ctrls[ c ]; c++ )
172 ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
173 ctrls[ 0 ] = &lc->lc_chaining_ctrl;
175 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
176 ctrls[ c + 1 ] = op->o_ctrls[ c ];
179 ctrls[ c + 1 ] = NULL;
183 op->o_chaining = lc->lc_chaining_ctrlflag;
189 chaining_control_remove(
191 LDAPControl ***oldctrlsp )
193 LDAPControl **oldctrls = *oldctrlsp;
195 /* we assume that the first control is the chaining control
196 * added by the chain overlay, so it's the only one we explicitly
198 if ( op->o_ctrls != oldctrls ) {
199 if ( op->o_ctrls != NULL ) {
200 assert( op->o_ctrls[ 0 ] != NULL );
206 op->o_ctrls = oldctrls;
213 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
216 ldap_chain_uri_cmp( const void *c1, const void *c2 )
218 const ldapinfo_t *li1 = (const ldapinfo_t *)c1;
219 const ldapinfo_t *li2 = (const ldapinfo_t *)c2;
221 assert( li1->li_bvuri != NULL );
222 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
223 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
225 assert( li2->li_bvuri != NULL );
226 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
227 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
229 return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
233 ldap_chain_uri_dup( void *c1, void *c2 )
235 ldapinfo_t *li1 = (ldapinfo_t *)c1;
236 ldapinfo_t *li2 = (ldapinfo_t *)c2;
238 assert( li1->li_bvuri != NULL );
239 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
240 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
242 assert( li2->li_bvuri != NULL );
243 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
244 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
246 if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
254 * Search specific response that strips entryDN from entries
257 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
259 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
261 assert( op->o_tag == LDAP_REQ_SEARCH );
263 /* if in error, don't proceed any further */
264 if ( lb->lb_status == LDAP_CH_ERR ) {
268 if ( rs->sr_type == REP_SEARCH ) {
269 Attribute **ap = &rs->sr_entry->e_attrs;
271 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
272 /* will be generated later by frontend
273 * (a cleaner solution would be that
274 * the frontend checks if it already exists */
275 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
282 /* there SHOULD be one only! */
287 /* tell the frontend not to add generated
288 * operational attributes */
289 rs->sr_flags |= REP_NO_OPERATIONALS;
291 return SLAP_CB_CONTINUE;
293 } else if ( rs->sr_type == REP_SEARCHREF ) {
294 /* if we get it here, it means the library was unable
295 * to chase the referral... */
296 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
297 rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
300 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
301 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
302 switch ( get_continuationBehavior( op ) ) {
303 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
304 lb->lb_status = LDAP_CH_ERR;
305 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
311 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
312 return SLAP_CB_CONTINUE;
314 } else if ( rs->sr_type == REP_RESULT ) {
315 if ( rs->sr_err == LDAP_REFERRAL
316 && lb->lb_depth < lb->lb_lc->lc_max_depth
317 && rs->sr_ref != NULL )
319 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_type,
320 rs->sr_ref, lb->lb_depth );
323 /* back-ldap tried to send result */
324 lb->lb_status = LDAP_CH_RES;
331 * Dummy response that simply traces if back-ldap tried to send
332 * anything to the client
335 ldap_chain_cb_response( Operation *op, SlapReply *rs )
337 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
339 /* if in error, don't proceed any further */
340 if ( lb->lb_status == LDAP_CH_ERR ) {
344 if ( rs->sr_type == REP_RESULT ) {
346 switch ( rs->sr_err ) {
347 case LDAP_COMPARE_TRUE:
348 case LDAP_COMPARE_FALSE:
349 if ( op->o_tag != LDAP_REQ_COMPARE ) {
355 lb->lb_status = LDAP_CH_RES;
359 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
360 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_type,
361 rs->sr_ref, lb->lb_depth );
365 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
366 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
367 switch ( get_continuationBehavior( op ) ) {
368 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
369 lb->lb_status = LDAP_CH_ERR;
370 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
376 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
383 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
385 /* strip the entryDN attribute, but keep returning results */
386 (void)ldap_chain_cb_search_response( op, rs );
389 return SLAP_CB_CONTINUE;
396 slap_operation_t op_type,
400 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
401 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
402 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
403 struct berval odn = op->o_req_dn,
404 ondn = op->o_req_ndn;
405 ldapinfo_t li = { 0 }, *lip = NULL;
406 struct berval bvuri[ 2 ] = { { 0 } };
408 /* NOTE: returned if ref is empty... */
412 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
413 LDAPControl **ctrls = NULL;
415 (void)chaining_control_add( lc, op, &ctrls );
416 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
420 for ( ; !BER_BVISNULL( ref ); ref++ ) {
421 SlapReply rs2 = { 0 };
422 LDAPURLDesc *srv = NULL;
423 req_search_s save_oq_search = op->oq_search,
424 tmp_oq_search = { 0 };
425 struct berval dn = BER_BVNULL,
432 /* We're setting the URI of the first referral;
433 * what if there are more?
439 If the client wishes to progress the operation, it MUST follow the
440 referral by contacting one of the supported services. If multiple
441 URIs are present, the client assumes that any supported URI may be
442 used to progress the operation.
444 * so we actually need to follow exactly one,
445 * and we can assume any is fine.
448 /* parse reference and use
449 * proto://[host][:port]/ only */
450 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
451 if ( rc != LDAP_URL_SUCCESS ) {
452 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
453 op->o_log_prefix, ref->bv_val, 0 );
460 if ( op->o_tag == LDAP_REQ_SEARCH ) {
461 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
462 /* RFC 4511: if scope is present, use it */
463 tmp_oq_search.rs_scope = srv->lud_scope;
466 /* RFC 4511: if scope is absent, use original */
467 tmp_oq_search.rs_scope = op->ors_scope;
472 srv->lud_scope = LDAP_SCOPE_DEFAULT;
473 dn.bv_val = srv->lud_dn;
474 filter = srv->lud_filter;
477 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
478 if ( srv->lud_dn == NULL ) {
483 ber_str2bv( srv->lud_dn, 0, 0, &dn );
484 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
485 if ( rc == LDAP_SUCCESS ) {
486 /* remove DN essentially because later on
487 * ldap_initialize() will parse the URL
488 * as a comma-separated URL list */
495 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
497 if ( srv->lud_filter != NULL
498 && srv->lud_filter[0] != '\0'
499 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
501 /* RFC 4511: if filter is present, use it;
502 * otherwise, use original */
503 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
504 if ( tmp_oq_search.rs_filter != NULL ) {
505 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
508 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
509 op->o_log_prefix, ref->bv_val, srv->lud_filter );
514 srv->lud_filter = NULL;
516 if ( rc == LDAP_SUCCESS ) {
517 li.li_uri = ldap_url_desc2str( srv );
520 srv->lud_dn = dn.bv_val;
521 srv->lud_filter = filter;
522 ldap_free_urldesc( srv );
524 if ( rc != LDAP_SUCCESS ) {
530 if ( li.li_uri == NULL ) {
531 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
532 op->o_log_prefix, ref->bv_val, 0 );
536 goto further_cleanup;
539 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
540 op->o_log_prefix, ref->bv_val, li.li_uri );
545 if ( op->o_tag == LDAP_REQ_SEARCH ) {
546 op->ors_scope = tmp_oq_search.rs_scope;
547 if ( tmp_oq_search.rs_filter != NULL ) {
548 op->ors_filter = tmp_oq_search.rs_filter;
549 op->ors_filterstr = tmp_oq_search.rs_filterstr;
553 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
555 /* Searches for a ldapinfo in the avl tree */
556 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
557 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
558 (caddr_t)&li, ldap_chain_uri_cmp );
559 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
562 op->o_bd->be_private = (void *)lip;
564 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
565 op->o_log_prefix, ref->bv_val, li.li_uri );
568 rc = ldap_chain_db_init_one( op->o_bd );
570 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
571 op->o_log_prefix, ref->bv_val, li.li_uri );
574 lip = (ldapinfo_t *)op->o_bd->be_private;
575 lip->li_uri = li.li_uri;
576 lip->li_bvuri = bvuri;
577 rc = ldap_chain_db_open_one( op->o_bd );
579 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
580 op->o_log_prefix, ref->bv_val, li.li_uri );
582 lip->li_bvuri = NULL;
583 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
587 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
588 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
589 if ( avl_insert( &lc->lc_lai.lai_tree,
590 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
592 /* someone just inserted another;
593 * don't bother, use this and then
597 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
603 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
604 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
607 lb->lb_op_type = op_type;
608 lb->lb_depth = depth + 1;
610 rc = (&lback->bi_op_bind)[ op_type ]( op, &rs2 );
612 /* note the first error */
613 if ( first_rc == -1 ) {
618 ldap_memfree( li.li_uri );
623 lip->li_bvuri = NULL;
624 (void)ldap_chain_db_close_one( op->o_bd );
625 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
630 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
631 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
634 if ( op->o_tag == LDAP_REQ_SEARCH ) {
635 if ( tmp_oq_search.rs_filter != NULL ) {
636 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
639 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
640 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
643 op->oq_search = save_oq_search;
646 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
655 op->o_req_ndn = ondn;
657 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
658 (void)chaining_control_remove( op, &ctrls );
659 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
661 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
676 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
677 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
678 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
679 ldapinfo_t li = { 0 }, *lip = NULL;
680 struct berval bvuri[ 2 ] = { { 0 } };
682 struct berval odn = op->o_req_dn,
683 ondn = op->o_req_ndn;
684 Entry *save_entry = rs->sr_entry;
685 slap_mask_t save_flags = rs->sr_flags;
690 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
691 LDAPControl **ctrls = NULL;
693 (void)chaining_control_add( lc, op, &ctrls );
694 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
696 assert( rs->sr_type == REP_SEARCHREF );
698 rs->sr_type = REP_SEARCH;
700 /* if we parse the URI then by no means
701 * we can cache stuff or reuse connections,
702 * because in back-ldap there's no caching
703 * based on the URI value, which is supposed
704 * to be set once for all (correct?) */
706 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
707 SlapReply rs2 = { REP_RESULT };
709 req_search_s save_oq_search = op->oq_search,
710 tmp_oq_search = { 0 };
718 /* parse reference and use
719 * proto://[host][:port]/ only */
720 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
721 if ( rc != LDAP_URL_SUCCESS ) {
722 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
723 op->o_log_prefix, ref->bv_val, 0 );
726 rs->sr_err = LDAP_OTHER;
730 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
731 /* RFC 4511: if scope is present, use it */
732 tmp_oq_search.rs_scope = srv->lud_scope;
735 /* RFC 4511: if scope is absent, use original */
736 /* Section 4.5.3: if scope is onelevel, use base */
737 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
738 tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
740 tmp_oq_search.rs_scope = op->ors_scope;
744 srv->lud_scope = LDAP_SCOPE_DEFAULT;
745 dn.bv_val = srv->lud_dn;
746 filter = srv->lud_filter;
749 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
750 if ( srv->lud_dn == NULL ) {
754 if ( save_entry != NULL ) {
755 /* use the "right" DN, if available */
756 pdn = save_entry->e_name;
757 ndn = save_entry->e_nname;
758 } /* else leave the original req DN in place, if any RFC 4511 */
761 /* RFC 4511: if DN is present, use it */
762 ber_str2bv( srv->lud_dn, 0, 0, &dn );
763 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
764 if ( rc == LDAP_SUCCESS ) {
765 /* remove DN essentially because later on
766 * ldap_initialize() will parse the URL
767 * as a comma-separated URL list */
774 if ( rc == LDAP_SUCCESS ) {
776 if ( srv->lud_filter != NULL
777 && srv->lud_filter[0] != '\0'
778 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
780 /* RFC 4511: if filter is present, use it;
781 * otherwise, use original */
782 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
783 if ( tmp_oq_search.rs_filter != NULL ) {
784 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
787 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
788 op->o_log_prefix, ref->bv_val, srv->lud_filter );
793 srv->lud_filter = NULL;
795 if ( rc == LDAP_SUCCESS ) {
796 li.li_uri = ldap_url_desc2str( srv );
799 srv->lud_dn = dn.bv_val;
800 srv->lud_filter = filter;
801 ldap_free_urldesc( srv );
803 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
804 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
805 op->o_log_prefix, ref->bv_val, 0 );
809 goto further_cleanup;
812 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
813 op->o_log_prefix, ref->bv_val, li.li_uri );
817 op->ors_scope = tmp_oq_search.rs_scope;
818 if ( tmp_oq_search.rs_filter != NULL ) {
819 op->ors_filter = tmp_oq_search.rs_filter;
820 op->ors_filterstr = tmp_oq_search.rs_filterstr;
823 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
825 /* Searches for a ldapinfo in the avl tree */
826 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
827 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
828 (caddr_t)&li, ldap_chain_uri_cmp );
829 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
832 op->o_bd->be_private = (void *)lip;
834 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
835 op->o_log_prefix, ref->bv_val, li.li_uri );
838 /* if none is found, create a temporary... */
839 rc = ldap_chain_db_init_one( op->o_bd );
841 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
842 op->o_log_prefix, ref->bv_val, li.li_uri );
845 lip = (ldapinfo_t *)op->o_bd->be_private;
846 lip->li_uri = li.li_uri;
847 lip->li_bvuri = bvuri;
848 rc = ldap_chain_db_open_one( op->o_bd );
850 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
851 op->o_log_prefix, ref->bv_val, li.li_uri );
853 lip->li_bvuri = NULL;
854 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
858 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
859 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
860 if ( avl_insert( &lc->lc_lai.lai_tree,
861 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
863 /* someone just inserted another;
864 * don't bother, use this and then
868 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
874 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
875 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
878 lb->lb_op_type = op_search;
879 lb->lb_depth = depth + 1;
881 /* FIXME: should we also copy filter and scope?
882 * according to RFC3296, no */
883 rc = lback->bi_op_search( op, &rs2 );
884 if ( first_rc == -1 ) {
889 ldap_memfree( li.li_uri );
894 lip->li_bvuri = NULL;
895 (void)ldap_chain_db_close_one( op->o_bd );
896 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
901 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
902 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
906 op->o_req_ndn = ondn;
908 if ( tmp_oq_search.rs_filter != NULL ) {
909 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
912 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
913 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
916 op->oq_search = save_oq_search;
918 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
926 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
927 (void)chaining_control_remove( op, &ctrls );
928 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
931 op->o_req_ndn = ondn;
932 rs->sr_type = REP_SEARCHREF;
933 rs->sr_entry = save_entry;
934 rs->sr_flags = save_flags;
936 if ( rc != LDAP_SUCCESS ) {
937 /* couldn't chase any of the referrals */
938 if ( first_rc != -1 ) {
942 rc = SLAP_CB_CONTINUE;
950 ldap_chain_response( Operation *op, SlapReply *rs )
952 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
953 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
954 BackendDB db, *bd = op->o_bd;
955 ldap_chain_cb_t lb = { 0 };
956 slap_callback *sc = op->o_callback,
959 const char *text = NULL;
962 struct berval ndn = op->o_ndn;
964 int sr_err = rs->sr_err;
965 slap_reply_t sr_type = rs->sr_type;
966 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
967 slap_mask_t chain_mask = 0;
968 ber_len_t chain_shift = 0;
969 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
971 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
972 return SLAP_CB_CONTINUE;
975 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
976 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
977 switch ( get_resolveBehavior( op ) ) {
978 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
979 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
980 return SLAP_CB_CONTINUE;
983 chain_mask = SLAP_CH_RESOLVE_MASK;
984 chain_shift = SLAP_CH_RESOLVE_SHIFT;
988 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
989 switch ( get_continuationBehavior( op ) ) {
990 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
991 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
992 return SLAP_CB_CONTINUE;
995 chain_mask = SLAP_CH_CONTINUATION_MASK;
996 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
1000 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1003 * TODO: add checks on who/when chain operations; e.g.:
1004 * a) what identities are authorized
1005 * b) what request DN (e.g. only chain requests rooted at <DN>)
1006 * c) what referral URIs
1007 * d) what protocol scheme (e.g. only ldaps://)
1012 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1017 matched = rs->sr_matched;
1018 rs->sr_matched = NULL;
1022 /* we need this to know if back-ldap returned any result */
1024 sc2.sc_next = sc->sc_next;
1025 sc2.sc_private = &lb;
1026 sc2.sc_response = ldap_chain_cb_response;
1027 op->o_callback = &sc2;
1029 /* Chaining can be performed by a privileged user on behalf
1030 * of normal users, using the ProxyAuthz control, by exploiting
1031 * the identity assertion feature of back-ldap; see idassert-*
1032 * directives in slapd-ldap(5).
1034 * FIXME: the idassert-authcDN is one, will it be fine regardless
1035 * of the URI we obtain from the referral?
1038 switch ( op->o_tag ) {
1039 case LDAP_REQ_BIND: {
1040 struct berval rndn = op->o_req_ndn;
1041 Connection *conn = op->o_conn;
1043 /* FIXME: can we really get a referral for binds? */
1044 op->o_req_ndn = slap_empty_bv;
1046 rc = ldap_chain_op( op, rs, op_bind, ref, 0 );
1047 op->o_req_ndn = rndn;
1053 rc = ldap_chain_op( op, rs, op_add, ref, 0 );
1056 case LDAP_REQ_DELETE:
1057 rc = ldap_chain_op( op, rs, op_delete, ref, 0 );
1060 case LDAP_REQ_MODRDN:
1061 rc = ldap_chain_op( op, rs, op_modrdn, ref, 0 );
1064 case LDAP_REQ_MODIFY:
1065 rc = ldap_chain_op( op, rs, op_modify, ref, 0 );
1068 case LDAP_REQ_COMPARE:
1069 rc = ldap_chain_op( op, rs, op_compare, ref, 0 );
1070 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1075 case LDAP_REQ_SEARCH:
1076 if ( rs->sr_type == REP_SEARCHREF ) {
1077 sc2.sc_response = ldap_chain_cb_search_response;
1078 rc = ldap_chain_search( op, rs, ref, 0 );
1081 /* we might get here before any database actually
1082 * performed a search; in those cases, we need
1083 * to check limits, to make sure safe defaults
1085 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1086 rc = ldap_chain_op( op, rs, op_search, ref, 0 );
1088 rc = SLAP_CB_CONTINUE;
1093 case LDAP_REQ_EXTENDED:
1094 rc = ldap_chain_op( op, rs, op_extended, ref, 0 );
1095 /* FIXME: ldap_back_extended() by design
1096 * doesn't send result; frontend is expected
1098 /* FIXME: what about chaining? */
1099 if ( rc != SLAPD_ABANDON ) {
1101 send_ldap_extended( op, rs );
1104 lb.lb_status = LDAP_CH_RES;
1108 rc = SLAP_CB_CONTINUE;
1118 sr_err = rs->sr_err;
1119 /* slapd-ldap sent response */
1120 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1121 /* FIXME: should we send response? */
1122 Debug( LDAP_DEBUG_ANY,
1123 "%s: ldap_chain_response: "
1124 "overlay should have sent result.\n",
1125 op->o_log_prefix, 0, 0 );
1130 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1131 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1135 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1136 case LDAP_CHAINING_REQUIRED:
1138 op->o_callback = NULL;
1139 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1140 "operation cannot be completed without chaining" );
1144 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1145 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1146 sr_err = rs->sr_err = rc;
1147 rs->sr_type = sr_type;
1150 rc = SLAP_CB_CONTINUE;
1151 rs->sr_err = sr_err;
1152 rs->sr_type = sr_type;
1154 rs->sr_matched = matched;
1157 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1160 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1163 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1164 /* give the remaining callbacks a chance */
1165 op->o_callback = sc->sc_next;
1166 rc = rs->sr_err = slap_map_api2result( rs );
1167 send_ldap_result( op, rs );
1171 rs->sr_err = sr_err;
1172 rs->sr_type = sr_type;
1174 rs->sr_matched = matched;
1177 op->o_callback = sc;
1183 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1185 ldap_chain_parse_ctrl(
1188 LDAPControl *ctrl );
1191 str2chain( const char *s )
1193 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1194 return LDAP_CHAINING_PREFERRED;
1196 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1197 return LDAP_CHAINING_REQUIRED;
1199 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1200 return LDAP_REFERRALS_PREFERRED;
1202 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1203 return LDAP_REFERRALS_REQUIRED;
1208 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1223 static ConfigDriver chain_cf_gen;
1224 static ConfigCfAdd chain_cfadd;
1225 static ConfigLDAPadd chain_ldadd;
1226 #ifdef SLAP_CONFIG_DELETE
1227 static ConfigLDAPdel chain_lddel;
1230 static ConfigTable chaincfg[] = {
1231 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1232 { "chain-chaining", "args",
1233 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1234 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1235 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1236 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1237 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1238 { "chain-cache-uri", "TRUE/FALSE",
1239 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1240 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1241 "DESC 'Enables caching of URIs not present in configuration' "
1242 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1243 { "chain-max-depth", "args",
1244 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1245 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1246 "DESC 'max referral depth' "
1247 "SYNTAX OMsInteger "
1248 "EQUALITY integerMatch "
1249 "SINGLE-VALUE )", NULL, NULL },
1250 { "chain-return-error", "TRUE/FALSE",
1251 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1252 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1253 "DESC 'Errors are returned instead of the original referral' "
1254 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1255 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1258 static ConfigOCs chainocs[] = {
1259 { "( OLcfgOvOc:3.1 "
1260 "NAME 'olcChainConfig' "
1261 "DESC 'Chain configuration' "
1262 "SUP olcOverlayConfig "
1264 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1265 "olcChainingBehavior $ "
1266 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1267 "olcChainCacheURI $ "
1268 "olcChainMaxReferralDepth $ "
1269 "olcChainReturnError "
1271 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1272 { "( OLcfgOvOc:3.2 "
1273 "NAME 'olcChainDatabase' "
1274 "DESC 'Chain remote server configuration' "
1276 Cft_Misc, olcDatabaseDummy, chain_ldadd
1277 #ifdef SLAP_CONFIG_DELETE
1285 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1292 AttributeDescription *ad = NULL;
1298 if ( p->ce_type != Cft_Overlay
1300 || p->ce_bi->bi_cf_ocs != chainocs )
1302 return LDAP_CONSTRAINT_VIOLATION;
1305 on = (slap_overinst *)p->ce_bi;
1306 lc = (ldap_chain_t *)on->on_bi.bi_private;
1308 assert( ca->be == NULL );
1309 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1311 ca->be->bd_info = (BackendInfo *)on;
1313 rc = slap_str2ad( "olcDbURI", &ad, &text );
1314 assert( rc == LDAP_SUCCESS );
1316 at = attr_find( e->e_attrs, ad );
1318 if ( lc->lc_common_li == NULL && at != NULL ) {
1319 /* FIXME: we should generate an empty default entry
1320 * if none is supplied */
1321 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1322 "first underlying database \"%s\" "
1323 "cannot contain attribute \"%s\".\n",
1324 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1325 rc = LDAP_CONSTRAINT_VIOLATION;
1330 if ( lc->lc_common_li != NULL && at == NULL ) {
1331 /* FIXME: we should generate an empty default entry
1332 * if none is supplied */
1333 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1334 "subsequent underlying database \"%s\" "
1335 "must contain attribute \"%s\".\n",
1336 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1337 rc = LDAP_CONSTRAINT_VIOLATION;
1341 if ( lc->lc_common_li == NULL ) {
1342 rc = ldap_chain_db_init_common( ca->be );
1345 li = ca->be->be_private;
1346 lc->lc_common_li = lc->lc_cfg_li = li;
1349 rc = ldap_chain_db_init_one( ca->be );
1353 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1354 "unable to init %sunderlying database \"%s\".\n",
1355 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1356 return LDAP_CONSTRAINT_VIOLATION;
1359 li = ca->be->be_private;
1362 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1363 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1364 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1365 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1367 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1368 "database \"%s\" insert failed.\n",
1369 e->e_name.bv_val, 0, 0 );
1370 rc = LDAP_CONSTRAINT_VIOLATION;
1375 ca->ca_private = on;
1378 if ( rc != LDAP_SUCCESS ) {
1379 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1387 typedef struct ldap_chain_cfadd_apply_t {
1393 } ldap_chain_cfadd_apply_t;
1396 ldap_chain_cfadd_apply( void *datum, void *arg )
1398 ldapinfo_t *li = (ldapinfo_t *)datum;
1399 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1403 /* FIXME: should not hardcode "olcDatabase" here */
1404 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1405 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1406 bv.bv_val = lca->ca->cr_msg;
1408 lca->ca->be->be_private = (void *)li;
1409 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1410 &bv, lback->bi_cf_ocs, &chainocs[1] );
1418 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1420 CfEntryInfo *pe = p->e_private;
1421 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1422 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1423 void *priv = (void *)ca->be->be_private;
1425 if ( lback->bi_cf_ocs ) {
1426 ldap_chain_cfadd_apply_t lca = { 0 };
1434 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1436 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1437 &lca, 1, AVL_INORDER );
1439 ca->be->be_private = priv;
1445 #ifdef SLAP_CONFIG_DELETE
1447 chain_lddel( CfEntryInfo *ce, Operation *op )
1449 CfEntryInfo *pe = ce->ce_parent;
1450 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1451 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1452 ldapinfo_t *li = (ldapinfo_t *) ce->ce_be->be_private;
1454 if ( li != lc->lc_common_li ) {
1455 if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
1456 Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. "
1457 "\"%s\" not found.\n", li->li_uri, 0, 0 );
1460 } else if ( lc->lc_lai.lai_tree ) {
1461 Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
1462 "LDAP database when other databases are still present.\n", 0, 0, 0 );
1465 lc->lc_common_li = NULL;
1468 ce->ce_be->bd_info = lback;
1470 if ( ce->ce_be->bd_info->bi_db_close ) {
1471 ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
1473 if ( ce->ce_be->bd_info->bi_db_destroy ) {
1474 ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
1480 return LDAP_SUCCESS;
1482 #endif /* SLAP_CONFIG_DELETE */
1484 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1485 static slap_verbmasks chaining_mode[] = {
1486 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1487 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1488 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1489 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1492 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1495 chain_cf_gen( ConfigArgs *c )
1497 slap_overinst *on = (slap_overinst *)c->bi;
1498 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1502 if ( c->op == SLAP_CONFIG_EMIT ) {
1504 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1506 struct berval resolve = BER_BVNULL,
1507 continuation = BER_BVNULL;
1509 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1513 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1514 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1516 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1518 + STRLENOF( "continuation=" ) + continuation.bv_len;
1519 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1520 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1521 "resolve=%s continuation=%s",
1522 resolve.bv_val, continuation.bv_val );
1524 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1525 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1526 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1527 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1528 " critical", STRLENOF( " critical" ) + 1 );
1529 c->value_bv.bv_len += STRLENOF( " critical" );
1534 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1537 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1541 c->value_int = lc->lc_max_depth;
1545 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1554 } else if ( c->op == LDAP_MOD_DELETE ) {
1560 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1568 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1579 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1580 char **argv = c->argv;
1582 BerElementBuffer berbuf;
1583 BerElement *ber = (BerElement *)&berbuf;
1587 Operation op = { 0 };
1588 SlapReply rs = { 0 };
1590 lc->lc_chaining_ctrlflag = 0;
1592 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1593 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1594 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1595 if ( resolve == -1 ) {
1596 Debug( LDAP_DEBUG_ANY, "%s: "
1597 "illegal <resolve> value %s "
1598 "in \"chain-chaining>\".\n",
1599 c->log, argv[ 0 ], 0 );
1603 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1604 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1605 if ( continuation == -1 ) {
1606 Debug( LDAP_DEBUG_ANY, "%s: "
1607 "illegal <continuation> value %s "
1608 "in \"chain-chaining\".\n",
1609 c->log, argv[ 0 ], 0 );
1613 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1617 Debug( LDAP_DEBUG_ANY, "%s: "
1618 "unknown option in \"chain-chaining\".\n",
1624 if ( resolve != -1 || continuation != -1 ) {
1627 if ( resolve == -1 ) {
1629 resolve = SLAP_CHAINING_DEFAULT;
1632 ber_init2( ber, NULL, LBER_USE_DER );
1634 err = ber_printf( ber, "{e" /* } */, resolve );
1637 Debug( LDAP_DEBUG_ANY, "%s: "
1638 "chaining behavior control encoding error!\n",
1643 if ( continuation > -1 ) {
1644 err = ber_printf( ber, "e", continuation );
1647 Debug( LDAP_DEBUG_ANY, "%s: "
1648 "chaining behavior control encoding error!\n",
1654 err = ber_printf( ber, /* { */ "N}" );
1657 Debug( LDAP_DEBUG_ANY, "%s: "
1658 "chaining behavior control encoding error!\n",
1663 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1664 exit( EXIT_FAILURE );
1668 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1671 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1672 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1674 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1676 Debug( LDAP_DEBUG_ANY, "%s: "
1677 "unable to parse chaining control%s%s.\n",
1678 c->log, rs.sr_text ? ": " : "",
1679 rs.sr_text ? rs.sr_text : "" );
1683 lc->lc_chaining_ctrlflag = op.o_chaining;
1685 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1688 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1689 Debug( LDAP_DEBUG_ANY, "%s: "
1690 "\"chaining\" control unsupported (ignored).\n",
1692 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1696 if ( c->value_int ) {
1697 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1699 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1704 if ( c->value_int < 0 ) {
1705 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1706 "<%s> invalid max referral depth %d",
1707 c->argv[0], c->value_int );
1708 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1709 c->log, c->cr_msg, 0 );
1713 lc->lc_max_depth = c->value_int;
1716 if ( c->value_int ) {
1717 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1719 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1735 slap_overinst *on = (slap_overinst *)be->bd_info;
1736 ldap_chain_t *lc = NULL;
1738 if ( lback == NULL ) {
1739 lback = backend_info( "ldap" );
1741 if ( lback == NULL ) {
1746 lc = ch_malloc( sizeof( ldap_chain_t ) );
1750 memset( lc, 0, sizeof( ldap_chain_t ) );
1751 lc->lc_max_depth = 1;
1752 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1754 on->on_bi.bi_private = (void *)lc;
1760 ldap_chain_db_config(
1767 slap_overinst *on = (slap_overinst *)be->bd_info;
1768 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1770 int rc = SLAP_CONF_UNKNOWN;
1772 if ( lc->lc_common_li == NULL ) {
1774 ldap_chain_db_init_common( &db );
1775 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1778 /* Something for the chain database? */
1779 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1780 char *save_argv0 = argv[ 0 ];
1782 static char *allowed_argv[] = {
1783 /* special: put URI here, so in the meanwhile
1784 * it detects whether a new URI is being provided */
1790 /* FIXME: maybe rebind-as-user should be allowed
1791 * only within known URIs... */
1798 int which_argv = -1;
1800 argv[ 0 ] += STRLENOF( "chain-" );
1802 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1803 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1808 if ( allowed_argv[ which_argv ] == NULL ) {
1811 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1812 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1813 "\"%s\" only allowed within a URI directive.\n.",
1814 fname, lineno, argv[ 0 ] );
1819 if ( which_argv == 0 ) {
1820 rc = ldap_chain_db_init_one( &db );
1822 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1823 "underlying slapd-ldap initialization failed.\n.",
1827 lc->lc_cfg_li = db.be_private;
1830 /* TODO: add checks on what other slapd-ldap(5) args
1831 * should be put in the template; this is not quite
1832 * harmful, because attributes that shouldn't don't
1833 * get actually used, but the user should at least
1838 db.be_private = (void *)lc->lc_cfg_li;
1839 db.be_cf_ocs = lback->bi_cf_ocs;
1841 rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1843 argv[ 0 ] = save_argv0;
1845 if ( which_argv == 0 ) {
1849 db.be_private = (void *)lc->lc_cfg_li;
1850 ldap_chain_db_destroy_one( &db, NULL );
1851 lc->lc_cfg_li = NULL;
1853 if ( lc->lc_cfg_li->li_bvuri == NULL
1854 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1855 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1857 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1858 "no URI list allowed in slapo-chain.\n",
1861 goto private_destroy;
1864 if ( avl_insert( &lc->lc_lai.lai_tree,
1865 (caddr_t)lc->lc_cfg_li,
1866 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1868 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1869 "duplicate URI in slapo-chain.\n",
1872 goto private_destroy;
1889 typedef struct ldap_chain_db_apply_t {
1892 } ldap_chain_db_apply_t;
1895 ldap_chain_db_apply( void *datum, void *arg )
1897 ldapinfo_t *li = (ldapinfo_t *)datum;
1898 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1900 lca->be->be_private = (void *)li;
1902 return lca->func( lca->be, NULL );
1911 slap_overinst *on = (slap_overinst *)be->bd_info;
1912 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1917 BI_db_func *func = (&lback->bi_db_open)[ which ];
1919 if ( func != NULL && lc->lc_common_li != NULL ) {
1923 db.be_private = lc->lc_common_li;
1925 rc = func( &db, NULL );
1931 if ( lc->lc_lai.lai_tree != NULL ) {
1932 ldap_chain_db_apply_t lca;
1937 rc = avl_apply( lc->lc_lai.lai_tree,
1938 ldap_chain_db_apply, (void *)&lca,
1939 1, AVL_INORDER ) != AVL_NOMORE;
1952 slap_overinst *on = (slap_overinst *) be->bd_info;
1953 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1954 slap_mask_t monitoring;
1957 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1958 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1962 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1964 if ( lc->lc_common_li == NULL ) {
1965 void *be_private = be->be_private;
1966 ldap_chain_db_init_common( be );
1967 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1968 be->be_private = be_private;
1971 /* filter out and restore monitoring */
1972 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1973 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1974 rc = ldap_chain_db_func( be, db_open );
1975 SLAP_DBFLAGS( be ) |= monitoring;
1981 ldap_chain_db_close(
1985 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1986 #ifdef SLAP_CONFIG_DELETE
1987 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1988 #endif /* SLAP_CONFIG_DELETE */
1989 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1990 return ldap_chain_db_func( be, db_close );
1994 ldap_chain_db_destroy(
1998 slap_overinst *on = (slap_overinst *) be->bd_info;
1999 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2003 rc = ldap_chain_db_func( be, db_destroy );
2006 avl_free( lc->lc_lai.lai_tree, NULL );
2007 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
2015 * inits one instance of the slapd-ldap backend, and stores
2016 * the private info in be_private of the arg
2019 ldap_chain_db_init_common(
2022 BackendInfo *bi = be->bd_info;
2026 be->bd_info = lback;
2027 be->be_private = NULL;
2028 rc = lback->bi_db_init( be, NULL );
2032 li = (ldapinfo_t *)be->be_private;
2033 li->li_urllist_f = NULL;
2034 li->li_urllist_p = NULL;
2042 * inits one instance of the slapd-ldap backend, stores
2043 * the private info in be_private of the arg and fills
2044 * selected fields with data from the template.
2046 * NOTE: add checks about the other fields of the template,
2047 * which are ignored and SHOULD NOT be configured by the user.
2050 ldap_chain_db_init_one(
2053 slap_overinst *on = (slap_overinst *)be->bd_info;
2054 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2056 BackendInfo *bi = be->bd_info;
2061 be->bd_info = lback;
2062 be->be_private = NULL;
2063 t = lback->bi_db_init( be, NULL );
2067 li = (ldapinfo_t *)be->be_private;
2068 li->li_urllist_f = NULL;
2069 li->li_urllist_p = NULL;
2071 /* copy common data */
2072 li->li_nretries = lc->lc_common_li->li_nretries;
2073 li->li_flags = lc->lc_common_li->li_flags;
2074 li->li_version = lc->lc_common_li->li_version;
2075 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2076 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2084 ldap_chain_db_open_one(
2087 if ( SLAP_DBMONITORING( be ) ) {
2088 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
2090 if ( li->li_uri == NULL ) {
2091 ber_str2bv( "cn=Common Connections", 0, 1,
2092 &li->li_monitor_info.lmi_conn_rdn );
2093 ber_str2bv( "cn=Operations on Common Connections", 0, 1,
2094 &li->li_monitor_info.lmi_conn_rdn );
2099 li->li_monitor_info.lmi_conn_rdn.bv_len
2100 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2101 ptr = li->li_monitor_info.lmi_conn_rdn.bv_val
2102 = ch_malloc( li->li_monitor_info.lmi_conn_rdn.bv_len + 1 );
2103 ptr = lutil_strcopy( ptr, "cn=" );
2104 ptr = lutil_strcopy( ptr, li->li_uri );
2107 li->li_monitor_info.lmi_ops_rdn.bv_len
2108 = STRLENOF( "cn=Operations on " ) + strlen( li->li_uri );
2109 ptr = li->li_monitor_info.lmi_ops_rdn.bv_val
2110 = ch_malloc( li->li_monitor_info.lmi_ops_rdn.bv_len + 1 );
2111 ptr = lutil_strcopy( ptr, "cn=Operations on " );
2112 ptr = lutil_strcopy( ptr, li->li_uri );
2117 return lback->bi_db_open( be, NULL );
2120 typedef struct ldap_chain_conn_apply_t {
2123 } ldap_chain_conn_apply_t;
2126 ldap_chain_conn_apply( void *datum, void *arg )
2128 ldapinfo_t *li = (ldapinfo_t *)datum;
2129 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2131 lca->be->be_private = (void *)li;
2133 return lback->bi_connection_destroy( lca->be, lca->conn );
2137 ldap_chain_connection_destroy(
2142 slap_overinst *on = (slap_overinst *) be->bd_info;
2143 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2144 void *private = be->be_private;
2145 ldap_chain_conn_apply_t lca;
2148 be->be_private = NULL;
2151 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2152 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2153 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2154 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2155 be->be_private = private;
2160 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2162 ldap_chain_parse_ctrl(
2172 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2173 rs->sr_text = "Chaining behavior control specified multiple times";
2174 return LDAP_PROTOCOL_ERROR;
2177 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2178 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2179 return LDAP_PROTOCOL_ERROR;
2182 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2183 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2188 /* Parse the control value
2189 * ChainingBehavior ::= SEQUENCE {
2190 * resolveBehavior Behavior OPTIONAL,
2191 * continuationBehavior Behavior OPTIONAL }
2193 * Behavior :: = ENUMERATED {
2194 * chainingPreferred (0),
2195 * chainingRequired (1),
2196 * referralsPreferred (2),
2197 * referralsRequired (3) }
2200 ber = ber_init( &ctrl->ldctl_value );
2202 rs->sr_text = "internal error";
2206 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2207 /* FIXME: since the whole SEQUENCE is optional,
2208 * should we accept no enumerations at all? */
2209 if ( tag != LBER_ENUMERATED ) {
2210 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2211 return LDAP_PROTOCOL_ERROR;
2214 switch ( behavior ) {
2215 case LDAP_CHAINING_PREFERRED:
2216 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2219 case LDAP_CHAINING_REQUIRED:
2220 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2223 case LDAP_REFERRALS_PREFERRED:
2224 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2227 case LDAP_REFERRALS_REQUIRED:
2228 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2232 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2233 return LDAP_PROTOCOL_ERROR;
2236 tag = ber_peek_tag( ber, &len );
2237 if ( tag == LBER_ENUMERATED ) {
2238 tag = ber_scanf( ber, "e", &behavior );
2239 if ( tag == LBER_ERROR ) {
2240 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2241 return LDAP_PROTOCOL_ERROR;
2245 if ( tag == LBER_DEFAULT ) {
2246 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2249 switch ( behavior ) {
2250 case LDAP_CHAINING_PREFERRED:
2251 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2254 case LDAP_CHAINING_REQUIRED:
2255 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2258 case LDAP_REFERRALS_PREFERRED:
2259 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2262 case LDAP_REFERRALS_REQUIRED:
2263 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2267 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2268 return LDAP_PROTOCOL_ERROR;
2272 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2273 rs->sr_text = "Chaining behavior control: decoding error";
2274 return LDAP_PROTOCOL_ERROR;
2277 (void) ber_free( ber, 1 );
2280 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2281 ? SLAP_CONTROL_CRITICAL
2282 : SLAP_CONTROL_NONCRITICAL );
2284 return LDAP_SUCCESS;
2286 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2289 chain_initialize( void )
2293 /* Make sure we don't exceed the bits reserved for userland */
2294 config_check_userland( CH_LAST );
2296 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2297 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2298 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2299 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2300 if ( rc != LDAP_SUCCESS ) {
2301 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2302 "unable to register chaining behavior control: %d.\n",
2306 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2308 ldapchain.on_bi.bi_type = "chain";
2309 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2310 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2311 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2312 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2313 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2315 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2317 ldapchain.on_response = ldap_chain_response;
2319 ldapchain.on_bi.bi_cf_ocs = chainocs;
2321 rc = config_register_schema( chaincfg, chainocs );
2326 return overlay_register( &ldapchain );