1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2006 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>
31 #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;
66 static BackendInfo *lback;
68 typedef struct ldap_chain_t {
70 * A "template" ldapinfo_t gets all common configuration items;
71 * then, for each configured URI, an entry is created in the tree;
72 * all the specific configuration items get in the current URI
75 * Then, for each referral, extract the URI and lookup the
76 * related structure. If configured to do so, allow URIs
77 * not found in the structure to create a temporary one
78 * that chains anonymously; maybe it can also be added to
79 * the tree? Should be all configurable.
82 /* "common" configuration info (anything occurring before an "uri") */
83 ldapinfo_t *lc_common_li;
85 /* current configuration info */
86 ldapinfo_t *lc_cfg_li;
88 /* tree of configured[/generated?] "uri" info */
89 ldap_avl_info_t lc_lai;
91 /* max depth in nested referrals chaining */
95 #define LDAP_CHAIN_F_NONE (0x00U)
96 #define LDAP_CHAIN_F_CHAINING (0x01U)
97 #define LDAP_CHAIN_F_CACHE_URI (0x02U)
98 #define LDAP_CHAIN_F_RETURN_ERR (0x04U)
100 #define LDAP_CHAIN_ISSET(lc, f) ( ( (lc)->lc_flags & (f) ) == (f) )
101 #define LDAP_CHAIN_CHAINING( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
102 #define LDAP_CHAIN_CACHE_URI( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
103 #define LDAP_CHAIN_RETURN_ERR( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
105 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
106 LDAPControl lc_chaining_ctrl;
107 char lc_chaining_ctrlflag;
108 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
111 static int ldap_chain_db_init_common( BackendDB *be );
112 static int ldap_chain_db_init_one( BackendDB *be );
113 #define ldap_chain_db_open_one(be) (lback)->bi_db_open( (be) )
114 #define ldap_chain_db_close_one(be) (0)
115 #define ldap_chain_db_destroy_one(be) (lback)->bi_db_destroy( (be) )
117 typedef struct ldap_chain_cb_t {
118 ldap_chain_status_t lb_status;
139 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
141 chaining_control_add(
144 LDAPControl ***oldctrlsp )
146 LDAPControl **ctrls = NULL;
149 *oldctrlsp = op->o_ctrls;
151 /* default chaining control not defined */
152 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
156 /* already present */
157 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
161 /* FIXME: check other incompatibilities */
163 /* add to other controls */
165 for ( c = 0; op->o_ctrls[ c ]; c++ )
169 ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
170 ctrls[ 0 ] = &lc->lc_chaining_ctrl;
172 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
173 ctrls[ c + 1 ] = op->o_ctrls[ c ];
176 ctrls[ c + 1 ] = NULL;
180 op->o_chaining = lc->lc_chaining_ctrlflag;
186 chaining_control_remove(
188 LDAPControl ***oldctrlsp )
190 LDAPControl **oldctrls = *oldctrlsp;
192 /* we assume that the first control is the chaining control
193 * added by the chain overlay, so it's the only one we explicitly
195 if ( op->o_ctrls != oldctrls ) {
196 assert( op->o_ctrls != NULL );
197 assert( op->o_ctrls[ 0 ] != NULL );
202 op->o_ctrls = oldctrls;
209 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
212 ldap_chain_uri_cmp( const void *c1, const void *c2 )
214 const ldapinfo_t *li1 = (const ldapinfo_t *)c1;
215 const ldapinfo_t *li2 = (const ldapinfo_t *)c2;
217 assert( li1->li_bvuri != NULL );
218 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
219 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
221 assert( li2->li_bvuri != NULL );
222 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
223 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
225 /* If local DNs don't match, it is definitely not a match */
226 return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
230 ldap_chain_uri_dup( void *c1, void *c2 )
232 ldapinfo_t *li1 = (ldapinfo_t *)c1;
233 ldapinfo_t *li2 = (ldapinfo_t *)c2;
235 assert( li1->li_bvuri != NULL );
236 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
237 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
239 assert( li2->li_bvuri != NULL );
240 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
241 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
243 /* Cannot have more than one shared session with same DN */
244 if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
252 * Search specific response that strips entryDN from entries
255 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
257 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
259 assert( op->o_tag == LDAP_REQ_SEARCH );
261 /* if in error, don't proceed any further */
262 if ( lb->lb_status == LDAP_CH_ERR ) {
266 if ( rs->sr_type == REP_SEARCH ) {
267 Attribute **ap = &rs->sr_entry->e_attrs;
269 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
270 /* will be generated later by frontend
271 * (a cleaner solution would be that
272 * the frontend checks if it already exists */
273 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
280 /* there SHOULD be one only! */
285 /* tell the frontend not to add generated
286 * operational attributes */
287 rs->sr_flags |= REP_NO_OPERATIONALS;
289 return SLAP_CB_CONTINUE;
291 } else if ( rs->sr_type == REP_SEARCHREF ) {
292 /* if we get it here, it means the library was unable
293 * to chase the referral... */
294 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
295 rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
298 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
299 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
300 switch ( get_continuationBehavior( op ) ) {
301 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
302 lb->lb_status = LDAP_CH_ERR;
303 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
309 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
310 return SLAP_CB_CONTINUE;
312 } else if ( rs->sr_type == REP_RESULT ) {
313 if ( rs->sr_err == LDAP_REFERRAL
314 && lb->lb_depth < lb->lb_lc->lc_max_depth
315 && rs->sr_ref != NULL )
317 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
320 /* back-ldap tried to send result */
321 lb->lb_status = LDAP_CH_RES;
328 * Dummy response that simply traces if back-ldap tried to send
329 * anything to the client
332 ldap_chain_cb_response( Operation *op, SlapReply *rs )
334 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
336 /* if in error, don't proceed any further */
337 if ( lb->lb_status == LDAP_CH_ERR ) {
341 if ( rs->sr_type == REP_RESULT ) {
343 switch ( rs->sr_err ) {
344 case LDAP_COMPARE_TRUE:
345 case LDAP_COMPARE_FALSE:
346 if ( op->o_tag != LDAP_REQ_COMPARE ) {
352 lb->lb_status = LDAP_CH_RES;
356 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
357 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
361 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
362 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
363 switch ( get_continuationBehavior( op ) ) {
364 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
365 lb->lb_status = LDAP_CH_ERR;
366 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
372 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
379 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
381 /* strip the entryDN attribute, but keep returning results */
382 (void)ldap_chain_cb_search_response( op, rs );
385 return SLAP_CB_CONTINUE;
396 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
397 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
398 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
399 ldapinfo_t li = { 0 }, *lip = NULL;
400 struct berval bvuri[ 2 ] = { { 0 } };
402 /* NOTE: returned if ref is empty... */
406 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
407 LDAPControl **ctrls = NULL;
409 (void)chaining_control_add( lc, op, &ctrls );
410 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
414 for ( ; !BER_BVISNULL( ref ); ref++ ) {
419 /* We're setting the URI of the first referral;
420 * what if there are more?
422 Document: draft-ietf-ldapbis-protocol-27.txt
426 If the client wishes to progress the operation, it MUST follow the
427 referral by contacting one of the supported services. If multiple
428 URIs are present, the client assumes that any supported URI may be
429 used to progress the operation.
431 * so we actually need to follow exactly one,
432 * and we can assume any is fine.
435 /* parse reference and use
436 * proto://[host][:port]/ only */
437 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
438 if ( rc != LDAP_URL_SUCCESS ) {
444 /* remove DN essentially because later on
445 * ldap_initialize() will parse the URL
446 * as a comma-separated URL list */
447 save_dn = srv->lud_dn;
449 srv->lud_scope = LDAP_SCOPE_DEFAULT;
450 li.li_uri = ldap_url_desc2str( srv );
451 srv->lud_dn = save_dn;
452 ldap_free_urldesc( srv );
454 if ( li.li_uri == NULL ) {
460 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
462 /* Searches for a ldapinfo in the avl tree */
463 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
464 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
465 (caddr_t)&li, ldap_chain_uri_cmp );
466 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
469 op->o_bd->be_private = (void *)lip;
472 rc = ldap_chain_db_init_one( op->o_bd );
476 lip = (ldapinfo_t *)op->o_bd->be_private;
477 lip->li_uri = li.li_uri;
478 lip->li_bvuri = bvuri;
479 rc = ldap_chain_db_open_one( op->o_bd );
481 (void)ldap_chain_db_destroy_one( op->o_bd );
485 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
486 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
487 if ( avl_insert( &lc->lc_lai.lai_tree,
488 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
490 /* someone just inserted another;
491 * don't bother, use this and then
495 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
503 lb->lb_depth = depth + 1;
507 /* note the first error */
508 if ( first_rc == -1 ) {
513 ldap_memfree( li.li_uri );
518 lip->li_bvuri = NULL;
519 (void)ldap_chain_db_close_one( op->o_bd );
520 (void)ldap_chain_db_destroy_one( op->o_bd );
523 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
528 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
529 (void)chaining_control_remove( op, &ctrls );
530 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
532 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
547 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
548 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
549 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
550 ldapinfo_t li = { 0 }, *lip = NULL;
551 struct berval bvuri[ 2 ] = { { 0 } };
553 struct berval odn = op->o_req_dn,
554 ondn = op->o_req_ndn;
555 slap_response *save_response = op->o_callback->sc_response;
560 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
561 LDAPControl **ctrls = NULL;
563 (void)chaining_control_add( lc, op, &ctrls );
564 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
566 rs->sr_type = REP_SEARCH;
568 op->o_callback->sc_response = ldap_chain_cb_search_response;
570 /* if we parse the URI then by no means
571 * we can cache stuff or reuse connections,
572 * because in back-ldap there's no caching
573 * based on the URI value, which is supposed
574 * to be set once for all (correct?) */
576 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
581 /* parse reference and use
582 * proto://[host][:port]/ only */
583 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
584 if ( rc != LDAP_URL_SUCCESS ) {
586 rs->sr_err = LDAP_OTHER;
590 /* remove DN essentially because later on
591 * ldap_initialize() will parse the URL
592 * as a comma-separated URL list */
593 save_dn = srv->lud_dn;
595 srv->lud_scope = LDAP_SCOPE_DEFAULT;
596 li.li_uri = ldap_url_desc2str( srv );
597 if ( li.li_uri != NULL ) {
598 ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
600 ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
604 srv->lud_dn = save_dn;
605 ldap_free_urldesc( srv );
607 if ( li.li_uri == NULL ) {
609 rs->sr_err = LDAP_OTHER;
613 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
615 /* Searches for a ldapinfo in the avl tree */
616 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
617 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
618 (caddr_t)&li, ldap_chain_uri_cmp );
619 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
622 op->o_bd->be_private = (void *)lip;
625 /* if none is found, create a temporary... */
626 rc = ldap_chain_db_init_one( op->o_bd );
630 lip = (ldapinfo_t *)op->o_bd->be_private;
631 lip->li_uri = li.li_uri;
632 lip->li_bvuri = bvuri;
633 rc = ldap_chain_db_open_one( op->o_bd );
635 (void)ldap_chain_db_destroy_one( op->o_bd );
639 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
640 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
641 if ( avl_insert( &lc->lc_lai.lai_tree,
642 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
644 /* someone just inserted another;
645 * don't bother, use this and then
649 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
656 lb->lb_op_f = lback->bi_op_search;
657 lb->lb_depth = depth + 1;
659 /* FIXME: should we also copy filter and scope?
660 * according to RFC3296, no */
661 rc = lback->bi_op_search( op, rs );
662 if ( first_rc == -1 ) {
667 ldap_memfree( li.li_uri );
670 op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
671 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
675 lip->li_bvuri = NULL;
676 (void)ldap_chain_db_close_one( op->o_bd );
677 (void)ldap_chain_db_destroy_one( op->o_bd );
680 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
687 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
688 (void)chaining_control_remove( op, &ctrls );
689 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
692 op->o_req_ndn = ondn;
693 op->o_callback->sc_response = save_response;
694 rs->sr_type = REP_SEARCHREF;
697 if ( rc != LDAP_SUCCESS ) {
698 /* couldn't chase any of the referrals */
699 if ( first_rc != -1 ) {
703 rc = SLAP_CB_CONTINUE;
711 ldap_chain_response( Operation *op, SlapReply *rs )
713 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
714 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
715 void *private = op->o_bd->be_private;
716 ldap_chain_cb_t lb = { 0 };
717 slap_callback *sc = op->o_callback,
722 struct berval ndn = op->o_ndn;
724 int sr_err = rs->sr_err;
725 slap_reply_t sr_type = rs->sr_type;
726 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
727 slap_mask_t chain_mask = 0;
728 ber_len_t chain_shift = 0;
729 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
731 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
732 return SLAP_CB_CONTINUE;
735 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
736 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
737 switch ( get_resolveBehavior( op ) ) {
738 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
739 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
740 return SLAP_CB_CONTINUE;
743 chain_mask = SLAP_CH_RESOLVE_MASK;
744 chain_shift = SLAP_CH_RESOLVE_SHIFT;
748 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
749 switch ( get_continuationBehavior( op ) ) {
750 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
751 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
752 return SLAP_CB_CONTINUE;
755 chain_mask = SLAP_CH_CONTINUATION_MASK;
756 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
760 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
763 * TODO: add checks on who/when chain operations; e.g.:
764 * a) what identities are authorized
765 * b) what request DN (e.g. only chain requests rooted at <DN>)
766 * c) what referral URIs
767 * d) what protocol scheme (e.g. only ldaps://)
771 matched = rs->sr_matched;
772 rs->sr_matched = NULL;
776 /* we need this to know if back-ldap returned any result */
778 sc2.sc_private = &lb;
779 sc2.sc_response = ldap_chain_cb_response;
780 op->o_callback = &sc2;
782 /* Chaining can be performed by a privileged user on behalf
783 * of normal users, using the ProxyAuthz control, by exploiting
784 * the identity assertion feature of back-ldap; see idassert-*
785 * directives in slapd-ldap(5).
787 * FIXME: the idassert-authcDN is one, will it be fine regardless
788 * of the URI we obtain from the referral?
791 switch ( op->o_tag ) {
792 case LDAP_REQ_BIND: {
793 struct berval rndn = op->o_req_ndn;
794 Connection *conn = op->o_conn;
796 /* FIXME: can we really get a referral for binds? */
797 op->o_req_ndn = slap_empty_bv;
799 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
800 op->o_req_ndn = rndn;
806 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
809 case LDAP_REQ_DELETE:
810 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
813 case LDAP_REQ_MODRDN:
814 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
817 case LDAP_REQ_MODIFY:
818 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
821 case LDAP_REQ_COMPARE:
822 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
823 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
828 case LDAP_REQ_SEARCH:
829 if ( rs->sr_type == REP_SEARCHREF ) {
830 rc = ldap_chain_search( op, rs, ref, 0 );
833 /* we might get here before any database actually
834 * performed a search; in those cases, we need
835 * to check limits, to make sure safe defaults
837 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
838 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
841 rc = SLAP_CB_CONTINUE;
846 case LDAP_REQ_EXTENDED:
847 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
848 /* FIXME: ldap_back_extended() by design
849 * doesn't send result; frontend is expected
851 /* FIXME: what about chaining? */
852 if ( rc != SLAPD_ABANDON ) {
853 send_ldap_extended( op, rs );
856 lb.lb_status = LDAP_CH_RES;
860 rc = SLAP_CB_CONTINUE;
870 /* slapd-ldap sent response */
871 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
872 /* FIXME: should we send response? */
873 Debug( LDAP_DEBUG_ANY,
874 "%s: ldap_chain_response: "
875 "overlay should have sent result.\n",
876 op->o_log_prefix, 0, 0 );
881 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
882 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
886 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
887 case LDAP_CHAINING_REQUIRED:
889 op->o_callback = NULL;
890 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
891 "operation cannot be completed without chaining" );
895 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
896 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
898 rs->sr_type = sr_type;
901 rc = SLAP_CB_CONTINUE;
903 rs->sr_type = sr_type;
904 rs->sr_matched = matched;
907 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
910 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
913 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
914 op->o_callback = NULL;
915 rc = rs->sr_err = slap_map_api2result( rs );
916 send_ldap_result( op, rs );
921 rs->sr_type = sr_type;
922 rs->sr_matched = matched;
924 op->o_bd->be_private = private;
931 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
933 ldap_chain_parse_ctrl(
939 str2chain( const char *s )
941 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
942 return LDAP_CHAINING_PREFERRED;
944 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
945 return LDAP_CHAINING_REQUIRED;
947 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
948 return LDAP_REFERRALS_PREFERRED;
950 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
951 return LDAP_REFERRALS_REQUIRED;
956 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
971 static ConfigDriver chain_cf_gen;
972 static ConfigCfAdd chain_cfadd;
973 static ConfigLDAPadd chain_ldadd;
975 static ConfigTable chaincfg[] = {
976 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
977 { "chain-chaining", "args",
978 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
979 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
980 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
981 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
982 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
983 { "chain-cache-uri", "TRUE/FALSE",
984 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
985 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
986 "DESC 'Enables caching of URIs not present in configuration' "
987 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
988 { "chain-max-depth", "args",
989 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
990 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
991 "DESC 'max referral depth' "
993 "EQUALITY integerMatch "
994 "SINGLE-VALUE )", NULL, NULL },
995 { "chain-return-error", "TRUE/FALSE",
996 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
997 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
998 "DESC 'Errors are returned instead of the original referral' "
999 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1000 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1003 static ConfigOCs chainocs[] = {
1004 { "( OLcfgOvOc:3.1 "
1005 "NAME 'olcChainConfig' "
1006 "DESC 'Chain configuration' "
1007 "SUP olcOverlayConfig "
1009 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1010 "olcChainingBehavior $ "
1011 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1012 "olcChainCacheURI $ "
1013 "olcChainMaxReferralDepth $ "
1014 "olcChainReturnError "
1016 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1017 { "( OLcfgOvOc:3.2 "
1018 "NAME 'olcChainDatabase' "
1019 "DESC 'Chain remote server configuration' "
1021 Cft_Misc, chaincfg, chain_ldadd },
1026 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1033 AttributeDescription *ad = NULL;
1039 if ( p->ce_type != Cft_Overlay
1041 || p->ce_bi->bi_cf_ocs != chainocs )
1043 return LDAP_CONSTRAINT_VIOLATION;
1046 on = (slap_overinst *)p->ce_bi;
1047 lc = (ldap_chain_t *)on->on_bi.bi_private;
1049 assert( ca->be == NULL );
1050 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1052 ca->be->bd_info = (BackendInfo *)on;
1054 rc = slap_str2ad( "olcDbURI", &ad, &text );
1055 assert( rc == LDAP_SUCCESS );
1057 at = attr_find( e->e_attrs, ad );
1058 if ( lc->lc_common_li == NULL && at != NULL ) {
1059 /* FIXME: we should generate an empty default entry
1060 * if none is supplied */
1061 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1062 "first underlying database \"%s\" "
1063 "cannot contain attribute \"%s\".\n",
1064 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1065 rc = LDAP_CONSTRAINT_VIOLATION;
1068 } else if ( lc->lc_common_li != NULL && at == NULL ) {
1069 /* FIXME: we should generate an empty default entry
1070 * if none is supplied */
1071 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1072 "subsequent underlying database \"%s\" "
1073 "must contain attribute \"%s\".\n",
1074 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1075 rc = LDAP_CONSTRAINT_VIOLATION;
1079 if ( lc->lc_common_li == NULL ) {
1080 rc = ldap_chain_db_init_common( ca->be );
1083 rc = ldap_chain_db_init_one( ca->be );
1087 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1088 "unable to init %sunderlying database \"%s\".\n",
1089 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1090 return LDAP_CONSTRAINT_VIOLATION;
1093 li = ca->be->be_private;
1095 if ( lc->lc_common_li == NULL ) {
1096 lc->lc_common_li = li;
1098 } else if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1099 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1101 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1102 "database \"%s\" insert failed.\n",
1103 e->e_name.bv_val, 0, 0 );
1104 rc = LDAP_CONSTRAINT_VIOLATION;
1109 if ( rc != LDAP_SUCCESS ) {
1110 (void)ldap_chain_db_destroy_one( ca->be );
1118 typedef struct ldap_chain_cfadd_apply_t {
1124 } ldap_chain_cfadd_apply_t;
1127 ldap_chain_cfadd_apply( void *datum, void *arg )
1129 ldapinfo_t *li = (ldapinfo_t *)datum;
1130 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1134 /* FIXME: should not hardcode "olcDatabase" here */
1135 bv.bv_len = snprintf( lca->ca->msg, sizeof( lca->ca->msg ),
1136 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1137 bv.bv_val = lca->ca->msg;
1139 lca->ca->be->be_private = (void *)li;
1140 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1141 &bv, lback->bi_cf_ocs, &chainocs[1] );
1149 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1151 CfEntryInfo *pe = p->e_private;
1152 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1153 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1154 void *priv = (void *)ca->be->be_private;
1156 if ( lback->bi_cf_ocs ) {
1157 ldap_chain_cfadd_apply_t lca = { 0 };
1165 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1167 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1168 &lca, 1, AVL_INORDER );
1170 ca->be->be_private = priv;
1176 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1177 static slap_verbmasks chaining_mode[] = {
1178 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1179 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1180 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1181 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1184 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1187 chain_cf_gen( ConfigArgs *c )
1189 slap_overinst *on = (slap_overinst *)c->bi;
1190 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1194 if ( c->op == SLAP_CONFIG_EMIT ) {
1196 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1198 struct berval resolve = BER_BVNULL,
1199 continuation = BER_BVNULL;
1201 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1205 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1206 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1208 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1210 + STRLENOF( "continuation=" ) + continuation.bv_len;
1211 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1212 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1213 "resolve=%s continuation=%s",
1214 resolve.bv_val, continuation.bv_val );
1216 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1217 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1218 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1219 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1220 " critical", STRLENOF( " critical" ) + 1 );
1221 c->value_bv.bv_len += STRLENOF( " critical" );
1226 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1229 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1233 c->value_int = lc->lc_max_depth;
1237 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1246 } else if ( c->op == LDAP_MOD_DELETE ) {
1252 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1260 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1271 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1272 char **argv = c->argv;
1274 BerElementBuffer berbuf;
1275 BerElement *ber = (BerElement *)&berbuf;
1279 Operation op = { 0 };
1280 SlapReply rs = { 0 };
1282 lc->lc_chaining_ctrlflag = 0;
1284 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1285 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1286 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1287 if ( resolve == -1 ) {
1288 Debug( LDAP_DEBUG_ANY, "%s: "
1289 "illegal <resolve> value %s "
1290 "in \"chain-chaining>\".\n",
1291 c->log, argv[ 0 ], 0 );
1295 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1296 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1297 if ( continuation == -1 ) {
1298 Debug( LDAP_DEBUG_ANY, "%s: "
1299 "illegal <continuation> value %s "
1300 "in \"chain-chaining\".\n",
1301 c->log, argv[ 0 ], 0 );
1305 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1309 Debug( LDAP_DEBUG_ANY, "%s: "
1310 "unknown option in \"chain-chaining\".\n",
1316 if ( resolve != -1 || continuation != -1 ) {
1319 if ( resolve == -1 ) {
1321 resolve = SLAP_CHAINING_DEFAULT;
1324 ber_init2( ber, NULL, LBER_USE_DER );
1326 err = ber_printf( ber, "{e" /* } */, resolve );
1329 Debug( LDAP_DEBUG_ANY, "%s: "
1330 "chaining behavior control encoding error!\n",
1335 if ( continuation > -1 ) {
1336 err = ber_printf( ber, "e", continuation );
1339 Debug( LDAP_DEBUG_ANY, "%s: "
1340 "chaining behavior control encoding error!\n",
1346 err = ber_printf( ber, /* { */ "N}" );
1349 Debug( LDAP_DEBUG_ANY, "%s: "
1350 "chaining behavior control encoding error!\n",
1355 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1356 exit( EXIT_FAILURE );
1360 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1363 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1364 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1366 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1368 Debug( LDAP_DEBUG_ANY, "%s: "
1369 "unable to parse chaining control%s%s.\n",
1370 c->log, rs.sr_text ? ": " : "",
1371 rs.sr_text ? rs.sr_text : "" );
1375 lc->lc_chaining_ctrlflag = op.o_chaining;
1377 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1380 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1381 Debug( LDAP_DEBUG_ANY, "%s: "
1382 "\"chaining\" control unsupported (ignored).\n",
1384 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1388 if ( c->value_int ) {
1389 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1391 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1396 if ( c->value_int < 0 ) {
1397 snprintf( c->msg, sizeof( c->msg ),
1398 "<%s> invalid max referral depth %d",
1399 c->argv[0], c->value_int );
1400 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1401 c->log, c->msg, 0 );
1405 lc->lc_max_depth = c->value_int;
1408 if ( c->value_int ) {
1409 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1411 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1426 slap_overinst *on = (slap_overinst *)be->bd_info;
1427 ldap_chain_t *lc = NULL;
1429 if ( lback == NULL ) {
1430 lback = backend_info( "ldap" );
1432 if ( lback == NULL ) {
1437 lc = ch_malloc( sizeof( ldap_chain_t ) );
1441 memset( lc, 0, sizeof( ldap_chain_t ) );
1442 lc->lc_max_depth = 1;
1443 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1445 on->on_bi.bi_private = (void *)lc;
1451 ldap_chain_db_config(
1458 slap_overinst *on = (slap_overinst *)be->bd_info;
1459 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1461 int rc = SLAP_CONF_UNKNOWN;
1463 if ( lc->lc_common_li == NULL ) {
1464 void *be_private = be->be_private;
1465 ldap_chain_db_init_common( be );
1466 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1467 be->be_private = be_private;
1470 /* Something for the chain database? */
1471 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1472 char *save_argv0 = argv[ 0 ];
1473 BackendInfo *bd_info = be->bd_info;
1474 void *be_private = be->be_private;
1475 ConfigOCs *be_cf_ocs = be->be_cf_ocs;
1476 static char *allowed_argv[] = {
1477 /* special: put URI here, so in the meanwhile
1478 * it detects whether a new URI is being provided */
1484 /* FIXME: maybe rebind-as-user should be allowed
1485 * only within known URIs... */
1492 int which_argv = -1;
1494 argv[ 0 ] += STRLENOF( "chain-" );
1496 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1497 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1502 if ( allowed_argv[ which_argv ] == NULL ) {
1505 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1506 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1507 "\"%s\" only allowed within a URI directive.\n.",
1508 fname, lineno, argv[ 0 ] );
1513 if ( which_argv == 0 ) {
1514 rc = ldap_chain_db_init_one( be );
1516 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1517 "underlying slapd-ldap initialization failed.\n.",
1521 lc->lc_cfg_li = be->be_private;
1524 /* TODO: add checks on what other slapd-ldap(5) args
1525 * should be put in the template; this is not quite
1526 * harmful, because attributes that shouldn't don't
1527 * get actually used, but the user should at least
1531 be->bd_info = lback;
1532 be->be_private = (void *)lc->lc_cfg_li;
1533 be->be_cf_ocs = lback->bi_cf_ocs;
1535 rc = config_generic_wrapper( be, fname, lineno, argc, argv );
1537 argv[ 0 ] = save_argv0;
1538 be->be_cf_ocs = be_cf_ocs;
1539 be->be_private = be_private;
1540 be->bd_info = bd_info;
1542 if ( which_argv == 0 ) {
1548 db.be_private = (void *)lc->lc_cfg_li;
1549 ldap_chain_db_destroy_one( &db );
1550 lc->lc_cfg_li = NULL;
1553 if ( lc->lc_cfg_li->li_bvuri == NULL
1554 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1555 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1557 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1558 "no URI list allowed in slapo-chain.\n",
1561 goto private_destroy;
1564 if ( avl_insert( &lc->lc_lai.lai_tree,
1565 (caddr_t)lc->lc_cfg_li,
1566 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1568 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1569 "duplicate URI in slapo-chain.\n",
1572 goto private_destroy;
1589 typedef struct ldap_chain_db_apply_t {
1592 } ldap_chain_db_apply_t;
1595 ldap_chain_db_apply( void *datum, void *arg )
1597 ldapinfo_t *li = (ldapinfo_t *)datum;
1598 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1600 lca->be->be_private = (void *)li;
1602 return lca->func( lca->be );
1611 slap_overinst *on = (slap_overinst *)be->bd_info;
1612 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1617 BI_db_func *func = (&lback->bi_db_open)[ which ];
1619 if ( func != NULL && lc->lc_common_li != NULL ) {
1623 db.be_private = lc->lc_common_li;
1631 if ( lc->lc_lai.lai_tree != NULL ) {
1632 ldap_chain_db_apply_t lca;
1637 rc = avl_apply( lc->lc_lai.lai_tree,
1638 ldap_chain_db_apply, (void *)&lca,
1639 1, AVL_INORDER ) != AVL_NOMORE;
1651 slap_overinst *on = (slap_overinst *) be->bd_info;
1652 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1654 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1657 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1661 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1663 if ( lc->lc_common_li == NULL ) {
1664 void *be_private = be->be_private;
1665 ldap_chain_db_init_common( be );
1666 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1667 be->be_private = be_private;
1670 return ldap_chain_db_func( be, db_open );
1674 ldap_chain_db_close(
1677 return ldap_chain_db_func( be, db_close );
1681 ldap_chain_db_destroy(
1684 slap_overinst *on = (slap_overinst *) be->bd_info;
1685 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1689 rc = ldap_chain_db_func( be, db_destroy );
1692 avl_free( lc->lc_lai.lai_tree, NULL );
1693 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1701 * inits one instance of the slapd-ldap backend, and stores
1702 * the private info in be_private of the arg
1705 ldap_chain_db_init_common(
1708 BackendInfo *bi = be->bd_info;
1712 be->bd_info = lback;
1713 be->be_private = NULL;
1714 t = lback->bi_db_init( be );
1718 li = (ldapinfo_t *)be->be_private;
1719 li->li_urllist_f = NULL;
1720 li->li_urllist_p = NULL;
1727 * inits one instance of the slapd-ldap backend, stores
1728 * the private info in be_private of the arg and fills
1729 * selected fields with data from the template.
1731 * NOTE: add checks about the other fields of the template,
1732 * which are ignored and SHOULD NOT be configured by the user.
1735 ldap_chain_db_init_one(
1738 slap_overinst *on = (slap_overinst *)be->bd_info;
1739 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1741 BackendInfo *bi = be->bd_info;
1746 be->bd_info = lback;
1747 be->be_private = NULL;
1748 t = lback->bi_db_init( be );
1752 li = (ldapinfo_t *)be->be_private;
1753 li->li_urllist_f = NULL;
1754 li->li_urllist_p = NULL;
1756 /* copy common data */
1757 li->li_nretries = lc->lc_common_li->li_nretries;
1758 li->li_flags = lc->lc_common_li->li_flags;
1759 li->li_version = lc->lc_common_li->li_version;
1760 for ( t = 0; t < LDAP_BACK_OP_LAST; t++ ) {
1761 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
1768 typedef struct ldap_chain_conn_apply_t {
1771 } ldap_chain_conn_apply_t;
1774 ldap_chain_conn_apply( void *datum, void *arg )
1776 ldapinfo_t *li = (ldapinfo_t *)datum;
1777 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
1779 lca->be->be_private = (void *)li;
1781 return lback->bi_connection_destroy( lca->be, lca->conn );
1785 ldap_chain_connection_destroy(
1790 slap_overinst *on = (slap_overinst *) be->bd_info;
1791 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1792 void *private = be->be_private;
1793 ldap_chain_conn_apply_t lca;
1796 be->be_private = NULL;
1799 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
1800 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
1801 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
1802 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
1803 be->be_private = private;
1808 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1810 ldap_chain_parse_ctrl(
1820 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1821 rs->sr_text = "Chaining behavior control specified multiple times";
1822 return LDAP_PROTOCOL_ERROR;
1825 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1826 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1827 return LDAP_PROTOCOL_ERROR;
1830 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1831 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1836 /* Parse the control value
1837 * ChainingBehavior ::= SEQUENCE {
1838 * resolveBehavior Behavior OPTIONAL,
1839 * continuationBehavior Behavior OPTIONAL }
1841 * Behavior :: = ENUMERATED {
1842 * chainingPreferred (0),
1843 * chainingRequired (1),
1844 * referralsPreferred (2),
1845 * referralsRequired (3) }
1848 ber = ber_init( &ctrl->ldctl_value );
1850 rs->sr_text = "internal error";
1854 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1855 /* FIXME: since the whole SEQUENCE is optional,
1856 * should we accept no enumerations at all? */
1857 if ( tag != LBER_ENUMERATED ) {
1858 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1859 return LDAP_PROTOCOL_ERROR;
1862 switch ( behavior ) {
1863 case LDAP_CHAINING_PREFERRED:
1864 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1867 case LDAP_CHAINING_REQUIRED:
1868 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1871 case LDAP_REFERRALS_PREFERRED:
1872 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1875 case LDAP_REFERRALS_REQUIRED:
1876 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1880 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1881 return LDAP_PROTOCOL_ERROR;
1884 tag = ber_peek_tag( ber, &len );
1885 if ( tag == LBER_ENUMERATED ) {
1886 tag = ber_scanf( ber, "e", &behavior );
1887 if ( tag == LBER_ERROR ) {
1888 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1889 return LDAP_PROTOCOL_ERROR;
1893 if ( tag == LBER_DEFAULT ) {
1894 mode |= SLAP_CH_CONTINUATION_DEFAULT;
1897 switch ( behavior ) {
1898 case LDAP_CHAINING_PREFERRED:
1899 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1902 case LDAP_CHAINING_REQUIRED:
1903 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1906 case LDAP_REFERRALS_PREFERRED:
1907 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1910 case LDAP_REFERRALS_REQUIRED:
1911 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1915 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1916 return LDAP_PROTOCOL_ERROR;
1920 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1921 rs->sr_text = "Chaining behavior control: decoding error";
1922 return LDAP_PROTOCOL_ERROR;
1925 (void) ber_free( ber, 1 );
1928 op->o_chaining = mode | ( ctrl->ldctl_iscritical
1929 ? SLAP_CONTROL_CRITICAL
1930 : SLAP_CONTROL_NONCRITICAL );
1932 return LDAP_SUCCESS;
1934 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1936 static slap_overinst ldapchain;
1939 chain_initialize( void )
1943 /* Make sure we don't exceed the bits reserved for userland */
1944 config_check_userland( CH_LAST );
1946 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1947 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1948 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1949 ldap_chain_parse_ctrl, &sc_chainingBehavior );
1950 if ( rc != LDAP_SUCCESS ) {
1951 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1952 "unable to register chaining behavior control: %d.\n",
1956 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1958 ldapchain.on_bi.bi_type = "chain";
1959 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1960 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1961 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1962 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
1963 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1965 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1967 ldapchain.on_response = ldap_chain_response;
1969 ldapchain.on_bi.bi_cf_ocs = chainocs;
1971 rc = config_register_schema( chaincfg, chainocs );
1976 return overlay_register( &ldapchain );