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 );
1268 if ( lc->lc_common_li == NULL && at != NULL ) {
1269 /* FIXME: we should generate an empty default entry
1270 * if none is supplied */
1271 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1272 "first underlying database \"%s\" "
1273 "cannot contain attribute \"%s\".\n",
1274 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1275 rc = LDAP_CONSTRAINT_VIOLATION;
1278 } else if ( lc->lc_common_li != NULL && at == NULL ) {
1279 /* FIXME: we should generate an empty default entry
1280 * if none is supplied */
1281 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1282 "subsequent underlying database \"%s\" "
1283 "must contain attribute \"%s\".\n",
1284 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1285 rc = LDAP_CONSTRAINT_VIOLATION;
1289 if ( lc->lc_common_li == NULL ) {
1290 rc = ldap_chain_db_init_common( ca->be );
1293 rc = ldap_chain_db_init_one( ca->be );
1297 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1298 "unable to init %sunderlying database \"%s\".\n",
1299 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1300 return LDAP_CONSTRAINT_VIOLATION;
1303 li = ca->be->be_private;
1305 if ( lc->lc_common_li == NULL ) {
1306 lc->lc_common_li = li;
1309 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1310 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1311 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1312 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1314 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1315 "database \"%s\" insert failed.\n",
1316 e->e_name.bv_val, 0, 0 );
1317 rc = LDAP_CONSTRAINT_VIOLATION;
1322 ca->ca_private = on;
1325 if ( rc != LDAP_SUCCESS ) {
1326 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1334 typedef struct ldap_chain_cfadd_apply_t {
1340 } ldap_chain_cfadd_apply_t;
1343 ldap_chain_cfadd_apply( void *datum, void *arg )
1345 ldapinfo_t *li = (ldapinfo_t *)datum;
1346 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1350 /* FIXME: should not hardcode "olcDatabase" here */
1351 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1352 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1353 bv.bv_val = lca->ca->cr_msg;
1355 lca->ca->be->be_private = (void *)li;
1356 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1357 &bv, lback->bi_cf_ocs, &chainocs[1] );
1365 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1367 CfEntryInfo *pe = p->e_private;
1368 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1369 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1370 void *priv = (void *)ca->be->be_private;
1372 if ( lback->bi_cf_ocs ) {
1373 ldap_chain_cfadd_apply_t lca = { 0 };
1381 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1383 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1384 &lca, 1, AVL_INORDER );
1386 ca->be->be_private = priv;
1392 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1393 static slap_verbmasks chaining_mode[] = {
1394 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1395 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1396 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1397 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1400 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1403 chain_cf_gen( ConfigArgs *c )
1405 slap_overinst *on = (slap_overinst *)c->bi;
1406 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1410 if ( c->op == SLAP_CONFIG_EMIT ) {
1412 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1414 struct berval resolve = BER_BVNULL,
1415 continuation = BER_BVNULL;
1417 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1421 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1422 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1424 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1426 + STRLENOF( "continuation=" ) + continuation.bv_len;
1427 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1428 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1429 "resolve=%s continuation=%s",
1430 resolve.bv_val, continuation.bv_val );
1432 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1433 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1434 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1435 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1436 " critical", STRLENOF( " critical" ) + 1 );
1437 c->value_bv.bv_len += STRLENOF( " critical" );
1442 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1445 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1449 c->value_int = lc->lc_max_depth;
1453 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1462 } else if ( c->op == LDAP_MOD_DELETE ) {
1468 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1476 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1487 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1488 char **argv = c->argv;
1490 BerElementBuffer berbuf;
1491 BerElement *ber = (BerElement *)&berbuf;
1495 Operation op = { 0 };
1496 SlapReply rs = { 0 };
1498 lc->lc_chaining_ctrlflag = 0;
1500 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1501 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1502 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1503 if ( resolve == -1 ) {
1504 Debug( LDAP_DEBUG_ANY, "%s: "
1505 "illegal <resolve> value %s "
1506 "in \"chain-chaining>\".\n",
1507 c->log, argv[ 0 ], 0 );
1511 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1512 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1513 if ( continuation == -1 ) {
1514 Debug( LDAP_DEBUG_ANY, "%s: "
1515 "illegal <continuation> value %s "
1516 "in \"chain-chaining\".\n",
1517 c->log, argv[ 0 ], 0 );
1521 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1525 Debug( LDAP_DEBUG_ANY, "%s: "
1526 "unknown option in \"chain-chaining\".\n",
1532 if ( resolve != -1 || continuation != -1 ) {
1535 if ( resolve == -1 ) {
1537 resolve = SLAP_CHAINING_DEFAULT;
1540 ber_init2( ber, NULL, LBER_USE_DER );
1542 err = ber_printf( ber, "{e" /* } */, resolve );
1545 Debug( LDAP_DEBUG_ANY, "%s: "
1546 "chaining behavior control encoding error!\n",
1551 if ( continuation > -1 ) {
1552 err = ber_printf( ber, "e", continuation );
1555 Debug( LDAP_DEBUG_ANY, "%s: "
1556 "chaining behavior control encoding error!\n",
1562 err = ber_printf( ber, /* { */ "N}" );
1565 Debug( LDAP_DEBUG_ANY, "%s: "
1566 "chaining behavior control encoding error!\n",
1571 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1572 exit( EXIT_FAILURE );
1576 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1579 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1580 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1582 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1584 Debug( LDAP_DEBUG_ANY, "%s: "
1585 "unable to parse chaining control%s%s.\n",
1586 c->log, rs.sr_text ? ": " : "",
1587 rs.sr_text ? rs.sr_text : "" );
1591 lc->lc_chaining_ctrlflag = op.o_chaining;
1593 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1596 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1597 Debug( LDAP_DEBUG_ANY, "%s: "
1598 "\"chaining\" control unsupported (ignored).\n",
1600 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1604 if ( c->value_int ) {
1605 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1607 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1612 if ( c->value_int < 0 ) {
1613 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1614 "<%s> invalid max referral depth %d",
1615 c->argv[0], c->value_int );
1616 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1617 c->log, c->cr_msg, 0 );
1621 lc->lc_max_depth = c->value_int;
1624 if ( c->value_int ) {
1625 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1627 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1643 slap_overinst *on = (slap_overinst *)be->bd_info;
1644 ldap_chain_t *lc = NULL;
1646 if ( lback == NULL ) {
1647 lback = backend_info( "ldap" );
1649 if ( lback == NULL ) {
1654 lc = ch_malloc( sizeof( ldap_chain_t ) );
1658 memset( lc, 0, sizeof( ldap_chain_t ) );
1659 lc->lc_max_depth = 1;
1660 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1662 on->on_bi.bi_private = (void *)lc;
1668 ldap_chain_db_config(
1675 slap_overinst *on = (slap_overinst *)be->bd_info;
1676 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1678 int rc = SLAP_CONF_UNKNOWN;
1680 if ( lc->lc_common_li == NULL ) {
1681 void *be_private = be->be_private;
1682 ldap_chain_db_init_common( be );
1683 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1684 be->be_private = be_private;
1687 /* Something for the chain database? */
1688 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1689 char *save_argv0 = argv[ 0 ];
1690 BackendInfo *bd_info = be->bd_info;
1691 void *be_private = be->be_private;
1692 ConfigOCs *be_cf_ocs = be->be_cf_ocs;
1693 static char *allowed_argv[] = {
1694 /* special: put URI here, so in the meanwhile
1695 * it detects whether a new URI is being provided */
1701 /* FIXME: maybe rebind-as-user should be allowed
1702 * only within known URIs... */
1709 int which_argv = -1;
1711 argv[ 0 ] += STRLENOF( "chain-" );
1713 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1714 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1719 if ( allowed_argv[ which_argv ] == NULL ) {
1722 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1723 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1724 "\"%s\" only allowed within a URI directive.\n.",
1725 fname, lineno, argv[ 0 ] );
1730 if ( which_argv == 0 ) {
1731 rc = ldap_chain_db_init_one( be );
1733 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1734 "underlying slapd-ldap initialization failed.\n.",
1738 lc->lc_cfg_li = be->be_private;
1741 /* TODO: add checks on what other slapd-ldap(5) args
1742 * should be put in the template; this is not quite
1743 * harmful, because attributes that shouldn't don't
1744 * get actually used, but the user should at least
1748 be->bd_info = lback;
1749 be->be_private = (void *)lc->lc_cfg_li;
1750 be->be_cf_ocs = lback->bi_cf_ocs;
1752 rc = config_generic_wrapper( be, fname, lineno, argc, argv );
1754 argv[ 0 ] = save_argv0;
1755 be->be_cf_ocs = be_cf_ocs;
1756 be->be_private = be_private;
1757 be->bd_info = bd_info;
1759 if ( which_argv == 0 ) {
1765 db.be_private = (void *)lc->lc_cfg_li;
1766 ldap_chain_db_destroy_one( &db, NULL );
1767 lc->lc_cfg_li = NULL;
1770 if ( lc->lc_cfg_li->li_bvuri == NULL
1771 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1772 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1774 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1775 "no URI list allowed in slapo-chain.\n",
1778 goto private_destroy;
1781 if ( avl_insert( &lc->lc_lai.lai_tree,
1782 (caddr_t)lc->lc_cfg_li,
1783 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1785 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1786 "duplicate URI in slapo-chain.\n",
1789 goto private_destroy;
1806 typedef struct ldap_chain_db_apply_t {
1809 } ldap_chain_db_apply_t;
1812 ldap_chain_db_apply( void *datum, void *arg )
1814 ldapinfo_t *li = (ldapinfo_t *)datum;
1815 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1817 lca->be->be_private = (void *)li;
1819 return lca->func( lca->be, NULL );
1828 slap_overinst *on = (slap_overinst *)be->bd_info;
1829 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1834 BI_db_func *func = (&lback->bi_db_open)[ which ];
1836 if ( func != NULL && lc->lc_common_li != NULL ) {
1840 db.be_private = lc->lc_common_li;
1842 rc = func( &db, NULL );
1848 if ( lc->lc_lai.lai_tree != NULL ) {
1849 ldap_chain_db_apply_t lca;
1854 rc = avl_apply( lc->lc_lai.lai_tree,
1855 ldap_chain_db_apply, (void *)&lca,
1856 1, AVL_INORDER ) != AVL_NOMORE;
1869 slap_overinst *on = (slap_overinst *) be->bd_info;
1870 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1871 slap_mask_t monitoring;
1874 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1875 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1879 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1881 if ( lc->lc_common_li == NULL ) {
1882 void *be_private = be->be_private;
1883 ldap_chain_db_init_common( be );
1884 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1885 be->be_private = be_private;
1888 /* filter out and restore monitoring */
1889 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1890 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1891 rc = ldap_chain_db_func( be, db_open );
1892 SLAP_DBFLAGS( be ) |= monitoring;
1898 ldap_chain_db_close(
1902 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1903 #ifdef SLAP_CONFIG_DELETE
1904 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1905 #endif /* SLAP_CONFIG_DELETE */
1906 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1907 return ldap_chain_db_func( be, db_close );
1911 ldap_chain_db_destroy(
1915 slap_overinst *on = (slap_overinst *) be->bd_info;
1916 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1920 rc = ldap_chain_db_func( be, db_destroy );
1923 avl_free( lc->lc_lai.lai_tree, NULL );
1924 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1932 * inits one instance of the slapd-ldap backend, and stores
1933 * the private info in be_private of the arg
1936 ldap_chain_db_init_common(
1939 BackendInfo *bi = be->bd_info;
1943 be->bd_info = lback;
1944 be->be_private = NULL;
1945 rc = lback->bi_db_init( be, NULL );
1949 li = (ldapinfo_t *)be->be_private;
1950 li->li_urllist_f = NULL;
1951 li->li_urllist_p = NULL;
1959 * inits one instance of the slapd-ldap backend, stores
1960 * the private info in be_private of the arg and fills
1961 * selected fields with data from the template.
1963 * NOTE: add checks about the other fields of the template,
1964 * which are ignored and SHOULD NOT be configured by the user.
1967 ldap_chain_db_init_one(
1970 slap_overinst *on = (slap_overinst *)be->bd_info;
1971 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1973 BackendInfo *bi = be->bd_info;
1978 be->bd_info = lback;
1979 be->be_private = NULL;
1980 t = lback->bi_db_init( be, NULL );
1984 li = (ldapinfo_t *)be->be_private;
1985 li->li_urllist_f = NULL;
1986 li->li_urllist_p = NULL;
1988 /* copy common data */
1989 li->li_nretries = lc->lc_common_li->li_nretries;
1990 li->li_flags = lc->lc_common_li->li_flags;
1991 li->li_version = lc->lc_common_li->li_version;
1992 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
1993 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2001 ldap_chain_db_open_one(
2004 if ( SLAP_DBMONITORING( be ) ) {
2005 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
2007 if ( li->li_uri == NULL ) {
2008 ber_str2bv( "cn=Common Connections", 0, 1,
2009 &li->li_monitor_info.lmi_rdn );
2014 li->li_monitor_info.lmi_rdn.bv_len
2015 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2016 ptr = li->li_monitor_info.lmi_rdn.bv_val
2017 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
2018 ptr = lutil_strcopy( ptr, "cn=" );
2019 ptr = lutil_strcopy( ptr, li->li_uri );
2024 return lback->bi_db_open( be, NULL );
2027 typedef struct ldap_chain_conn_apply_t {
2030 } ldap_chain_conn_apply_t;
2033 ldap_chain_conn_apply( void *datum, void *arg )
2035 ldapinfo_t *li = (ldapinfo_t *)datum;
2036 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2038 lca->be->be_private = (void *)li;
2040 return lback->bi_connection_destroy( lca->be, lca->conn );
2044 ldap_chain_connection_destroy(
2049 slap_overinst *on = (slap_overinst *) be->bd_info;
2050 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2051 void *private = be->be_private;
2052 ldap_chain_conn_apply_t lca;
2055 be->be_private = NULL;
2058 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2059 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2060 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2061 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2062 be->be_private = private;
2067 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2069 ldap_chain_parse_ctrl(
2079 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2080 rs->sr_text = "Chaining behavior control specified multiple times";
2081 return LDAP_PROTOCOL_ERROR;
2084 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2085 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2086 return LDAP_PROTOCOL_ERROR;
2089 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2090 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2095 /* Parse the control value
2096 * ChainingBehavior ::= SEQUENCE {
2097 * resolveBehavior Behavior OPTIONAL,
2098 * continuationBehavior Behavior OPTIONAL }
2100 * Behavior :: = ENUMERATED {
2101 * chainingPreferred (0),
2102 * chainingRequired (1),
2103 * referralsPreferred (2),
2104 * referralsRequired (3) }
2107 ber = ber_init( &ctrl->ldctl_value );
2109 rs->sr_text = "internal error";
2113 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2114 /* FIXME: since the whole SEQUENCE is optional,
2115 * should we accept no enumerations at all? */
2116 if ( tag != LBER_ENUMERATED ) {
2117 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2118 return LDAP_PROTOCOL_ERROR;
2121 switch ( behavior ) {
2122 case LDAP_CHAINING_PREFERRED:
2123 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2126 case LDAP_CHAINING_REQUIRED:
2127 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2130 case LDAP_REFERRALS_PREFERRED:
2131 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2134 case LDAP_REFERRALS_REQUIRED:
2135 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2139 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2140 return LDAP_PROTOCOL_ERROR;
2143 tag = ber_peek_tag( ber, &len );
2144 if ( tag == LBER_ENUMERATED ) {
2145 tag = ber_scanf( ber, "e", &behavior );
2146 if ( tag == LBER_ERROR ) {
2147 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2148 return LDAP_PROTOCOL_ERROR;
2152 if ( tag == LBER_DEFAULT ) {
2153 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2156 switch ( behavior ) {
2157 case LDAP_CHAINING_PREFERRED:
2158 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2161 case LDAP_CHAINING_REQUIRED:
2162 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2165 case LDAP_REFERRALS_PREFERRED:
2166 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2169 case LDAP_REFERRALS_REQUIRED:
2170 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2174 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2175 return LDAP_PROTOCOL_ERROR;
2179 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2180 rs->sr_text = "Chaining behavior control: decoding error";
2181 return LDAP_PROTOCOL_ERROR;
2184 (void) ber_free( ber, 1 );
2187 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2188 ? SLAP_CONTROL_CRITICAL
2189 : SLAP_CONTROL_NONCRITICAL );
2191 return LDAP_SUCCESS;
2193 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2196 chain_initialize( void )
2200 /* Make sure we don't exceed the bits reserved for userland */
2201 config_check_userland( CH_LAST );
2203 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2204 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2205 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2206 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2207 if ( rc != LDAP_SUCCESS ) {
2208 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2209 "unable to register chaining behavior control: %d.\n",
2213 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2215 ldapchain.on_bi.bi_type = "chain";
2216 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2217 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2218 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2219 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2220 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2222 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2224 ldapchain.on_response = ldap_chain_response;
2226 ldapchain.on_bi.bi_cf_ocs = chainocs;
2228 rc = config_register_schema( chaincfg, chainocs );
2233 return overlay_register( &ldapchain );