1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2011 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;
1225 static ConfigTable chaincfg[] = {
1226 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1227 { "chain-chaining", "args",
1228 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1229 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1230 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1231 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1232 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1233 { "chain-cache-uri", "TRUE/FALSE",
1234 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1235 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1236 "DESC 'Enables caching of URIs not present in configuration' "
1237 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1238 { "chain-max-depth", "args",
1239 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1240 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1241 "DESC 'max referral depth' "
1242 "SYNTAX OMsInteger "
1243 "EQUALITY integerMatch "
1244 "SINGLE-VALUE )", NULL, NULL },
1245 { "chain-return-error", "TRUE/FALSE",
1246 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1247 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1248 "DESC 'Errors are returned instead of the original referral' "
1249 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1250 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1253 static ConfigOCs chainocs[] = {
1254 { "( OLcfgOvOc:3.1 "
1255 "NAME 'olcChainConfig' "
1256 "DESC 'Chain configuration' "
1257 "SUP olcOverlayConfig "
1259 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1260 "olcChainingBehavior $ "
1261 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1262 "olcChainCacheURI $ "
1263 "olcChainMaxReferralDepth $ "
1264 "olcChainReturnError "
1266 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1267 { "( OLcfgOvOc:3.2 "
1268 "NAME 'olcChainDatabase' "
1269 "DESC 'Chain remote server configuration' "
1270 "SUP olcLDAPConfig )",
1271 Cft_Misc, olcDatabaseDummy, chain_ldadd },
1276 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1283 AttributeDescription *ad = NULL;
1289 if ( p->ce_type != Cft_Overlay
1291 || p->ce_bi->bi_cf_ocs != chainocs )
1293 return LDAP_CONSTRAINT_VIOLATION;
1296 on = (slap_overinst *)p->ce_bi;
1297 lc = (ldap_chain_t *)on->on_bi.bi_private;
1299 assert( ca->be == NULL );
1300 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1302 ca->be->bd_info = (BackendInfo *)on;
1304 rc = slap_str2ad( "olcDbURI", &ad, &text );
1305 assert( rc == LDAP_SUCCESS );
1307 at = attr_find( e->e_attrs, ad );
1309 if ( lc->lc_common_li == NULL && at != NULL ) {
1310 /* FIXME: we should generate an empty default entry
1311 * if none is supplied */
1312 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1313 "first underlying database \"%s\" "
1314 "cannot contain attribute \"%s\".\n",
1315 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1316 rc = LDAP_CONSTRAINT_VIOLATION;
1321 if ( lc->lc_common_li != NULL && at == NULL ) {
1322 /* FIXME: we should generate an empty default entry
1323 * if none is supplied */
1324 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1325 "subsequent underlying database \"%s\" "
1326 "must contain attribute \"%s\".\n",
1327 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1328 rc = LDAP_CONSTRAINT_VIOLATION;
1332 if ( lc->lc_common_li == NULL ) {
1333 rc = ldap_chain_db_init_common( ca->be );
1336 rc = ldap_chain_db_init_one( ca->be );
1340 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1341 "unable to init %sunderlying database \"%s\".\n",
1342 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1343 return LDAP_CONSTRAINT_VIOLATION;
1346 li = ca->be->be_private;
1348 if ( lc->lc_common_li == NULL ) {
1349 lc->lc_common_li = li;
1352 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1353 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1354 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1355 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1357 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1358 "database \"%s\" insert failed.\n",
1359 e->e_name.bv_val, 0, 0 );
1360 rc = LDAP_CONSTRAINT_VIOLATION;
1365 ca->ca_private = on;
1368 if ( rc != LDAP_SUCCESS ) {
1369 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1377 typedef struct ldap_chain_cfadd_apply_t {
1383 } ldap_chain_cfadd_apply_t;
1386 ldap_chain_cfadd_apply( void *datum, void *arg )
1388 ldapinfo_t *li = (ldapinfo_t *)datum;
1389 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1393 /* FIXME: should not hardcode "olcDatabase" here */
1394 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1395 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1396 bv.bv_val = lca->ca->cr_msg;
1398 lca->ca->be->be_private = (void *)li;
1399 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1400 &bv, lback->bi_cf_ocs, &chainocs[1] );
1408 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1410 CfEntryInfo *pe = p->e_private;
1411 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1412 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1413 void *priv = (void *)ca->be->be_private;
1415 if ( lback->bi_cf_ocs ) {
1416 ldap_chain_cfadd_apply_t lca = { 0 };
1424 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1426 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1427 &lca, 1, AVL_INORDER );
1429 ca->be->be_private = priv;
1435 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1436 static slap_verbmasks chaining_mode[] = {
1437 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1438 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1439 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1440 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1443 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1446 chain_cf_gen( ConfigArgs *c )
1448 slap_overinst *on = (slap_overinst *)c->bi;
1449 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1453 if ( c->op == SLAP_CONFIG_EMIT ) {
1455 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1457 struct berval resolve = BER_BVNULL,
1458 continuation = BER_BVNULL;
1460 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1464 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1465 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1467 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1469 + STRLENOF( "continuation=" ) + continuation.bv_len;
1470 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1471 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1472 "resolve=%s continuation=%s",
1473 resolve.bv_val, continuation.bv_val );
1475 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1476 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1477 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1478 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1479 " critical", STRLENOF( " critical" ) + 1 );
1480 c->value_bv.bv_len += STRLENOF( " critical" );
1485 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1488 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1492 c->value_int = lc->lc_max_depth;
1496 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1505 } else if ( c->op == LDAP_MOD_DELETE ) {
1511 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1519 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1530 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1531 char **argv = c->argv;
1533 BerElementBuffer berbuf;
1534 BerElement *ber = (BerElement *)&berbuf;
1538 Operation op = { 0 };
1539 SlapReply rs = { 0 };
1541 lc->lc_chaining_ctrlflag = 0;
1543 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1544 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1545 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1546 if ( resolve == -1 ) {
1547 Debug( LDAP_DEBUG_ANY, "%s: "
1548 "illegal <resolve> value %s "
1549 "in \"chain-chaining>\".\n",
1550 c->log, argv[ 0 ], 0 );
1554 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1555 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1556 if ( continuation == -1 ) {
1557 Debug( LDAP_DEBUG_ANY, "%s: "
1558 "illegal <continuation> value %s "
1559 "in \"chain-chaining\".\n",
1560 c->log, argv[ 0 ], 0 );
1564 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1568 Debug( LDAP_DEBUG_ANY, "%s: "
1569 "unknown option in \"chain-chaining\".\n",
1575 if ( resolve != -1 || continuation != -1 ) {
1578 if ( resolve == -1 ) {
1580 resolve = SLAP_CHAINING_DEFAULT;
1583 ber_init2( ber, NULL, LBER_USE_DER );
1585 err = ber_printf( ber, "{e" /* } */, resolve );
1588 Debug( LDAP_DEBUG_ANY, "%s: "
1589 "chaining behavior control encoding error!\n",
1594 if ( continuation > -1 ) {
1595 err = ber_printf( ber, "e", continuation );
1598 Debug( LDAP_DEBUG_ANY, "%s: "
1599 "chaining behavior control encoding error!\n",
1605 err = ber_printf( ber, /* { */ "N}" );
1608 Debug( LDAP_DEBUG_ANY, "%s: "
1609 "chaining behavior control encoding error!\n",
1614 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1615 exit( EXIT_FAILURE );
1619 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1622 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1623 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1625 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1627 Debug( LDAP_DEBUG_ANY, "%s: "
1628 "unable to parse chaining control%s%s.\n",
1629 c->log, rs.sr_text ? ": " : "",
1630 rs.sr_text ? rs.sr_text : "" );
1634 lc->lc_chaining_ctrlflag = op.o_chaining;
1636 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1639 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1640 Debug( LDAP_DEBUG_ANY, "%s: "
1641 "\"chaining\" control unsupported (ignored).\n",
1643 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1647 if ( c->value_int ) {
1648 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1650 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1655 if ( c->value_int < 0 ) {
1656 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1657 "<%s> invalid max referral depth %d",
1658 c->argv[0], c->value_int );
1659 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1660 c->log, c->cr_msg, 0 );
1664 lc->lc_max_depth = c->value_int;
1667 if ( c->value_int ) {
1668 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1670 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1686 slap_overinst *on = (slap_overinst *)be->bd_info;
1687 ldap_chain_t *lc = NULL;
1689 if ( lback == NULL ) {
1690 lback = backend_info( "ldap" );
1692 if ( lback == NULL ) {
1697 lc = ch_malloc( sizeof( ldap_chain_t ) );
1701 memset( lc, 0, sizeof( ldap_chain_t ) );
1702 lc->lc_max_depth = 1;
1703 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1705 on->on_bi.bi_private = (void *)lc;
1711 ldap_chain_db_config(
1718 slap_overinst *on = (slap_overinst *)be->bd_info;
1719 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1721 int rc = SLAP_CONF_UNKNOWN;
1723 if ( lc->lc_common_li == NULL ) {
1725 ldap_chain_db_init_common( &db );
1726 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1729 /* Something for the chain database? */
1730 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1731 char *save_argv0 = argv[ 0 ];
1733 static char *allowed_argv[] = {
1734 /* special: put URI here, so in the meanwhile
1735 * it detects whether a new URI is being provided */
1741 /* FIXME: maybe rebind-as-user should be allowed
1742 * only within known URIs... */
1749 int which_argv = -1;
1751 argv[ 0 ] += STRLENOF( "chain-" );
1753 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1754 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1759 if ( allowed_argv[ which_argv ] == NULL ) {
1762 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1763 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1764 "\"%s\" only allowed within a URI directive.\n.",
1765 fname, lineno, argv[ 0 ] );
1770 if ( which_argv == 0 ) {
1771 rc = ldap_chain_db_init_one( &db );
1773 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1774 "underlying slapd-ldap initialization failed.\n.",
1778 lc->lc_cfg_li = db.be_private;
1781 /* TODO: add checks on what other slapd-ldap(5) args
1782 * should be put in the template; this is not quite
1783 * harmful, because attributes that shouldn't don't
1784 * get actually used, but the user should at least
1789 db.be_private = (void *)lc->lc_cfg_li;
1790 db.be_cf_ocs = lback->bi_cf_ocs;
1792 rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1794 argv[ 0 ] = save_argv0;
1796 if ( which_argv == 0 ) {
1800 db.be_private = (void *)lc->lc_cfg_li;
1801 ldap_chain_db_destroy_one( &db, NULL );
1802 lc->lc_cfg_li = NULL;
1804 if ( lc->lc_cfg_li->li_bvuri == NULL
1805 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1806 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1808 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1809 "no URI list allowed in slapo-chain.\n",
1812 goto private_destroy;
1815 if ( avl_insert( &lc->lc_lai.lai_tree,
1816 (caddr_t)lc->lc_cfg_li,
1817 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1819 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1820 "duplicate URI in slapo-chain.\n",
1823 goto private_destroy;
1840 typedef struct ldap_chain_db_apply_t {
1843 } ldap_chain_db_apply_t;
1846 ldap_chain_db_apply( void *datum, void *arg )
1848 ldapinfo_t *li = (ldapinfo_t *)datum;
1849 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1851 lca->be->be_private = (void *)li;
1853 return lca->func( lca->be, NULL );
1862 slap_overinst *on = (slap_overinst *)be->bd_info;
1863 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1868 BI_db_func *func = (&lback->bi_db_open)[ which ];
1870 if ( func != NULL && lc->lc_common_li != NULL ) {
1874 db.be_private = lc->lc_common_li;
1876 rc = func( &db, NULL );
1882 if ( lc->lc_lai.lai_tree != NULL ) {
1883 ldap_chain_db_apply_t lca;
1888 rc = avl_apply( lc->lc_lai.lai_tree,
1889 ldap_chain_db_apply, (void *)&lca,
1890 1, AVL_INORDER ) != AVL_NOMORE;
1903 slap_overinst *on = (slap_overinst *) be->bd_info;
1904 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1905 slap_mask_t monitoring;
1908 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1909 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1913 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1915 if ( lc->lc_common_li == NULL ) {
1916 void *be_private = be->be_private;
1917 ldap_chain_db_init_common( be );
1918 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1919 be->be_private = be_private;
1922 /* filter out and restore monitoring */
1923 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1924 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1925 rc = ldap_chain_db_func( be, db_open );
1926 SLAP_DBFLAGS( be ) |= monitoring;
1932 ldap_chain_db_close(
1936 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1937 #ifdef SLAP_CONFIG_DELETE
1938 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1939 #endif /* SLAP_CONFIG_DELETE */
1940 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1941 return ldap_chain_db_func( be, db_close );
1945 ldap_chain_db_destroy(
1949 slap_overinst *on = (slap_overinst *) be->bd_info;
1950 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1954 rc = ldap_chain_db_func( be, db_destroy );
1957 avl_free( lc->lc_lai.lai_tree, NULL );
1958 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1966 * inits one instance of the slapd-ldap backend, and stores
1967 * the private info in be_private of the arg
1970 ldap_chain_db_init_common(
1973 BackendInfo *bi = be->bd_info;
1977 be->bd_info = lback;
1978 be->be_private = NULL;
1979 rc = lback->bi_db_init( be, NULL );
1983 li = (ldapinfo_t *)be->be_private;
1984 li->li_urllist_f = NULL;
1985 li->li_urllist_p = NULL;
1993 * inits one instance of the slapd-ldap backend, stores
1994 * the private info in be_private of the arg and fills
1995 * selected fields with data from the template.
1997 * NOTE: add checks about the other fields of the template,
1998 * which are ignored and SHOULD NOT be configured by the user.
2001 ldap_chain_db_init_one(
2004 slap_overinst *on = (slap_overinst *)be->bd_info;
2005 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2007 BackendInfo *bi = be->bd_info;
2012 be->bd_info = lback;
2013 be->be_private = NULL;
2014 t = lback->bi_db_init( be, NULL );
2018 li = (ldapinfo_t *)be->be_private;
2019 li->li_urllist_f = NULL;
2020 li->li_urllist_p = NULL;
2022 /* copy common data */
2023 li->li_nretries = lc->lc_common_li->li_nretries;
2024 li->li_flags = lc->lc_common_li->li_flags;
2025 li->li_version = lc->lc_common_li->li_version;
2026 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2027 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2035 ldap_chain_db_open_one(
2038 if ( SLAP_DBMONITORING( be ) ) {
2039 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
2041 if ( li->li_uri == NULL ) {
2042 ber_str2bv( "cn=Common Connections", 0, 1,
2043 &li->li_monitor_info.lmi_rdn );
2048 li->li_monitor_info.lmi_rdn.bv_len
2049 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2050 ptr = li->li_monitor_info.lmi_rdn.bv_val
2051 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
2052 ptr = lutil_strcopy( ptr, "cn=" );
2053 ptr = lutil_strcopy( ptr, li->li_uri );
2058 return lback->bi_db_open( be, NULL );
2061 typedef struct ldap_chain_conn_apply_t {
2064 } ldap_chain_conn_apply_t;
2067 ldap_chain_conn_apply( void *datum, void *arg )
2069 ldapinfo_t *li = (ldapinfo_t *)datum;
2070 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2072 lca->be->be_private = (void *)li;
2074 return lback->bi_connection_destroy( lca->be, lca->conn );
2078 ldap_chain_connection_destroy(
2083 slap_overinst *on = (slap_overinst *) be->bd_info;
2084 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2085 void *private = be->be_private;
2086 ldap_chain_conn_apply_t lca;
2089 be->be_private = NULL;
2092 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2093 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2094 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2095 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2096 be->be_private = private;
2101 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2103 ldap_chain_parse_ctrl(
2113 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2114 rs->sr_text = "Chaining behavior control specified multiple times";
2115 return LDAP_PROTOCOL_ERROR;
2118 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2119 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2120 return LDAP_PROTOCOL_ERROR;
2123 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2124 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2129 /* Parse the control value
2130 * ChainingBehavior ::= SEQUENCE {
2131 * resolveBehavior Behavior OPTIONAL,
2132 * continuationBehavior Behavior OPTIONAL }
2134 * Behavior :: = ENUMERATED {
2135 * chainingPreferred (0),
2136 * chainingRequired (1),
2137 * referralsPreferred (2),
2138 * referralsRequired (3) }
2141 ber = ber_init( &ctrl->ldctl_value );
2143 rs->sr_text = "internal error";
2147 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2148 /* FIXME: since the whole SEQUENCE is optional,
2149 * should we accept no enumerations at all? */
2150 if ( tag != LBER_ENUMERATED ) {
2151 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2152 return LDAP_PROTOCOL_ERROR;
2155 switch ( behavior ) {
2156 case LDAP_CHAINING_PREFERRED:
2157 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2160 case LDAP_CHAINING_REQUIRED:
2161 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2164 case LDAP_REFERRALS_PREFERRED:
2165 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2168 case LDAP_REFERRALS_REQUIRED:
2169 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2173 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2174 return LDAP_PROTOCOL_ERROR;
2177 tag = ber_peek_tag( ber, &len );
2178 if ( tag == LBER_ENUMERATED ) {
2179 tag = ber_scanf( ber, "e", &behavior );
2180 if ( tag == LBER_ERROR ) {
2181 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2182 return LDAP_PROTOCOL_ERROR;
2186 if ( tag == LBER_DEFAULT ) {
2187 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2190 switch ( behavior ) {
2191 case LDAP_CHAINING_PREFERRED:
2192 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2195 case LDAP_CHAINING_REQUIRED:
2196 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2199 case LDAP_REFERRALS_PREFERRED:
2200 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2203 case LDAP_REFERRALS_REQUIRED:
2204 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2208 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2209 return LDAP_PROTOCOL_ERROR;
2213 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2214 rs->sr_text = "Chaining behavior control: decoding error";
2215 return LDAP_PROTOCOL_ERROR;
2218 (void) ber_free( ber, 1 );
2221 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2222 ? SLAP_CONTROL_CRITICAL
2223 : SLAP_CONTROL_NONCRITICAL );
2225 return LDAP_SUCCESS;
2227 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2230 chain_initialize( void )
2234 /* Make sure we don't exceed the bits reserved for userland */
2235 config_check_userland( CH_LAST );
2237 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2238 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2239 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2240 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2241 if ( rc != LDAP_SUCCESS ) {
2242 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2243 "unable to register chaining behavior control: %d.\n",
2247 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2249 ldapchain.on_bi.bi_type = "chain";
2250 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2251 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2252 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2253 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2254 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2256 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2258 ldapchain.on_response = ldap_chain_response;
2260 ldapchain.on_bi.bi_cf_ocs = chainocs;
2262 rc = config_register_schema( chaincfg, chainocs );
2267 return overlay_register( &ldapchain );