]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
f9e3ac157c8c8a003f82e08e068a522e7e7f5b3b
[openldap] / servers / slapd / back-ldap / chain.c
1 /* chain.c - chain LDAP operations */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2003-2011 The OpenLDAP Foundation.
6  * Portions Copyright 2003 Howard Chu.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
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>.
16  */
17 /* ACKNOWLEDGEMENTS:
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.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/string.h>
28 #include <ac/socket.h>
29
30 #include "lutil.h"
31 #include "slap.h"
32 #include "back-ldap.h"
33 #include "config.h"
34
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)
51
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)
57
58 static int              sc_chainingBehavior;
59 #endif /*  LDAP_CONTROL_X_CHAINING_BEHAVIOR */
60
61 typedef enum {
62         LDAP_CH_NONE = 0,
63         LDAP_CH_RES,
64         LDAP_CH_ERR
65 } ldap_chain_status_t;
66
67 static BackendInfo      *lback;
68
69 typedef struct ldap_chain_t {
70         /*
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 
74          * structure.
75          *
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.
81          */
82
83         /* "common" configuration info (anything occurring before an "uri") */
84         ldapinfo_t              *lc_common_li;
85
86         /* current configuration info */
87         ldapinfo_t              *lc_cfg_li;
88
89         /* tree of configured[/generated?] "uri" info */
90         ldap_avl_info_t         lc_lai;
91
92         /* max depth in nested referrals chaining */
93         int                     lc_max_depth;
94
95         unsigned                lc_flags;
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)
100
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 )
105
106 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
107         LDAPControl             lc_chaining_ctrl;
108         char                    lc_chaining_ctrlflag;
109 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
110 } ldap_chain_t;
111
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) )
117
118 typedef struct ldap_chain_cb_t {
119         ldap_chain_status_t     lb_status;
120         ldap_chain_t            *lb_lc;
121         BI_op_func              *lb_op_f;
122         int                     lb_depth;
123 } ldap_chain_cb_t;
124
125 static int
126 ldap_chain_op(
127         Operation       *op,
128         SlapReply       *rs,
129         BI_op_func      *op_f,
130         BerVarray       ref,
131         int             depth );
132
133 static int
134 ldap_chain_search(
135         Operation       *op,
136         SlapReply       *rs,
137         BerVarray       ref,
138         int             depth );
139
140 static slap_overinst ldapchain;
141
142 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
143 static int
144 chaining_control_add(
145                 ldap_chain_t    *lc,
146                 Operation       *op, 
147                 LDAPControl     ***oldctrlsp )
148 {
149         LDAPControl     **ctrls = NULL;
150         int             c = 0;
151
152         *oldctrlsp = op->o_ctrls;
153
154         /* default chaining control not defined */
155         if ( !LDAP_CHAIN_CHAINING( lc ) ) {
156                 return 0;
157         }
158
159         /* already present */
160         if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
161                 return 0;
162         }
163
164         /* FIXME: check other incompatibilities */
165
166         /* add to other controls */
167         if ( op->o_ctrls ) {
168                 for ( c = 0; op->o_ctrls[ c ]; c++ )
169                         /* count them */ ;
170         }
171
172         ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
173         ctrls[ 0 ] = &lc->lc_chaining_ctrl;
174         if ( op->o_ctrls ) {
175                 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
176                         ctrls[ c + 1 ] = op->o_ctrls[ c ];
177                 }
178         }
179         ctrls[ c + 1 ] = NULL;
180
181         op->o_ctrls = ctrls;
182
183         op->o_chaining = lc->lc_chaining_ctrlflag;
184
185         return 0;
186 }
187
188 static int
189 chaining_control_remove(
190                 Operation       *op, 
191                 LDAPControl     ***oldctrlsp )
192 {
193         LDAPControl     **oldctrls = *oldctrlsp;
194
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 
197          * free */
198         if ( op->o_ctrls != oldctrls ) {
199                 assert( op->o_ctrls != NULL );
200                 assert( op->o_ctrls[ 0 ] != NULL );
201
202                 free( op->o_ctrls );
203
204                 op->o_chaining = 0;
205                 op->o_ctrls = oldctrls;
206         } 
207
208         *oldctrlsp = NULL;
209
210         return 0;
211 }
212 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
213
214 static int
215 ldap_chain_uri_cmp( const void *c1, const void *c2 )
216 {
217         const ldapinfo_t        *li1 = (const ldapinfo_t *)c1;
218         const ldapinfo_t        *li2 = (const ldapinfo_t *)c2;
219
220         assert( li1->li_bvuri != NULL );
221         assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
222         assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
223
224         assert( li2->li_bvuri != NULL );
225         assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
226         assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
227
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 ] );
230 }
231
232 static int
233 ldap_chain_uri_dup( void *c1, void *c2 )
234 {
235         ldapinfo_t      *li1 = (ldapinfo_t *)c1;
236         ldapinfo_t      *li2 = (ldapinfo_t *)c2;
237
238         assert( li1->li_bvuri != NULL );
239         assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
240         assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
241
242         assert( li2->li_bvuri != NULL );
243         assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
244         assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
245
246         /* Cannot have more than one shared session with same DN */
247         if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
248                 return -1;
249         }
250                 
251         return 0;
252 }
253
254 /*
255  * Search specific response that strips entryDN from entries
256  */
257 static int
258 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
259 {
260         ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
261
262         assert( op->o_tag == LDAP_REQ_SEARCH );
263
264         /* if in error, don't proceed any further */
265         if ( lb->lb_status == LDAP_CH_ERR ) {
266                 return 0;
267         }
268
269         if ( rs->sr_type == REP_SEARCH ) {
270                 Attribute       **ap = &rs->sr_entry->e_attrs;
271
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 )
277                         {
278                                 Attribute *a = *ap;
279
280                                 *ap = (*ap)->a_next;
281                                 attr_free( a );
282
283                                 /* there SHOULD be one only! */
284                                 break;
285                         }
286                 }
287
288                 /* tell the frontend not to add generated
289                  * operational attributes */
290                 rs->sr_flags |= REP_NO_OPERATIONALS;
291                 
292                 return SLAP_CB_CONTINUE;
293
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 );
299                 }
300
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;
307
308                         default:
309                                 break;
310                         }
311                 }
312 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
313                 return SLAP_CB_CONTINUE;
314
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 )
319                 {
320                         rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
321                 }
322
323                 /* back-ldap tried to send result */
324                 lb->lb_status = LDAP_CH_RES;
325         }
326
327         return 0;
328 }
329
330 /*
331  * Dummy response that simply traces if back-ldap tried to send 
332  * anything to the client
333  */
334 static int
335 ldap_chain_cb_response( Operation *op, SlapReply *rs )
336 {
337         ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
338
339         /* if in error, don't proceed any further */
340         if ( lb->lb_status == LDAP_CH_ERR ) {
341                 return 0;
342         }
343
344         if ( rs->sr_type == REP_RESULT ) {
345 retry:;
346                 switch ( rs->sr_err ) {
347                 case LDAP_COMPARE_TRUE:
348                 case LDAP_COMPARE_FALSE:
349                         if ( op->o_tag != LDAP_REQ_COMPARE ) {
350                                 return rs->sr_err;
351                         }
352                         /* fallthru */
353
354                 case LDAP_SUCCESS:
355                         lb->lb_status = LDAP_CH_RES;
356                         break;
357
358                 case LDAP_REFERRAL:
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 );
361                                 goto retry;
362                         }
363
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;
370
371                                 default:
372                                         break;
373                                 }
374                         }
375 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
376                         break;
377
378                 default:
379                         return rs->sr_err;
380                 }
381
382         } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
383         {
384                 /* strip the entryDN attribute, but keep returning results */
385                 (void)ldap_chain_cb_search_response( op, rs );
386         }
387
388         return SLAP_CB_CONTINUE;
389 }
390
391 static int
392 ldap_chain_op(
393         Operation       *op,
394         SlapReply       *rs,
395         BI_op_func      *op_f,
396         BerVarray       ref,
397         int             depth )
398 {
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 } };
406
407         /* NOTE: returned if ref is empty... */
408         int             rc = LDAP_OTHER,
409                         first_rc;
410
411 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
412         LDAPControl     **ctrls = NULL;
413         
414         (void)chaining_control_add( lc, op, &ctrls );
415 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
416
417         li.li_bvuri = bvuri;
418         first_rc = -1;
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,
425                                 pdn = odn,
426                                 ndn = ondn;
427                 char            *filter = NULL;
428                 int             temporary = 0;
429                 int             free_dn = 0;
430                         
431                 /* We're setting the URI of the first referral;
432                  * what if there are more?
433
434 Document: RFC 4511
435
436 4.1.10. Referral 
437    ...
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. 
442
443                  * so we actually need to follow exactly one,
444                  * and we can assume any is fine.
445                  */
446         
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 );
453
454                         /* try next */
455                         rc = LDAP_OTHER;
456                         continue;
457                 }
458
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;
463
464                         } else {
465                                 /* RFC 4511: if scope is absent, use original */
466                                 tmp_oq_search.rs_scope = op->ors_scope;
467                         }
468                 }
469
470                 rc = LDAP_SUCCESS;
471                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
472                 dn.bv_val = srv->lud_dn;
473                 filter = srv->lud_filter;
474
475                 /* normalize DN */
476                 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
477                         if ( srv->lud_dn == NULL ) {
478                                 srv->lud_dn = "";
479                         }
480
481                 } else {
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 */
488                                 srv->lud_dn = "";
489                                 free_dn = 1;
490                         }
491                 }
492
493                 /* prepare filter */
494                 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
495                         /* filter */
496                         if ( srv->lud_filter != NULL
497                                 && srv->lud_filter[0] != '\0'
498                                 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
499                         {
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 );
505
506                                 } else {
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 );
509                                         rc = LDAP_OTHER;
510                                 }
511                         }
512                 }
513                 srv->lud_filter = NULL;
514
515                 if ( rc == LDAP_SUCCESS ) {
516                         li.li_uri = ldap_url_desc2str( srv );
517                 }
518
519                 srv->lud_dn = dn.bv_val;
520                 srv->lud_filter = filter;
521                 ldap_free_urldesc( srv );
522
523                 if ( rc != LDAP_SUCCESS ) {
524                         /* try next */
525                         rc = LDAP_OTHER;
526                         continue;
527                 }
528
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 );
532
533                         /* try next */
534                         rc = LDAP_OTHER;
535                         goto further_cleanup;
536                 }
537
538                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
539                         op->o_log_prefix, ref->bv_val, li.li_uri );
540
541                 op->o_req_dn = pdn;
542                 op->o_req_ndn = ndn;
543
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;
549                         }
550                 }
551
552                 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
553
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 );
559
560                 if ( lip != NULL ) {
561                         op->o_bd->be_private = (void *)lip;
562
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 );
565
566                 } else {
567                         rc = ldap_chain_db_init_one( op->o_bd );
568                         if ( rc != 0 ) {
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 );
571                                 goto cleanup;
572                         }
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 );
577                         if ( rc != 0 ) {
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 );
580                                 lip->li_uri = NULL;
581                                 lip->li_bvuri = NULL;
582                                 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
583                                 goto cleanup;
584                         }
585
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 ) )
590                                 {
591                                         /* someone just inserted another;
592                                          * don't bother, use this and then
593                                          * just free it */
594                                         temporary = 1;
595                                 }
596                                 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
597
598                         } else {
599                                 temporary = 1;
600                         }
601
602                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
603                                 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
604                 }
605
606                 lb->lb_op_f = op_f;
607                 lb->lb_depth = depth + 1;
608
609                 rc = op_f( op, &rs2 );
610
611                 /* note the first error */
612                 if ( first_rc == -1 ) {
613                         first_rc = rc;
614                 }
615
616 cleanup:;
617                 ldap_memfree( li.li_uri );
618                 li.li_uri = NULL;
619
620                 if ( temporary ) {
621                         lip->li_uri = NULL;
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 );
625                 }
626
627 further_cleanup:;
628                 if ( free_dn ) {
629                         op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
630                         op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
631                 }
632         
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 );
636                         }
637
638                         if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
639                                 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
640                         }
641
642                         op->oq_search = save_oq_search;
643                 }
644
645                 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
646                         *rs = rs2;
647                         break;
648                 }
649
650                 rc = rs2.sr_err;
651         }
652
653         op->o_req_dn = odn;
654         op->o_req_ndn = ondn;
655
656 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
657         (void)chaining_control_remove( op, &ctrls );
658 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
659
660         if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
661                 rc = first_rc;
662         }
663
664         return rc;
665 }
666
667 static int
668 ldap_chain_search(
669         Operation       *op,
670         SlapReply       *rs,
671         BerVarray       ref,
672         int             depth )
673
674 {
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 } };
680
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;
685
686         int             rc = LDAP_OTHER,
687                         first_rc = -1;
688
689 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
690         LDAPControl     **ctrls = NULL;
691         
692         (void)chaining_control_add( lc, op, &ctrls );
693 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
694
695         assert( rs->sr_type == REP_SEARCHREF );
696
697         rs->sr_type = REP_SEARCH;
698
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?) */
704         li.li_bvuri = bvuri;
705         for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
706                 SlapReply       rs2 = { REP_RESULT };
707                 LDAPURLDesc     *srv;
708                 req_search_s    save_oq_search = op->oq_search,
709                                 tmp_oq_search = { 0 };
710                 struct berval   dn,
711                                 pdn = op->o_req_dn,
712                                 ndn = op->o_req_ndn;
713                 char            *filter = NULL;
714                 int             temporary = 0;
715                 int             free_dn = 0;
716
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 );
723
724                         /* try next */
725                         rs->sr_err = LDAP_OTHER;
726                         continue;
727                 }
728
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;
732
733                 } else {
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;
738                         else
739                                 tmp_oq_search.rs_scope = op->ors_scope;
740                 }
741
742                 rc = LDAP_SUCCESS;
743                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
744                 dn.bv_val = srv->lud_dn;
745                 filter = srv->lud_filter;
746
747                 /* normalize DN */
748                 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
749                         if ( srv->lud_dn == NULL ) {
750                                 srv->lud_dn = "";
751                         }
752
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 */
758                         
759                 } else {
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 */
767                                 srv->lud_dn = "";
768                                 free_dn = 1;
769                         }
770                 }
771
772                 /* prepare filter */
773                 if ( rc == LDAP_SUCCESS ) {
774                         /* filter */
775                         if ( srv->lud_filter != NULL
776                                 && srv->lud_filter[0] != '\0'
777                                 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
778                         {
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 );
784
785                                 } else {
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 );
788                                         rc = LDAP_OTHER;
789                                 }
790                         }
791                 }
792                 srv->lud_filter = NULL;
793
794                 if ( rc == LDAP_SUCCESS ) {
795                         li.li_uri = ldap_url_desc2str( srv );
796                 }
797
798                 srv->lud_dn = dn.bv_val;
799                 srv->lud_filter = filter;
800                 ldap_free_urldesc( srv );
801
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 );
805
806                         /* try next */
807                         rc = LDAP_OTHER;
808                         goto further_cleanup;
809                 }
810
811                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
812                         op->o_log_prefix, ref->bv_val, li.li_uri );
813
814                 op->o_req_dn = pdn;
815                 op->o_req_ndn = ndn;
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;
820                 }
821
822                 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
823
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 );
829
830                 if ( lip != NULL ) {
831                         op->o_bd->be_private = (void *)lip;
832
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 );
835
836                 } else {
837                         /* if none is found, create a temporary... */
838                         rc = ldap_chain_db_init_one( op->o_bd );
839                         if ( rc != 0 ) {
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 );
842                                 goto cleanup;
843                         }
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 );
848                         if ( rc != 0 ) {
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 );
851                                 lip->li_uri = NULL;
852                                 lip->li_bvuri = NULL;
853                                 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
854                                 goto cleanup;
855                         }
856
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 ) )
861                                 {
862                                         /* someone just inserted another;
863                                          * don't bother, use this and then
864                                          * just free it */
865                                         temporary = 1;
866                                 }
867                                 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
868
869                         } else {
870                                 temporary = 1;
871                         }
872
873                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
874                                 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
875                 }
876
877                 lb->lb_op_f = lback->bi_op_search;
878                 lb->lb_depth = depth + 1;
879
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 ) {
884                         first_rc = rc;
885                 }
886
887 cleanup:;
888                 ldap_memfree( li.li_uri );
889                 li.li_uri = NULL;
890
891                 if ( temporary ) {
892                         lip->li_uri = NULL;
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 );
896                 }
897                 
898 further_cleanup:;
899                 if ( free_dn ) {
900                         op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
901                         op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
902                 }
903
904                 op->o_req_dn = odn;
905                 op->o_req_ndn = ondn;
906
907                 if ( tmp_oq_search.rs_filter != NULL ) {
908                         filter_free_x( op, tmp_oq_search.rs_filter, 1 );
909                 }
910
911                 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
912                         slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
913                 }
914
915                 op->oq_search = save_oq_search;
916                 
917                 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
918                         *rs = rs2;
919                         break;
920                 }
921
922                 rc = rs2.sr_err;
923         }
924
925 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
926         (void)chaining_control_remove( op, &ctrls );
927 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
928
929         op->o_req_dn = odn;
930         op->o_req_ndn = ondn;
931         rs->sr_type = REP_SEARCHREF;
932         rs->sr_entry = save_entry;
933         rs->sr_flags = save_flags;
934
935         if ( rc != LDAP_SUCCESS ) {
936                 /* couldn't chase any of the referrals */
937                 if ( first_rc != -1 ) {
938                         rc = first_rc;
939
940                 } else {
941                         rc = SLAP_CB_CONTINUE;
942                 }
943         }
944
945         return rc;
946 }
947
948 static int
949 ldap_chain_response( Operation *op, SlapReply *rs )
950 {
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,
956                         sc2 = { 0 };
957         int             rc = 0;
958         const char      *text = NULL;
959         const char      *matched;
960         BerVarray       ref;
961         struct berval   ndn = op->o_ndn;
962
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 */
969
970         if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
971                 return SLAP_CB_CONTINUE;
972         }
973
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;
980
981                 default:
982                         chain_mask = SLAP_CH_RESOLVE_MASK;
983                         chain_shift = SLAP_CH_RESOLVE_SHIFT;
984                         break;
985                 }
986
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;
992
993                 default:
994                         chain_mask = SLAP_CH_CONTINUATION_MASK;
995                         chain_shift = SLAP_CH_CONTINUATION_SHIFT;
996                         break;
997                 }
998         }
999 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1000
1001         /*
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://)
1007          *   e) what ssf
1008          */
1009
1010         db = *op->o_bd;
1011         SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1012         op->o_bd = &db;
1013
1014         text = rs->sr_text;
1015         rs->sr_text = NULL;
1016         matched = rs->sr_matched;
1017         rs->sr_matched = NULL;
1018         ref = rs->sr_ref;
1019         rs->sr_ref = NULL;
1020
1021         /* we need this to know if back-ldap returned any result */
1022         lb.lb_lc = lc;
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;
1027
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).
1032          *
1033          * FIXME: the idassert-authcDN is one, will it be fine regardless
1034          * of the URI we obtain from the referral?
1035          */
1036
1037         switch ( op->o_tag ) {
1038         case LDAP_REQ_BIND: {
1039                 struct berval   rndn = op->o_req_ndn;
1040                 Connection      *conn = op->o_conn;
1041
1042                 /* FIXME: can we really get a referral for binds? */
1043                 op->o_req_ndn = slap_empty_bv;
1044                 op->o_conn = NULL;
1045                 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1046                 op->o_req_ndn = rndn;
1047                 op->o_conn = conn;
1048                 }
1049                 break;
1050
1051         case LDAP_REQ_ADD:
1052                 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1053                 break;
1054
1055         case LDAP_REQ_DELETE:
1056                 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1057                 break;
1058
1059         case LDAP_REQ_MODRDN:
1060                 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1061                 break;
1062
1063         case LDAP_REQ_MODIFY:
1064                 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1065                 break;
1066
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 ) {
1070                         rc = LDAP_SUCCESS;
1071                 }
1072                 break;
1073
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 );
1078                         
1079                 } else {
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
1083                          * are in place */
1084                         if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1085                                 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1086
1087                         } else {
1088                                 rc = SLAP_CB_CONTINUE;
1089                         }
1090                 }
1091                 break;
1092
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
1097                  * to send it... */
1098                 /* FIXME: what about chaining? */
1099                 if ( rc != SLAPD_ABANDON ) {
1100                         rs->sr_err = rc;
1101                         send_ldap_extended( op, rs );
1102                         rc = LDAP_SUCCESS;
1103                 }
1104                 lb.lb_status = LDAP_CH_RES;
1105                 break;
1106
1107         default:
1108                 rc = SLAP_CB_CONTINUE;
1109                 break;
1110         }
1111
1112         switch ( rc ) {
1113         case SLAPD_ABANDON:
1114                 goto dont_chain;
1115
1116         case LDAP_SUCCESS:
1117         case LDAP_REFERRAL:
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 );
1126                 }
1127                 break;
1128
1129         default:
1130 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1131                 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1132                         goto cannot_chain;
1133                 }
1134
1135                 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1136                 case LDAP_CHAINING_REQUIRED:
1137 cannot_chain:;
1138                         op->o_callback = NULL;
1139                         send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1140                                 "operation cannot be completed without chaining" );
1141                         goto dont_chain;
1142
1143                 default:
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;
1148
1149                         } else {
1150                                 rc = SLAP_CB_CONTINUE;
1151                                 rs->sr_err = sr_err;
1152                                 rs->sr_type = sr_type;
1153                                 rs->sr_text = text;
1154                                 rs->sr_matched = matched;
1155                                 rs->sr_ref = ref;
1156                         }
1157 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1158                         break;
1159                 }
1160 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1161         }
1162
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 );
1168         }
1169
1170 dont_chain:;
1171         rs->sr_err = sr_err;
1172         rs->sr_type = sr_type;
1173         rs->sr_text = text;
1174         rs->sr_matched = matched;
1175         rs->sr_ref = ref;
1176         op->o_bd = bd;
1177         op->o_callback = sc;
1178         op->o_ndn = ndn;
1179
1180         return rc;
1181 }
1182
1183 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1184 static int
1185 ldap_chain_parse_ctrl(
1186         Operation       *op,
1187         SlapReply       *rs,
1188         LDAPControl     *ctrl );
1189
1190 static int
1191 str2chain( const char *s )
1192 {
1193         if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1194                 return LDAP_CHAINING_PREFERRED;
1195                 
1196         } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1197                 return LDAP_CHAINING_REQUIRED;
1198
1199         } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1200                 return LDAP_REFERRALS_PREFERRED;
1201                 
1202         } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1203                 return LDAP_REFERRALS_REQUIRED;
1204         }
1205
1206         return -1;
1207 }
1208 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1209
1210 /*
1211  * configuration...
1212  */
1213
1214 enum {
1215         CH_CHAINING = 1,
1216         CH_CACHE_URI,
1217         CH_MAX_DEPTH,
1218         CH_RETURN_ERR,
1219
1220         CH_LAST
1221 };
1222
1223 static ConfigDriver chain_cf_gen;
1224 static ConfigCfAdd chain_cfadd;
1225 static ConfigLDAPadd chain_ldadd;
1226
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 }
1253 };
1254
1255 static ConfigOCs chainocs[] = {
1256         { "( OLcfgOvOc:3.1 "
1257                 "NAME 'olcChainConfig' "
1258                 "DESC 'Chain configuration' "
1259                 "SUP olcOverlayConfig "
1260                 "MAY ( "
1261 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1262                         "olcChainingBehavior $ "
1263 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1264                         "olcChainCacheURI $ "
1265                         "olcChainMaxReferralDepth $ "
1266                         "olcChainReturnError "
1267                         ") )",
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 },
1274         { NULL, 0, NULL }
1275 };
1276
1277 static int
1278 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1279 {
1280         slap_overinst           *on;
1281         ldap_chain_t            *lc;
1282
1283         ldapinfo_t              *li;
1284
1285         AttributeDescription    *ad = NULL;
1286         Attribute               *at;
1287         const char              *text;
1288
1289         int                     rc;
1290
1291         if ( p->ce_type != Cft_Overlay
1292                 || !p->ce_bi
1293                 || p->ce_bi->bi_cf_ocs != chainocs )
1294         {
1295                 return LDAP_CONSTRAINT_VIOLATION;
1296         }
1297
1298         on = (slap_overinst *)p->ce_bi;
1299         lc = (ldap_chain_t *)on->on_bi.bi_private;
1300
1301         assert( ca->be == NULL );
1302         ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1303
1304         ca->be->bd_info = (BackendInfo *)on;
1305
1306         rc = slap_str2ad( "olcDbURI", &ad, &text );
1307         assert( rc == LDAP_SUCCESS );
1308
1309         at = attr_find( e->e_attrs, ad );
1310 #if 0
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;
1319                 goto done;
1320
1321         } else
1322 #endif
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;
1331                 goto done;
1332         }
1333
1334         if ( lc->lc_common_li == NULL ) {
1335                 rc = ldap_chain_db_init_common( ca->be );
1336
1337         } else {
1338                 rc = ldap_chain_db_init_one( ca->be );
1339         }
1340
1341         if ( rc != 0 ) {
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;
1346         }
1347
1348         li = ca->be->be_private;
1349
1350         if ( lc->lc_common_li == NULL ) {
1351                 lc->lc_common_li = li;
1352
1353         } else {
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 ) )
1358                 {
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;
1363                         goto done;
1364                 }
1365         }
1366
1367         ca->ca_private = on;
1368
1369 done:;
1370         if ( rc != LDAP_SUCCESS ) {
1371                 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1372                 ch_free( ca->be );
1373                 ca->be = NULL;
1374         }
1375
1376         return rc;
1377 }
1378
1379 typedef struct ldap_chain_cfadd_apply_t {
1380         Operation       *op;
1381         SlapReply       *rs;
1382         Entry           *p;
1383         ConfigArgs      *ca;
1384         int             count;
1385 } ldap_chain_cfadd_apply_t;
1386
1387 static int
1388 ldap_chain_cfadd_apply( void *datum, void *arg )
1389 {
1390         ldapinfo_t                      *li = (ldapinfo_t *)datum;
1391         ldap_chain_cfadd_apply_t        *lca = (ldap_chain_cfadd_apply_t *)arg;
1392
1393         struct berval                   bv;
1394
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;
1399
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] );
1403
1404         lca->count++;
1405
1406         return 0;
1407 }
1408
1409 static int
1410 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1411 {
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;
1416
1417         if ( lback->bi_cf_ocs ) {
1418                 ldap_chain_cfadd_apply_t        lca = { 0 };
1419
1420                 lca.op = op;
1421                 lca.rs = rs;
1422                 lca.p = p;
1423                 lca.ca = ca;
1424                 lca.count = 0;
1425
1426                 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1427
1428                 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1429                         &lca, 1, AVL_INORDER );
1430
1431                 ca->be->be_private = priv;
1432         }
1433
1434         return 0;
1435 }
1436
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 },
1443         { BER_BVNULL,                           0 }
1444 };
1445 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1446
1447 static int
1448 chain_cf_gen( ConfigArgs *c )
1449 {
1450         slap_overinst   *on = (slap_overinst *)c->bi;
1451         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1452
1453         int             rc = 0;
1454
1455         if ( c->op == SLAP_CONFIG_EMIT ) {
1456                 switch( c->type ) {
1457 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1458                 case CH_CHAINING: {
1459                         struct berval   resolve = BER_BVNULL,
1460                                         continuation = BER_BVNULL;
1461
1462                         if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1463                                 return 1;
1464                         }
1465
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 );
1468
1469                         c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1470                                 + STRLENOF( " " )
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 );
1476
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" );
1483                         }
1484
1485                         break;
1486                 }
1487 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1488
1489                 case CH_CACHE_URI:
1490                         c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1491                         break;
1492
1493                 case CH_MAX_DEPTH:
1494                         c->value_int = lc->lc_max_depth;
1495                         break;
1496
1497                 case CH_RETURN_ERR:
1498                         c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1499                         break;
1500
1501                 default:
1502                         assert( 0 );
1503                         rc = 1;
1504                 }
1505                 return rc;
1506
1507         } else if ( c->op == LDAP_MOD_DELETE ) {
1508                 switch( c->type ) {
1509                 case CH_CHAINING:
1510                         return 1;
1511
1512                 case CH_CACHE_URI:
1513                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1514                         break;
1515
1516                 case CH_MAX_DEPTH:
1517                         c->value_int = 0;
1518                         break;
1519
1520                 case CH_RETURN_ERR:
1521                         lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1522                         break;
1523
1524                 default:
1525                         return 1;
1526                 }
1527                 return rc;
1528         }
1529
1530         switch( c->type ) {
1531         case CH_CHAINING: {
1532 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1533                 char                    **argv = c->argv;
1534                 int                     argc = c->argc;
1535                 BerElementBuffer        berbuf;
1536                 BerElement              *ber = (BerElement *)&berbuf;
1537                 int                     resolve = -1,
1538                                         continuation = -1,
1539                                         iscritical = 0;
1540                 Operation               op = { 0 };
1541                 SlapReply               rs = { 0 };
1542
1543                 lc->lc_chaining_ctrlflag = 0;
1544
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 );
1553                                         return 1;
1554                                 }
1555
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 );
1563                                         return 1;
1564                                 }
1565
1566                         } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1567                                 iscritical = 1;
1568
1569                         } else {
1570                                 Debug( LDAP_DEBUG_ANY, "%s: "
1571                                         "unknown option in \"chain-chaining\".\n",
1572                                         c->log, 0, 0 );
1573                                 return 1;
1574                         }
1575                 }
1576
1577                 if ( resolve != -1 || continuation != -1 ) {
1578                         int     err;
1579
1580                         if ( resolve == -1 ) {
1581                                 /* default */
1582                                 resolve = SLAP_CHAINING_DEFAULT;
1583                         }
1584
1585                         ber_init2( ber, NULL, LBER_USE_DER );
1586
1587                         err = ber_printf( ber, "{e" /* } */, resolve );
1588                         if ( err == -1 ) {
1589                                 ber_free( ber, 1 );
1590                                 Debug( LDAP_DEBUG_ANY, "%s: "
1591                                         "chaining behavior control encoding error!\n",
1592                                         c->log, 0, 0 );
1593                                 return 1;
1594                         }
1595
1596                         if ( continuation > -1 ) {
1597                                 err = ber_printf( ber, "e", continuation );
1598                                 if ( err == -1 ) {
1599                                         ber_free( ber, 1 );
1600                                         Debug( LDAP_DEBUG_ANY, "%s: "
1601                                                 "chaining behavior control encoding error!\n",
1602                                                 c->log, 0, 0 );
1603                                         return 1;
1604                                 }
1605                         }
1606
1607                         err = ber_printf( ber, /* { */ "N}" );
1608                         if ( err == -1 ) {
1609                                 ber_free( ber, 1 );
1610                                 Debug( LDAP_DEBUG_ANY, "%s: "
1611                                         "chaining behavior control encoding error!\n",
1612                                         c->log, 0, 0 );
1613                                 return 1;
1614                         }
1615
1616                         if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1617                                 exit( EXIT_FAILURE );
1618                         }
1619
1620                 } else {
1621                         BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1622                 }
1623
1624                 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1625                 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1626
1627                 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1628                 {
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 : "" );
1633                         return 1;
1634                 }
1635
1636                 lc->lc_chaining_ctrlflag = op.o_chaining;
1637
1638                 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1639
1640                 rc = 0;
1641 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1642                 Debug( LDAP_DEBUG_ANY, "%s: "
1643                         "\"chaining\" control unsupported (ignored).\n",
1644                         c->log, 0, 0 );
1645 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1646                 } break;
1647
1648         case CH_CACHE_URI:
1649                 if ( c->value_int ) {
1650                         lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1651                 } else {
1652                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1653                 }
1654                 break;
1655
1656         case CH_MAX_DEPTH:
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 );
1663                         rc = 1;
1664                         break;
1665                 }
1666                 lc->lc_max_depth = c->value_int;
1667
1668         case CH_RETURN_ERR:
1669                 if ( c->value_int ) {
1670                         lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1671                 } else {
1672                         lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1673                 }
1674                 break;
1675
1676         default:
1677                 assert( 0 );
1678                 return 1;
1679         }
1680         return rc;
1681 }
1682
1683 static int
1684 ldap_chain_db_init(
1685         BackendDB *be,
1686         ConfigReply *cr )
1687 {
1688         slap_overinst   *on = (slap_overinst *)be->bd_info;
1689         ldap_chain_t    *lc = NULL;
1690
1691         if ( lback == NULL ) {
1692                 lback = backend_info( "ldap" );
1693
1694                 if ( lback == NULL ) {
1695                         return 1;
1696                 }
1697         }
1698
1699         lc = ch_malloc( sizeof( ldap_chain_t ) );
1700         if ( lc == NULL ) {
1701                 return 1;
1702         }
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 );
1706
1707         on->on_bi.bi_private = (void *)lc;
1708
1709         return 0;
1710 }
1711
1712 static int
1713 ldap_chain_db_config(
1714         BackendDB       *be,
1715         const char      *fname,
1716         int             lineno,
1717         int             argc,
1718         char            **argv )
1719 {
1720         slap_overinst   *on = (slap_overinst *)be->bd_info;
1721         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1722
1723         int             rc = SLAP_CONF_UNKNOWN;
1724                 
1725         if ( lc->lc_common_li == NULL ) {
1726                 void    *be_private = be->be_private;
1727                 ldap_chain_db_init_common( be );
1728                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1729                 be->be_private = be_private;
1730         }
1731
1732         /* Something for the chain database? */
1733         if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1734                 char            *save_argv0 = argv[ 0 ];
1735                 BackendInfo     *bd_info = be->bd_info;
1736                 void            *be_private = be->be_private;
1737                 ConfigOCs       *be_cf_ocs = be->be_cf_ocs;
1738                 static char     *allowed_argv[] = {
1739                         /* special: put URI here, so in the meanwhile
1740                          * it detects whether a new URI is being provided */
1741                         "uri",
1742                         "nretries",
1743                         "timeout",
1744                         /* flags */
1745                         "tls",
1746                         /* FIXME: maybe rebind-as-user should be allowed
1747                          * only within known URIs... */
1748                         "rebind-as-user",
1749                         "chase-referrals",
1750                         "t-f-support",
1751                         "proxy-whoami",
1752                         NULL
1753                 };
1754                 int             which_argv = -1;
1755
1756                 argv[ 0 ] += STRLENOF( "chain-" );
1757
1758                 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1759                         if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1760                                 break;
1761                         }
1762                 }
1763
1764                 if ( allowed_argv[ which_argv ] == NULL ) {
1765                         which_argv = -1;
1766
1767                         if ( lc->lc_cfg_li == lc->lc_common_li ) {
1768                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1769                                         "\"%s\" only allowed within a URI directive.\n.",
1770                                         fname, lineno, argv[ 0 ] );
1771                                 return 1;
1772                         }
1773                 }
1774
1775                 if ( which_argv == 0 ) {
1776                         rc = ldap_chain_db_init_one( be );
1777                         if ( rc != 0 ) {
1778                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1779                                         "underlying slapd-ldap initialization failed.\n.",
1780                                         fname, lineno, 0 );
1781                                 return 1;
1782                         }
1783                         lc->lc_cfg_li = be->be_private;
1784                 }
1785
1786                 /* TODO: add checks on what other slapd-ldap(5) args
1787                  * should be put in the template; this is not quite
1788                  * harmful, because attributes that shouldn't don't
1789                  * get actually used, but the user should at least
1790                  * be warned.
1791                  */
1792
1793                 be->bd_info = lback;
1794                 be->be_private = (void *)lc->lc_cfg_li;
1795                 be->be_cf_ocs = lback->bi_cf_ocs;
1796
1797                 rc = config_generic_wrapper( be, fname, lineno, argc, argv );
1798
1799                 argv[ 0 ] = save_argv0;
1800                 be->be_cf_ocs = be_cf_ocs;
1801                 be->be_private = be_private;
1802                 be->bd_info = bd_info;
1803
1804                 if ( which_argv == 0 ) {
1805 private_destroy:;
1806                         if ( rc != 0 ) {
1807                                 BackendDB               db = *be;
1808
1809                                 db.bd_info = lback;
1810                                 db.be_private = (void *)lc->lc_cfg_li;
1811                                 ldap_chain_db_destroy_one( &db, NULL );
1812                                 lc->lc_cfg_li = NULL;
1813
1814                         } else {
1815                                 if ( lc->lc_cfg_li->li_bvuri == NULL
1816                                         || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1817                                         || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1818                                 {
1819                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1820                                                 "no URI list allowed in slapo-chain.\n",
1821                                                 fname, lineno, 0 );
1822                                         rc = 1;
1823                                         goto private_destroy;
1824                                 }
1825
1826                                 if ( avl_insert( &lc->lc_lai.lai_tree,
1827                                         (caddr_t)lc->lc_cfg_li,
1828                                         ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1829                                 {
1830                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1831                                                 "duplicate URI in slapo-chain.\n",
1832                                                 fname, lineno, 0 );
1833                                         rc = 1;
1834                                         goto private_destroy;
1835                                 }
1836                         }
1837                 }
1838         }
1839         
1840         return rc;
1841 }
1842
1843 enum db_which {
1844         db_open = 0,
1845         db_close,
1846         db_destroy,
1847
1848         db_last
1849 };
1850
1851 typedef struct ldap_chain_db_apply_t {
1852         BackendDB       *be;
1853         BI_db_func      *func;
1854 } ldap_chain_db_apply_t;
1855
1856 static int
1857 ldap_chain_db_apply( void *datum, void *arg )
1858 {
1859         ldapinfo_t              *li = (ldapinfo_t *)datum;
1860         ldap_chain_db_apply_t   *lca = (ldap_chain_db_apply_t *)arg;
1861
1862         lca->be->be_private = (void *)li;
1863
1864         return lca->func( lca->be, NULL );
1865 }
1866
1867 static int
1868 ldap_chain_db_func(
1869         BackendDB *be,
1870         enum db_which which
1871 )
1872 {
1873         slap_overinst   *on = (slap_overinst *)be->bd_info;
1874         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1875
1876         int             rc = 0;
1877
1878         if ( lc ) {
1879                 BI_db_func      *func = (&lback->bi_db_open)[ which ];
1880
1881                 if ( func != NULL && lc->lc_common_li != NULL ) {
1882                         BackendDB               db = *be;
1883
1884                         db.bd_info = lback;
1885                         db.be_private = lc->lc_common_li;
1886
1887                         rc = func( &db, NULL );
1888
1889                         if ( rc != 0 ) {
1890                                 return rc;
1891                         }
1892
1893                         if ( lc->lc_lai.lai_tree != NULL ) {
1894                                 ldap_chain_db_apply_t   lca;
1895
1896                                 lca.be = &db;
1897                                 lca.func = func;
1898
1899                                 rc = avl_apply( lc->lc_lai.lai_tree,
1900                                         ldap_chain_db_apply, (void *)&lca,
1901                                         1, AVL_INORDER ) != AVL_NOMORE;
1902                         }
1903                 }
1904         }
1905
1906         return rc;
1907 }
1908
1909 static int
1910 ldap_chain_db_open(
1911         BackendDB       *be,
1912         ConfigReply     *cr )
1913 {
1914         slap_overinst   *on = (slap_overinst *) be->bd_info;
1915         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1916         slap_mask_t     monitoring;
1917         int             rc = 0;
1918
1919 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1920         rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1921         if ( rc != 0 ) {
1922                 return rc;
1923         }
1924 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1925
1926         if ( lc->lc_common_li == NULL ) {
1927                 void    *be_private = be->be_private;
1928                 ldap_chain_db_init_common( be );
1929                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1930                 be->be_private = be_private;
1931         }
1932
1933         /* filter out and restore monitoring */
1934         monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1935         SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1936         rc = ldap_chain_db_func( be, db_open );
1937         SLAP_DBFLAGS( be ) |= monitoring;
1938
1939         return rc;
1940 }
1941
1942 static int
1943 ldap_chain_db_close(
1944         BackendDB       *be,
1945         ConfigReply     *cr )
1946 {
1947         return ldap_chain_db_func( be, db_close );
1948 }
1949
1950 static int
1951 ldap_chain_db_destroy(
1952         BackendDB       *be,
1953         ConfigReply     *cr )
1954 {
1955         slap_overinst   *on = (slap_overinst *) be->bd_info;
1956         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1957
1958         int             rc;
1959
1960         rc = ldap_chain_db_func( be, db_destroy );
1961
1962         if ( lc ) {
1963                 avl_free( lc->lc_lai.lai_tree, NULL );
1964                 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
1965                 ch_free( lc );
1966         }
1967
1968         return rc;
1969 }
1970
1971 /*
1972  * inits one instance of the slapd-ldap backend, and stores
1973  * the private info in be_private of the arg
1974  */
1975 static int
1976 ldap_chain_db_init_common(
1977         BackendDB       *be )
1978 {
1979         BackendInfo     *bi = be->bd_info;
1980         ldapinfo_t      *li;
1981         int             rc;
1982
1983         be->bd_info = lback;
1984         be->be_private = NULL;
1985         rc = lback->bi_db_init( be, NULL );
1986         if ( rc != 0 ) {
1987                 return rc;
1988         }
1989         li = (ldapinfo_t *)be->be_private;
1990         li->li_urllist_f = NULL;
1991         li->li_urllist_p = NULL;
1992
1993         be->bd_info = bi;
1994
1995         return 0;
1996 }
1997
1998 /*
1999  * inits one instance of the slapd-ldap backend, stores
2000  * the private info in be_private of the arg and fills
2001  * selected fields with data from the template.
2002  *
2003  * NOTE: add checks about the other fields of the template,
2004  * which are ignored and SHOULD NOT be configured by the user.
2005  */
2006 static int
2007 ldap_chain_db_init_one(
2008         BackendDB       *be )
2009 {
2010         slap_overinst   *on = (slap_overinst *)be->bd_info;
2011         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
2012
2013         BackendInfo     *bi = be->bd_info;
2014         ldapinfo_t      *li;
2015
2016         slap_op_t       t;
2017
2018         be->bd_info = lback;
2019         be->be_private = NULL;
2020         t = lback->bi_db_init( be, NULL );
2021         if ( t != 0 ) {
2022                 return t;
2023         }
2024         li = (ldapinfo_t *)be->be_private;
2025         li->li_urllist_f = NULL;
2026         li->li_urllist_p = NULL;
2027
2028         /* copy common data */
2029         li->li_nretries = lc->lc_common_li->li_nretries;
2030         li->li_flags = lc->lc_common_li->li_flags;
2031         li->li_version = lc->lc_common_li->li_version;
2032         for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2033                 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2034         }
2035         be->bd_info = bi;
2036
2037         return 0;
2038 }
2039
2040 static int
2041 ldap_chain_db_open_one(
2042         BackendDB       *be )
2043 {
2044         if ( SLAP_DBMONITORING( be ) ) {
2045                 ldapinfo_t      *li = (ldapinfo_t *)be->be_private;
2046
2047                 if ( li->li_uri == NULL ) {
2048                         ber_str2bv( "cn=Common Connections", 0, 1,
2049                                 &li->li_monitor_info.lmi_rdn );
2050
2051                 } else {
2052                         char            *ptr;
2053
2054                         li->li_monitor_info.lmi_rdn.bv_len
2055                                 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2056                         ptr = li->li_monitor_info.lmi_rdn.bv_val
2057                                 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
2058                         ptr = lutil_strcopy( ptr, "cn=" );
2059                         ptr = lutil_strcopy( ptr, li->li_uri );
2060                         ptr[ 0 ] = '\0';
2061                 }
2062         }
2063
2064         return lback->bi_db_open( be, NULL );
2065 }
2066
2067 typedef struct ldap_chain_conn_apply_t {
2068         BackendDB       *be;
2069         Connection      *conn;
2070 } ldap_chain_conn_apply_t;
2071
2072 static int
2073 ldap_chain_conn_apply( void *datum, void *arg )
2074 {
2075         ldapinfo_t              *li = (ldapinfo_t *)datum;
2076         ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2077
2078         lca->be->be_private = (void *)li;
2079
2080         return lback->bi_connection_destroy( lca->be, lca->conn );
2081 }
2082
2083 static int
2084 ldap_chain_connection_destroy(
2085         BackendDB *be,
2086         Connection *conn
2087 )
2088 {
2089         slap_overinst           *on = (slap_overinst *) be->bd_info;
2090         ldap_chain_t            *lc = (ldap_chain_t *)on->on_bi.bi_private;
2091         void                    *private = be->be_private;
2092         ldap_chain_conn_apply_t lca;
2093         int                     rc;
2094
2095         be->be_private = NULL;
2096         lca.be = be;
2097         lca.conn = conn;
2098         ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2099         rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2100                 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2101         ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2102         be->be_private = private;
2103
2104         return rc;
2105 }
2106
2107 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2108 static int
2109 ldap_chain_parse_ctrl(
2110         Operation       *op,
2111         SlapReply       *rs,
2112         LDAPControl     *ctrl )
2113 {
2114         ber_tag_t       tag;
2115         BerElement      *ber;
2116         ber_int_t       mode,
2117                         behavior;
2118
2119         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2120                 rs->sr_text = "Chaining behavior control specified multiple times";
2121                 return LDAP_PROTOCOL_ERROR;
2122         }
2123
2124         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2125                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2126                 return LDAP_PROTOCOL_ERROR;
2127         }
2128
2129         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2130                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2131
2132         } else {
2133                 ber_len_t       len;
2134
2135                 /* Parse the control value
2136                  *      ChainingBehavior ::= SEQUENCE { 
2137                  *           resolveBehavior         Behavior OPTIONAL, 
2138                  *           continuationBehavior    Behavior OPTIONAL } 
2139                  *                             
2140                  *      Behavior :: = ENUMERATED { 
2141                  *           chainingPreferred       (0), 
2142                  *           chainingRequired        (1), 
2143                  *           referralsPreferred      (2), 
2144                  *           referralsRequired       (3) } 
2145                  */
2146
2147                 ber = ber_init( &ctrl->ldctl_value );
2148                 if( ber == NULL ) {
2149                         rs->sr_text = "internal error";
2150                         return LDAP_OTHER;
2151                 }
2152
2153                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2154                 /* FIXME: since the whole SEQUENCE is optional,
2155                  * should we accept no enumerations at all? */
2156                 if ( tag != LBER_ENUMERATED ) {
2157                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2158                         return LDAP_PROTOCOL_ERROR;
2159                 }
2160
2161                 switch ( behavior ) {
2162                 case LDAP_CHAINING_PREFERRED:
2163                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2164                         break;
2165
2166                 case LDAP_CHAINING_REQUIRED:
2167                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2168                         break;
2169
2170                 case LDAP_REFERRALS_PREFERRED:
2171                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2172                         break;
2173
2174                 case LDAP_REFERRALS_REQUIRED:
2175                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2176                         break;
2177
2178                 default:
2179                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2180                         return LDAP_PROTOCOL_ERROR;
2181                 }
2182
2183                 tag = ber_peek_tag( ber, &len );
2184                 if ( tag == LBER_ENUMERATED ) {
2185                         tag = ber_scanf( ber, "e", &behavior );
2186                         if ( tag == LBER_ERROR ) {
2187                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2188                                 return LDAP_PROTOCOL_ERROR;
2189                         }
2190                 }
2191
2192                 if ( tag == LBER_DEFAULT ) {
2193                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
2194
2195                 } else {
2196                         switch ( behavior ) {
2197                         case LDAP_CHAINING_PREFERRED:
2198                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2199                                 break;
2200
2201                         case LDAP_CHAINING_REQUIRED:
2202                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2203                                 break;
2204
2205                         case LDAP_REFERRALS_PREFERRED:
2206                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2207                                 break;
2208
2209                         case LDAP_REFERRALS_REQUIRED:
2210                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2211                                 break;
2212
2213                         default:
2214                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2215                                 return LDAP_PROTOCOL_ERROR;
2216                         }
2217                 }
2218
2219                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2220                         rs->sr_text = "Chaining behavior control: decoding error";
2221                         return LDAP_PROTOCOL_ERROR;
2222                 }
2223
2224                 (void) ber_free( ber, 1 );
2225         }
2226
2227         op->o_chaining = mode | ( ctrl->ldctl_iscritical
2228                         ? SLAP_CONTROL_CRITICAL
2229                         : SLAP_CONTROL_NONCRITICAL );
2230
2231         return LDAP_SUCCESS;
2232 }
2233 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2234
2235 int
2236 chain_initialize( void )
2237 {
2238         int rc;
2239
2240         /* Make sure we don't exceed the bits reserved for userland */
2241         config_check_userland( CH_LAST );
2242
2243 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2244         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2245                         /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2246                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
2247         if ( rc != LDAP_SUCCESS ) {
2248                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2249                         "unable to register chaining behavior control: %d.\n",
2250                         rc, 0, 0 );
2251                 return rc;
2252         }
2253 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2254
2255         ldapchain.on_bi.bi_type = "chain";
2256         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2257         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2258         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2259         ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2260         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2261
2262         ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2263
2264         ldapchain.on_response = ldap_chain_response;
2265
2266         ldapchain.on_bi.bi_cf_ocs = chainocs;
2267
2268         rc = config_register_schema( chaincfg, chainocs );
2269         if ( rc ) {
2270                 return rc;
2271         }
2272
2273         return overlay_register( &ldapchain );
2274 }
2275