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