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