]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
fix previous commit :)
[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 );
136                 assert( op->o_ctrls[ 0 ] );
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 static ConfigDriver chain_cf_gen;
683 static ConfigLDAPadd chain_ldadd;
684 static ConfigCfAdd chain_cfadd;
685
686 static ConfigTable chaincfg[] = {
687 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
688         { "chain-chaining", "args",
689                 2, 4, 0, ARG_MAGIC|ARG_BERVAL|PC_CHAINING, chain_cf_gen,
690                 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
691                         "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
692                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
693 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
694         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
695 };
696
697 static ConfigOCs chainocs[] = {
698         { "( OLcfgOvOc:3.1 "
699                 "NAME 'olcChainConfig' "
700                 "DESC 'Chain configuration' "
701                 "SUP olcOverlayConfig "
702                 "MAY olcChainingBehavior )", Cft_Overlay, chaincfg, NULL, chain_cfadd },
703         { "( OLcfgOvOc:3.2 "
704                 "NAME 'olcChainDatabase' "
705                 "DESC 'Chain remote server configuration' "
706                 "AUXILIARY )", Cft_Misc, chaincfg, chain_ldadd },
707         { NULL, 0, NULL }
708 };
709
710 static int
711 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
712 {
713         if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
714                 p->ce_bi->bi_cf_ocs != chainocs )
715                 return LDAP_CONSTRAINT_VIOLATION;
716
717         return LDAP_SUCCESS;
718 }
719
720 static int
721 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
722 {
723         CfEntryInfo     *pe = p->e_private;
724         slap_overinst   *on = (slap_overinst *)pe->ce_bi;
725         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
726         void            *priv = (void *)ca->be->be_private;
727         struct berval   bv;
728
729         /* FIXME: should not hardcode "olcDatabase" here */
730         bv.bv_len = sprintf( ca->msg, "olcDatabase=%s", lback->bi_type );
731         bv.bv_val = ca->msg;
732
733         /* We can only create this entry if the database is table-driven */
734         if ( lback->bi_cf_ocs ) {
735                 ca->be->be_private = (void *)lc->lc_li;
736                 config_build_entry( op, rs, pe, ca, &bv, lback->bi_cf_ocs, &chainocs[1] );
737                 ca->be->be_private = priv;
738         }
739
740         return 0;
741 }
742
743 static slap_verbmasks chaining_mode[] = {
744         { BER_BVC("referralsRequired"),         LDAP_REFERRALS_REQUIRED },
745         { BER_BVC("referralsPreferred"),        LDAP_REFERRALS_PREFERRED },
746         { BER_BVC("chainingRequired"),          LDAP_CHAINING_REQUIRED },
747         { BER_BVC("chainingPreferred"),         LDAP_CHAINING_PREFERRED },
748         { BER_BVNULL,                           0 }
749 };
750
751 static int
752 chain_cf_gen( ConfigArgs *c )
753 {
754         slap_overinst   *on = (slap_overinst *)c->bi;
755         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
756
757         int             rc = 0;
758
759         if ( c->op == SLAP_CONFIG_EMIT ) {
760                 switch( c->type ) {
761 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
762                 case PC_CHAINING: {
763                         struct berval   resolve = BER_BVNULL,
764                                         continuation = BER_BVNULL;
765
766                         if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
767                                 return 1;
768                         }
769
770                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
771                         enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
772
773                         c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
774                                 + STRLENOF( " " )
775                                 + STRLENOF( "continuation=" ) + continuation.bv_len;
776                         c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
777                         snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
778                                 "resolve=%s continuation=%s",
779                                 resolve.bv_val, continuation.bv_val );
780
781                         if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
782                                 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
783                                         c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
784                                 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
785                                         " critical", STRLENOF( " critical" ) + 1 );
786                                 c->value_bv.bv_len += STRLENOF( " critical" );
787                         }
788
789                         break;
790                 }
791 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
792
793                 default:
794                         assert( 0 );
795                         rc = 1;
796                 }
797                 return rc;
798
799         } else if ( c->op == LDAP_MOD_DELETE ) {
800                 return 1;       /* FIXME */
801 #if 0
802                 switch( c->type ) {
803                 case PC_ATTR:
804                 case PC_TEMP:
805                 }
806                 return rc;
807 #endif
808         }
809
810         switch( c->type ) {
811 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
812         case PC_CHAINING: {
813                 char                    **argv = c->argv;
814                 int                     argc = c->argc;
815                 BerElementBuffer        berbuf;
816                 BerElement              *ber = (BerElement *)&berbuf;
817                 int                     resolve = -1,
818                                         continuation = -1,
819                                         iscritical = 0;
820                 Operation               op = { 0 };
821                 SlapReply               rs = { 0 };
822
823                 lc->lc_chaining_ctrlflag = 0;
824
825                 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
826                         if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
827                                 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
828                                 if ( resolve == -1 ) {
829                                         fprintf( stderr, "%s line %d: "
830                                                 "illegal <resolve> value %s "
831                                                 "in \"chain-chaining>\"\n",
832                                                 c->fname, c->lineno, argv[ 0 ] );
833                                         return 1;
834                                 }
835
836                         } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
837                                 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
838                                 if ( continuation == -1 ) {
839                                         fprintf( stderr, "%s line %d: "
840                                                 "illegal <continuation> value %s "
841                                                 "in \"chain-chaining\"\n",
842                                                 c->fname, c->lineno, argv[ 0 ] );
843                                         return 1;
844                                 }
845
846                         } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
847                                 iscritical = 1;
848
849                         } else {
850                                 fprintf( stderr, "%s line %d: "
851                                         "unknown option in \"chain-chaining\"\n",
852                                         c->fname, c->lineno );
853                                 return 1;
854                         }
855                 }
856
857                 if ( resolve != -1 || continuation != -1 ) {
858                         int     err;
859
860                         if ( resolve == -1 ) {
861                                 /* default */
862                                 resolve = SLAP_CHAINING_DEFAULT;
863                         }
864
865                         ber_init2( ber, NULL, LBER_USE_DER );
866
867                         err = ber_printf( ber, "{e" /* } */, resolve );
868                         if ( err == -1 ) {
869                                 ber_free( ber, 1 );
870                                 fprintf( stderr, "%s line %d: "
871                                         "chaining behavior control encoding error!\n",
872                                         c->fname, c->lineno );
873                                 return 1;
874                         }
875
876                         if ( continuation > -1 ) {
877                                 err = ber_printf( ber, "e", continuation );
878                                 if ( err == -1 ) {
879                                         ber_free( ber, 1 );
880                                         fprintf( stderr, "%s line %d: "
881                                                 "chaining behavior control encoding error!\n",
882                                                 c->fname, c->lineno );
883                                         return 1;
884                                 }
885                         }
886
887                         err = ber_printf( ber, /* { */ "N}" );
888                         if ( err == -1 ) {
889                                 ber_free( ber, 1 );
890                                 fprintf( stderr, "%s line %d: "
891                                         "chaining behavior control encoding error!\n",
892                                         c->fname, c->lineno );
893                                 return 1;
894                         }
895
896                         if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
897                                 exit( EXIT_FAILURE );
898                         }
899
900                 } else {
901                         BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
902                 }
903
904                 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
905                 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
906
907                 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
908                 {
909                         fprintf( stderr, "%s line %d: "
910                                 "unable to parse chaining control%s%s\n",
911                                 c->fname, c->lineno,
912                                 rs.sr_text ? ": " : "",
913                                 rs.sr_text ? rs.sr_text : "" );
914                         return 1;
915                 }
916
917                 lc->lc_chaining_ctrlflag = op.o_chaining;
918
919                 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
920
921                 rc = 0;
922
923                 break;
924         }
925 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
926
927         default:
928                 assert( 0 );
929                 return 1;
930         }
931         return rc;
932 }
933
934 static int
935 ldap_chain_db_config(
936         BackendDB       *be,
937         const char      *fname,
938         int             lineno,
939         int             argc,
940         char            **argv )
941 {
942         slap_overinst   *on = (slap_overinst *) be->bd_info;
943         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
944         char            *argv0 = NULL;
945         int             rc;
946         BackendDB       db = *be;
947
948         if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
949                 argv0 = argv[ 0 ];
950                 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
951
952 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
953                 if ( strcasecmp( argv[ 0 ], "chaining" ) == 0 ) {
954                         char                    **tmpargv = argv;
955                         BerElementBuffer        berbuf;
956                         BerElement              *ber = (BerElement *)&berbuf;
957                         int                     resolve = -1,
958                                                 continuation = -1,
959                                                 iscritical = 0;
960                         Operation               op = { 0 };
961                         SlapReply               rs = { 0 };
962
963                         lc->lc_chaining_ctrlflag = 0;
964
965                         for ( argc--, tmpargv++; argc > 0; argc--, tmpargv++ ) {
966                                 if ( strncasecmp( tmpargv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
967                                         resolve = str2chain( tmpargv[ 0 ] + STRLENOF( "resolve=" ) );
968                                         if ( resolve == -1 ) {
969                                                 fprintf( stderr, "%s line %d: "
970                                                         "illegal <resolve> value %s "
971                                                         "in \"chain-chaining>\"\n",
972                                                         fname, lineno, tmpargv[ 0 ] );
973                                                 return 1;
974                                         }
975
976                                 } else if ( strncasecmp( tmpargv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
977                                         continuation = str2chain( tmpargv[ 0 ] + STRLENOF( "continuation=" ) );
978                                         if ( continuation == -1 ) {
979                                                 fprintf( stderr, "%s line %d: "
980                                                         "illegal <continuation> value %s "
981                                                         "in \"chain-chaining\"\n",
982                                                         fname, lineno, tmpargv[ 0 ] );
983                                                 return 1;
984                                         }
985
986                                 } else if ( strcasecmp( tmpargv[ 0 ], "critical" ) == 0 ) {
987                                         iscritical = 1;
988
989                                 } else {
990                                         fprintf( stderr, "%s line %d: "
991                                                 "unknown option in \"chain-chaining\"\n",
992                                                 fname, lineno );
993                                         return 1;
994                                 }
995                         }
996
997                         if ( resolve != -1 || continuation != -1 ) {
998                                 int     err;
999
1000                                 if ( resolve == -1 ) {
1001                                         /* default */
1002                                         resolve = SLAP_CHAINING_DEFAULT;
1003                                 }
1004
1005                                 ber_init2( ber, NULL, LBER_USE_DER );
1006
1007                                 err = ber_printf( ber, "{e" /* } */, resolve );
1008                                 if ( err == -1 ) {
1009                                         ber_free( ber, 1 );
1010                                         fprintf( stderr, "%s line %d: "
1011                                                 "chaining behavior control encoding error!\n",
1012                                                 fname, lineno );
1013                                         return 1;
1014                                 }
1015
1016                                 if ( continuation > -1 ) {
1017                                         err = ber_printf( ber, "e", continuation );
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
1027                                 err = ber_printf( ber, /* { */ "N}" );
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                                 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1037                                         exit( EXIT_FAILURE );
1038                                 }
1039
1040                         } else {
1041                                 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1042                         }
1043
1044                         lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1045                         lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1046
1047                         if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1048                         {
1049                                 fprintf( stderr, "%s line %d: "
1050                                         "unable to parse chaining control%s%s\n",
1051                                         fname, lineno,
1052                                         rs.sr_text ? ": " : "",
1053                                         rs.sr_text ? rs.sr_text : "" );
1054                                 return 1;
1055                         }
1056
1057                         lc->lc_chaining_ctrlflag = op.o_chaining;
1058
1059                         lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1060
1061                         rc = 0;
1062                         goto done;
1063                 }
1064 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1065         }
1066
1067         db.be_cf_ocs = lback->bi_cf_ocs;
1068         db.be_private = lc->lc_li;
1069         rc = lback->bi_db_config( &db, fname, lineno, argc, argv );
1070
1071 done:;
1072         if ( argv0 ) {
1073                 argv[ 0 ] = argv0;
1074         }
1075
1076         return rc;
1077 }
1078
1079 static int
1080 ldap_chain_db_init(
1081         BackendDB *be )
1082 {
1083         slap_overinst   *on = (slap_overinst *)be->bd_info;
1084         ldap_chain_t    *lc = NULL;
1085         int             rc;
1086         BackendDB       bd = *be;
1087
1088         if ( lback == NULL ) {
1089                 lback = backend_info( "ldap" );
1090
1091                 if ( lback == NULL ) {
1092                         return -1;
1093                 }
1094         }
1095
1096         lc = ch_malloc( sizeof( ldap_chain_t ) );
1097         memset( lc, 0, sizeof( ldap_chain_t ) );
1098
1099         bd.be_private = NULL;
1100         rc = lback->bi_db_init( &bd );
1101         lc->lc_li = (struct ldapinfo *)bd.be_private;
1102         on->on_bi.bi_private = (void *)lc;
1103
1104         return rc;
1105 }
1106
1107 static int
1108 ldap_chain_db_open(
1109         BackendDB *be
1110 )
1111 {
1112         int     rc = 0;
1113
1114 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1115         rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1116 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1117
1118         return rc;
1119 }
1120
1121 static int
1122 ldap_chain_db_destroy(
1123         BackendDB *be
1124 )
1125 {
1126         slap_overinst   *on = (slap_overinst *) be->bd_info;
1127         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1128         void            *private = be->be_private;
1129         int             rc;
1130
1131         be->be_private = (void *)lc->lc_li;
1132         rc = lback->bi_db_destroy( be );
1133         lc->lc_li = be->be_private;
1134         ch_free( lc );
1135         on->on_bi.bi_private = NULL;
1136         be->be_private = private;
1137         return rc;
1138 }
1139
1140 static int
1141 ldap_chain_connection_destroy(
1142         BackendDB *be,
1143         Connection *conn
1144 )
1145 {
1146         slap_overinst   *on = (slap_overinst *) be->bd_info;
1147         ldap_chain_t    *lc = (ldap_chain_t *)on->on_bi.bi_private;
1148         void            *private = be->be_private;
1149         int             rc;
1150
1151         be->be_private = (void *)lc->lc_li;
1152         rc = lback->bi_connection_destroy( be, conn );
1153         be->be_private = private;
1154
1155         return rc;
1156 }
1157
1158 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1159 static int
1160 ldap_chain_parse_ctrl(
1161         Operation       *op,
1162         SlapReply       *rs,
1163         LDAPControl     *ctrl )
1164 {
1165         ber_tag_t       tag;
1166         BerElement      *ber;
1167         ber_int_t       mode,
1168                         behavior;
1169
1170         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1171                 rs->sr_text = "Chaining behavior control specified multiple times";
1172                 return LDAP_PROTOCOL_ERROR;
1173         }
1174
1175         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1176                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1177                 return LDAP_PROTOCOL_ERROR;
1178         }
1179
1180         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1181                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1182
1183         } else {
1184                 ber_len_t       len;
1185
1186                 /* Parse the control value
1187                  *      ChainingBehavior ::= SEQUENCE { 
1188                  *           resolveBehavior         Behavior OPTIONAL, 
1189                  *           continuationBehavior    Behavior OPTIONAL } 
1190                  *                             
1191                  *      Behavior :: = ENUMERATED { 
1192                  *           chainingPreferred       (0), 
1193                  *           chainingRequired        (1), 
1194                  *           referralsPreferred      (2), 
1195                  *           referralsRequired       (3) } 
1196                  */
1197
1198                 ber = ber_init( &ctrl->ldctl_value );
1199                 if( ber == NULL ) {
1200                         rs->sr_text = "internal error";
1201                         return LDAP_OTHER;
1202                 }
1203
1204                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1205                 /* FIXME: since the whole SEQUENCE is optional,
1206                  * should we accept no enumerations at all? */
1207                 if ( tag != LBER_ENUMERATED ) {
1208                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1209                         return LDAP_PROTOCOL_ERROR;
1210                 }
1211
1212                 switch ( behavior ) {
1213                 case LDAP_CHAINING_PREFERRED:
1214                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1215                         break;
1216
1217                 case LDAP_CHAINING_REQUIRED:
1218                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1219                         break;
1220
1221                 case LDAP_REFERRALS_PREFERRED:
1222                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1223                         break;
1224
1225                 case LDAP_REFERRALS_REQUIRED:
1226                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1227                         break;
1228
1229                 default:
1230                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1231                         return LDAP_PROTOCOL_ERROR;
1232                 }
1233
1234                 tag = ber_peek_tag( ber, &len );
1235                 if ( tag == LBER_ENUMERATED ) {
1236                         tag = ber_scanf( ber, "e", &behavior );
1237                         if ( tag == LBER_ERROR ) {
1238                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1239                                 return LDAP_PROTOCOL_ERROR;
1240                         }
1241                 }
1242
1243                 if ( tag == LBER_DEFAULT ) {
1244                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
1245
1246                 } else {
1247                         switch ( behavior ) {
1248                         case LDAP_CHAINING_PREFERRED:
1249                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1250                                 break;
1251
1252                         case LDAP_CHAINING_REQUIRED:
1253                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1254                                 break;
1255
1256                         case LDAP_REFERRALS_PREFERRED:
1257                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1258                                 break;
1259
1260                         case LDAP_REFERRALS_REQUIRED:
1261                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1262                                 break;
1263
1264                         default:
1265                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1266                                 return LDAP_PROTOCOL_ERROR;
1267                         }
1268                 }
1269
1270                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1271                         rs->sr_text = "Chaining behavior control: decoding error";
1272                         return LDAP_PROTOCOL_ERROR;
1273                 }
1274
1275                 (void) ber_free( ber, 1 );
1276         }
1277
1278         op->o_chaining = mode | ( ctrl->ldctl_iscritical
1279                         ? SLAP_CONTROL_CRITICAL
1280                         : SLAP_CONTROL_NONCRITICAL );
1281
1282         return LDAP_SUCCESS;
1283 }
1284 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1285
1286 static slap_overinst ldapchain;
1287
1288 int
1289 chain_init( void )
1290 {
1291 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1292         int     rc;
1293
1294         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1295                         /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1296                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
1297         if ( rc != LDAP_SUCCESS ) {
1298                 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
1299                 return rc;
1300         }
1301 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1302
1303         ldapchain.on_bi.bi_type = "chain";
1304         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1305         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1306         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1307         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1308
1309         /* ... otherwise the underlying backend's function would be called,
1310          * likely passing an invalid entry; on the contrary, the requested
1311          * operational attributes should have been returned while chasing
1312          * the referrals.  This all in all is a bit messy, because part
1313          * of the operational attributes are generated by the backend;
1314          * part by the frontend; back-ldap should receive all the available
1315          * ones from the remote server, but then, on its own, it strips those
1316          * it assumes will be (re)generated by the frontend (e.g.
1317          * subschemaSubentry.) */
1318         ldapchain.on_bi.bi_operational = ldap_chain_operational;
1319         
1320         ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1321
1322         ldapchain.on_response = ldap_chain_response;
1323
1324         ldapchain.on_bi.bi_cf_ocs = chainocs;
1325
1326         rc = config_register_schema( chaincfg, chainocs );
1327         if ( rc ) {
1328                 return rc;
1329         }
1330
1331         return overlay_register( &ldapchain );
1332 }
1333