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