]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
Sync with HEAD
[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                 /* slap_mods2entry () should be called in do_add() */
445                 assert( op->ora_e->e_attrs != NULL );
446                 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
447                 break;
448         case LDAP_REQ_DELETE:
449                 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
450                 break;
451         case LDAP_REQ_MODRDN:
452                 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
453                 break;
454         case LDAP_REQ_MODIFY:
455                 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
456                 break;
457         case LDAP_REQ_COMPARE:
458                 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
459                 break;
460         case LDAP_REQ_SEARCH:
461                 if ( rs->sr_type == REP_SEARCHREF ) {
462                         struct berval   *curr = ref,
463                                         odn = op->o_req_dn,
464                                         ondn = op->o_req_ndn;
465
466 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
467                         LDAPControl     **ctrls = NULL;
468         
469                         (void)chaining_control_add( lc, op, &ctrls );
470 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
471
472                         rs->sr_type = REP_SEARCH;
473
474                         sc2.sc_response = ldap_chain_cb_search_response;
475
476                         li = *lip;
477                         li.url = NULL;
478                         op->o_bd->be_private = &li;
479                         
480                         /* if we parse the URI then by no means 
481                          * we can cache stuff or reuse connections, 
482                          * because in back-ldap there's no caching
483                          * based on the URI value, which is supposed
484                          * to be set once for all (correct?) */
485                         op->o_do_not_cache = 1;
486
487                         /* copy the private info because we need to modify it */
488                         for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
489                                 LDAPURLDesc     *srv;
490                                 char            *save_dn;
491
492                                 /* parse reference and use
493                                  * proto://[host][:port]/ only */
494                                 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
495                                 if ( rc != LDAP_URL_SUCCESS ) {
496                                         /* try next */
497                                         rs->sr_err = LDAP_OTHER;
498                                         continue;
499                                 }
500
501                                 /* remove DN essentially because later on 
502                                  * ldap_initialize() will parse the URL 
503                                  * as a comma-separated URL list */
504                                 save_dn = srv->lud_dn;
505                                 srv->lud_dn = "";
506                                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
507                                 li.url = ldap_url_desc2str( srv );
508                                 if ( li.url != NULL ) {
509                                         ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
510                                                         op->o_tmpmemctx );
511                                         ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
512                                                         op->o_tmpmemctx );
513                                 }
514
515                                 srv->lud_dn = save_dn;
516                                 ldap_free_urldesc( srv );
517
518                                 if ( li.url == NULL ) {
519                                         /* try next */
520                                         rs->sr_err = LDAP_OTHER;
521                                         continue;
522                                 }
523
524
525                                 /* FIXME: should we also copy filter and scope?
526                                  * according to RFC3296, no */
527                                 rc = lback->bi_op_search( op, rs );
528
529                                 ldap_memfree( li.url );
530                                 li.url = NULL;
531
532                                 op->o_tmpfree( op->o_req_dn.bv_val,
533                                                 op->o_tmpmemctx );
534                                 op->o_tmpfree( op->o_req_ndn.bv_val,
535                                                 op->o_tmpmemctx );
536
537                                 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
538                                         break;
539                                 }
540
541                                 rc = rs->sr_err;
542                         }
543
544 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
545                         (void)chaining_control_remove( op, &ctrls );
546 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
547
548                         op->o_req_dn = odn;
549                         op->o_req_ndn = ondn;
550                         rs->sr_type = REP_SEARCHREF;
551                         rs->sr_entry = NULL;
552
553                         if ( rc != LDAP_SUCCESS ) {
554                                 /* couldn't chase any of the referrals */
555                                 rc = SLAP_CB_CONTINUE;
556                         }
557                         
558                 } else {
559                         rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
560                 }
561                 break;
562         case LDAP_REQ_EXTENDED:
563                 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
564                 /* FIXME: ldap_back_extended() by design 
565                  * doesn't send result; frontend is expected
566                  * to send it... */
567                 if ( rc != SLAPD_ABANDON ) {
568                         send_ldap_extended( op, rs );
569                         rc = LDAP_SUCCESS;
570                 }
571                 break;
572         default:
573                 rc = SLAP_CB_CONTINUE;
574                 break;
575         }
576
577 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
578         if ( rc != LDAP_SUCCESS || sc2.sc_private == LDAP_CH_ERR ) {
579                 if ( rs->sr_err == LDAP_CANNOT_CHAIN ) {
580                         goto cannot_chain;
581                 }
582
583                 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
584                 case LDAP_CHAINING_REQUIRED:
585 cannot_chain:;
586                         op->o_callback = NULL;
587                         send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" );
588                         break;
589
590                 default:
591                         rc = SLAP_CB_CONTINUE;
592                         rs->sr_err = sr_err;
593                         rs->sr_type = sr_type;
594                         break;
595                 }
596                 goto dont_chain;
597         }
598 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
599
600         if ( sc2.sc_private == LDAP_CH_NONE ) {
601                 op->o_callback = NULL;
602                 rc = rs->sr_err = slap_map_api2result( rs );
603                 send_ldap_result( op, rs );
604         }
605
606 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
607 dont_chain:;
608 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
609         op->o_do_not_cache = cache;
610         op->o_bd->be_private = private;
611         op->o_callback = sc;
612         op->o_ndn = ndn;
613         rs->sr_ref = ref;
614
615         return rc;
616 }
617
618 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
619 static int
620 ldap_chain_parse_ctrl(
621         Operation       *op,
622         SlapReply       *rs,
623         LDAPControl     *ctrl );
624
625 static int
626 str2chain( const char *s )
627 {
628         if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
629                 return LDAP_CHAINING_PREFERRED;
630                 
631         } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
632                 return LDAP_CHAINING_REQUIRED;
633
634         } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
635                 return LDAP_REFERRALS_PREFERRED;
636                 
637         } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
638                 return LDAP_REFERRALS_REQUIRED;
639         }
640
641         return -1;
642 }
643 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
644
645 enum {
646         PC_CHAINING = 1
647 };
648
649 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
650 static ConfigDriver chain_cf_gen;
651 static ConfigCfAdd chain_cfadd;
652 #endif
653 static ConfigLDAPadd chain_ldadd;
654
655 static ConfigTable chaincfg[] = {
656 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
657         { "chain-chaining", "args",
658                 2, 4, 0, ARG_MAGIC|ARG_BERVAL|PC_CHAINING, chain_cf_gen,
659                 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
660                         "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
661                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
662 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
663         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
664 };
665
666 static ConfigOCs chainocs[] = {
667 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
668         { "( OLcfgOvOc:3.1 "
669                 "NAME 'olcChainConfig' "
670                 "DESC 'Chain configuration' "
671                 "SUP olcOverlayConfig "
672                 "MAY olcChainingBehavior )", Cft_Overlay, chaincfg, NULL, chain_cfadd },
673 #endif
674         { "( OLcfgOvOc:3.2 "
675                 "NAME 'olcChainDatabase' "
676                 "DESC 'Chain remote server configuration' "
677                 "AUXILIARY )", Cft_Misc, chaincfg, chain_ldadd },
678         { NULL, 0, NULL }
679 };
680
681 static int
682 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
683 {
684         if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
685                 p->ce_bi->bi_cf_ocs != chainocs )
686                 return LDAP_CONSTRAINT_VIOLATION;
687
688         return LDAP_SUCCESS;
689 }
690
691 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
692
693 static int
694 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
695 {
696         CfEntryInfo     *pe = p->e_private;
697         slap_overinst   *on = (slap_overinst *)pe->ce_bi;
698         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
699         void            *priv = (void *)ca->be->be_private;
700         struct berval   bv;
701
702         /* FIXME: should not hardcode "olcDatabase" here */
703         bv.bv_len = sprintf( ca->msg, "olcDatabase=%s", lback->bi_type );
704         bv.bv_val = ca->msg;
705
706         /* We can only create this entry if the database is table-driven */
707         if ( lback->bi_cf_ocs ) {
708                 ca->be->be_private = (void *)lc->lc_li;
709                 config_build_entry( op, rs, pe, ca, &bv, lback->bi_cf_ocs, &chainocs[1] );
710                 ca->be->be_private = priv;
711         }
712
713         return 0;
714 }
715
716 static slap_verbmasks chaining_mode[] = {
717         { BER_BVC("referralsRequired"),         LDAP_REFERRALS_REQUIRED },
718         { BER_BVC("referralsPreferred"),        LDAP_REFERRALS_PREFERRED },
719         { BER_BVC("chainingRequired"),          LDAP_CHAINING_REQUIRED },
720         { BER_BVC("chainingPreferred"),         LDAP_CHAINING_PREFERRED },
721         { BER_BVNULL,                           0 }
722 };
723
724 static int
725 chain_cf_gen( ConfigArgs *c )
726 {
727         slap_overinst   *on = (slap_overinst *)c->bi;
728 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
729         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
730 #endif
731
732         int             rc = 0;
733
734         if ( c->op == SLAP_CONFIG_EMIT ) {
735                 switch( c->type ) {
736 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
737                 case PC_CHAINING: {
738                         struct berval   resolve = BER_BVNULL,
739                                         continuation = BER_BVNULL;
740
741                         if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
742                                 return 1;
743                         }
744
745                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
746                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
747
748                         c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
749                                 + STRLENOF( " " )
750                                 + STRLENOF( "continuation=" ) + continuation.bv_len;
751                         c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
752                         snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
753                                 "resolve=%s continuation=%s",
754                                 resolve.bv_val, continuation.bv_val );
755
756                         if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
757                                 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
758                                         c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
759                                 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
760                                         " critical", STRLENOF( " critical" ) + 1 );
761                                 c->value_bv.bv_len += STRLENOF( " critical" );
762                         }
763
764                         break;
765                 }
766 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
767
768                 default:
769                         assert( 0 );
770                         rc = 1;
771                 }
772                 return rc;
773
774         } else if ( c->op == LDAP_MOD_DELETE ) {
775                 return 1;       /* FIXME */
776 #if 0
777                 switch( c->type ) {
778                 case PC_ATTR:
779                 case PC_TEMP:
780                 }
781                 return rc;
782 #endif
783         }
784
785         switch( c->type ) {
786 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
787         case PC_CHAINING: {
788                 char                    **argv = c->argv;
789                 int                     argc = c->argc;
790                 BerElementBuffer        berbuf;
791                 BerElement              *ber = (BerElement *)&berbuf;
792                 int                     resolve = -1,
793                                         continuation = -1,
794                                         iscritical = 0;
795                 Operation               op = { 0 };
796                 SlapReply               rs = { 0 };
797
798                 lc->lc_chaining_ctrlflag = 0;
799
800                 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
801                         if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
802                                 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
803                                 if ( resolve == -1 ) {
804                                         fprintf( stderr, "%s line %d: "
805                                                 "illegal <resolve> value %s "
806                                                 "in \"chain-chaining>\"\n",
807                                                 c->fname, c->lineno, argv[ 0 ] );
808                                         return 1;
809                                 }
810
811                         } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
812                                 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
813                                 if ( continuation == -1 ) {
814                                         fprintf( stderr, "%s line %d: "
815                                                 "illegal <continuation> value %s "
816                                                 "in \"chain-chaining\"\n",
817                                                 c->fname, c->lineno, argv[ 0 ] );
818                                         return 1;
819                                 }
820
821                         } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
822                                 iscritical = 1;
823
824                         } else {
825                                 fprintf( stderr, "%s line %d: "
826                                         "unknown option in \"chain-chaining\"\n",
827                                         c->fname, c->lineno );
828                                 return 1;
829                         }
830                 }
831
832                 if ( resolve != -1 || continuation != -1 ) {
833                         int     err;
834
835                         if ( resolve == -1 ) {
836                                 /* default */
837                                 resolve = SLAP_CHAINING_DEFAULT;
838                         }
839
840                         ber_init2( ber, NULL, LBER_USE_DER );
841
842                         err = ber_printf( ber, "{e" /* } */, resolve );
843                         if ( err == -1 ) {
844                                 ber_free( ber, 1 );
845                                 fprintf( stderr, "%s line %d: "
846                                         "chaining behavior control encoding error!\n",
847                                         c->fname, c->lineno );
848                                 return 1;
849                         }
850
851                         if ( continuation > -1 ) {
852                                 err = ber_printf( ber, "e", continuation );
853                                 if ( err == -1 ) {
854                                         ber_free( ber, 1 );
855                                         fprintf( stderr, "%s line %d: "
856                                                 "chaining behavior control encoding error!\n",
857                                                 c->fname, c->lineno );
858                                         return 1;
859                                 }
860                         }
861
862                         err = ber_printf( ber, /* { */ "N}" );
863                         if ( err == -1 ) {
864                                 ber_free( ber, 1 );
865                                 fprintf( stderr, "%s line %d: "
866                                         "chaining behavior control encoding error!\n",
867                                         c->fname, c->lineno );
868                                 return 1;
869                         }
870
871                         if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
872                                 exit( EXIT_FAILURE );
873                         }
874
875                 } else {
876                         BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
877                 }
878
879                 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
880                 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
881
882                 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
883                 {
884                         fprintf( stderr, "%s line %d: "
885                                 "unable to parse chaining control%s%s\n",
886                                 c->fname, c->lineno,
887                                 rs.sr_text ? ": " : "",
888                                 rs.sr_text ? rs.sr_text : "" );
889                         return 1;
890                 }
891
892                 lc->lc_chaining_ctrlflag = op.o_chaining;
893
894                 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
895
896                 rc = 0;
897
898                 break;
899         }
900 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
901
902         default:
903                 assert( 0 );
904                 return 1;
905         }
906         return rc;
907 }
908
909 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
910
911 static int
912 ldap_chain_db_config(
913         BackendDB       *be,
914         const char      *fname,
915         int             lineno,
916         int             argc,
917         char            **argv )
918 {
919         slap_overinst   *on = (slap_overinst *) be->bd_info;
920         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
921         char            *argv0 = NULL;
922         int             rc;
923         BackendDB       db = *be;
924
925         if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
926                 argv0 = argv[ 0 ];
927                 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
928
929 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
930                 if ( strcasecmp( argv[ 0 ], "chaining" ) == 0 ) {
931                         char                    **tmpargv = argv;
932                         BerElementBuffer        berbuf;
933                         BerElement              *ber = (BerElement *)&berbuf;
934                         int                     resolve = -1,
935                                                 continuation = -1,
936                                                 iscritical = 0;
937                         Operation               op = { 0 };
938                         SlapReply               rs = { 0 };
939
940                         lc->lc_chaining_ctrlflag = 0;
941
942                         for ( argc--, tmpargv++; argc > 0; argc--, tmpargv++ ) {
943                                 if ( strncasecmp( tmpargv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
944                                         resolve = str2chain( tmpargv[ 0 ] + STRLENOF( "resolve=" ) );
945                                         if ( resolve == -1 ) {
946                                                 fprintf( stderr, "%s line %d: "
947                                                         "illegal <resolve> value %s "
948                                                         "in \"chain-chaining>\"\n",
949                                                         fname, lineno, tmpargv[ 0 ] );
950                                                 return 1;
951                                         }
952
953                                 } else if ( strncasecmp( tmpargv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
954                                         continuation = str2chain( tmpargv[ 0 ] + STRLENOF( "continuation=" ) );
955                                         if ( continuation == -1 ) {
956                                                 fprintf( stderr, "%s line %d: "
957                                                         "illegal <continuation> value %s "
958                                                         "in \"chain-chaining\"\n",
959                                                         fname, lineno, tmpargv[ 0 ] );
960                                                 return 1;
961                                         }
962
963                                 } else if ( strcasecmp( tmpargv[ 0 ], "critical" ) == 0 ) {
964                                         iscritical = 1;
965
966                                 } else {
967                                         fprintf( stderr, "%s line %d: "
968                                                 "unknown option in \"chain-chaining\"\n",
969                                                 fname, lineno );
970                                         return 1;
971                                 }
972                         }
973
974                         if ( resolve != -1 || continuation != -1 ) {
975                                 int     err;
976
977                                 if ( resolve == -1 ) {
978                                         /* default */
979                                         resolve = SLAP_CHAINING_DEFAULT;
980                                 }
981
982                                 ber_init2( ber, NULL, LBER_USE_DER );
983
984                                 err = ber_printf( ber, "{e" /* } */, resolve );
985                                 if ( err == -1 ) {
986                                         ber_free( ber, 1 );
987                                         fprintf( stderr, "%s line %d: "
988                                                 "chaining behavior control encoding error!\n",
989                                                 fname, lineno );
990                                         return 1;
991                                 }
992
993                                 if ( continuation > -1 ) {
994                                         err = ber_printf( ber, "e", continuation );
995                                         if ( err == -1 ) {
996                                                 ber_free( ber, 1 );
997                                                 fprintf( stderr, "%s line %d: "
998                                                         "chaining behavior control encoding error!\n",
999                                                         fname, lineno );
1000                                                 return 1;
1001                                         }
1002                                 }
1003
1004                                 err = ber_printf( ber, /* { */ "N}" );
1005                                 if ( err == -1 ) {
1006                                         ber_free( ber, 1 );
1007                                         fprintf( stderr, "%s line %d: "
1008                                                 "chaining behavior control encoding error!\n",
1009                                                 fname, lineno );
1010                                         return 1;
1011                                 }
1012
1013                                 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1014                                         exit( EXIT_FAILURE );
1015                                 }
1016
1017                         } else {
1018                                 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1019                         }
1020
1021                         lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1022                         lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1023
1024                         if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1025                         {
1026                                 fprintf( stderr, "%s line %d: "
1027                                         "unable to parse chaining control%s%s\n",
1028                                         fname, lineno,
1029                                         rs.sr_text ? ": " : "",
1030                                         rs.sr_text ? rs.sr_text : "" );
1031                                 return 1;
1032                         }
1033
1034                         lc->lc_chaining_ctrlflag = op.o_chaining;
1035
1036                         lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1037
1038                         rc = 0;
1039                         goto done;
1040                 }
1041 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1042         }
1043
1044         db.be_cf_ocs = lback->bi_cf_ocs;
1045         db.be_private = lc->lc_li;
1046         rc = lback->bi_db_config( &db, fname, lineno, argc, argv );
1047
1048 done:;
1049         if ( argv0 ) {
1050                 argv[ 0 ] = argv0;
1051         }
1052
1053         return rc;
1054 }
1055
1056 static int
1057 ldap_chain_db_init(
1058         BackendDB *be )
1059 {
1060         slap_overinst   *on = (slap_overinst *)be->bd_info;
1061         ldap_chain_t    *lc = NULL;
1062         int             rc;
1063         BackendDB       bd = *be;
1064
1065         if ( lback == NULL ) {
1066                 lback = backend_info( "ldap" );
1067
1068                 if ( lback == NULL ) {
1069                         return -1;
1070                 }
1071         }
1072
1073         lc = ch_malloc( sizeof( ldap_chain_t ) );
1074         memset( lc, 0, sizeof( ldap_chain_t ) );
1075
1076         bd.be_private = NULL;
1077         rc = lback->bi_db_init( &bd );
1078         lc->lc_li = (struct ldapinfo *)bd.be_private;
1079         on->on_bi.bi_private = (void *)lc;
1080
1081         return rc;
1082 }
1083
1084 static int
1085 ldap_chain_db_open(
1086         BackendDB *be
1087 )
1088 {
1089         int     rc = 0;
1090
1091 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1092         rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1093 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1094
1095         return rc;
1096 }
1097
1098 static int
1099 ldap_chain_db_destroy(
1100         BackendDB *be
1101 )
1102 {
1103         slap_overinst   *on = (slap_overinst *) be->bd_info;
1104         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1105         void            *private = be->be_private;
1106         int             rc;
1107
1108         be->be_private = (void *)lc->lc_li;
1109         rc = lback->bi_db_destroy( be );
1110         lc->lc_li = be->be_private;
1111         ch_free( lc );
1112         on->on_bi.bi_private = NULL;
1113         be->be_private = private;
1114         return rc;
1115 }
1116
1117 static int
1118 ldap_chain_connection_destroy(
1119         BackendDB *be,
1120         Connection *conn
1121 )
1122 {
1123         slap_overinst   *on = (slap_overinst *) be->bd_info;
1124         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1125         void            *private = be->be_private;
1126         int             rc;
1127
1128         be->be_private = (void *)lc->lc_li;
1129         rc = lback->bi_connection_destroy( be, conn );
1130         be->be_private = private;
1131
1132         return rc;
1133 }
1134
1135 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1136 static int
1137 ldap_chain_parse_ctrl(
1138         Operation       *op,
1139         SlapReply       *rs,
1140         LDAPControl     *ctrl )
1141 {
1142         ber_tag_t       tag;
1143         BerElement      *ber;
1144         ber_int_t       mode,
1145                         behavior;
1146
1147         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1148                 rs->sr_text = "Chaining behavior control specified multiple times";
1149                 return LDAP_PROTOCOL_ERROR;
1150         }
1151
1152         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1153                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1154                 return LDAP_PROTOCOL_ERROR;
1155         }
1156
1157         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1158                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1159
1160         } else {
1161                 ber_len_t       len;
1162
1163                 /* Parse the control value
1164                  *      ChainingBehavior ::= SEQUENCE { 
1165                  *           resolveBehavior         Behavior OPTIONAL, 
1166                  *           continuationBehavior    Behavior OPTIONAL } 
1167                  *                             
1168                  *      Behavior :: = ENUMERATED { 
1169                  *           chainingPreferred       (0), 
1170                  *           chainingRequired        (1), 
1171                  *           referralsPreferred      (2), 
1172                  *           referralsRequired       (3) } 
1173                  */
1174
1175                 ber = ber_init( &ctrl->ldctl_value );
1176                 if( ber == NULL ) {
1177                         rs->sr_text = "internal error";
1178                         return LDAP_OTHER;
1179                 }
1180
1181                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1182                 /* FIXME: since the whole SEQUENCE is optional,
1183                  * should we accept no enumerations at all? */
1184                 if ( tag != LBER_ENUMERATED ) {
1185                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1186                         return LDAP_PROTOCOL_ERROR;
1187                 }
1188
1189                 switch ( behavior ) {
1190                 case LDAP_CHAINING_PREFERRED:
1191                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1192                         break;
1193
1194                 case LDAP_CHAINING_REQUIRED:
1195                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1196                         break;
1197
1198                 case LDAP_REFERRALS_PREFERRED:
1199                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1200                         break;
1201
1202                 case LDAP_REFERRALS_REQUIRED:
1203                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1204                         break;
1205
1206                 default:
1207                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1208                         return LDAP_PROTOCOL_ERROR;
1209                 }
1210
1211                 tag = ber_peek_tag( ber, &len );
1212                 if ( tag == LBER_ENUMERATED ) {
1213                         tag = ber_scanf( ber, "e", &behavior );
1214                         if ( tag == LBER_ERROR ) {
1215                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1216                                 return LDAP_PROTOCOL_ERROR;
1217                         }
1218                 }
1219
1220                 if ( tag == LBER_DEFAULT ) {
1221                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
1222
1223                 } else {
1224                         switch ( behavior ) {
1225                         case LDAP_CHAINING_PREFERRED:
1226                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1227                                 break;
1228
1229                         case LDAP_CHAINING_REQUIRED:
1230                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1231                                 break;
1232
1233                         case LDAP_REFERRALS_PREFERRED:
1234                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1235                                 break;
1236
1237                         case LDAP_REFERRALS_REQUIRED:
1238                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1239                                 break;
1240
1241                         default:
1242                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1243                                 return LDAP_PROTOCOL_ERROR;
1244                         }
1245                 }
1246
1247                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1248                         rs->sr_text = "Chaining behavior control: decoding error";
1249                         return LDAP_PROTOCOL_ERROR;
1250                 }
1251
1252                 (void) ber_free( ber, 1 );
1253         }
1254
1255         op->o_chaining = mode | ( ctrl->ldctl_iscritical
1256                         ? SLAP_CONTROL_CRITICAL
1257                         : SLAP_CONTROL_NONCRITICAL );
1258
1259         return LDAP_SUCCESS;
1260 }
1261 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1262
1263 static slap_overinst ldapchain;
1264
1265 int
1266 chain_init( void )
1267 {
1268         int     rc;
1269
1270 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1271         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1272                         /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1273                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
1274         if ( rc != LDAP_SUCCESS ) {
1275                 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
1276                 return rc;
1277         }
1278 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1279
1280         ldapchain.on_bi.bi_type = "chain";
1281         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1282         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1283         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1284         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1285
1286         /* ... otherwise the underlying backend's function would be called,
1287          * likely passing an invalid entry; on the contrary, the requested
1288          * operational attributes should have been returned while chasing
1289          * the referrals.  This all in all is a bit messy, because part
1290          * of the operational attributes are generated by the backend;
1291          * part by the frontend; back-ldap should receive all the available
1292          * ones from the remote server, but then, on its own, it strips those
1293          * it assumes will be (re)generated by the frontend (e.g.
1294          * subschemaSubentry.) */
1295         ldapchain.on_bi.bi_operational = ldap_chain_operational;
1296         
1297         ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1298
1299         ldapchain.on_response = ldap_chain_response;
1300
1301         ldapchain.on_bi.bi_cf_ocs = chainocs;
1302
1303         rc = config_register_schema( chaincfg, chainocs );
1304         if ( rc ) {
1305                 return rc;
1306         }
1307
1308         return overlay_register( &ldapchain );
1309 }
1310