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