]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
ITS#7381 more for prev commit
[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-2013 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         return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
229 }
230
231 static int
232 ldap_chain_uri_dup( void *c1, void *c2 )
233 {
234         ldapinfo_t      *li1 = (ldapinfo_t *)c1;
235         ldapinfo_t      *li2 = (ldapinfo_t *)c2;
236
237         assert( li1->li_bvuri != NULL );
238         assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
239         assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
240
241         assert( li2->li_bvuri != NULL );
242         assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
243         assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
244
245         if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
246                 return -1;
247         }
248
249         return 0;
250 }
251
252 /*
253  * Search specific response that strips entryDN from entries
254  */
255 static int
256 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
257 {
258         ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
259
260         assert( op->o_tag == LDAP_REQ_SEARCH );
261
262         /* if in error, don't proceed any further */
263         if ( lb->lb_status == LDAP_CH_ERR ) {
264                 return 0;
265         }
266
267         if ( rs->sr_type == REP_SEARCH ) {
268                 Attribute       **ap = &rs->sr_entry->e_attrs;
269
270                 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
271                         /* will be generated later by frontend
272                          * (a cleaner solution would be that
273                          * the frontend checks if it already exists */
274                         if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
275                         {
276                                 Attribute *a = *ap;
277
278                                 *ap = (*ap)->a_next;
279                                 attr_free( a );
280
281                                 /* there SHOULD be one only! */
282                                 break;
283                         }
284                 }
285
286                 /* tell the frontend not to add generated
287                  * operational attributes */
288                 rs->sr_flags |= REP_NO_OPERATIONALS;
289                 
290                 return SLAP_CB_CONTINUE;
291
292         } else if ( rs->sr_type == REP_SEARCHREF ) {
293                 /* if we get it here, it means the library was unable
294                  * to chase the referral... */
295                 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
296                         rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
297                 }
298
299 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
300                 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
301                         switch ( get_continuationBehavior( op ) ) {
302                         case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
303                                 lb->lb_status = LDAP_CH_ERR;
304                                 return rs->sr_err = LDAP_X_CANNOT_CHAIN;
305
306                         default:
307                                 break;
308                         }
309                 }
310 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
311                 return SLAP_CB_CONTINUE;
312
313         } else if ( rs->sr_type == REP_RESULT ) {
314                 if ( rs->sr_err == LDAP_REFERRAL
315                         && lb->lb_depth < lb->lb_lc->lc_max_depth
316                         && rs->sr_ref != NULL )
317                 {
318                         rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
319                 }
320
321                 /* back-ldap tried to send result */
322                 lb->lb_status = LDAP_CH_RES;
323         }
324
325         return 0;
326 }
327
328 /*
329  * Dummy response that simply traces if back-ldap tried to send 
330  * anything to the client
331  */
332 static int
333 ldap_chain_cb_response( Operation *op, SlapReply *rs )
334 {
335         ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
336
337         /* if in error, don't proceed any further */
338         if ( lb->lb_status == LDAP_CH_ERR ) {
339                 return 0;
340         }
341
342         if ( rs->sr_type == REP_RESULT ) {
343 retry:;
344                 switch ( rs->sr_err ) {
345                 case LDAP_COMPARE_TRUE:
346                 case LDAP_COMPARE_FALSE:
347                         if ( op->o_tag != LDAP_REQ_COMPARE ) {
348                                 return rs->sr_err;
349                         }
350                         /* fallthru */
351
352                 case LDAP_SUCCESS:
353                         lb->lb_status = LDAP_CH_RES;
354                         break;
355
356                 case LDAP_REFERRAL:
357                         if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
358                                 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
359                                 goto retry;
360                         }
361
362 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
363                         if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
364                                 switch ( get_continuationBehavior( op ) ) {
365                                 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
366                                         lb->lb_status = LDAP_CH_ERR;
367                                         return rs->sr_err = LDAP_X_CANNOT_CHAIN;
368
369                                 default:
370                                         break;
371                                 }
372                         }
373 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
374                         break;
375
376                 default:
377                         return rs->sr_err;
378                 }
379
380         } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
381         {
382                 /* strip the entryDN attribute, but keep returning results */
383                 (void)ldap_chain_cb_search_response( op, rs );
384         }
385
386         return SLAP_CB_CONTINUE;
387 }
388
389 static int
390 ldap_chain_op(
391         Operation       *op,
392         SlapReply       *rs,
393         BI_op_func      *op_f,
394         BerVarray       ref,
395         int             depth )
396 {
397         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
398         ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
399         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
400         struct berval   odn = op->o_req_dn,
401                         ondn = op->o_req_ndn;
402         ldapinfo_t      li = { 0 }, *lip = NULL;
403         struct berval   bvuri[ 2 ] = { { 0 } };
404
405         /* NOTE: returned if ref is empty... */
406         int             rc = LDAP_OTHER,
407                         first_rc;
408
409 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
410         LDAPControl     **ctrls = NULL;
411         
412         (void)chaining_control_add( lc, op, &ctrls );
413 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
414
415         li.li_bvuri = bvuri;
416         first_rc = -1;
417         for ( ; !BER_BVISNULL( ref ); ref++ ) {
418                 SlapReply       rs2 = { 0 };
419                 LDAPURLDesc     *srv = NULL;
420                 req_search_s    save_oq_search = op->oq_search,
421                                 tmp_oq_search = { 0 };
422                 struct berval   dn = BER_BVNULL,
423                                 pdn = odn,
424                                 ndn = ondn;
425                 char            *filter = NULL;
426                 int             temporary = 0;
427                 int             free_dn = 0;
428                         
429                 /* We're setting the URI of the first referral;
430                  * what if there are more?
431
432 Document: RFC 4511
433
434 4.1.10. Referral 
435    ...
436    If the client wishes to progress the operation, it MUST follow the 
437    referral by contacting one of the supported services. If multiple 
438    URIs are present, the client assumes that any supported URI may be 
439    used to progress the operation. 
440
441                  * so we actually need to follow exactly one,
442                  * and we can assume any is fine.
443                  */
444         
445                 /* parse reference and use 
446                  * proto://[host][:port]/ only */
447                 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
448                 if ( rc != LDAP_URL_SUCCESS ) {
449                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
450                                 op->o_log_prefix, ref->bv_val, 0 );
451
452                         /* try next */
453                         rc = LDAP_OTHER;
454                         continue;
455                 }
456
457                 if ( op->o_tag == LDAP_REQ_SEARCH ) {
458                         if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
459                                 /* RFC 4511: if scope is present, use it */
460                                 tmp_oq_search.rs_scope = srv->lud_scope;
461
462                         } else {
463                                 /* RFC 4511: if scope is absent, use original */
464                                 tmp_oq_search.rs_scope = op->ors_scope;
465                         }
466                 }
467
468                 rc = LDAP_SUCCESS;
469                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
470                 dn.bv_val = srv->lud_dn;
471                 filter = srv->lud_filter;
472
473                 /* normalize DN */
474                 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
475                         if ( srv->lud_dn == NULL ) {
476                                 srv->lud_dn = "";
477                         }
478
479                 } else {
480                         ber_str2bv( srv->lud_dn, 0, 0, &dn );
481                         rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
482                         if ( rc == LDAP_SUCCESS ) {
483                                 /* remove DN essentially because later on 
484                                  * ldap_initialize() will parse the URL 
485                                  * as a comma-separated URL list */
486                                 srv->lud_dn = "";
487                                 free_dn = 1;
488                         }
489                 }
490
491                 /* prepare filter */
492                 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
493                         /* filter */
494                         if ( srv->lud_filter != NULL
495                                 && srv->lud_filter[0] != '\0'
496                                 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
497                         {
498                                 /* RFC 4511: if filter is present, use it;
499                                  * otherwise, use original */
500                                 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
501                                 if ( tmp_oq_search.rs_filter != NULL ) {
502                                         filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
503
504                                 } else {
505                                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
506                                                 op->o_log_prefix, ref->bv_val, srv->lud_filter );
507                                         rc = LDAP_OTHER;
508                                 }
509                         }
510                 }
511                 srv->lud_filter = NULL;
512
513                 if ( rc == LDAP_SUCCESS ) {
514                         li.li_uri = ldap_url_desc2str( srv );
515                 }
516
517                 srv->lud_dn = dn.bv_val;
518                 srv->lud_filter = filter;
519                 ldap_free_urldesc( srv );
520
521                 if ( rc != LDAP_SUCCESS ) {
522                         /* try next */
523                         rc = LDAP_OTHER;
524                         continue;
525                 }
526
527                 if ( li.li_uri == NULL ) {
528                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
529                                 op->o_log_prefix, ref->bv_val, 0 );
530
531                         /* try next */
532                         rc = LDAP_OTHER;
533                         goto further_cleanup;
534                 }
535
536                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
537                         op->o_log_prefix, ref->bv_val, li.li_uri );
538
539                 op->o_req_dn = pdn;
540                 op->o_req_ndn = ndn;
541
542                 if ( op->o_tag == LDAP_REQ_SEARCH ) {
543                         op->ors_scope = tmp_oq_search.rs_scope;
544                         if ( tmp_oq_search.rs_filter != NULL ) {
545                                 op->ors_filter = tmp_oq_search.rs_filter;
546                                 op->ors_filterstr = tmp_oq_search.rs_filterstr;
547                         }
548                 }
549
550                 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
551
552                 /* Searches for a ldapinfo in the avl tree */
553                 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
554                 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 
555                         (caddr_t)&li, ldap_chain_uri_cmp );
556                 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
557
558                 if ( lip != NULL ) {
559                         op->o_bd->be_private = (void *)lip;
560
561                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
562                                 op->o_log_prefix, ref->bv_val, li.li_uri );
563
564                 } else {
565                         rc = ldap_chain_db_init_one( op->o_bd );
566                         if ( rc != 0 ) {
567                                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
568                                         op->o_log_prefix, ref->bv_val, li.li_uri );
569                                 goto cleanup;
570                         }
571                         lip = (ldapinfo_t *)op->o_bd->be_private;
572                         lip->li_uri = li.li_uri;
573                         lip->li_bvuri = bvuri;
574                         rc = ldap_chain_db_open_one( op->o_bd );
575                         if ( rc != 0 ) {
576                                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
577                                         op->o_log_prefix, ref->bv_val, li.li_uri );
578                                 lip->li_uri = NULL;
579                                 lip->li_bvuri = NULL;
580                                 (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
581                                 goto cleanup;
582                         }
583
584                         if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
585                                 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
586                                 if ( avl_insert( &lc->lc_lai.lai_tree,
587                                         (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
588                                 {
589                                         /* someone just inserted another;
590                                          * don't bother, use this and then
591                                          * just free it */
592                                         temporary = 1;
593                                 }
594                                 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
595
596                         } else {
597                                 temporary = 1;
598                         }
599
600                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
601                                 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
602                 }
603
604                 lb->lb_op_f = op_f;
605                 lb->lb_depth = depth + 1;
606
607                 rc = op_f( op, &rs2 );
608
609                 /* note the first error */
610                 if ( first_rc == -1 ) {
611                         first_rc = rc;
612                 }
613
614 cleanup:;
615                 ldap_memfree( li.li_uri );
616                 li.li_uri = NULL;
617
618                 if ( temporary ) {
619                         lip->li_uri = NULL;
620                         lip->li_bvuri = NULL;
621                         (void)ldap_chain_db_close_one( op->o_bd );
622                         (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
623                 }
624
625 further_cleanup:;
626                 if ( free_dn ) {
627                         op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
628                         op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
629                 }
630         
631                 if ( op->o_tag == LDAP_REQ_SEARCH ) {   
632                         if ( tmp_oq_search.rs_filter != NULL ) {
633                                 filter_free_x( op, tmp_oq_search.rs_filter, 1 );
634                         }
635
636                         if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
637                                 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
638                         }
639
640                         op->oq_search = save_oq_search;
641                 }
642
643                 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
644                         *rs = rs2;
645                         break;
646                 }
647
648                 rc = rs2.sr_err;
649         }
650
651         op->o_req_dn = odn;
652         op->o_req_ndn = ondn;
653
654 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
655         (void)chaining_control_remove( op, &ctrls );
656 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
657
658         if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
659                 rc = first_rc;
660         }
661
662         return rc;
663 }
664
665 static int
666 ldap_chain_search(
667         Operation       *op,
668         SlapReply       *rs,
669         BerVarray       ref,
670         int             depth )
671
672 {
673         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
674         ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
675         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
676         ldapinfo_t      li = { 0 }, *lip = NULL;
677         struct berval   bvuri[ 2 ] = { { 0 } };
678
679         struct berval   odn = op->o_req_dn,
680                         ondn = op->o_req_ndn;
681         Entry           *save_entry = rs->sr_entry;
682         slap_mask_t     save_flags = rs->sr_flags;
683
684         int             rc = LDAP_OTHER,
685                         first_rc = -1;
686
687 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
688         LDAPControl     **ctrls = NULL;
689         
690         (void)chaining_control_add( lc, op, &ctrls );
691 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
692
693         assert( rs->sr_type == REP_SEARCHREF );
694
695         rs->sr_type = REP_SEARCH;
696
697         /* if we parse the URI then by no means 
698          * we can cache stuff or reuse connections, 
699          * because in back-ldap there's no caching
700          * based on the URI value, which is supposed
701          * to be set once for all (correct?) */
702         li.li_bvuri = bvuri;
703         for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
704                 SlapReply       rs2 = { REP_RESULT };
705                 LDAPURLDesc     *srv;
706                 req_search_s    save_oq_search = op->oq_search,
707                                 tmp_oq_search = { 0 };
708                 struct berval   dn,
709                                 pdn = op->o_req_dn,
710                                 ndn = op->o_req_ndn;
711                 char            *filter = NULL;
712                 int             temporary = 0;
713                 int             free_dn = 0;
714
715                 /* parse reference and use
716                  * proto://[host][:port]/ only */
717                 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
718                 if ( rc != LDAP_URL_SUCCESS ) {
719                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
720                                 op->o_log_prefix, ref->bv_val, 0 );
721
722                         /* try next */
723                         rs->sr_err = LDAP_OTHER;
724                         continue;
725                 }
726
727                 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
728                         /* RFC 4511: if scope is present, use it */
729                         tmp_oq_search.rs_scope = srv->lud_scope;
730
731                 } else {
732                         /* RFC 4511: if scope is absent, use original */
733                         /* Section 4.5.3: if scope is onelevel, use base */
734                         if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
735                                 tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
736                         else
737                                 tmp_oq_search.rs_scope = op->ors_scope;
738                 }
739
740                 rc = LDAP_SUCCESS;
741                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
742                 dn.bv_val = srv->lud_dn;
743                 filter = srv->lud_filter;
744
745                 /* normalize DN */
746                 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
747                         if ( srv->lud_dn == NULL ) {
748                                 srv->lud_dn = "";
749                         }
750
751                         if ( save_entry != NULL ) {
752                                 /* use the "right" DN, if available */
753                                 pdn = save_entry->e_name;
754                                 ndn = save_entry->e_nname;
755                         } /* else leave the original req DN in place, if any RFC 4511 */
756                         
757                 } else {
758                         /* RFC 4511: if DN is present, use it */
759                         ber_str2bv( srv->lud_dn, 0, 0, &dn );
760                         rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
761                         if ( rc == LDAP_SUCCESS ) {
762                                 /* remove DN essentially because later on 
763                                  * ldap_initialize() will parse the URL 
764                                  * as a comma-separated URL list */
765                                 srv->lud_dn = "";
766                                 free_dn = 1;
767                         }
768                 }
769
770                 /* prepare filter */
771                 if ( rc == LDAP_SUCCESS ) {
772                         /* filter */
773                         if ( srv->lud_filter != NULL
774                                 && srv->lud_filter[0] != '\0'
775                                 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
776                         {
777                                 /* RFC 4511: if filter is present, use it;
778                                  * otherwise, use original */
779                                 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
780                                 if ( tmp_oq_search.rs_filter != NULL ) {
781                                         filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
782
783                                 } else {
784                                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
785                                                 op->o_log_prefix, ref->bv_val, srv->lud_filter );
786                                         rc = LDAP_OTHER;
787                                 }
788                         }
789                 }
790                 srv->lud_filter = NULL;
791
792                 if ( rc == LDAP_SUCCESS ) {
793                         li.li_uri = ldap_url_desc2str( srv );
794                 }
795
796                 srv->lud_dn = dn.bv_val;
797                 srv->lud_filter = filter;
798                 ldap_free_urldesc( srv );
799
800                 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
801                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
802                                 op->o_log_prefix, ref->bv_val, 0 );
803
804                         /* try next */
805                         rc = LDAP_OTHER;
806                         goto further_cleanup;
807                 }
808
809                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
810                         op->o_log_prefix, ref->bv_val, li.li_uri );
811
812                 op->o_req_dn = pdn;
813                 op->o_req_ndn = ndn;
814                 op->ors_scope = tmp_oq_search.rs_scope;
815                 if ( tmp_oq_search.rs_filter != NULL ) {
816                         op->ors_filter = tmp_oq_search.rs_filter;
817                         op->ors_filterstr = tmp_oq_search.rs_filterstr;
818                 }
819
820                 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
821
822                 /* Searches for a ldapinfo in the avl tree */
823                 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
824                 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 
825                         (caddr_t)&li, ldap_chain_uri_cmp );
826                 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
827
828                 if ( lip != NULL ) {
829                         op->o_bd->be_private = (void *)lip;
830
831                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
832                                 op->o_log_prefix, ref->bv_val, li.li_uri );
833
834                 } else {
835                         /* if none is found, create a temporary... */
836                         rc = ldap_chain_db_init_one( op->o_bd );
837                         if ( rc != 0 ) {
838                                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
839                                         op->o_log_prefix, ref->bv_val, li.li_uri );
840                                 goto cleanup;
841                         }
842                         lip = (ldapinfo_t *)op->o_bd->be_private;
843                         lip->li_uri = li.li_uri;
844                         lip->li_bvuri = bvuri;
845                         rc = ldap_chain_db_open_one( op->o_bd );
846                         if ( rc != 0 ) {
847                                 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
848                                         op->o_log_prefix, ref->bv_val, li.li_uri );
849                                 lip->li_uri = NULL;
850                                 lip->li_bvuri = NULL;
851                                 (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
852                                 goto cleanup;
853                         }
854
855                         if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
856                                 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
857                                 if ( avl_insert( &lc->lc_lai.lai_tree,
858                                         (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
859                                 {
860                                         /* someone just inserted another;
861                                          * don't bother, use this and then
862                                          * just free it */
863                                         temporary = 1;
864                                 }
865                                 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
866
867                         } else {
868                                 temporary = 1;
869                         }
870
871                         Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
872                                 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
873                 }
874
875                 lb->lb_op_f = lback->bi_op_search;
876                 lb->lb_depth = depth + 1;
877
878                 /* FIXME: should we also copy filter and scope?
879                  * according to RFC3296, no */
880                 rc = lback->bi_op_search( op, &rs2 );
881                 if ( first_rc == -1 ) {
882                         first_rc = rc;
883                 }
884
885 cleanup:;
886                 ldap_memfree( li.li_uri );
887                 li.li_uri = NULL;
888
889                 if ( temporary ) {
890                         lip->li_uri = NULL;
891                         lip->li_bvuri = NULL;
892                         (void)ldap_chain_db_close_one( op->o_bd );
893                         (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
894                 }
895                 
896 further_cleanup:;
897                 if ( free_dn ) {
898                         op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
899                         op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
900                 }
901
902                 op->o_req_dn = odn;
903                 op->o_req_ndn = ondn;
904
905                 if ( tmp_oq_search.rs_filter != NULL ) {
906                         filter_free_x( op, tmp_oq_search.rs_filter, 1 );
907                 }
908
909                 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
910                         slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
911                 }
912
913                 op->oq_search = save_oq_search;
914                 
915                 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
916                         *rs = rs2;
917                         break;
918                 }
919
920                 rc = rs2.sr_err;
921         }
922
923 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
924         (void)chaining_control_remove( op, &ctrls );
925 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
926
927         op->o_req_dn = odn;
928         op->o_req_ndn = ondn;
929         rs->sr_type = REP_SEARCHREF;
930         rs->sr_entry = save_entry;
931         rs->sr_flags = save_flags;
932
933         if ( rc != LDAP_SUCCESS ) {
934                 /* couldn't chase any of the referrals */
935                 if ( first_rc != -1 ) {
936                         rc = first_rc;
937
938                 } else {
939                         rc = SLAP_CB_CONTINUE;
940                 }
941         }
942
943         return rc;
944 }
945
946 static int
947 ldap_chain_response( Operation *op, SlapReply *rs )
948 {
949         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
950         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
951         BackendDB       db, *bd = op->o_bd;
952         ldap_chain_cb_t lb = { 0 };
953         slap_callback   *sc = op->o_callback,
954                         sc2 = { 0 };
955         int             rc = 0;
956         const char      *text = NULL;
957         const char      *matched;
958         BerVarray       ref;
959         struct berval   ndn = op->o_ndn;
960
961         int             sr_err = rs->sr_err;
962         slap_reply_t    sr_type = rs->sr_type;
963 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
964         slap_mask_t     chain_mask = 0;
965         ber_len_t       chain_shift = 0;
966 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
967
968         if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
969                 return SLAP_CB_CONTINUE;
970         }
971
972 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
973         if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
974                 switch ( get_resolveBehavior( op ) ) {
975                 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
976                 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
977                         return SLAP_CB_CONTINUE;
978
979                 default:
980                         chain_mask = SLAP_CH_RESOLVE_MASK;
981                         chain_shift = SLAP_CH_RESOLVE_SHIFT;
982                         break;
983                 }
984
985         } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
986                 switch ( get_continuationBehavior( op ) ) {
987                 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
988                 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
989                         return SLAP_CB_CONTINUE;
990
991                 default:
992                         chain_mask = SLAP_CH_CONTINUATION_MASK;
993                         chain_shift = SLAP_CH_CONTINUATION_SHIFT;
994                         break;
995                 }
996         }
997 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
998
999         /*
1000          * TODO: add checks on who/when chain operations; e.g.:
1001          *   a) what identities are authorized
1002          *   b) what request DN (e.g. only chain requests rooted at <DN>)
1003          *   c) what referral URIs
1004          *   d) what protocol scheme (e.g. only ldaps://)
1005          *   e) what ssf
1006          */
1007
1008         db = *op->o_bd;
1009         SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1010         op->o_bd = &db;
1011
1012         text = rs->sr_text;
1013         rs->sr_text = NULL;
1014         matched = rs->sr_matched;
1015         rs->sr_matched = NULL;
1016         ref = rs->sr_ref;
1017         rs->sr_ref = NULL;
1018
1019         /* we need this to know if back-ldap returned any result */
1020         lb.lb_lc = lc;
1021         sc2.sc_next = sc->sc_next;
1022         sc2.sc_private = &lb;
1023         sc2.sc_response = ldap_chain_cb_response;
1024         op->o_callback = &sc2;
1025
1026         /* Chaining can be performed by a privileged user on behalf
1027          * of normal users, using the ProxyAuthz control, by exploiting
1028          * the identity assertion feature of back-ldap; see idassert-*
1029          * directives in slapd-ldap(5).
1030          *
1031          * FIXME: the idassert-authcDN is one, will it be fine regardless
1032          * of the URI we obtain from the referral?
1033          */
1034
1035         switch ( op->o_tag ) {
1036         case LDAP_REQ_BIND: {
1037                 struct berval   rndn = op->o_req_ndn;
1038                 Connection      *conn = op->o_conn;
1039
1040                 /* FIXME: can we really get a referral for binds? */
1041                 op->o_req_ndn = slap_empty_bv;
1042                 op->o_conn = NULL;
1043                 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1044                 op->o_req_ndn = rndn;
1045                 op->o_conn = conn;
1046                 }
1047                 break;
1048
1049         case LDAP_REQ_ADD:
1050                 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1051                 break;
1052
1053         case LDAP_REQ_DELETE:
1054                 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1055                 break;
1056
1057         case LDAP_REQ_MODRDN:
1058                 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1059                 break;
1060
1061         case LDAP_REQ_MODIFY:
1062                 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1063                 break;
1064
1065         case LDAP_REQ_COMPARE:
1066                 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
1067                 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1068                         rc = LDAP_SUCCESS;
1069                 }
1070                 break;
1071
1072         case LDAP_REQ_SEARCH:
1073                 if ( rs->sr_type == REP_SEARCHREF ) {
1074                         sc2.sc_response = ldap_chain_cb_search_response;
1075                         rc = ldap_chain_search( op, rs, ref, 0 );
1076                         
1077                 } else {
1078                         /* we might get here before any database actually 
1079                          * performed a search; in those cases, we need
1080                          * to check limits, to make sure safe defaults
1081                          * are in place */
1082                         if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1083                                 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1084
1085                         } else {
1086                                 rc = SLAP_CB_CONTINUE;
1087                         }
1088                 }
1089                 break;
1090
1091         case LDAP_REQ_EXTENDED:
1092                 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
1093                 /* FIXME: ldap_back_extended() by design 
1094                  * doesn't send result; frontend is expected
1095                  * to send it... */
1096                 /* FIXME: what about chaining? */
1097                 if ( rc != SLAPD_ABANDON ) {
1098                         rs->sr_err = rc;
1099                         send_ldap_extended( op, rs );
1100                         rc = LDAP_SUCCESS;
1101                 }
1102                 lb.lb_status = LDAP_CH_RES;
1103                 break;
1104
1105         default:
1106                 rc = SLAP_CB_CONTINUE;
1107                 break;
1108         }
1109
1110         switch ( rc ) {
1111         case SLAPD_ABANDON:
1112                 goto dont_chain;
1113
1114         case LDAP_SUCCESS:
1115         case LDAP_REFERRAL:
1116                 sr_err = rs->sr_err;
1117                 /* slapd-ldap sent response */
1118                 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1119                         /* FIXME: should we send response? */
1120                         Debug( LDAP_DEBUG_ANY,
1121                                 "%s: ldap_chain_response: "
1122                                 "overlay should have sent result.\n",
1123                                 op->o_log_prefix, 0, 0 );
1124                 }
1125                 break;
1126
1127         default:
1128 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1129                 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1130                         goto cannot_chain;
1131                 }
1132
1133                 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1134                 case LDAP_CHAINING_REQUIRED:
1135 cannot_chain:;
1136                         op->o_callback = NULL;
1137                         send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1138                                 "operation cannot be completed without chaining" );
1139                         goto dont_chain;
1140
1141                 default:
1142 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1143                         if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1144                                 sr_err = rs->sr_err = rc;
1145                                 rs->sr_type = sr_type;
1146
1147                         } else {
1148                                 rc = SLAP_CB_CONTINUE;
1149                                 rs->sr_err = sr_err;
1150                                 rs->sr_type = sr_type;
1151                                 rs->sr_text = text;
1152                                 rs->sr_matched = matched;
1153                                 rs->sr_ref = ref;
1154                         }
1155 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1156                         break;
1157                 }
1158 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1159         }
1160
1161         if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1162                 /* give the remaining callbacks a chance */
1163                 op->o_callback = sc->sc_next;
1164                 rc = rs->sr_err = slap_map_api2result( rs );
1165                 send_ldap_result( op, rs );
1166         }
1167
1168 dont_chain:;
1169         rs->sr_err = sr_err;
1170         rs->sr_type = sr_type;
1171         rs->sr_text = text;
1172         rs->sr_matched = matched;
1173         rs->sr_ref = ref;
1174         op->o_bd = bd;
1175         op->o_callback = sc;
1176         op->o_ndn = ndn;
1177
1178         return rc;
1179 }
1180
1181 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1182 static int
1183 ldap_chain_parse_ctrl(
1184         Operation       *op,
1185         SlapReply       *rs,
1186         LDAPControl     *ctrl );
1187
1188 static int
1189 str2chain( const char *s )
1190 {
1191         if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1192                 return LDAP_CHAINING_PREFERRED;
1193                 
1194         } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1195                 return LDAP_CHAINING_REQUIRED;
1196
1197         } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1198                 return LDAP_REFERRALS_PREFERRED;
1199                 
1200         } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1201                 return LDAP_REFERRALS_REQUIRED;
1202         }
1203
1204         return -1;
1205 }
1206 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1207
1208 /*
1209  * configuration...
1210  */
1211
1212 enum {
1213         CH_CHAINING = 1,
1214         CH_CACHE_URI,
1215         CH_MAX_DEPTH,
1216         CH_RETURN_ERR,
1217
1218         CH_LAST
1219 };
1220
1221 static ConfigDriver chain_cf_gen;
1222 static ConfigCfAdd chain_cfadd;
1223 static ConfigLDAPadd chain_ldadd;
1224 #ifdef SLAP_CONFIG_DELETE
1225 static ConfigLDAPdel chain_lddel;
1226 #endif
1227
1228 static ConfigTable chaincfg[] = {
1229 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1230         { "chain-chaining", "args",
1231                 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1232                 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1233                         "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1234                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1235 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1236         { "chain-cache-uri", "TRUE/FALSE",
1237                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1238                 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1239                         "DESC 'Enables caching of URIs not present in configuration' "
1240                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1241         { "chain-max-depth", "args",
1242                 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1243                 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1244                         "DESC 'max referral depth' "
1245                         "SYNTAX OMsInteger "
1246                         "EQUALITY integerMatch "
1247                         "SINGLE-VALUE )", NULL, NULL },
1248         { "chain-return-error", "TRUE/FALSE",
1249                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1250                 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1251                         "DESC 'Errors are returned instead of the original referral' "
1252                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1253         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1254 };
1255
1256 static ConfigOCs chainocs[] = {
1257         { "( OLcfgOvOc:3.1 "
1258                 "NAME 'olcChainConfig' "
1259                 "DESC 'Chain configuration' "
1260                 "SUP olcOverlayConfig "
1261                 "MAY ( "
1262 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1263                         "olcChainingBehavior $ "
1264 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1265                         "olcChainCacheURI $ "
1266                         "olcChainMaxReferralDepth $ "
1267                         "olcChainReturnError "
1268                         ") )",
1269                 Cft_Overlay, chaincfg, NULL, chain_cfadd },
1270         { "( OLcfgOvOc:3.2 "
1271                 "NAME 'olcChainDatabase' "
1272                 "DESC 'Chain remote server configuration' "
1273                 "AUXILIARY )",
1274                 Cft_Misc, olcDatabaseDummy, chain_ldadd
1275 #ifdef SLAP_CONFIG_DELETE
1276                 , NULL, chain_lddel
1277 #endif
1278         },
1279         { NULL, 0, NULL }
1280 };
1281
1282 static int
1283 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1284 {
1285         slap_overinst           *on;
1286         ldap_chain_t            *lc;
1287
1288         ldapinfo_t              *li;
1289
1290         AttributeDescription    *ad = NULL;
1291         Attribute               *at;
1292         const char              *text;
1293
1294         int                     rc;
1295
1296         if ( p->ce_type != Cft_Overlay
1297                 || !p->ce_bi
1298                 || p->ce_bi->bi_cf_ocs != chainocs )
1299         {
1300                 return LDAP_CONSTRAINT_VIOLATION;
1301         }
1302
1303         on = (slap_overinst *)p->ce_bi;
1304         lc = (ldap_chain_t *)on->on_bi.bi_private;
1305
1306         assert( ca->be == NULL );
1307         ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1308
1309         ca->be->bd_info = (BackendInfo *)on;
1310
1311         rc = slap_str2ad( "olcDbURI", &ad, &text );
1312         assert( rc == LDAP_SUCCESS );
1313
1314         at = attr_find( e->e_attrs, ad );
1315 #if 0
1316         if ( lc->lc_common_li == NULL && at != NULL ) {
1317                 /* FIXME: we should generate an empty default entry
1318                  * if none is supplied */
1319                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1320                         "first underlying database \"%s\" "
1321                         "cannot contain attribute \"%s\".\n",
1322                         e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1323                 rc = LDAP_CONSTRAINT_VIOLATION;
1324                 goto done;
1325
1326         } else
1327 #endif
1328         if ( lc->lc_common_li != NULL && at == NULL ) {
1329                 /* FIXME: we should generate an empty default entry
1330                  * if none is supplied */
1331                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1332                         "subsequent underlying database \"%s\" "
1333                         "must contain attribute \"%s\".\n",
1334                         e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1335                 rc = LDAP_CONSTRAINT_VIOLATION;
1336                 goto done;
1337         }
1338
1339         if ( lc->lc_common_li == NULL ) {
1340                 rc = ldap_chain_db_init_common( ca->be );
1341                 if ( rc != 0 )
1342                         goto fail;
1343                 li = ca->be->be_private;
1344                 lc->lc_common_li = lc->lc_cfg_li = li;
1345
1346         }
1347         rc = ldap_chain_db_init_one( ca->be );
1348
1349         if ( rc != 0 ) {
1350 fail:
1351                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1352                         "unable to init %sunderlying database \"%s\".\n",
1353                         lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1354                 return LDAP_CONSTRAINT_VIOLATION;
1355         }
1356
1357         li = ca->be->be_private;
1358
1359         if ( at ) {
1360                 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1361                 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1362                 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1363                         ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1364                 {
1365                         Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1366                                 "database \"%s\" insert failed.\n",
1367                                 e->e_name.bv_val, 0, 0 );
1368                         rc = LDAP_CONSTRAINT_VIOLATION;
1369                         goto done;
1370                 }
1371         }
1372
1373         ca->ca_private = on;
1374
1375 done:;
1376         if ( rc != LDAP_SUCCESS ) {
1377                 (void)ldap_chain_db_destroy_one( ca->be, NULL );
1378                 ch_free( ca->be );
1379                 ca->be = NULL;
1380         }
1381
1382         return rc;
1383 }
1384
1385 typedef struct ldap_chain_cfadd_apply_t {
1386         Operation       *op;
1387         SlapReply       *rs;
1388         Entry           *p;
1389         ConfigArgs      *ca;
1390         int             count;
1391 } ldap_chain_cfadd_apply_t;
1392
1393 static int
1394 ldap_chain_cfadd_apply( void *datum, void *arg )
1395 {
1396         ldapinfo_t                      *li = (ldapinfo_t *)datum;
1397         ldap_chain_cfadd_apply_t        *lca = (ldap_chain_cfadd_apply_t *)arg;
1398
1399         struct berval                   bv;
1400
1401         /* FIXME: should not hardcode "olcDatabase" here */
1402         bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1403                 "olcDatabase={%d}%s", lca->count, lback->bi_type );
1404         bv.bv_val = lca->ca->cr_msg;
1405
1406         lca->ca->be->be_private = (void *)li;
1407         config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1408                 &bv, lback->bi_cf_ocs, &chainocs[1] );
1409
1410         lca->count++;
1411
1412         return 0;
1413 }
1414
1415 static int
1416 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1417 {
1418         CfEntryInfo     *pe = p->e_private;
1419         slap_overinst   *on = (slap_overinst *)pe->ce_bi;
1420         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1421         void            *priv = (void *)ca->be->be_private;
1422
1423         if ( lback->bi_cf_ocs ) {
1424                 ldap_chain_cfadd_apply_t        lca = { 0 };
1425
1426                 lca.op = op;
1427                 lca.rs = rs;
1428                 lca.p = p;
1429                 lca.ca = ca;
1430                 lca.count = 0;
1431
1432                 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1433
1434                 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1435                         &lca, 1, AVL_INORDER );
1436
1437                 ca->be->be_private = priv;
1438         }
1439
1440         return 0;
1441 }
1442
1443 #ifdef SLAP_CONFIG_DELETE
1444 static int
1445 chain_lddel( CfEntryInfo *ce, Operation *op )
1446 {
1447         CfEntryInfo     *pe = ce->ce_parent;
1448         slap_overinst   *on = (slap_overinst *)pe->ce_bi;
1449         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1450         ldapinfo_t      *li = (ldapinfo_t *) ce->ce_be->be_private;
1451
1452         if ( li != lc->lc_common_li ) {
1453                 if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
1454                         Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. "
1455                                 "\"%s\" not found.\n", li->li_uri, 0, 0 );
1456                         return -1;
1457                 }
1458         } else if ( lc->lc_lai.lai_tree ) {
1459                 Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
1460                         "LDAP database when other databases are still present.\n", 0, 0, 0 );
1461                 return -1;
1462         } else {
1463                 lc->lc_common_li = NULL;
1464         }
1465
1466         ce->ce_be->bd_info = lback;
1467
1468         if ( ce->ce_be->bd_info->bi_db_close ) {
1469                 ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
1470         }
1471         if ( ce->ce_be->bd_info->bi_db_destroy ) {
1472                 ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
1473         }
1474
1475         ch_free(ce->ce_be);
1476         ce->ce_be = NULL;
1477
1478         return LDAP_SUCCESS;
1479 }
1480 #endif /* SLAP_CONFIG_DELETE */
1481
1482 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1483 static slap_verbmasks chaining_mode[] = {
1484         { BER_BVC("referralsRequired"),         LDAP_REFERRALS_REQUIRED },
1485         { BER_BVC("referralsPreferred"),        LDAP_REFERRALS_PREFERRED },
1486         { BER_BVC("chainingRequired"),          LDAP_CHAINING_REQUIRED },
1487         { BER_BVC("chainingPreferred"),         LDAP_CHAINING_PREFERRED },
1488         { BER_BVNULL,                           0 }
1489 };
1490 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1491
1492 static int
1493 chain_cf_gen( ConfigArgs *c )
1494 {
1495         slap_overinst   *on = (slap_overinst *)c->bi;
1496         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1497
1498         int             rc = 0;
1499
1500         if ( c->op == SLAP_CONFIG_EMIT ) {
1501                 switch( c->type ) {
1502 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1503                 case CH_CHAINING: {
1504                         struct berval   resolve = BER_BVNULL,
1505                                         continuation = BER_BVNULL;
1506
1507                         if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1508                                 return 1;
1509                         }
1510
1511                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1512                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1513
1514                         c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1515                                 + STRLENOF( " " )
1516                                 + STRLENOF( "continuation=" ) + continuation.bv_len;
1517                         c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1518                         snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1519                                 "resolve=%s continuation=%s",
1520                                 resolve.bv_val, continuation.bv_val );
1521
1522                         if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1523                                 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1524                                         c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1525                                 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1526                                         " critical", STRLENOF( " critical" ) + 1 );
1527                                 c->value_bv.bv_len += STRLENOF( " critical" );
1528                         }
1529
1530                         break;
1531                 }
1532 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1533
1534                 case CH_CACHE_URI:
1535                         c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1536                         break;
1537
1538                 case CH_MAX_DEPTH:
1539                         c->value_int = lc->lc_max_depth;
1540                         break;
1541
1542                 case CH_RETURN_ERR:
1543                         c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1544                         break;
1545
1546                 default:
1547                         assert( 0 );
1548                         rc = 1;
1549                 }
1550                 return rc;
1551
1552         } else if ( c->op == LDAP_MOD_DELETE ) {
1553                 switch( c->type ) {
1554                 case CH_CHAINING:
1555                         return 1;
1556
1557                 case CH_CACHE_URI:
1558                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1559                         break;
1560
1561                 case CH_MAX_DEPTH:
1562                         c->value_int = 0;
1563                         break;
1564
1565                 case CH_RETURN_ERR:
1566                         lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1567                         break;
1568
1569                 default:
1570                         return 1;
1571                 }
1572                 return rc;
1573         }
1574
1575         switch( c->type ) {
1576         case CH_CHAINING: {
1577 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1578                 char                    **argv = c->argv;
1579                 int                     argc = c->argc;
1580                 BerElementBuffer        berbuf;
1581                 BerElement              *ber = (BerElement *)&berbuf;
1582                 int                     resolve = -1,
1583                                         continuation = -1,
1584                                         iscritical = 0;
1585                 Operation               op = { 0 };
1586                 SlapReply               rs = { 0 };
1587
1588                 lc->lc_chaining_ctrlflag = 0;
1589
1590                 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1591                         if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1592                                 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1593                                 if ( resolve == -1 ) {
1594                                         Debug( LDAP_DEBUG_ANY, "%s: "
1595                                                 "illegal <resolve> value %s "
1596                                                 "in \"chain-chaining>\".\n",
1597                                                 c->log, argv[ 0 ], 0 );
1598                                         return 1;
1599                                 }
1600
1601                         } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1602                                 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1603                                 if ( continuation == -1 ) {
1604                                         Debug( LDAP_DEBUG_ANY, "%s: "
1605                                                 "illegal <continuation> value %s "
1606                                                 "in \"chain-chaining\".\n",
1607                                                 c->log, argv[ 0 ], 0 );
1608                                         return 1;
1609                                 }
1610
1611                         } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1612                                 iscritical = 1;
1613
1614                         } else {
1615                                 Debug( LDAP_DEBUG_ANY, "%s: "
1616                                         "unknown option in \"chain-chaining\".\n",
1617                                         c->log, 0, 0 );
1618                                 return 1;
1619                         }
1620                 }
1621
1622                 if ( resolve != -1 || continuation != -1 ) {
1623                         int     err;
1624
1625                         if ( resolve == -1 ) {
1626                                 /* default */
1627                                 resolve = SLAP_CHAINING_DEFAULT;
1628                         }
1629
1630                         ber_init2( ber, NULL, LBER_USE_DER );
1631
1632                         err = ber_printf( ber, "{e" /* } */, resolve );
1633                         if ( err == -1 ) {
1634                                 ber_free( ber, 1 );
1635                                 Debug( LDAP_DEBUG_ANY, "%s: "
1636                                         "chaining behavior control encoding error!\n",
1637                                         c->log, 0, 0 );
1638                                 return 1;
1639                         }
1640
1641                         if ( continuation > -1 ) {
1642                                 err = ber_printf( ber, "e", continuation );
1643                                 if ( err == -1 ) {
1644                                         ber_free( ber, 1 );
1645                                         Debug( LDAP_DEBUG_ANY, "%s: "
1646                                                 "chaining behavior control encoding error!\n",
1647                                                 c->log, 0, 0 );
1648                                         return 1;
1649                                 }
1650                         }
1651
1652                         err = ber_printf( ber, /* { */ "N}" );
1653                         if ( err == -1 ) {
1654                                 ber_free( ber, 1 );
1655                                 Debug( LDAP_DEBUG_ANY, "%s: "
1656                                         "chaining behavior control encoding error!\n",
1657                                         c->log, 0, 0 );
1658                                 return 1;
1659                         }
1660
1661                         if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1662                                 exit( EXIT_FAILURE );
1663                         }
1664
1665                 } else {
1666                         BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1667                 }
1668
1669                 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1670                 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1671
1672                 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1673                 {
1674                         Debug( LDAP_DEBUG_ANY, "%s: "
1675                                 "unable to parse chaining control%s%s.\n",
1676                                 c->log, rs.sr_text ? ": " : "",
1677                                 rs.sr_text ? rs.sr_text : "" );
1678                         return 1;
1679                 }
1680
1681                 lc->lc_chaining_ctrlflag = op.o_chaining;
1682
1683                 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1684
1685                 rc = 0;
1686 #else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1687                 Debug( LDAP_DEBUG_ANY, "%s: "
1688                         "\"chaining\" control unsupported (ignored).\n",
1689                         c->log, 0, 0 );
1690 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1691                 } break;
1692
1693         case CH_CACHE_URI:
1694                 if ( c->value_int ) {
1695                         lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1696                 } else {
1697                         lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1698                 }
1699                 break;
1700
1701         case CH_MAX_DEPTH:
1702                 if ( c->value_int < 0 ) {
1703                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1704                                 "<%s> invalid max referral depth %d",
1705                                 c->argv[0], c->value_int );
1706                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1707                                 c->log, c->cr_msg, 0 );
1708                         rc = 1;
1709                         break;
1710                 }
1711                 lc->lc_max_depth = c->value_int;
1712
1713         case CH_RETURN_ERR:
1714                 if ( c->value_int ) {
1715                         lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1716                 } else {
1717                         lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1718                 }
1719                 break;
1720
1721         default:
1722                 assert( 0 );
1723                 return 1;
1724         }
1725         return rc;
1726 }
1727
1728 static int
1729 ldap_chain_db_init(
1730         BackendDB *be,
1731         ConfigReply *cr )
1732 {
1733         slap_overinst   *on = (slap_overinst *)be->bd_info;
1734         ldap_chain_t    *lc = NULL;
1735
1736         if ( lback == NULL ) {
1737                 lback = backend_info( "ldap" );
1738
1739                 if ( lback == NULL ) {
1740                         return 1;
1741                 }
1742         }
1743
1744         lc = ch_malloc( sizeof( ldap_chain_t ) );
1745         if ( lc == NULL ) {
1746                 return 1;
1747         }
1748         memset( lc, 0, sizeof( ldap_chain_t ) );
1749         lc->lc_max_depth = 1;
1750         ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1751
1752         on->on_bi.bi_private = (void *)lc;
1753
1754         return 0;
1755 }
1756
1757 static int
1758 ldap_chain_db_config(
1759         BackendDB       *be,
1760         const char      *fname,
1761         int             lineno,
1762         int             argc,
1763         char            **argv )
1764 {
1765         slap_overinst   *on = (slap_overinst *)be->bd_info;
1766         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1767
1768         int             rc = SLAP_CONF_UNKNOWN;
1769
1770         if ( lc->lc_common_li == NULL ) {
1771                 BackendDB db = *be;
1772                 ldap_chain_db_init_common( &db );
1773                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1774         }
1775
1776         /* Something for the chain database? */
1777         if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1778                 char            *save_argv0 = argv[ 0 ];
1779                 BackendDB       db = *be;
1780                 static char     *allowed_argv[] = {
1781                         /* special: put URI here, so in the meanwhile
1782                          * it detects whether a new URI is being provided */
1783                         "uri",
1784                         "nretries",
1785                         "timeout",
1786                         /* flags */
1787                         "tls",
1788                         /* FIXME: maybe rebind-as-user should be allowed
1789                          * only within known URIs... */
1790                         "rebind-as-user",
1791                         "chase-referrals",
1792                         "t-f-support",
1793                         "proxy-whoami",
1794                         NULL
1795                 };
1796                 int             which_argv = -1;
1797
1798                 argv[ 0 ] += STRLENOF( "chain-" );
1799
1800                 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1801                         if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1802                                 break;
1803                         }
1804                 }
1805
1806                 if ( allowed_argv[ which_argv ] == NULL ) {
1807                         which_argv = -1;
1808
1809                         if ( lc->lc_cfg_li == lc->lc_common_li ) {
1810                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1811                                         "\"%s\" only allowed within a URI directive.\n.",
1812                                         fname, lineno, argv[ 0 ] );
1813                                 return 1;
1814                         }
1815                 }
1816
1817                 if ( which_argv == 0 ) {
1818                         rc = ldap_chain_db_init_one( &db );
1819                         if ( rc != 0 ) {
1820                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1821                                         "underlying slapd-ldap initialization failed.\n.",
1822                                         fname, lineno, 0 );
1823                                 return 1;
1824                         }
1825                         lc->lc_cfg_li = db.be_private;
1826                 }
1827
1828                 /* TODO: add checks on what other slapd-ldap(5) args
1829                  * should be put in the template; this is not quite
1830                  * harmful, because attributes that shouldn't don't
1831                  * get actually used, but the user should at least
1832                  * be warned.
1833                  */
1834
1835                 db.bd_info = lback;
1836                 db.be_private = (void *)lc->lc_cfg_li;
1837                 db.be_cf_ocs = lback->bi_cf_ocs;
1838
1839                 rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1840
1841                 argv[ 0 ] = save_argv0;
1842
1843                 if ( which_argv == 0 ) {
1844 private_destroy:;
1845                         if ( rc != 0 ) {
1846                                 db.bd_info = lback;
1847                                 db.be_private = (void *)lc->lc_cfg_li;
1848                                 ldap_chain_db_destroy_one( &db, NULL );
1849                                 lc->lc_cfg_li = NULL;
1850                         } else {
1851                                 if ( lc->lc_cfg_li->li_bvuri == NULL
1852                                         || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1853                                         || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1854                                 {
1855                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1856                                                 "no URI list allowed in slapo-chain.\n",
1857                                                 fname, lineno, 0 );
1858                                         rc = 1;
1859                                         goto private_destroy;
1860                                 }
1861
1862                                 if ( avl_insert( &lc->lc_lai.lai_tree,
1863                                         (caddr_t)lc->lc_cfg_li,
1864                                         ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1865                                 {
1866                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1867                                                 "duplicate URI in slapo-chain.\n",
1868                                                 fname, lineno, 0 );
1869                                         rc = 1;
1870                                         goto private_destroy;
1871                                 }
1872                         }
1873                 }
1874         }
1875
1876         return rc;
1877 }
1878
1879 enum db_which {
1880         db_open = 0,
1881         db_close,
1882         db_destroy,
1883
1884         db_last
1885 };
1886
1887 typedef struct ldap_chain_db_apply_t {
1888         BackendDB       *be;
1889         BI_db_func      *func;
1890 } ldap_chain_db_apply_t;
1891
1892 static int
1893 ldap_chain_db_apply( void *datum, void *arg )
1894 {
1895         ldapinfo_t              *li = (ldapinfo_t *)datum;
1896         ldap_chain_db_apply_t   *lca = (ldap_chain_db_apply_t *)arg;
1897
1898         lca->be->be_private = (void *)li;
1899
1900         return lca->func( lca->be, NULL );
1901 }
1902
1903 static int
1904 ldap_chain_db_func(
1905         BackendDB *be,
1906         enum db_which which
1907 )
1908 {
1909         slap_overinst   *on = (slap_overinst *)be->bd_info;
1910         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1911
1912         int             rc = 0;
1913
1914         if ( lc ) {
1915                 BI_db_func      *func = (&lback->bi_db_open)[ which ];
1916
1917                 if ( func != NULL && lc->lc_common_li != NULL ) {
1918                         BackendDB               db = *be;
1919
1920                         db.bd_info = lback;
1921                         db.be_private = lc->lc_common_li;
1922
1923                         rc = func( &db, NULL );
1924
1925                         if ( rc != 0 ) {
1926                                 return rc;
1927                         }
1928
1929                         if ( lc->lc_lai.lai_tree != NULL ) {
1930                                 ldap_chain_db_apply_t   lca;
1931
1932                                 lca.be = &db;
1933                                 lca.func = func;
1934
1935                                 rc = avl_apply( lc->lc_lai.lai_tree,
1936                                         ldap_chain_db_apply, (void *)&lca,
1937                                         1, AVL_INORDER ) != AVL_NOMORE;
1938                         }
1939                 }
1940         }
1941
1942         return rc;
1943 }
1944
1945 static int
1946 ldap_chain_db_open(
1947         BackendDB       *be,
1948         ConfigReply     *cr )
1949 {
1950         slap_overinst   *on = (slap_overinst *) be->bd_info;
1951         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1952         slap_mask_t     monitoring;
1953         int             rc = 0;
1954
1955 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1956         rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1957         if ( rc != 0 ) {
1958                 return rc;
1959         }
1960 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1961
1962         if ( lc->lc_common_li == NULL ) {
1963                 void    *be_private = be->be_private;
1964                 ldap_chain_db_init_common( be );
1965                 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1966                 be->be_private = be_private;
1967         }
1968
1969         /* filter out and restore monitoring */
1970         monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1971         SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1972         rc = ldap_chain_db_func( be, db_open );
1973         SLAP_DBFLAGS( be ) |= monitoring;
1974
1975         return rc;
1976 }
1977
1978 static int
1979 ldap_chain_db_close(
1980         BackendDB       *be,
1981         ConfigReply     *cr )
1982 {
1983 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1984 #ifdef SLAP_CONFIG_DELETE
1985         overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1986 #endif /* SLAP_CONFIG_DELETE */
1987 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1988         return ldap_chain_db_func( be, db_close );
1989 }
1990
1991 static int
1992 ldap_chain_db_destroy(
1993         BackendDB       *be,
1994         ConfigReply     *cr )
1995 {
1996         slap_overinst   *on = (slap_overinst *) be->bd_info;
1997         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1998
1999         int             rc;
2000
2001         rc = ldap_chain_db_func( be, db_destroy );
2002
2003         if ( lc ) {
2004                 avl_free( lc->lc_lai.lai_tree, NULL );
2005                 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
2006                 ch_free( lc );
2007         }
2008
2009         return rc;
2010 }
2011
2012 /*
2013  * inits one instance of the slapd-ldap backend, and stores
2014  * the private info in be_private of the arg
2015  */
2016 static int
2017 ldap_chain_db_init_common(
2018         BackendDB       *be )
2019 {
2020         BackendInfo     *bi = be->bd_info;
2021         ldapinfo_t      *li;
2022         int             rc;
2023
2024         be->bd_info = lback;
2025         be->be_private = NULL;
2026         rc = lback->bi_db_init( be, NULL );
2027         if ( rc != 0 ) {
2028                 return rc;
2029         }
2030         li = (ldapinfo_t *)be->be_private;
2031         li->li_urllist_f = NULL;
2032         li->li_urllist_p = NULL;
2033
2034         be->bd_info = bi;
2035
2036         return 0;
2037 }
2038
2039 /*
2040  * inits one instance of the slapd-ldap backend, stores
2041  * the private info in be_private of the arg and fills
2042  * selected fields with data from the template.
2043  *
2044  * NOTE: add checks about the other fields of the template,
2045  * which are ignored and SHOULD NOT be configured by the user.
2046  */
2047 static int
2048 ldap_chain_db_init_one(
2049         BackendDB       *be )
2050 {
2051         slap_overinst   *on = (slap_overinst *)be->bd_info;
2052         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
2053
2054         BackendInfo     *bi = be->bd_info;
2055         ldapinfo_t      *li;
2056
2057         slap_op_t       t;
2058
2059         be->bd_info = lback;
2060         be->be_private = NULL;
2061         t = lback->bi_db_init( be, NULL );
2062         if ( t != 0 ) {
2063                 return t;
2064         }
2065         li = (ldapinfo_t *)be->be_private;
2066         li->li_urllist_f = NULL;
2067         li->li_urllist_p = NULL;
2068
2069         /* copy common data */
2070         li->li_nretries = lc->lc_common_li->li_nretries;
2071         li->li_flags = lc->lc_common_li->li_flags;
2072         li->li_version = lc->lc_common_li->li_version;
2073         for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2074                 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2075         }
2076         be->bd_info = bi;
2077
2078         return 0;
2079 }
2080
2081 static int
2082 ldap_chain_db_open_one(
2083         BackendDB       *be )
2084 {
2085         if ( SLAP_DBMONITORING( be ) ) {
2086                 ldapinfo_t      *li = (ldapinfo_t *)be->be_private;
2087
2088                 if ( li->li_uri == NULL ) {
2089                         ber_str2bv( "cn=Common Connections", 0, 1,
2090                                 &li->li_monitor_info.lmi_conn_rdn );
2091                         ber_str2bv( "cn=Operations on Common Connections", 0, 1,
2092                                 &li->li_monitor_info.lmi_conn_rdn );
2093
2094                 } else {
2095                         char            *ptr;
2096
2097                         li->li_monitor_info.lmi_conn_rdn.bv_len
2098                                 = STRLENOF( "cn=" ) + strlen( li->li_uri );
2099                         ptr = li->li_monitor_info.lmi_conn_rdn.bv_val
2100                                 = ch_malloc( li->li_monitor_info.lmi_conn_rdn.bv_len + 1 );
2101                         ptr = lutil_strcopy( ptr, "cn=" );
2102                         ptr = lutil_strcopy( ptr, li->li_uri );
2103                         ptr[ 0 ] = '\0';
2104
2105                         li->li_monitor_info.lmi_ops_rdn.bv_len
2106                                 = STRLENOF( "cn=Operations on " ) + strlen( li->li_uri );
2107                         ptr = li->li_monitor_info.lmi_ops_rdn.bv_val
2108                                 = ch_malloc( li->li_monitor_info.lmi_ops_rdn.bv_len + 1 );
2109                         ptr = lutil_strcopy( ptr, "cn=Operations on " );
2110                         ptr = lutil_strcopy( ptr, li->li_uri );
2111                         ptr[ 0 ] = '\0';
2112                 }
2113         }
2114
2115         return lback->bi_db_open( be, NULL );
2116 }
2117
2118 typedef struct ldap_chain_conn_apply_t {
2119         BackendDB       *be;
2120         Connection      *conn;
2121 } ldap_chain_conn_apply_t;
2122
2123 static int
2124 ldap_chain_conn_apply( void *datum, void *arg )
2125 {
2126         ldapinfo_t              *li = (ldapinfo_t *)datum;
2127         ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
2128
2129         lca->be->be_private = (void *)li;
2130
2131         return lback->bi_connection_destroy( lca->be, lca->conn );
2132 }
2133
2134 static int
2135 ldap_chain_connection_destroy(
2136         BackendDB *be,
2137         Connection *conn
2138 )
2139 {
2140         slap_overinst           *on = (slap_overinst *) be->bd_info;
2141         ldap_chain_t            *lc = (ldap_chain_t *)on->on_bi.bi_private;
2142         void                    *private = be->be_private;
2143         ldap_chain_conn_apply_t lca;
2144         int                     rc;
2145
2146         be->be_private = NULL;
2147         lca.be = be;
2148         lca.conn = conn;
2149         ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2150         rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2151                 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2152         ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2153         be->be_private = private;
2154
2155         return rc;
2156 }
2157
2158 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2159 static int
2160 ldap_chain_parse_ctrl(
2161         Operation       *op,
2162         SlapReply       *rs,
2163         LDAPControl     *ctrl )
2164 {
2165         ber_tag_t       tag;
2166         BerElement      *ber;
2167         ber_int_t       mode,
2168                         behavior;
2169
2170         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2171                 rs->sr_text = "Chaining behavior control specified multiple times";
2172                 return LDAP_PROTOCOL_ERROR;
2173         }
2174
2175         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2176                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
2177                 return LDAP_PROTOCOL_ERROR;
2178         }
2179
2180         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2181                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2182
2183         } else {
2184                 ber_len_t       len;
2185
2186                 /* Parse the control value
2187                  *      ChainingBehavior ::= SEQUENCE { 
2188                  *           resolveBehavior         Behavior OPTIONAL, 
2189                  *           continuationBehavior    Behavior OPTIONAL } 
2190                  *                             
2191                  *      Behavior :: = ENUMERATED { 
2192                  *           chainingPreferred       (0), 
2193                  *           chainingRequired        (1), 
2194                  *           referralsPreferred      (2), 
2195                  *           referralsRequired       (3) } 
2196                  */
2197
2198                 ber = ber_init( &ctrl->ldctl_value );
2199                 if( ber == NULL ) {
2200                         rs->sr_text = "internal error";
2201                         return LDAP_OTHER;
2202                 }
2203
2204                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
2205                 /* FIXME: since the whole SEQUENCE is optional,
2206                  * should we accept no enumerations at all? */
2207                 if ( tag != LBER_ENUMERATED ) {
2208                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2209                         return LDAP_PROTOCOL_ERROR;
2210                 }
2211
2212                 switch ( behavior ) {
2213                 case LDAP_CHAINING_PREFERRED:
2214                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2215                         break;
2216
2217                 case LDAP_CHAINING_REQUIRED:
2218                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2219                         break;
2220
2221                 case LDAP_REFERRALS_PREFERRED:
2222                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2223                         break;
2224
2225                 case LDAP_REFERRALS_REQUIRED:
2226                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2227                         break;
2228
2229                 default:
2230                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2231                         return LDAP_PROTOCOL_ERROR;
2232                 }
2233
2234                 tag = ber_peek_tag( ber, &len );
2235                 if ( tag == LBER_ENUMERATED ) {
2236                         tag = ber_scanf( ber, "e", &behavior );
2237                         if ( tag == LBER_ERROR ) {
2238                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2239                                 return LDAP_PROTOCOL_ERROR;
2240                         }
2241                 }
2242
2243                 if ( tag == LBER_DEFAULT ) {
2244                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
2245
2246                 } else {
2247                         switch ( behavior ) {
2248                         case LDAP_CHAINING_PREFERRED:
2249                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2250                                 break;
2251
2252                         case LDAP_CHAINING_REQUIRED:
2253                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2254                                 break;
2255
2256                         case LDAP_REFERRALS_PREFERRED:
2257                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2258                                 break;
2259
2260                         case LDAP_REFERRALS_REQUIRED:
2261                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2262                                 break;
2263
2264                         default:
2265                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2266                                 return LDAP_PROTOCOL_ERROR;
2267                         }
2268                 }
2269
2270                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2271                         rs->sr_text = "Chaining behavior control: decoding error";
2272                         return LDAP_PROTOCOL_ERROR;
2273                 }
2274
2275                 (void) ber_free( ber, 1 );
2276         }
2277
2278         op->o_chaining = mode | ( ctrl->ldctl_iscritical
2279                         ? SLAP_CONTROL_CRITICAL
2280                         : SLAP_CONTROL_NONCRITICAL );
2281
2282         return LDAP_SUCCESS;
2283 }
2284 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2285
2286 int
2287 chain_initialize( void )
2288 {
2289         int rc;
2290
2291         /* Make sure we don't exceed the bits reserved for userland */
2292         config_check_userland( CH_LAST );
2293
2294 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2295         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2296                         /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2297                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
2298         if ( rc != LDAP_SUCCESS ) {
2299                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2300                         "unable to register chaining behavior control: %d.\n",
2301                         rc, 0, 0 );
2302                 return rc;
2303         }
2304 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2305
2306         ldapchain.on_bi.bi_type = "chain";
2307         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2308         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2309         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2310         ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2311         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2312
2313         ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2314
2315         ldapchain.on_response = ldap_chain_response;
2316
2317         ldapchain.on_bi.bi_cf_ocs = chainocs;
2318
2319         rc = config_register_schema( chaincfg, chainocs );
2320         if ( rc ) {
2321                 return rc;
2322         }
2323
2324         return overlay_register( &ldapchain );
2325 }
2326