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