1 /* chain.c - chain LDAP operations */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2005 The OpenLDAP Foundation.
6 * Portions Copyright 2003 Howard Chu.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
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>.
18 * This work was initially developed by the Howard Chu for inclusion
19 * in OpenLDAP Software.
26 #include <ac/string.h>
27 #include <ac/socket.h>
30 #include "back-ldap.h"
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
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 */
55 #define LDAP_CH_NONE ((void *)(0))
56 #define LDAP_CH_RES ((void *)(1))
57 #define LDAP_CH_ERR ((void *)(2))
59 static int sc_chainingBehavior;
60 static BackendInfo *lback;
62 typedef struct ldap_chain_t {
63 struct ldapinfo *lc_li;
65 #define LDAP_CHAIN_F_NONE 0x00U
66 #define LDAP_CHAIN_F_CHAINING 0x01U
67 LDAPControl lc_chaining_ctrl;
72 ldap_chain_operational( Operation *op, SlapReply *rs )
74 /* Trap entries generated by back-ldap.
76 * FIXME: we need a better way to recognize them; a cleaner
77 * solution would be to be able to intercept the response
78 * of be_operational(), so that we can divert only those
79 * calls that fail because operational attributes were
80 * requested for entries that do not belong to the underlying
81 * database. This fix is likely to intercept also entries
82 * generated by back-perl and so. */
83 if ( rs->sr_entry->e_private == NULL ) {
87 return SLAP_CB_CONTINUE;
91 * Search specific response that strips entryDN from entries
94 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
96 assert( op->o_tag == LDAP_REQ_SEARCH );
98 /* if in error, don't proceed any further */
99 if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
103 if ( rs->sr_type == REP_SEARCH ) {
104 Attribute **ap = &rs->sr_entry->e_attrs;
106 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
107 /* will be generated later by frontend
108 * (a cleaner solution would be that
109 * the frontend checks if it already exists */
110 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
117 /* there SHOULD be one only! */
122 return SLAP_CB_CONTINUE;
124 } else if ( rs->sr_type == REP_SEARCHREF ) {
125 /* if we get it here, it means the library was unable
126 * to chase the referral... */
128 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
129 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
130 switch ( get_continuationBehavior( op ) ) {
131 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
132 op->o_callback->sc_private = LDAP_CH_ERR;
139 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
140 return SLAP_CB_CONTINUE;
142 } else if ( rs->sr_type == REP_RESULT ) {
143 /* back-ldap tried to send result */
144 op->o_callback->sc_private = LDAP_CH_RES;
151 * Dummy response that simply traces if back-ldap tried to send
152 * anything to the client
155 ldap_chain_cb_response( Operation *op, SlapReply *rs )
157 /* if in error, don't proceed any further */
158 if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
162 if ( rs->sr_type == REP_RESULT ) {
163 op->o_callback->sc_private = LDAP_CH_RES;
165 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
167 /* strip the entryDN attribute, but keep returning results */
168 (void)ldap_chain_cb_search_response( op, rs );
171 return SLAP_CB_CONTINUE;
178 int ( *op_f )( Operation *op, SlapReply *rs ),
181 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
182 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
183 struct ldapinfo li, *lip = lc->lc_li;
185 /* NOTE: returned if ref is empty... */
188 if ( lip->url != NULL ) {
189 op->o_bd->be_private = lip;
190 return ( *op_f )( op, rs );
194 op->o_bd->be_private = &li;
196 /* if we parse the URI then by no means
197 * we can cache stuff or reuse connections,
198 * because in back-ldap there's no caching
199 * based on the URI value, which is supposed
200 * to be set once for all (correct?) */
201 op->o_do_not_cache = 1;
203 for ( ; !BER_BVISNULL( ref ); ref++ ) {
207 /* We're setting the URI of the first referral;
208 * what if there are more?
210 Document: draft-ietf-ldapbis-protocol-27.txt
214 If the client wishes to progress the operation, it MUST follow the
215 referral by contacting one of the supported services. If multiple
216 URIs are present, the client assumes that any supported URI may be
217 used to progress the operation.
219 * so we actually need to follow exactly one,
220 * and we can assume any is fine.
223 /* parse reference and use
224 * proto://[host][:port]/ only */
225 rc = ldap_url_parse_ext( ref->bv_val, &srv );
226 if ( rc != LDAP_URL_SUCCESS ) {
232 /* remove DN essentially because later on
233 * ldap_initialize() will parse the URL
234 * as a comma-separated URL list */
235 save_dn = srv->lud_dn;
237 srv->lud_scope = LDAP_SCOPE_DEFAULT;
238 li.url = ldap_url_desc2str( srv );
239 srv->lud_dn = save_dn;
240 ldap_free_urldesc( srv );
242 if ( li.url == NULL ) {
248 rc = ( *op_f )( op, rs );
250 ldap_memfree( li.url );
253 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
262 ldap_chain_response( Operation *op, SlapReply *rs )
264 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
265 void *private = op->o_bd->be_private;
266 slap_callback *sc = op->o_callback,
269 int cache = op->o_do_not_cache;
271 struct berval ndn = op->o_ndn;
273 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
274 struct ldapinfo li, *lip = lc->lc_li;
276 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
277 int sr_err = rs->sr_err;
278 slap_reply_t sr_type = rs->sr_type;
279 slap_mask_t chain_mask = 0;
280 ber_len_t chain_shift = 0;
281 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
283 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
284 return SLAP_CB_CONTINUE;
287 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
288 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
289 switch ( get_resolveBehavior( op ) ) {
290 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
291 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
292 return SLAP_CB_CONTINUE;
295 chain_mask = SLAP_CH_RESOLVE_MASK;
296 chain_shift = SLAP_CH_RESOLVE_SHIFT;
300 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
301 switch ( get_continuationBehavior( op ) ) {
302 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
303 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
304 return SLAP_CB_CONTINUE;
307 chain_mask = SLAP_CH_CONTINUATION_MASK;
308 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
312 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
315 * TODO: add checks on who/when chain operations; e.g.:
316 * a) what identities are authorized
317 * b) what request DN (e.g. only chain requests rooted at <DN>)
318 * c) what referral URIs
319 * d) what protocol scheme (e.g. only ldaps://)
326 /* we need this to know if back-ldap returned any result */
327 sc2.sc_response = ldap_chain_cb_response;
328 op->o_callback = &sc2;
330 /* Chaining can be performed by a privileged user on behalf
331 * of normal users, using the ProxyAuthz control, by exploiting
332 * the identity assertion feature of back-ldap; see idassert-*
333 * directives in slapd-ldap(5).
335 * FIXME: the idassert-authcDN is one, will it be fine regardless
336 * of the URI we obtain from the referral?
339 switch ( op->o_tag ) {
340 case LDAP_REQ_BIND: {
341 struct berval rndn = op->o_req_ndn;
342 Connection *conn = op->o_conn;
344 /* FIXME: can we really get a referral for binds? */
345 op->o_req_ndn = slap_empty_bv;
347 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
348 op->o_req_ndn = rndn;
354 int cleanup_attrs = 0;
356 if ( op->ora_e->e_attrs == NULL ) {
357 char textbuf[ SLAP_TEXT_BUFLEN ];
358 size_t textlen = sizeof( textbuf );
361 /* FIXME: op->o_bd is still set to the BackendDB
362 * structure of the database that tried to handle
363 * the operation and actually returned a referral
365 assert( SLAP_DBFLAGS( op->o_bd ) & SLAP_DBFLAG_GLOBAL_OVERLAY );
368 /* global overlay: create entry */
369 /* NOTE: this is a hack to use the chain overlay
370 * as global. I expect to be able to remove this
371 * soon by using slap_mods2entry() earlier in
372 * do_add(), adding the operational attrs later
374 rs->sr_err = slap_mods2entry( op->ora_modlist,
376 &rs->sr_text, textbuf, textlen );
377 if ( rs->sr_err != LDAP_SUCCESS ) {
378 send_ldap_result( op, rs );
383 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
384 if ( cleanup_attrs ) {
385 attrs_free( op->ora_e->e_attrs );
386 op->ora_e->e_attrs = NULL;
390 case LDAP_REQ_DELETE:
391 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
393 case LDAP_REQ_MODRDN:
394 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
396 case LDAP_REQ_MODIFY:
397 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
399 case LDAP_REQ_COMPARE:
400 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
402 case LDAP_REQ_SEARCH:
403 if ( rs->sr_type == REP_SEARCHREF ) {
404 struct berval *curr = ref,
406 ondn = op->o_req_ndn;
408 rs->sr_type = REP_SEARCH;
410 sc2.sc_response = ldap_chain_cb_search_response;
414 op->o_bd->be_private = &li;
416 /* if we parse the URI then by no means
417 * we can cache stuff or reuse connections,
418 * because in back-ldap there's no caching
419 * based on the URI value, which is supposed
420 * to be set once for all (correct?) */
421 op->o_do_not_cache = 1;
423 /* copy the private info because we need to modify it */
424 for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
428 /* parse reference and use
429 * proto://[host][:port]/ only */
430 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
431 if ( rc != LDAP_URL_SUCCESS ) {
433 rs->sr_err = LDAP_OTHER;
437 /* remove DN essentially because later on
438 * ldap_initialize() will parse the URL
439 * as a comma-separated URL list */
440 save_dn = srv->lud_dn;
442 srv->lud_scope = LDAP_SCOPE_DEFAULT;
443 li.url = ldap_url_desc2str( srv );
444 if ( li.url != NULL ) {
445 ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
447 ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
451 srv->lud_dn = save_dn;
452 ldap_free_urldesc( srv );
454 if ( li.url == NULL ) {
456 rs->sr_err = LDAP_OTHER;
461 /* FIXME: should we also copy filter and scope?
462 * according to RFC3296, no */
463 rc = lback->bi_op_search( op, rs );
465 ldap_memfree( li.url );
468 op->o_tmpfree( op->o_req_dn.bv_val,
470 op->o_tmpfree( op->o_req_ndn.bv_val,
473 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
481 op->o_req_ndn = ondn;
482 rs->sr_type = REP_SEARCHREF;
485 if ( rc != LDAP_SUCCESS ) {
486 /* couldn't chase any of the referrals */
487 rc = SLAP_CB_CONTINUE;
491 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
494 case LDAP_REQ_EXTENDED:
495 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
496 /* FIXME: ldap_back_extended() by design
497 * doesn't send result; frontend is expected
499 if ( rc != SLAPD_ABANDON ) {
500 send_ldap_extended( op, rs );
505 rc = SLAP_CB_CONTINUE;
509 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
510 if ( rc != LDAP_SUCCESS || sc2.sc_private == LDAP_CH_ERR ) {
511 if ( rs->sr_err == LDAP_CANNOT_CHAIN ) {
515 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
516 case LDAP_CHAINING_REQUIRED:
518 op->o_callback = NULL;
519 send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" );
523 rc = SLAP_CB_CONTINUE;
525 rs->sr_type = sr_type;
530 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
532 if ( sc2.sc_private == LDAP_CH_NONE ) {
533 op->o_callback = NULL;
534 rc = rs->sr_err = slap_map_api2result( rs );
535 send_ldap_result( op, rs );
538 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
540 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
541 op->o_do_not_cache = cache;
542 op->o_bd->be_private = private;
551 str2chain( const char *s )
553 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
554 return LDAP_CHAINING_PREFERRED;
556 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
557 return LDAP_CHAINING_REQUIRED;
559 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
560 return LDAP_REFERRALS_PREFERRED;
562 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
563 return LDAP_REFERRALS_REQUIRED;
570 ldap_chain_db_config(
578 slap_overinst *on = (slap_overinst *) be->bd_info;
579 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
580 void *private = be->be_private;
584 be->be_private = lc->lc_li;
585 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
587 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
589 if ( strcasecmp( argv0, "chaining" ) == 0 ) {
590 BerElementBuffer berbuf;
591 BerElement *ber = (BerElement *)&berbuf;
596 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
597 if ( strncasecmp( argv[0], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
598 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
599 if ( resolve == -1 ) {
600 fprintf( stderr, "%s line %d: "
601 "illegal <resolve> value %s "
602 "in \"chain-chaining>\"\n",
603 fname, lineno, argv[ 0 ] );
607 } else if ( strncasecmp( argv[0], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
608 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
609 if ( continuation == -1 ) {
610 fprintf( stderr, "%s line %d: "
611 "illegal <continuation> value %s "
612 "in \"chain-chaining\"\n",
613 fname, lineno, argv[ 0 ] );
617 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
621 fprintf( stderr, "%s line %d: "
622 "unknown option in \"chain-chaining\"\n",
628 if ( resolve != -1 || continuation != -1 ) {
631 if ( resolve == -1 ) {
633 resolve = LDAP_CHAINING_PREFERRED;
636 ber_init2( ber, NULL, LBER_USE_DER );
638 err = ber_printf( ber, "{e" /* } */, resolve );
641 fprintf( stderr, "%s line %d: "
642 "chaining behavior control encoding error!\n",
647 if ( continuation > -1 ) {
648 err = ber_printf( ber, "e", continuation );
651 fprintf( stderr, "%s line %d: "
652 "chaining behavior control encoding error!\n",
658 err = ber_printf( ber, /* { */ "N}" );
661 fprintf( stderr, "%s line %d: "
662 "chaining behavior control encoding error!\n",
667 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
668 exit( EXIT_FAILURE );
672 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
675 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
676 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
678 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
685 rc = lback->bi_db_config( be, fname, lineno, argc, argv );
686 be->be_private = private;
700 slap_overinst *on = (slap_overinst *)be->bd_info;
701 ldap_chain_t *lc = NULL;
705 if ( lback == NULL ) {
706 lback = backend_info( "ldap" );
708 if ( lback == NULL ) {
713 lc = ch_malloc( sizeof( ldap_chain_t ) );
714 memset( lc, 0, sizeof( ldap_chain_t ) );
716 bd.be_private = NULL;
717 rc = lback->bi_db_init( &bd );
718 lc->lc_li = (struct ldapinfo *)bd.be_private;
719 on->on_bi.bi_private = (void *)lc;
731 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
732 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
733 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
739 ldap_chain_db_destroy(
743 slap_overinst *on = (slap_overinst *) be->bd_info;
744 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
745 void *private = be->be_private;
748 be->be_private = (void *)lc->lc_li;
749 rc = lback->bi_db_destroy( be );
750 lc->lc_li = be->be_private;
752 on->on_bi.bi_private = NULL;
753 be->be_private = private;
758 ldap_chain_connection_destroy(
763 slap_overinst *on = (slap_overinst *) be->bd_info;
764 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
765 void *private = be->be_private;
768 be->be_private = (void *)lc->lc_li;
769 rc = lback->bi_connection_destroy( be, conn );
770 be->be_private = private;
775 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
777 ldap_chain_parse_ctrl(
787 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
788 rs->sr_text = "Chaining behavior control specified multiple times";
789 return LDAP_PROTOCOL_ERROR;
792 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
793 rs->sr_text = "Chaining behavior control specified with pagedResults control";
794 return LDAP_PROTOCOL_ERROR;
797 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
798 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
803 /* Parse the control value
804 * ChainingBehavior ::= SEQUENCE {
805 * resolveBehavior Behavior OPTIONAL,
806 * continuationBehavior Behavior OPTIONAL }
808 * Behavior :: = ENUMERATED {
809 * chainingPreferred (0),
810 * chainingRequired (1),
811 * referralsPreferred (2),
812 * referralsRequired (3) }
815 ber = ber_init( &ctrl->ldctl_value );
817 rs->sr_text = "internal error";
821 tag = ber_scanf( ber, "{e" /* } */, &behavior );
822 /* FIXME: since the whole SEQUENCE is optional,
823 * should we accept no enumerations at all? */
824 if ( tag != LBER_ENUMERATED ) {
825 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
826 return LDAP_PROTOCOL_ERROR;
829 switch ( behavior ) {
830 case LDAP_CHAINING_PREFERRED:
831 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
834 case LDAP_CHAINING_REQUIRED:
835 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
838 case LDAP_REFERRALS_PREFERRED:
839 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
842 case LDAP_REFERRALS_REQUIRED:
843 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
847 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
848 return LDAP_PROTOCOL_ERROR;
851 tag = ber_peek_tag( ber, &len );
852 if ( tag == LBER_ENUMERATED ) {
853 tag = ber_scanf( ber, "e", &behavior );
854 if ( tag == LBER_ERROR ) {
855 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
856 return LDAP_PROTOCOL_ERROR;
860 if ( tag == LBER_DEFAULT ) {
861 mode |= SLAP_CH_CONTINUATION_DEFAULT;
864 switch ( behavior ) {
865 case LDAP_CHAINING_PREFERRED:
866 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
869 case LDAP_CHAINING_REQUIRED:
870 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
873 case LDAP_REFERRALS_PREFERRED:
874 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
877 case LDAP_REFERRALS_REQUIRED:
878 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
882 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
883 return LDAP_PROTOCOL_ERROR;
887 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
888 rs->sr_text = "Chaining behavior control: decoding error";
889 return LDAP_PROTOCOL_ERROR;
892 (void) ber_free( ber, 1 );
895 op->o_chaining = mode | ( ctrl->ldctl_iscritical
896 ? SLAP_CONTROL_CRITICAL
897 : SLAP_CONTROL_NONCRITICAL );
901 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
903 static slap_overinst ldapchain;
908 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
911 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
912 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
913 ldap_chain_parse_ctrl, &sc_chainingBehavior );
914 if ( rc != LDAP_SUCCESS ) {
915 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
918 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
920 ldapchain.on_bi.bi_type = "chain";
921 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
922 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
923 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
924 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
926 /* ... otherwise the underlying backend's function would be called,
927 * likely passing an invalid entry; on the contrary, the requested
928 * operational attributes should have been returned while chasing
929 * the referrals. This all in all is a bit messy, because part
930 * of the operational attributes are generated by the backend;
931 * part by the frontend; back-ldap should receive all the available
932 * ones from the remote server, but then, on its own, it strips those
933 * it assumes will be (re)generated by the frontend (e.g.
934 * subschemaSubentry.) */
935 ldapchain.on_bi.bi_operational = ldap_chain_operational;
937 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
939 ldapchain.on_response = ldap_chain_response;
941 return overlay_register( &ldapchain );