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;
559 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
560 LDAPControl **ctrls = NULL;
562 (void)chaining_control_add( lc, op, &ctrls );
563 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
565 rs->sr_type = REP_SEARCH;
567 op->o_callback->sc_response = ldap_chain_cb_search_response;
569 /* if we parse the URI then by no means
570 * we can cache stuff or reuse connections,
571 * because in back-ldap there's no caching
572 * based on the URI value, which is supposed
573 * to be set once for all (correct?) */
575 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
580 /* parse reference and use
581 * proto://[host][:port]/ only */
582 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
583 if ( rc != LDAP_URL_SUCCESS ) {
585 rs->sr_err = LDAP_OTHER;
589 /* remove DN essentially because later on
590 * ldap_initialize() will parse the URL
591 * as a comma-separated URL list */
592 save_dn = srv->lud_dn;
594 srv->lud_scope = LDAP_SCOPE_DEFAULT;
595 li.li_uri = ldap_url_desc2str( srv );
596 if ( li.li_uri != NULL ) {
597 ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
599 ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
603 srv->lud_dn = save_dn;
604 ldap_free_urldesc( srv );
606 if ( li.li_uri == NULL ) {
608 rs->sr_err = LDAP_OTHER;
612 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
614 /* Searches for a ldapinfo in the avl tree */
615 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
616 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
617 (caddr_t)&li, ldap_chain_uri_cmp );
618 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
621 op->o_bd->be_private = (void *)lip;
624 /* if none is found, create a temporary... */
625 rc = ldap_chain_db_init_one( op->o_bd );
629 lip = (ldapinfo_t *)op->o_bd->be_private;
630 lip->li_uri = li.li_uri;
631 lip->li_bvuri = bvuri;
632 rc = ldap_chain_db_open_one( op->o_bd );
634 (void)ldap_chain_db_destroy_one( op->o_bd );
638 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
639 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
640 if ( avl_insert( &lc->lc_lai.lai_tree,
641 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
643 /* someone just inserted another;
644 * don't bother, use this and then
648 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
655 lb->lb_op_f = lback->bi_op_search;
656 lb->lb_depth = depth + 1;
658 /* FIXME: should we also copy filter and scope?
659 * according to RFC3296, no */
660 rc = lback->bi_op_search( op, rs );
663 ldap_memfree( li.li_uri );
666 op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
667 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
671 lip->li_bvuri = NULL;
672 (void)ldap_chain_db_close_one( op->o_bd );
673 (void)ldap_chain_db_destroy_one( op->o_bd );
676 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
683 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
684 (void)chaining_control_remove( op, &ctrls );
685 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
688 op->o_req_ndn = ondn;
689 op->o_callback->sc_response = save_response;
690 rs->sr_type = REP_SEARCHREF;
693 if ( rc != LDAP_SUCCESS ) {
694 /* couldn't chase any of the referrals */
695 rc = SLAP_CB_CONTINUE;
702 ldap_chain_response( Operation *op, SlapReply *rs )
704 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
705 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
706 void *private = op->o_bd->be_private;
707 ldap_chain_cb_t lb = { 0 };
708 slap_callback *sc = op->o_callback,
713 struct berval ndn = op->o_ndn;
715 int sr_err = rs->sr_err;
716 slap_reply_t sr_type = rs->sr_type;
717 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
718 slap_mask_t chain_mask = 0;
719 ber_len_t chain_shift = 0;
720 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
722 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
723 return SLAP_CB_CONTINUE;
726 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
727 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
728 switch ( get_resolveBehavior( op ) ) {
729 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
730 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
731 return SLAP_CB_CONTINUE;
734 chain_mask = SLAP_CH_RESOLVE_MASK;
735 chain_shift = SLAP_CH_RESOLVE_SHIFT;
739 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
740 switch ( get_continuationBehavior( op ) ) {
741 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
742 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
743 return SLAP_CB_CONTINUE;
746 chain_mask = SLAP_CH_CONTINUATION_MASK;
747 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
751 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
754 * TODO: add checks on who/when chain operations; e.g.:
755 * a) what identities are authorized
756 * b) what request DN (e.g. only chain requests rooted at <DN>)
757 * c) what referral URIs
758 * d) what protocol scheme (e.g. only ldaps://)
762 matched = rs->sr_matched;
763 rs->sr_matched = NULL;
767 /* we need this to know if back-ldap returned any result */
769 sc2.sc_private = &lb;
770 sc2.sc_response = ldap_chain_cb_response;
771 op->o_callback = &sc2;
773 /* Chaining can be performed by a privileged user on behalf
774 * of normal users, using the ProxyAuthz control, by exploiting
775 * the identity assertion feature of back-ldap; see idassert-*
776 * directives in slapd-ldap(5).
778 * FIXME: the idassert-authcDN is one, will it be fine regardless
779 * of the URI we obtain from the referral?
782 switch ( op->o_tag ) {
783 case LDAP_REQ_BIND: {
784 struct berval rndn = op->o_req_ndn;
785 Connection *conn = op->o_conn;
787 /* FIXME: can we really get a referral for binds? */
788 op->o_req_ndn = slap_empty_bv;
790 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
791 op->o_req_ndn = rndn;
797 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
800 case LDAP_REQ_DELETE:
801 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
804 case LDAP_REQ_MODRDN:
805 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
808 case LDAP_REQ_MODIFY:
809 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
812 case LDAP_REQ_COMPARE:
813 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
814 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
819 case LDAP_REQ_SEARCH:
820 if ( rs->sr_type == REP_SEARCHREF ) {
821 rc = ldap_chain_search( op, rs, ref, 0 );
824 /* we might get here before any database actually
825 * performed a search; in those cases, we need
826 * to check limits, to make sure safe defaults
828 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
829 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
832 rc = SLAP_CB_CONTINUE;
837 case LDAP_REQ_EXTENDED:
838 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
839 /* FIXME: ldap_back_extended() by design
840 * doesn't send result; frontend is expected
842 /* FIXME: what about chaining? */
843 if ( rc != SLAPD_ABANDON ) {
844 send_ldap_extended( op, rs );
847 lb.lb_status = LDAP_CH_RES;
851 rc = SLAP_CB_CONTINUE;
861 /* slapd-ldap sent response */
862 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
863 /* FIXME: should we send response? */
864 Debug( LDAP_DEBUG_ANY,
865 "%s: ldap_chain_response: "
866 "overlay should have sent result.\n",
867 op->o_log_prefix, 0, 0 );
872 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
873 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
877 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
878 case LDAP_CHAINING_REQUIRED:
880 op->o_callback = NULL;
881 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
882 "operation cannot be completed without chaining" );
886 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
887 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
889 rs->sr_type = sr_type;
892 rc = SLAP_CB_CONTINUE;
894 rs->sr_type = sr_type;
895 rs->sr_matched = matched;
898 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
901 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
904 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
905 op->o_callback = NULL;
906 rc = rs->sr_err = slap_map_api2result( rs );
907 send_ldap_result( op, rs );
912 rs->sr_type = sr_type;
913 rs->sr_matched = matched;
915 op->o_bd->be_private = private;
922 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
924 ldap_chain_parse_ctrl(
930 str2chain( const char *s )
932 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
933 return LDAP_CHAINING_PREFERRED;
935 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
936 return LDAP_CHAINING_REQUIRED;
938 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
939 return LDAP_REFERRALS_PREFERRED;
941 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
942 return LDAP_REFERRALS_REQUIRED;
947 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
962 static ConfigDriver chain_cf_gen;
963 static ConfigCfAdd chain_cfadd;
964 static ConfigLDAPadd chain_ldadd;
966 static ConfigTable chaincfg[] = {
967 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
968 { "chain-chaining", "args",
969 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
970 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
971 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
972 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
973 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
974 { "chain-cache-uri", "TRUE/FALSE",
975 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
976 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
977 "DESC 'Enables caching of URIs not present in configuration' "
978 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
979 { "chain-max-depth", "args",
980 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
981 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
982 "DESC 'max referral depth' "
984 "EQUALITY integerMatch "
985 "SINGLE-VALUE )", NULL, NULL },
986 { "chain-return-error", "TRUE/FALSE",
987 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
988 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
989 "DESC 'Errors are returned instead of the original referral' "
990 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
991 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
994 static ConfigOCs chainocs[] = {
996 "NAME 'olcChainConfig' "
997 "DESC 'Chain configuration' "
998 "SUP olcOverlayConfig "
1000 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1001 "olcChainingBehavior $ "
1002 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1003 "olcChainCacheURI $ "
1004 "olcChainMaxReferralDepth $ "
1005 "olcChainReturnError "
1007 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1008 { "( OLcfgOvOc:3.2 "
1009 "NAME 'olcChainDatabase' "
1010 "DESC 'Chain remote server configuration' "
1012 Cft_Misc, chaincfg, chain_ldadd },
1017 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1024 AttributeDescription *ad = NULL;
1030 if ( p->ce_type != Cft_Overlay
1032 || p->ce_bi->bi_cf_ocs != chainocs )
1034 return LDAP_CONSTRAINT_VIOLATION;
1037 on = (slap_overinst *)p->ce_bi;
1038 lc = (ldap_chain_t *)on->on_bi.bi_private;
1040 assert( ca->be == NULL );
1041 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1043 ca->be->bd_info = (BackendInfo *)on;
1045 rc = slap_str2ad( "olcDbURI", &ad, &text );
1046 assert( rc == LDAP_SUCCESS );
1048 at = attr_find( e->e_attrs, ad );
1049 if ( lc->lc_common_li == NULL && at != NULL ) {
1050 /* FIXME: we should generate an empty default entry
1051 * if none is supplied */
1052 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1053 "first underlying database \"%s\" "
1054 "cannot contain attribute \"%s\".\n",
1055 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1056 rc = LDAP_CONSTRAINT_VIOLATION;
1059 } else if ( lc->lc_common_li != NULL && at == NULL ) {
1060 /* FIXME: we should generate an empty default entry
1061 * if none is supplied */
1062 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1063 "subsequent underlying database \"%s\" "
1064 "must contain attribute \"%s\".\n",
1065 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1066 rc = LDAP_CONSTRAINT_VIOLATION;
1070 if ( lc->lc_common_li == NULL ) {
1071 rc = ldap_chain_db_init_common( ca->be );
1074 rc = ldap_chain_db_init_one( ca->be );
1078 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1079 "unable to init %sunderlying database \"%s\".\n",
1080 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1081 return LDAP_CONSTRAINT_VIOLATION;
1084 li = ca->be->be_private;
1086 if ( lc->lc_common_li == NULL ) {
1087 lc->lc_common_li = li;
1089 } else if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1090 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1092 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1093 "database \"%s\" insert failed.\n",
1094 e->e_name.bv_val, 0, 0 );
1095 rc = LDAP_CONSTRAINT_VIOLATION;
1100 if ( rc != LDAP_SUCCESS ) {
1101 (void)ldap_chain_db_destroy_one( ca->be );
1109 typedef struct ldap_chain_cfadd_apply_t {
1115 } ldap_chain_cfadd_apply_t;
1118 ldap_chain_cfadd_apply( void *datum, void *arg )
1120 ldapinfo_t *li = (ldapinfo_t *)datum;
1121 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1125 /* FIXME: should not hardcode "olcDatabase" here */
1126 bv.bv_len = snprintf( lca->ca->msg, sizeof( lca->ca->msg ),
1127 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1128 bv.bv_val = lca->ca->msg;
1130 lca->ca->be->be_private = (void *)li;
1131 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1132 &bv, lback->bi_cf_ocs, &chainocs[1] );
1140 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1142 CfEntryInfo *pe = p->e_private;
1143 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1144 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1145 void *priv = (void *)ca->be->be_private;
1147 if ( lback->bi_cf_ocs ) {
1148 ldap_chain_cfadd_apply_t lca = { 0 };
1156 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1158 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1159 &lca, 1, AVL_INORDER );
1161 ca->be->be_private = priv;
1167 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1168 static slap_verbmasks chaining_mode[] = {
1169 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1170 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1171 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1172 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1175 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1178 chain_cf_gen( ConfigArgs *c )
1180 slap_overinst *on = (slap_overinst *)c->bi;
1181 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1185 if ( c->op == SLAP_CONFIG_EMIT ) {
1187 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1189 struct berval resolve = BER_BVNULL,
1190 continuation = BER_BVNULL;
1192 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1196 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1197 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1199 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1201 + STRLENOF( "continuation=" ) + continuation.bv_len;
1202 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1203 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1204 "resolve=%s continuation=%s",
1205 resolve.bv_val, continuation.bv_val );
1207 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1208 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1209 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1210 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1211 " critical", STRLENOF( " critical" ) + 1 );
1212 c->value_bv.bv_len += STRLENOF( " critical" );
1217 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1220 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1224 c->value_int = lc->lc_max_depth;
1228 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1237 } else if ( c->op == LDAP_MOD_DELETE ) {
1243 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1251 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1262 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1263 char **argv = c->argv;
1265 BerElementBuffer berbuf;
1266 BerElement *ber = (BerElement *)&berbuf;
1270 Operation op = { 0 };
1271 SlapReply rs = { 0 };
1273 lc->lc_chaining_ctrlflag = 0;
1275 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1276 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1277 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1278 if ( resolve == -1 ) {
1279 Debug( LDAP_DEBUG_ANY, "%s: "
1280 "illegal <resolve> value %s "
1281 "in \"chain-chaining>\".\n",
1282 c->log, argv[ 0 ], 0 );
1286 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1287 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1288 if ( continuation == -1 ) {
1289 Debug( LDAP_DEBUG_ANY, "%s: "
1290 "illegal <continuation> value %s "
1291 "in \"chain-chaining\".\n",
1292 c->log, argv[ 0 ], 0 );
1296 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1300 Debug( LDAP_DEBUG_ANY, "%s: "
1301 "unknown option in \"chain-chaining\".\n",
1307 if ( resolve != -1 || continuation != -1 ) {
1310 if ( resolve == -1 ) {
1312 resolve = SLAP_CHAINING_DEFAULT;
1315 ber_init2( ber, NULL, LBER_USE_DER );
1317 err = ber_printf( ber, "{e" /* } */, resolve );
1320 Debug( LDAP_DEBUG_ANY, "%s: "
1321 "chaining behavior control encoding error!\n",
1326 if ( continuation > -1 ) {
1327 err = ber_printf( ber, "e", continuation );
1330 Debug( LDAP_DEBUG_ANY, "%s: "
1331 "chaining behavior control encoding error!\n",
1337 err = ber_printf( ber, /* { */ "N}" );
1340 Debug( LDAP_DEBUG_ANY, "%s: "
1341 "chaining behavior control encoding error!\n",
1346 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1347 exit( EXIT_FAILURE );
1351 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1354 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1355 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1357 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1359 Debug( LDAP_DEBUG_ANY, "%s: "
1360 "unable to parse chaining control%s%s.\n",
1361 c->log, rs.sr_text ? ": " : "",
1362 rs.sr_text ? rs.sr_text : "" );
1366 lc->lc_chaining_ctrlflag = op.o_chaining;
1368 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1371 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1372 Debug( LDAP_DEBUG_ANY, "%s: "
1373 "\"chaining\" control unsupported (ignored).\n",
1375 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1379 if ( c->value_int ) {
1380 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1382 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1387 if ( c->value_int < 0 ) {
1388 snprintf( c->msg, sizeof( c->msg ),
1389 "<%s> invalid max referral depth %d",
1390 c->argv[0], c->value_int );
1391 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1392 c->log, c->msg, 0 );
1396 lc->lc_max_depth = c->value_int;
1399 if ( c->value_int ) {
1400 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1402 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1417 slap_overinst *on = (slap_overinst *)be->bd_info;
1418 ldap_chain_t *lc = NULL;
1420 if ( lback == NULL ) {
1421 lback = backend_info( "ldap" );
1423 if ( lback == NULL ) {
1428 lc = ch_malloc( sizeof( ldap_chain_t ) );
1432 memset( lc, 0, sizeof( ldap_chain_t ) );
1433 lc->lc_max_depth = 1;
1434 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1436 on->on_bi.bi_private = (void *)lc;
1442 ldap_chain_db_config(
1449 slap_overinst *on = (slap_overinst *)be->bd_info;
1450 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1452 int rc = SLAP_CONF_UNKNOWN;
1454 if ( lc->lc_common_li == NULL ) {
1455 void *be_private = be->be_private;
1456 ldap_chain_db_init_common( be );
1457 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1458 be->be_private = be_private;
1461 /* Something for the chain database? */
1462 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1463 char *save_argv0 = argv[ 0 ];
1464 BackendInfo *bd_info = be->bd_info;
1465 void *be_private = be->be_private;
1466 ConfigOCs *be_cf_ocs = be->be_cf_ocs;
1467 static char *allowed_argv[] = {
1468 /* special: put URI here, so in the meanwhile
1469 * it detects whether a new URI is being provided */
1475 /* FIXME: maybe rebind-as-user should be allowed
1476 * only within known URIs... */
1483 int which_argv = -1;
1485 argv[ 0 ] += STRLENOF( "chain-" );
1487 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1488 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1493 if ( allowed_argv[ which_argv ] == NULL ) {
1496 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1497 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1498 "\"%s\" only allowed within a URI directive.\n.",
1499 fname, lineno, argv[ 0 ] );
1504 if ( which_argv == 0 ) {
1505 rc = ldap_chain_db_init_one( be );
1507 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1508 "underlying slapd-ldap initialization failed.\n.",
1512 lc->lc_cfg_li = be->be_private;
1515 /* TODO: add checks on what other slapd-ldap(5) args
1516 * should be put in the template; this is not quite
1517 * harmful, because attributes that shouldn't don't
1518 * get actually used, but the user should at least
1522 be->bd_info = lback;
1523 be->be_private = (void *)lc->lc_cfg_li;
1524 be->be_cf_ocs = lback->bi_cf_ocs;
1526 rc = config_generic_wrapper( be, fname, lineno, argc, argv );
1528 argv[ 0 ] = save_argv0;
1529 be->be_cf_ocs = be_cf_ocs;
1530 be->be_private = be_private;
1531 be->bd_info = bd_info;
1533 if ( which_argv == 0 ) {
1539 db.be_private = (void *)lc->lc_cfg_li;
1540 ldap_chain_db_destroy_one( &db );
1541 lc->lc_cfg_li = NULL;
1544 if ( lc->lc_cfg_li->li_bvuri == NULL
1545 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1546 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1548 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1549 "no URI list allowed in slapo-chain.\n",
1552 goto private_destroy;
1555 if ( avl_insert( &lc->lc_lai.lai_tree,
1556 (caddr_t)lc->lc_cfg_li,
1557 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1559 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1560 "duplicate URI in slapo-chain.\n",
1563 goto private_destroy;
1580 typedef struct ldap_chain_db_apply_t {
1583 } ldap_chain_db_apply_t;
1586 ldap_chain_db_apply( void *datum, void *arg )
1588 ldapinfo_t *li = (ldapinfo_t *)datum;
1589 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1591 lca->be->be_private = (void *)li;
1593 return lca->func( lca->be );
1602 slap_overinst *on = (slap_overinst *)be->bd_info;
1603 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1608 BI_db_func *func = (&lback->bi_db_open)[ which ];
1610 if ( func != NULL && lc->lc_common_li != NULL ) {
1614 db.be_private = lc->lc_common_li;
1622 if ( lc->lc_lai.lai_tree != NULL ) {
1623 ldap_chain_db_apply_t lca;
1628 rc = avl_apply( lc->lc_lai.lai_tree,
1629 ldap_chain_db_apply, (void *)&lca,
1630 1, AVL_INORDER ) != AVL_NOMORE;
1642 slap_overinst *on = (slap_overinst *) be->bd_info;
1643 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1645 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1648 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1652 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1654 if ( lc->lc_common_li == NULL ) {
1655 void *be_private = be->be_private;
1656 ldap_chain_db_init_common( be );
1657 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1658 be->be_private = be_private;
1661 return ldap_chain_db_func( be, db_open );
1665 ldap_chain_db_close(
1668 return ldap_chain_db_func( be, db_close );
1672 ldap_chain_db_destroy(
1675 slap_overinst *on = (slap_overinst *) be->bd_info;
1676 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1680 rc = ldap_chain_db_func( be, db_destroy );
1683 avl_free( lc->lc_lai.lai_tree, NULL );
1684 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1692 * inits one instance of the slapd-ldap backend, and stores
1693 * the private info in be_private of the arg
1696 ldap_chain_db_init_common(
1699 BackendInfo *bi = be->bd_info;
1703 be->bd_info = lback;
1704 be->be_private = NULL;
1705 t = lback->bi_db_init( be );
1709 li = (ldapinfo_t *)be->be_private;
1710 li->li_urllist_f = NULL;
1711 li->li_urllist_p = NULL;
1718 * inits one instance of the slapd-ldap backend, stores
1719 * the private info in be_private of the arg and fills
1720 * selected fields with data from the template.
1722 * NOTE: add checks about the other fields of the template,
1723 * which are ignored and SHOULD NOT be configured by the user.
1726 ldap_chain_db_init_one(
1729 slap_overinst *on = (slap_overinst *)be->bd_info;
1730 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1732 BackendInfo *bi = be->bd_info;
1737 be->bd_info = lback;
1738 be->be_private = NULL;
1739 t = lback->bi_db_init( be );
1743 li = (ldapinfo_t *)be->be_private;
1744 li->li_urllist_f = NULL;
1745 li->li_urllist_p = NULL;
1747 /* copy common data */
1748 li->li_nretries = lc->lc_common_li->li_nretries;
1749 li->li_flags = lc->lc_common_li->li_flags;
1750 li->li_version = lc->lc_common_li->li_version;
1751 for ( t = 0; t < LDAP_BACK_OP_LAST; t++ ) {
1752 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
1759 typedef struct ldap_chain_conn_apply_t {
1762 } ldap_chain_conn_apply_t;
1765 ldap_chain_conn_apply( void *datum, void *arg )
1767 ldapinfo_t *li = (ldapinfo_t *)datum;
1768 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
1770 lca->be->be_private = (void *)li;
1772 return lback->bi_connection_destroy( lca->be, lca->conn );
1776 ldap_chain_connection_destroy(
1781 slap_overinst *on = (slap_overinst *) be->bd_info;
1782 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1783 void *private = be->be_private;
1784 ldap_chain_conn_apply_t lca;
1787 be->be_private = NULL;
1790 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
1791 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
1792 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
1793 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
1794 be->be_private = private;
1799 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1801 ldap_chain_parse_ctrl(
1811 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1812 rs->sr_text = "Chaining behavior control specified multiple times";
1813 return LDAP_PROTOCOL_ERROR;
1816 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1817 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1818 return LDAP_PROTOCOL_ERROR;
1821 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1822 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1827 /* Parse the control value
1828 * ChainingBehavior ::= SEQUENCE {
1829 * resolveBehavior Behavior OPTIONAL,
1830 * continuationBehavior Behavior OPTIONAL }
1832 * Behavior :: = ENUMERATED {
1833 * chainingPreferred (0),
1834 * chainingRequired (1),
1835 * referralsPreferred (2),
1836 * referralsRequired (3) }
1839 ber = ber_init( &ctrl->ldctl_value );
1841 rs->sr_text = "internal error";
1845 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1846 /* FIXME: since the whole SEQUENCE is optional,
1847 * should we accept no enumerations at all? */
1848 if ( tag != LBER_ENUMERATED ) {
1849 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1850 return LDAP_PROTOCOL_ERROR;
1853 switch ( behavior ) {
1854 case LDAP_CHAINING_PREFERRED:
1855 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1858 case LDAP_CHAINING_REQUIRED:
1859 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1862 case LDAP_REFERRALS_PREFERRED:
1863 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1866 case LDAP_REFERRALS_REQUIRED:
1867 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1871 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1872 return LDAP_PROTOCOL_ERROR;
1875 tag = ber_peek_tag( ber, &len );
1876 if ( tag == LBER_ENUMERATED ) {
1877 tag = ber_scanf( ber, "e", &behavior );
1878 if ( tag == LBER_ERROR ) {
1879 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1880 return LDAP_PROTOCOL_ERROR;
1884 if ( tag == LBER_DEFAULT ) {
1885 mode |= SLAP_CH_CONTINUATION_DEFAULT;
1888 switch ( behavior ) {
1889 case LDAP_CHAINING_PREFERRED:
1890 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1893 case LDAP_CHAINING_REQUIRED:
1894 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1897 case LDAP_REFERRALS_PREFERRED:
1898 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1901 case LDAP_REFERRALS_REQUIRED:
1902 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1906 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1907 return LDAP_PROTOCOL_ERROR;
1911 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1912 rs->sr_text = "Chaining behavior control: decoding error";
1913 return LDAP_PROTOCOL_ERROR;
1916 (void) ber_free( ber, 1 );
1919 op->o_chaining = mode | ( ctrl->ldctl_iscritical
1920 ? SLAP_CONTROL_CRITICAL
1921 : SLAP_CONTROL_NONCRITICAL );
1923 return LDAP_SUCCESS;
1925 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1927 static slap_overinst ldapchain;
1930 chain_initialize( void )
1934 /* Make sure we don't exceed the bits reserved for userland */
1935 config_check_userland( CH_LAST );
1937 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1938 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1939 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1940 ldap_chain_parse_ctrl, &sc_chainingBehavior );
1941 if ( rc != LDAP_SUCCESS ) {
1942 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1943 "unable to register chaining behavior control: %d.\n",
1947 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1949 ldapchain.on_bi.bi_type = "chain";
1950 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1951 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1952 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1953 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
1954 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1956 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1958 ldapchain.on_response = ldap_chain_response;
1960 ldapchain.on_bi.bi_cf_ocs = chainocs;
1962 rc = config_register_schema( chaincfg, chainocs );
1967 return overlay_register( &ldapchain );