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