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) (lback)->bi_db_destroy( (be) )
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 ) {
452 ber_str2bv( srv->lud_dn, 0, 0, &dn );
453 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
454 if ( rc == LDAP_SUCCESS ) {
455 /* remove DN essentially because later on
456 * ldap_initialize() will parse the URL
457 * as a comma-separated URL list */
459 srv->lud_scope = LDAP_SCOPE_DEFAULT;
460 li.li_uri = ldap_url_desc2str( srv );
461 srv->lud_dn = dn.bv_val;
463 ldap_free_urldesc( srv );
465 if ( rc != LDAP_SUCCESS ) {
471 if ( li.li_uri == NULL ) {
474 goto further_cleanup;
480 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
482 /* Searches for a ldapinfo in the avl tree */
483 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
484 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
485 (caddr_t)&li, ldap_chain_uri_cmp );
486 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
489 op->o_bd->be_private = (void *)lip;
492 rc = ldap_chain_db_init_one( op->o_bd );
496 lip = (ldapinfo_t *)op->o_bd->be_private;
497 lip->li_uri = li.li_uri;
498 lip->li_bvuri = bvuri;
499 rc = ldap_chain_db_open_one( op->o_bd );
502 lip->li_bvuri = NULL;
503 (void)ldap_chain_db_destroy_one( op->o_bd );
507 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
508 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
509 if ( avl_insert( &lc->lc_lai.lai_tree,
510 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
512 /* someone just inserted another;
513 * don't bother, use this and then
517 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
525 lb->lb_depth = depth + 1;
527 rc = op_f( op, &rs2 );
529 /* note the first error */
530 if ( first_rc == -1 ) {
535 ldap_memfree( li.li_uri );
540 lip->li_bvuri = NULL;
541 (void)ldap_chain_db_close_one( op->o_bd );
542 (void)ldap_chain_db_destroy_one( op->o_bd );
546 if ( !BER_BVISNULL( &pdn ) ) {
547 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
549 op->o_req_dn = save_req_dn;
551 if ( !BER_BVISNULL( &ndn ) ) {
552 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
554 op->o_req_ndn = save_req_ndn;
556 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
564 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
565 (void)chaining_control_remove( op, &ctrls );
566 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
568 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
583 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
584 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
585 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
586 ldapinfo_t li = { 0 }, *lip = NULL;
587 struct berval bvuri[ 2 ] = { { 0 } };
589 struct berval odn = op->o_req_dn,
590 ondn = op->o_req_ndn;
591 slap_response *save_response = op->o_callback->sc_response;
596 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
597 LDAPControl **ctrls = NULL;
599 (void)chaining_control_add( lc, op, &ctrls );
600 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
602 rs->sr_type = REP_SEARCH;
604 op->o_callback->sc_response = ldap_chain_cb_search_response;
606 /* if we parse the URI then by no means
607 * we can cache stuff or reuse connections,
608 * because in back-ldap there's no caching
609 * based on the URI value, which is supposed
610 * to be set once for all (correct?) */
612 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
613 SlapReply rs2 = { 0 };
615 struct berval save_req_dn = op->o_req_dn,
616 save_req_ndn = op->o_req_ndn,
622 /* parse reference and use
623 * proto://[host][:port]/ only */
624 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
625 if ( rc != LDAP_URL_SUCCESS ) {
627 rs->sr_err = LDAP_OTHER;
632 ber_str2bv( srv->lud_dn, 0, 0, &dn );
633 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
634 if ( rc == LDAP_SUCCESS ) {
635 /* remove DN essentially because later on
636 * ldap_initialize() will parse the URL
637 * as a comma-separated URL list */
639 srv->lud_scope = LDAP_SCOPE_DEFAULT;
640 li.li_uri = ldap_url_desc2str( srv );
641 srv->lud_dn = dn.bv_val;
643 ldap_free_urldesc( srv );
645 if ( rc != LDAP_SUCCESS ) {
651 if ( li.li_uri == NULL ) {
654 goto further_cleanup;
660 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
662 /* Searches for a ldapinfo in the avl tree */
663 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
664 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
665 (caddr_t)&li, ldap_chain_uri_cmp );
666 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
669 op->o_bd->be_private = (void *)lip;
672 /* if none is found, create a temporary... */
673 rc = ldap_chain_db_init_one( op->o_bd );
677 lip = (ldapinfo_t *)op->o_bd->be_private;
678 lip->li_uri = li.li_uri;
679 lip->li_bvuri = bvuri;
680 rc = ldap_chain_db_open_one( op->o_bd );
683 lip->li_bvuri = NULL;
684 (void)ldap_chain_db_destroy_one( op->o_bd );
688 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
689 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
690 if ( avl_insert( &lc->lc_lai.lai_tree,
691 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
693 /* someone just inserted another;
694 * don't bother, use this and then
698 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
705 lb->lb_op_f = lback->bi_op_search;
706 lb->lb_depth = depth + 1;
708 /* FIXME: should we also copy filter and scope?
709 * according to RFC3296, no */
710 rc = lback->bi_op_search( op, &rs2 );
711 if ( first_rc == -1 ) {
716 ldap_memfree( li.li_uri );
719 op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
720 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
724 lip->li_bvuri = NULL;
725 (void)ldap_chain_db_close_one( op->o_bd );
726 (void)ldap_chain_db_destroy_one( op->o_bd );
730 if ( !BER_BVISNULL( &pdn ) ) {
731 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
733 op->o_req_dn = save_req_dn;
735 if ( !BER_BVISNULL( &ndn ) ) {
736 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
738 op->o_req_ndn = save_req_ndn;
740 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
748 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
749 (void)chaining_control_remove( op, &ctrls );
750 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
753 op->o_req_ndn = ondn;
754 op->o_callback->sc_response = save_response;
755 rs->sr_type = REP_SEARCHREF;
758 if ( rc != LDAP_SUCCESS ) {
759 /* couldn't chase any of the referrals */
760 if ( first_rc != -1 ) {
764 rc = SLAP_CB_CONTINUE;
772 ldap_chain_response( Operation *op, SlapReply *rs )
774 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
775 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
776 BackendDB db, *bd = op->o_bd;
777 ldap_chain_cb_t lb = { 0 };
778 slap_callback *sc = op->o_callback,
781 const char *text = NULL;
784 struct berval ndn = op->o_ndn;
786 int sr_err = rs->sr_err;
787 slap_reply_t sr_type = rs->sr_type;
788 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
789 slap_mask_t chain_mask = 0;
790 ber_len_t chain_shift = 0;
791 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
793 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
794 return SLAP_CB_CONTINUE;
797 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
798 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
799 switch ( get_resolveBehavior( op ) ) {
800 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
801 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
802 return SLAP_CB_CONTINUE;
805 chain_mask = SLAP_CH_RESOLVE_MASK;
806 chain_shift = SLAP_CH_RESOLVE_SHIFT;
810 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
811 switch ( get_continuationBehavior( op ) ) {
812 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
813 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
814 return SLAP_CB_CONTINUE;
817 chain_mask = SLAP_CH_CONTINUATION_MASK;
818 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
822 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
825 * TODO: add checks on who/when chain operations; e.g.:
826 * a) what identities are authorized
827 * b) what request DN (e.g. only chain requests rooted at <DN>)
828 * c) what referral URIs
829 * d) what protocol scheme (e.g. only ldaps://)
834 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
839 matched = rs->sr_matched;
840 rs->sr_matched = NULL;
844 /* we need this to know if back-ldap returned any result */
846 sc2.sc_private = &lb;
847 sc2.sc_response = ldap_chain_cb_response;
848 op->o_callback = &sc2;
850 /* Chaining can be performed by a privileged user on behalf
851 * of normal users, using the ProxyAuthz control, by exploiting
852 * the identity assertion feature of back-ldap; see idassert-*
853 * directives in slapd-ldap(5).
855 * FIXME: the idassert-authcDN is one, will it be fine regardless
856 * of the URI we obtain from the referral?
859 switch ( op->o_tag ) {
860 case LDAP_REQ_BIND: {
861 struct berval rndn = op->o_req_ndn;
862 Connection *conn = op->o_conn;
864 /* FIXME: can we really get a referral for binds? */
865 op->o_req_ndn = slap_empty_bv;
867 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
868 op->o_req_ndn = rndn;
874 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
877 case LDAP_REQ_DELETE:
878 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
881 case LDAP_REQ_MODRDN:
882 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
885 case LDAP_REQ_MODIFY:
886 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
889 case LDAP_REQ_COMPARE:
890 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
891 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
896 case LDAP_REQ_SEARCH:
897 if ( rs->sr_type == REP_SEARCHREF ) {
898 rc = ldap_chain_search( op, rs, ref, 0 );
901 /* we might get here before any database actually
902 * performed a search; in those cases, we need
903 * to check limits, to make sure safe defaults
905 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
906 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
909 rc = SLAP_CB_CONTINUE;
914 case LDAP_REQ_EXTENDED:
915 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
916 /* FIXME: ldap_back_extended() by design
917 * doesn't send result; frontend is expected
919 /* FIXME: what about chaining? */
920 if ( rc != SLAPD_ABANDON ) {
921 send_ldap_extended( op, rs );
924 lb.lb_status = LDAP_CH_RES;
928 rc = SLAP_CB_CONTINUE;
938 /* slapd-ldap sent response */
939 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
940 /* FIXME: should we send response? */
941 Debug( LDAP_DEBUG_ANY,
942 "%s: ldap_chain_response: "
943 "overlay should have sent result.\n",
944 op->o_log_prefix, 0, 0 );
949 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
950 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
954 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
955 case LDAP_CHAINING_REQUIRED:
957 op->o_callback = NULL;
958 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
959 "operation cannot be completed without chaining" );
963 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
964 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
966 rs->sr_type = sr_type;
969 rc = SLAP_CB_CONTINUE;
971 rs->sr_type = sr_type;
973 rs->sr_matched = matched;
976 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
979 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
982 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
983 op->o_callback = NULL;
984 rc = rs->sr_err = slap_map_api2result( rs );
985 send_ldap_result( op, rs );
990 rs->sr_type = sr_type;
992 rs->sr_matched = matched;
1001 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1003 ldap_chain_parse_ctrl(
1006 LDAPControl *ctrl );
1009 str2chain( const char *s )
1011 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1012 return LDAP_CHAINING_PREFERRED;
1014 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1015 return LDAP_CHAINING_REQUIRED;
1017 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1018 return LDAP_REFERRALS_PREFERRED;
1020 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1021 return LDAP_REFERRALS_REQUIRED;
1026 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1041 static ConfigDriver chain_cf_gen;
1042 static ConfigCfAdd chain_cfadd;
1043 static ConfigLDAPadd chain_ldadd;
1045 static ConfigTable chaincfg[] = {
1046 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1047 { "chain-chaining", "args",
1048 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1049 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1050 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1051 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1052 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1053 { "chain-cache-uri", "TRUE/FALSE",
1054 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1055 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1056 "DESC 'Enables caching of URIs not present in configuration' "
1057 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1058 { "chain-max-depth", "args",
1059 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1060 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1061 "DESC 'max referral depth' "
1062 "SYNTAX OMsInteger "
1063 "EQUALITY integerMatch "
1064 "SINGLE-VALUE )", NULL, NULL },
1065 { "chain-return-error", "TRUE/FALSE",
1066 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1067 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1068 "DESC 'Errors are returned instead of the original referral' "
1069 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1070 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1073 static ConfigOCs chainocs[] = {
1074 { "( OLcfgOvOc:3.1 "
1075 "NAME 'olcChainConfig' "
1076 "DESC 'Chain configuration' "
1077 "SUP olcOverlayConfig "
1079 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1080 "olcChainingBehavior $ "
1081 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1082 "olcChainCacheURI $ "
1083 "olcChainMaxReferralDepth $ "
1084 "olcChainReturnError "
1086 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1087 { "( OLcfgOvOc:3.2 "
1088 "NAME 'olcChainDatabase' "
1089 "DESC 'Chain remote server configuration' "
1091 Cft_Misc, chaincfg, chain_ldadd },
1096 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1103 AttributeDescription *ad = NULL;
1109 if ( p->ce_type != Cft_Overlay
1111 || p->ce_bi->bi_cf_ocs != chainocs )
1113 return LDAP_CONSTRAINT_VIOLATION;
1116 on = (slap_overinst *)p->ce_bi;
1117 lc = (ldap_chain_t *)on->on_bi.bi_private;
1119 assert( ca->be == NULL );
1120 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1122 ca->be->bd_info = (BackendInfo *)on;
1124 rc = slap_str2ad( "olcDbURI", &ad, &text );
1125 assert( rc == LDAP_SUCCESS );
1127 at = attr_find( e->e_attrs, ad );
1128 if ( lc->lc_common_li == NULL && at != NULL ) {
1129 /* FIXME: we should generate an empty default entry
1130 * if none is supplied */
1131 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1132 "first underlying database \"%s\" "
1133 "cannot contain attribute \"%s\".\n",
1134 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1135 rc = LDAP_CONSTRAINT_VIOLATION;
1138 } else if ( lc->lc_common_li != NULL && at == NULL ) {
1139 /* FIXME: we should generate an empty default entry
1140 * if none is supplied */
1141 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1142 "subsequent underlying database \"%s\" "
1143 "must contain attribute \"%s\".\n",
1144 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1145 rc = LDAP_CONSTRAINT_VIOLATION;
1149 if ( lc->lc_common_li == NULL ) {
1150 rc = ldap_chain_db_init_common( ca->be );
1153 rc = ldap_chain_db_init_one( ca->be );
1157 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1158 "unable to init %sunderlying database \"%s\".\n",
1159 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1160 return LDAP_CONSTRAINT_VIOLATION;
1163 li = ca->be->be_private;
1165 if ( lc->lc_common_li == NULL ) {
1166 lc->lc_common_li = li;
1169 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1170 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1171 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1172 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1174 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1175 "database \"%s\" insert failed.\n",
1176 e->e_name.bv_val, 0, 0 );
1177 rc = LDAP_CONSTRAINT_VIOLATION;
1183 if ( rc != LDAP_SUCCESS ) {
1184 (void)ldap_chain_db_destroy_one( ca->be );
1192 typedef struct ldap_chain_cfadd_apply_t {
1198 } ldap_chain_cfadd_apply_t;
1201 ldap_chain_cfadd_apply( void *datum, void *arg )
1203 ldapinfo_t *li = (ldapinfo_t *)datum;
1204 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1208 /* FIXME: should not hardcode "olcDatabase" here */
1209 bv.bv_len = snprintf( lca->ca->msg, sizeof( lca->ca->msg ),
1210 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1211 bv.bv_val = lca->ca->msg;
1213 lca->ca->be->be_private = (void *)li;
1214 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1215 &bv, lback->bi_cf_ocs, &chainocs[1] );
1223 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1225 CfEntryInfo *pe = p->e_private;
1226 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1227 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1228 void *priv = (void *)ca->be->be_private;
1230 if ( lback->bi_cf_ocs ) {
1231 ldap_chain_cfadd_apply_t lca = { 0 };
1239 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1241 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1242 &lca, 1, AVL_INORDER );
1244 ca->be->be_private = priv;
1250 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1251 static slap_verbmasks chaining_mode[] = {
1252 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1253 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1254 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1255 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1258 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1261 chain_cf_gen( ConfigArgs *c )
1263 slap_overinst *on = (slap_overinst *)c->bi;
1264 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1268 if ( c->op == SLAP_CONFIG_EMIT ) {
1270 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1272 struct berval resolve = BER_BVNULL,
1273 continuation = BER_BVNULL;
1275 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1279 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1280 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1282 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1284 + STRLENOF( "continuation=" ) + continuation.bv_len;
1285 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1286 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1287 "resolve=%s continuation=%s",
1288 resolve.bv_val, continuation.bv_val );
1290 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1291 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1292 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1293 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1294 " critical", STRLENOF( " critical" ) + 1 );
1295 c->value_bv.bv_len += STRLENOF( " critical" );
1300 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1303 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1307 c->value_int = lc->lc_max_depth;
1311 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1320 } else if ( c->op == LDAP_MOD_DELETE ) {
1326 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1334 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1345 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1346 char **argv = c->argv;
1348 BerElementBuffer berbuf;
1349 BerElement *ber = (BerElement *)&berbuf;
1353 Operation op = { 0 };
1354 SlapReply rs = { 0 };
1356 lc->lc_chaining_ctrlflag = 0;
1358 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1359 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1360 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1361 if ( resolve == -1 ) {
1362 Debug( LDAP_DEBUG_ANY, "%s: "
1363 "illegal <resolve> value %s "
1364 "in \"chain-chaining>\".\n",
1365 c->log, argv[ 0 ], 0 );
1369 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1370 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1371 if ( continuation == -1 ) {
1372 Debug( LDAP_DEBUG_ANY, "%s: "
1373 "illegal <continuation> value %s "
1374 "in \"chain-chaining\".\n",
1375 c->log, argv[ 0 ], 0 );
1379 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1383 Debug( LDAP_DEBUG_ANY, "%s: "
1384 "unknown option in \"chain-chaining\".\n",
1390 if ( resolve != -1 || continuation != -1 ) {
1393 if ( resolve == -1 ) {
1395 resolve = SLAP_CHAINING_DEFAULT;
1398 ber_init2( ber, NULL, LBER_USE_DER );
1400 err = ber_printf( ber, "{e" /* } */, resolve );
1403 Debug( LDAP_DEBUG_ANY, "%s: "
1404 "chaining behavior control encoding error!\n",
1409 if ( continuation > -1 ) {
1410 err = ber_printf( ber, "e", continuation );
1413 Debug( LDAP_DEBUG_ANY, "%s: "
1414 "chaining behavior control encoding error!\n",
1420 err = ber_printf( ber, /* { */ "N}" );
1423 Debug( LDAP_DEBUG_ANY, "%s: "
1424 "chaining behavior control encoding error!\n",
1429 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1430 exit( EXIT_FAILURE );
1434 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1437 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1438 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1440 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1442 Debug( LDAP_DEBUG_ANY, "%s: "
1443 "unable to parse chaining control%s%s.\n",
1444 c->log, rs.sr_text ? ": " : "",
1445 rs.sr_text ? rs.sr_text : "" );
1449 lc->lc_chaining_ctrlflag = op.o_chaining;
1451 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1454 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1455 Debug( LDAP_DEBUG_ANY, "%s: "
1456 "\"chaining\" control unsupported (ignored).\n",
1458 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1462 if ( c->value_int ) {
1463 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1465 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1470 if ( c->value_int < 0 ) {
1471 snprintf( c->msg, sizeof( c->msg ),
1472 "<%s> invalid max referral depth %d",
1473 c->argv[0], c->value_int );
1474 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1475 c->log, c->msg, 0 );
1479 lc->lc_max_depth = c->value_int;
1482 if ( c->value_int ) {
1483 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1485 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1500 slap_overinst *on = (slap_overinst *)be->bd_info;
1501 ldap_chain_t *lc = NULL;
1503 if ( lback == NULL ) {
1504 static BackendInfo lback2;
1506 lback = backend_info( "ldap" );
1508 if ( lback == NULL ) {
1513 lback2.bi_type = ldapchain.on_bi.bi_type;
1517 lc = ch_malloc( sizeof( ldap_chain_t ) );
1521 memset( lc, 0, sizeof( ldap_chain_t ) );
1522 lc->lc_max_depth = 1;
1523 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1525 on->on_bi.bi_private = (void *)lc;
1531 ldap_chain_db_config(
1538 slap_overinst *on = (slap_overinst *)be->bd_info;
1539 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1541 int rc = SLAP_CONF_UNKNOWN;
1543 if ( lc->lc_common_li == NULL ) {
1544 void *be_private = be->be_private;
1545 ldap_chain_db_init_common( be );
1546 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1547 be->be_private = be_private;
1550 /* Something for the chain database? */
1551 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1552 char *save_argv0 = argv[ 0 ];
1553 BackendInfo *bd_info = be->bd_info;
1554 void *be_private = be->be_private;
1555 ConfigOCs *be_cf_ocs = be->be_cf_ocs;
1556 static char *allowed_argv[] = {
1557 /* special: put URI here, so in the meanwhile
1558 * it detects whether a new URI is being provided */
1564 /* FIXME: maybe rebind-as-user should be allowed
1565 * only within known URIs... */
1572 int which_argv = -1;
1574 argv[ 0 ] += STRLENOF( "chain-" );
1576 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1577 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1582 if ( allowed_argv[ which_argv ] == NULL ) {
1585 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1586 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1587 "\"%s\" only allowed within a URI directive.\n.",
1588 fname, lineno, argv[ 0 ] );
1593 if ( which_argv == 0 ) {
1594 rc = ldap_chain_db_init_one( be );
1596 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1597 "underlying slapd-ldap initialization failed.\n.",
1601 lc->lc_cfg_li = be->be_private;
1604 /* TODO: add checks on what other slapd-ldap(5) args
1605 * should be put in the template; this is not quite
1606 * harmful, because attributes that shouldn't don't
1607 * get actually used, but the user should at least
1611 be->bd_info = lback;
1612 be->be_private = (void *)lc->lc_cfg_li;
1613 be->be_cf_ocs = lback->bi_cf_ocs;
1615 rc = config_generic_wrapper( be, fname, lineno, argc, argv );
1617 argv[ 0 ] = save_argv0;
1618 be->be_cf_ocs = be_cf_ocs;
1619 be->be_private = be_private;
1620 be->bd_info = bd_info;
1622 if ( which_argv == 0 ) {
1628 db.be_private = (void *)lc->lc_cfg_li;
1629 ldap_chain_db_destroy_one( &db );
1630 lc->lc_cfg_li = NULL;
1633 if ( lc->lc_cfg_li->li_bvuri == NULL
1634 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1635 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1637 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1638 "no URI list allowed in slapo-chain.\n",
1641 goto private_destroy;
1644 if ( avl_insert( &lc->lc_lai.lai_tree,
1645 (caddr_t)lc->lc_cfg_li,
1646 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1648 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1649 "duplicate URI in slapo-chain.\n",
1652 goto private_destroy;
1669 typedef struct ldap_chain_db_apply_t {
1672 } ldap_chain_db_apply_t;
1675 ldap_chain_db_apply( void *datum, void *arg )
1677 ldapinfo_t *li = (ldapinfo_t *)datum;
1678 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1680 lca->be->be_private = (void *)li;
1682 return lca->func( lca->be );
1691 slap_overinst *on = (slap_overinst *)be->bd_info;
1692 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1697 BI_db_func *func = (&lback->bi_db_open)[ which ];
1699 if ( func != NULL && lc->lc_common_li != NULL ) {
1703 db.be_private = lc->lc_common_li;
1711 if ( lc->lc_lai.lai_tree != NULL ) {
1712 ldap_chain_db_apply_t lca;
1717 rc = avl_apply( lc->lc_lai.lai_tree,
1718 ldap_chain_db_apply, (void *)&lca,
1719 1, AVL_INORDER ) != AVL_NOMORE;
1731 slap_overinst *on = (slap_overinst *) be->bd_info;
1732 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1733 slap_mask_t monitoring;
1736 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1737 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1741 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1743 if ( lc->lc_common_li == NULL ) {
1744 void *be_private = be->be_private;
1745 ldap_chain_db_init_common( be );
1746 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1747 be->be_private = be_private;
1750 /* filter out and restore monitoring */
1751 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1752 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1753 rc = ldap_chain_db_func( be, db_open );
1754 SLAP_DBFLAGS( be ) |= monitoring;
1760 ldap_chain_db_close(
1763 return ldap_chain_db_func( be, db_close );
1767 ldap_chain_db_destroy(
1770 slap_overinst *on = (slap_overinst *) be->bd_info;
1771 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1775 rc = ldap_chain_db_func( be, db_destroy );
1778 avl_free( lc->lc_lai.lai_tree, NULL );
1779 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1787 * inits one instance of the slapd-ldap backend, and stores
1788 * the private info in be_private of the arg
1791 ldap_chain_db_init_common(
1794 BackendInfo *bi = be->bd_info;
1798 be->bd_info = lback;
1799 be->be_private = NULL;
1800 rc = lback->bi_db_init( be );
1804 li = (ldapinfo_t *)be->be_private;
1805 li->li_urllist_f = NULL;
1806 li->li_urllist_p = NULL;
1814 * inits one instance of the slapd-ldap backend, stores
1815 * the private info in be_private of the arg and fills
1816 * selected fields with data from the template.
1818 * NOTE: add checks about the other fields of the template,
1819 * which are ignored and SHOULD NOT be configured by the user.
1822 ldap_chain_db_init_one(
1825 slap_overinst *on = (slap_overinst *)be->bd_info;
1826 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1828 BackendInfo *bi = be->bd_info;
1833 be->bd_info = lback;
1834 be->be_private = NULL;
1835 t = lback->bi_db_init( be );
1839 li = (ldapinfo_t *)be->be_private;
1840 li->li_urllist_f = NULL;
1841 li->li_urllist_p = NULL;
1843 /* copy common data */
1844 li->li_nretries = lc->lc_common_li->li_nretries;
1845 li->li_flags = lc->lc_common_li->li_flags;
1846 li->li_version = lc->lc_common_li->li_version;
1847 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
1848 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
1856 ldap_chain_db_open_one(
1859 if ( SLAP_DBMONITORING( be ) ) {
1860 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
1862 if ( li->li_uri == NULL ) {
1863 ber_str2bv( "cn=Common Connections", 0, 1,
1864 &li->li_monitor_info.lmi_rdn );
1869 li->li_monitor_info.lmi_rdn.bv_len
1870 = STRLENOF( "cn=" ) + strlen( li->li_uri );
1871 ptr = li->li_monitor_info.lmi_rdn.bv_val
1872 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
1873 ptr = lutil_strcopy( ptr, "cn=" );
1874 ptr = lutil_strcopy( ptr, li->li_uri );
1879 return lback->bi_db_open( be );
1882 typedef struct ldap_chain_conn_apply_t {
1885 } ldap_chain_conn_apply_t;
1888 ldap_chain_conn_apply( void *datum, void *arg )
1890 ldapinfo_t *li = (ldapinfo_t *)datum;
1891 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
1893 lca->be->be_private = (void *)li;
1895 return lback->bi_connection_destroy( lca->be, lca->conn );
1899 ldap_chain_connection_destroy(
1904 slap_overinst *on = (slap_overinst *) be->bd_info;
1905 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1906 void *private = be->be_private;
1907 ldap_chain_conn_apply_t lca;
1910 be->be_private = NULL;
1913 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
1914 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
1915 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
1916 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
1917 be->be_private = private;
1922 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1924 ldap_chain_parse_ctrl(
1934 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1935 rs->sr_text = "Chaining behavior control specified multiple times";
1936 return LDAP_PROTOCOL_ERROR;
1939 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1940 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1941 return LDAP_PROTOCOL_ERROR;
1944 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1945 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1950 /* Parse the control value
1951 * ChainingBehavior ::= SEQUENCE {
1952 * resolveBehavior Behavior OPTIONAL,
1953 * continuationBehavior Behavior OPTIONAL }
1955 * Behavior :: = ENUMERATED {
1956 * chainingPreferred (0),
1957 * chainingRequired (1),
1958 * referralsPreferred (2),
1959 * referralsRequired (3) }
1962 ber = ber_init( &ctrl->ldctl_value );
1964 rs->sr_text = "internal error";
1968 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1969 /* FIXME: since the whole SEQUENCE is optional,
1970 * should we accept no enumerations at all? */
1971 if ( tag != LBER_ENUMERATED ) {
1972 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1973 return LDAP_PROTOCOL_ERROR;
1976 switch ( behavior ) {
1977 case LDAP_CHAINING_PREFERRED:
1978 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1981 case LDAP_CHAINING_REQUIRED:
1982 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1985 case LDAP_REFERRALS_PREFERRED:
1986 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1989 case LDAP_REFERRALS_REQUIRED:
1990 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1994 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1995 return LDAP_PROTOCOL_ERROR;
1998 tag = ber_peek_tag( ber, &len );
1999 if ( tag == LBER_ENUMERATED ) {
2000 tag = ber_scanf( ber, "e", &behavior );
2001 if ( tag == LBER_ERROR ) {
2002 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2003 return LDAP_PROTOCOL_ERROR;
2007 if ( tag == LBER_DEFAULT ) {
2008 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2011 switch ( behavior ) {
2012 case LDAP_CHAINING_PREFERRED:
2013 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2016 case LDAP_CHAINING_REQUIRED:
2017 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2020 case LDAP_REFERRALS_PREFERRED:
2021 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2024 case LDAP_REFERRALS_REQUIRED:
2025 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2029 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2030 return LDAP_PROTOCOL_ERROR;
2034 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2035 rs->sr_text = "Chaining behavior control: decoding error";
2036 return LDAP_PROTOCOL_ERROR;
2039 (void) ber_free( ber, 1 );
2042 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2043 ? SLAP_CONTROL_CRITICAL
2044 : SLAP_CONTROL_NONCRITICAL );
2046 return LDAP_SUCCESS;
2048 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2051 chain_initialize( void )
2055 /* Make sure we don't exceed the bits reserved for userland */
2056 config_check_userland( CH_LAST );
2058 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2059 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2060 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2061 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2062 if ( rc != LDAP_SUCCESS ) {
2063 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2064 "unable to register chaining behavior control: %d.\n",
2068 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2070 ldapchain.on_bi.bi_type = "chain";
2071 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2072 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2073 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2074 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2075 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2077 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2079 ldapchain.on_response = ldap_chain_response;
2081 ldapchain.on_bi.bi_cf_ocs = chainocs;
2083 rc = config_register_schema( chaincfg, chainocs );
2088 return overlay_register( &ldapchain );