]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/chain.c
6322cf375066a59ace30eb3efd8c817c094d079c
[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 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
33 #define SLAP_CH_RESOLVE_SHIFT                           SLAP_CONTROL_SHIFT
34 #define SLAP_CH_RESOLVE_MASK                            (0x3 << SLAP_CH_RESOLVE_SHIFT)
35 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED              (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
36 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED               (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
37 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED             (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
38 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED              (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
39 #define SLAP_CH_RESOLVE_DEFAULT                         SLAP_CH_RESOLVE_CHAINING_PREFERRED
40 #define SLAP_CH_CONTINUATION_SHIFT                      (SLAP_CH_RESOLVE_SHIFT + 2)
41 #define SLAP_CH_CONTINUATION_MASK                       (0x3 << SLAP_CH_CONTINUATION_SHIFT)
42 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED         (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
43 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED          (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
44 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED        (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
45 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED         (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
46 #define SLAP_CH_CONTINUATION_DEFAULT                    SLAP_CH_CONTINUATION_CHAINING_PREFERRED
47
48 #define o_chaining                      o_ctrlflag[sc_chainingBehavior]
49 #define get_chaining(op)                ((op)->o_chaining & SLAP_CONTROL_MASK)
50 #define get_chainingBehavior(op)        ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
51 #define get_resolveBehavior(op)         ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
52 #define get_continuationBehavior(op)    ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
53 #endif /*  LDAP_CONTROL_X_CHAINING_BEHAVIOR */
54
55 static int              sc_chainingBehavior;
56 static BackendInfo      *lback;
57
58 static int
59 ldap_chain_operational( Operation *op, SlapReply *rs )
60 {
61         /* Trap entries generated by back-ldap.
62          * 
63          * FIXME: we need a better way to recognize them; a cleaner
64          * solution would be to be able to intercept the response
65          * of be_operational(), so that we can divert only those
66          * calls that fail because operational attributes were
67          * requested for entries that do not belong to the underlying
68          * database.  This fix is likely to intercept also entries
69          * generated by back-perl and so. */
70         if ( rs->sr_entry->e_private == NULL ) {
71                 return 0;
72         }
73
74         return SLAP_CB_CONTINUE;
75 }
76
77 /*
78  * Search specific response that strips entryDN from entries
79  */
80 static int
81 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
82 {
83         assert( op->o_tag == LDAP_REQ_SEARCH );
84
85         if ( rs->sr_type == REP_SEARCH ) {
86                 Attribute       **ap = &rs->sr_entry->e_attrs;
87
88                 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
89                         /* will be generated later by frontend
90                          * (a cleaner solution would be that
91                          * the frontend checks if it already exists */
92                         if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
93                         {
94                                 Attribute *a = *ap;
95
96                                 *ap = (*ap)->a_next;
97                                 attr_free( a );
98
99                                 /* there SHOULD be one only! */
100                                 break;
101                         }
102                 }
103                 
104                 return SLAP_CB_CONTINUE;
105
106         } else if ( rs->sr_type == REP_RESULT ) {
107                 /* back-ldap tried to send result */
108                 op->o_callback->sc_private = (void *)(1);
109         }
110
111         return 0;
112 }
113
114 /*
115  * Dummy response that simply traces if back-ldap tried to send 
116  * anything to the client
117  */
118 static int
119 ldap_chain_cb_response( Operation *op, SlapReply *rs )
120 {
121         if ( rs->sr_type == REP_RESULT ) {
122                 op->o_callback->sc_private = (void *)(1);
123
124         } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
125         {
126                 /* strip the entryDN attribute, but keep returning results */
127                 (void)ldap_chain_cb_search_response( op, rs );
128         }
129
130         return SLAP_CB_CONTINUE;
131 }
132
133 static int
134 ldap_chain_op(
135         Operation       *op,
136         SlapReply       *rs,
137         int             ( *op_f )( Operation *op, SlapReply *rs ), 
138         BerVarray       ref )
139 {
140         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
141         struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
142
143         /* NOTE: returned if ref is empty... */
144         int             rc = LDAP_OTHER;
145
146         if ( lip->url != NULL ) {
147                 op->o_bd->be_private = on->on_bi.bi_private;
148                 return ( *op_f )( op, rs );
149         }
150
151         li = *lip;
152         op->o_bd->be_private = &li;
153
154         /* if we parse the URI then by no means 
155          * we can cache stuff or reuse connections, 
156          * because in back-ldap there's no caching
157          * based on the URI value, which is supposed
158          * to be set once for all (correct?) */
159         op->o_do_not_cache = 1;
160
161         for ( ; !BER_BVISNULL( ref ); ref++ ) {
162                 LDAPURLDesc     *srv;
163                 char            *save_dn;
164                         
165                 /* We're setting the URI of the first referral;
166                  * what if there are more?
167
168 Document: draft-ietf-ldapbis-protocol-27.txt
169
170 4.1.10. Referral 
171    ...
172    If the client wishes to progress the operation, it MUST follow the 
173    referral by contacting one of the supported services. If multiple 
174    URIs are present, the client assumes that any supported URI may be 
175    used to progress the operation. 
176
177                  * so we actually need to follow exactly one,
178                  * and we can assume any is fine.
179                  */
180         
181                 /* parse reference and use 
182                  * proto://[host][:port]/ only */
183                 rc = ldap_url_parse_ext( ref->bv_val, &srv );
184                 if ( rc != LDAP_URL_SUCCESS ) {
185                         /* try next */
186                         rc = LDAP_OTHER;
187                         continue;
188                 }
189
190                 /* remove DN essentially because later on 
191                  * ldap_initialize() will parse the URL 
192                  * as a comma-separated URL list */
193                 save_dn = srv->lud_dn;
194                 srv->lud_dn = "";
195                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
196                 li.url = ldap_url_desc2str( srv );
197                 srv->lud_dn = save_dn;
198                 ldap_free_urldesc( srv );
199
200                 if ( li.url == NULL ) {
201                         /* try next */
202                         rc = LDAP_OTHER;
203                         continue;
204                 }
205
206                 rc = ( *op_f )( op, rs );
207
208                 ldap_memfree( li.url );
209                 li.url = NULL;
210                 
211                 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
212                         break;
213                 }
214         }
215
216         return rc;
217 }
218
219 static int
220 ldap_chain_response( Operation *op, SlapReply *rs )
221 {
222         slap_overinst   *on = (slap_overinst *) op->o_bd->bd_info;
223         void            *private = op->o_bd->be_private;
224         slap_callback   *sc = op->o_callback,
225                         sc2 = { 0 };
226         int             rc = 0;
227         int             cache = op->o_do_not_cache;
228         BerVarray       ref;
229         struct berval   ndn = op->o_ndn;
230
231         struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
232
233 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
234         int             sr_err = rs->sr_err;
235         slap_reply_t    sr_type = rs->sr_type;
236         slap_mask_t     chain_mask = 0;
237         ber_len_t       chain_shift = 0;
238 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
239
240         if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
241                 return SLAP_CB_CONTINUE;
242         }
243
244 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
245         if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
246                 switch ( get_resolveBehavior( op ) ) {
247                 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
248                 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
249                         return SLAP_CB_CONTINUE;
250
251                 default:
252                         chain_mask = SLAP_CH_RESOLVE_MASK;
253                         chain_shift = SLAP_CH_RESOLVE_SHIFT;
254                         break;
255                 }
256
257         } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
258                 switch ( get_continuationBehavior( op ) ) {
259                 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
260                 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
261                         return SLAP_CB_CONTINUE;
262
263                 default:
264                         chain_mask = SLAP_CH_CONTINUATION_MASK;
265                         chain_shift = SLAP_CH_CONTINUATION_SHIFT;
266                         break;
267                 }
268         }
269 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
270
271         /*
272          * TODO: add checks on who/when chain operations; e.g.:
273          *   a) what identities are authorized
274          *   b) what request DN (e.g. only chain requests rooted at <DN>)
275          *   c) what referral URIs
276          *   d) what protocol scheme (e.g. only ldaps://)
277          *   e) what ssf
278          */
279
280         ref = rs->sr_ref;
281         rs->sr_ref = NULL;
282
283         /* we need this to know if back-ldap returned any result */
284         sc2.sc_response = ldap_chain_cb_response;
285         op->o_callback = &sc2;
286
287         /* Chaining can be performed by a privileged user on behalf
288          * of normal users, using the ProxyAuthz control, by exploiting
289          * the identity assertion feature of back-ldap; see idassert-*
290          * directives in slapd-ldap(5).
291          *
292          * FIXME: the idassert-authcDN is one, will it be fine regardless
293          * of the URI we obtain from the referral?
294          */
295
296         switch ( op->o_tag ) {
297         case LDAP_REQ_BIND: {
298                 struct berval   rndn = op->o_req_ndn;
299                 Connection      *conn = op->o_conn;
300
301                 /* FIXME: can we really get a referral for binds? */
302                 op->o_req_ndn = slap_empty_bv;
303                 op->o_conn = NULL;
304                 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
305                 op->o_req_ndn = rndn;
306                 op->o_conn = conn;
307                 }
308                 break;
309         case LDAP_REQ_ADD:
310                 {
311                 int             cleanup_attrs = 0;
312
313                 if ( op->ora_e->e_attrs == NULL ) {
314                         char            textbuf[ SLAP_TEXT_BUFLEN ];
315                         size_t          textlen = sizeof( textbuf );
316
317                         assert( SLAP_DBFLAGS( op->o_bd ) & SLAP_DBFLAG_GLOBAL_OVERLAY );
318
319                         /* global overlay: create entry */
320                         /* NOTE: this is a hack to use the chain overlay
321                          * as global.  I expect to be able to remove this
322                          * soon by using slap_mods2entry() earlier in
323                          * do_add(), adding the operational attrs later
324                          * if required. */
325                         rs->sr_err = slap_mods2entry( op->ora_modlist,
326                                         &op->ora_e, 0, 1,
327                                         &rs->sr_text, textbuf, textlen );
328                         if ( rs->sr_err != LDAP_SUCCESS ) {
329                                 send_ldap_result( op, rs );
330                                 rc = 1;
331                                 break;
332                         }
333                 }
334                 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
335                 if ( cleanup_attrs ) {
336                         attrs_free( op->ora_e->e_attrs );
337                         op->ora_e->e_attrs = NULL;
338                 }
339                 break;
340                 }
341         case LDAP_REQ_DELETE:
342                 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
343                 break;
344         case LDAP_REQ_MODRDN:
345                 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
346                 break;
347         case LDAP_REQ_MODIFY:
348                 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
349                 break;
350         case LDAP_REQ_COMPARE:
351                 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
352                 break;
353         case LDAP_REQ_SEARCH:
354                 if ( rs->sr_type == REP_SEARCHREF ) {
355                         struct berval   *curr = ref,
356                                         odn = op->o_req_dn,
357                                         ondn = op->o_req_ndn;
358
359                         rs->sr_type = REP_SEARCH;
360
361                         sc2.sc_response = ldap_chain_cb_search_response;
362
363                         li = *lip;
364                         li.url = NULL;
365                         op->o_bd->be_private = &li;
366                         
367                         /* if we parse the URI then by no means 
368                          * we can cache stuff or reuse connections, 
369                          * because in back-ldap there's no caching
370                          * based on the URI value, which is supposed
371                          * to be set once for all (correct?) */
372                         op->o_do_not_cache = 1;
373
374                         /* copy the private info because we need to modify it */
375                         for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
376                                 LDAPURLDesc     *srv;
377                                 char            *save_dn;
378
379                                 /* parse reference and use
380                                  * proto://[host][:port]/ only */
381                                 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
382                                 if ( rc != LDAP_URL_SUCCESS ) {
383                                         /* try next */
384                                         rs->sr_err = LDAP_OTHER;
385                                         continue;
386                                 }
387
388                                 /* remove DN essentially because later on 
389                                  * ldap_initialize() will parse the URL 
390                                  * as a comma-separated URL list */
391                                 save_dn = srv->lud_dn;
392                                 srv->lud_dn = "";
393                                 srv->lud_scope = LDAP_SCOPE_DEFAULT;
394                                 li.url = ldap_url_desc2str( srv );
395                                 if ( li.url != NULL ) {
396                                         ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
397                                                         op->o_tmpmemctx );
398                                         ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
399                                                         op->o_tmpmemctx );
400                                 }
401
402                                 srv->lud_dn = save_dn;
403                                 ldap_free_urldesc( srv );
404
405                                 if ( li.url == NULL ) {
406                                         /* try next */
407                                         rs->sr_err = LDAP_OTHER;
408                                         continue;
409                                 }
410
411
412                                 /* FIXME: should we also copy filter and scope?
413                                  * according to RFC3296, no */
414                                 rc = lback->bi_op_search( op, rs );
415
416                                 ldap_memfree( li.url );
417                                 li.url = NULL;
418
419                                 op->o_tmpfree( op->o_req_dn.bv_val,
420                                                 op->o_tmpmemctx );
421                                 op->o_tmpfree( op->o_req_ndn.bv_val,
422                                                 op->o_tmpmemctx );
423
424                                 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
425                                         break;
426                                 }
427                         }
428
429                         op->o_req_dn = odn;
430                         op->o_req_ndn = ondn;
431                         rs->sr_type = REP_SEARCHREF;
432                         rs->sr_entry = NULL;
433
434                         if ( rc != LDAP_SUCCESS ) {
435                                 /* couldn't chase any of the referrals */
436                                 rc = SLAP_CB_CONTINUE;
437                         }
438                         
439                 } else {
440                         rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
441                 }
442                 break;
443         case LDAP_REQ_EXTENDED:
444                 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
445                 /* FIXME: ldap_back_extended() by design 
446                  * doesn't send result; frontend is expected
447                  * to send it... */
448                 if ( rc != SLAPD_ABANDON ) {
449                         send_ldap_extended( op, rs );
450                         rc = LDAP_SUCCESS;
451                 }
452                 break;
453         default:
454                 rc = SLAP_CB_CONTINUE;
455                 break;
456         }
457
458 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
459         if ( rc != LDAP_SUCCESS ) {
460                 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
461                 case LDAP_CHAINING_REQUIRED:
462                         op->o_callback = NULL;
463                         send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" );
464                         break;
465
466                 default:
467                         rc = SLAP_CB_CONTINUE;
468                         rs->sr_err = sr_err;
469                         rs->sr_type = sr_type;
470                         break;
471                 }
472                 goto dont_chain;
473         }
474 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
475
476         if ( sc2.sc_private == NULL ) {
477                 op->o_callback = NULL;
478                 rc = rs->sr_err = slap_map_api2result( rs );
479                 send_ldap_result( op, rs );
480         }
481
482 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
483 dont_chain:;
484 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
485         op->o_do_not_cache = cache;
486         op->o_bd->be_private = private;
487         op->o_callback = sc;
488         op->o_ndn = ndn;
489         rs->sr_ref = ref;
490
491         return rc;
492 }
493
494 static int
495 ldap_chain_db_config(
496         BackendDB       *be,
497         const char      *fname,
498         int             lineno,
499         int             argc,
500         char    **argv
501 )
502 {
503         slap_overinst   *on = (slap_overinst *) be->bd_info;
504         void            *private = be->be_private;
505         char            *argv0 = NULL;
506         int             rc;
507
508         be->be_private = on->on_bi.bi_private;
509         if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
510                 argv0 = argv[ 0 ];
511                 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
512         }
513         rc = lback->bi_db_config( be, fname, lineno, argc, argv );
514         if ( argv0 ) {
515                 argv[ 0 ] = argv0;
516         }
517         
518         be->be_private = private;
519         return rc;
520 }
521
522 static int
523 ldap_chain_db_init(
524         BackendDB *be
525 )
526 {
527         slap_overinst   *on = (slap_overinst *)be->bd_info;
528         int             rc;
529         BackendDB       bd = *be;
530
531         if ( lback == NULL ) {
532                 lback = backend_info( "ldap" );
533
534                 if ( lback == NULL ) {
535                         return -1;
536                 }
537         }
538
539         bd.be_private = NULL;
540         rc = lback->bi_db_init( &bd );
541         on->on_bi.bi_private = bd.be_private;
542
543         return rc;
544 }
545
546 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
547 static int
548 ldap_chain_db_open(
549         BackendDB *be
550 )
551 {
552         return overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
553 }
554 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
555
556 static int
557 ldap_chain_db_destroy(
558         BackendDB *be
559 )
560 {
561         slap_overinst *on = (slap_overinst *) be->bd_info;
562         void *private = be->be_private;
563         int rc;
564
565         be->be_private = on->on_bi.bi_private;
566         rc = lback->bi_db_destroy( be );
567         on->on_bi.bi_private = be->be_private;
568         be->be_private = private;
569         return rc;
570 }
571
572 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
573 static int
574 ldap_chain_parse_ctrl(
575         Operation       *op,
576         SlapReply       *rs,
577         LDAPControl     *ctrl )
578 {
579         ber_tag_t       tag;
580         BerElement      *ber;
581         ber_int_t       mode,
582                         behavior;
583
584         if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
585                 rs->sr_text = "Chaining behavior control specified multiple times";
586                 return LDAP_PROTOCOL_ERROR;
587         }
588
589         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
590                 rs->sr_text = "Chaining behavior control specified with pagedResults control";
591                 return LDAP_PROTOCOL_ERROR;
592         }
593
594         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
595                 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
596
597         } else {
598                 ber_len_t       len;
599
600                 /* Parse the control value
601                  *      ChainingBehavior ::= SEQUENCE { 
602                  *           resolveBehavior         Behavior OPTIONAL, 
603                  *           continuationBehavior    Behavior OPTIONAL } 
604                  *                             
605                  *      Behavior :: = ENUMERATED { 
606                  *           chainingPreferred       (0), 
607                  *           chainingRequired        (1), 
608                  *           referralsPreferred      (2), 
609                  *           referralsRequired       (3) } 
610                  */
611
612                 ber = ber_init( &ctrl->ldctl_value );
613                 if( ber == NULL ) {
614                         rs->sr_text = "internal error";
615                         return LDAP_OTHER;
616                 }
617
618                 tag = ber_scanf( ber, "{e" /* } */, &behavior );
619                 /* FIXME: since the whole SEQUENCE is optional,
620                  * should we accept no enumerations at all? */
621                 if ( tag != LBER_ENUMERATED ) {
622                         rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
623                         return LDAP_PROTOCOL_ERROR;
624                 }
625
626                 switch ( behavior ) {
627                 case LDAP_CHAINING_PREFERRED:
628                         mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
629                         break;
630
631                 case LDAP_CHAINING_REQUIRED:
632                         mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
633                         break;
634
635                 case LDAP_REFERRALS_PREFERRED:
636                         mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
637                         break;
638
639                 case LDAP_REFERRALS_REQUIRED:
640                         mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
641                         break;
642
643                 default:
644                         rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
645                         return LDAP_PROTOCOL_ERROR;
646                 }
647
648                 tag = ber_peek_tag( ber, &len );
649                 if ( tag == LBER_ENUMERATED ) {
650                         tag = ber_scanf( ber, "e", &behavior );
651                         if ( tag == LBER_ERROR ) {
652                                 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
653                                 return LDAP_PROTOCOL_ERROR;
654                         }
655                 }
656
657                 if ( tag == LBER_DEFAULT ) {
658                         mode |= SLAP_CH_CONTINUATION_DEFAULT;
659
660                 } else {
661                         switch ( behavior ) {
662                         case LDAP_CHAINING_PREFERRED:
663                                 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
664                                 break;
665
666                         case LDAP_CHAINING_REQUIRED:
667                                 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
668                                 break;
669
670                         case LDAP_REFERRALS_PREFERRED:
671                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
672                                 break;
673
674                         case LDAP_REFERRALS_REQUIRED:
675                                 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
676                                 break;
677
678                         default:
679                                 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
680                                 return LDAP_PROTOCOL_ERROR;
681                         }
682                 }
683
684                 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
685                         rs->sr_text = "Chaining behavior control: decoding error";
686                         return LDAP_PROTOCOL_ERROR;
687                 }
688
689                 (void) ber_free( ber, 1 );
690         }
691
692         op->o_chaining = mode | ( ctrl->ldctl_iscritical
693                         ? SLAP_CONTROL_CRITICAL
694                         : SLAP_CONTROL_NONCRITICAL );
695
696         return LDAP_SUCCESS;
697 }
698 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
699
700 static slap_overinst ldapchain;
701
702 int
703 chain_init( void )
704 {
705 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
706         int     rc;
707
708         rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
709                         /* SLAP_CTRL_FRONTEND| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
710                         ldap_chain_parse_ctrl, &sc_chainingBehavior );
711         if ( rc != LDAP_SUCCESS ) {
712                 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
713                 return rc;
714         }
715 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
716
717         ldapchain.on_bi.bi_type = "chain";
718         ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
719 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
720         ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
721 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
722         ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
723         ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
724         
725         /* ... otherwise the underlying backend's function would be called,
726          * likely passing an invalid entry; on the contrary, the requested
727          * operational attributes should have been returned while chasing
728          * the referrals.  This all in all is a bit messy, because part
729          * of the operational attributes are generated by they backend;
730          * part by the frontend; back-ldap should receive all the available
731          * ones from the remote server, but then, on it own, it strips those
732          * it assumes will be (re)generated by the frontend (e.g.
733          * subschemaSubentry.) */
734         ldapchain.on_bi.bi_operational = ldap_chain_operational;
735         
736         ldapchain.on_response = ldap_chain_response;
737
738         return overlay_register( &ldapchain );
739 }
740