]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
more table-driven config cleanup
[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-2005 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  */
21
22 #include "portable.h"
23
24 #include <stdio.h>
25
26 #include <ac/string.h>
27 #include <ac/socket.h>
28
29 #include "slap.h"
30 #include "back-ldap.h"
31
32 #include "config.h"
33
34 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
35 #define SLAP_CHAINING_DEFAULT                           LDAP_CHAINING_PREFERRED
36 #define SLAP_CH_RESOLVE_SHIFT                           SLAP_CONTROL_SHIFT
37 #define SLAP_CH_RESOLVE_MASK                            (0x3 << SLAP_CH_RESOLVE_SHIFT)
38 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED              (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
39 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED               (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
40 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED             (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
41 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED              (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
42 #define SLAP_CH_RESOLVE_DEFAULT                         (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
43 #define SLAP_CH_CONTINUATION_SHIFT                      (SLAP_CH_RESOLVE_SHIFT + 2)
44 #define SLAP_CH_CONTINUATION_MASK                       (0x3 << SLAP_CH_CONTINUATION_SHIFT)
45 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED         (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
46 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED          (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
47 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED        (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
48 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED         (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
49 #define SLAP_CH_CONTINUATION_DEFAULT                    (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
50
51 #define o_chaining                      o_ctrlflag[sc_chainingBehavior]
52 #define get_chaining(op)                ((op)->o_chaining & SLAP_CONTROL_MASK)
53 #define get_chainingBehavior(op)        ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
54 #define get_resolveBehavior(op)         ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
55 #define get_continuationBehavior(op)    ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
56
57 static int              sc_chainingBehavior;
58 #endif /*  LDAP_CONTROL_X_CHAINING_BEHAVIOR */
59
60 #define LDAP_CH_NONE                    ((void *)(0))
61 #define LDAP_CH_RES                     ((void *)(1))
62 #define LDAP_CH_ERR                     ((void *)(2))
63
64 static BackendInfo      *lback;
65
66 typedef struct ldap_chain_t {
67         struct ldapinfo         *lc_li;
68         unsigned                lc_flags;
69 #define LDAP_CHAIN_F_NONE               0x00U
70 #define LDAP_CHAIN_F_CHAINING           0x01U
71
72 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
73         LDAPControl             lc_chaining_ctrl;
74         char                    lc_chaining_ctrlflag;
75 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
76 } ldap_chain_t;
77
78 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
79 static int
80 chaining_control_add(
81                 ldap_chain_t    *lc,
82                 Operation       *op, 
83                 LDAPControl     ***oldctrlsp )
84 {
85         LDAPControl     **ctrls = NULL;
86         int             c = 0;
87
88         *oldctrlsp = op->o_ctrls;
89
90         /* default chaining control not defined */
91         if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
92                 return 0;
93         }
94
95         /* already present */
96         if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
97                 return 0;
98         }
99
100         /* FIXME: check other incompatibilities */
101
102         /* add to other controls */
103         if ( op->o_ctrls ) {
104                 for ( c = 0; op->o_ctrls[ c ]; c++ )
105                         /* count them */ ;
106         }
107
108         ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
109         ctrls[ 0 ] = &lc->lc_chaining_ctrl;
110         if ( op->o_ctrls ) {
111                 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
112                         ctrls[ c + 1 ] = op->o_ctrls[ c ];
113                 }
114         }
115         ctrls[ c + 1 ] = NULL;
116
117         op->o_ctrls = ctrls;
118
119         op->o_chaining = lc->lc_chaining_ctrlflag;
120
121         return 0;
122 }
123
124 static int
125 chaining_control_remove(
126                 Operation       *op, 
127                 LDAPControl     ***oldctrlsp )
128 {
129         LDAPControl     **oldctrls = *oldctrlsp;
130
131         /* we assume that the first control is the chaining control
132          * added by the chain overlay, so it's the only one we explicitly 
133          * free */
134         if ( op->o_ctrls != oldctrls ) {
135                 assert( op->o_ctrls != NULL );
136                 assert( op->o_ctrls[ 0 ] != NULL );
137
138                 free( op->o_ctrls );
139
140                 op->o_chaining = 0;
141                 op->o_ctrls = oldctrls;
142         } 
143
144         *oldctrlsp = NULL;
145
146         return 0;
147 }
148 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
149
150 static int
151 ldap_chain_operational( Operation *op, SlapReply *rs )
152 {
153         /* Trap entries generated by back-ldap.
154          * 
155          * FIXME: we need a better way to recognize them; a cleaner
156          * solution would be to be able to intercept the response
157          * of be_operational(), so that we can divert only those
158          * calls that fail because operational attributes were
159          * requested for entries that do not belong to the underlying
160          * database.  This fix is likely to intercept also entries
161          * generated by back-perl and so. */
162         if ( rs->sr_entry->e_private == NULL ) {
163                 return 0;
164         }
165
166         return SLAP_CB_CONTINUE;
167 }
168
169 /*
170  * Search specific response that strips entryDN from entries
171  */
172 static int
173 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
174 {
175         assert( op->o_tag == LDAP_REQ_SEARCH );
176
177         /* if in error, don't proceed any further */
178         if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
179                 return 0;
180         }
181
182         if ( rs->sr_type == REP_SEARCH ) {
183                 Attribute       **ap = &rs->sr_entry->e_attrs;
184
185                 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
186                         /* will be generated later by frontend
187                          * (a cleaner solution would be that
188                          * the frontend checks if it already exists */
189                         if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
190                         {
191                                 Attribute *a = *ap;
192
193                                 *ap = (*ap)->a_next;
194                                 attr_free( a );
195
196                                 /* there SHOULD be one only! */
197                                 break;
198                         }
199                 }
200                 
201                 return SLAP_CB_CONTINUE;
202
203         } else if ( rs->sr_type == REP_SEARCHREF ) {
204                 /* if we get it here, it means the library was unable
205                  * to chase the referral... */
206
207 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
208                 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
209                         switch ( get_continuationBehavior( op ) ) {
210                         case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
211                                 op->o_callback->sc_private = LDAP_CH_ERR;
212                                 return -1;
213
214                         default:
215                                 break;
216                         }
217                 }
218 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
219                 return SLAP_CB_CONTINUE;
220
221         } else if ( rs->sr_type == REP_RESULT ) {
222                 /* back-ldap tried to send result */
223                 op->o_callback->sc_private = LDAP_CH_RES;
224         }
225
226         return 0;
227 }
228
229 /*
230  * Dummy response that simply traces if back-ldap tried to send 
231  * anything to the client
232  */
233 static int
234 ldap_chain_cb_response( Operation *op, SlapReply *rs )
235 {
236         /* if in error, don't proceed any further */
237         if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
238                 return 0;
239         }
240
241         if ( rs->sr_type == REP_RESULT ) {
242                 op->o_callback->sc_private = LDAP_CH_RES;
243
244         } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
245         {
246                 /* strip the entryDN attribute, but keep returning results */
247                 (void)ldap_chain_cb_search_response( op, rs );
248         }
249
250         return SLAP_CB_CONTINUE;
251 }
252
253 static int
254 ldap_chain_op(
255         Operation       *op,
256         SlapReply       *rs,
257         int             ( *op_f )( Operation *op, SlapReply *rs ), 
258         BerVarray       ref )
259 {
260         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
261         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
262         struct ldapinfo li, *lip = lc->lc_li;
263
264         /* NOTE: returned if ref is empty... */
265         int             rc = LDAP_OTHER;
266
267 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
268         LDAPControl     **ctrls = NULL;
269         
270         (void)chaining_control_add( lc, op, &ctrls );
271 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
272
273         if ( lip->url != NULL ) {
274                 op->o_bd->be_private = lip;
275                 rc = ( *op_f )( op, rs );
276                 goto done;
277         }
278
279         li = *lip;
280         op->o_bd->be_private = &li;
281
282         /* if we parse the URI then by no means 
283          * we can cache stuff or reuse connections, 
284          * because in back-ldap there's no caching
285          * based on the URI value, which is supposed
286          * to be set once for all (correct?) */
287         op->o_do_not_cache = 1;
288
289         for ( ; !BER_BVISNULL( ref ); ref++ ) {
290                 LDAPURLDesc     *srv;
291                 char            *save_dn;
292                         
293                 /* We're setting the URI of the first referral;
294                  * what if there are more?
295
296 Document: draft-ietf-ldapbis-protocol-27.txt
297
298 4.1.10. Referral 
299    ...
300    If the client wishes to progress the operation, it MUST follow the 
301    referral by contacting one of the supported services. If multiple 
302    URIs are present, the client assumes that any supported URI may be 
303    used to progress the operation. 
304
305                  * so we actually need to follow exactly one,
306                  * and we can assume any is fine.
307                  */
308         
309                 /* parse reference and use 
310                  * proto://[host][:port]/ only */
311                 rc = ldap_url_parse_ext( ref->bv_val, &srv );
312                 if ( rc != LDAP_URL_SUCCESS ) {
313                         /* try next */
314                         rc = LDAP_OTHER;
315                         continue;
316                 }
317
318                 /* remove DN essentially because later on 
319                  * ldap_initialize() will parse the URL 
320                  * as a comma-separated URL list */
321                 save_dn = srv->lud_dn;
322                 srv->lud_dn = "";
323                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
324                 li.url = ldap_url_desc2str( srv );
325                 srv->lud_dn = save_dn;
326                 ldap_free_urldesc( srv );
327
328                 if ( li.url == NULL ) {
329                         /* try next */
330                         rc = LDAP_OTHER;
331                         continue;
332                 }
333
334                 rc = ( *op_f )( op, rs );
335
336                 ldap_memfree( li.url );
337                 li.url = NULL;
338                 
339                 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
340                         break;
341                 }
342         }
343
344 done:;
345 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
346         (void)chaining_control_remove( op, &ctrls );
347 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
348
349         return rc;
350 }
351
352 static int
353 ldap_chain_response( Operation *op, SlapReply *rs )
354 {
355         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
356         void            *private = op->o_bd->be_private;
357         slap_callback   *sc = op->o_callback,
358                         sc2 = { 0 };
359         int             rc = 0;
360         int             cache = op->o_do_not_cache;
361         BerVarray       ref;
362         struct berval   ndn = op->o_ndn;
363
364         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
365         struct ldapinfo li, *lip = lc->lc_li;
366
367 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
368         int             sr_err = rs->sr_err;
369         slap_reply_t    sr_type = rs->sr_type;
370         slap_mask_t     chain_mask = 0;
371         ber_len_t       chain_shift = 0;
372 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
373
374         if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
375                 return SLAP_CB_CONTINUE;
376         }
377
378 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
379         if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
380                 switch ( get_resolveBehavior( op ) ) {
381                 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
382                 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
383                         return SLAP_CB_CONTINUE;
384
385                 default:
386                         chain_mask = SLAP_CH_RESOLVE_MASK;
387                         chain_shift = SLAP_CH_RESOLVE_SHIFT;
388                         break;
389                 }
390
391         } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
392                 switch ( get_continuationBehavior( op ) ) {
393                 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
394                 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
395                         return SLAP_CB_CONTINUE;
396
397                 default:
398                         chain_mask = SLAP_CH_CONTINUATION_MASK;
399                         chain_shift = SLAP_CH_CONTINUATION_SHIFT;
400                         break;
401                 }
402         }
403 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
404
405         /*
406          * TODO: add checks on who/when chain operations; e.g.:
407          *   a) what identities are authorized
408          *   b) what request DN (e.g. only chain requests rooted at <DN>)
409          *   c) what referral URIs
410          *   d) what protocol scheme (e.g. only ldaps://)
411          *   e) what ssf
412          */
413
414         ref = rs->sr_ref;
415         rs->sr_ref = NULL;
416
417         /* we need this to know if back-ldap returned any result */
418         sc2.sc_response = ldap_chain_cb_response;
419         op->o_callback = &sc2;
420
421         /* Chaining can be performed by a privileged user on behalf
422          * of normal users, using the ProxyAuthz control, by exploiting
423          * the identity assertion feature of back-ldap; see idassert-*
424          * directives in slapd-ldap(5).
425          *
426          * FIXME: the idassert-authcDN is one, will it be fine regardless
427          * of the URI we obtain from the referral?
428          */
429
430         switch ( op->o_tag ) {
431         case LDAP_REQ_BIND: {
432                 struct berval   rndn = op->o_req_ndn;
433                 Connection      *conn = op->o_conn;
434
435                 /* FIXME: can we really get a referral for binds? */
436                 op->o_req_ndn = slap_empty_bv;
437                 op->o_conn = NULL;
438                 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
439                 op->o_req_ndn = rndn;
440                 op->o_conn = conn;
441                 }
442                 break;
443         case LDAP_REQ_ADD:
444                 {
445                 int             cleanup_attrs = 0;
446
447                 if ( op->ora_e->e_attrs == NULL ) {
448                         char            textbuf[ SLAP_TEXT_BUFLEN ];
449                         size_t          textlen = sizeof( textbuf );
450
451 #if 0
452                         /* FIXME: op->o_bd is still set to the BackendDB 
453                          * structure of the database that tried to handle
454                          * the operation and actually returned a referral
455                          * ... */
456                         assert( SLAP_DBFLAGS( op->o_bd ) & SLAP_DBFLAG_GLOBAL_OVERLAY );
457 #endif
458
459                         /* global overlay: create entry */
460                         /* NOTE: this is a hack to use the chain overlay
461                          * as global.  I expect to be able to remove this
462                          * soon by using slap_mods2entry() earlier in
463                          * do_add(), adding the operational attrs later
464                          * if required. */
465                         rs->sr_err = slap_mods2entry( op->ora_modlist,
466                                         &op->ora_e, 0, 1,
467                                         &rs->sr_text, textbuf, textlen );
468                         if ( rs->sr_err != LDAP_SUCCESS ) {
469                                 send_ldap_result( op, rs );
470                                 rc = 1;
471                                 break;
472                         }
473                 }
474                 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
475                 if ( cleanup_attrs ) {
476                         attrs_free( op->ora_e->e_attrs );
477                         op->ora_e->e_attrs = NULL;
478                 }
479                 break;
480                 }
481         case LDAP_REQ_DELETE:
482                 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
483                 break;
484         case LDAP_REQ_MODRDN:
485                 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
486                 break;
487         case LDAP_REQ_MODIFY:
488                 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
489                 break;
490         case LDAP_REQ_COMPARE:
491                 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
492                 break;
493         case LDAP_REQ_SEARCH:
494                 if ( rs->sr_type == REP_SEARCHREF ) {
495                         struct berval   *curr = ref,
496                                         odn = op->o_req_dn,
497                                         ondn = op->o_req_ndn;
498
499 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
500                         LDAPControl     **ctrls = NULL;
501         
502                         (void)chaining_control_add( lc, op, &ctrls );
503 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
504
505                         rs->sr_type = REP_SEARCH;
506
507                         sc2.sc_response = ldap_chain_cb_search_response;
508
509                         li = *lip;
510                         li.url = NULL;
511                         op->o_bd->be_private = &li;
512                         
513                         /* if we parse the URI then by no means 
514                          * we can cache stuff or reuse connections, 
515                          * because in back-ldap there's no caching
516                          * based on the URI value, which is supposed
517                          * to be set once for all (correct?) */
518                         op->o_do_not_cache = 1;
519
520                         /* copy the private info because we need to modify it */
521                         for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
522                                 LDAPURLDesc     *srv;
523                                 char            *save_dn;
524
525                                 /* parse reference and use
526                                  * proto://[host][:port]/ only */
527                                 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
528                                 if ( rc != LDAP_URL_SUCCESS ) {
529                                         /* try next */
530                                         rs->sr_err = LDAP_OTHER;
531                                         continue;
532                                 }
533
534                                 /* remove DN essentially because later on 
535                                  * ldap_initialize() will parse the URL 
536                                  * as a comma-separated URL list */
537                                 save_dn = srv->lud_dn;
538                                 srv->lud_dn = "";
539                                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
540                                 li.url = ldap_url_desc2str( srv );
541                                 if ( li.url != NULL ) {
542                                         ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
543                                                         op->o_tmpmemctx );
544                                         ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
545                                                         op->o_tmpmemctx );
546                                 }
547
548                                 srv->lud_dn = save_dn;
549                                 ldap_free_urldesc( srv );
550
551                                 if ( li.url == NULL ) {
552                                         /* try next */
553                                         rs->sr_err = LDAP_OTHER;
554                                         continue;
555                                 }
556
557
558                                 /* FIXME: should we also copy filter and scope?
559                                  * according to RFC3296, no */
560                                 rc = lback->bi_op_search( op, rs );
561
562                                 ldap_memfree( li.url );
563                                 li.url = NULL;
564
565                                 op->o_tmpfree( op->o_req_dn.bv_val,
566                                                 op->o_tmpmemctx );
567                                 op->o_tmpfree( op->o_req_ndn.bv_val,
568                                                 op->o_tmpmemctx );
569
570                                 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
571                                         break;
572                                 }
573
574                                 rc = rs->sr_err;
575                         }
576
577 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
578                         (void)chaining_control_remove( op, &ctrls );
579 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
580
581                         op->o_req_dn = odn;
582                         op->o_req_ndn = ondn;
583                         rs->sr_type = REP_SEARCHREF;
584                         rs->sr_entry = NULL;
585
586                         if ( rc != LDAP_SUCCESS ) {
587                                 /* couldn't chase any of the referrals */
588                                 rc = SLAP_CB_CONTINUE;
589                         }
590                         
591                 } else {
592                         rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
593                 }
594                 break;
595         case LDAP_REQ_EXTENDED:
596                 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
597                 /* FIXME: ldap_back_extended() by design 
598                  * doesn't send result; frontend is expected
599                  * to send it... */
600                 if ( rc != SLAPD_ABANDON ) {
601                         send_ldap_extended( op, rs );
602                         rc = LDAP_SUCCESS;
603                 }
604                 break;
605         default:
606                 rc = SLAP_CB_CONTINUE;
607                 break;
608         }
609
610 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
611         if ( rc != LDAP_SUCCESS || sc2.sc_private == LDAP_CH_ERR ) {
612                 if ( rs->sr_err == LDAP_CANNOT_CHAIN ) {
613                         goto cannot_chain;
614                 }
615
616                 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
617                 case LDAP_CHAINING_REQUIRED:
618 cannot_chain:;
619                         op->o_callback = NULL;
620                         send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" );
621                         break;
622
623                 default:
624                         rc = SLAP_CB_CONTINUE;
625                         rs->sr_err = sr_err;
626                         rs->sr_type = sr_type;
627                         break;
628                 }
629                 goto dont_chain;
630         }
631 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
632
633         if ( sc2.sc_private == LDAP_CH_NONE ) {
634                 op->o_callback = NULL;
635                 rc = rs->sr_err = slap_map_api2result( rs );
636                 send_ldap_result( op, rs );
637         }
638
639 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
640 dont_chain:;
641 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
642         op->o_do_not_cache = cache;
643         op->o_bd->be_private = private;
644         op->o_callback = sc;
645         op->o_ndn = ndn;
646         rs->sr_ref = ref;
647
648         return rc;
649 }
650
651 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
652 static int
653 ldap_chain_parse_ctrl(
654         Operation       *op,
655         SlapReply       *rs,
656         LDAPControl     *ctrl );
657
658 static int
659 str2chain( const char *s )
660 {
661         if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
662                 return LDAP_CHAINING_PREFERRED;
663                 
664         } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
665                 return LDAP_CHAINING_REQUIRED;
666
667         } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
668                 return LDAP_REFERRALS_PREFERRED;
669                 
670         } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
671                 return LDAP_REFERRALS_REQUIRED;
672         }
673
674         return -1;
675 }
676 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
677
678 enum {
679         PC_CHAINING = 1
680 };
681
682 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
683 static ConfigDriver chain_cf_gen;
684 static ConfigCfAdd chain_cfadd;
685 #endif
686 static ConfigLDAPadd chain_ldadd;
687
688 static ConfigTable chaincfg[] = {
689 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
690         { "chain-chaining", "args",
691                 2, 4, 0, ARG_MAGIC|ARG_BERVAL|PC_CHAINING, chain_cf_gen,
692                 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
693                         "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
694                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
695 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
696         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
697 };
698
699 static ConfigOCs chainocs[] = {
700 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
701         { "( OLcfgOvOc:3.1 "
702                 "NAME 'olcChainConfig' "
703                 "DESC 'Chain configuration' "
704                 "SUP olcOverlayConfig "
705                 "MAY olcChainingBehavior )", Cft_Overlay, chaincfg, NULL, chain_cfadd },
706 #endif
707         { "( OLcfgOvOc:3.2 "
708                 "NAME 'olcChainDatabase' "
709                 "DESC 'Chain remote server configuration' "
710                 "AUXILIARY )", Cft_Misc, chaincfg, chain_ldadd },
711         { NULL, 0, NULL }
712 };
713
714 static int
715 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
716 {
717         if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
718                 p->ce_bi->bi_cf_ocs != chainocs )
719                 return LDAP_CONSTRAINT_VIOLATION;
720
721         return LDAP_SUCCESS;
722 }
723
724 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
725
726 static int
727 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
728 {
729         CfEntryInfo     *pe = p->e_private;
730         slap_overinst   *on = (slap_overinst *)pe->ce_bi;
731         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
732         void            *priv = (void *)ca->be->be_private;
733         struct berval   bv;
734
735         /* FIXME: should not hardcode "olcDatabase" here */
736         bv.bv_len = sprintf( ca->msg, "olcDatabase=%s", lback->bi_type );
737         bv.bv_val = ca->msg;
738
739         /* We can only create this entry if the database is table-driven */
740         if ( lback->bi_cf_ocs ) {
741                 ca->be->be_private = (void *)lc->lc_li;
742                 config_build_entry( op, rs, pe, ca, &bv, lback->bi_cf_ocs, &chainocs[1] );
743                 ca->be->be_private = priv;
744         }
745
746         return 0;
747 }
748
749 static slap_verbmasks chaining_mode[] = {
750         { BER_BVC("referralsRequired"),         LDAP_REFERRALS_REQUIRED },
751         { BER_BVC("referralsPreferred"),        LDAP_REFERRALS_PREFERRED },
752         { BER_BVC("chainingRequired"),          LDAP_CHAINING_REQUIRED },
753         { BER_BVC("chainingPreferred"),         LDAP_CHAINING_PREFERRED },
754         { BER_BVNULL,                           0 }
755 };
756
757 static int
758 chain_cf_gen( ConfigArgs *c )
759 {
760         slap_overinst   *on = (slap_overinst *)c->bi;
761 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
762         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
763 #endif
764
765         int             rc = 0;
766
767         if ( c->op == SLAP_CONFIG_EMIT ) {
768                 switch( c->type ) {
769 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
770                 case PC_CHAINING: {
771                         struct berval   resolve = BER_BVNULL,
772                                         continuation = BER_BVNULL;
773
774                         if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
775                                 return 1;
776                         }
777
778                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
779                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
780
781                         c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
782                                 + STRLENOF( " " )
783                                 + STRLENOF( "continuation=" ) + continuation.bv_len;
784                         c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
785                         snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
786                                 "resolve=%s continuation=%s",
787                                 resolve.bv_val, continuation.bv_val );
788
789                         if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
790                                 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
791                                         c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
792                                 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
793                                         " critical", STRLENOF( " critical" ) + 1 );
794                                 c->value_bv.bv_len += STRLENOF( " critical" );
795                         }
796
797                         break;
798                 }
799 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
800
801                 default:
802                         assert( 0 );
803                         rc = 1;
804                 }
805                 return rc;
806
807         } else if ( c->op == LDAP_MOD_DELETE ) {
808                 return 1;       /* FIXME */
809 #if 0
810                 switch( c->type ) {
811                 case PC_ATTR:
812                 case PC_TEMP:
813                 }
814                 return rc;
815 #endif
816         }
817
818         switch( c->type ) {
819 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
820         case PC_CHAINING: {
821                 char                    **argv = c->argv;
822                 int                     argc = c->argc;
823                 BerElementBuffer        berbuf;
824                 BerElement              *ber = (BerElement *)&berbuf;
825                 int                     resolve = -1,
826                                         continuation = -1,
827                                         iscritical = 0;
828                 Operation               op = { 0 };
829                 SlapReply               rs = { 0 };
830
831                 lc->lc_chaining_ctrlflag = 0;
832
833                 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
834                         if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
835                                 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
836                                 if ( resolve == -1 ) {
837                                         fprintf( stderr, "%s line %d: "
838                                                 "illegal <resolve> value %s "
839                                                 "in \"chain-chaining>\"\n",
840                                                 c->fname, c->lineno, argv[ 0 ] );
841                                         return 1;
842                                 }
843
844                         } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
845                                 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
846                                 if ( continuation == -1 ) {
847                                         fprintf( stderr, "%s line %d: "
848                                                 "illegal <continuation> value %s "
849                                                 "in \"chain-chaining\"\n",
850                                                 c->fname, c->lineno, argv[ 0 ] );
851                                         return 1;
852                                 }
853
854                         } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
855                                 iscritical = 1;
856
857                         } else {
858                                 fprintf( stderr, "%s line %d: "
859                                         "unknown option in \"chain-chaining\"\n",
860                                         c->fname, c->lineno );
861                                 return 1;
862                         }
863                 }
864
865                 if ( resolve != -1 || continuation != -1 ) {
866                         int     err;
867
868                         if ( resolve == -1 ) {
869                                 /* default */
870                                 resolve = SLAP_CHAINING_DEFAULT;
871                         }
872
873                         ber_init2( ber, NULL, LBER_USE_DER );
874
875                         err = ber_printf( ber, "{e" /* } */, resolve );
876                         if ( err == -1 ) {
877                                 ber_free( ber, 1 );
878                                 fprintf( stderr, "%s line %d: "
879                                         "chaining behavior control encoding error!\n",
880                                         c->fname, c->lineno );
881                                 return 1;
882                         }
883
884                         if ( continuation > -1 ) {
885                                 err = ber_printf( ber, "e", continuation );
886                                 if ( err == -1 ) {
887                                         ber_free( ber, 1 );
888                                         fprintf( stderr, "%s line %d: "
889                                                 "chaining behavior control encoding error!\n",
890                                                 c->fname, c->lineno );
891                                         return 1;
892                                 }
893                         }
894
895                         err = ber_printf( ber, /* { */ "N}" );
896                         if ( err == -1 ) {
897                                 ber_free( ber, 1 );
898                                 fprintf( stderr, "%s line %d: "
899                                         "chaining behavior control encoding error!\n",
900                                         c->fname, c->lineno );
901                                 return 1;
902                         }
903
904                         if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
905                                 exit( EXIT_FAILURE );
906                         }
907
908                 } else {
909                         BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
910                 }
911
912                 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
913                 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
914
915                 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
916                 {
917                         fprintf( stderr, "%s line %d: "
918                                 "unable to parse chaining control%s%s\n",
919                                 c->fname, c->lineno,
920                                 rs.sr_text ? ": " : "",
921                                 rs.sr_text ? rs.sr_text : "" );
922                         return 1;
923                 }
924
925                 lc->lc_chaining_ctrlflag = op.o_chaining;
926
927                 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
928
929                 rc = 0;
930
931                 break;
932         }
933 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
934
935         default:
936                 assert( 0 );
937                 return 1;
938         }
939         return rc;
940 }
941
942 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
943
944 static int
945 ldap_chain_db_config(
946         BackendDB       *be,
947         const char      *fname,
948         int             lineno,
949         int             argc,
950         char            **argv )
951 {
952         slap_overinst   *on = (slap_overinst *) be->bd_info;
953         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
954         char            *argv0 = NULL;
955         int             rc;
956         BackendDB       db = *be;
957
958         if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
959                 argv0 = argv[ 0 ];
960                 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
961
962 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
963                 if ( strcasecmp( argv[ 0 ], "chaining" ) == 0 ) {
964                         char                    **tmpargv = argv;
965                         BerElementBuffer        berbuf;
966                         BerElement              *ber = (BerElement *)&berbuf;
967                         int                     resolve = -1,
968                                                 continuation = -1,
969                                                 iscritical = 0;
970                         Operation               op = { 0 };
971                         SlapReply               rs = { 0 };
972
973                         lc->lc_chaining_ctrlflag = 0;
974
975                         for ( argc--, tmpargv++; argc > 0; argc--, tmpargv++ ) {
976                                 if ( strncasecmp( tmpargv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
977                                         resolve = str2chain( tmpargv[ 0 ] + STRLENOF( "resolve=" ) );
978                                         if ( resolve == -1 ) {
979                                                 fprintf( stderr, "%s line %d: "
980                                                         "illegal <resolve> value %s "
981                                                         "in \"chain-chaining>\"\n",
982                                                         fname, lineno, tmpargv[ 0 ] );
983                                                 return 1;
984                                         }
985
986                                 } else if ( strncasecmp( tmpargv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
987                                         continuation = str2chain( tmpargv[ 0 ] + STRLENOF( "continuation=" ) );
988                                         if ( continuation == -1 ) {
989                                                 fprintf( stderr, "%s line %d: "
990                                                         "illegal <continuation> value %s "
991                                                         "in \"chain-chaining\"\n",
992                                                         fname, lineno, tmpargv[ 0 ] );
993                                                 return 1;
994                                         }
995
996                                 } else if ( strcasecmp( tmpargv[ 0 ], "critical" ) == 0 ) {
997                                         iscritical = 1;
998
999                                 } else {
1000                                         fprintf( stderr, "%s line %d: "
1001                                                 "unknown option in \"chain-chaining\"\n",
1002                                                 fname, lineno );
1003                                         return 1;
1004                                 }
1005                         }
1006
1007                         if ( resolve != -1 || continuation != -1 ) {
1008                                 int     err;
1009
1010                                 if ( resolve == -1 ) {
1011                                         /* default */
1012                                         resolve = SLAP_CHAINING_DEFAULT;
1013                                 }
1014
1015                                 ber_init2( ber, NULL, LBER_USE_DER );
1016
1017                                 err = ber_printf( ber, "{e" /* } */, resolve );
1018                                 if ( err == -1 ) {
1019                                         ber_free( ber, 1 );
1020                                         fprintf( stderr, "%s line %d: "
1021                                                 "chaining behavior control encoding error!\n",
1022                                                 fname, lineno );
1023                                         return 1;
1024                                 }
1025
1026                                 if ( continuation > -1 ) {
1027                                         err = ber_printf( ber, "e", continuation );
1028                                         if ( err == -1 ) {
1029                                                 ber_free( ber, 1 );
1030                                                 fprintf( stderr, "%s line %d: "
1031                                                         "chaining behavior control encoding error!\n",
1032                                                         fname, lineno );
1033                                                 return 1;
1034                                         }
1035                                 }
1036
1037                                 err = ber_printf( ber, /* { */ "N}" );
1038                                 if ( err == -1 ) {
1039                                         ber_free( ber, 1 );
1040                                         fprintf( stderr, "%s line %d: "
1041                                                 "chaining behavior control encoding error!\n",
1042                                                 fname, lineno );
1043                                         return 1;
1044                                 }
1045
1046                                 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1047                                         exit( EXIT_FAILURE );
1048                                 }
1049
1050                         } else {
1051                                 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1052                         }
1053
1054                         lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1055                         lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1056
1057                         if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1058                         {
1059                                 fprintf( stderr, "%s line %d: "
1060                                         "unable to parse chaining control%s%s\n",
1061                                         fname, lineno,
1062                                         rs.sr_text ? ": " : "",
1063                                         rs.sr_text ? rs.sr_text : "" );
1064                                 return 1;
1065                         }
1066
1067                         lc->lc_chaining_ctrlflag = op.o_chaining;
1068
1069                         lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1070
1071                         rc = 0;
1072                         goto done;
1073                 }
1074 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1075         }
1076
1077         db.be_cf_ocs = lback->bi_cf_ocs;
1078         db.be_private = lc->lc_li;
1079         rc = lback->bi_db_config( &db, fname, lineno, argc, argv );
1080
1081 done:;
1082         if ( argv0 ) {
1083                 argv[ 0 ] = argv0;
1084         }
1085
1086         return rc;
1087 }
1088
1089 static int
1090 ldap_chain_db_init(
1091         BackendDB *be )
1092 {
1093         slap_overinst   *on = (slap_overinst *)be->bd_info;
1094         ldap_chain_t    *lc = NULL;
1095         int             rc;
1096         BackendDB       bd = *be;
1097
1098         if ( lback == NULL ) {
1099                 lback = backend_info( "ldap" );
1100
1101                 if ( lback == NULL ) {
1102                         return -1;
1103                 }
1104         }
1105
1106         lc = ch_malloc( sizeof( ldap_chain_t ) );
1107         memset( lc, 0, sizeof( ldap_chain_t ) );
1108
1109         bd.be_private = NULL;
1110         rc = lback->bi_db_init( &bd );
1111         lc->lc_li = (struct ldapinfo *)bd.be_private;
1112         on->on_bi.bi_private = (void *)lc;
1113
1114         return rc;
1115 }
1116
1117 static int
1118 ldap_chain_db_open(
1119         BackendDB *be
1120 )
1121 {
1122         int     rc = 0;
1123
1124 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1125         rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1126 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1127
1128         return rc;
1129 }
1130
1131 static int
1132 ldap_chain_db_destroy(
1133         BackendDB *be
1134 )
1135 {
1136         slap_overinst   *on = (slap_overinst *) be->bd_info;
1137         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1138         void            *private = be->be_private;
1139         int             rc;
1140
1141         be->be_private = (void *)lc->lc_li;
1142         rc = lback->bi_db_destroy( be );
1143         lc->lc_li = be->be_private;
1144         ch_free( lc );
1145         on->on_bi.bi_private = NULL;
1146         be->be_private = private;
1147         return rc;
1148 }
1149
1150 static int
1151 ldap_chain_connection_destroy(
1152         BackendDB *be,
1153         Connection *conn
1154 )
1155 {
1156         slap_overinst   *on = (slap_overinst *) be->bd_info;
1157         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1158         void            *private = be->be_private;
1159         int             rc;
1160
1161         be->be_private = (void *)lc->lc_li;
1162         rc = lback->bi_connection_destroy( be, conn );
1163         be->be_private = private;
1164
1165         return rc;
1166 }
1167
1168 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1169 static int
1170 ldap_chain_parse_ctrl(
1171         Operation       *op,
1172         SlapReply       *rs,
1173         LDAPControl     *ctrl )
1174 {
1175         ber_tag_t       tag;
1176         BerElement      *ber;
1177         ber_int_t       mode,
1178                         behavior;
1179
1180         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1181                 rs->sr_text = "Chaining behavior control specified multiple times";
1182                 return LDAP_PROTOCOL_ERROR;
1183         }
1184
1185         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1186                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1187                 return LDAP_PROTOCOL_ERROR;
1188         }
1189
1190         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1191                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1192
1193         } else {
1194                 ber_len_t       len;
1195
1196                 /* Parse the control value
1197                  *      ChainingBehavior ::= SEQUENCE { 
1198                  *           resolveBehavior         Behavior OPTIONAL, 
1199                  *           continuationBehavior    Behavior OPTIONAL } 
1200                  *                             
1201                  *      Behavior :: = ENUMERATED { 
1202                  *           chainingPreferred       (0), 
1203                  *           chainingRequired        (1), 
1204                  *           referralsPreferred      (2), 
1205                  *           referralsRequired       (3) } 
1206                  */
1207
1208                 ber = ber_init( &ctrl->ldctl_value );
1209                 if( ber == NULL ) {
1210                         rs->sr_text = "internal error";
1211                         return LDAP_OTHER;
1212                 }
1213
1214                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1215                 /* FIXME: since the whole SEQUENCE is optional,
1216                  * should we accept no enumerations at all? */
1217                 if ( tag != LBER_ENUMERATED ) {
1218                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1219                         return LDAP_PROTOCOL_ERROR;
1220                 }
1221
1222                 switch ( behavior ) {
1223                 case LDAP_CHAINING_PREFERRED:
1224                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1225                         break;
1226
1227                 case LDAP_CHAINING_REQUIRED:
1228                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1229                         break;
1230
1231                 case LDAP_REFERRALS_PREFERRED:
1232                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1233                         break;
1234
1235                 case LDAP_REFERRALS_REQUIRED:
1236                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1237                         break;
1238
1239                 default:
1240                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1241                         return LDAP_PROTOCOL_ERROR;
1242                 }
1243
1244                 tag = ber_peek_tag( ber, &len );
1245                 if ( tag == LBER_ENUMERATED ) {
1246                         tag = ber_scanf( ber, "e", &behavior );
1247                         if ( tag == LBER_ERROR ) {
1248                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1249                                 return LDAP_PROTOCOL_ERROR;
1250                         }
1251                 }
1252
1253                 if ( tag == LBER_DEFAULT ) {
1254                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
1255
1256                 } else {
1257                         switch ( behavior ) {
1258                         case LDAP_CHAINING_PREFERRED:
1259                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1260                                 break;
1261
1262                         case LDAP_CHAINING_REQUIRED:
1263                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1264                                 break;
1265
1266                         case LDAP_REFERRALS_PREFERRED:
1267                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1268                                 break;
1269
1270                         case LDAP_REFERRALS_REQUIRED:
1271                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1272                                 break;
1273
1274                         default:
1275                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1276                                 return LDAP_PROTOCOL_ERROR;
1277                         }
1278                 }
1279
1280                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1281                         rs->sr_text = "Chaining behavior control: decoding error";
1282                         return LDAP_PROTOCOL_ERROR;
1283                 }
1284
1285                 (void) ber_free( ber, 1 );
1286         }
1287
1288         op->o_chaining = mode | ( ctrl->ldctl_iscritical
1289                         ? SLAP_CONTROL_CRITICAL
1290                         : SLAP_CONTROL_NONCRITICAL );
1291
1292         return LDAP_SUCCESS;
1293 }
1294 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1295
1296 static slap_overinst ldapchain;
1297
1298 int
1299 chain_init( void )
1300 {
1301         int     rc;
1302
1303 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1304         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1305                         /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1306                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
1307         if ( rc != LDAP_SUCCESS ) {
1308                 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
1309                 return rc;
1310         }
1311 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1312
1313         ldapchain.on_bi.bi_type = "chain";
1314         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1315         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1316         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1317         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1318
1319         /* ... otherwise the underlying backend's function would be called,
1320          * likely passing an invalid entry; on the contrary, the requested
1321          * operational attributes should have been returned while chasing
1322          * the referrals.  This all in all is a bit messy, because part
1323          * of the operational attributes are generated by the backend;
1324          * part by the frontend; back-ldap should receive all the available
1325          * ones from the remote server, but then, on its own, it strips those
1326          * it assumes will be (re)generated by the frontend (e.g.
1327          * subschemaSubentry.) */
1328         ldapchain.on_bi.bi_operational = ldap_chain_operational;
1329         
1330         ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1331
1332         ldapchain.on_response = ldap_chain_response;
1333
1334         ldapchain.on_bi.bi_cf_ocs = chainocs;
1335
1336         rc = config_register_schema( chaincfg, chainocs );
1337         if ( rc ) {
1338                 return rc;
1339         }
1340
1341         return overlay_register( &ldapchain );
1342 }
1343