1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2010 The OpenLDAP Foundation.
6 * Portions Copyright 2003 Howard Chu.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
18 * This work was initially developed by the Howard Chu for inclusion
19 * in OpenLDAP Software.
20 * This work was subsequently modified by Pierangelo Masarati.
27 #include <ac/string.h>
28 #include <ac/socket.h>
32 #include "back-ldap.h"
35 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
36 #define SLAP_CHAINING_DEFAULT LDAP_CHAINING_PREFERRED
37 #define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT
38 #define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT)
39 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
40 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
41 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
42 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
43 #define SLAP_CH_RESOLVE_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
44 #define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2)
45 #define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT)
46 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
47 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
48 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
49 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
50 #define SLAP_CH_CONTINUATION_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
52 #define o_chaining o_ctrlflag[sc_chainingBehavior]
53 #define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK)
54 #define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
55 #define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
56 #define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
58 static int sc_chainingBehavior;
59 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
65 } ldap_chain_status_t;
67 static BackendInfo *lback;
69 typedef struct ldap_chain_t {
71 * A "template" ldapinfo_t gets all common configuration items;
72 * then, for each configured URI, an entry is created in the tree;
73 * all the specific configuration items get in the current URI
76 * Then, for each referral, extract the URI and lookup the
77 * related structure. If configured to do so, allow URIs
78 * not found in the structure to create a temporary one
79 * that chains anonymously; maybe it can also be added to
80 * the tree? Should be all configurable.
83 /* "common" configuration info (anything occurring before an "uri") */
84 ldapinfo_t *lc_common_li;
86 /* current configuration info */
87 ldapinfo_t *lc_cfg_li;
89 /* tree of configured[/generated?] "uri" info */
90 ldap_avl_info_t lc_lai;
92 /* max depth in nested referrals chaining */
96 #define LDAP_CHAIN_F_NONE (0x00U)
97 #define LDAP_CHAIN_F_CHAINING (0x01U)
98 #define LDAP_CHAIN_F_CACHE_URI (0x02U)
99 #define LDAP_CHAIN_F_RETURN_ERR (0x04U)
101 #define LDAP_CHAIN_ISSET(lc, f) ( ( (lc)->lc_flags & (f) ) == (f) )
102 #define LDAP_CHAIN_CHAINING( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
103 #define LDAP_CHAIN_CACHE_URI( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
104 #define LDAP_CHAIN_RETURN_ERR( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
106 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
107 LDAPControl lc_chaining_ctrl;
108 char lc_chaining_ctrlflag;
109 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
112 static int ldap_chain_db_init_common( BackendDB *be );
113 static int ldap_chain_db_init_one( BackendDB *be );
114 static int ldap_chain_db_open_one( BackendDB *be );
115 #define ldap_chain_db_close_one(be) (0)
116 #define ldap_chain_db_destroy_one(be, rs) (lback)->bi_db_destroy( (be), (rs) )
118 typedef struct ldap_chain_cb_t {
119 ldap_chain_status_t lb_status;
140 static slap_overinst ldapchain;
142 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
144 chaining_control_add(
147 LDAPControl ***oldctrlsp )
149 LDAPControl **ctrls = NULL;
152 *oldctrlsp = op->o_ctrls;
154 /* default chaining control not defined */
155 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
159 /* already present */
160 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
164 /* FIXME: check other incompatibilities */
166 /* add to other controls */
168 for ( c = 0; op->o_ctrls[ c ]; c++ )
172 ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
173 ctrls[ 0 ] = &lc->lc_chaining_ctrl;
175 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
176 ctrls[ c + 1 ] = op->o_ctrls[ c ];
179 ctrls[ c + 1 ] = NULL;
183 op->o_chaining = lc->lc_chaining_ctrlflag;
189 chaining_control_remove(
191 LDAPControl ***oldctrlsp )
193 LDAPControl **oldctrls = *oldctrlsp;
195 /* we assume that the first control is the chaining control
196 * added by the chain overlay, so it's the only one we explicitly
198 if ( op->o_ctrls != oldctrls ) {
199 assert( op->o_ctrls != NULL );
200 assert( op->o_ctrls[ 0 ] != NULL );
205 op->o_ctrls = oldctrls;
212 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
215 ldap_chain_uri_cmp( const void *c1, const void *c2 )
217 const ldapinfo_t *li1 = (const ldapinfo_t *)c1;
218 const ldapinfo_t *li2 = (const ldapinfo_t *)c2;
220 assert( li1->li_bvuri != NULL );
221 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
222 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
224 assert( li2->li_bvuri != NULL );
225 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
226 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
228 /* If local DNs don't match, it is definitely not a match */
229 return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
233 ldap_chain_uri_dup( void *c1, void *c2 )
235 ldapinfo_t *li1 = (ldapinfo_t *)c1;
236 ldapinfo_t *li2 = (ldapinfo_t *)c2;
238 assert( li1->li_bvuri != NULL );
239 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
240 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
242 assert( li2->li_bvuri != NULL );
243 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
244 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
246 /* Cannot have more than one shared session with same DN */
247 if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
255 * Search specific response that strips entryDN from entries
258 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
260 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
262 assert( op->o_tag == LDAP_REQ_SEARCH );
264 /* if in error, don't proceed any further */
265 if ( lb->lb_status == LDAP_CH_ERR ) {
269 if ( rs->sr_type == REP_SEARCH ) {
270 Attribute **ap = &rs->sr_entry->e_attrs;
272 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
273 /* will be generated later by frontend
274 * (a cleaner solution would be that
275 * the frontend checks if it already exists */
276 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
283 /* there SHOULD be one only! */
288 /* tell the frontend not to add generated
289 * operational attributes */
290 rs->sr_flags |= REP_NO_OPERATIONALS;
292 return SLAP_CB_CONTINUE;
294 } else if ( rs->sr_type == REP_SEARCHREF ) {
295 /* if we get it here, it means the library was unable
296 * to chase the referral... */
297 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
298 rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
301 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
302 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
303 switch ( get_continuationBehavior( op ) ) {
304 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
305 lb->lb_status = LDAP_CH_ERR;
306 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
312 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
313 return SLAP_CB_CONTINUE;
315 } else if ( rs->sr_type == REP_RESULT ) {
316 if ( rs->sr_err == LDAP_REFERRAL
317 && lb->lb_depth < lb->lb_lc->lc_max_depth
318 && rs->sr_ref != NULL )
320 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
323 /* back-ldap tried to send result */
324 lb->lb_status = LDAP_CH_RES;
331 * Dummy response that simply traces if back-ldap tried to send
332 * anything to the client
335 ldap_chain_cb_response( Operation *op, SlapReply *rs )
337 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
339 /* if in error, don't proceed any further */
340 if ( lb->lb_status == LDAP_CH_ERR ) {
344 if ( rs->sr_type == REP_RESULT ) {
346 switch ( rs->sr_err ) {
347 case LDAP_COMPARE_TRUE:
348 case LDAP_COMPARE_FALSE:
349 if ( op->o_tag != LDAP_REQ_COMPARE ) {
355 lb->lb_status = LDAP_CH_RES;
359 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
360 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
364 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
365 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
366 switch ( get_continuationBehavior( op ) ) {
367 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
368 lb->lb_status = LDAP_CH_ERR;
369 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
375 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
382 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
384 /* strip the entryDN attribute, but keep returning results */
385 (void)ldap_chain_cb_search_response( op, rs );
388 return SLAP_CB_CONTINUE;
399 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
400 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
401 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
402 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 save_req_dn = op->o_req_dn,
423 save_req_ndn = op->o_req_ndn,
431 /* We're setting the URI of the first referral;
432 * what if there are more?
438 If the client wishes to progress the operation, it MUST follow the
439 referral by contacting one of the supported services. If multiple
440 URIs are present, the client assumes that any supported URI may be
441 used to progress the operation.
443 * so we actually need to follow exactly one,
444 * and we can assume any is fine.
447 /* parse reference and use
448 * proto://[host][:port]/ only */
449 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
450 if ( rc != LDAP_URL_SUCCESS ) {
456 if ( op->o_tag == LDAP_REQ_SEARCH ) {
457 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
458 /* RFC 4511: if scope is present, use it */
459 tmp_oq_search.rs_scope = srv->lud_scope;
462 /* RFC 4511: if scope is absent, use original */
463 tmp_oq_search.rs_scope = op->ors_scope;
468 srv->lud_scope = LDAP_SCOPE_DEFAULT;
469 dn.bv_val = srv->lud_dn;
470 filter = srv->lud_filter;
473 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
474 if ( srv->lud_dn == NULL ) {
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 );
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 ) {
530 goto further_cleanup;
536 if ( op->o_tag == LDAP_REQ_SEARCH ) {
537 op->ors_scope = tmp_oq_search.rs_scope;
538 if ( tmp_oq_search.rs_filter != NULL ) {
539 op->ors_filter = tmp_oq_search.rs_filter;
540 op->ors_filterstr = tmp_oq_search.rs_filterstr;
544 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
546 /* Searches for a ldapinfo in the avl tree */
547 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
548 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
549 (caddr_t)&li, ldap_chain_uri_cmp );
550 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
553 op->o_bd->be_private = (void *)lip;
556 rc = ldap_chain_db_init_one( op->o_bd );
560 lip = (ldapinfo_t *)op->o_bd->be_private;
561 lip->li_uri = li.li_uri;
562 lip->li_bvuri = bvuri;
563 rc = ldap_chain_db_open_one( op->o_bd );
566 lip->li_bvuri = NULL;
567 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
571 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
572 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
573 if ( avl_insert( &lc->lc_lai.lai_tree,
574 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
576 /* someone just inserted another;
577 * don't bother, use this and then
581 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
589 lb->lb_depth = depth + 1;
591 rc = op_f( op, &rs2 );
593 /* note the first error */
594 if ( first_rc == -1 ) {
599 ldap_memfree( li.li_uri );
604 lip->li_bvuri = NULL;
605 (void)ldap_chain_db_close_one( op->o_bd );
606 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
611 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
612 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
614 op->o_req_dn = save_req_dn;
615 op->o_req_ndn = save_req_ndn;
617 if ( op->o_tag == LDAP_REQ_SEARCH ) {
618 if ( tmp_oq_search.rs_filter != NULL ) {
619 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
622 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
623 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
626 op->oq_search = save_oq_search;
629 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
637 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
638 (void)chaining_control_remove( op, &ctrls );
639 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
641 if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
656 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
657 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
658 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
659 ldapinfo_t li = { 0 }, *lip = NULL;
660 struct berval bvuri[ 2 ] = { { 0 } };
662 struct berval odn = op->o_req_dn,
663 ondn = op->o_req_ndn;
664 slap_response *save_response = op->o_callback->sc_response;
665 Entry *save_entry = rs->sr_entry;
666 slap_mask_t save_flags = rs->sr_flags;
671 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
672 LDAPControl **ctrls = NULL;
674 (void)chaining_control_add( lc, op, &ctrls );
675 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
677 assert( rs->sr_type == REP_SEARCHREF );
679 rs->sr_type = REP_SEARCH;
681 op->o_callback->sc_response = ldap_chain_cb_search_response;
683 /* if we parse the URI then by no means
684 * we can cache stuff or reuse connections,
685 * because in back-ldap there's no caching
686 * based on the URI value, which is supposed
687 * to be set once for all (correct?) */
689 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
690 SlapReply rs2 = { 0 };
692 req_search_s save_oq_search = op->oq_search,
693 tmp_oq_search = { 0 };
694 struct berval save_req_dn = op->o_req_dn,
695 save_req_ndn = op->o_req_ndn,
703 /* parse reference and use
704 * proto://[host][:port]/ only */
705 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
706 if ( rc != LDAP_URL_SUCCESS ) {
708 rs->sr_err = LDAP_OTHER;
712 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
713 /* RFC 4511: if scope is present, use it */
714 tmp_oq_search.rs_scope = srv->lud_scope;
717 /* RFC 4511: if scope is absent, use original */
718 tmp_oq_search.rs_scope = op->ors_scope;
722 srv->lud_scope = LDAP_SCOPE_DEFAULT;
723 dn.bv_val = srv->lud_dn;
724 filter = srv->lud_filter;
727 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
728 if ( srv->lud_dn == NULL ) {
732 /* RFC 4511: if DN is absent, use original */
733 if ( save_entry == NULL ) {
738 /* use the "right" DN, if available */
739 pdn = save_entry->e_name;
740 ndn = save_entry->e_nname;
744 /* RFC 4511: if DN is present, use it */
745 ber_str2bv( srv->lud_dn, 0, 0, &dn );
746 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
747 if ( rc == LDAP_SUCCESS ) {
748 /* remove DN essentially because later on
749 * ldap_initialize() will parse the URL
750 * as a comma-separated URL list */
756 if ( rc == LDAP_SUCCESS ) {
758 if ( srv->lud_filter != NULL
759 && srv->lud_filter[0] != '\0'
760 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
762 /* RFC 4511: if filter is present, use it;
763 * otherwise, use original */
764 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
765 if ( tmp_oq_search.rs_filter != NULL ) {
766 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
773 srv->lud_filter = NULL;
775 if ( rc == LDAP_SUCCESS ) {
776 li.li_uri = ldap_url_desc2str( srv );
779 srv->lud_dn = dn.bv_val;
780 srv->lud_filter = filter;
781 ldap_free_urldesc( srv );
783 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
786 goto further_cleanup;
791 op->ors_scope = tmp_oq_search.rs_scope;
792 if ( tmp_oq_search.rs_filter != NULL ) {
793 op->ors_filter = tmp_oq_search.rs_filter;
794 op->ors_filterstr = tmp_oq_search.rs_filterstr;
797 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
799 /* Searches for a ldapinfo in the avl tree */
800 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
801 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
802 (caddr_t)&li, ldap_chain_uri_cmp );
803 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
806 op->o_bd->be_private = (void *)lip;
809 /* if none is found, create a temporary... */
810 rc = ldap_chain_db_init_one( op->o_bd );
814 lip = (ldapinfo_t *)op->o_bd->be_private;
815 lip->li_uri = li.li_uri;
816 lip->li_bvuri = bvuri;
817 rc = ldap_chain_db_open_one( op->o_bd );
820 lip->li_bvuri = NULL;
821 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
825 if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
826 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
827 if ( avl_insert( &lc->lc_lai.lai_tree,
828 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
830 /* someone just inserted another;
831 * don't bother, use this and then
835 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
842 lb->lb_op_f = lback->bi_op_search;
843 lb->lb_depth = depth + 1;
845 /* FIXME: should we also copy filter and scope?
846 * according to RFC3296, no */
847 rc = lback->bi_op_search( op, &rs2 );
848 if ( first_rc == -1 ) {
853 ldap_memfree( li.li_uri );
858 lip->li_bvuri = NULL;
859 (void)ldap_chain_db_close_one( op->o_bd );
860 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
865 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
866 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
869 op->o_req_dn = save_req_dn;
870 op->o_req_ndn = save_req_ndn;
872 if ( tmp_oq_search.rs_filter != NULL ) {
873 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
876 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
877 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
880 op->oq_search = save_oq_search;
882 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
890 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
891 (void)chaining_control_remove( op, &ctrls );
892 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
895 op->o_req_ndn = ondn;
896 op->o_callback->sc_response = save_response;
897 rs->sr_type = REP_SEARCHREF;
898 rs->sr_entry = save_entry;
899 rs->sr_flags = save_flags;
901 if ( rc != LDAP_SUCCESS ) {
902 /* couldn't chase any of the referrals */
903 if ( first_rc != -1 ) {
907 rc = SLAP_CB_CONTINUE;
915 ldap_chain_response( Operation *op, SlapReply *rs )
917 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
918 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
919 BackendDB db, *bd = op->o_bd;
920 ldap_chain_cb_t lb = { 0 };
921 slap_callback *sc = op->o_callback,
924 const char *text = NULL;
927 struct berval ndn = op->o_ndn;
929 int sr_err = rs->sr_err;
930 slap_reply_t sr_type = rs->sr_type;
931 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
932 slap_mask_t chain_mask = 0;
933 ber_len_t chain_shift = 0;
934 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
936 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
937 return SLAP_CB_CONTINUE;
940 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
941 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
942 switch ( get_resolveBehavior( op ) ) {
943 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
944 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
945 return SLAP_CB_CONTINUE;
948 chain_mask = SLAP_CH_RESOLVE_MASK;
949 chain_shift = SLAP_CH_RESOLVE_SHIFT;
953 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
954 switch ( get_continuationBehavior( op ) ) {
955 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
956 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
957 return SLAP_CB_CONTINUE;
960 chain_mask = SLAP_CH_CONTINUATION_MASK;
961 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
965 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
968 * TODO: add checks on who/when chain operations; e.g.:
969 * a) what identities are authorized
970 * b) what request DN (e.g. only chain requests rooted at <DN>)
971 * c) what referral URIs
972 * d) what protocol scheme (e.g. only ldaps://)
977 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
982 matched = rs->sr_matched;
983 rs->sr_matched = NULL;
987 /* we need this to know if back-ldap returned any result */
989 sc2.sc_next = sc->sc_next;
990 sc2.sc_private = &lb;
991 sc2.sc_response = ldap_chain_cb_response;
992 op->o_callback = &sc2;
994 /* Chaining can be performed by a privileged user on behalf
995 * of normal users, using the ProxyAuthz control, by exploiting
996 * the identity assertion feature of back-ldap; see idassert-*
997 * directives in slapd-ldap(5).
999 * FIXME: the idassert-authcDN is one, will it be fine regardless
1000 * of the URI we obtain from the referral?
1003 switch ( op->o_tag ) {
1004 case LDAP_REQ_BIND: {
1005 struct berval rndn = op->o_req_ndn;
1006 Connection *conn = op->o_conn;
1008 /* FIXME: can we really get a referral for binds? */
1009 op->o_req_ndn = slap_empty_bv;
1011 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1012 op->o_req_ndn = rndn;
1018 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1021 case LDAP_REQ_DELETE:
1022 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1025 case LDAP_REQ_MODRDN:
1026 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1029 case LDAP_REQ_MODIFY:
1030 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1033 case LDAP_REQ_COMPARE:
1034 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
1035 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1040 case LDAP_REQ_SEARCH:
1041 if ( rs->sr_type == REP_SEARCHREF ) {
1042 rc = ldap_chain_search( op, rs, ref, 0 );
1045 /* we might get here before any database actually
1046 * performed a search; in those cases, we need
1047 * to check limits, to make sure safe defaults
1049 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1050 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1053 rc = SLAP_CB_CONTINUE;
1058 case LDAP_REQ_EXTENDED:
1059 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
1060 /* FIXME: ldap_back_extended() by design
1061 * doesn't send result; frontend is expected
1063 /* FIXME: what about chaining? */
1064 if ( rc != SLAPD_ABANDON ) {
1066 send_ldap_extended( op, rs );
1069 lb.lb_status = LDAP_CH_RES;
1073 rc = SLAP_CB_CONTINUE;
1083 sr_err = rs->sr_err;
1084 /* slapd-ldap sent response */
1085 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1086 /* FIXME: should we send response? */
1087 Debug( LDAP_DEBUG_ANY,
1088 "%s: ldap_chain_response: "
1089 "overlay should have sent result.\n",
1090 op->o_log_prefix, 0, 0 );
1095 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1096 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1100 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1101 case LDAP_CHAINING_REQUIRED:
1103 op->o_callback = NULL;
1104 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1105 "operation cannot be completed without chaining" );
1109 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1110 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1111 sr_err = rs->sr_err = rc;
1112 rs->sr_type = sr_type;
1115 rc = SLAP_CB_CONTINUE;
1116 rs->sr_err = sr_err;
1117 rs->sr_type = sr_type;
1119 rs->sr_matched = matched;
1122 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1125 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1128 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1129 /* give the remaining callbacks a chance */
1130 op->o_callback = sc->sc_next;
1131 rc = rs->sr_err = slap_map_api2result( rs );
1132 send_ldap_result( op, rs );
1136 rs->sr_err = sr_err;
1137 rs->sr_type = sr_type;
1139 rs->sr_matched = matched;
1142 op->o_callback = sc;
1148 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1150 ldap_chain_parse_ctrl(
1153 LDAPControl *ctrl );
1156 str2chain( const char *s )
1158 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1159 return LDAP_CHAINING_PREFERRED;
1161 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1162 return LDAP_CHAINING_REQUIRED;
1164 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1165 return LDAP_REFERRALS_PREFERRED;
1167 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1168 return LDAP_REFERRALS_REQUIRED;
1173 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1188 static ConfigDriver chain_cf_gen;
1189 static ConfigCfAdd chain_cfadd;
1190 static ConfigLDAPadd chain_ldadd;
1192 static ConfigTable chaincfg[] = {
1193 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1194 { "chain-chaining", "args",
1195 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1196 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1197 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1198 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1199 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1200 { "chain-cache-uri", "TRUE/FALSE",
1201 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1202 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1203 "DESC 'Enables caching of URIs not present in configuration' "
1204 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1205 { "chain-max-depth", "args",
1206 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1207 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1208 "DESC 'max referral depth' "
1209 "SYNTAX OMsInteger "
1210 "EQUALITY integerMatch "
1211 "SINGLE-VALUE )", NULL, NULL },
1212 { "chain-return-error", "TRUE/FALSE",
1213 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1214 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1215 "DESC 'Errors are returned instead of the original referral' "
1216 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1217 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1220 static ConfigOCs chainocs[] = {
1221 { "( OLcfgOvOc:3.1 "
1222 "NAME 'olcChainConfig' "
1223 "DESC 'Chain configuration' "
1224 "SUP olcOverlayConfig "
1226 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1227 "olcChainingBehavior $ "
1228 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1229 "olcChainCacheURI $ "
1230 "olcChainMaxReferralDepth $ "
1231 "olcChainReturnError "
1233 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1234 { "( OLcfgOvOc:3.2 "
1235 "NAME 'olcChainDatabase' "
1236 "DESC 'Chain remote server configuration' "
1238 Cft_Misc, olcDatabaseDummy, chain_ldadd },
1243 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1250 AttributeDescription *ad = NULL;
1256 if ( p->ce_type != Cft_Overlay
1258 || p->ce_bi->bi_cf_ocs != chainocs )
1260 return LDAP_CONSTRAINT_VIOLATION;
1263 on = (slap_overinst *)p->ce_bi;
1264 lc = (ldap_chain_t *)on->on_bi.bi_private;
1266 assert( ca->be == NULL );
1267 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1269 ca->be->bd_info = (BackendInfo *)on;
1271 rc = slap_str2ad( "olcDbURI", &ad, &text );
1272 assert( rc == LDAP_SUCCESS );
1274 at = attr_find( e->e_attrs, ad );
1275 if ( lc->lc_common_li == NULL && at != NULL ) {
1276 /* FIXME: we should generate an empty default entry
1277 * if none is supplied */
1278 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1279 "first underlying database \"%s\" "
1280 "cannot contain attribute \"%s\".\n",
1281 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1282 rc = LDAP_CONSTRAINT_VIOLATION;
1285 } else if ( lc->lc_common_li != NULL && at == NULL ) {
1286 /* FIXME: we should generate an empty default entry
1287 * if none is supplied */
1288 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1289 "subsequent underlying database \"%s\" "
1290 "must contain attribute \"%s\".\n",
1291 e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1292 rc = LDAP_CONSTRAINT_VIOLATION;
1296 if ( lc->lc_common_li == NULL ) {
1297 rc = ldap_chain_db_init_common( ca->be );
1300 rc = ldap_chain_db_init_one( ca->be );
1304 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1305 "unable to init %sunderlying database \"%s\".\n",
1306 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1307 return LDAP_CONSTRAINT_VIOLATION;
1310 li = ca->be->be_private;
1312 if ( lc->lc_common_li == NULL ) {
1313 lc->lc_common_li = li;
1316 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1317 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1318 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1319 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1321 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1322 "database \"%s\" insert failed.\n",
1323 e->e_name.bv_val, 0, 0 );
1324 rc = LDAP_CONSTRAINT_VIOLATION;
1329 ca->ca_private = on;
1332 if ( rc != LDAP_SUCCESS ) {
1333 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1341 typedef struct ldap_chain_cfadd_apply_t {
1347 } ldap_chain_cfadd_apply_t;
1350 ldap_chain_cfadd_apply( void *datum, void *arg )
1352 ldapinfo_t *li = (ldapinfo_t *)datum;
1353 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg;
1357 /* FIXME: should not hardcode "olcDatabase" here */
1358 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1359 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1360 bv.bv_val = lca->ca->cr_msg;
1362 lca->ca->be->be_private = (void *)li;
1363 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1364 &bv, lback->bi_cf_ocs, &chainocs[1] );
1372 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1374 CfEntryInfo *pe = p->e_private;
1375 slap_overinst *on = (slap_overinst *)pe->ce_bi;
1376 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1377 void *priv = (void *)ca->be->be_private;
1379 if ( lback->bi_cf_ocs ) {
1380 ldap_chain_cfadd_apply_t lca = { 0 };
1388 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1390 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1391 &lca, 1, AVL_INORDER );
1393 ca->be->be_private = priv;
1399 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1400 static slap_verbmasks chaining_mode[] = {
1401 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
1402 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
1403 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
1404 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
1407 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1410 chain_cf_gen( ConfigArgs *c )
1412 slap_overinst *on = (slap_overinst *)c->bi;
1413 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1417 if ( c->op == SLAP_CONFIG_EMIT ) {
1419 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1421 struct berval resolve = BER_BVNULL,
1422 continuation = BER_BVNULL;
1424 if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1428 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1429 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1431 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1433 + STRLENOF( "continuation=" ) + continuation.bv_len;
1434 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1435 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1436 "resolve=%s continuation=%s",
1437 resolve.bv_val, continuation.bv_val );
1439 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1440 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1441 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1442 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1443 " critical", STRLENOF( " critical" ) + 1 );
1444 c->value_bv.bv_len += STRLENOF( " critical" );
1449 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1452 c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1456 c->value_int = lc->lc_max_depth;
1460 c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1469 } else if ( c->op == LDAP_MOD_DELETE ) {
1475 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1483 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1494 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1495 char **argv = c->argv;
1497 BerElementBuffer berbuf;
1498 BerElement *ber = (BerElement *)&berbuf;
1502 Operation op = { 0 };
1503 SlapReply rs = { 0 };
1505 lc->lc_chaining_ctrlflag = 0;
1507 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1508 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1509 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1510 if ( resolve == -1 ) {
1511 Debug( LDAP_DEBUG_ANY, "%s: "
1512 "illegal <resolve> value %s "
1513 "in \"chain-chaining>\".\n",
1514 c->log, argv[ 0 ], 0 );
1518 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1519 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1520 if ( continuation == -1 ) {
1521 Debug( LDAP_DEBUG_ANY, "%s: "
1522 "illegal <continuation> value %s "
1523 "in \"chain-chaining\".\n",
1524 c->log, argv[ 0 ], 0 );
1528 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1532 Debug( LDAP_DEBUG_ANY, "%s: "
1533 "unknown option in \"chain-chaining\".\n",
1539 if ( resolve != -1 || continuation != -1 ) {
1542 if ( resolve == -1 ) {
1544 resolve = SLAP_CHAINING_DEFAULT;
1547 ber_init2( ber, NULL, LBER_USE_DER );
1549 err = ber_printf( ber, "{e" /* } */, resolve );
1552 Debug( LDAP_DEBUG_ANY, "%s: "
1553 "chaining behavior control encoding error!\n",
1558 if ( continuation > -1 ) {
1559 err = ber_printf( ber, "e", continuation );
1562 Debug( LDAP_DEBUG_ANY, "%s: "
1563 "chaining behavior control encoding error!\n",
1569 err = ber_printf( ber, /* { */ "N}" );
1572 Debug( LDAP_DEBUG_ANY, "%s: "
1573 "chaining behavior control encoding error!\n",
1578 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1579 exit( EXIT_FAILURE );
1583 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1586 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1587 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1589 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1591 Debug( LDAP_DEBUG_ANY, "%s: "
1592 "unable to parse chaining control%s%s.\n",
1593 c->log, rs.sr_text ? ": " : "",
1594 rs.sr_text ? rs.sr_text : "" );
1598 lc->lc_chaining_ctrlflag = op.o_chaining;
1600 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1603 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1604 Debug( LDAP_DEBUG_ANY, "%s: "
1605 "\"chaining\" control unsupported (ignored).\n",
1607 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1611 if ( c->value_int ) {
1612 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1614 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1619 if ( c->value_int < 0 ) {
1620 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1621 "<%s> invalid max referral depth %d",
1622 c->argv[0], c->value_int );
1623 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1624 c->log, c->cr_msg, 0 );
1628 lc->lc_max_depth = c->value_int;
1631 if ( c->value_int ) {
1632 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1634 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1650 slap_overinst *on = (slap_overinst *)be->bd_info;
1651 ldap_chain_t *lc = NULL;
1653 if ( lback == NULL ) {
1654 lback = backend_info( "ldap" );
1656 if ( lback == NULL ) {
1661 lc = ch_malloc( sizeof( ldap_chain_t ) );
1665 memset( lc, 0, sizeof( ldap_chain_t ) );
1666 lc->lc_max_depth = 1;
1667 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1669 on->on_bi.bi_private = (void *)lc;
1675 ldap_chain_db_config(
1682 slap_overinst *on = (slap_overinst *)be->bd_info;
1683 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1685 int rc = SLAP_CONF_UNKNOWN;
1687 if ( lc->lc_common_li == NULL ) {
1688 void *be_private = be->be_private;
1689 ldap_chain_db_init_common( be );
1690 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1691 be->be_private = be_private;
1694 /* Something for the chain database? */
1695 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1696 char *save_argv0 = argv[ 0 ];
1697 BackendInfo *bd_info = be->bd_info;
1698 void *be_private = be->be_private;
1699 ConfigOCs *be_cf_ocs = be->be_cf_ocs;
1700 static char *allowed_argv[] = {
1701 /* special: put URI here, so in the meanwhile
1702 * it detects whether a new URI is being provided */
1708 /* FIXME: maybe rebind-as-user should be allowed
1709 * only within known URIs... */
1716 int which_argv = -1;
1718 argv[ 0 ] += STRLENOF( "chain-" );
1720 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1721 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1726 if ( allowed_argv[ which_argv ] == NULL ) {
1729 if ( lc->lc_cfg_li == lc->lc_common_li ) {
1730 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1731 "\"%s\" only allowed within a URI directive.\n.",
1732 fname, lineno, argv[ 0 ] );
1737 if ( which_argv == 0 ) {
1738 rc = ldap_chain_db_init_one( be );
1740 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1741 "underlying slapd-ldap initialization failed.\n.",
1745 lc->lc_cfg_li = be->be_private;
1748 /* TODO: add checks on what other slapd-ldap(5) args
1749 * should be put in the template; this is not quite
1750 * harmful, because attributes that shouldn't don't
1751 * get actually used, but the user should at least
1755 be->bd_info = lback;
1756 be->be_private = (void *)lc->lc_cfg_li;
1757 be->be_cf_ocs = lback->bi_cf_ocs;
1759 rc = config_generic_wrapper( be, fname, lineno, argc, argv );
1761 argv[ 0 ] = save_argv0;
1762 be->be_cf_ocs = be_cf_ocs;
1763 be->be_private = be_private;
1764 be->bd_info = bd_info;
1766 if ( which_argv == 0 ) {
1772 db.be_private = (void *)lc->lc_cfg_li;
1773 ldap_chain_db_destroy_one( &db, NULL );
1774 lc->lc_cfg_li = NULL;
1777 if ( lc->lc_cfg_li->li_bvuri == NULL
1778 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1779 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1781 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1782 "no URI list allowed in slapo-chain.\n",
1785 goto private_destroy;
1788 if ( avl_insert( &lc->lc_lai.lai_tree,
1789 (caddr_t)lc->lc_cfg_li,
1790 ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1792 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1793 "duplicate URI in slapo-chain.\n",
1796 goto private_destroy;
1813 typedef struct ldap_chain_db_apply_t {
1816 } ldap_chain_db_apply_t;
1819 ldap_chain_db_apply( void *datum, void *arg )
1821 ldapinfo_t *li = (ldapinfo_t *)datum;
1822 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg;
1824 lca->be->be_private = (void *)li;
1826 return lca->func( lca->be, NULL );
1835 slap_overinst *on = (slap_overinst *)be->bd_info;
1836 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1841 BI_db_func *func = (&lback->bi_db_open)[ which ];
1843 if ( func != NULL && lc->lc_common_li != NULL ) {
1847 db.be_private = lc->lc_common_li;
1849 rc = func( &db, NULL );
1855 if ( lc->lc_lai.lai_tree != NULL ) {
1856 ldap_chain_db_apply_t lca;
1861 rc = avl_apply( lc->lc_lai.lai_tree,
1862 ldap_chain_db_apply, (void *)&lca,
1863 1, AVL_INORDER ) != AVL_NOMORE;
1876 slap_overinst *on = (slap_overinst *) be->bd_info;
1877 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1878 slap_mask_t monitoring;
1881 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1882 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1886 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1888 if ( lc->lc_common_li == NULL ) {
1889 void *be_private = be->be_private;
1890 ldap_chain_db_init_common( be );
1891 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1892 be->be_private = be_private;
1895 /* filter out and restore monitoring */
1896 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1897 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1898 rc = ldap_chain_db_func( be, db_open );
1899 SLAP_DBFLAGS( be ) |= monitoring;
1905 ldap_chain_db_close(
1909 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1910 #ifdef SLAP_CONFIG_DELETE
1911 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1912 #endif /* SLAP_CONFIG_DELETE */
1913 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1914 return ldap_chain_db_func( be, db_close );
1918 ldap_chain_db_destroy(
1922 slap_overinst *on = (slap_overinst *) be->bd_info;
1923 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1927 rc = ldap_chain_db_func( be, db_destroy );
1930 avl_free( lc->lc_lai.lai_tree, NULL );
1931 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1939 * inits one instance of the slapd-ldap backend, and stores
1940 * the private info in be_private of the arg
1943 ldap_chain_db_init_common(
1946 BackendInfo *bi = be->bd_info;
1950 be->bd_info = lback;
1951 be->be_private = NULL;
1952 rc = lback->bi_db_init( be, NULL );
1956 li = (ldapinfo_t *)be->be_private;
1957 li->li_urllist_f = NULL;
1958 li->li_urllist_p = NULL;
1966 * inits one instance of the slapd-ldap backend, stores
1967 * the private info in be_private of the arg and fills
1968 * selected fields with data from the template.
1970 * NOTE: add checks about the other fields of the template,
1971 * which are ignored and SHOULD NOT be configured by the user.
1974 ldap_chain_db_init_one(
1977 slap_overinst *on = (slap_overinst *)be->bd_info;
1978 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1980 BackendInfo *bi = be->bd_info;
1985 be->bd_info = lback;
1986 be->be_private = NULL;
1987 t = lback->bi_db_init( be, NULL );
1991 li = (ldapinfo_t *)be->be_private;
1992 li->li_urllist_f = NULL;
1993 li->li_urllist_p = NULL;
1995 /* copy common data */
1996 li->li_nretries = lc->lc_common_li->li_nretries;
1997 li->li_flags = lc->lc_common_li->li_flags;
1998 li->li_version = lc->lc_common_li->li_version;
1999 for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2000 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2008 ldap_chain_db_open_one(
2011 if ( SLAP_DBMONITORING( be ) ) {
2012 ldapinfo_t *li = (ldapinfo_t *)be->be_private;
2014 if ( li->li_uri == NULL ) {
2015 ber_str2bv( "cn=Common Connections", 0, 1,
2016 &li->li_monitor_info.lmi_rdn );
2021 li->li_monitor_info.lmi_rdn.bv_len
2022 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2023 ptr = li->li_monitor_info.lmi_rdn.bv_val
2024 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
2025 ptr = lutil_strcopy( ptr, "cn=" );
2026 ptr = lutil_strcopy( ptr, li->li_uri );
2031 return lback->bi_db_open( be, NULL );
2034 typedef struct ldap_chain_conn_apply_t {
2037 } ldap_chain_conn_apply_t;
2040 ldap_chain_conn_apply( void *datum, void *arg )
2042 ldapinfo_t *li = (ldapinfo_t *)datum;
2043 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2045 lca->be->be_private = (void *)li;
2047 return lback->bi_connection_destroy( lca->be, lca->conn );
2051 ldap_chain_connection_destroy(
2056 slap_overinst *on = (slap_overinst *) be->bd_info;
2057 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
2058 void *private = be->be_private;
2059 ldap_chain_conn_apply_t lca;
2062 be->be_private = NULL;
2065 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2066 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2067 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2068 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2069 be->be_private = private;
2074 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2076 ldap_chain_parse_ctrl(
2086 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2087 rs->sr_text = "Chaining behavior control specified multiple times";
2088 return LDAP_PROTOCOL_ERROR;
2091 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2092 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2093 return LDAP_PROTOCOL_ERROR;
2096 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2097 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2102 /* Parse the control value
2103 * ChainingBehavior ::= SEQUENCE {
2104 * resolveBehavior Behavior OPTIONAL,
2105 * continuationBehavior Behavior OPTIONAL }
2107 * Behavior :: = ENUMERATED {
2108 * chainingPreferred (0),
2109 * chainingRequired (1),
2110 * referralsPreferred (2),
2111 * referralsRequired (3) }
2114 ber = ber_init( &ctrl->ldctl_value );
2116 rs->sr_text = "internal error";
2120 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2121 /* FIXME: since the whole SEQUENCE is optional,
2122 * should we accept no enumerations at all? */
2123 if ( tag != LBER_ENUMERATED ) {
2124 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2125 return LDAP_PROTOCOL_ERROR;
2128 switch ( behavior ) {
2129 case LDAP_CHAINING_PREFERRED:
2130 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2133 case LDAP_CHAINING_REQUIRED:
2134 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2137 case LDAP_REFERRALS_PREFERRED:
2138 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2141 case LDAP_REFERRALS_REQUIRED:
2142 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2146 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2147 return LDAP_PROTOCOL_ERROR;
2150 tag = ber_peek_tag( ber, &len );
2151 if ( tag == LBER_ENUMERATED ) {
2152 tag = ber_scanf( ber, "e", &behavior );
2153 if ( tag == LBER_ERROR ) {
2154 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2155 return LDAP_PROTOCOL_ERROR;
2159 if ( tag == LBER_DEFAULT ) {
2160 mode |= SLAP_CH_CONTINUATION_DEFAULT;
2163 switch ( behavior ) {
2164 case LDAP_CHAINING_PREFERRED:
2165 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2168 case LDAP_CHAINING_REQUIRED:
2169 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2172 case LDAP_REFERRALS_PREFERRED:
2173 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2176 case LDAP_REFERRALS_REQUIRED:
2177 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2181 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2182 return LDAP_PROTOCOL_ERROR;
2186 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2187 rs->sr_text = "Chaining behavior control: decoding error";
2188 return LDAP_PROTOCOL_ERROR;
2191 (void) ber_free( ber, 1 );
2194 op->o_chaining = mode | ( ctrl->ldctl_iscritical
2195 ? SLAP_CONTROL_CRITICAL
2196 : SLAP_CONTROL_NONCRITICAL );
2198 return LDAP_SUCCESS;
2200 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2203 chain_initialize( void )
2207 /* Make sure we don't exceed the bits reserved for userland */
2208 config_check_userland( CH_LAST );
2210 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2211 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2212 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2213 ldap_chain_parse_ctrl, &sc_chainingBehavior );
2214 if ( rc != LDAP_SUCCESS ) {
2215 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2216 "unable to register chaining behavior control: %d.\n",
2220 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2222 ldapchain.on_bi.bi_type = "chain";
2223 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2224 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2225 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2226 ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2227 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2229 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2231 ldapchain.on_response = ldap_chain_response;
2233 ldapchain.on_bi.bi_cf_ocs = chainocs;
2235 rc = config_register_schema( chaincfg, chainocs );
2240 return overlay_register( &ldapchain );