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