1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2010 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 struct berval odn = op->o_req_dn,
403 ondn = op->o_req_ndn;
404 ldapinfo_t li = { 0 }, *lip = NULL;
405 struct berval bvuri[ 2 ] = { { 0 } };
407 /* NOTE: returned if ref is empty... */
411 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
412 LDAPControl **ctrls = NULL;
414 (void)chaining_control_add( lc, op, &ctrls );
415 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
419 for ( ; !BER_BVISNULL( ref ); ref++ ) {
420 SlapReply rs2 = { 0 };
421 LDAPURLDesc *srv = NULL;
422 req_search_s save_oq_search = op->oq_search,
423 tmp_oq_search = { 0 };
424 struct berval dn = BER_BVNULL,
431 /* We're setting the URI of the first referral;
432 * what if there are more?
438 If the client wishes to progress the operation, it MUST follow the
439 referral by contacting one of the supported services. If multiple
440 URIs are present, the client assumes that any supported URI may be
441 used to progress the operation.
443 * so we actually need to follow exactly one,
444 * and we can assume any is fine.
447 /* parse reference and use
448 * proto://[host][:port]/ only */
449 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
450 if ( rc != LDAP_URL_SUCCESS ) {
456 if ( op->o_tag == LDAP_REQ_SEARCH ) {
457 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
458 /* RFC 4511: if scope is present, use it */
459 tmp_oq_search.rs_scope = srv->lud_scope;
462 /* RFC 4511: if scope is absent, use original */
463 tmp_oq_search.rs_scope = op->ors_scope;
468 srv->lud_scope = LDAP_SCOPE_DEFAULT;
469 dn.bv_val = srv->lud_dn;
470 filter = srv->lud_filter;
473 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
474 if ( srv->lud_dn == NULL ) {
479 ber_str2bv( srv->lud_dn, 0, 0, &dn );
480 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
481 if ( rc == LDAP_SUCCESS ) {
482 /* remove DN essentially because later on
483 * ldap_initialize() will parse the URL
484 * as a comma-separated URL list */
491 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
493 if ( srv->lud_filter != NULL
494 && srv->lud_filter[0] != '\0'
495 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
497 /* RFC 4511: if filter is present, use it;
498 * otherwise, use original */
499 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
500 if ( tmp_oq_search.rs_filter != NULL ) {
501 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
508 srv->lud_filter = NULL;
510 if ( rc == LDAP_SUCCESS ) {
511 li.li_uri = ldap_url_desc2str( srv );
514 srv->lud_dn = dn.bv_val;
515 srv->lud_filter = filter;
516 ldap_free_urldesc( srv );
518 if ( rc != LDAP_SUCCESS ) {
524 if ( li.li_uri == NULL ) {
527 goto further_cleanup;
533 if ( op->o_tag == LDAP_REQ_SEARCH ) {
534 op->ors_scope = tmp_oq_search.rs_scope;
535 if ( tmp_oq_search.rs_filter != NULL ) {
536 op->ors_filter = tmp_oq_search.rs_filter;
537 op->ors_filterstr = tmp_oq_search.rs_filterstr;
541 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
543 /* Searches for a ldapinfo in the avl tree */
544 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
545 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
546 (caddr_t)&li, ldap_chain_uri_cmp );
547 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
550 op->o_bd->be_private = (void *)lip;
553 rc = ldap_chain_db_init_one( op->o_bd );
557 lip = (ldapinfo_t *)op->o_bd->be_private;
558 lip->li_uri = li.li_uri;
559 lip->li_bvuri = bvuri;
560 rc = ldap_chain_db_open_one( op->o_bd );
563 lip->li_bvuri = NULL;
564 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
568 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
569 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
570 if ( avl_insert( &lc->lc_lai.lai_tree,
571 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
573 /* someone just inserted another;
574 * don't bother, use this and then
578 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
586 lb->lb_depth = depth + 1;
588 rc = op_f( op, &rs2 );
590 /* note the first error */
591 if ( first_rc == -1 ) {
596 ldap_memfree( li.li_uri );
601 lip->li_bvuri = NULL;
602 (void)ldap_chain_db_close_one( op->o_bd );
603 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
608 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
609 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
612 if ( op->o_tag == LDAP_REQ_SEARCH ) {
613 if ( tmp_oq_search.rs_filter != NULL ) {
614 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
617 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
618 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
621 op->oq_search = save_oq_search;
624 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
633 op->o_req_ndn = ondn;
635 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
636 (void)chaining_control_remove( op, &ctrls );
637 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
639 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
654 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
655 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
656 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
657 ldapinfo_t li = { 0 }, *lip = NULL;
658 struct berval bvuri[ 2 ] = { { 0 } };
660 struct berval odn = op->o_req_dn,
661 ondn = op->o_req_ndn;
662 Entry *save_entry = rs->sr_entry;
663 slap_mask_t save_flags = rs->sr_flags;
668 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
669 LDAPControl **ctrls = NULL;
671 (void)chaining_control_add( lc, op, &ctrls );
672 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
674 assert( rs->sr_type == REP_SEARCHREF );
676 rs->sr_type = REP_SEARCH;
678 /* if we parse the URI then by no means
679 * we can cache stuff or reuse connections,
680 * because in back-ldap there's no caching
681 * based on the URI value, which is supposed
682 * to be set once for all (correct?) */
684 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
685 SlapReply rs2 = { 0 };
687 req_search_s save_oq_search = op->oq_search,
688 tmp_oq_search = { 0 };
696 /* parse reference and use
697 * proto://[host][:port]/ only */
698 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
699 if ( rc != LDAP_URL_SUCCESS ) {
701 rs->sr_err = LDAP_OTHER;
705 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
706 /* RFC 4511: if scope is present, use it */
707 tmp_oq_search.rs_scope = srv->lud_scope;
710 /* RFC 4511: if scope is absent, use original */
711 /* Section 4.5.3: if scope is onelevel, use base */
712 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
713 tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
715 tmp_oq_search.rs_scope = op->ors_scope;
719 srv->lud_scope = LDAP_SCOPE_DEFAULT;
720 dn.bv_val = srv->lud_dn;
721 filter = srv->lud_filter;
724 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
725 if ( srv->lud_dn == NULL ) {
729 if ( save_entry != NULL ) {
730 /* use the "right" DN, if available */
731 pdn = save_entry->e_name;
732 ndn = save_entry->e_nname;
733 } /* else leave the original req DN in place, if any RFC 4511 */
736 /* RFC 4511: if DN is present, use it */
737 ber_str2bv( srv->lud_dn, 0, 0, &dn );
738 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
739 if ( rc == LDAP_SUCCESS ) {
740 /* remove DN essentially because later on
741 * ldap_initialize() will parse the URL
742 * as a comma-separated URL list */
749 if ( rc == LDAP_SUCCESS ) {
751 if ( srv->lud_filter != NULL
752 && srv->lud_filter[0] != '\0'
753 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
755 /* RFC 4511: if filter is present, use it;
756 * otherwise, use original */
757 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
758 if ( tmp_oq_search.rs_filter != NULL ) {
759 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
766 srv->lud_filter = NULL;
768 if ( rc == LDAP_SUCCESS ) {
769 li.li_uri = ldap_url_desc2str( srv );
772 srv->lud_dn = dn.bv_val;
773 srv->lud_filter = filter;
774 ldap_free_urldesc( srv );
776 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
779 goto further_cleanup;
784 op->ors_scope = tmp_oq_search.rs_scope;
785 if ( tmp_oq_search.rs_filter != NULL ) {
786 op->ors_filter = tmp_oq_search.rs_filter;
787 op->ors_filterstr = tmp_oq_search.rs_filterstr;
790 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
792 /* Searches for a ldapinfo in the avl tree */
793 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
794 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
795 (caddr_t)&li, ldap_chain_uri_cmp );
796 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
799 op->o_bd->be_private = (void *)lip;
802 /* if none is found, create a temporary... */
803 rc = ldap_chain_db_init_one( op->o_bd );
807 lip = (ldapinfo_t *)op->o_bd->be_private;
808 lip->li_uri = li.li_uri;
809 lip->li_bvuri = bvuri;
810 rc = ldap_chain_db_open_one( op->o_bd );
813 lip->li_bvuri = NULL;
814 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
818 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
819 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
820 if ( avl_insert( &lc->lc_lai.lai_tree,
821 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
823 /* someone just inserted another;
824 * don't bother, use this and then
828 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
835 lb->lb_op_f = lback->bi_op_search;
836 lb->lb_depth = depth + 1;
838 /* FIXME: should we also copy filter and scope?
839 * according to RFC3296, no */
840 rc = lback->bi_op_search( op, &rs2 );
841 if ( first_rc == -1 ) {
846 ldap_memfree( li.li_uri );
851 lip->li_bvuri = NULL;
852 (void)ldap_chain_db_close_one( op->o_bd );
853 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
858 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
859 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
863 op->o_req_ndn = ondn;
865 if ( tmp_oq_search.rs_filter != NULL ) {
866 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
869 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
870 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
873 op->oq_search = save_oq_search;
875 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
883 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
884 (void)chaining_control_remove( op, &ctrls );
885 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
888 op->o_req_ndn = ondn;
889 rs->sr_type = REP_SEARCHREF;
890 rs->sr_entry = save_entry;
891 rs->sr_flags = save_flags;
893 if ( rc != LDAP_SUCCESS ) {
894 /* couldn't chase any of the referrals */
895 if ( first_rc != -1 ) {
899 rc = SLAP_CB_CONTINUE;
907 ldap_chain_response( Operation *op, SlapReply *rs )
909 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
910 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
911 BackendDB db, *bd = op->o_bd;
912 ldap_chain_cb_t lb = { 0 };
913 slap_callback *sc = op->o_callback,
916 const char *text = NULL;
919 struct berval ndn = op->o_ndn;
921 int sr_err = rs->sr_err;
922 slap_reply_t sr_type = rs->sr_type;
923 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
924 slap_mask_t chain_mask = 0;
925 ber_len_t chain_shift = 0;
926 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
928 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
929 return SLAP_CB_CONTINUE;
932 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
933 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
934 switch ( get_resolveBehavior( op ) ) {
935 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
936 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
937 return SLAP_CB_CONTINUE;
940 chain_mask = SLAP_CH_RESOLVE_MASK;
941 chain_shift = SLAP_CH_RESOLVE_SHIFT;
945 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
946 switch ( get_continuationBehavior( op ) ) {
947 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
948 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
949 return SLAP_CB_CONTINUE;
952 chain_mask = SLAP_CH_CONTINUATION_MASK;
953 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
957 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
960 * TODO: add checks on who/when chain operations; e.g.:
961 * a) what identities are authorized
962 * b) what request DN (e.g. only chain requests rooted at <DN>)
963 * c) what referral URIs
964 * d) what protocol scheme (e.g. only ldaps://)
969 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
974 matched = rs->sr_matched;
975 rs->sr_matched = NULL;
979 /* we need this to know if back-ldap returned any result */
981 sc2.sc_next = sc->sc_next;
982 sc2.sc_private = &lb;
983 sc2.sc_response = ldap_chain_cb_response;
984 op->o_callback = &sc2;
986 /* Chaining can be performed by a privileged user on behalf
987 * of normal users, using the ProxyAuthz control, by exploiting
988 * the identity assertion feature of back-ldap; see idassert-*
989 * directives in slapd-ldap(5).
991 * FIXME: the idassert-authcDN is one, will it be fine regardless
992 * of the URI we obtain from the referral?
995 switch ( op->o_tag ) {
996 case LDAP_REQ_BIND: {
997 struct berval rndn = op->o_req_ndn;
998 Connection *conn = op->o_conn;
1000 /* FIXME: can we really get a referral for binds? */
1001 op->o_req_ndn = slap_empty_bv;
1003 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1004 op->o_req_ndn = rndn;
1010 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1013 case LDAP_REQ_DELETE:
1014 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1017 case LDAP_REQ_MODRDN:
1018 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1021 case LDAP_REQ_MODIFY:
1022 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1025 case LDAP_REQ_COMPARE:
1026 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
1027 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1032 case LDAP_REQ_SEARCH:
1033 if ( rs->sr_type == REP_SEARCHREF ) {
1034 sc2.sc_response = ldap_chain_cb_search_response;
1035 rc = ldap_chain_search( op, rs, ref, 0 );
1038 /* we might get here before any database actually
1039 * performed a search; in those cases, we need
1040 * to check limits, to make sure safe defaults
1042 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1043 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1046 rc = SLAP_CB_CONTINUE;
1051 case LDAP_REQ_EXTENDED:
1052 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
1053 /* FIXME: ldap_back_extended() by design
1054 * doesn't send result; frontend is expected
1056 /* FIXME: what about chaining? */
1057 if ( rc != SLAPD_ABANDON ) {
1059 send_ldap_extended( op, rs );
1062 lb.lb_status = LDAP_CH_RES;
1066 rc = SLAP_CB_CONTINUE;
1076 sr_err = rs->sr_err;
1077 /* slapd-ldap sent response */
1078 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1079 /* FIXME: should we send response? */
1080 Debug( LDAP_DEBUG_ANY,
1081 "%s: ldap_chain_response: "
1082 "overlay should have sent result.\n",
1083 op->o_log_prefix, 0, 0 );
1088 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1089 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1093 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1094 case LDAP_CHAINING_REQUIRED:
1096 op->o_callback = NULL;
1097 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1098 "operation cannot be completed without chaining" );
1102 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1103 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1104 sr_err = rs->sr_err = rc;
1105 rs->sr_type = sr_type;
1108 rc = SLAP_CB_CONTINUE;
1109 rs->sr_err = sr_err;
1110 rs->sr_type = sr_type;
1112 rs->sr_matched = matched;
1115 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1118 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1121 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1122 /* give the remaining callbacks a chance */
1123 op->o_callback = sc->sc_next;
1124 rc = rs->sr_err = slap_map_api2result( rs );
1125 send_ldap_result( op, rs );
1129 rs->sr_err = sr_err;
1130 rs->sr_type = sr_type;
1132 rs->sr_matched = matched;
1135 op->o_callback = sc;
1141 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1143 ldap_chain_parse_ctrl(
1146 LDAPControl *ctrl );
1149 str2chain( const char *s )
1151 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1152 return LDAP_CHAINING_PREFERRED;
1154 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1155 return LDAP_CHAINING_REQUIRED;
1157 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1158 return LDAP_REFERRALS_PREFERRED;
1160 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1161 return LDAP_REFERRALS_REQUIRED;
1166 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1181 static ConfigDriver chain_cf_gen;
1182 static ConfigCfAdd chain_cfadd;
1183 static ConfigLDAPadd chain_ldadd;
1185 static ConfigTable chaincfg[] = {
1186 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1187 { "chain-chaining", "args",
1188 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1189 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1190 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1191 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1192 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1193 { "chain-cache-uri", "TRUE/FALSE",
1194 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1195 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1196 "DESC 'Enables caching of URIs not present in configuration' "
1197 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1198 { "chain-max-depth", "args",
1199 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1200 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1201 "DESC 'max referral depth' "
1202 "SYNTAX OMsInteger "
1203 "EQUALITY integerMatch "
1204 "SINGLE-VALUE )", NULL, NULL },
1205 { "chain-return-error", "TRUE/FALSE",
1206 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1207 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1208 "DESC 'Errors are returned instead of the original referral' "
1209 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1210 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1213 static ConfigOCs chainocs[] = {
1214 { "( OLcfgOvOc:3.1 "
1215 "NAME 'olcChainConfig' "
1216 "DESC 'Chain configuration' "
1217 "SUP olcOverlayConfig "
1219 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1220 "olcChainingBehavior $ "
1221 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1222 "olcChainCacheURI $ "
1223 "olcChainMaxReferralDepth $ "
1224 "olcChainReturnError "
1226 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1227 { "( OLcfgOvOc:3.2 "
1228 "NAME 'olcChainDatabase' "
1229 "DESC 'Chain remote server configuration' "
1231 Cft_Misc, olcDatabaseDummy, chain_ldadd },
1236 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1243 AttributeDescription *ad = NULL;
1249 if ( p->ce_type != Cft_Overlay
1251 || p->ce_bi->bi_cf_ocs != chainocs )
1253 return LDAP_CONSTRAINT_VIOLATION;
1256 on = (slap_overinst *)p->ce_bi;
1257 lc = (ldap_chain_t *)on->on_bi.bi_private;
1259 assert( ca->be == NULL );
1260 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1262 ca->be->bd_info = (BackendInfo *)on;
1264 rc = slap_str2ad( "olcDbURI", &ad, &text );
1265 assert( rc == LDAP_SUCCESS );
1267 at = attr_find( e->e_attrs, ad );
1269 if ( lc->lc_common_li == NULL && at != NULL ) {
1270 /* FIXME: we should generate an empty default entry
1271 * if none is supplied */
1272 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1273 "first underlying database \"%s\" "
1274 "cannot contain attribute \"%s\".\n",
1275 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1276 rc = LDAP_CONSTRAINT_VIOLATION;
1281 if ( lc->lc_common_li != NULL && at == NULL ) {
1282 /* FIXME: we should generate an empty default entry
1283 * if none is supplied */
1284 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1285 "subsequent underlying database \"%s\" "
1286 "must contain attribute \"%s\".\n",
1287 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1288 rc = LDAP_CONSTRAINT_VIOLATION;
1292 if ( lc->lc_common_li == NULL ) {
1293 rc = ldap_chain_db_init_common( ca->be );
1296 rc = ldap_chain_db_init_one( ca->be );
1300 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1301 "unable to init %sunderlying database \"%s\".\n",
1302 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1303 return LDAP_CONSTRAINT_VIOLATION;
1306 li = ca->be->be_private;
1308 if ( lc->lc_common_li == NULL ) {
1309 lc->lc_common_li = li;
1312 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1313 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1314 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1315 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1317 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1318 "database \"%s\" insert failed.\n",
1319 e->e_name.bv_val, 0, 0 );
1320 rc = LDAP_CONSTRAINT_VIOLATION;
1325 ca->ca_private = on;
1328 if ( rc != LDAP_SUCCESS ) {
1329 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1337 typedef struct ldap_chain_cfadd_apply_t {
1343 } ldap_chain_cfadd_apply_t;
1346 ldap_chain_cfadd_apply( void *datum, void *arg )
1348 ldapinfo_t *li = (ldapinfo_t *)datum;
1349 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1353 /* FIXME: should not hardcode "olcDatabase" here */
1354 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1355 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1356 bv.bv_val = lca->ca->cr_msg;
1358 lca->ca->be->be_private = (void *)li;
1359 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1360 &bv, lback->bi_cf_ocs, &chainocs[1] );
1368 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1370 CfEntryInfo *pe = p->e_private;
1371 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1372 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1373 void *priv = (void *)ca->be->be_private;
1375 if ( lback->bi_cf_ocs ) {
1376 ldap_chain_cfadd_apply_t lca = { 0 };
1384 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1386 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1387 &lca, 1, AVL_INORDER );
1389 ca->be->be_private = priv;
1395 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1396 static slap_verbmasks chaining_mode[] = {
1397 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1398 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1399 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1400 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1403 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1406 chain_cf_gen( ConfigArgs *c )
1408 slap_overinst *on = (slap_overinst *)c->bi;
1409 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1413 if ( c->op == SLAP_CONFIG_EMIT ) {
1415 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1417 struct berval resolve = BER_BVNULL,
1418 continuation = BER_BVNULL;
1420 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1424 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1425 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1427 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1429 + STRLENOF( "continuation=" ) + continuation.bv_len;
1430 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1431 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1432 "resolve=%s continuation=%s",
1433 resolve.bv_val, continuation.bv_val );
1435 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1436 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1437 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1438 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1439 " critical", STRLENOF( " critical" ) + 1 );
1440 c->value_bv.bv_len += STRLENOF( " critical" );
1445 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1448 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1452 c->value_int = lc->lc_max_depth;
1456 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1465 } else if ( c->op == LDAP_MOD_DELETE ) {
1471 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1479 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1490 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1491 char **argv = c->argv;
1493 BerElementBuffer berbuf;
1494 BerElement *ber = (BerElement *)&berbuf;
1498 Operation op = { 0 };
1499 SlapReply rs = { 0 };
1501 lc->lc_chaining_ctrlflag = 0;
1503 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1504 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1505 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1506 if ( resolve == -1 ) {
1507 Debug( LDAP_DEBUG_ANY, "%s: "
1508 "illegal <resolve> value %s "
1509 "in \"chain-chaining>\".\n",
1510 c->log, argv[ 0 ], 0 );
1514 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1515 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1516 if ( continuation == -1 ) {
1517 Debug( LDAP_DEBUG_ANY, "%s: "
1518 "illegal <continuation> value %s "
1519 "in \"chain-chaining\".\n",
1520 c->log, argv[ 0 ], 0 );
1524 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1528 Debug( LDAP_DEBUG_ANY, "%s: "
1529 "unknown option in \"chain-chaining\".\n",
1535 if ( resolve != -1 || continuation != -1 ) {
1538 if ( resolve == -1 ) {
1540 resolve = SLAP_CHAINING_DEFAULT;
1543 ber_init2( ber, NULL, LBER_USE_DER );
1545 err = ber_printf( ber, "{e" /* } */, resolve );
1548 Debug( LDAP_DEBUG_ANY, "%s: "
1549 "chaining behavior control encoding error!\n",
1554 if ( continuation > -1 ) {
1555 err = ber_printf( ber, "e", continuation );
1558 Debug( LDAP_DEBUG_ANY, "%s: "
1559 "chaining behavior control encoding error!\n",
1565 err = ber_printf( ber, /* { */ "N}" );
1568 Debug( LDAP_DEBUG_ANY, "%s: "
1569 "chaining behavior control encoding error!\n",
1574 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1575 exit( EXIT_FAILURE );
1579 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1582 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1583 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1585 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1587 Debug( LDAP_DEBUG_ANY, "%s: "
1588 "unable to parse chaining control%s%s.\n",
1589 c->log, rs.sr_text ? ": " : "",
1590 rs.sr_text ? rs.sr_text : "" );
1594 lc->lc_chaining_ctrlflag = op.o_chaining;
1596 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1599 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1600 Debug( LDAP_DEBUG_ANY, "%s: "
1601 "\"chaining\" control unsupported (ignored).\n",
1603 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1607 if ( c->value_int ) {
1608 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1610 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1615 if ( c->value_int < 0 ) {
1616 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1617 "<%s> invalid max referral depth %d",
1618 c->argv[0], c->value_int );
1619 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1620 c->log, c->cr_msg, 0 );
1624 lc->lc_max_depth = c->value_int;
1627 if ( c->value_int ) {
1628 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1630 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1646 slap_overinst *on = (slap_overinst *)be->bd_info;
1647 ldap_chain_t *lc = NULL;
1649 if ( lback == NULL ) {
1650 lback = backend_info( "ldap" );
1652 if ( lback == NULL ) {
1657 lc = ch_malloc( sizeof( ldap_chain_t ) );
1661 memset( lc, 0, sizeof( ldap_chain_t ) );
1662 lc->lc_max_depth = 1;
1663 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1665 on->on_bi.bi_private = (void *)lc;
1671 ldap_chain_db_config(
1678 slap_overinst *on = (slap_overinst *)be->bd_info;
1679 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1681 int rc = SLAP_CONF_UNKNOWN;
1683 if ( lc->lc_common_li == NULL ) {
1684 void *be_private = be->be_private;
1685 ldap_chain_db_init_common( be );
1686 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1687 be->be_private = be_private;
1690 /* Something for the chain database? */
1691 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1692 char *save_argv0 = argv[ 0 ];
1693 BackendInfo *bd_info = be->bd_info;
1694 void *be_private = be->be_private;
1695 ConfigOCs *be_cf_ocs = be->be_cf_ocs;
1696 static char *allowed_argv[] = {
1697 /* special: put URI here, so in the meanwhile
1698 * it detects whether a new URI is being provided */
1704 /* FIXME: maybe rebind-as-user should be allowed
1705 * only within known URIs... */
1712 int which_argv = -1;
1714 argv[ 0 ] += STRLENOF( "chain-" );
1716 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1717 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1722 if ( allowed_argv[ which_argv ] == NULL ) {
1725 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1726 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1727 "\"%s\" only allowed within a URI directive.\n.",
1728 fname, lineno, argv[ 0 ] );
1733 if ( which_argv == 0 ) {
1734 rc = ldap_chain_db_init_one( be );
1736 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1737 "underlying slapd-ldap initialization failed.\n.",
1741 lc->lc_cfg_li = be->be_private;
1744 /* TODO: add checks on what other slapd-ldap(5) args
1745 * should be put in the template; this is not quite
1746 * harmful, because attributes that shouldn't don't
1747 * get actually used, but the user should at least
1751 be->bd_info = lback;
1752 be->be_private = (void *)lc->lc_cfg_li;
1753 be->be_cf_ocs = lback->bi_cf_ocs;
1755 rc = config_generic_wrapper( be, fname, lineno, argc, argv );
1757 argv[ 0 ] = save_argv0;
1758 be->be_cf_ocs = be_cf_ocs;
1759 be->be_private = be_private;
1760 be->bd_info = bd_info;
1762 if ( which_argv == 0 ) {
1768 db.be_private = (void *)lc->lc_cfg_li;
1769 ldap_chain_db_destroy_one( &db, NULL );
1770 lc->lc_cfg_li = NULL;
1773 if ( lc->lc_cfg_li->li_bvuri == NULL
1774 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1775 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1777 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1778 "no URI list allowed in slapo-chain.\n",
1781 goto private_destroy;
1784 if ( avl_insert( &lc->lc_lai.lai_tree,
1785 (caddr_t)lc->lc_cfg_li,
1786 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1788 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1789 "duplicate URI in slapo-chain.\n",
1792 goto private_destroy;
1809 typedef struct ldap_chain_db_apply_t {
1812 } ldap_chain_db_apply_t;
1815 ldap_chain_db_apply( void *datum, void *arg )
1817 ldapinfo_t *li = (ldapinfo_t *)datum;
1818 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1820 lca->be->be_private = (void *)li;
1822 return lca->func( lca->be, NULL );
1831 slap_overinst *on = (slap_overinst *)be->bd_info;
1832 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1837 BI_db_func *func = (&lback->bi_db_open)[ which ];
1839 if ( func != NULL && lc->lc_common_li != NULL ) {
1843 db.be_private = lc->lc_common_li;
1845 rc = func( &db, NULL );
1851 if ( lc->lc_lai.lai_tree != NULL ) {
1852 ldap_chain_db_apply_t lca;
1857 rc = avl_apply( lc->lc_lai.lai_tree,
1858 ldap_chain_db_apply, (void *)&lca,
1859 1, AVL_INORDER ) != AVL_NOMORE;
1872 slap_overinst *on = (slap_overinst *) be->bd_info;
1873 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1874 slap_mask_t monitoring;
1877 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1878 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1882 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1884 if ( lc->lc_common_li == NULL ) {
1885 void *be_private = be->be_private;
1886 ldap_chain_db_init_common( be );
1887 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1888 be->be_private = be_private;
1891 /* filter out and restore monitoring */
1892 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1893 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1894 rc = ldap_chain_db_func( be, db_open );
1895 SLAP_DBFLAGS( be ) |= monitoring;
1901 ldap_chain_db_close(
1905 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1906 #ifdef SLAP_CONFIG_DELETE
1907 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1908 #endif /* SLAP_CONFIG_DELETE */
1909 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1910 return ldap_chain_db_func( be, db_close );
1914 ldap_chain_db_destroy(
1918 slap_overinst *on = (slap_overinst *) be->bd_info;
1919 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1923 rc = ldap_chain_db_func( be, db_destroy );
1926 avl_free( lc->lc_lai.lai_tree, NULL );
1927 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1935 * inits one instance of the slapd-ldap backend, and stores
1936 * the private info in be_private of the arg
1939 ldap_chain_db_init_common(
1942 BackendInfo *bi = be->bd_info;
1946 be->bd_info = lback;
1947 be->be_private = NULL;
1948 rc = lback->bi_db_init( be, NULL );
1952 li = (ldapinfo_t *)be->be_private;
1953 li->li_urllist_f = NULL;
1954 li->li_urllist_p = NULL;
1962 * inits one instance of the slapd-ldap backend, stores
1963 * the private info in be_private of the arg and fills
1964 * selected fields with data from the template.
1966 * NOTE: add checks about the other fields of the template,
1967 * which are ignored and SHOULD NOT be configured by the user.
1970 ldap_chain_db_init_one(
1973 slap_overinst *on = (slap_overinst *)be->bd_info;
1974 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1976 BackendInfo *bi = be->bd_info;
1981 be->bd_info = lback;
1982 be->be_private = NULL;
1983 t = lback->bi_db_init( be, NULL );
1987 li = (ldapinfo_t *)be->be_private;
1988 li->li_urllist_f = NULL;
1989 li->li_urllist_p = NULL;
1991 /* copy common data */
1992 li->li_nretries = lc->lc_common_li->li_nretries;
1993 li->li_flags = lc->lc_common_li->li_flags;
1994 li->li_version = lc->lc_common_li->li_version;
1995 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
1996 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2004 ldap_chain_db_open_one(
2007 if ( SLAP_DBMONITORING( be ) ) {
2008 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
2010 if ( li->li_uri == NULL ) {
2011 ber_str2bv( "cn=Common Connections", 0, 1,
2012 &li->li_monitor_info.lmi_rdn );
2017 li->li_monitor_info.lmi_rdn.bv_len
2018 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2019 ptr = li->li_monitor_info.lmi_rdn.bv_val
2020 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
2021 ptr = lutil_strcopy( ptr, "cn=" );
2022 ptr = lutil_strcopy( ptr, li->li_uri );
2027 return lback->bi_db_open( be, NULL );
2030 typedef struct ldap_chain_conn_apply_t {
2033 } ldap_chain_conn_apply_t;
2036 ldap_chain_conn_apply( void *datum, void *arg )
2038 ldapinfo_t *li = (ldapinfo_t *)datum;
2039 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2041 lca->be->be_private = (void *)li;
2043 return lback->bi_connection_destroy( lca->be, lca->conn );
2047 ldap_chain_connection_destroy(
2052 slap_overinst *on = (slap_overinst *) be->bd_info;
2053 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2054 void *private = be->be_private;
2055 ldap_chain_conn_apply_t lca;
2058 be->be_private = NULL;
2061 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2062 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2063 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2064 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2065 be->be_private = private;
2070 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2072 ldap_chain_parse_ctrl(
2082 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2083 rs->sr_text = "Chaining behavior control specified multiple times";
2084 return LDAP_PROTOCOL_ERROR;
2087 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2088 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2089 return LDAP_PROTOCOL_ERROR;
2092 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2093 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2098 /* Parse the control value
2099 * ChainingBehavior ::= SEQUENCE {
2100 * resolveBehavior Behavior OPTIONAL,
2101 * continuationBehavior Behavior OPTIONAL }
2103 * Behavior :: = ENUMERATED {
2104 * chainingPreferred (0),
2105 * chainingRequired (1),
2106 * referralsPreferred (2),
2107 * referralsRequired (3) }
2110 ber = ber_init( &ctrl->ldctl_value );
2112 rs->sr_text = "internal error";
2116 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2117 /* FIXME: since the whole SEQUENCE is optional,
2118 * should we accept no enumerations at all? */
2119 if ( tag != LBER_ENUMERATED ) {
2120 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2121 return LDAP_PROTOCOL_ERROR;
2124 switch ( behavior ) {
2125 case LDAP_CHAINING_PREFERRED:
2126 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2129 case LDAP_CHAINING_REQUIRED:
2130 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2133 case LDAP_REFERRALS_PREFERRED:
2134 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2137 case LDAP_REFERRALS_REQUIRED:
2138 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2142 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2143 return LDAP_PROTOCOL_ERROR;
2146 tag = ber_peek_tag( ber, &len );
2147 if ( tag == LBER_ENUMERATED ) {
2148 tag = ber_scanf( ber, "e", &behavior );
2149 if ( tag == LBER_ERROR ) {
2150 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2151 return LDAP_PROTOCOL_ERROR;
2155 if ( tag == LBER_DEFAULT ) {
2156 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2159 switch ( behavior ) {
2160 case LDAP_CHAINING_PREFERRED:
2161 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2164 case LDAP_CHAINING_REQUIRED:
2165 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2168 case LDAP_REFERRALS_PREFERRED:
2169 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2172 case LDAP_REFERRALS_REQUIRED:
2173 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2177 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2178 return LDAP_PROTOCOL_ERROR;
2182 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2183 rs->sr_text = "Chaining behavior control: decoding error";
2184 return LDAP_PROTOCOL_ERROR;
2187 (void) ber_free( ber, 1 );
2190 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2191 ? SLAP_CONTROL_CRITICAL
2192 : SLAP_CONTROL_NONCRITICAL );
2194 return LDAP_SUCCESS;
2196 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2199 chain_initialize( void )
2203 /* Make sure we don't exceed the bits reserved for userland */
2204 config_check_userland( CH_LAST );
2206 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2207 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2208 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2209 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2210 if ( rc != LDAP_SUCCESS ) {
2211 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2212 "unable to register chaining behavior control: %d.\n",
2216 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2218 ldapchain.on_bi.bi_type = "chain";
2219 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2220 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2221 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2222 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2223 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2225 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2227 ldapchain.on_response = ldap_chain_response;
2229 ldapchain.on_bi.bi_cf_ocs = chainocs;
2231 rc = config_register_schema( chaincfg, chainocs );
2236 return overlay_register( &ldapchain );