]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
64f5bb5fc38adfb3423bfab39b0eb6a180a87f78
[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 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1504         int     rc = 0;
1505
1506         rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1507         if ( rc != 0 ) {
1508                 return rc;
1509         }
1510 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1511
1512         return ldap_chain_db_func( be, db_open );
1513 }
1514
1515 static int
1516 ldap_chain_db_close(
1517         BackendDB       *be )
1518 {
1519         return ldap_chain_db_func( be, db_close );
1520 }
1521
1522 static int
1523 ldap_chain_db_destroy(
1524         BackendDB       *be )
1525 {
1526         slap_overinst   *on = (slap_overinst *) be->bd_info;
1527         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1528
1529         int             rc;
1530
1531         rc = ldap_chain_db_func( be, db_destroy );
1532
1533         if ( lc ) {
1534                 avl_free( lc->lc_lai.lai_tree, NULL );
1535                 ch_free( lc );
1536         }
1537
1538         return rc;
1539 }
1540
1541 /*
1542  * inits one instance of the slapd-ldap backend, and stores
1543  * the private info in be_private of the arg
1544  */
1545 static int
1546 ldap_chain_db_init_common(
1547         BackendDB       *be )
1548 {
1549         BackendInfo     *bi = be->bd_info;
1550         int             t;
1551
1552         be->bd_info = lback;
1553         be->be_private = NULL;
1554         t = lback->bi_db_init( be );
1555         if ( t != 0 ) {
1556                 return t;
1557         }
1558         be->bd_info = bi;
1559
1560         return 0;
1561 }
1562
1563 /*
1564  * inits one instance of the slapd-ldap backend, stores
1565  * the private info in be_private of the arg and fills
1566  * selected fields with data from the template.
1567  *
1568  * NOTE: add checks about the other fields of the template,
1569  * which are ignored and SHOULD NOT be configured by the user.
1570  */
1571 static int
1572 ldap_chain_db_init_one(
1573         BackendDB       *be )
1574 {
1575         slap_overinst   *on = (slap_overinst *)be->bd_info;
1576         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1577
1578         BackendInfo     *bi = be->bd_info;
1579         ldapinfo_t      *li;
1580
1581         int             t;
1582
1583         be->bd_info = lback;
1584         be->be_private = NULL;
1585         t = lback->bi_db_init( be );
1586         if ( t != 0 ) {
1587                 return t;
1588         }
1589         li = (ldapinfo_t *)be->be_private;
1590
1591         /* copy common data */
1592         li->li_nretries = lc->lc_common_li->li_nretries;
1593         li->li_flags = lc->lc_common_li->li_flags;
1594         li->li_version = lc->lc_common_li->li_version;
1595         for ( t = 0; t < LDAP_BACK_OP_LAST; t++ ) {
1596                 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
1597         }
1598         be->bd_info = bi;
1599
1600         return 0;
1601 }
1602
1603 typedef struct ldap_chain_conn_apply_t {
1604         BackendDB       *be;
1605         Connection      *conn;
1606 } ldap_chain_conn_apply_t;
1607
1608 static int
1609 ldap_chain_conn_apply( void *datum, void *arg )
1610 {
1611         ldapinfo_t              *li = (ldapinfo_t *)datum;
1612         ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg;
1613
1614         lca->be->be_private = (void *)li;
1615
1616         return lback->bi_connection_destroy( lca->be, lca->conn );
1617 }
1618
1619 static int
1620 ldap_chain_connection_destroy(
1621         BackendDB *be,
1622         Connection *conn
1623 )
1624 {
1625         slap_overinst           *on = (slap_overinst *) be->bd_info;
1626         ldap_chain_t            *lc = (ldap_chain_t *)on->on_bi.bi_private;
1627         void                    *private = be->be_private;
1628         ldap_chain_conn_apply_t lca;
1629         int                     rc;
1630
1631         be->be_private = NULL;
1632         lca.be = be;
1633         lca.conn = conn;
1634         ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
1635         rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
1636                 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
1637         ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
1638         be->be_private = private;
1639
1640         return rc;
1641 }
1642
1643 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1644 static int
1645 ldap_chain_parse_ctrl(
1646         Operation       *op,
1647         SlapReply       *rs,
1648         LDAPControl     *ctrl )
1649 {
1650         ber_tag_t       tag;
1651         BerElement      *ber;
1652         ber_int_t       mode,
1653                         behavior;
1654
1655         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1656                 rs->sr_text = "Chaining behavior control specified multiple times";
1657                 return LDAP_PROTOCOL_ERROR;
1658         }
1659
1660         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1661                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1662                 return LDAP_PROTOCOL_ERROR;
1663         }
1664
1665         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1666                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1667
1668         } else {
1669                 ber_len_t       len;
1670
1671                 /* Parse the control value
1672                  *      ChainingBehavior ::= SEQUENCE { 
1673                  *           resolveBehavior         Behavior OPTIONAL, 
1674                  *           continuationBehavior    Behavior OPTIONAL } 
1675                  *                             
1676                  *      Behavior :: = ENUMERATED { 
1677                  *           chainingPreferred       (0), 
1678                  *           chainingRequired        (1), 
1679                  *           referralsPreferred      (2), 
1680                  *           referralsRequired       (3) } 
1681                  */
1682
1683                 ber = ber_init( &ctrl->ldctl_value );
1684                 if( ber == NULL ) {
1685                         rs->sr_text = "internal error";
1686                         return LDAP_OTHER;
1687                 }
1688
1689                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1690                 /* FIXME: since the whole SEQUENCE is optional,
1691                  * should we accept no enumerations at all? */
1692                 if ( tag != LBER_ENUMERATED ) {
1693                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1694                         return LDAP_PROTOCOL_ERROR;
1695                 }
1696
1697                 switch ( behavior ) {
1698                 case LDAP_CHAINING_PREFERRED:
1699                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1700                         break;
1701
1702                 case LDAP_CHAINING_REQUIRED:
1703                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1704                         break;
1705
1706                 case LDAP_REFERRALS_PREFERRED:
1707                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1708                         break;
1709
1710                 case LDAP_REFERRALS_REQUIRED:
1711                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1712                         break;
1713
1714                 default:
1715                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1716                         return LDAP_PROTOCOL_ERROR;
1717                 }
1718
1719                 tag = ber_peek_tag( ber, &len );
1720                 if ( tag == LBER_ENUMERATED ) {
1721                         tag = ber_scanf( ber, "e", &behavior );
1722                         if ( tag == LBER_ERROR ) {
1723                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1724                                 return LDAP_PROTOCOL_ERROR;
1725                         }
1726                 }
1727
1728                 if ( tag == LBER_DEFAULT ) {
1729                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
1730
1731                 } else {
1732                         switch ( behavior ) {
1733                         case LDAP_CHAINING_PREFERRED:
1734                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1735                                 break;
1736
1737                         case LDAP_CHAINING_REQUIRED:
1738                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1739                                 break;
1740
1741                         case LDAP_REFERRALS_PREFERRED:
1742                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1743                                 break;
1744
1745                         case LDAP_REFERRALS_REQUIRED:
1746                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1747                                 break;
1748
1749                         default:
1750                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1751                                 return LDAP_PROTOCOL_ERROR;
1752                         }
1753                 }
1754
1755                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1756                         rs->sr_text = "Chaining behavior control: decoding error";
1757                         return LDAP_PROTOCOL_ERROR;
1758                 }
1759
1760                 (void) ber_free( ber, 1 );
1761         }
1762
1763         op->o_chaining = mode | ( ctrl->ldctl_iscritical
1764                         ? SLAP_CONTROL_CRITICAL
1765                         : SLAP_CONTROL_NONCRITICAL );
1766
1767         return LDAP_SUCCESS;
1768 }
1769 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1770
1771 static slap_overinst ldapchain;
1772
1773 int
1774 chain_initialize( void )
1775 {
1776         int     rc;
1777
1778         /* Make sure we don't exceed the bits reserved for userland */
1779         config_check_userland( CH_LAST );
1780
1781 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1782         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1783                         /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1784                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
1785         if ( rc != LDAP_SUCCESS ) {
1786                 Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1787                         "unable to register chaining behavior control: %d.\n",
1788                         rc, 0, 0 );
1789                 return rc;
1790         }
1791 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1792
1793         ldapchain.on_bi.bi_type = "chain";
1794         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1795         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1796         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1797         ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
1798         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1799
1800         /* ... otherwise the underlying backend's function would be called,
1801          * likely passing an invalid entry; on the contrary, the requested
1802          * operational attributes should have been returned while chasing
1803          * the referrals.  This all in all is a bit messy, because part
1804          * of the operational attributes are generated by the backend;
1805          * part by the frontend; back-ldap should receive all the available
1806          * ones from the remote server, but then, on its own, it strips those
1807          * it assumes will be (re)generated by the frontend (e.g.
1808          * subschemaSubentry.) */
1809         ldapchain.on_bi.bi_operational = ldap_chain_operational;
1810         
1811         ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1812
1813         ldapchain.on_response = ldap_chain_response;
1814
1815         ldapchain.on_bi.bi_cf_ocs = chainocs;
1816
1817         rc = config_register_schema( chaincfg, chainocs );
1818         if ( rc ) {
1819                 return rc;
1820         }
1821
1822         return overlay_register( &ldapchain );
1823 }
1824