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