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