1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2007 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;
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 static int ldap_chain_db_open_one( BackendDB *be );
114 #define ldap_chain_db_close_one(be) (0)
115 #define ldap_chain_db_destroy_one(be, rs) (lback)->bi_db_destroy( (be), (rs) )
117 typedef struct ldap_chain_cb_t {
118 ldap_chain_status_t lb_status;
139 static slap_overinst ldapchain;
141 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
143 chaining_control_add(
146 LDAPControl ***oldctrlsp )
148 LDAPControl **ctrls = NULL;
151 *oldctrlsp = op->o_ctrls;
153 /* default chaining control not defined */
154 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
158 /* already present */
159 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
163 /* FIXME: check other incompatibilities */
165 /* add to other controls */
167 for ( c = 0; op->o_ctrls[ c ]; c++ )
171 ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
172 ctrls[ 0 ] = &lc->lc_chaining_ctrl;
174 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
175 ctrls[ c + 1 ] = op->o_ctrls[ c ];
178 ctrls[ c + 1 ] = NULL;
182 op->o_chaining = lc->lc_chaining_ctrlflag;
188 chaining_control_remove(
190 LDAPControl ***oldctrlsp )
192 LDAPControl **oldctrls = *oldctrlsp;
194 /* we assume that the first control is the chaining control
195 * added by the chain overlay, so it's the only one we explicitly
197 if ( op->o_ctrls != oldctrls ) {
198 assert( op->o_ctrls != NULL );
199 assert( op->o_ctrls[ 0 ] != NULL );
204 op->o_ctrls = oldctrls;
211 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
214 ldap_chain_uri_cmp( const void *c1, const void *c2 )
216 const ldapinfo_t *li1 = (const ldapinfo_t *)c1;
217 const ldapinfo_t *li2 = (const ldapinfo_t *)c2;
219 assert( li1->li_bvuri != NULL );
220 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
221 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
223 assert( li2->li_bvuri != NULL );
224 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
225 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
227 /* If local DNs don't match, it is definitely not a match */
228 return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
232 ldap_chain_uri_dup( void *c1, void *c2 )
234 ldapinfo_t *li1 = (ldapinfo_t *)c1;
235 ldapinfo_t *li2 = (ldapinfo_t *)c2;
237 assert( li1->li_bvuri != NULL );
238 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
239 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
241 assert( li2->li_bvuri != NULL );
242 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
243 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
245 /* Cannot have more than one shared session with same DN */
246 if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
254 * Search specific response that strips entryDN from entries
257 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
259 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
261 assert( op->o_tag == LDAP_REQ_SEARCH );
263 /* if in error, don't proceed any further */
264 if ( lb->lb_status == LDAP_CH_ERR ) {
268 if ( rs->sr_type == REP_SEARCH ) {
269 Attribute **ap = &rs->sr_entry->e_attrs;
271 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
272 /* will be generated later by frontend
273 * (a cleaner solution would be that
274 * the frontend checks if it already exists */
275 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
282 /* there SHOULD be one only! */
287 /* tell the frontend not to add generated
288 * operational attributes */
289 rs->sr_flags |= REP_NO_OPERATIONALS;
291 return SLAP_CB_CONTINUE;
293 } else if ( rs->sr_type == REP_SEARCHREF ) {
294 /* if we get it here, it means the library was unable
295 * to chase the referral... */
296 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
297 rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
300 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
301 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
302 switch ( get_continuationBehavior( op ) ) {
303 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
304 lb->lb_status = LDAP_CH_ERR;
305 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
311 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
312 return SLAP_CB_CONTINUE;
314 } else if ( rs->sr_type == REP_RESULT ) {
315 if ( rs->sr_err == LDAP_REFERRAL
316 && lb->lb_depth < lb->lb_lc->lc_max_depth
317 && rs->sr_ref != NULL )
319 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
322 /* back-ldap tried to send result */
323 lb->lb_status = LDAP_CH_RES;
330 * Dummy response that simply traces if back-ldap tried to send
331 * anything to the client
334 ldap_chain_cb_response( Operation *op, SlapReply *rs )
336 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
338 /* if in error, don't proceed any further */
339 if ( lb->lb_status == LDAP_CH_ERR ) {
343 if ( rs->sr_type == REP_RESULT ) {
345 switch ( rs->sr_err ) {
346 case LDAP_COMPARE_TRUE:
347 case LDAP_COMPARE_FALSE:
348 if ( op->o_tag != LDAP_REQ_COMPARE ) {
354 lb->lb_status = LDAP_CH_RES;
358 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
359 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
363 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
364 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
365 switch ( get_continuationBehavior( op ) ) {
366 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
367 lb->lb_status = LDAP_CH_ERR;
368 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
374 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
381 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
383 /* strip the entryDN attribute, but keep returning results */
384 (void)ldap_chain_cb_search_response( op, rs );
387 return SLAP_CB_CONTINUE;
398 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
399 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
400 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
401 ldapinfo_t li = { 0 }, *lip = NULL;
402 struct berval bvuri[ 2 ] = { { 0 } };
404 /* NOTE: returned if ref is empty... */
408 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
409 LDAPControl **ctrls = NULL;
411 (void)chaining_control_add( lc, op, &ctrls );
412 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
416 for ( ; !BER_BVISNULL( ref ); ref++ ) {
417 SlapReply rs2 = { 0 };
418 LDAPURLDesc *srv = NULL;
419 struct berval save_req_dn = op->o_req_dn,
420 save_req_ndn = op->o_req_ndn,
426 /* We're setting the URI of the first referral;
427 * what if there are more?
433 If the client wishes to progress the operation, it MUST follow the
434 referral by contacting one of the supported services. If multiple
435 URIs are present, the client assumes that any supported URI may be
436 used to progress the operation.
438 * so we actually need to follow exactly one,
439 * and we can assume any is fine.
442 /* parse reference and use
443 * proto://[host][:port]/ only */
444 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
445 if ( rc != LDAP_URL_SUCCESS ) {
453 srv->lud_scope = LDAP_SCOPE_DEFAULT;
454 if ( srv->lud_dn != NULL ) {
455 ber_str2bv( srv->lud_dn, 0, 0, &dn );
456 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
457 if ( rc == LDAP_SUCCESS ) {
458 /* remove DN essentially because later on
459 * ldap_initialize() will parse the URL
460 * as a comma-separated URL list */
468 li.li_uri = ldap_url_desc2str( srv );
469 srv->lud_dn = dn.bv_val;
470 ldap_free_urldesc( srv );
472 if ( rc != LDAP_SUCCESS ) {
478 if ( li.li_uri == NULL ) {
481 goto further_cleanup;
487 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
489 /* Searches for a ldapinfo in the avl tree */
490 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
491 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
492 (caddr_t)&li, ldap_chain_uri_cmp );
493 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
496 op->o_bd->be_private = (void *)lip;
499 rc = ldap_chain_db_init_one( op->o_bd );
503 lip = (ldapinfo_t *)op->o_bd->be_private;
504 lip->li_uri = li.li_uri;
505 lip->li_bvuri = bvuri;
506 rc = ldap_chain_db_open_one( op->o_bd );
509 lip->li_bvuri = NULL;
510 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
514 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
515 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
516 if ( avl_insert( &lc->lc_lai.lai_tree,
517 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
519 /* someone just inserted another;
520 * don't bother, use this and then
524 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
532 lb->lb_depth = depth + 1;
534 rc = op_f( op, &rs2 );
536 /* note the first error */
537 if ( first_rc == -1 ) {
542 ldap_memfree( li.li_uri );
547 lip->li_bvuri = NULL;
548 (void)ldap_chain_db_close_one( op->o_bd );
549 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
553 if ( !BER_BVISNULL( &pdn ) ) {
554 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
556 op->o_req_dn = save_req_dn;
558 if ( !BER_BVISNULL( &ndn ) ) {
559 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
561 op->o_req_ndn = save_req_ndn;
563 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
571 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
572 (void)chaining_control_remove( op, &ctrls );
573 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
575 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
590 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
591 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
592 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
593 ldapinfo_t li = { 0 }, *lip = NULL;
594 struct berval bvuri[ 2 ] = { { 0 } };
596 struct berval odn = op->o_req_dn,
597 ondn = op->o_req_ndn;
598 slap_response *save_response = op->o_callback->sc_response;
603 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
604 LDAPControl **ctrls = NULL;
606 (void)chaining_control_add( lc, op, &ctrls );
607 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
609 rs->sr_type = REP_SEARCH;
611 op->o_callback->sc_response = ldap_chain_cb_search_response;
613 /* if we parse the URI then by no means
614 * we can cache stuff or reuse connections,
615 * because in back-ldap there's no caching
616 * based on the URI value, which is supposed
617 * to be set once for all (correct?) */
619 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
620 SlapReply rs2 = { 0 };
622 struct berval save_req_dn = op->o_req_dn,
623 save_req_ndn = op->o_req_ndn,
629 /* parse reference and use
630 * proto://[host][:port]/ only */
631 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
632 if ( rc != LDAP_URL_SUCCESS ) {
634 rs->sr_err = LDAP_OTHER;
639 rc = LDAP_INVALID_SYNTAX;
640 if ( srv->lud_dn != NULL ) {
641 ber_str2bv( srv->lud_dn, 0, 0, &dn );
642 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
643 if ( rc == LDAP_SUCCESS ) {
644 /* remove DN essentially because later on
645 * ldap_initialize() will parse the URL
646 * as a comma-separated URL list */
648 srv->lud_scope = LDAP_SCOPE_DEFAULT;
649 li.li_uri = ldap_url_desc2str( srv );
650 srv->lud_dn = dn.bv_val;
653 ldap_free_urldesc( srv );
655 if ( rc != LDAP_SUCCESS ) {
661 if ( li.li_uri == NULL ) {
664 goto further_cleanup;
670 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
672 /* Searches for a ldapinfo in the avl tree */
673 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
674 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
675 (caddr_t)&li, ldap_chain_uri_cmp );
676 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
679 op->o_bd->be_private = (void *)lip;
682 /* if none is found, create a temporary... */
683 rc = ldap_chain_db_init_one( op->o_bd );
687 lip = (ldapinfo_t *)op->o_bd->be_private;
688 lip->li_uri = li.li_uri;
689 lip->li_bvuri = bvuri;
690 rc = ldap_chain_db_open_one( op->o_bd );
693 lip->li_bvuri = NULL;
694 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
698 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
699 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
700 if ( avl_insert( &lc->lc_lai.lai_tree,
701 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
703 /* someone just inserted another;
704 * don't bother, use this and then
708 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
715 lb->lb_op_f = lback->bi_op_search;
716 lb->lb_depth = depth + 1;
718 /* FIXME: should we also copy filter and scope?
719 * according to RFC3296, no */
720 rc = lback->bi_op_search( op, &rs2 );
721 if ( first_rc == -1 ) {
726 ldap_memfree( li.li_uri );
729 op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
730 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
734 lip->li_bvuri = NULL;
735 (void)ldap_chain_db_close_one( op->o_bd );
736 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
740 if ( !BER_BVISNULL( &pdn ) ) {
741 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
743 op->o_req_dn = save_req_dn;
745 if ( !BER_BVISNULL( &ndn ) ) {
746 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
748 op->o_req_ndn = save_req_ndn;
750 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
758 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
759 (void)chaining_control_remove( op, &ctrls );
760 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
763 op->o_req_ndn = ondn;
764 op->o_callback->sc_response = save_response;
765 rs->sr_type = REP_SEARCHREF;
768 if ( rc != LDAP_SUCCESS ) {
769 /* couldn't chase any of the referrals */
770 if ( first_rc != -1 ) {
774 rc = SLAP_CB_CONTINUE;
782 ldap_chain_response( Operation *op, SlapReply *rs )
784 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
785 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
786 BackendDB db, *bd = op->o_bd;
787 ldap_chain_cb_t lb = { 0 };
788 slap_callback *sc = op->o_callback,
791 const char *text = NULL;
794 struct berval ndn = op->o_ndn;
796 int sr_err = rs->sr_err;
797 slap_reply_t sr_type = rs->sr_type;
798 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
799 slap_mask_t chain_mask = 0;
800 ber_len_t chain_shift = 0;
801 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
803 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
804 return SLAP_CB_CONTINUE;
807 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
808 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
809 switch ( get_resolveBehavior( op ) ) {
810 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
811 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
812 return SLAP_CB_CONTINUE;
815 chain_mask = SLAP_CH_RESOLVE_MASK;
816 chain_shift = SLAP_CH_RESOLVE_SHIFT;
820 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
821 switch ( get_continuationBehavior( op ) ) {
822 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
823 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
824 return SLAP_CB_CONTINUE;
827 chain_mask = SLAP_CH_CONTINUATION_MASK;
828 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
832 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
835 * TODO: add checks on who/when chain operations; e.g.:
836 * a) what identities are authorized
837 * b) what request DN (e.g. only chain requests rooted at <DN>)
838 * c) what referral URIs
839 * d) what protocol scheme (e.g. only ldaps://)
844 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
849 matched = rs->sr_matched;
850 rs->sr_matched = NULL;
854 /* we need this to know if back-ldap returned any result */
856 sc2.sc_private = &lb;
857 sc2.sc_response = ldap_chain_cb_response;
858 op->o_callback = &sc2;
860 /* Chaining can be performed by a privileged user on behalf
861 * of normal users, using the ProxyAuthz control, by exploiting
862 * the identity assertion feature of back-ldap; see idassert-*
863 * directives in slapd-ldap(5).
865 * FIXME: the idassert-authcDN is one, will it be fine regardless
866 * of the URI we obtain from the referral?
869 switch ( op->o_tag ) {
870 case LDAP_REQ_BIND: {
871 struct berval rndn = op->o_req_ndn;
872 Connection *conn = op->o_conn;
874 /* FIXME: can we really get a referral for binds? */
875 op->o_req_ndn = slap_empty_bv;
877 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
878 op->o_req_ndn = rndn;
884 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
887 case LDAP_REQ_DELETE:
888 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
891 case LDAP_REQ_MODRDN:
892 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
895 case LDAP_REQ_MODIFY:
896 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
899 case LDAP_REQ_COMPARE:
900 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
901 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
906 case LDAP_REQ_SEARCH:
907 if ( rs->sr_type == REP_SEARCHREF ) {
908 rc = ldap_chain_search( op, rs, ref, 0 );
911 /* we might get here before any database actually
912 * performed a search; in those cases, we need
913 * to check limits, to make sure safe defaults
915 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
916 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
919 rc = SLAP_CB_CONTINUE;
924 case LDAP_REQ_EXTENDED:
925 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
926 /* FIXME: ldap_back_extended() by design
927 * doesn't send result; frontend is expected
929 /* FIXME: what about chaining? */
930 if ( rc != SLAPD_ABANDON ) {
932 send_ldap_extended( op, rs );
935 lb.lb_status = LDAP_CH_RES;
939 rc = SLAP_CB_CONTINUE;
949 /* slapd-ldap sent response */
950 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
951 /* FIXME: should we send response? */
952 Debug( LDAP_DEBUG_ANY,
953 "%s: ldap_chain_response: "
954 "overlay should have sent result.\n",
955 op->o_log_prefix, 0, 0 );
960 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
961 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
965 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
966 case LDAP_CHAINING_REQUIRED:
968 op->o_callback = NULL;
969 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
970 "operation cannot be completed without chaining" );
974 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
975 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
977 rs->sr_type = sr_type;
980 rc = SLAP_CB_CONTINUE;
982 rs->sr_type = sr_type;
984 rs->sr_matched = matched;
987 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
990 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
993 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
994 op->o_callback = NULL;
995 rc = rs->sr_err = slap_map_api2result( rs );
996 send_ldap_result( op, rs );
1000 rs->sr_err = sr_err;
1001 rs->sr_type = sr_type;
1003 rs->sr_matched = matched;
1006 op->o_callback = sc;
1012 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1014 ldap_chain_parse_ctrl(
1017 LDAPControl *ctrl );
1020 str2chain( const char *s )
1022 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1023 return LDAP_CHAINING_PREFERRED;
1025 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1026 return LDAP_CHAINING_REQUIRED;
1028 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1029 return LDAP_REFERRALS_PREFERRED;
1031 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1032 return LDAP_REFERRALS_REQUIRED;
1037 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1052 static ConfigDriver chain_cf_gen;
1053 static ConfigCfAdd chain_cfadd;
1054 static ConfigLDAPadd chain_ldadd;
1056 static ConfigTable chaincfg[] = {
1057 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1058 { "chain-chaining", "args",
1059 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1060 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1061 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1062 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1063 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1064 { "chain-cache-uri", "TRUE/FALSE",
1065 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1066 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1067 "DESC 'Enables caching of URIs not present in configuration' "
1068 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1069 { "chain-max-depth", "args",
1070 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1071 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1072 "DESC 'max referral depth' "
1073 "SYNTAX OMsInteger "
1074 "EQUALITY integerMatch "
1075 "SINGLE-VALUE )", NULL, NULL },
1076 { "chain-return-error", "TRUE/FALSE",
1077 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1078 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1079 "DESC 'Errors are returned instead of the original referral' "
1080 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1081 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1084 static ConfigOCs chainocs[] = {
1085 { "( OLcfgOvOc:3.1 "
1086 "NAME 'olcChainConfig' "
1087 "DESC 'Chain configuration' "
1088 "SUP olcOverlayConfig "
1090 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1091 "olcChainingBehavior $ "
1092 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1093 "olcChainCacheURI $ "
1094 "olcChainMaxReferralDepth $ "
1095 "olcChainReturnError "
1097 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1098 { "( OLcfgOvOc:3.2 "
1099 "NAME 'olcChainDatabase' "
1100 "DESC 'Chain remote server configuration' "
1102 Cft_Misc, chaincfg, chain_ldadd },
1107 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1114 AttributeDescription *ad = NULL;
1120 if ( p->ce_type != Cft_Overlay
1122 || p->ce_bi->bi_cf_ocs != chainocs )
1124 return LDAP_CONSTRAINT_VIOLATION;
1127 on = (slap_overinst *)p->ce_bi;
1128 lc = (ldap_chain_t *)on->on_bi.bi_private;
1130 assert( ca->be == NULL );
1131 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1133 ca->be->bd_info = (BackendInfo *)on;
1135 rc = slap_str2ad( "olcDbURI", &ad, &text );
1136 assert( rc == LDAP_SUCCESS );
1138 at = attr_find( e->e_attrs, ad );
1139 if ( lc->lc_common_li == NULL && at != NULL ) {
1140 /* FIXME: we should generate an empty default entry
1141 * if none is supplied */
1142 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1143 "first underlying database \"%s\" "
1144 "cannot contain attribute \"%s\".\n",
1145 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1146 rc = LDAP_CONSTRAINT_VIOLATION;
1149 } else if ( lc->lc_common_li != NULL && at == NULL ) {
1150 /* FIXME: we should generate an empty default entry
1151 * if none is supplied */
1152 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1153 "subsequent underlying database \"%s\" "
1154 "must contain attribute \"%s\".\n",
1155 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1156 rc = LDAP_CONSTRAINT_VIOLATION;
1160 if ( lc->lc_common_li == NULL ) {
1161 rc = ldap_chain_db_init_common( ca->be );
1164 rc = ldap_chain_db_init_one( ca->be );
1168 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1169 "unable to init %sunderlying database \"%s\".\n",
1170 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1171 return LDAP_CONSTRAINT_VIOLATION;
1174 li = ca->be->be_private;
1176 if ( lc->lc_common_li == NULL ) {
1177 lc->lc_common_li = li;
1180 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1181 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1182 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1183 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1185 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1186 "database \"%s\" insert failed.\n",
1187 e->e_name.bv_val, 0, 0 );
1188 rc = LDAP_CONSTRAINT_VIOLATION;
1194 if ( rc != LDAP_SUCCESS ) {
1195 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1203 typedef struct ldap_chain_cfadd_apply_t {
1209 } ldap_chain_cfadd_apply_t;
1212 ldap_chain_cfadd_apply( void *datum, void *arg )
1214 ldapinfo_t *li = (ldapinfo_t *)datum;
1215 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1219 /* FIXME: should not hardcode "olcDatabase" here */
1220 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1221 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1222 bv.bv_val = lca->ca->cr_msg;
1224 lca->ca->be->be_private = (void *)li;
1225 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1226 &bv, lback->bi_cf_ocs, &chainocs[1] );
1234 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1236 CfEntryInfo *pe = p->e_private;
1237 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1238 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1239 void *priv = (void *)ca->be->be_private;
1241 if ( lback->bi_cf_ocs ) {
1242 ldap_chain_cfadd_apply_t lca = { 0 };
1250 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1252 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1253 &lca, 1, AVL_INORDER );
1255 ca->be->be_private = priv;
1261 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1262 static slap_verbmasks chaining_mode[] = {
1263 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1264 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1265 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1266 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1269 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1272 chain_cf_gen( ConfigArgs *c )
1274 slap_overinst *on = (slap_overinst *)c->bi;
1275 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1279 if ( c->op == SLAP_CONFIG_EMIT ) {
1281 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1283 struct berval resolve = BER_BVNULL,
1284 continuation = BER_BVNULL;
1286 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1290 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1291 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1293 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1295 + STRLENOF( "continuation=" ) + continuation.bv_len;
1296 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1297 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1298 "resolve=%s continuation=%s",
1299 resolve.bv_val, continuation.bv_val );
1301 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1302 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1303 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1304 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1305 " critical", STRLENOF( " critical" ) + 1 );
1306 c->value_bv.bv_len += STRLENOF( " critical" );
1311 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1314 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1318 c->value_int = lc->lc_max_depth;
1322 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1331 } else if ( c->op == LDAP_MOD_DELETE ) {
1337 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1345 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1356 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1357 char **argv = c->argv;
1359 BerElementBuffer berbuf;
1360 BerElement *ber = (BerElement *)&berbuf;
1364 Operation op = { 0 };
1365 SlapReply rs = { 0 };
1367 lc->lc_chaining_ctrlflag = 0;
1369 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1370 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1371 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1372 if ( resolve == -1 ) {
1373 Debug( LDAP_DEBUG_ANY, "%s: "
1374 "illegal <resolve> value %s "
1375 "in \"chain-chaining>\".\n",
1376 c->log, argv[ 0 ], 0 );
1380 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1381 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1382 if ( continuation == -1 ) {
1383 Debug( LDAP_DEBUG_ANY, "%s: "
1384 "illegal <continuation> value %s "
1385 "in \"chain-chaining\".\n",
1386 c->log, argv[ 0 ], 0 );
1390 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1394 Debug( LDAP_DEBUG_ANY, "%s: "
1395 "unknown option in \"chain-chaining\".\n",
1401 if ( resolve != -1 || continuation != -1 ) {
1404 if ( resolve == -1 ) {
1406 resolve = SLAP_CHAINING_DEFAULT;
1409 ber_init2( ber, NULL, LBER_USE_DER );
1411 err = ber_printf( ber, "{e" /* } */, resolve );
1414 Debug( LDAP_DEBUG_ANY, "%s: "
1415 "chaining behavior control encoding error!\n",
1420 if ( continuation > -1 ) {
1421 err = ber_printf( ber, "e", continuation );
1424 Debug( LDAP_DEBUG_ANY, "%s: "
1425 "chaining behavior control encoding error!\n",
1431 err = ber_printf( ber, /* { */ "N}" );
1434 Debug( LDAP_DEBUG_ANY, "%s: "
1435 "chaining behavior control encoding error!\n",
1440 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1441 exit( EXIT_FAILURE );
1445 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1448 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1449 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1451 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1453 Debug( LDAP_DEBUG_ANY, "%s: "
1454 "unable to parse chaining control%s%s.\n",
1455 c->log, rs.sr_text ? ": " : "",
1456 rs.sr_text ? rs.sr_text : "" );
1460 lc->lc_chaining_ctrlflag = op.o_chaining;
1462 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1465 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1466 Debug( LDAP_DEBUG_ANY, "%s: "
1467 "\"chaining\" control unsupported (ignored).\n",
1469 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1473 if ( c->value_int ) {
1474 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1476 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1481 if ( c->value_int < 0 ) {
1482 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1483 "<%s> invalid max referral depth %d",
1484 c->argv[0], c->value_int );
1485 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1486 c->log, c->cr_msg, 0 );
1490 lc->lc_max_depth = c->value_int;
1493 if ( c->value_int ) {
1494 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1496 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1512 slap_overinst *on = (slap_overinst *)be->bd_info;
1513 ldap_chain_t *lc = NULL;
1515 if ( lback == NULL ) {
1516 static BackendInfo lback2;
1518 lback = backend_info( "ldap" );
1520 if ( lback == NULL ) {
1525 lback2.bi_type = ldapchain.on_bi.bi_type;
1529 lc = ch_malloc( sizeof( ldap_chain_t ) );
1533 memset( lc, 0, sizeof( ldap_chain_t ) );
1534 lc->lc_max_depth = 1;
1535 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1537 on->on_bi.bi_private = (void *)lc;
1543 ldap_chain_db_config(
1550 slap_overinst *on = (slap_overinst *)be->bd_info;
1551 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1553 int rc = SLAP_CONF_UNKNOWN;
1555 if ( lc->lc_common_li == NULL ) {
1556 void *be_private = be->be_private;
1557 ldap_chain_db_init_common( be );
1558 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1559 be->be_private = be_private;
1562 /* Something for the chain database? */
1563 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1564 char *save_argv0 = argv[ 0 ];
1565 BackendInfo *bd_info = be->bd_info;
1566 void *be_private = be->be_private;
1567 ConfigOCs *be_cf_ocs = be->be_cf_ocs;
1568 static char *allowed_argv[] = {
1569 /* special: put URI here, so in the meanwhile
1570 * it detects whether a new URI is being provided */
1576 /* FIXME: maybe rebind-as-user should be allowed
1577 * only within known URIs... */
1584 int which_argv = -1;
1586 argv[ 0 ] += STRLENOF( "chain-" );
1588 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1589 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1594 if ( allowed_argv[ which_argv ] == NULL ) {
1597 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1598 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1599 "\"%s\" only allowed within a URI directive.\n.",
1600 fname, lineno, argv[ 0 ] );
1605 if ( which_argv == 0 ) {
1606 rc = ldap_chain_db_init_one( be );
1608 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1609 "underlying slapd-ldap initialization failed.\n.",
1613 lc->lc_cfg_li = be->be_private;
1616 /* TODO: add checks on what other slapd-ldap(5) args
1617 * should be put in the template; this is not quite
1618 * harmful, because attributes that shouldn't don't
1619 * get actually used, but the user should at least
1623 be->bd_info = lback;
1624 be->be_private = (void *)lc->lc_cfg_li;
1625 be->be_cf_ocs = lback->bi_cf_ocs;
1627 rc = config_generic_wrapper( be, fname, lineno, argc, argv );
1629 argv[ 0 ] = save_argv0;
1630 be->be_cf_ocs = be_cf_ocs;
1631 be->be_private = be_private;
1632 be->bd_info = bd_info;
1634 if ( which_argv == 0 ) {
1640 db.be_private = (void *)lc->lc_cfg_li;
1641 ldap_chain_db_destroy_one( &db, NULL );
1642 lc->lc_cfg_li = NULL;
1645 if ( lc->lc_cfg_li->li_bvuri == NULL
1646 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1647 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1649 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1650 "no URI list allowed in slapo-chain.\n",
1653 goto private_destroy;
1656 if ( avl_insert( &lc->lc_lai.lai_tree,
1657 (caddr_t)lc->lc_cfg_li,
1658 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1660 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1661 "duplicate URI in slapo-chain.\n",
1664 goto private_destroy;
1681 typedef struct ldap_chain_db_apply_t {
1684 } ldap_chain_db_apply_t;
1687 ldap_chain_db_apply( void *datum, void *arg )
1689 ldapinfo_t *li = (ldapinfo_t *)datum;
1690 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1692 lca->be->be_private = (void *)li;
1694 return lca->func( lca->be, NULL );
1703 slap_overinst *on = (slap_overinst *)be->bd_info;
1704 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1709 BI_db_func *func = (&lback->bi_db_open)[ which ];
1711 if ( func != NULL && lc->lc_common_li != NULL ) {
1715 db.be_private = lc->lc_common_li;
1717 rc = func( &db, NULL );
1723 if ( lc->lc_lai.lai_tree != NULL ) {
1724 ldap_chain_db_apply_t lca;
1729 rc = avl_apply( lc->lc_lai.lai_tree,
1730 ldap_chain_db_apply, (void *)&lca,
1731 1, AVL_INORDER ) != AVL_NOMORE;
1744 slap_overinst *on = (slap_overinst *) be->bd_info;
1745 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1746 slap_mask_t monitoring;
1749 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1750 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1754 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1756 if ( lc->lc_common_li == NULL ) {
1757 void *be_private = be->be_private;
1758 ldap_chain_db_init_common( be );
1759 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1760 be->be_private = be_private;
1763 /* filter out and restore monitoring */
1764 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1765 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1766 rc = ldap_chain_db_func( be, db_open );
1767 SLAP_DBFLAGS( be ) |= monitoring;
1773 ldap_chain_db_close(
1777 return ldap_chain_db_func( be, db_close );
1781 ldap_chain_db_destroy(
1785 slap_overinst *on = (slap_overinst *) be->bd_info;
1786 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1790 rc = ldap_chain_db_func( be, db_destroy );
1793 avl_free( lc->lc_lai.lai_tree, NULL );
1794 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1802 * inits one instance of the slapd-ldap backend, and stores
1803 * the private info in be_private of the arg
1806 ldap_chain_db_init_common(
1809 BackendInfo *bi = be->bd_info;
1813 be->bd_info = lback;
1814 be->be_private = NULL;
1815 rc = lback->bi_db_init( be, NULL );
1819 li = (ldapinfo_t *)be->be_private;
1820 li->li_urllist_f = NULL;
1821 li->li_urllist_p = NULL;
1829 * inits one instance of the slapd-ldap backend, stores
1830 * the private info in be_private of the arg and fills
1831 * selected fields with data from the template.
1833 * NOTE: add checks about the other fields of the template,
1834 * which are ignored and SHOULD NOT be configured by the user.
1837 ldap_chain_db_init_one(
1840 slap_overinst *on = (slap_overinst *)be->bd_info;
1841 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1843 BackendInfo *bi = be->bd_info;
1848 be->bd_info = lback;
1849 be->be_private = NULL;
1850 t = lback->bi_db_init( be, NULL );
1854 li = (ldapinfo_t *)be->be_private;
1855 li->li_urllist_f = NULL;
1856 li->li_urllist_p = NULL;
1858 /* copy common data */
1859 li->li_nretries = lc->lc_common_li->li_nretries;
1860 li->li_flags = lc->lc_common_li->li_flags;
1861 li->li_version = lc->lc_common_li->li_version;
1862 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
1863 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
1871 ldap_chain_db_open_one(
1874 if ( SLAP_DBMONITORING( be ) ) {
1875 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
1877 if ( li->li_uri == NULL ) {
1878 ber_str2bv( "cn=Common Connections", 0, 1,
1879 &li->li_monitor_info.lmi_rdn );
1884 li->li_monitor_info.lmi_rdn.bv_len
1885 = STRLENOF( "cn=" ) + strlen( li->li_uri );
1886 ptr = li->li_monitor_info.lmi_rdn.bv_val
1887 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
1888 ptr = lutil_strcopy( ptr, "cn=" );
1889 ptr = lutil_strcopy( ptr, li->li_uri );
1894 return lback->bi_db_open( be, NULL );
1897 typedef struct ldap_chain_conn_apply_t {
1900 } ldap_chain_conn_apply_t;
1903 ldap_chain_conn_apply( void *datum, void *arg )
1905 ldapinfo_t *li = (ldapinfo_t *)datum;
1906 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
1908 lca->be->be_private = (void *)li;
1910 return lback->bi_connection_destroy( lca->be, lca->conn );
1914 ldap_chain_connection_destroy(
1919 slap_overinst *on = (slap_overinst *) be->bd_info;
1920 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1921 void *private = be->be_private;
1922 ldap_chain_conn_apply_t lca;
1925 be->be_private = NULL;
1928 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
1929 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
1930 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
1931 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
1932 be->be_private = private;
1937 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1939 ldap_chain_parse_ctrl(
1949 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1950 rs->sr_text = "Chaining behavior control specified multiple times";
1951 return LDAP_PROTOCOL_ERROR;
1954 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1955 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1956 return LDAP_PROTOCOL_ERROR;
1959 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1960 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1965 /* Parse the control value
1966 * ChainingBehavior ::= SEQUENCE {
1967 * resolveBehavior Behavior OPTIONAL,
1968 * continuationBehavior Behavior OPTIONAL }
1970 * Behavior :: = ENUMERATED {
1971 * chainingPreferred (0),
1972 * chainingRequired (1),
1973 * referralsPreferred (2),
1974 * referralsRequired (3) }
1977 ber = ber_init( &ctrl->ldctl_value );
1979 rs->sr_text = "internal error";
1983 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1984 /* FIXME: since the whole SEQUENCE is optional,
1985 * should we accept no enumerations at all? */
1986 if ( tag != LBER_ENUMERATED ) {
1987 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1988 return LDAP_PROTOCOL_ERROR;
1991 switch ( behavior ) {
1992 case LDAP_CHAINING_PREFERRED:
1993 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1996 case LDAP_CHAINING_REQUIRED:
1997 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2000 case LDAP_REFERRALS_PREFERRED:
2001 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2004 case LDAP_REFERRALS_REQUIRED:
2005 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2009 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2010 return LDAP_PROTOCOL_ERROR;
2013 tag = ber_peek_tag( ber, &len );
2014 if ( tag == LBER_ENUMERATED ) {
2015 tag = ber_scanf( ber, "e", &behavior );
2016 if ( tag == LBER_ERROR ) {
2017 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2018 return LDAP_PROTOCOL_ERROR;
2022 if ( tag == LBER_DEFAULT ) {
2023 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2026 switch ( behavior ) {
2027 case LDAP_CHAINING_PREFERRED:
2028 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2031 case LDAP_CHAINING_REQUIRED:
2032 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2035 case LDAP_REFERRALS_PREFERRED:
2036 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2039 case LDAP_REFERRALS_REQUIRED:
2040 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2044 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2045 return LDAP_PROTOCOL_ERROR;
2049 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2050 rs->sr_text = "Chaining behavior control: decoding error";
2051 return LDAP_PROTOCOL_ERROR;
2054 (void) ber_free( ber, 1 );
2057 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2058 ? SLAP_CONTROL_CRITICAL
2059 : SLAP_CONTROL_NONCRITICAL );
2061 return LDAP_SUCCESS;
2063 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2066 chain_initialize( void )
2070 /* Make sure we don't exceed the bits reserved for userland */
2071 config_check_userland( CH_LAST );
2073 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2074 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2075 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2076 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2077 if ( rc != LDAP_SUCCESS ) {
2078 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2079 "unable to register chaining behavior control: %d.\n",
2083 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2085 ldapchain.on_bi.bi_type = "chain";
2086 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2087 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2088 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2089 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2090 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2092 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2094 ldapchain.on_response = ldap_chain_response;
2096 ldapchain.on_bi.bi_cf_ocs = chainocs;
2098 rc = config_register_schema( chaincfg, chainocs );
2103 return overlay_register( &ldapchain );