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