1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2009 The OpenLDAP Foundation.
6 * Portions Copyright 2003 Howard Chu.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
18 * This work was initially developed by the Howard Chu for inclusion
19 * in OpenLDAP Software.
20 * This work was subsequently modified by Pierangelo Masarati.
27 #include <ac/string.h>
28 #include <ac/socket.h>
32 #include "back-ldap.h"
35 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
36 #define SLAP_CHAINING_DEFAULT LDAP_CHAINING_PREFERRED
37 #define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT
38 #define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT)
39 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
40 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
41 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
42 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
43 #define SLAP_CH_RESOLVE_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
44 #define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2)
45 #define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT)
46 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
47 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
48 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
49 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
50 #define SLAP_CH_CONTINUATION_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
52 #define o_chaining o_ctrlflag[sc_chainingBehavior]
53 #define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK)
54 #define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
55 #define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
56 #define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
58 static int sc_chainingBehavior;
59 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
65 } ldap_chain_status_t;
67 static BackendInfo *lback;
69 typedef struct ldap_chain_t {
71 * A "template" ldapinfo_t gets all common configuration items;
72 * then, for each configured URI, an entry is created in the tree;
73 * all the specific configuration items get in the current URI
76 * Then, for each referral, extract the URI and lookup the
77 * related structure. If configured to do so, allow URIs
78 * not found in the structure to create a temporary one
79 * that chains anonymously; maybe it can also be added to
80 * the tree? Should be all configurable.
83 /* "common" configuration info (anything occurring before an "uri") */
84 ldapinfo_t *lc_common_li;
86 /* current configuration info */
87 ldapinfo_t *lc_cfg_li;
89 /* tree of configured[/generated?] "uri" info */
90 ldap_avl_info_t lc_lai;
92 /* max depth in nested referrals chaining */
96 #define LDAP_CHAIN_F_NONE (0x00U)
97 #define LDAP_CHAIN_F_CHAINING (0x01U)
98 #define LDAP_CHAIN_F_CACHE_URI (0x02U)
99 #define LDAP_CHAIN_F_RETURN_ERR (0x04U)
101 #define LDAP_CHAIN_ISSET(lc, f) ( ( (lc)->lc_flags & (f) ) == (f) )
102 #define LDAP_CHAIN_CHAINING( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
103 #define LDAP_CHAIN_CACHE_URI( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
104 #define LDAP_CHAIN_RETURN_ERR( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
106 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
107 LDAPControl lc_chaining_ctrl;
108 char lc_chaining_ctrlflag;
109 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
112 static int ldap_chain_db_init_common( BackendDB *be );
113 static int ldap_chain_db_init_one( BackendDB *be );
114 static int ldap_chain_db_open_one( BackendDB *be );
115 #define ldap_chain_db_close_one(be) (0)
116 #define ldap_chain_db_destroy_one(be, rs) (lback)->bi_db_destroy( (be), (rs) )
118 typedef struct ldap_chain_cb_t {
119 ldap_chain_status_t lb_status;
140 static slap_overinst ldapchain;
142 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
144 chaining_control_add(
147 LDAPControl ***oldctrlsp )
149 LDAPControl **ctrls = NULL;
152 *oldctrlsp = op->o_ctrls;
154 /* default chaining control not defined */
155 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
159 /* already present */
160 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
164 /* FIXME: check other incompatibilities */
166 /* add to other controls */
168 for ( c = 0; op->o_ctrls[ c ]; c++ )
172 ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
173 ctrls[ 0 ] = &lc->lc_chaining_ctrl;
175 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
176 ctrls[ c + 1 ] = op->o_ctrls[ c ];
179 ctrls[ c + 1 ] = NULL;
183 op->o_chaining = lc->lc_chaining_ctrlflag;
189 chaining_control_remove(
191 LDAPControl ***oldctrlsp )
193 LDAPControl **oldctrls = *oldctrlsp;
195 /* we assume that the first control is the chaining control
196 * added by the chain overlay, so it's the only one we explicitly
198 if ( op->o_ctrls != oldctrls ) {
199 assert( op->o_ctrls != NULL );
200 assert( op->o_ctrls[ 0 ] != NULL );
205 op->o_ctrls = oldctrls;
212 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
215 ldap_chain_uri_cmp( const void *c1, const void *c2 )
217 const ldapinfo_t *li1 = (const ldapinfo_t *)c1;
218 const ldapinfo_t *li2 = (const ldapinfo_t *)c2;
220 assert( li1->li_bvuri != NULL );
221 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
222 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
224 assert( li2->li_bvuri != NULL );
225 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
226 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
228 /* If local DNs don't match, it is definitely not a match */
229 return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
233 ldap_chain_uri_dup( void *c1, void *c2 )
235 ldapinfo_t *li1 = (ldapinfo_t *)c1;
236 ldapinfo_t *li2 = (ldapinfo_t *)c2;
238 assert( li1->li_bvuri != NULL );
239 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
240 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
242 assert( li2->li_bvuri != NULL );
243 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
244 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
246 /* Cannot have more than one shared session with same DN */
247 if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
255 * Search specific response that strips entryDN from entries
258 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
260 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
262 assert( op->o_tag == LDAP_REQ_SEARCH );
264 /* if in error, don't proceed any further */
265 if ( lb->lb_status == LDAP_CH_ERR ) {
269 if ( rs->sr_type == REP_SEARCH ) {
270 Attribute **ap = &rs->sr_entry->e_attrs;
272 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
273 /* will be generated later by frontend
274 * (a cleaner solution would be that
275 * the frontend checks if it already exists */
276 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
283 /* there SHOULD be one only! */
288 /* tell the frontend not to add generated
289 * operational attributes */
290 rs->sr_flags |= REP_NO_OPERATIONALS;
292 return SLAP_CB_CONTINUE;
294 } else if ( rs->sr_type == REP_SEARCHREF ) {
295 /* if we get it here, it means the library was unable
296 * to chase the referral... */
297 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
298 rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
301 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
302 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
303 switch ( get_continuationBehavior( op ) ) {
304 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
305 lb->lb_status = LDAP_CH_ERR;
306 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
312 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
313 return SLAP_CB_CONTINUE;
315 } else if ( rs->sr_type == REP_RESULT ) {
316 if ( rs->sr_err == LDAP_REFERRAL
317 && lb->lb_depth < lb->lb_lc->lc_max_depth
318 && rs->sr_ref != NULL )
320 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
323 /* back-ldap tried to send result */
324 lb->lb_status = LDAP_CH_RES;
331 * Dummy response that simply traces if back-ldap tried to send
332 * anything to the client
335 ldap_chain_cb_response( Operation *op, SlapReply *rs )
337 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
339 /* if in error, don't proceed any further */
340 if ( lb->lb_status == LDAP_CH_ERR ) {
344 if ( rs->sr_type == REP_RESULT ) {
346 switch ( rs->sr_err ) {
347 case LDAP_COMPARE_TRUE:
348 case LDAP_COMPARE_FALSE:
349 if ( op->o_tag != LDAP_REQ_COMPARE ) {
355 lb->lb_status = LDAP_CH_RES;
359 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
360 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
364 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
365 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
366 switch ( get_continuationBehavior( op ) ) {
367 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
368 lb->lb_status = LDAP_CH_ERR;
369 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
375 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
382 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
384 /* strip the entryDN attribute, but keep returning results */
385 (void)ldap_chain_cb_search_response( op, rs );
388 return SLAP_CB_CONTINUE;
399 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
400 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
401 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
402 ldapinfo_t li = { 0 }, *lip = NULL;
403 struct berval bvuri[ 2 ] = { { 0 } };
405 /* NOTE: returned if ref is empty... */
409 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
410 LDAPControl **ctrls = NULL;
412 (void)chaining_control_add( lc, op, &ctrls );
413 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
417 for ( ; !BER_BVISNULL( ref ); ref++ ) {
418 SlapReply rs2 = { 0 };
419 LDAPURLDesc *srv = NULL;
420 struct berval save_req_dn = op->o_req_dn,
421 save_req_ndn = op->o_req_ndn,
427 /* We're setting the URI of the first referral;
428 * what if there are more?
434 If the client wishes to progress the operation, it MUST follow the
435 referral by contacting one of the supported services. If multiple
436 URIs are present, the client assumes that any supported URI may be
437 used to progress the operation.
439 * so we actually need to follow exactly one,
440 * and we can assume any is fine.
443 /* parse reference and use
444 * proto://[host][:port]/ only */
445 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
446 if ( rc != LDAP_URL_SUCCESS ) {
454 srv->lud_scope = LDAP_SCOPE_DEFAULT;
455 if ( srv->lud_dn != NULL ) {
456 ber_str2bv( srv->lud_dn, 0, 0, &dn );
457 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
458 if ( rc == LDAP_SUCCESS ) {
459 /* remove DN essentially because later on
460 * ldap_initialize() will parse the URL
461 * as a comma-separated URL list */
469 li.li_uri = ldap_url_desc2str( srv );
470 srv->lud_dn = dn.bv_val;
471 ldap_free_urldesc( srv );
473 if ( rc != LDAP_SUCCESS ) {
479 if ( li.li_uri == NULL ) {
482 goto further_cleanup;
488 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
490 /* Searches for a ldapinfo in the avl tree */
491 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
492 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
493 (caddr_t)&li, ldap_chain_uri_cmp );
494 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
497 op->o_bd->be_private = (void *)lip;
500 rc = ldap_chain_db_init_one( op->o_bd );
504 lip = (ldapinfo_t *)op->o_bd->be_private;
505 lip->li_uri = li.li_uri;
506 lip->li_bvuri = bvuri;
507 rc = ldap_chain_db_open_one( op->o_bd );
510 lip->li_bvuri = NULL;
511 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
515 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
516 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
517 if ( avl_insert( &lc->lc_lai.lai_tree,
518 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
520 /* someone just inserted another;
521 * don't bother, use this and then
525 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
533 lb->lb_depth = depth + 1;
535 rc = op_f( op, &rs2 );
537 /* note the first error */
538 if ( first_rc == -1 ) {
543 ldap_memfree( li.li_uri );
548 lip->li_bvuri = NULL;
549 (void)ldap_chain_db_close_one( op->o_bd );
550 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
554 if ( !BER_BVISNULL( &pdn ) ) {
555 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
557 op->o_req_dn = save_req_dn;
559 if ( !BER_BVISNULL( &ndn ) ) {
560 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
562 op->o_req_ndn = save_req_ndn;
564 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
572 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
573 (void)chaining_control_remove( op, &ctrls );
574 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
576 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
591 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
592 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
593 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
594 ldapinfo_t li = { 0 }, *lip = NULL;
595 struct berval bvuri[ 2 ] = { { 0 } };
597 struct berval odn = op->o_req_dn,
598 ondn = op->o_req_ndn;
599 slap_response *save_response = op->o_callback->sc_response;
600 Entry *save_entry = rs->sr_entry;
601 slap_mask_t save_flags = rs->sr_flags;
606 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
607 LDAPControl **ctrls = NULL;
609 (void)chaining_control_add( lc, op, &ctrls );
610 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
612 rs->sr_type = REP_SEARCH;
614 op->o_callback->sc_response = ldap_chain_cb_search_response;
616 /* if we parse the URI then by no means
617 * we can cache stuff or reuse connections,
618 * because in back-ldap there's no caching
619 * based on the URI value, which is supposed
620 * to be set once for all (correct?) */
622 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
623 SlapReply rs2 = { 0 };
625 struct berval save_req_dn = op->o_req_dn,
626 save_req_ndn = op->o_req_ndn,
632 /* parse reference and use
633 * proto://[host][:port]/ only */
634 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
635 if ( rc != LDAP_URL_SUCCESS ) {
637 rs->sr_err = LDAP_OTHER;
642 rc = LDAP_INVALID_SYNTAX;
643 if ( srv->lud_dn != NULL ) {
644 ber_str2bv( srv->lud_dn, 0, 0, &dn );
645 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
646 if ( rc == LDAP_SUCCESS ) {
647 /* remove DN essentially because later on
648 * ldap_initialize() will parse the URL
649 * as a comma-separated URL list */
651 srv->lud_scope = LDAP_SCOPE_DEFAULT;
652 li.li_uri = ldap_url_desc2str( srv );
653 srv->lud_dn = dn.bv_val;
656 ldap_free_urldesc( srv );
658 if ( rc != LDAP_SUCCESS ) {
664 if ( li.li_uri == NULL ) {
667 goto further_cleanup;
673 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
675 /* Searches for a ldapinfo in the avl tree */
676 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
677 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
678 (caddr_t)&li, ldap_chain_uri_cmp );
679 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
682 op->o_bd->be_private = (void *)lip;
685 /* if none is found, create a temporary... */
686 rc = ldap_chain_db_init_one( op->o_bd );
690 lip = (ldapinfo_t *)op->o_bd->be_private;
691 lip->li_uri = li.li_uri;
692 lip->li_bvuri = bvuri;
693 rc = ldap_chain_db_open_one( op->o_bd );
696 lip->li_bvuri = NULL;
697 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
701 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
702 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
703 if ( avl_insert( &lc->lc_lai.lai_tree,
704 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
706 /* someone just inserted another;
707 * don't bother, use this and then
711 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
718 lb->lb_op_f = lback->bi_op_search;
719 lb->lb_depth = depth + 1;
721 /* FIXME: should we also copy filter and scope?
722 * according to RFC3296, no */
723 rc = lback->bi_op_search( op, &rs2 );
724 if ( first_rc == -1 ) {
729 ldap_memfree( li.li_uri );
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;
766 rs->sr_entry = save_entry;
767 rs->sr_flags = save_flags;
769 if ( rc != LDAP_SUCCESS ) {
770 /* couldn't chase any of the referrals */
771 if ( first_rc != -1 ) {
775 rc = SLAP_CB_CONTINUE;
783 ldap_chain_response( Operation *op, SlapReply *rs )
785 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
786 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
787 BackendDB db, *bd = op->o_bd;
788 ldap_chain_cb_t lb = { 0 };
789 slap_callback *sc = op->o_callback,
792 const char *text = NULL;
795 struct berval ndn = op->o_ndn;
797 int sr_err = rs->sr_err;
798 slap_reply_t sr_type = rs->sr_type;
799 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
800 slap_mask_t chain_mask = 0;
801 ber_len_t chain_shift = 0;
802 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
804 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
805 return SLAP_CB_CONTINUE;
808 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
809 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
810 switch ( get_resolveBehavior( op ) ) {
811 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
812 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
813 return SLAP_CB_CONTINUE;
816 chain_mask = SLAP_CH_RESOLVE_MASK;
817 chain_shift = SLAP_CH_RESOLVE_SHIFT;
821 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
822 switch ( get_continuationBehavior( op ) ) {
823 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
824 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
825 return SLAP_CB_CONTINUE;
828 chain_mask = SLAP_CH_CONTINUATION_MASK;
829 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
833 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
836 * TODO: add checks on who/when chain operations; e.g.:
837 * a) what identities are authorized
838 * b) what request DN (e.g. only chain requests rooted at <DN>)
839 * c) what referral URIs
840 * d) what protocol scheme (e.g. only ldaps://)
845 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
850 matched = rs->sr_matched;
851 rs->sr_matched = NULL;
855 /* we need this to know if back-ldap returned any result */
857 sc2.sc_next = sc->sc_next;
858 sc2.sc_private = &lb;
859 sc2.sc_response = ldap_chain_cb_response;
860 op->o_callback = &sc2;
862 /* Chaining can be performed by a privileged user on behalf
863 * of normal users, using the ProxyAuthz control, by exploiting
864 * the identity assertion feature of back-ldap; see idassert-*
865 * directives in slapd-ldap(5).
867 * FIXME: the idassert-authcDN is one, will it be fine regardless
868 * of the URI we obtain from the referral?
871 switch ( op->o_tag ) {
872 case LDAP_REQ_BIND: {
873 struct berval rndn = op->o_req_ndn;
874 Connection *conn = op->o_conn;
876 /* FIXME: can we really get a referral for binds? */
877 op->o_req_ndn = slap_empty_bv;
879 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
880 op->o_req_ndn = rndn;
886 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
889 case LDAP_REQ_DELETE:
890 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
893 case LDAP_REQ_MODRDN:
894 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
897 case LDAP_REQ_MODIFY:
898 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
901 case LDAP_REQ_COMPARE:
902 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
903 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
908 case LDAP_REQ_SEARCH:
909 if ( rs->sr_type == REP_SEARCHREF ) {
910 rc = ldap_chain_search( op, rs, ref, 0 );
913 /* we might get here before any database actually
914 * performed a search; in those cases, we need
915 * to check limits, to make sure safe defaults
917 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
918 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
921 rc = SLAP_CB_CONTINUE;
926 case LDAP_REQ_EXTENDED:
927 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
928 /* FIXME: ldap_back_extended() by design
929 * doesn't send result; frontend is expected
931 /* FIXME: what about chaining? */
932 if ( rc != SLAPD_ABANDON ) {
934 send_ldap_extended( op, rs );
937 lb.lb_status = LDAP_CH_RES;
941 rc = SLAP_CB_CONTINUE;
952 /* slapd-ldap sent response */
953 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
954 /* FIXME: should we send response? */
955 Debug( LDAP_DEBUG_ANY,
956 "%s: ldap_chain_response: "
957 "overlay should have sent result.\n",
958 op->o_log_prefix, 0, 0 );
963 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
964 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
968 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
969 case LDAP_CHAINING_REQUIRED:
971 op->o_callback = NULL;
972 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
973 "operation cannot be completed without chaining" );
977 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
978 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
980 rs->sr_type = sr_type;
983 rc = SLAP_CB_CONTINUE;
985 rs->sr_type = sr_type;
987 rs->sr_matched = matched;
990 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
993 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
996 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
997 op->o_callback = NULL;
998 rc = rs->sr_err = slap_map_api2result( rs );
999 send_ldap_result( op, rs );
1003 rs->sr_err = sr_err;
1004 rs->sr_type = sr_type;
1006 rs->sr_matched = matched;
1009 op->o_callback = sc;
1015 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1017 ldap_chain_parse_ctrl(
1020 LDAPControl *ctrl );
1023 str2chain( const char *s )
1025 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1026 return LDAP_CHAINING_PREFERRED;
1028 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1029 return LDAP_CHAINING_REQUIRED;
1031 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1032 return LDAP_REFERRALS_PREFERRED;
1034 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1035 return LDAP_REFERRALS_REQUIRED;
1040 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1055 static ConfigDriver chain_cf_gen;
1056 static ConfigCfAdd chain_cfadd;
1057 static ConfigLDAPadd chain_ldadd;
1059 static ConfigTable chaincfg[] = {
1060 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1061 { "chain-chaining", "args",
1062 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1063 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1064 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1065 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1066 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1067 { "chain-cache-uri", "TRUE/FALSE",
1068 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1069 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1070 "DESC 'Enables caching of URIs not present in configuration' "
1071 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1072 { "chain-max-depth", "args",
1073 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1074 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1075 "DESC 'max referral depth' "
1076 "SYNTAX OMsInteger "
1077 "EQUALITY integerMatch "
1078 "SINGLE-VALUE )", NULL, NULL },
1079 { "chain-return-error", "TRUE/FALSE",
1080 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1081 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1082 "DESC 'Errors are returned instead of the original referral' "
1083 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1084 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1087 static ConfigOCs chainocs[] = {
1088 { "( OLcfgOvOc:3.1 "
1089 "NAME 'olcChainConfig' "
1090 "DESC 'Chain configuration' "
1091 "SUP olcOverlayConfig "
1093 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1094 "olcChainingBehavior $ "
1095 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1096 "olcChainCacheURI $ "
1097 "olcChainMaxReferralDepth $ "
1098 "olcChainReturnError "
1100 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1101 { "( OLcfgOvOc:3.2 "
1102 "NAME 'olcChainDatabase' "
1103 "DESC 'Chain remote server configuration' "
1105 Cft_Misc, olcDatabaseDummy, chain_ldadd },
1110 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1117 AttributeDescription *ad = NULL;
1123 if ( p->ce_type != Cft_Overlay
1125 || p->ce_bi->bi_cf_ocs != chainocs )
1127 return LDAP_CONSTRAINT_VIOLATION;
1130 on = (slap_overinst *)p->ce_bi;
1131 lc = (ldap_chain_t *)on->on_bi.bi_private;
1133 assert( ca->be == NULL );
1134 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1136 ca->be->bd_info = (BackendInfo *)on;
1138 rc = slap_str2ad( "olcDbURI", &ad, &text );
1139 assert( rc == LDAP_SUCCESS );
1141 at = attr_find( e->e_attrs, ad );
1142 if ( lc->lc_common_li == NULL && at != NULL ) {
1143 /* FIXME: we should generate an empty default entry
1144 * if none is supplied */
1145 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1146 "first underlying database \"%s\" "
1147 "cannot contain attribute \"%s\".\n",
1148 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1149 rc = LDAP_CONSTRAINT_VIOLATION;
1152 } else if ( lc->lc_common_li != NULL && at == NULL ) {
1153 /* FIXME: we should generate an empty default entry
1154 * if none is supplied */
1155 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1156 "subsequent underlying database \"%s\" "
1157 "must contain attribute \"%s\".\n",
1158 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1159 rc = LDAP_CONSTRAINT_VIOLATION;
1163 if ( lc->lc_common_li == NULL ) {
1164 rc = ldap_chain_db_init_common( ca->be );
1167 rc = ldap_chain_db_init_one( ca->be );
1171 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1172 "unable to init %sunderlying database \"%s\".\n",
1173 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1174 return LDAP_CONSTRAINT_VIOLATION;
1177 li = ca->be->be_private;
1179 if ( lc->lc_common_li == NULL ) {
1180 lc->lc_common_li = li;
1183 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1184 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1185 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1186 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1188 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1189 "database \"%s\" insert failed.\n",
1190 e->e_name.bv_val, 0, 0 );
1191 rc = LDAP_CONSTRAINT_VIOLATION;
1196 ca->ca_private = on;
1199 if ( rc != LDAP_SUCCESS ) {
1200 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1208 typedef struct ldap_chain_cfadd_apply_t {
1214 } ldap_chain_cfadd_apply_t;
1217 ldap_chain_cfadd_apply( void *datum, void *arg )
1219 ldapinfo_t *li = (ldapinfo_t *)datum;
1220 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1224 /* FIXME: should not hardcode "olcDatabase" here */
1225 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1226 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1227 bv.bv_val = lca->ca->cr_msg;
1229 lca->ca->be->be_private = (void *)li;
1230 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1231 &bv, lback->bi_cf_ocs, &chainocs[1] );
1239 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1241 CfEntryInfo *pe = p->e_private;
1242 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1243 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1244 void *priv = (void *)ca->be->be_private;
1246 if ( lback->bi_cf_ocs ) {
1247 ldap_chain_cfadd_apply_t lca = { 0 };
1255 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1257 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1258 &lca, 1, AVL_INORDER );
1260 ca->be->be_private = priv;
1266 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1267 static slap_verbmasks chaining_mode[] = {
1268 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1269 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1270 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1271 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1274 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1277 chain_cf_gen( ConfigArgs *c )
1279 slap_overinst *on = (slap_overinst *)c->bi;
1280 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1284 if ( c->op == SLAP_CONFIG_EMIT ) {
1286 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1288 struct berval resolve = BER_BVNULL,
1289 continuation = BER_BVNULL;
1291 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1295 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1296 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1298 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1300 + STRLENOF( "continuation=" ) + continuation.bv_len;
1301 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1302 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1303 "resolve=%s continuation=%s",
1304 resolve.bv_val, continuation.bv_val );
1306 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1307 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1308 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1309 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1310 " critical", STRLENOF( " critical" ) + 1 );
1311 c->value_bv.bv_len += STRLENOF( " critical" );
1316 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1319 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1323 c->value_int = lc->lc_max_depth;
1327 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1336 } else if ( c->op == LDAP_MOD_DELETE ) {
1342 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1350 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1361 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1362 char **argv = c->argv;
1364 BerElementBuffer berbuf;
1365 BerElement *ber = (BerElement *)&berbuf;
1369 Operation op = { 0 };
1370 SlapReply rs = { 0 };
1372 lc->lc_chaining_ctrlflag = 0;
1374 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1375 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1376 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1377 if ( resolve == -1 ) {
1378 Debug( LDAP_DEBUG_ANY, "%s: "
1379 "illegal <resolve> value %s "
1380 "in \"chain-chaining>\".\n",
1381 c->log, argv[ 0 ], 0 );
1385 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1386 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1387 if ( continuation == -1 ) {
1388 Debug( LDAP_DEBUG_ANY, "%s: "
1389 "illegal <continuation> value %s "
1390 "in \"chain-chaining\".\n",
1391 c->log, argv[ 0 ], 0 );
1395 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1399 Debug( LDAP_DEBUG_ANY, "%s: "
1400 "unknown option in \"chain-chaining\".\n",
1406 if ( resolve != -1 || continuation != -1 ) {
1409 if ( resolve == -1 ) {
1411 resolve = SLAP_CHAINING_DEFAULT;
1414 ber_init2( ber, NULL, LBER_USE_DER );
1416 err = ber_printf( ber, "{e" /* } */, resolve );
1419 Debug( LDAP_DEBUG_ANY, "%s: "
1420 "chaining behavior control encoding error!\n",
1425 if ( continuation > -1 ) {
1426 err = ber_printf( ber, "e", continuation );
1429 Debug( LDAP_DEBUG_ANY, "%s: "
1430 "chaining behavior control encoding error!\n",
1436 err = ber_printf( ber, /* { */ "N}" );
1439 Debug( LDAP_DEBUG_ANY, "%s: "
1440 "chaining behavior control encoding error!\n",
1445 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1446 exit( EXIT_FAILURE );
1450 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1453 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1454 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1456 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1458 Debug( LDAP_DEBUG_ANY, "%s: "
1459 "unable to parse chaining control%s%s.\n",
1460 c->log, rs.sr_text ? ": " : "",
1461 rs.sr_text ? rs.sr_text : "" );
1465 lc->lc_chaining_ctrlflag = op.o_chaining;
1467 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1470 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1471 Debug( LDAP_DEBUG_ANY, "%s: "
1472 "\"chaining\" control unsupported (ignored).\n",
1474 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1478 if ( c->value_int ) {
1479 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1481 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1486 if ( c->value_int < 0 ) {
1487 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1488 "<%s> invalid max referral depth %d",
1489 c->argv[0], c->value_int );
1490 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1491 c->log, c->cr_msg, 0 );
1495 lc->lc_max_depth = c->value_int;
1498 if ( c->value_int ) {
1499 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1501 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1517 slap_overinst *on = (slap_overinst *)be->bd_info;
1518 ldap_chain_t *lc = NULL;
1520 if ( lback == NULL ) {
1521 lback = backend_info( "ldap" );
1523 if ( lback == NULL ) {
1528 lc = ch_malloc( sizeof( ldap_chain_t ) );
1532 memset( lc, 0, sizeof( ldap_chain_t ) );
1533 lc->lc_max_depth = 1;
1534 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1536 on->on_bi.bi_private = (void *)lc;
1542 ldap_chain_db_config(
1549 slap_overinst *on = (slap_overinst *)be->bd_info;
1550 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1552 int rc = SLAP_CONF_UNKNOWN;
1554 if ( lc->lc_common_li == NULL ) {
1555 void *be_private = be->be_private;
1556 ldap_chain_db_init_common( be );
1557 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1558 be->be_private = be_private;
1561 /* Something for the chain database? */
1562 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1563 char *save_argv0 = argv[ 0 ];
1564 BackendInfo *bd_info = be->bd_info;
1565 void *be_private = be->be_private;
1566 ConfigOCs *be_cf_ocs = be->be_cf_ocs;
1567 static char *allowed_argv[] = {
1568 /* special: put URI here, so in the meanwhile
1569 * it detects whether a new URI is being provided */
1575 /* FIXME: maybe rebind-as-user should be allowed
1576 * only within known URIs... */
1583 int which_argv = -1;
1585 argv[ 0 ] += STRLENOF( "chain-" );
1587 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1588 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1593 if ( allowed_argv[ which_argv ] == NULL ) {
1596 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1597 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1598 "\"%s\" only allowed within a URI directive.\n.",
1599 fname, lineno, argv[ 0 ] );
1604 if ( which_argv == 0 ) {
1605 rc = ldap_chain_db_init_one( be );
1607 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1608 "underlying slapd-ldap initialization failed.\n.",
1612 lc->lc_cfg_li = be->be_private;
1615 /* TODO: add checks on what other slapd-ldap(5) args
1616 * should be put in the template; this is not quite
1617 * harmful, because attributes that shouldn't don't
1618 * get actually used, but the user should at least
1622 be->bd_info = lback;
1623 be->be_private = (void *)lc->lc_cfg_li;
1624 be->be_cf_ocs = lback->bi_cf_ocs;
1626 rc = config_generic_wrapper( be, fname, lineno, argc, argv );
1628 argv[ 0 ] = save_argv0;
1629 be->be_cf_ocs = be_cf_ocs;
1630 be->be_private = be_private;
1631 be->bd_info = bd_info;
1633 if ( which_argv == 0 ) {
1639 db.be_private = (void *)lc->lc_cfg_li;
1640 ldap_chain_db_destroy_one( &db, NULL );
1641 lc->lc_cfg_li = NULL;
1644 if ( lc->lc_cfg_li->li_bvuri == NULL
1645 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1646 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1648 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1649 "no URI list allowed in slapo-chain.\n",
1652 goto private_destroy;
1655 if ( avl_insert( &lc->lc_lai.lai_tree,
1656 (caddr_t)lc->lc_cfg_li,
1657 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1659 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1660 "duplicate URI in slapo-chain.\n",
1663 goto private_destroy;
1680 typedef struct ldap_chain_db_apply_t {
1683 } ldap_chain_db_apply_t;
1686 ldap_chain_db_apply( void *datum, void *arg )
1688 ldapinfo_t *li = (ldapinfo_t *)datum;
1689 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1691 lca->be->be_private = (void *)li;
1693 return lca->func( lca->be, NULL );
1702 slap_overinst *on = (slap_overinst *)be->bd_info;
1703 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1708 BI_db_func *func = (&lback->bi_db_open)[ which ];
1710 if ( func != NULL && lc->lc_common_li != NULL ) {
1714 db.be_private = lc->lc_common_li;
1716 rc = func( &db, NULL );
1722 if ( lc->lc_lai.lai_tree != NULL ) {
1723 ldap_chain_db_apply_t lca;
1728 rc = avl_apply( lc->lc_lai.lai_tree,
1729 ldap_chain_db_apply, (void *)&lca,
1730 1, AVL_INORDER ) != AVL_NOMORE;
1743 slap_overinst *on = (slap_overinst *) be->bd_info;
1744 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1745 slap_mask_t monitoring;
1748 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1749 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1753 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1755 if ( lc->lc_common_li == NULL ) {
1756 void *be_private = be->be_private;
1757 ldap_chain_db_init_common( be );
1758 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1759 be->be_private = be_private;
1762 /* filter out and restore monitoring */
1763 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1764 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1765 rc = ldap_chain_db_func( be, db_open );
1766 SLAP_DBFLAGS( be ) |= monitoring;
1772 ldap_chain_db_close(
1776 return ldap_chain_db_func( be, db_close );
1780 ldap_chain_db_destroy(
1784 slap_overinst *on = (slap_overinst *) be->bd_info;
1785 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1789 rc = ldap_chain_db_func( be, db_destroy );
1792 avl_free( lc->lc_lai.lai_tree, NULL );
1793 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1801 * inits one instance of the slapd-ldap backend, and stores
1802 * the private info in be_private of the arg
1805 ldap_chain_db_init_common(
1808 BackendInfo *bi = be->bd_info;
1812 be->bd_info = lback;
1813 be->be_private = NULL;
1814 rc = lback->bi_db_init( be, NULL );
1818 li = (ldapinfo_t *)be->be_private;
1819 li->li_urllist_f = NULL;
1820 li->li_urllist_p = NULL;
1828 * inits one instance of the slapd-ldap backend, stores
1829 * the private info in be_private of the arg and fills
1830 * selected fields with data from the template.
1832 * NOTE: add checks about the other fields of the template,
1833 * which are ignored and SHOULD NOT be configured by the user.
1836 ldap_chain_db_init_one(
1839 slap_overinst *on = (slap_overinst *)be->bd_info;
1840 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1842 BackendInfo *bi = be->bd_info;
1847 be->bd_info = lback;
1848 be->be_private = NULL;
1849 t = lback->bi_db_init( be, NULL );
1853 li = (ldapinfo_t *)be->be_private;
1854 li->li_urllist_f = NULL;
1855 li->li_urllist_p = NULL;
1857 /* copy common data */
1858 li->li_nretries = lc->lc_common_li->li_nretries;
1859 li->li_flags = lc->lc_common_li->li_flags;
1860 li->li_version = lc->lc_common_li->li_version;
1861 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
1862 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
1870 ldap_chain_db_open_one(
1873 if ( SLAP_DBMONITORING( be ) ) {
1874 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
1876 if ( li->li_uri == NULL ) {
1877 ber_str2bv( "cn=Common Connections", 0, 1,
1878 &li->li_monitor_info.lmi_rdn );
1883 li->li_monitor_info.lmi_rdn.bv_len
1884 = STRLENOF( "cn=" ) + strlen( li->li_uri );
1885 ptr = li->li_monitor_info.lmi_rdn.bv_val
1886 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
1887 ptr = lutil_strcopy( ptr, "cn=" );
1888 ptr = lutil_strcopy( ptr, li->li_uri );
1893 return lback->bi_db_open( be, NULL );
1896 typedef struct ldap_chain_conn_apply_t {
1899 } ldap_chain_conn_apply_t;
1902 ldap_chain_conn_apply( void *datum, void *arg )
1904 ldapinfo_t *li = (ldapinfo_t *)datum;
1905 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
1907 lca->be->be_private = (void *)li;
1909 return lback->bi_connection_destroy( lca->be, lca->conn );
1913 ldap_chain_connection_destroy(
1918 slap_overinst *on = (slap_overinst *) be->bd_info;
1919 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1920 void *private = be->be_private;
1921 ldap_chain_conn_apply_t lca;
1924 be->be_private = NULL;
1927 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
1928 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
1929 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
1930 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
1931 be->be_private = private;
1936 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1938 ldap_chain_parse_ctrl(
1948 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1949 rs->sr_text = "Chaining behavior control specified multiple times";
1950 return LDAP_PROTOCOL_ERROR;
1953 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1954 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1955 return LDAP_PROTOCOL_ERROR;
1958 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1959 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1964 /* Parse the control value
1965 * ChainingBehavior ::= SEQUENCE {
1966 * resolveBehavior Behavior OPTIONAL,
1967 * continuationBehavior Behavior OPTIONAL }
1969 * Behavior :: = ENUMERATED {
1970 * chainingPreferred (0),
1971 * chainingRequired (1),
1972 * referralsPreferred (2),
1973 * referralsRequired (3) }
1976 ber = ber_init( &ctrl->ldctl_value );
1978 rs->sr_text = "internal error";
1982 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1983 /* FIXME: since the whole SEQUENCE is optional,
1984 * should we accept no enumerations at all? */
1985 if ( tag != LBER_ENUMERATED ) {
1986 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1987 return LDAP_PROTOCOL_ERROR;
1990 switch ( behavior ) {
1991 case LDAP_CHAINING_PREFERRED:
1992 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1995 case LDAP_CHAINING_REQUIRED:
1996 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1999 case LDAP_REFERRALS_PREFERRED:
2000 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2003 case LDAP_REFERRALS_REQUIRED:
2004 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2008 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2009 return LDAP_PROTOCOL_ERROR;
2012 tag = ber_peek_tag( ber, &len );
2013 if ( tag == LBER_ENUMERATED ) {
2014 tag = ber_scanf( ber, "e", &behavior );
2015 if ( tag == LBER_ERROR ) {
2016 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2017 return LDAP_PROTOCOL_ERROR;
2021 if ( tag == LBER_DEFAULT ) {
2022 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2025 switch ( behavior ) {
2026 case LDAP_CHAINING_PREFERRED:
2027 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2030 case LDAP_CHAINING_REQUIRED:
2031 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2034 case LDAP_REFERRALS_PREFERRED:
2035 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2038 case LDAP_REFERRALS_REQUIRED:
2039 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2043 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2044 return LDAP_PROTOCOL_ERROR;
2048 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2049 rs->sr_text = "Chaining behavior control: decoding error";
2050 return LDAP_PROTOCOL_ERROR;
2053 (void) ber_free( ber, 1 );
2056 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2057 ? SLAP_CONTROL_CRITICAL
2058 : SLAP_CONTROL_NONCRITICAL );
2060 return LDAP_SUCCESS;
2062 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2065 chain_initialize( void )
2069 /* Make sure we don't exceed the bits reserved for userland */
2070 config_check_userland( CH_LAST );
2072 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2073 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2074 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2075 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2076 if ( rc != LDAP_SUCCESS ) {
2077 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2078 "unable to register chaining behavior control: %d.\n",
2082 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2084 ldapchain.on_bi.bi_type = "chain";
2085 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2086 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2087 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2088 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2089 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2091 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2093 ldapchain.on_response = ldap_chain_response;
2095 ldapchain.on_bi.bi_cf_ocs = chainocs;
2097 rc = config_register_schema( chaincfg, chainocs );
2102 return overlay_register( &ldapchain );