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