1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2012 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 return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
232 ldap_chain_uri_dup( void *c1, void *c2 )
234 ldapinfo_t *li1 = (ldapinfo_t *)c1;
235 ldapinfo_t *li2 = (ldapinfo_t *)c2;
237 assert( li1->li_bvuri != NULL );
238 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
239 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
241 assert( li2->li_bvuri != NULL );
242 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
243 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
245 if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
253 * Search specific response that strips entryDN from entries
256 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
258 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
260 assert( op->o_tag == LDAP_REQ_SEARCH );
262 /* if in error, don't proceed any further */
263 if ( lb->lb_status == LDAP_CH_ERR ) {
267 if ( rs->sr_type == REP_SEARCH ) {
268 Attribute **ap = &rs->sr_entry->e_attrs;
270 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
271 /* will be generated later by frontend
272 * (a cleaner solution would be that
273 * the frontend checks if it already exists */
274 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
281 /* there SHOULD be one only! */
286 /* tell the frontend not to add generated
287 * operational attributes */
288 rs->sr_flags |= REP_NO_OPERATIONALS;
290 return SLAP_CB_CONTINUE;
292 } else if ( rs->sr_type == REP_SEARCHREF ) {
293 /* if we get it here, it means the library was unable
294 * to chase the referral... */
295 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
296 rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
299 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
300 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
301 switch ( get_continuationBehavior( op ) ) {
302 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
303 lb->lb_status = LDAP_CH_ERR;
304 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
310 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
311 return SLAP_CB_CONTINUE;
313 } else if ( rs->sr_type == REP_RESULT ) {
314 if ( rs->sr_err == LDAP_REFERRAL
315 && lb->lb_depth < lb->lb_lc->lc_max_depth
316 && rs->sr_ref != NULL )
318 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
321 /* back-ldap tried to send result */
322 lb->lb_status = LDAP_CH_RES;
329 * Dummy response that simply traces if back-ldap tried to send
330 * anything to the client
333 ldap_chain_cb_response( Operation *op, SlapReply *rs )
335 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
337 /* if in error, don't proceed any further */
338 if ( lb->lb_status == LDAP_CH_ERR ) {
342 if ( rs->sr_type == REP_RESULT ) {
344 switch ( rs->sr_err ) {
345 case LDAP_COMPARE_TRUE:
346 case LDAP_COMPARE_FALSE:
347 if ( op->o_tag != LDAP_REQ_COMPARE ) {
353 lb->lb_status = LDAP_CH_RES;
357 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
358 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
362 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
363 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
364 switch ( get_continuationBehavior( op ) ) {
365 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
366 lb->lb_status = LDAP_CH_ERR;
367 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
373 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
380 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
382 /* strip the entryDN attribute, but keep returning results */
383 (void)ldap_chain_cb_search_response( op, rs );
386 return SLAP_CB_CONTINUE;
397 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
398 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
399 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
400 struct berval odn = op->o_req_dn,
401 ondn = op->o_req_ndn;
402 ldapinfo_t li = { 0 }, *lip = NULL;
403 struct berval bvuri[ 2 ] = { { 0 } };
405 /* NOTE: returned if ref is empty... */
409 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
410 LDAPControl **ctrls = NULL;
412 (void)chaining_control_add( lc, op, &ctrls );
413 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
417 for ( ; !BER_BVISNULL( ref ); ref++ ) {
418 SlapReply rs2 = { 0 };
419 LDAPURLDesc *srv = NULL;
420 req_search_s save_oq_search = op->oq_search,
421 tmp_oq_search = { 0 };
422 struct berval dn = BER_BVNULL,
429 /* We're setting the URI of the first referral;
430 * what if there are more?
436 If the client wishes to progress the operation, it MUST follow the
437 referral by contacting one of the supported services. If multiple
438 URIs are present, the client assumes that any supported URI may be
439 used to progress the operation.
441 * so we actually need to follow exactly one,
442 * and we can assume any is fine.
445 /* parse reference and use
446 * proto://[host][:port]/ only */
447 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
448 if ( rc != LDAP_URL_SUCCESS ) {
449 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
450 op->o_log_prefix, ref->bv_val, 0 );
457 if ( op->o_tag == LDAP_REQ_SEARCH ) {
458 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
459 /* RFC 4511: if scope is present, use it */
460 tmp_oq_search.rs_scope = srv->lud_scope;
463 /* RFC 4511: if scope is absent, use original */
464 tmp_oq_search.rs_scope = op->ors_scope;
469 srv->lud_scope = LDAP_SCOPE_DEFAULT;
470 dn.bv_val = srv->lud_dn;
471 filter = srv->lud_filter;
474 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
475 if ( srv->lud_dn == NULL ) {
480 ber_str2bv( srv->lud_dn, 0, 0, &dn );
481 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
482 if ( rc == LDAP_SUCCESS ) {
483 /* remove DN essentially because later on
484 * ldap_initialize() will parse the URL
485 * as a comma-separated URL list */
492 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
494 if ( srv->lud_filter != NULL
495 && srv->lud_filter[0] != '\0'
496 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
498 /* RFC 4511: if filter is present, use it;
499 * otherwise, use original */
500 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
501 if ( tmp_oq_search.rs_filter != NULL ) {
502 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
505 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
506 op->o_log_prefix, ref->bv_val, srv->lud_filter );
511 srv->lud_filter = NULL;
513 if ( rc == LDAP_SUCCESS ) {
514 li.li_uri = ldap_url_desc2str( srv );
517 srv->lud_dn = dn.bv_val;
518 srv->lud_filter = filter;
519 ldap_free_urldesc( srv );
521 if ( rc != LDAP_SUCCESS ) {
527 if ( li.li_uri == NULL ) {
528 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
529 op->o_log_prefix, ref->bv_val, 0 );
533 goto further_cleanup;
536 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
537 op->o_log_prefix, ref->bv_val, li.li_uri );
542 if ( op->o_tag == LDAP_REQ_SEARCH ) {
543 op->ors_scope = tmp_oq_search.rs_scope;
544 if ( tmp_oq_search.rs_filter != NULL ) {
545 op->ors_filter = tmp_oq_search.rs_filter;
546 op->ors_filterstr = tmp_oq_search.rs_filterstr;
550 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
552 /* Searches for a ldapinfo in the avl tree */
553 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
554 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
555 (caddr_t)&li, ldap_chain_uri_cmp );
556 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
559 op->o_bd->be_private = (void *)lip;
561 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
562 op->o_log_prefix, ref->bv_val, li.li_uri );
565 rc = ldap_chain_db_init_one( op->o_bd );
567 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
568 op->o_log_prefix, ref->bv_val, li.li_uri );
571 lip = (ldapinfo_t *)op->o_bd->be_private;
572 lip->li_uri = li.li_uri;
573 lip->li_bvuri = bvuri;
574 rc = ldap_chain_db_open_one( op->o_bd );
576 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
577 op->o_log_prefix, ref->bv_val, li.li_uri );
579 lip->li_bvuri = NULL;
580 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
584 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
585 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
586 if ( avl_insert( &lc->lc_lai.lai_tree,
587 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
589 /* someone just inserted another;
590 * don't bother, use this and then
594 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
600 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
601 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
605 lb->lb_depth = depth + 1;
607 rc = op_f( op, &rs2 );
609 /* note the first error */
610 if ( first_rc == -1 ) {
615 ldap_memfree( li.li_uri );
620 lip->li_bvuri = NULL;
621 (void)ldap_chain_db_close_one( op->o_bd );
622 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
627 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
628 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
631 if ( op->o_tag == LDAP_REQ_SEARCH ) {
632 if ( tmp_oq_search.rs_filter != NULL ) {
633 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
636 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
637 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
640 op->oq_search = save_oq_search;
643 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
652 op->o_req_ndn = ondn;
654 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
655 (void)chaining_control_remove( op, &ctrls );
656 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
658 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
673 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
674 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
675 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
676 ldapinfo_t li = { 0 }, *lip = NULL;
677 struct berval bvuri[ 2 ] = { { 0 } };
679 struct berval odn = op->o_req_dn,
680 ondn = op->o_req_ndn;
681 Entry *save_entry = rs->sr_entry;
682 slap_mask_t save_flags = rs->sr_flags;
687 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
688 LDAPControl **ctrls = NULL;
690 (void)chaining_control_add( lc, op, &ctrls );
691 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
693 assert( rs->sr_type == REP_SEARCHREF );
695 rs->sr_type = REP_SEARCH;
697 /* if we parse the URI then by no means
698 * we can cache stuff or reuse connections,
699 * because in back-ldap there's no caching
700 * based on the URI value, which is supposed
701 * to be set once for all (correct?) */
703 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
704 SlapReply rs2 = { REP_RESULT };
706 req_search_s save_oq_search = op->oq_search,
707 tmp_oq_search = { 0 };
715 /* parse reference and use
716 * proto://[host][:port]/ only */
717 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
718 if ( rc != LDAP_URL_SUCCESS ) {
719 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
720 op->o_log_prefix, ref->bv_val, 0 );
723 rs->sr_err = LDAP_OTHER;
727 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
728 /* RFC 4511: if scope is present, use it */
729 tmp_oq_search.rs_scope = srv->lud_scope;
732 /* RFC 4511: if scope is absent, use original */
733 /* Section 4.5.3: if scope is onelevel, use base */
734 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
735 tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
737 tmp_oq_search.rs_scope = op->ors_scope;
741 srv->lud_scope = LDAP_SCOPE_DEFAULT;
742 dn.bv_val = srv->lud_dn;
743 filter = srv->lud_filter;
746 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
747 if ( srv->lud_dn == NULL ) {
751 if ( save_entry != NULL ) {
752 /* use the "right" DN, if available */
753 pdn = save_entry->e_name;
754 ndn = save_entry->e_nname;
755 } /* else leave the original req DN in place, if any RFC 4511 */
758 /* RFC 4511: if DN is present, use it */
759 ber_str2bv( srv->lud_dn, 0, 0, &dn );
760 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
761 if ( rc == LDAP_SUCCESS ) {
762 /* remove DN essentially because later on
763 * ldap_initialize() will parse the URL
764 * as a comma-separated URL list */
771 if ( rc == LDAP_SUCCESS ) {
773 if ( srv->lud_filter != NULL
774 && srv->lud_filter[0] != '\0'
775 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
777 /* RFC 4511: if filter is present, use it;
778 * otherwise, use original */
779 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
780 if ( tmp_oq_search.rs_filter != NULL ) {
781 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
784 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
785 op->o_log_prefix, ref->bv_val, srv->lud_filter );
790 srv->lud_filter = NULL;
792 if ( rc == LDAP_SUCCESS ) {
793 li.li_uri = ldap_url_desc2str( srv );
796 srv->lud_dn = dn.bv_val;
797 srv->lud_filter = filter;
798 ldap_free_urldesc( srv );
800 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
801 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
802 op->o_log_prefix, ref->bv_val, 0 );
806 goto further_cleanup;
809 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
810 op->o_log_prefix, ref->bv_val, li.li_uri );
814 op->ors_scope = tmp_oq_search.rs_scope;
815 if ( tmp_oq_search.rs_filter != NULL ) {
816 op->ors_filter = tmp_oq_search.rs_filter;
817 op->ors_filterstr = tmp_oq_search.rs_filterstr;
820 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
822 /* Searches for a ldapinfo in the avl tree */
823 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
824 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
825 (caddr_t)&li, ldap_chain_uri_cmp );
826 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
829 op->o_bd->be_private = (void *)lip;
831 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
832 op->o_log_prefix, ref->bv_val, li.li_uri );
835 /* if none is found, create a temporary... */
836 rc = ldap_chain_db_init_one( op->o_bd );
838 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
839 op->o_log_prefix, ref->bv_val, li.li_uri );
842 lip = (ldapinfo_t *)op->o_bd->be_private;
843 lip->li_uri = li.li_uri;
844 lip->li_bvuri = bvuri;
845 rc = ldap_chain_db_open_one( op->o_bd );
847 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
848 op->o_log_prefix, ref->bv_val, li.li_uri );
850 lip->li_bvuri = NULL;
851 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
855 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
856 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
857 if ( avl_insert( &lc->lc_lai.lai_tree,
858 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
860 /* someone just inserted another;
861 * don't bother, use this and then
865 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
871 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
872 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
875 lb->lb_op_f = lback->bi_op_search;
876 lb->lb_depth = depth + 1;
878 /* FIXME: should we also copy filter and scope?
879 * according to RFC3296, no */
880 rc = lback->bi_op_search( op, &rs2 );
881 if ( first_rc == -1 ) {
886 ldap_memfree( li.li_uri );
891 lip->li_bvuri = NULL;
892 (void)ldap_chain_db_close_one( op->o_bd );
893 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
898 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
899 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
903 op->o_req_ndn = ondn;
905 if ( tmp_oq_search.rs_filter != NULL ) {
906 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
909 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
910 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
913 op->oq_search = save_oq_search;
915 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
923 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
924 (void)chaining_control_remove( op, &ctrls );
925 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
928 op->o_req_ndn = ondn;
929 rs->sr_type = REP_SEARCHREF;
930 rs->sr_entry = save_entry;
931 rs->sr_flags = save_flags;
933 if ( rc != LDAP_SUCCESS ) {
934 /* couldn't chase any of the referrals */
935 if ( first_rc != -1 ) {
939 rc = SLAP_CB_CONTINUE;
947 ldap_chain_response( Operation *op, SlapReply *rs )
949 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
950 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
951 BackendDB db, *bd = op->o_bd;
952 ldap_chain_cb_t lb = { 0 };
953 slap_callback *sc = op->o_callback,
956 const char *text = NULL;
959 struct berval ndn = op->o_ndn;
961 int sr_err = rs->sr_err;
962 slap_reply_t sr_type = rs->sr_type;
963 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
964 slap_mask_t chain_mask = 0;
965 ber_len_t chain_shift = 0;
966 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
968 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
969 return SLAP_CB_CONTINUE;
972 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
973 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
974 switch ( get_resolveBehavior( op ) ) {
975 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
976 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
977 return SLAP_CB_CONTINUE;
980 chain_mask = SLAP_CH_RESOLVE_MASK;
981 chain_shift = SLAP_CH_RESOLVE_SHIFT;
985 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
986 switch ( get_continuationBehavior( op ) ) {
987 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
988 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
989 return SLAP_CB_CONTINUE;
992 chain_mask = SLAP_CH_CONTINUATION_MASK;
993 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
997 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1000 * TODO: add checks on who/when chain operations; e.g.:
1001 * a) what identities are authorized
1002 * b) what request DN (e.g. only chain requests rooted at <DN>)
1003 * c) what referral URIs
1004 * d) what protocol scheme (e.g. only ldaps://)
1009 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1014 matched = rs->sr_matched;
1015 rs->sr_matched = NULL;
1019 /* we need this to know if back-ldap returned any result */
1021 sc2.sc_next = sc->sc_next;
1022 sc2.sc_private = &lb;
1023 sc2.sc_response = ldap_chain_cb_response;
1024 op->o_callback = &sc2;
1026 /* Chaining can be performed by a privileged user on behalf
1027 * of normal users, using the ProxyAuthz control, by exploiting
1028 * the identity assertion feature of back-ldap; see idassert-*
1029 * directives in slapd-ldap(5).
1031 * FIXME: the idassert-authcDN is one, will it be fine regardless
1032 * of the URI we obtain from the referral?
1035 switch ( op->o_tag ) {
1036 case LDAP_REQ_BIND: {
1037 struct berval rndn = op->o_req_ndn;
1038 Connection *conn = op->o_conn;
1040 /* FIXME: can we really get a referral for binds? */
1041 op->o_req_ndn = slap_empty_bv;
1043 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1044 op->o_req_ndn = rndn;
1050 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1053 case LDAP_REQ_DELETE:
1054 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1057 case LDAP_REQ_MODRDN:
1058 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1061 case LDAP_REQ_MODIFY:
1062 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1065 case LDAP_REQ_COMPARE:
1066 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
1067 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1072 case LDAP_REQ_SEARCH:
1073 if ( rs->sr_type == REP_SEARCHREF ) {
1074 sc2.sc_response = ldap_chain_cb_search_response;
1075 rc = ldap_chain_search( op, rs, ref, 0 );
1078 /* we might get here before any database actually
1079 * performed a search; in those cases, we need
1080 * to check limits, to make sure safe defaults
1082 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1083 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1086 rc = SLAP_CB_CONTINUE;
1091 case LDAP_REQ_EXTENDED:
1092 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
1093 /* FIXME: ldap_back_extended() by design
1094 * doesn't send result; frontend is expected
1096 /* FIXME: what about chaining? */
1097 if ( rc != SLAPD_ABANDON ) {
1099 send_ldap_extended( op, rs );
1102 lb.lb_status = LDAP_CH_RES;
1106 rc = SLAP_CB_CONTINUE;
1116 sr_err = rs->sr_err;
1117 /* slapd-ldap sent response */
1118 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1119 /* FIXME: should we send response? */
1120 Debug( LDAP_DEBUG_ANY,
1121 "%s: ldap_chain_response: "
1122 "overlay should have sent result.\n",
1123 op->o_log_prefix, 0, 0 );
1128 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1129 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1133 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1134 case LDAP_CHAINING_REQUIRED:
1136 op->o_callback = NULL;
1137 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1138 "operation cannot be completed without chaining" );
1142 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1143 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1144 sr_err = rs->sr_err = rc;
1145 rs->sr_type = sr_type;
1148 rc = SLAP_CB_CONTINUE;
1149 rs->sr_err = sr_err;
1150 rs->sr_type = sr_type;
1152 rs->sr_matched = matched;
1155 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1158 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1161 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1162 /* give the remaining callbacks a chance */
1163 op->o_callback = sc->sc_next;
1164 rc = rs->sr_err = slap_map_api2result( rs );
1165 send_ldap_result( op, rs );
1169 rs->sr_err = sr_err;
1170 rs->sr_type = sr_type;
1172 rs->sr_matched = matched;
1175 op->o_callback = sc;
1181 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1183 ldap_chain_parse_ctrl(
1186 LDAPControl *ctrl );
1189 str2chain( const char *s )
1191 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1192 return LDAP_CHAINING_PREFERRED;
1194 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1195 return LDAP_CHAINING_REQUIRED;
1197 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1198 return LDAP_REFERRALS_PREFERRED;
1200 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1201 return LDAP_REFERRALS_REQUIRED;
1206 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1221 static ConfigDriver chain_cf_gen;
1222 static ConfigCfAdd chain_cfadd;
1223 static ConfigLDAPadd chain_ldadd;
1224 #ifdef SLAP_CONFIG_DELETE
1225 static ConfigLDAPdel chain_lddel;
1228 static ConfigTable chaincfg[] = {
1229 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1230 { "chain-chaining", "args",
1231 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1232 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1233 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1234 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1235 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1236 { "chain-cache-uri", "TRUE/FALSE",
1237 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1238 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1239 "DESC 'Enables caching of URIs not present in configuration' "
1240 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1241 { "chain-max-depth", "args",
1242 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1243 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1244 "DESC 'max referral depth' "
1245 "SYNTAX OMsInteger "
1246 "EQUALITY integerMatch "
1247 "SINGLE-VALUE )", NULL, NULL },
1248 { "chain-return-error", "TRUE/FALSE",
1249 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1250 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1251 "DESC 'Errors are returned instead of the original referral' "
1252 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1253 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1256 static ConfigOCs chainocs[] = {
1257 { "( OLcfgOvOc:3.1 "
1258 "NAME 'olcChainConfig' "
1259 "DESC 'Chain configuration' "
1260 "SUP olcOverlayConfig "
1262 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1263 "olcChainingBehavior $ "
1264 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1265 "olcChainCacheURI $ "
1266 "olcChainMaxReferralDepth $ "
1267 "olcChainReturnError "
1269 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1270 { "( OLcfgOvOc:3.2 "
1271 "NAME 'olcChainDatabase' "
1272 "DESC 'Chain remote server configuration' "
1274 Cft_Misc, olcDatabaseDummy, chain_ldadd
1275 #ifdef SLAP_CONFIG_DELETE
1283 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1290 AttributeDescription *ad = NULL;
1296 if ( p->ce_type != Cft_Overlay
1298 || p->ce_bi->bi_cf_ocs != chainocs )
1300 return LDAP_CONSTRAINT_VIOLATION;
1303 on = (slap_overinst *)p->ce_bi;
1304 lc = (ldap_chain_t *)on->on_bi.bi_private;
1306 assert( ca->be == NULL );
1307 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1309 ca->be->bd_info = (BackendInfo *)on;
1311 rc = slap_str2ad( "olcDbURI", &ad, &text );
1312 assert( rc == LDAP_SUCCESS );
1314 at = attr_find( e->e_attrs, ad );
1316 if ( lc->lc_common_li == NULL && at != NULL ) {
1317 /* FIXME: we should generate an empty default entry
1318 * if none is supplied */
1319 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1320 "first underlying database \"%s\" "
1321 "cannot contain attribute \"%s\".\n",
1322 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1323 rc = LDAP_CONSTRAINT_VIOLATION;
1328 if ( lc->lc_common_li != NULL && at == NULL ) {
1329 /* FIXME: we should generate an empty default entry
1330 * if none is supplied */
1331 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1332 "subsequent underlying database \"%s\" "
1333 "must contain attribute \"%s\".\n",
1334 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1335 rc = LDAP_CONSTRAINT_VIOLATION;
1339 if ( lc->lc_common_li == NULL ) {
1340 rc = ldap_chain_db_init_common( ca->be );
1343 rc = ldap_chain_db_init_one( ca->be );
1347 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1348 "unable to init %sunderlying database \"%s\".\n",
1349 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1350 return LDAP_CONSTRAINT_VIOLATION;
1353 li = ca->be->be_private;
1355 if ( lc->lc_common_li == NULL ) {
1356 lc->lc_common_li = li;
1359 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1360 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1361 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1362 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1364 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1365 "database \"%s\" insert failed.\n",
1366 e->e_name.bv_val, 0, 0 );
1367 rc = LDAP_CONSTRAINT_VIOLATION;
1372 ca->ca_private = on;
1375 if ( rc != LDAP_SUCCESS ) {
1376 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1384 typedef struct ldap_chain_cfadd_apply_t {
1390 } ldap_chain_cfadd_apply_t;
1393 ldap_chain_cfadd_apply( void *datum, void *arg )
1395 ldapinfo_t *li = (ldapinfo_t *)datum;
1396 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1400 /* FIXME: should not hardcode "olcDatabase" here */
1401 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1402 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1403 bv.bv_val = lca->ca->cr_msg;
1405 lca->ca->be->be_private = (void *)li;
1406 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1407 &bv, lback->bi_cf_ocs, &chainocs[1] );
1415 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1417 CfEntryInfo *pe = p->e_private;
1418 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1419 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1420 void *priv = (void *)ca->be->be_private;
1422 if ( lback->bi_cf_ocs ) {
1423 ldap_chain_cfadd_apply_t lca = { 0 };
1431 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1433 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1434 &lca, 1, AVL_INORDER );
1436 ca->be->be_private = priv;
1442 #ifdef SLAP_CONFIG_DELETE
1444 chain_lddel( CfEntryInfo *ce, Operation *op )
1446 CfEntryInfo *pe = ce->ce_parent;
1447 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1448 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1449 ldapinfo_t *li = (ldapinfo_t *) ce->ce_be->be_private;
1451 if ( li != lc->lc_common_li ) {
1452 if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
1453 Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. "
1454 "\"%s\" not found.\n", li->li_uri, 0, 0 );
1457 } else if ( lc->lc_lai.lai_tree ) {
1458 Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
1459 "LDAP database when other databases are still present.\n", 0, 0, 0 );
1462 lc->lc_common_li = NULL;
1465 ce->ce_be->bd_info = lback;
1467 if ( ce->ce_be->bd_info->bi_db_close ) {
1468 ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
1470 if ( ce->ce_be->bd_info->bi_db_destroy ) {
1471 ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
1477 return LDAP_SUCCESS;
1479 #endif /* SLAP_CONFIG_DELETE */
1481 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1482 static slap_verbmasks chaining_mode[] = {
1483 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1484 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1485 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1486 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1489 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1492 chain_cf_gen( ConfigArgs *c )
1494 slap_overinst *on = (slap_overinst *)c->bi;
1495 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1499 if ( c->op == SLAP_CONFIG_EMIT ) {
1501 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1503 struct berval resolve = BER_BVNULL,
1504 continuation = BER_BVNULL;
1506 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1510 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1511 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1513 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1515 + STRLENOF( "continuation=" ) + continuation.bv_len;
1516 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1517 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1518 "resolve=%s continuation=%s",
1519 resolve.bv_val, continuation.bv_val );
1521 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1522 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1523 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1524 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1525 " critical", STRLENOF( " critical" ) + 1 );
1526 c->value_bv.bv_len += STRLENOF( " critical" );
1531 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1534 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1538 c->value_int = lc->lc_max_depth;
1542 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1551 } else if ( c->op == LDAP_MOD_DELETE ) {
1557 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1565 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1576 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1577 char **argv = c->argv;
1579 BerElementBuffer berbuf;
1580 BerElement *ber = (BerElement *)&berbuf;
1584 Operation op = { 0 };
1585 SlapReply rs = { 0 };
1587 lc->lc_chaining_ctrlflag = 0;
1589 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1590 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1591 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1592 if ( resolve == -1 ) {
1593 Debug( LDAP_DEBUG_ANY, "%s: "
1594 "illegal <resolve> value %s "
1595 "in \"chain-chaining>\".\n",
1596 c->log, argv[ 0 ], 0 );
1600 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1601 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1602 if ( continuation == -1 ) {
1603 Debug( LDAP_DEBUG_ANY, "%s: "
1604 "illegal <continuation> value %s "
1605 "in \"chain-chaining\".\n",
1606 c->log, argv[ 0 ], 0 );
1610 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1614 Debug( LDAP_DEBUG_ANY, "%s: "
1615 "unknown option in \"chain-chaining\".\n",
1621 if ( resolve != -1 || continuation != -1 ) {
1624 if ( resolve == -1 ) {
1626 resolve = SLAP_CHAINING_DEFAULT;
1629 ber_init2( ber, NULL, LBER_USE_DER );
1631 err = ber_printf( ber, "{e" /* } */, resolve );
1634 Debug( LDAP_DEBUG_ANY, "%s: "
1635 "chaining behavior control encoding error!\n",
1640 if ( continuation > -1 ) {
1641 err = ber_printf( ber, "e", continuation );
1644 Debug( LDAP_DEBUG_ANY, "%s: "
1645 "chaining behavior control encoding error!\n",
1651 err = ber_printf( ber, /* { */ "N}" );
1654 Debug( LDAP_DEBUG_ANY, "%s: "
1655 "chaining behavior control encoding error!\n",
1660 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1661 exit( EXIT_FAILURE );
1665 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1668 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1669 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1671 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1673 Debug( LDAP_DEBUG_ANY, "%s: "
1674 "unable to parse chaining control%s%s.\n",
1675 c->log, rs.sr_text ? ": " : "",
1676 rs.sr_text ? rs.sr_text : "" );
1680 lc->lc_chaining_ctrlflag = op.o_chaining;
1682 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1685 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1686 Debug( LDAP_DEBUG_ANY, "%s: "
1687 "\"chaining\" control unsupported (ignored).\n",
1689 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1693 if ( c->value_int ) {
1694 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1696 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1701 if ( c->value_int < 0 ) {
1702 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1703 "<%s> invalid max referral depth %d",
1704 c->argv[0], c->value_int );
1705 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1706 c->log, c->cr_msg, 0 );
1710 lc->lc_max_depth = c->value_int;
1713 if ( c->value_int ) {
1714 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1716 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1732 slap_overinst *on = (slap_overinst *)be->bd_info;
1733 ldap_chain_t *lc = NULL;
1735 if ( lback == NULL ) {
1736 lback = backend_info( "ldap" );
1738 if ( lback == NULL ) {
1743 lc = ch_malloc( sizeof( ldap_chain_t ) );
1747 memset( lc, 0, sizeof( ldap_chain_t ) );
1748 lc->lc_max_depth = 1;
1749 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1751 on->on_bi.bi_private = (void *)lc;
1757 ldap_chain_db_config(
1764 slap_overinst *on = (slap_overinst *)be->bd_info;
1765 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1767 int rc = SLAP_CONF_UNKNOWN;
1769 if ( lc->lc_common_li == NULL ) {
1771 ldap_chain_db_init_common( &db );
1772 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1775 /* Something for the chain database? */
1776 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1777 char *save_argv0 = argv[ 0 ];
1779 static char *allowed_argv[] = {
1780 /* special: put URI here, so in the meanwhile
1781 * it detects whether a new URI is being provided */
1787 /* FIXME: maybe rebind-as-user should be allowed
1788 * only within known URIs... */
1795 int which_argv = -1;
1797 argv[ 0 ] += STRLENOF( "chain-" );
1799 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1800 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1805 if ( allowed_argv[ which_argv ] == NULL ) {
1808 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1809 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1810 "\"%s\" only allowed within a URI directive.\n.",
1811 fname, lineno, argv[ 0 ] );
1816 if ( which_argv == 0 ) {
1817 rc = ldap_chain_db_init_one( &db );
1819 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1820 "underlying slapd-ldap initialization failed.\n.",
1824 lc->lc_cfg_li = db.be_private;
1827 /* TODO: add checks on what other slapd-ldap(5) args
1828 * should be put in the template; this is not quite
1829 * harmful, because attributes that shouldn't don't
1830 * get actually used, but the user should at least
1835 db.be_private = (void *)lc->lc_cfg_li;
1836 db.be_cf_ocs = lback->bi_cf_ocs;
1838 rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1840 argv[ 0 ] = save_argv0;
1842 if ( which_argv == 0 ) {
1846 db.be_private = (void *)lc->lc_cfg_li;
1847 ldap_chain_db_destroy_one( &db, NULL );
1848 lc->lc_cfg_li = NULL;
1850 if ( lc->lc_cfg_li->li_bvuri == NULL
1851 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1852 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1854 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1855 "no URI list allowed in slapo-chain.\n",
1858 goto private_destroy;
1861 if ( avl_insert( &lc->lc_lai.lai_tree,
1862 (caddr_t)lc->lc_cfg_li,
1863 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1865 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1866 "duplicate URI in slapo-chain.\n",
1869 goto private_destroy;
1886 typedef struct ldap_chain_db_apply_t {
1889 } ldap_chain_db_apply_t;
1892 ldap_chain_db_apply( void *datum, void *arg )
1894 ldapinfo_t *li = (ldapinfo_t *)datum;
1895 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1897 lca->be->be_private = (void *)li;
1899 return lca->func( lca->be, NULL );
1908 slap_overinst *on = (slap_overinst *)be->bd_info;
1909 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1914 BI_db_func *func = (&lback->bi_db_open)[ which ];
1916 if ( func != NULL && lc->lc_common_li != NULL ) {
1920 db.be_private = lc->lc_common_li;
1922 rc = func( &db, NULL );
1928 if ( lc->lc_lai.lai_tree != NULL ) {
1929 ldap_chain_db_apply_t lca;
1934 rc = avl_apply( lc->lc_lai.lai_tree,
1935 ldap_chain_db_apply, (void *)&lca,
1936 1, AVL_INORDER ) != AVL_NOMORE;
1949 slap_overinst *on = (slap_overinst *) be->bd_info;
1950 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1951 slap_mask_t monitoring;
1954 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1955 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1959 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1961 if ( lc->lc_common_li == NULL ) {
1962 void *be_private = be->be_private;
1963 ldap_chain_db_init_common( be );
1964 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1965 be->be_private = be_private;
1968 /* filter out and restore monitoring */
1969 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1970 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1971 rc = ldap_chain_db_func( be, db_open );
1972 SLAP_DBFLAGS( be ) |= monitoring;
1978 ldap_chain_db_close(
1982 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1983 #ifdef SLAP_CONFIG_DELETE
1984 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1985 #endif /* SLAP_CONFIG_DELETE */
1986 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1987 return ldap_chain_db_func( be, db_close );
1991 ldap_chain_db_destroy(
1995 slap_overinst *on = (slap_overinst *) be->bd_info;
1996 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2000 rc = ldap_chain_db_func( be, db_destroy );
2003 avl_free( lc->lc_lai.lai_tree, NULL );
2004 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
2012 * inits one instance of the slapd-ldap backend, and stores
2013 * the private info in be_private of the arg
2016 ldap_chain_db_init_common(
2019 BackendInfo *bi = be->bd_info;
2023 be->bd_info = lback;
2024 be->be_private = NULL;
2025 rc = lback->bi_db_init( be, NULL );
2029 li = (ldapinfo_t *)be->be_private;
2030 li->li_urllist_f = NULL;
2031 li->li_urllist_p = NULL;
2039 * inits one instance of the slapd-ldap backend, stores
2040 * the private info in be_private of the arg and fills
2041 * selected fields with data from the template.
2043 * NOTE: add checks about the other fields of the template,
2044 * which are ignored and SHOULD NOT be configured by the user.
2047 ldap_chain_db_init_one(
2050 slap_overinst *on = (slap_overinst *)be->bd_info;
2051 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2053 BackendInfo *bi = be->bd_info;
2058 be->bd_info = lback;
2059 be->be_private = NULL;
2060 t = lback->bi_db_init( be, NULL );
2064 li = (ldapinfo_t *)be->be_private;
2065 li->li_urllist_f = NULL;
2066 li->li_urllist_p = NULL;
2068 /* copy common data */
2069 li->li_nretries = lc->lc_common_li->li_nretries;
2070 li->li_flags = lc->lc_common_li->li_flags;
2071 li->li_version = lc->lc_common_li->li_version;
2072 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2073 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2081 ldap_chain_db_open_one(
2084 if ( SLAP_DBMONITORING( be ) ) {
2085 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
2087 if ( li->li_uri == NULL ) {
2088 ber_str2bv( "cn=Common Connections", 0, 1,
2089 &li->li_monitor_info.lmi_rdn );
2094 li->li_monitor_info.lmi_rdn.bv_len
2095 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2096 ptr = li->li_monitor_info.lmi_rdn.bv_val
2097 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
2098 ptr = lutil_strcopy( ptr, "cn=" );
2099 ptr = lutil_strcopy( ptr, li->li_uri );
2104 return lback->bi_db_open( be, NULL );
2107 typedef struct ldap_chain_conn_apply_t {
2110 } ldap_chain_conn_apply_t;
2113 ldap_chain_conn_apply( void *datum, void *arg )
2115 ldapinfo_t *li = (ldapinfo_t *)datum;
2116 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2118 lca->be->be_private = (void *)li;
2120 return lback->bi_connection_destroy( lca->be, lca->conn );
2124 ldap_chain_connection_destroy(
2129 slap_overinst *on = (slap_overinst *) be->bd_info;
2130 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2131 void *private = be->be_private;
2132 ldap_chain_conn_apply_t lca;
2135 be->be_private = NULL;
2138 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2139 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2140 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2141 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2142 be->be_private = private;
2147 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2149 ldap_chain_parse_ctrl(
2159 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2160 rs->sr_text = "Chaining behavior control specified multiple times";
2161 return LDAP_PROTOCOL_ERROR;
2164 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2165 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2166 return LDAP_PROTOCOL_ERROR;
2169 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2170 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2175 /* Parse the control value
2176 * ChainingBehavior ::= SEQUENCE {
2177 * resolveBehavior Behavior OPTIONAL,
2178 * continuationBehavior Behavior OPTIONAL }
2180 * Behavior :: = ENUMERATED {
2181 * chainingPreferred (0),
2182 * chainingRequired (1),
2183 * referralsPreferred (2),
2184 * referralsRequired (3) }
2187 ber = ber_init( &ctrl->ldctl_value );
2189 rs->sr_text = "internal error";
2193 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2194 /* FIXME: since the whole SEQUENCE is optional,
2195 * should we accept no enumerations at all? */
2196 if ( tag != LBER_ENUMERATED ) {
2197 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2198 return LDAP_PROTOCOL_ERROR;
2201 switch ( behavior ) {
2202 case LDAP_CHAINING_PREFERRED:
2203 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2206 case LDAP_CHAINING_REQUIRED:
2207 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2210 case LDAP_REFERRALS_PREFERRED:
2211 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2214 case LDAP_REFERRALS_REQUIRED:
2215 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2219 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2220 return LDAP_PROTOCOL_ERROR;
2223 tag = ber_peek_tag( ber, &len );
2224 if ( tag == LBER_ENUMERATED ) {
2225 tag = ber_scanf( ber, "e", &behavior );
2226 if ( tag == LBER_ERROR ) {
2227 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2228 return LDAP_PROTOCOL_ERROR;
2232 if ( tag == LBER_DEFAULT ) {
2233 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2236 switch ( behavior ) {
2237 case LDAP_CHAINING_PREFERRED:
2238 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2241 case LDAP_CHAINING_REQUIRED:
2242 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2245 case LDAP_REFERRALS_PREFERRED:
2246 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2249 case LDAP_REFERRALS_REQUIRED:
2250 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2254 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2255 return LDAP_PROTOCOL_ERROR;
2259 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2260 rs->sr_text = "Chaining behavior control: decoding error";
2261 return LDAP_PROTOCOL_ERROR;
2264 (void) ber_free( ber, 1 );
2267 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2268 ? SLAP_CONTROL_CRITICAL
2269 : SLAP_CONTROL_NONCRITICAL );
2271 return LDAP_SUCCESS;
2273 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2276 chain_initialize( void )
2280 /* Make sure we don't exceed the bits reserved for userland */
2281 config_check_userland( CH_LAST );
2283 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2284 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2285 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2286 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2287 if ( rc != LDAP_SUCCESS ) {
2288 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2289 "unable to register chaining behavior control: %d.\n",
2293 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2295 ldapchain.on_bi.bi_type = "chain";
2296 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2297 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2298 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2299 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2300 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2302 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2304 ldapchain.on_response = ldap_chain_response;
2306 ldapchain.on_bi.bi_cf_ocs = chainocs;
2308 rc = config_register_schema( chaincfg, chainocs );
2313 return overlay_register( &ldapchain );