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"
34 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
35 #define SLAP_CHAINING_DEFAULT LDAP_CHAINING_PREFERRED
36 #define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT
37 #define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT)
38 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
39 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
40 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
41 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
42 #define SLAP_CH_RESOLVE_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
43 #define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2)
44 #define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT)
45 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
46 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
47 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
48 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
49 #define SLAP_CH_CONTINUATION_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
51 #define o_chaining o_ctrlflag[sc_chainingBehavior]
52 #define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK)
53 #define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
54 #define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
55 #define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
57 static int sc_chainingBehavior;
58 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
60 #define LDAP_CH_NONE ((void *)(0))
61 #define LDAP_CH_RES ((void *)(1))
62 #define LDAP_CH_ERR ((void *)(2))
64 static BackendInfo *lback;
66 typedef struct ldap_chain_t {
67 struct ldapinfo *lc_li;
69 #define LDAP_CHAIN_F_NONE 0x00U
70 #define LDAP_CHAIN_F_CHAINING 0x01U
72 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
73 LDAPControl lc_chaining_ctrl;
74 char lc_chaining_ctrlflag;
75 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
78 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
83 LDAPControl ***oldctrlsp )
85 LDAPControl **ctrls = NULL;
88 *oldctrlsp = op->o_ctrls;
90 /* default chaining control not defined */
91 if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
96 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
100 /* FIXME: check other incompatibilities */
102 /* add to other controls */
104 for ( c = 0; op->o_ctrls[ c ]; c++ )
108 ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
109 ctrls[ 0 ] = &lc->lc_chaining_ctrl;
111 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
112 ctrls[ c + 1 ] = op->o_ctrls[ c ];
115 ctrls[ c + 1 ] = NULL;
119 op->o_chaining = lc->lc_chaining_ctrlflag;
125 chaining_control_remove(
127 LDAPControl ***oldctrlsp )
129 LDAPControl **oldctrls = *oldctrlsp;
131 /* we assume that the first control is the chaining control
132 * added by the chain overlay, so it's the only one we explicitly
134 if ( op->o_ctrls != oldctrls ) {
135 assert( op->o_ctrls );
136 assert( op->o_ctrls[ 0 ] );
141 op->o_ctrls = oldctrls;
148 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
151 ldap_chain_operational( Operation *op, SlapReply *rs )
153 /* Trap entries generated by back-ldap.
155 * FIXME: we need a better way to recognize them; a cleaner
156 * solution would be to be able to intercept the response
157 * of be_operational(), so that we can divert only those
158 * calls that fail because operational attributes were
159 * requested for entries that do not belong to the underlying
160 * database. This fix is likely to intercept also entries
161 * generated by back-perl and so. */
162 if ( rs->sr_entry->e_private == NULL ) {
166 return SLAP_CB_CONTINUE;
170 * Search specific response that strips entryDN from entries
173 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
175 assert( op->o_tag == LDAP_REQ_SEARCH );
177 /* if in error, don't proceed any further */
178 if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
182 if ( rs->sr_type == REP_SEARCH ) {
183 Attribute **ap = &rs->sr_entry->e_attrs;
185 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
186 /* will be generated later by frontend
187 * (a cleaner solution would be that
188 * the frontend checks if it already exists */
189 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
196 /* there SHOULD be one only! */
201 return SLAP_CB_CONTINUE;
203 } else if ( rs->sr_type == REP_SEARCHREF ) {
204 /* if we get it here, it means the library was unable
205 * to chase the referral... */
207 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
208 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
209 switch ( get_continuationBehavior( op ) ) {
210 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
211 op->o_callback->sc_private = LDAP_CH_ERR;
218 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
219 return SLAP_CB_CONTINUE;
221 } else if ( rs->sr_type == REP_RESULT ) {
222 /* back-ldap tried to send result */
223 op->o_callback->sc_private = LDAP_CH_RES;
230 * Dummy response that simply traces if back-ldap tried to send
231 * anything to the client
234 ldap_chain_cb_response( Operation *op, SlapReply *rs )
236 /* if in error, don't proceed any further */
237 if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
241 if ( rs->sr_type == REP_RESULT ) {
242 op->o_callback->sc_private = LDAP_CH_RES;
244 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
246 /* strip the entryDN attribute, but keep returning results */
247 (void)ldap_chain_cb_search_response( op, rs );
250 return SLAP_CB_CONTINUE;
257 int ( *op_f )( Operation *op, SlapReply *rs ),
260 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
261 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
262 struct ldapinfo li, *lip = lc->lc_li;
264 /* NOTE: returned if ref is empty... */
267 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
268 LDAPControl **ctrls = NULL;
270 (void)chaining_control_add( lc, op, &ctrls );
271 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
273 if ( lip->url != NULL ) {
274 op->o_bd->be_private = lip;
275 rc = ( *op_f )( op, rs );
280 op->o_bd->be_private = &li;
282 /* if we parse the URI then by no means
283 * we can cache stuff or reuse connections,
284 * because in back-ldap there's no caching
285 * based on the URI value, which is supposed
286 * to be set once for all (correct?) */
287 op->o_do_not_cache = 1;
289 for ( ; !BER_BVISNULL( ref ); ref++ ) {
293 /* We're setting the URI of the first referral;
294 * what if there are more?
296 Document: draft-ietf-ldapbis-protocol-27.txt
300 If the client wishes to progress the operation, it MUST follow the
301 referral by contacting one of the supported services. If multiple
302 URIs are present, the client assumes that any supported URI may be
303 used to progress the operation.
305 * so we actually need to follow exactly one,
306 * and we can assume any is fine.
309 /* parse reference and use
310 * proto://[host][:port]/ only */
311 rc = ldap_url_parse_ext( ref->bv_val, &srv );
312 if ( rc != LDAP_URL_SUCCESS ) {
318 /* remove DN essentially because later on
319 * ldap_initialize() will parse the URL
320 * as a comma-separated URL list */
321 save_dn = srv->lud_dn;
323 srv->lud_scope = LDAP_SCOPE_DEFAULT;
324 li.url = ldap_url_desc2str( srv );
325 srv->lud_dn = save_dn;
326 ldap_free_urldesc( srv );
328 if ( li.url == NULL ) {
334 rc = ( *op_f )( op, rs );
336 ldap_memfree( li.url );
339 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
345 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
346 (void)chaining_control_remove( op, &ctrls );
347 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
353 ldap_chain_response( Operation *op, SlapReply *rs )
355 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
356 void *private = op->o_bd->be_private;
357 slap_callback *sc = op->o_callback,
360 int cache = op->o_do_not_cache;
362 struct berval ndn = op->o_ndn;
364 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
365 struct ldapinfo li, *lip = lc->lc_li;
367 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
368 int sr_err = rs->sr_err;
369 slap_reply_t sr_type = rs->sr_type;
370 slap_mask_t chain_mask = 0;
371 ber_len_t chain_shift = 0;
372 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
374 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
375 return SLAP_CB_CONTINUE;
378 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
379 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
380 switch ( get_resolveBehavior( op ) ) {
381 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
382 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
383 return SLAP_CB_CONTINUE;
386 chain_mask = SLAP_CH_RESOLVE_MASK;
387 chain_shift = SLAP_CH_RESOLVE_SHIFT;
391 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
392 switch ( get_continuationBehavior( op ) ) {
393 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
394 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
395 return SLAP_CB_CONTINUE;
398 chain_mask = SLAP_CH_CONTINUATION_MASK;
399 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
403 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
406 * TODO: add checks on who/when chain operations; e.g.:
407 * a) what identities are authorized
408 * b) what request DN (e.g. only chain requests rooted at <DN>)
409 * c) what referral URIs
410 * d) what protocol scheme (e.g. only ldaps://)
417 /* we need this to know if back-ldap returned any result */
418 sc2.sc_response = ldap_chain_cb_response;
419 op->o_callback = &sc2;
421 /* Chaining can be performed by a privileged user on behalf
422 * of normal users, using the ProxyAuthz control, by exploiting
423 * the identity assertion feature of back-ldap; see idassert-*
424 * directives in slapd-ldap(5).
426 * FIXME: the idassert-authcDN is one, will it be fine regardless
427 * of the URI we obtain from the referral?
430 switch ( op->o_tag ) {
431 case LDAP_REQ_BIND: {
432 struct berval rndn = op->o_req_ndn;
433 Connection *conn = op->o_conn;
435 /* FIXME: can we really get a referral for binds? */
436 op->o_req_ndn = slap_empty_bv;
438 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
439 op->o_req_ndn = rndn;
445 int cleanup_attrs = 0;
447 if ( op->ora_e->e_attrs == NULL ) {
448 char textbuf[ SLAP_TEXT_BUFLEN ];
449 size_t textlen = sizeof( textbuf );
452 /* FIXME: op->o_bd is still set to the BackendDB
453 * structure of the database that tried to handle
454 * the operation and actually returned a referral
456 assert( SLAP_DBFLAGS( op->o_bd ) & SLAP_DBFLAG_GLOBAL_OVERLAY );
459 /* global overlay: create entry */
460 /* NOTE: this is a hack to use the chain overlay
461 * as global. I expect to be able to remove this
462 * soon by using slap_mods2entry() earlier in
463 * do_add(), adding the operational attrs later
465 rs->sr_err = slap_mods2entry( op->ora_modlist,
467 &rs->sr_text, textbuf, textlen );
468 if ( rs->sr_err != LDAP_SUCCESS ) {
469 send_ldap_result( op, rs );
474 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
475 if ( cleanup_attrs ) {
476 attrs_free( op->ora_e->e_attrs );
477 op->ora_e->e_attrs = NULL;
481 case LDAP_REQ_DELETE:
482 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
484 case LDAP_REQ_MODRDN:
485 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
487 case LDAP_REQ_MODIFY:
488 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
490 case LDAP_REQ_COMPARE:
491 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
493 case LDAP_REQ_SEARCH:
494 if ( rs->sr_type == REP_SEARCHREF ) {
495 struct berval *curr = ref,
497 ondn = op->o_req_ndn;
499 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
500 LDAPControl **ctrls = NULL;
502 (void)chaining_control_add( lc, op, &ctrls );
503 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
505 rs->sr_type = REP_SEARCH;
507 sc2.sc_response = ldap_chain_cb_search_response;
511 op->o_bd->be_private = &li;
513 /* if we parse the URI then by no means
514 * we can cache stuff or reuse connections,
515 * because in back-ldap there's no caching
516 * based on the URI value, which is supposed
517 * to be set once for all (correct?) */
518 op->o_do_not_cache = 1;
520 /* copy the private info because we need to modify it */
521 for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
525 /* parse reference and use
526 * proto://[host][:port]/ only */
527 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
528 if ( rc != LDAP_URL_SUCCESS ) {
530 rs->sr_err = LDAP_OTHER;
534 /* remove DN essentially because later on
535 * ldap_initialize() will parse the URL
536 * as a comma-separated URL list */
537 save_dn = srv->lud_dn;
539 srv->lud_scope = LDAP_SCOPE_DEFAULT;
540 li.url = ldap_url_desc2str( srv );
541 if ( li.url != NULL ) {
542 ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
544 ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
548 srv->lud_dn = save_dn;
549 ldap_free_urldesc( srv );
551 if ( li.url == NULL ) {
553 rs->sr_err = LDAP_OTHER;
558 /* FIXME: should we also copy filter and scope?
559 * according to RFC3296, no */
560 rc = lback->bi_op_search( op, rs );
562 ldap_memfree( li.url );
565 op->o_tmpfree( op->o_req_dn.bv_val,
567 op->o_tmpfree( op->o_req_ndn.bv_val,
570 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
577 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
578 (void)chaining_control_remove( op, &ctrls );
579 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
582 op->o_req_ndn = ondn;
583 rs->sr_type = REP_SEARCHREF;
586 if ( rc != LDAP_SUCCESS ) {
587 /* couldn't chase any of the referrals */
588 rc = SLAP_CB_CONTINUE;
592 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
595 case LDAP_REQ_EXTENDED:
596 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
597 /* FIXME: ldap_back_extended() by design
598 * doesn't send result; frontend is expected
600 if ( rc != SLAPD_ABANDON ) {
601 send_ldap_extended( op, rs );
606 rc = SLAP_CB_CONTINUE;
610 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
611 if ( rc != LDAP_SUCCESS || sc2.sc_private == LDAP_CH_ERR ) {
612 if ( rs->sr_err == LDAP_CANNOT_CHAIN ) {
616 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
617 case LDAP_CHAINING_REQUIRED:
619 op->o_callback = NULL;
620 send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" );
624 rc = SLAP_CB_CONTINUE;
626 rs->sr_type = sr_type;
631 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
633 if ( sc2.sc_private == LDAP_CH_NONE ) {
634 op->o_callback = NULL;
635 rc = rs->sr_err = slap_map_api2result( rs );
636 send_ldap_result( op, rs );
639 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
641 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
642 op->o_do_not_cache = cache;
643 op->o_bd->be_private = private;
651 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
653 ldap_chain_parse_ctrl(
659 str2chain( const char *s )
661 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
662 return LDAP_CHAINING_PREFERRED;
664 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
665 return LDAP_CHAINING_REQUIRED;
667 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
668 return LDAP_REFERRALS_PREFERRED;
670 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
671 return LDAP_REFERRALS_REQUIRED;
676 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
682 static ConfigDriver chain_cf_gen;
683 static ConfigLDAPadd chain_ldadd;
684 static ConfigCfAdd chain_cfadd;
686 static ConfigTable chaincfg[] = {
687 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
688 { "chain-chaining", "args",
689 2, 4, 0, ARG_MAGIC|ARG_BERVAL|PC_CHAINING, chain_cf_gen,
690 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
691 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
692 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
693 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
694 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
697 static ConfigOCs chainocs[] = {
699 "NAME 'olcChainConfig' "
700 "DESC 'Chain configuration' "
701 "SUP olcOverlayConfig "
702 "MAY olcChainingBehavior )", Cft_Overlay, chaincfg, NULL, chain_cfadd },
704 "NAME 'olcChainDatabase' "
705 "DESC 'Chain remote server configuration' "
706 "AUXILIARY )", Cft_Misc, chaincfg, chain_ldadd },
711 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
713 if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
714 p->ce_bi->bi_cf_ocs != chainocs )
715 return LDAP_CONSTRAINT_VIOLATION;
721 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
723 CfEntryInfo *pe = p->e_private;
724 slap_overinst *on = (slap_overinst *)pe->ce_bi;
725 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
726 void *priv = (void *)ca->be->be_private;
729 /* FIXME: should not hardcode "olcDatabase" here */
730 bv.bv_len = sprintf( ca->msg, "olcDatabase=%s", lback->bi_type );
733 /* We can only create this entry if the database is table-driven */
734 if ( lback->bi_cf_ocs ) {
735 ca->be->be_private = (void *)lc->lc_li;
736 config_build_entry( op, rs, pe, ca, &bv, lback->bi_cf_ocs, &chainocs[1] );
737 ca->be->be_private = priv;
743 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
744 static slap_verbmasks chaining_mode[] = {
745 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
746 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
747 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
748 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
754 chain_cf_gen( ConfigArgs *c )
756 slap_overinst *on = (slap_overinst *)c->bi;
757 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
761 if ( c->op == SLAP_CONFIG_EMIT ) {
763 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
765 struct berval resolve = BER_BVNULL,
766 continuation = BER_BVNULL;
768 if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
772 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
773 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
775 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
777 + STRLENOF( "continuation=" ) + continuation.bv_len;
778 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
779 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
780 "resolve=%s continuation=%s",
781 resolve.bv_val, continuation.bv_val );
783 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
784 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
785 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
786 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
787 " critical", STRLENOF( " critical" ) + 1 );
788 c->value_bv.bv_len += STRLENOF( " critical" );
793 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
801 } else if ( c->op == LDAP_MOD_DELETE ) {
802 return 1; /* FIXME */
813 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
815 char **argv = c->argv;
817 BerElementBuffer berbuf;
818 BerElement *ber = (BerElement *)&berbuf;
822 Operation op = { 0 };
823 SlapReply rs = { 0 };
825 lc->lc_chaining_ctrlflag = 0;
827 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
828 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
829 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
830 if ( resolve == -1 ) {
831 fprintf( stderr, "%s line %d: "
832 "illegal <resolve> value %s "
833 "in \"chain-chaining>\"\n",
834 c->fname, c->lineno, argv[ 0 ] );
838 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
839 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
840 if ( continuation == -1 ) {
841 fprintf( stderr, "%s line %d: "
842 "illegal <continuation> value %s "
843 "in \"chain-chaining\"\n",
844 c->fname, c->lineno, argv[ 0 ] );
848 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
852 fprintf( stderr, "%s line %d: "
853 "unknown option in \"chain-chaining\"\n",
854 c->fname, c->lineno );
859 if ( resolve != -1 || continuation != -1 ) {
862 if ( resolve == -1 ) {
864 resolve = SLAP_CHAINING_DEFAULT;
867 ber_init2( ber, NULL, LBER_USE_DER );
869 err = ber_printf( ber, "{e" /* } */, resolve );
872 fprintf( stderr, "%s line %d: "
873 "chaining behavior control encoding error!\n",
874 c->fname, c->lineno );
878 if ( continuation > -1 ) {
879 err = ber_printf( ber, "e", continuation );
882 fprintf( stderr, "%s line %d: "
883 "chaining behavior control encoding error!\n",
884 c->fname, c->lineno );
889 err = ber_printf( ber, /* { */ "N}" );
892 fprintf( stderr, "%s line %d: "
893 "chaining behavior control encoding error!\n",
894 c->fname, c->lineno );
898 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
899 exit( EXIT_FAILURE );
903 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
906 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
907 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
909 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
911 fprintf( stderr, "%s line %d: "
912 "unable to parse chaining control%s%s\n",
914 rs.sr_text ? ": " : "",
915 rs.sr_text ? rs.sr_text : "" );
919 lc->lc_chaining_ctrlflag = op.o_chaining;
921 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
927 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
937 ldap_chain_db_config(
944 slap_overinst *on = (slap_overinst *) be->bd_info;
945 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
950 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
952 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
954 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
955 if ( strcasecmp( argv[ 0 ], "chaining" ) == 0 ) {
956 char **tmpargv = argv;
957 BerElementBuffer berbuf;
958 BerElement *ber = (BerElement *)&berbuf;
962 Operation op = { 0 };
963 SlapReply rs = { 0 };
965 lc->lc_chaining_ctrlflag = 0;
967 for ( argc--, tmpargv++; argc > 0; argc--, tmpargv++ ) {
968 if ( strncasecmp( tmpargv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
969 resolve = str2chain( tmpargv[ 0 ] + STRLENOF( "resolve=" ) );
970 if ( resolve == -1 ) {
971 fprintf( stderr, "%s line %d: "
972 "illegal <resolve> value %s "
973 "in \"chain-chaining>\"\n",
974 fname, lineno, tmpargv[ 0 ] );
978 } else if ( strncasecmp( tmpargv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
979 continuation = str2chain( tmpargv[ 0 ] + STRLENOF( "continuation=" ) );
980 if ( continuation == -1 ) {
981 fprintf( stderr, "%s line %d: "
982 "illegal <continuation> value %s "
983 "in \"chain-chaining\"\n",
984 fname, lineno, tmpargv[ 0 ] );
988 } else if ( strcasecmp( tmpargv[ 0 ], "critical" ) == 0 ) {
992 fprintf( stderr, "%s line %d: "
993 "unknown option in \"chain-chaining\"\n",
999 if ( resolve != -1 || continuation != -1 ) {
1002 if ( resolve == -1 ) {
1004 resolve = SLAP_CHAINING_DEFAULT;
1007 ber_init2( ber, NULL, LBER_USE_DER );
1009 err = ber_printf( ber, "{e" /* } */, resolve );
1012 fprintf( stderr, "%s line %d: "
1013 "chaining behavior control encoding error!\n",
1018 if ( continuation > -1 ) {
1019 err = ber_printf( ber, "e", continuation );
1022 fprintf( stderr, "%s line %d: "
1023 "chaining behavior control encoding error!\n",
1029 err = ber_printf( ber, /* { */ "N}" );
1032 fprintf( stderr, "%s line %d: "
1033 "chaining behavior control encoding error!\n",
1038 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1039 exit( EXIT_FAILURE );
1043 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1046 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1047 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1049 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1051 fprintf( stderr, "%s line %d: "
1052 "unable to parse chaining control%s%s\n",
1054 rs.sr_text ? ": " : "",
1055 rs.sr_text ? rs.sr_text : "" );
1059 lc->lc_chaining_ctrlflag = op.o_chaining;
1061 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1066 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1069 db.be_cf_ocs = lback->bi_cf_ocs;
1070 db.be_private = lc->lc_li;
1071 rc = lback->bi_db_config( &db, fname, lineno, argc, argv );
1085 slap_overinst *on = (slap_overinst *)be->bd_info;
1086 ldap_chain_t *lc = NULL;
1090 if ( lback == NULL ) {
1091 lback = backend_info( "ldap" );
1093 if ( lback == NULL ) {
1098 lc = ch_malloc( sizeof( ldap_chain_t ) );
1099 memset( lc, 0, sizeof( ldap_chain_t ) );
1101 bd.be_private = NULL;
1102 rc = lback->bi_db_init( &bd );
1103 lc->lc_li = (struct ldapinfo *)bd.be_private;
1104 on->on_bi.bi_private = (void *)lc;
1116 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1117 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1118 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1124 ldap_chain_db_destroy(
1128 slap_overinst *on = (slap_overinst *) be->bd_info;
1129 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1130 void *private = be->be_private;
1133 be->be_private = (void *)lc->lc_li;
1134 rc = lback->bi_db_destroy( be );
1135 lc->lc_li = be->be_private;
1137 on->on_bi.bi_private = NULL;
1138 be->be_private = private;
1143 ldap_chain_connection_destroy(
1148 slap_overinst *on = (slap_overinst *) be->bd_info;
1149 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1150 void *private = be->be_private;
1153 be->be_private = (void *)lc->lc_li;
1154 rc = lback->bi_connection_destroy( be, conn );
1155 be->be_private = private;
1160 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1162 ldap_chain_parse_ctrl(
1172 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1173 rs->sr_text = "Chaining behavior control specified multiple times";
1174 return LDAP_PROTOCOL_ERROR;
1177 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1178 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1179 return LDAP_PROTOCOL_ERROR;
1182 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1183 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1188 /* Parse the control value
1189 * ChainingBehavior ::= SEQUENCE {
1190 * resolveBehavior Behavior OPTIONAL,
1191 * continuationBehavior Behavior OPTIONAL }
1193 * Behavior :: = ENUMERATED {
1194 * chainingPreferred (0),
1195 * chainingRequired (1),
1196 * referralsPreferred (2),
1197 * referralsRequired (3) }
1200 ber = ber_init( &ctrl->ldctl_value );
1202 rs->sr_text = "internal error";
1206 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1207 /* FIXME: since the whole SEQUENCE is optional,
1208 * should we accept no enumerations at all? */
1209 if ( tag != LBER_ENUMERATED ) {
1210 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1211 return LDAP_PROTOCOL_ERROR;
1214 switch ( behavior ) {
1215 case LDAP_CHAINING_PREFERRED:
1216 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1219 case LDAP_CHAINING_REQUIRED:
1220 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1223 case LDAP_REFERRALS_PREFERRED:
1224 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1227 case LDAP_REFERRALS_REQUIRED:
1228 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1232 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1233 return LDAP_PROTOCOL_ERROR;
1236 tag = ber_peek_tag( ber, &len );
1237 if ( tag == LBER_ENUMERATED ) {
1238 tag = ber_scanf( ber, "e", &behavior );
1239 if ( tag == LBER_ERROR ) {
1240 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1241 return LDAP_PROTOCOL_ERROR;
1245 if ( tag == LBER_DEFAULT ) {
1246 mode |= SLAP_CH_CONTINUATION_DEFAULT;
1249 switch ( behavior ) {
1250 case LDAP_CHAINING_PREFERRED:
1251 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1254 case LDAP_CHAINING_REQUIRED:
1255 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1258 case LDAP_REFERRALS_PREFERRED:
1259 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1262 case LDAP_REFERRALS_REQUIRED:
1263 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1267 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1268 return LDAP_PROTOCOL_ERROR;
1272 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1273 rs->sr_text = "Chaining behavior control: decoding error";
1274 return LDAP_PROTOCOL_ERROR;
1277 (void) ber_free( ber, 1 );
1280 op->o_chaining = mode | ( ctrl->ldctl_iscritical
1281 ? SLAP_CONTROL_CRITICAL
1282 : SLAP_CONTROL_NONCRITICAL );
1284 return LDAP_SUCCESS;
1286 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1288 static slap_overinst ldapchain;
1295 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1296 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1297 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1298 ldap_chain_parse_ctrl, &sc_chainingBehavior );
1299 if ( rc != LDAP_SUCCESS ) {
1300 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
1303 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1305 ldapchain.on_bi.bi_type = "chain";
1306 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1307 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1308 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1309 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1311 /* ... otherwise the underlying backend's function would be called,
1312 * likely passing an invalid entry; on the contrary, the requested
1313 * operational attributes should have been returned while chasing
1314 * the referrals. This all in all is a bit messy, because part
1315 * of the operational attributes are generated by the backend;
1316 * part by the frontend; back-ldap should receive all the available
1317 * ones from the remote server, but then, on its own, it strips those
1318 * it assumes will be (re)generated by the frontend (e.g.
1319 * subschemaSubentry.) */
1320 ldapchain.on_bi.bi_operational = ldap_chain_operational;
1322 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1324 ldapchain.on_response = ldap_chain_response;
1326 ldapchain.on_bi.bi_cf_ocs = chainocs;
1328 rc = config_register_schema( chaincfg, chainocs );
1333 return overlay_register( &ldapchain );