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 /* If local DNs don't match, it is definitely not a match */
229 return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
233 ldap_chain_uri_dup( void *c1, void *c2 )
235 ldapinfo_t *li1 = (ldapinfo_t *)c1;
236 ldapinfo_t *li2 = (ldapinfo_t *)c2;
238 assert( li1->li_bvuri != NULL );
239 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
240 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
242 assert( li2->li_bvuri != NULL );
243 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
244 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
246 /* Cannot have more than one shared session with same DN */
247 if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
255 * Search specific response that strips entryDN from entries
258 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
260 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
262 assert( op->o_tag == LDAP_REQ_SEARCH );
264 /* if in error, don't proceed any further */
265 if ( lb->lb_status == LDAP_CH_ERR ) {
269 if ( rs->sr_type == REP_SEARCH ) {
270 Attribute **ap = &rs->sr_entry->e_attrs;
272 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
273 /* will be generated later by frontend
274 * (a cleaner solution would be that
275 * the frontend checks if it already exists */
276 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
283 /* there SHOULD be one only! */
288 /* tell the frontend not to add generated
289 * operational attributes */
290 rs->sr_flags |= REP_NO_OPERATIONALS;
292 return SLAP_CB_CONTINUE;
294 } else if ( rs->sr_type == REP_SEARCHREF ) {
295 /* if we get it here, it means the library was unable
296 * to chase the referral... */
297 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
298 rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
301 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
302 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
303 switch ( get_continuationBehavior( op ) ) {
304 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
305 lb->lb_status = LDAP_CH_ERR;
306 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
312 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
313 return SLAP_CB_CONTINUE;
315 } else if ( rs->sr_type == REP_RESULT ) {
316 if ( rs->sr_err == LDAP_REFERRAL
317 && lb->lb_depth < lb->lb_lc->lc_max_depth
318 && rs->sr_ref != NULL )
320 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
323 /* back-ldap tried to send result */
324 lb->lb_status = LDAP_CH_RES;
331 * Dummy response that simply traces if back-ldap tried to send
332 * anything to the client
335 ldap_chain_cb_response( Operation *op, SlapReply *rs )
337 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
339 /* if in error, don't proceed any further */
340 if ( lb->lb_status == LDAP_CH_ERR ) {
344 if ( rs->sr_type == REP_RESULT ) {
346 switch ( rs->sr_err ) {
347 case LDAP_COMPARE_TRUE:
348 case LDAP_COMPARE_FALSE:
349 if ( op->o_tag != LDAP_REQ_COMPARE ) {
355 lb->lb_status = LDAP_CH_RES;
359 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
360 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
364 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
365 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
366 switch ( get_continuationBehavior( op ) ) {
367 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
368 lb->lb_status = LDAP_CH_ERR;
369 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
375 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
382 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
384 /* strip the entryDN attribute, but keep returning results */
385 (void)ldap_chain_cb_search_response( op, rs );
388 return SLAP_CB_CONTINUE;
399 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
400 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
401 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
402 struct berval odn = op->o_req_dn,
403 ondn = op->o_req_ndn;
404 ldapinfo_t li = { 0 }, *lip = NULL;
405 struct berval bvuri[ 2 ] = { { 0 } };
407 /* NOTE: returned if ref is empty... */
411 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
412 LDAPControl **ctrls = NULL;
414 (void)chaining_control_add( lc, op, &ctrls );
415 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
419 for ( ; !BER_BVISNULL( ref ); ref++ ) {
420 SlapReply rs2 = { 0 };
421 LDAPURLDesc *srv = NULL;
422 req_search_s save_oq_search = op->oq_search,
423 tmp_oq_search = { 0 };
424 struct berval dn = BER_BVNULL,
431 /* We're setting the URI of the first referral;
432 * what if there are more?
438 If the client wishes to progress the operation, it MUST follow the
439 referral by contacting one of the supported services. If multiple
440 URIs are present, the client assumes that any supported URI may be
441 used to progress the operation.
443 * so we actually need to follow exactly one,
444 * and we can assume any is fine.
447 /* parse reference and use
448 * proto://[host][:port]/ only */
449 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
450 if ( rc != LDAP_URL_SUCCESS ) {
451 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
452 op->o_log_prefix, ref->bv_val, 0 );
459 if ( op->o_tag == LDAP_REQ_SEARCH ) {
460 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
461 /* RFC 4511: if scope is present, use it */
462 tmp_oq_search.rs_scope = srv->lud_scope;
465 /* RFC 4511: if scope is absent, use original */
466 tmp_oq_search.rs_scope = op->ors_scope;
471 srv->lud_scope = LDAP_SCOPE_DEFAULT;
472 dn.bv_val = srv->lud_dn;
473 filter = srv->lud_filter;
476 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
477 if ( srv->lud_dn == NULL ) {
482 ber_str2bv( srv->lud_dn, 0, 0, &dn );
483 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
484 if ( rc == LDAP_SUCCESS ) {
485 /* remove DN essentially because later on
486 * ldap_initialize() will parse the URL
487 * as a comma-separated URL list */
494 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
496 if ( srv->lud_filter != NULL
497 && srv->lud_filter[0] != '\0'
498 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
500 /* RFC 4511: if filter is present, use it;
501 * otherwise, use original */
502 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
503 if ( tmp_oq_search.rs_filter != NULL ) {
504 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
507 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
508 op->o_log_prefix, ref->bv_val, srv->lud_filter );
513 srv->lud_filter = NULL;
515 if ( rc == LDAP_SUCCESS ) {
516 li.li_uri = ldap_url_desc2str( srv );
519 srv->lud_dn = dn.bv_val;
520 srv->lud_filter = filter;
521 ldap_free_urldesc( srv );
523 if ( rc != LDAP_SUCCESS ) {
529 if ( li.li_uri == NULL ) {
530 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
531 op->o_log_prefix, ref->bv_val, 0 );
535 goto further_cleanup;
538 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
539 op->o_log_prefix, ref->bv_val, li.li_uri );
544 if ( op->o_tag == LDAP_REQ_SEARCH ) {
545 op->ors_scope = tmp_oq_search.rs_scope;
546 if ( tmp_oq_search.rs_filter != NULL ) {
547 op->ors_filter = tmp_oq_search.rs_filter;
548 op->ors_filterstr = tmp_oq_search.rs_filterstr;
552 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
554 /* Searches for a ldapinfo in the avl tree */
555 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
556 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
557 (caddr_t)&li, ldap_chain_uri_cmp );
558 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
561 op->o_bd->be_private = (void *)lip;
563 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
564 op->o_log_prefix, ref->bv_val, li.li_uri );
567 rc = ldap_chain_db_init_one( op->o_bd );
569 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
570 op->o_log_prefix, ref->bv_val, li.li_uri );
573 lip = (ldapinfo_t *)op->o_bd->be_private;
574 lip->li_uri = li.li_uri;
575 lip->li_bvuri = bvuri;
576 rc = ldap_chain_db_open_one( op->o_bd );
578 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
579 op->o_log_prefix, ref->bv_val, li.li_uri );
581 lip->li_bvuri = NULL;
582 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
586 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
587 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
588 if ( avl_insert( &lc->lc_lai.lai_tree,
589 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
591 /* someone just inserted another;
592 * don't bother, use this and then
596 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
602 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
603 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
607 lb->lb_depth = depth + 1;
609 rc = op_f( op, &rs2 );
611 /* note the first error */
612 if ( first_rc == -1 ) {
617 ldap_memfree( li.li_uri );
622 lip->li_bvuri = NULL;
623 (void)ldap_chain_db_close_one( op->o_bd );
624 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
629 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
630 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
633 if ( op->o_tag == LDAP_REQ_SEARCH ) {
634 if ( tmp_oq_search.rs_filter != NULL ) {
635 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
638 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
639 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
642 op->oq_search = save_oq_search;
645 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
654 op->o_req_ndn = ondn;
656 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
657 (void)chaining_control_remove( op, &ctrls );
658 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
660 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
675 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
676 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
677 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
678 ldapinfo_t li = { 0 }, *lip = NULL;
679 struct berval bvuri[ 2 ] = { { 0 } };
681 struct berval odn = op->o_req_dn,
682 ondn = op->o_req_ndn;
683 Entry *save_entry = rs->sr_entry;
684 slap_mask_t save_flags = rs->sr_flags;
689 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
690 LDAPControl **ctrls = NULL;
692 (void)chaining_control_add( lc, op, &ctrls );
693 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
695 assert( rs->sr_type == REP_SEARCHREF );
697 rs->sr_type = REP_SEARCH;
699 /* if we parse the URI then by no means
700 * we can cache stuff or reuse connections,
701 * because in back-ldap there's no caching
702 * based on the URI value, which is supposed
703 * to be set once for all (correct?) */
705 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
706 SlapReply rs2 = { REP_RESULT };
708 req_search_s save_oq_search = op->oq_search,
709 tmp_oq_search = { 0 };
717 /* parse reference and use
718 * proto://[host][:port]/ only */
719 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
720 if ( rc != LDAP_URL_SUCCESS ) {
721 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
722 op->o_log_prefix, ref->bv_val, 0 );
725 rs->sr_err = LDAP_OTHER;
729 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
730 /* RFC 4511: if scope is present, use it */
731 tmp_oq_search.rs_scope = srv->lud_scope;
734 /* RFC 4511: if scope is absent, use original */
735 /* Section 4.5.3: if scope is onelevel, use base */
736 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
737 tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
739 tmp_oq_search.rs_scope = op->ors_scope;
743 srv->lud_scope = LDAP_SCOPE_DEFAULT;
744 dn.bv_val = srv->lud_dn;
745 filter = srv->lud_filter;
748 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
749 if ( srv->lud_dn == NULL ) {
753 if ( save_entry != NULL ) {
754 /* use the "right" DN, if available */
755 pdn = save_entry->e_name;
756 ndn = save_entry->e_nname;
757 } /* else leave the original req DN in place, if any RFC 4511 */
760 /* RFC 4511: if DN is present, use it */
761 ber_str2bv( srv->lud_dn, 0, 0, &dn );
762 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
763 if ( rc == LDAP_SUCCESS ) {
764 /* remove DN essentially because later on
765 * ldap_initialize() will parse the URL
766 * as a comma-separated URL list */
773 if ( rc == LDAP_SUCCESS ) {
775 if ( srv->lud_filter != NULL
776 && srv->lud_filter[0] != '\0'
777 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
779 /* RFC 4511: if filter is present, use it;
780 * otherwise, use original */
781 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
782 if ( tmp_oq_search.rs_filter != NULL ) {
783 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
786 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
787 op->o_log_prefix, ref->bv_val, srv->lud_filter );
792 srv->lud_filter = NULL;
794 if ( rc == LDAP_SUCCESS ) {
795 li.li_uri = ldap_url_desc2str( srv );
798 srv->lud_dn = dn.bv_val;
799 srv->lud_filter = filter;
800 ldap_free_urldesc( srv );
802 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
803 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
804 op->o_log_prefix, ref->bv_val, 0 );
808 goto further_cleanup;
811 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
812 op->o_log_prefix, ref->bv_val, li.li_uri );
816 op->ors_scope = tmp_oq_search.rs_scope;
817 if ( tmp_oq_search.rs_filter != NULL ) {
818 op->ors_filter = tmp_oq_search.rs_filter;
819 op->ors_filterstr = tmp_oq_search.rs_filterstr;
822 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
824 /* Searches for a ldapinfo in the avl tree */
825 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
826 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
827 (caddr_t)&li, ldap_chain_uri_cmp );
828 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
831 op->o_bd->be_private = (void *)lip;
833 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
834 op->o_log_prefix, ref->bv_val, li.li_uri );
837 /* if none is found, create a temporary... */
838 rc = ldap_chain_db_init_one( op->o_bd );
840 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
841 op->o_log_prefix, ref->bv_val, li.li_uri );
844 lip = (ldapinfo_t *)op->o_bd->be_private;
845 lip->li_uri = li.li_uri;
846 lip->li_bvuri = bvuri;
847 rc = ldap_chain_db_open_one( op->o_bd );
849 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
850 op->o_log_prefix, ref->bv_val, li.li_uri );
852 lip->li_bvuri = NULL;
853 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
857 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
858 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
859 if ( avl_insert( &lc->lc_lai.lai_tree,
860 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
862 /* someone just inserted another;
863 * don't bother, use this and then
867 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
873 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
874 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
877 lb->lb_op_f = lback->bi_op_search;
878 lb->lb_depth = depth + 1;
880 /* FIXME: should we also copy filter and scope?
881 * according to RFC3296, no */
882 rc = lback->bi_op_search( op, &rs2 );
883 if ( first_rc == -1 ) {
888 ldap_memfree( li.li_uri );
893 lip->li_bvuri = NULL;
894 (void)ldap_chain_db_close_one( op->o_bd );
895 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
900 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
901 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
905 op->o_req_ndn = ondn;
907 if ( tmp_oq_search.rs_filter != NULL ) {
908 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
911 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
912 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
915 op->oq_search = save_oq_search;
917 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
925 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
926 (void)chaining_control_remove( op, &ctrls );
927 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
930 op->o_req_ndn = ondn;
931 rs->sr_type = REP_SEARCHREF;
932 rs->sr_entry = save_entry;
933 rs->sr_flags = save_flags;
935 if ( rc != LDAP_SUCCESS ) {
936 /* couldn't chase any of the referrals */
937 if ( first_rc != -1 ) {
941 rc = SLAP_CB_CONTINUE;
949 ldap_chain_response( Operation *op, SlapReply *rs )
951 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
952 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
953 BackendDB db, *bd = op->o_bd;
954 ldap_chain_cb_t lb = { 0 };
955 slap_callback *sc = op->o_callback,
958 const char *text = NULL;
961 struct berval ndn = op->o_ndn;
963 int sr_err = rs->sr_err;
964 slap_reply_t sr_type = rs->sr_type;
965 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
966 slap_mask_t chain_mask = 0;
967 ber_len_t chain_shift = 0;
968 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
970 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
971 return SLAP_CB_CONTINUE;
974 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
975 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
976 switch ( get_resolveBehavior( op ) ) {
977 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
978 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
979 return SLAP_CB_CONTINUE;
982 chain_mask = SLAP_CH_RESOLVE_MASK;
983 chain_shift = SLAP_CH_RESOLVE_SHIFT;
987 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
988 switch ( get_continuationBehavior( op ) ) {
989 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
990 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
991 return SLAP_CB_CONTINUE;
994 chain_mask = SLAP_CH_CONTINUATION_MASK;
995 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
999 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1002 * TODO: add checks on who/when chain operations; e.g.:
1003 * a) what identities are authorized
1004 * b) what request DN (e.g. only chain requests rooted at <DN>)
1005 * c) what referral URIs
1006 * d) what protocol scheme (e.g. only ldaps://)
1011 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1016 matched = rs->sr_matched;
1017 rs->sr_matched = NULL;
1021 /* we need this to know if back-ldap returned any result */
1023 sc2.sc_next = sc->sc_next;
1024 sc2.sc_private = &lb;
1025 sc2.sc_response = ldap_chain_cb_response;
1026 op->o_callback = &sc2;
1028 /* Chaining can be performed by a privileged user on behalf
1029 * of normal users, using the ProxyAuthz control, by exploiting
1030 * the identity assertion feature of back-ldap; see idassert-*
1031 * directives in slapd-ldap(5).
1033 * FIXME: the idassert-authcDN is one, will it be fine regardless
1034 * of the URI we obtain from the referral?
1037 switch ( op->o_tag ) {
1038 case LDAP_REQ_BIND: {
1039 struct berval rndn = op->o_req_ndn;
1040 Connection *conn = op->o_conn;
1042 /* FIXME: can we really get a referral for binds? */
1043 op->o_req_ndn = slap_empty_bv;
1045 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1046 op->o_req_ndn = rndn;
1052 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1055 case LDAP_REQ_DELETE:
1056 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1059 case LDAP_REQ_MODRDN:
1060 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1063 case LDAP_REQ_MODIFY:
1064 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1067 case LDAP_REQ_COMPARE:
1068 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
1069 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1074 case LDAP_REQ_SEARCH:
1075 if ( rs->sr_type == REP_SEARCHREF ) {
1076 sc2.sc_response = ldap_chain_cb_search_response;
1077 rc = ldap_chain_search( op, rs, ref, 0 );
1080 /* we might get here before any database actually
1081 * performed a search; in those cases, we need
1082 * to check limits, to make sure safe defaults
1084 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1085 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1088 rc = SLAP_CB_CONTINUE;
1093 case LDAP_REQ_EXTENDED:
1094 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
1095 /* FIXME: ldap_back_extended() by design
1096 * doesn't send result; frontend is expected
1098 /* FIXME: what about chaining? */
1099 if ( rc != SLAPD_ABANDON ) {
1101 send_ldap_extended( op, rs );
1104 lb.lb_status = LDAP_CH_RES;
1108 rc = SLAP_CB_CONTINUE;
1118 sr_err = rs->sr_err;
1119 /* slapd-ldap sent response */
1120 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1121 /* FIXME: should we send response? */
1122 Debug( LDAP_DEBUG_ANY,
1123 "%s: ldap_chain_response: "
1124 "overlay should have sent result.\n",
1125 op->o_log_prefix, 0, 0 );
1130 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1131 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1135 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1136 case LDAP_CHAINING_REQUIRED:
1138 op->o_callback = NULL;
1139 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1140 "operation cannot be completed without chaining" );
1144 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1145 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1146 sr_err = rs->sr_err = rc;
1147 rs->sr_type = sr_type;
1150 rc = SLAP_CB_CONTINUE;
1151 rs->sr_err = sr_err;
1152 rs->sr_type = sr_type;
1154 rs->sr_matched = matched;
1157 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1160 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1163 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1164 /* give the remaining callbacks a chance */
1165 op->o_callback = sc->sc_next;
1166 rc = rs->sr_err = slap_map_api2result( rs );
1167 send_ldap_result( op, rs );
1171 rs->sr_err = sr_err;
1172 rs->sr_type = sr_type;
1174 rs->sr_matched = matched;
1177 op->o_callback = sc;
1183 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1185 ldap_chain_parse_ctrl(
1188 LDAPControl *ctrl );
1191 str2chain( const char *s )
1193 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1194 return LDAP_CHAINING_PREFERRED;
1196 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1197 return LDAP_CHAINING_REQUIRED;
1199 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1200 return LDAP_REFERRALS_PREFERRED;
1202 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1203 return LDAP_REFERRALS_REQUIRED;
1208 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1223 static ConfigDriver chain_cf_gen;
1224 static ConfigCfAdd chain_cfadd;
1225 static ConfigLDAPadd chain_ldadd;
1227 static ConfigTable chaincfg[] = {
1228 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1229 { "chain-chaining", "args",
1230 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1231 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1232 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1233 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1234 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1235 { "chain-cache-uri", "TRUE/FALSE",
1236 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1237 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1238 "DESC 'Enables caching of URIs not present in configuration' "
1239 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1240 { "chain-max-depth", "args",
1241 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1242 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1243 "DESC 'max referral depth' "
1244 "SYNTAX OMsInteger "
1245 "EQUALITY integerMatch "
1246 "SINGLE-VALUE )", NULL, NULL },
1247 { "chain-return-error", "TRUE/FALSE",
1248 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1249 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1250 "DESC 'Errors are returned instead of the original referral' "
1251 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1252 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1255 static ConfigOCs chainocs[] = {
1256 { "( OLcfgOvOc:3.1 "
1257 "NAME 'olcChainConfig' "
1258 "DESC 'Chain configuration' "
1259 "SUP olcOverlayConfig "
1261 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1262 "olcChainingBehavior $ "
1263 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1264 "olcChainCacheURI $ "
1265 "olcChainMaxReferralDepth $ "
1266 "olcChainReturnError "
1268 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1269 { "( OLcfgOvOc:3.2 "
1270 "NAME 'olcChainDatabase' "
1271 "DESC 'Chain remote server configuration' "
1272 "SUP olcLDAPConfig )",
1273 Cft_Misc, olcDatabaseDummy, chain_ldadd },
1278 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1285 AttributeDescription *ad = NULL;
1291 if ( p->ce_type != Cft_Overlay
1293 || p->ce_bi->bi_cf_ocs != chainocs )
1295 return LDAP_CONSTRAINT_VIOLATION;
1298 on = (slap_overinst *)p->ce_bi;
1299 lc = (ldap_chain_t *)on->on_bi.bi_private;
1301 assert( ca->be == NULL );
1302 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1304 ca->be->bd_info = (BackendInfo *)on;
1306 rc = slap_str2ad( "olcDbURI", &ad, &text );
1307 assert( rc == LDAP_SUCCESS );
1309 at = attr_find( e->e_attrs, ad );
1311 if ( lc->lc_common_li == NULL && at != NULL ) {
1312 /* FIXME: we should generate an empty default entry
1313 * if none is supplied */
1314 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1315 "first underlying database \"%s\" "
1316 "cannot contain attribute \"%s\".\n",
1317 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1318 rc = LDAP_CONSTRAINT_VIOLATION;
1323 if ( lc->lc_common_li != NULL && at == NULL ) {
1324 /* FIXME: we should generate an empty default entry
1325 * if none is supplied */
1326 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1327 "subsequent underlying database \"%s\" "
1328 "must contain attribute \"%s\".\n",
1329 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1330 rc = LDAP_CONSTRAINT_VIOLATION;
1334 if ( lc->lc_common_li == NULL ) {
1335 rc = ldap_chain_db_init_common( ca->be );
1338 rc = ldap_chain_db_init_one( ca->be );
1342 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1343 "unable to init %sunderlying database \"%s\".\n",
1344 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1345 return LDAP_CONSTRAINT_VIOLATION;
1348 li = ca->be->be_private;
1350 if ( lc->lc_common_li == NULL ) {
1351 lc->lc_common_li = li;
1354 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1355 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1356 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1357 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1359 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1360 "database \"%s\" insert failed.\n",
1361 e->e_name.bv_val, 0, 0 );
1362 rc = LDAP_CONSTRAINT_VIOLATION;
1367 ca->ca_private = on;
1370 if ( rc != LDAP_SUCCESS ) {
1371 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1379 typedef struct ldap_chain_cfadd_apply_t {
1385 } ldap_chain_cfadd_apply_t;
1388 ldap_chain_cfadd_apply( void *datum, void *arg )
1390 ldapinfo_t *li = (ldapinfo_t *)datum;
1391 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1395 /* FIXME: should not hardcode "olcDatabase" here */
1396 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1397 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1398 bv.bv_val = lca->ca->cr_msg;
1400 lca->ca->be->be_private = (void *)li;
1401 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1402 &bv, lback->bi_cf_ocs, &chainocs[1] );
1410 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1412 CfEntryInfo *pe = p->e_private;
1413 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1414 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1415 void *priv = (void *)ca->be->be_private;
1417 if ( lback->bi_cf_ocs ) {
1418 ldap_chain_cfadd_apply_t lca = { 0 };
1426 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1428 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1429 &lca, 1, AVL_INORDER );
1431 ca->be->be_private = priv;
1437 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1438 static slap_verbmasks chaining_mode[] = {
1439 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1440 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1441 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1442 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1445 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1448 chain_cf_gen( ConfigArgs *c )
1450 slap_overinst *on = (slap_overinst *)c->bi;
1451 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1455 if ( c->op == SLAP_CONFIG_EMIT ) {
1457 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1459 struct berval resolve = BER_BVNULL,
1460 continuation = BER_BVNULL;
1462 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1466 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1467 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1469 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1471 + STRLENOF( "continuation=" ) + continuation.bv_len;
1472 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1473 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1474 "resolve=%s continuation=%s",
1475 resolve.bv_val, continuation.bv_val );
1477 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1478 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1479 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1480 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1481 " critical", STRLENOF( " critical" ) + 1 );
1482 c->value_bv.bv_len += STRLENOF( " critical" );
1487 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1490 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1494 c->value_int = lc->lc_max_depth;
1498 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1507 } else if ( c->op == LDAP_MOD_DELETE ) {
1513 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1521 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1532 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1533 char **argv = c->argv;
1535 BerElementBuffer berbuf;
1536 BerElement *ber = (BerElement *)&berbuf;
1540 Operation op = { 0 };
1541 SlapReply rs = { 0 };
1543 lc->lc_chaining_ctrlflag = 0;
1545 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1546 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1547 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1548 if ( resolve == -1 ) {
1549 Debug( LDAP_DEBUG_ANY, "%s: "
1550 "illegal <resolve> value %s "
1551 "in \"chain-chaining>\".\n",
1552 c->log, argv[ 0 ], 0 );
1556 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1557 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1558 if ( continuation == -1 ) {
1559 Debug( LDAP_DEBUG_ANY, "%s: "
1560 "illegal <continuation> value %s "
1561 "in \"chain-chaining\".\n",
1562 c->log, argv[ 0 ], 0 );
1566 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1570 Debug( LDAP_DEBUG_ANY, "%s: "
1571 "unknown option in \"chain-chaining\".\n",
1577 if ( resolve != -1 || continuation != -1 ) {
1580 if ( resolve == -1 ) {
1582 resolve = SLAP_CHAINING_DEFAULT;
1585 ber_init2( ber, NULL, LBER_USE_DER );
1587 err = ber_printf( ber, "{e" /* } */, resolve );
1590 Debug( LDAP_DEBUG_ANY, "%s: "
1591 "chaining behavior control encoding error!\n",
1596 if ( continuation > -1 ) {
1597 err = ber_printf( ber, "e", continuation );
1600 Debug( LDAP_DEBUG_ANY, "%s: "
1601 "chaining behavior control encoding error!\n",
1607 err = ber_printf( ber, /* { */ "N}" );
1610 Debug( LDAP_DEBUG_ANY, "%s: "
1611 "chaining behavior control encoding error!\n",
1616 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1617 exit( EXIT_FAILURE );
1621 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1624 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1625 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1627 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1629 Debug( LDAP_DEBUG_ANY, "%s: "
1630 "unable to parse chaining control%s%s.\n",
1631 c->log, rs.sr_text ? ": " : "",
1632 rs.sr_text ? rs.sr_text : "" );
1636 lc->lc_chaining_ctrlflag = op.o_chaining;
1638 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1641 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1642 Debug( LDAP_DEBUG_ANY, "%s: "
1643 "\"chaining\" control unsupported (ignored).\n",
1645 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1649 if ( c->value_int ) {
1650 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1652 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1657 if ( c->value_int < 0 ) {
1658 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1659 "<%s> invalid max referral depth %d",
1660 c->argv[0], c->value_int );
1661 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1662 c->log, c->cr_msg, 0 );
1666 lc->lc_max_depth = c->value_int;
1669 if ( c->value_int ) {
1670 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1672 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1688 slap_overinst *on = (slap_overinst *)be->bd_info;
1689 ldap_chain_t *lc = NULL;
1691 if ( lback == NULL ) {
1692 lback = backend_info( "ldap" );
1694 if ( lback == NULL ) {
1699 lc = ch_malloc( sizeof( ldap_chain_t ) );
1703 memset( lc, 0, sizeof( ldap_chain_t ) );
1704 lc->lc_max_depth = 1;
1705 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1707 on->on_bi.bi_private = (void *)lc;
1713 ldap_chain_db_config(
1720 slap_overinst *on = (slap_overinst *)be->bd_info;
1721 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1723 int rc = SLAP_CONF_UNKNOWN;
1725 if ( lc->lc_common_li == NULL ) {
1727 ldap_chain_db_init_common( &db );
1728 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1731 /* Something for the chain database? */
1732 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1733 char *save_argv0 = argv[ 0 ];
1735 static char *allowed_argv[] = {
1736 /* special: put URI here, so in the meanwhile
1737 * it detects whether a new URI is being provided */
1743 /* FIXME: maybe rebind-as-user should be allowed
1744 * only within known URIs... */
1751 int which_argv = -1;
1753 argv[ 0 ] += STRLENOF( "chain-" );
1755 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1756 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1761 if ( allowed_argv[ which_argv ] == NULL ) {
1764 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1765 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1766 "\"%s\" only allowed within a URI directive.\n.",
1767 fname, lineno, argv[ 0 ] );
1772 if ( which_argv == 0 ) {
1773 rc = ldap_chain_db_init_one( &db );
1775 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1776 "underlying slapd-ldap initialization failed.\n.",
1780 lc->lc_cfg_li = db.be_private;
1783 /* TODO: add checks on what other slapd-ldap(5) args
1784 * should be put in the template; this is not quite
1785 * harmful, because attributes that shouldn't don't
1786 * get actually used, but the user should at least
1791 db.be_private = (void *)lc->lc_cfg_li;
1792 db.be_cf_ocs = lback->bi_cf_ocs;
1794 rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1796 argv[ 0 ] = save_argv0;
1798 if ( which_argv == 0 ) {
1802 db.be_private = (void *)lc->lc_cfg_li;
1803 ldap_chain_db_destroy_one( &db, NULL );
1804 lc->lc_cfg_li = NULL;
1806 if ( lc->lc_cfg_li->li_bvuri == NULL
1807 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1808 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1810 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1811 "no URI list allowed in slapo-chain.\n",
1814 goto private_destroy;
1817 if ( avl_insert( &lc->lc_lai.lai_tree,
1818 (caddr_t)lc->lc_cfg_li,
1819 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1821 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1822 "duplicate URI in slapo-chain.\n",
1825 goto private_destroy;
1842 typedef struct ldap_chain_db_apply_t {
1845 } ldap_chain_db_apply_t;
1848 ldap_chain_db_apply( void *datum, void *arg )
1850 ldapinfo_t *li = (ldapinfo_t *)datum;
1851 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1853 lca->be->be_private = (void *)li;
1855 return lca->func( lca->be, NULL );
1864 slap_overinst *on = (slap_overinst *)be->bd_info;
1865 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1870 BI_db_func *func = (&lback->bi_db_open)[ which ];
1872 if ( func != NULL && lc->lc_common_li != NULL ) {
1876 db.be_private = lc->lc_common_li;
1878 rc = func( &db, NULL );
1884 if ( lc->lc_lai.lai_tree != NULL ) {
1885 ldap_chain_db_apply_t lca;
1890 rc = avl_apply( lc->lc_lai.lai_tree,
1891 ldap_chain_db_apply, (void *)&lca,
1892 1, AVL_INORDER ) != AVL_NOMORE;
1905 slap_overinst *on = (slap_overinst *) be->bd_info;
1906 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1907 slap_mask_t monitoring;
1910 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1911 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1915 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1917 if ( lc->lc_common_li == NULL ) {
1918 void *be_private = be->be_private;
1919 ldap_chain_db_init_common( be );
1920 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1921 be->be_private = be_private;
1924 /* filter out and restore monitoring */
1925 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1926 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1927 rc = ldap_chain_db_func( be, db_open );
1928 SLAP_DBFLAGS( be ) |= monitoring;
1934 ldap_chain_db_close(
1938 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1939 #ifdef SLAP_CONFIG_DELETE
1940 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1941 #endif /* SLAP_CONFIG_DELETE */
1942 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1943 return ldap_chain_db_func( be, db_close );
1947 ldap_chain_db_destroy(
1951 slap_overinst *on = (slap_overinst *) be->bd_info;
1952 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1956 rc = ldap_chain_db_func( be, db_destroy );
1959 avl_free( lc->lc_lai.lai_tree, NULL );
1960 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1968 * inits one instance of the slapd-ldap backend, and stores
1969 * the private info in be_private of the arg
1972 ldap_chain_db_init_common(
1975 BackendInfo *bi = be->bd_info;
1979 be->bd_info = lback;
1980 be->be_private = NULL;
1981 rc = lback->bi_db_init( be, NULL );
1985 li = (ldapinfo_t *)be->be_private;
1986 li->li_urllist_f = NULL;
1987 li->li_urllist_p = NULL;
1995 * inits one instance of the slapd-ldap backend, stores
1996 * the private info in be_private of the arg and fills
1997 * selected fields with data from the template.
1999 * NOTE: add checks about the other fields of the template,
2000 * which are ignored and SHOULD NOT be configured by the user.
2003 ldap_chain_db_init_one(
2006 slap_overinst *on = (slap_overinst *)be->bd_info;
2007 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2009 BackendInfo *bi = be->bd_info;
2014 be->bd_info = lback;
2015 be->be_private = NULL;
2016 t = lback->bi_db_init( be, NULL );
2020 li = (ldapinfo_t *)be->be_private;
2021 li->li_urllist_f = NULL;
2022 li->li_urllist_p = NULL;
2024 /* copy common data */
2025 li->li_nretries = lc->lc_common_li->li_nretries;
2026 li->li_flags = lc->lc_common_li->li_flags;
2027 li->li_version = lc->lc_common_li->li_version;
2028 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2029 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2037 ldap_chain_db_open_one(
2040 if ( SLAP_DBMONITORING( be ) ) {
2041 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
2043 if ( li->li_uri == NULL ) {
2044 ber_str2bv( "cn=Common Connections", 0, 1,
2045 &li->li_monitor_info.lmi_rdn );
2050 li->li_monitor_info.lmi_rdn.bv_len
2051 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2052 ptr = li->li_monitor_info.lmi_rdn.bv_val
2053 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
2054 ptr = lutil_strcopy( ptr, "cn=" );
2055 ptr = lutil_strcopy( ptr, li->li_uri );
2060 return lback->bi_db_open( be, NULL );
2063 typedef struct ldap_chain_conn_apply_t {
2066 } ldap_chain_conn_apply_t;
2069 ldap_chain_conn_apply( void *datum, void *arg )
2071 ldapinfo_t *li = (ldapinfo_t *)datum;
2072 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2074 lca->be->be_private = (void *)li;
2076 return lback->bi_connection_destroy( lca->be, lca->conn );
2080 ldap_chain_connection_destroy(
2085 slap_overinst *on = (slap_overinst *) be->bd_info;
2086 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2087 void *private = be->be_private;
2088 ldap_chain_conn_apply_t lca;
2091 be->be_private = NULL;
2094 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2095 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2096 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2097 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2098 be->be_private = private;
2103 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2105 ldap_chain_parse_ctrl(
2115 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2116 rs->sr_text = "Chaining behavior control specified multiple times";
2117 return LDAP_PROTOCOL_ERROR;
2120 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2121 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2122 return LDAP_PROTOCOL_ERROR;
2125 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2126 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2131 /* Parse the control value
2132 * ChainingBehavior ::= SEQUENCE {
2133 * resolveBehavior Behavior OPTIONAL,
2134 * continuationBehavior Behavior OPTIONAL }
2136 * Behavior :: = ENUMERATED {
2137 * chainingPreferred (0),
2138 * chainingRequired (1),
2139 * referralsPreferred (2),
2140 * referralsRequired (3) }
2143 ber = ber_init( &ctrl->ldctl_value );
2145 rs->sr_text = "internal error";
2149 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2150 /* FIXME: since the whole SEQUENCE is optional,
2151 * should we accept no enumerations at all? */
2152 if ( tag != LBER_ENUMERATED ) {
2153 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2154 return LDAP_PROTOCOL_ERROR;
2157 switch ( behavior ) {
2158 case LDAP_CHAINING_PREFERRED:
2159 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2162 case LDAP_CHAINING_REQUIRED:
2163 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2166 case LDAP_REFERRALS_PREFERRED:
2167 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2170 case LDAP_REFERRALS_REQUIRED:
2171 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2175 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2176 return LDAP_PROTOCOL_ERROR;
2179 tag = ber_peek_tag( ber, &len );
2180 if ( tag == LBER_ENUMERATED ) {
2181 tag = ber_scanf( ber, "e", &behavior );
2182 if ( tag == LBER_ERROR ) {
2183 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2184 return LDAP_PROTOCOL_ERROR;
2188 if ( tag == LBER_DEFAULT ) {
2189 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2192 switch ( behavior ) {
2193 case LDAP_CHAINING_PREFERRED:
2194 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2197 case LDAP_CHAINING_REQUIRED:
2198 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2201 case LDAP_REFERRALS_PREFERRED:
2202 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2205 case LDAP_REFERRALS_REQUIRED:
2206 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2210 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2211 return LDAP_PROTOCOL_ERROR;
2215 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2216 rs->sr_text = "Chaining behavior control: decoding error";
2217 return LDAP_PROTOCOL_ERROR;
2220 (void) ber_free( ber, 1 );
2223 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2224 ? SLAP_CONTROL_CRITICAL
2225 : SLAP_CONTROL_NONCRITICAL );
2227 return LDAP_SUCCESS;
2229 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2232 chain_initialize( void )
2236 /* Make sure we don't exceed the bits reserved for userland */
2237 config_check_userland( CH_LAST );
2239 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2240 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2241 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2242 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2243 if ( rc != LDAP_SUCCESS ) {
2244 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2245 "unable to register chaining behavior control: %d.\n",
2249 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2251 ldapchain.on_bi.bi_type = "chain";
2252 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2253 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2254 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2255 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2256 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2258 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2260 ldapchain.on_response = ldap_chain_response;
2262 ldapchain.on_bi.bi_cf_ocs = chainocs;
2264 rc = config_register_schema( chaincfg, chainocs );
2269 return overlay_register( &ldapchain );