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_CHAINING_DEFAULT LDAP_CHAINING_PREFERRED
34 #define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT
35 #define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT)
36 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
37 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
38 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
39 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
40 #define SLAP_CH_RESOLVE_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
41 #define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2)
42 #define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT)
43 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
44 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
45 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
46 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
47 #define SLAP_CH_CONTINUATION_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
49 #define o_chaining o_ctrlflag[sc_chainingBehavior]
50 #define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK)
51 #define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
52 #define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
53 #define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
55 static int sc_chainingBehavior;
56 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
58 #define LDAP_CH_NONE ((void *)(0))
59 #define LDAP_CH_RES ((void *)(1))
60 #define LDAP_CH_ERR ((void *)(2))
62 static BackendInfo *lback;
64 typedef struct ldap_chain_t {
65 struct ldapinfo *lc_li;
67 #define LDAP_CHAIN_F_NONE 0x00U
68 #define LDAP_CHAIN_F_CHAINING 0x01U
70 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
71 LDAPControl lc_chaining_ctrl;
72 char lc_chaining_ctrlflag;
73 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
76 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
81 LDAPControl ***oldctrlsp )
83 LDAPControl **ctrls = NULL;
86 *oldctrlsp = op->o_ctrls;
88 /* default chaining control not defined */
89 if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
94 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
98 /* FIXME: check other incompatibilities */
100 /* add to other controls */
102 for ( c = 0; op->o_ctrls[ c ]; c++ )
106 ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
107 ctrls[ 0 ] = &lc->lc_chaining_ctrl;
109 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
110 ctrls[ c + 1 ] = op->o_ctrls[ c ];
113 ctrls[ c + 1 ] = NULL;
117 op->o_chaining = lc->lc_chaining_ctrlflag;
123 chaining_control_remove(
125 LDAPControl ***oldctrlsp )
127 LDAPControl **oldctrls = *oldctrlsp;
129 /* we assume that the first control is the chaining control
130 * added by the chain overlay, so it's the only one we explicitly
132 if ( op->o_ctrls != oldctrls ) {
133 assert( op->o_ctrls );
134 assert( op->o_ctrls[ 0 ] );
139 op->o_ctrls = oldctrls;
146 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
149 ldap_chain_operational( Operation *op, SlapReply *rs )
151 /* Trap entries generated by back-ldap.
153 * FIXME: we need a better way to recognize them; a cleaner
154 * solution would be to be able to intercept the response
155 * of be_operational(), so that we can divert only those
156 * calls that fail because operational attributes were
157 * requested for entries that do not belong to the underlying
158 * database. This fix is likely to intercept also entries
159 * generated by back-perl and so. */
160 if ( rs->sr_entry->e_private == NULL ) {
164 return SLAP_CB_CONTINUE;
168 * Search specific response that strips entryDN from entries
171 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
173 assert( op->o_tag == LDAP_REQ_SEARCH );
175 /* if in error, don't proceed any further */
176 if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
180 if ( rs->sr_type == REP_SEARCH ) {
181 Attribute **ap = &rs->sr_entry->e_attrs;
183 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
184 /* will be generated later by frontend
185 * (a cleaner solution would be that
186 * the frontend checks if it already exists */
187 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
194 /* there SHOULD be one only! */
199 return SLAP_CB_CONTINUE;
201 } else if ( rs->sr_type == REP_SEARCHREF ) {
202 /* if we get it here, it means the library was unable
203 * to chase the referral... */
205 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
206 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
207 switch ( get_continuationBehavior( op ) ) {
208 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
209 op->o_callback->sc_private = LDAP_CH_ERR;
216 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
217 return SLAP_CB_CONTINUE;
219 } else if ( rs->sr_type == REP_RESULT ) {
220 /* back-ldap tried to send result */
221 op->o_callback->sc_private = LDAP_CH_RES;
228 * Dummy response that simply traces if back-ldap tried to send
229 * anything to the client
232 ldap_chain_cb_response( Operation *op, SlapReply *rs )
234 /* if in error, don't proceed any further */
235 if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
239 if ( rs->sr_type == REP_RESULT ) {
240 op->o_callback->sc_private = LDAP_CH_RES;
242 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
244 /* strip the entryDN attribute, but keep returning results */
245 (void)ldap_chain_cb_search_response( op, rs );
248 return SLAP_CB_CONTINUE;
255 int ( *op_f )( Operation *op, SlapReply *rs ),
258 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
259 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
260 struct ldapinfo li, *lip = lc->lc_li;
262 /* NOTE: returned if ref is empty... */
265 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
266 LDAPControl **ctrls = NULL;
268 (void)chaining_control_add( lc, op, &ctrls );
269 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
271 if ( lip->url != NULL ) {
272 op->o_bd->be_private = lip;
273 rc = ( *op_f )( op, rs );
278 op->o_bd->be_private = &li;
280 /* if we parse the URI then by no means
281 * we can cache stuff or reuse connections,
282 * because in back-ldap there's no caching
283 * based on the URI value, which is supposed
284 * to be set once for all (correct?) */
285 op->o_do_not_cache = 1;
287 for ( ; !BER_BVISNULL( ref ); ref++ ) {
291 /* We're setting the URI of the first referral;
292 * what if there are more?
294 Document: draft-ietf-ldapbis-protocol-27.txt
298 If the client wishes to progress the operation, it MUST follow the
299 referral by contacting one of the supported services. If multiple
300 URIs are present, the client assumes that any supported URI may be
301 used to progress the operation.
303 * so we actually need to follow exactly one,
304 * and we can assume any is fine.
307 /* parse reference and use
308 * proto://[host][:port]/ only */
309 rc = ldap_url_parse_ext( ref->bv_val, &srv );
310 if ( rc != LDAP_URL_SUCCESS ) {
316 /* remove DN essentially because later on
317 * ldap_initialize() will parse the URL
318 * as a comma-separated URL list */
319 save_dn = srv->lud_dn;
321 srv->lud_scope = LDAP_SCOPE_DEFAULT;
322 li.url = ldap_url_desc2str( srv );
323 srv->lud_dn = save_dn;
324 ldap_free_urldesc( srv );
326 if ( li.url == NULL ) {
332 rc = ( *op_f )( op, rs );
334 ldap_memfree( li.url );
337 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
343 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
344 (void)chaining_control_remove( op, &ctrls );
345 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
351 ldap_chain_response( Operation *op, SlapReply *rs )
353 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
354 void *private = op->o_bd->be_private;
355 slap_callback *sc = op->o_callback,
358 int cache = op->o_do_not_cache;
360 struct berval ndn = op->o_ndn;
362 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
363 struct ldapinfo li, *lip = lc->lc_li;
365 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
366 int sr_err = rs->sr_err;
367 slap_reply_t sr_type = rs->sr_type;
368 slap_mask_t chain_mask = 0;
369 ber_len_t chain_shift = 0;
370 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
372 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
373 return SLAP_CB_CONTINUE;
376 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
377 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
378 switch ( get_resolveBehavior( op ) ) {
379 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
380 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
381 return SLAP_CB_CONTINUE;
384 chain_mask = SLAP_CH_RESOLVE_MASK;
385 chain_shift = SLAP_CH_RESOLVE_SHIFT;
389 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
390 switch ( get_continuationBehavior( op ) ) {
391 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
392 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
393 return SLAP_CB_CONTINUE;
396 chain_mask = SLAP_CH_CONTINUATION_MASK;
397 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
401 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
404 * TODO: add checks on who/when chain operations; e.g.:
405 * a) what identities are authorized
406 * b) what request DN (e.g. only chain requests rooted at <DN>)
407 * c) what referral URIs
408 * d) what protocol scheme (e.g. only ldaps://)
415 /* we need this to know if back-ldap returned any result */
416 sc2.sc_response = ldap_chain_cb_response;
417 op->o_callback = &sc2;
419 /* Chaining can be performed by a privileged user on behalf
420 * of normal users, using the ProxyAuthz control, by exploiting
421 * the identity assertion feature of back-ldap; see idassert-*
422 * directives in slapd-ldap(5).
424 * FIXME: the idassert-authcDN is one, will it be fine regardless
425 * of the URI we obtain from the referral?
428 switch ( op->o_tag ) {
429 case LDAP_REQ_BIND: {
430 struct berval rndn = op->o_req_ndn;
431 Connection *conn = op->o_conn;
433 /* FIXME: can we really get a referral for binds? */
434 op->o_req_ndn = slap_empty_bv;
436 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
437 op->o_req_ndn = rndn;
443 int cleanup_attrs = 0;
445 if ( op->ora_e->e_attrs == NULL ) {
446 char textbuf[ SLAP_TEXT_BUFLEN ];
447 size_t textlen = sizeof( textbuf );
450 /* FIXME: op->o_bd is still set to the BackendDB
451 * structure of the database that tried to handle
452 * the operation and actually returned a referral
454 assert( SLAP_DBFLAGS( op->o_bd ) & SLAP_DBFLAG_GLOBAL_OVERLAY );
457 /* global overlay: create entry */
458 /* NOTE: this is a hack to use the chain overlay
459 * as global. I expect to be able to remove this
460 * soon by using slap_mods2entry() earlier in
461 * do_add(), adding the operational attrs later
463 rs->sr_err = slap_mods2entry( op->ora_modlist,
465 &rs->sr_text, textbuf, textlen );
466 if ( rs->sr_err != LDAP_SUCCESS ) {
467 send_ldap_result( op, rs );
472 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
473 if ( cleanup_attrs ) {
474 attrs_free( op->ora_e->e_attrs );
475 op->ora_e->e_attrs = NULL;
479 case LDAP_REQ_DELETE:
480 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
482 case LDAP_REQ_MODRDN:
483 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
485 case LDAP_REQ_MODIFY:
486 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
488 case LDAP_REQ_COMPARE:
489 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
491 case LDAP_REQ_SEARCH:
492 if ( rs->sr_type == REP_SEARCHREF ) {
493 struct berval *curr = ref,
495 ondn = op->o_req_ndn;
497 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
498 LDAPControl **ctrls = NULL;
500 (void)chaining_control_add( lc, op, &ctrls );
501 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
503 rs->sr_type = REP_SEARCH;
505 sc2.sc_response = ldap_chain_cb_search_response;
509 op->o_bd->be_private = &li;
511 /* if we parse the URI then by no means
512 * we can cache stuff or reuse connections,
513 * because in back-ldap there's no caching
514 * based on the URI value, which is supposed
515 * to be set once for all (correct?) */
516 op->o_do_not_cache = 1;
518 /* copy the private info because we need to modify it */
519 for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
523 /* parse reference and use
524 * proto://[host][:port]/ only */
525 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
526 if ( rc != LDAP_URL_SUCCESS ) {
528 rs->sr_err = LDAP_OTHER;
532 /* remove DN essentially because later on
533 * ldap_initialize() will parse the URL
534 * as a comma-separated URL list */
535 save_dn = srv->lud_dn;
537 srv->lud_scope = LDAP_SCOPE_DEFAULT;
538 li.url = ldap_url_desc2str( srv );
539 if ( li.url != NULL ) {
540 ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
542 ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
546 srv->lud_dn = save_dn;
547 ldap_free_urldesc( srv );
549 if ( li.url == NULL ) {
551 rs->sr_err = LDAP_OTHER;
556 /* FIXME: should we also copy filter and scope?
557 * according to RFC3296, no */
558 rc = lback->bi_op_search( op, rs );
560 ldap_memfree( li.url );
563 op->o_tmpfree( op->o_req_dn.bv_val,
565 op->o_tmpfree( op->o_req_ndn.bv_val,
568 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
575 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
576 (void)chaining_control_remove( op, &ctrls );
577 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
580 op->o_req_ndn = ondn;
581 rs->sr_type = REP_SEARCHREF;
584 if ( rc != LDAP_SUCCESS ) {
585 /* couldn't chase any of the referrals */
586 rc = SLAP_CB_CONTINUE;
590 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
593 case LDAP_REQ_EXTENDED:
594 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
595 /* FIXME: ldap_back_extended() by design
596 * doesn't send result; frontend is expected
598 if ( rc != SLAPD_ABANDON ) {
599 send_ldap_extended( op, rs );
604 rc = SLAP_CB_CONTINUE;
608 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
609 if ( rc != LDAP_SUCCESS || sc2.sc_private == LDAP_CH_ERR ) {
610 if ( rs->sr_err == LDAP_CANNOT_CHAIN ) {
614 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
615 case LDAP_CHAINING_REQUIRED:
617 op->o_callback = NULL;
618 send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" );
622 rc = SLAP_CB_CONTINUE;
624 rs->sr_type = sr_type;
629 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
631 if ( sc2.sc_private == LDAP_CH_NONE ) {
632 op->o_callback = NULL;
633 rc = rs->sr_err = slap_map_api2result( rs );
634 send_ldap_result( op, rs );
637 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
639 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
640 op->o_do_not_cache = cache;
641 op->o_bd->be_private = private;
649 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
651 ldap_chain_parse_ctrl(
657 str2chain( const char *s )
659 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
660 return LDAP_CHAINING_PREFERRED;
662 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
663 return LDAP_CHAINING_REQUIRED;
665 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
666 return LDAP_REFERRALS_PREFERRED;
668 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
669 return LDAP_REFERRALS_REQUIRED;
674 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
677 ldap_chain_db_config(
685 slap_overinst *on = (slap_overinst *) be->bd_info;
686 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
687 void *private = be->be_private;
691 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
693 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
695 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
696 if ( strcasecmp( argv[ 0 ], "chaining" ) == 0 ) {
697 char **tmpargv = argv;
698 BerElementBuffer berbuf;
699 BerElement *ber = (BerElement *)&berbuf;
703 Operation op = { 0 };
704 SlapReply rs = { 0 };
706 lc->lc_chaining_ctrlflag = 0;
708 for ( argc--, tmpargv++; argc > 0; argc--, tmpargv++ ) {
709 if ( strncasecmp( tmpargv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
710 resolve = str2chain( tmpargv[ 0 ] + STRLENOF( "resolve=" ) );
711 if ( resolve == -1 ) {
712 fprintf( stderr, "%s line %d: "
713 "illegal <resolve> value %s "
714 "in \"chain-chaining>\"\n",
715 fname, lineno, tmpargv[ 0 ] );
719 } else if ( strncasecmp( tmpargv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
720 continuation = str2chain( tmpargv[ 0 ] + STRLENOF( "continuation=" ) );
721 if ( continuation == -1 ) {
722 fprintf( stderr, "%s line %d: "
723 "illegal <continuation> value %s "
724 "in \"chain-chaining\"\n",
725 fname, lineno, tmpargv[ 0 ] );
729 } else if ( strcasecmp( tmpargv[ 0 ], "critical" ) == 0 ) {
733 fprintf( stderr, "%s line %d: "
734 "unknown option in \"chain-chaining\"\n",
740 if ( resolve != -1 || continuation != -1 ) {
743 if ( resolve == -1 ) {
745 resolve = SLAP_CHAINING_DEFAULT;
748 ber_init2( ber, NULL, LBER_USE_DER );
750 err = ber_printf( ber, "{e" /* } */, resolve );
753 fprintf( stderr, "%s line %d: "
754 "chaining behavior control encoding error!\n",
759 if ( continuation > -1 ) {
760 err = ber_printf( ber, "e", continuation );
763 fprintf( stderr, "%s line %d: "
764 "chaining behavior control encoding error!\n",
770 err = ber_printf( ber, /* { */ "N}" );
773 fprintf( stderr, "%s line %d: "
774 "chaining behavior control encoding error!\n",
779 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
780 exit( EXIT_FAILURE );
784 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
787 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
788 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
790 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
792 fprintf( stderr, "%s line %d: "
793 "unable to parse chaining control%s%s\n",
795 rs.sr_text ? ": " : "",
796 rs.sr_text ? rs.sr_text : "" );
800 lc->lc_chaining_ctrlflag = op.o_chaining;
802 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
807 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
811 be->be_private = lc->lc_li;
812 rc = lback->bi_db_config( be, fname, lineno, argc, argv );
813 be->be_private = private;
828 slap_overinst *on = (slap_overinst *)be->bd_info;
829 ldap_chain_t *lc = NULL;
833 if ( lback == NULL ) {
834 lback = backend_info( "ldap" );
836 if ( lback == NULL ) {
841 lc = ch_malloc( sizeof( ldap_chain_t ) );
842 memset( lc, 0, sizeof( ldap_chain_t ) );
844 bd.be_private = NULL;
845 rc = lback->bi_db_init( &bd );
846 lc->lc_li = (struct ldapinfo *)bd.be_private;
847 on->on_bi.bi_private = (void *)lc;
859 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
860 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
861 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
867 ldap_chain_db_destroy(
871 slap_overinst *on = (slap_overinst *) be->bd_info;
872 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
873 void *private = be->be_private;
876 be->be_private = (void *)lc->lc_li;
877 rc = lback->bi_db_destroy( be );
878 lc->lc_li = be->be_private;
880 on->on_bi.bi_private = NULL;
881 be->be_private = private;
886 ldap_chain_connection_destroy(
891 slap_overinst *on = (slap_overinst *) be->bd_info;
892 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
893 void *private = be->be_private;
896 be->be_private = (void *)lc->lc_li;
897 rc = lback->bi_connection_destroy( be, conn );
898 be->be_private = private;
903 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
905 ldap_chain_parse_ctrl(
915 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
916 rs->sr_text = "Chaining behavior control specified multiple times";
917 return LDAP_PROTOCOL_ERROR;
920 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
921 rs->sr_text = "Chaining behavior control specified with pagedResults control";
922 return LDAP_PROTOCOL_ERROR;
925 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
926 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
931 /* Parse the control value
932 * ChainingBehavior ::= SEQUENCE {
933 * resolveBehavior Behavior OPTIONAL,
934 * continuationBehavior Behavior OPTIONAL }
936 * Behavior :: = ENUMERATED {
937 * chainingPreferred (0),
938 * chainingRequired (1),
939 * referralsPreferred (2),
940 * referralsRequired (3) }
943 ber = ber_init( &ctrl->ldctl_value );
945 rs->sr_text = "internal error";
949 tag = ber_scanf( ber, "{e" /* } */, &behavior );
950 /* FIXME: since the whole SEQUENCE is optional,
951 * should we accept no enumerations at all? */
952 if ( tag != LBER_ENUMERATED ) {
953 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
954 return LDAP_PROTOCOL_ERROR;
957 switch ( behavior ) {
958 case LDAP_CHAINING_PREFERRED:
959 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
962 case LDAP_CHAINING_REQUIRED:
963 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
966 case LDAP_REFERRALS_PREFERRED:
967 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
970 case LDAP_REFERRALS_REQUIRED:
971 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
975 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
976 return LDAP_PROTOCOL_ERROR;
979 tag = ber_peek_tag( ber, &len );
980 if ( tag == LBER_ENUMERATED ) {
981 tag = ber_scanf( ber, "e", &behavior );
982 if ( tag == LBER_ERROR ) {
983 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
984 return LDAP_PROTOCOL_ERROR;
988 if ( tag == LBER_DEFAULT ) {
989 mode |= SLAP_CH_CONTINUATION_DEFAULT;
992 switch ( behavior ) {
993 case LDAP_CHAINING_PREFERRED:
994 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
997 case LDAP_CHAINING_REQUIRED:
998 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1001 case LDAP_REFERRALS_PREFERRED:
1002 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1005 case LDAP_REFERRALS_REQUIRED:
1006 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1010 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1011 return LDAP_PROTOCOL_ERROR;
1015 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1016 rs->sr_text = "Chaining behavior control: decoding error";
1017 return LDAP_PROTOCOL_ERROR;
1020 (void) ber_free( ber, 1 );
1023 op->o_chaining = mode | ( ctrl->ldctl_iscritical
1024 ? SLAP_CONTROL_CRITICAL
1025 : SLAP_CONTROL_NONCRITICAL );
1027 return LDAP_SUCCESS;
1029 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1031 static slap_overinst ldapchain;
1036 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1039 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1040 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1041 ldap_chain_parse_ctrl, &sc_chainingBehavior );
1042 if ( rc != LDAP_SUCCESS ) {
1043 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
1046 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1048 ldapchain.on_bi.bi_type = "chain";
1049 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1050 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1051 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1052 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1054 /* ... otherwise the underlying backend's function would be called,
1055 * likely passing an invalid entry; on the contrary, the requested
1056 * operational attributes should have been returned while chasing
1057 * the referrals. This all in all is a bit messy, because part
1058 * of the operational attributes are generated by the backend;
1059 * part by the frontend; back-ldap should receive all the available
1060 * ones from the remote server, but then, on its own, it strips those
1061 * it assumes will be (re)generated by the frontend (e.g.
1062 * subschemaSubentry.) */
1063 ldapchain.on_bi.bi_operational = ldap_chain_operational;
1065 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1067 ldapchain.on_response = ldap_chain_response;
1069 return overlay_register( &ldapchain );