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 != NULL );
136 assert( op->o_ctrls[ 0 ] != NULL );
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[] = {
698 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
700 "NAME 'olcChainConfig' "
701 "DESC 'Chain configuration' "
702 "SUP olcOverlayConfig "
703 "MAY olcChainingBehavior )", Cft_Overlay, chaincfg, NULL, chain_cfadd },
706 "NAME 'olcChainDatabase' "
707 "DESC 'Chain remote server configuration' "
708 "AUXILIARY )", Cft_Misc, chaincfg, chain_ldadd },
713 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
715 if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
716 p->ce_bi->bi_cf_ocs != chainocs )
717 return LDAP_CONSTRAINT_VIOLATION;
723 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
725 CfEntryInfo *pe = p->e_private;
726 slap_overinst *on = (slap_overinst *)pe->ce_bi;
727 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
728 void *priv = (void *)ca->be->be_private;
731 /* FIXME: should not hardcode "olcDatabase" here */
732 bv.bv_len = sprintf( ca->msg, "olcDatabase=%s", lback->bi_type );
735 /* We can only create this entry if the database is table-driven */
736 if ( lback->bi_cf_ocs ) {
737 ca->be->be_private = (void *)lc->lc_li;
738 config_build_entry( op, rs, pe, ca, &bv, lback->bi_cf_ocs, &chainocs[1] );
739 ca->be->be_private = priv;
745 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
746 static slap_verbmasks chaining_mode[] = {
747 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
748 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
749 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
750 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
756 chain_cf_gen( ConfigArgs *c )
758 slap_overinst *on = (slap_overinst *)c->bi;
759 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
763 if ( c->op == SLAP_CONFIG_EMIT ) {
765 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
767 struct berval resolve = BER_BVNULL,
768 continuation = BER_BVNULL;
770 if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
774 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
775 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
777 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
779 + STRLENOF( "continuation=" ) + continuation.bv_len;
780 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
781 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
782 "resolve=%s continuation=%s",
783 resolve.bv_val, continuation.bv_val );
785 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
786 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
787 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
788 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
789 " critical", STRLENOF( " critical" ) + 1 );
790 c->value_bv.bv_len += STRLENOF( " critical" );
795 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
803 } else if ( c->op == LDAP_MOD_DELETE ) {
804 return 1; /* FIXME */
815 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
817 char **argv = c->argv;
819 BerElementBuffer berbuf;
820 BerElement *ber = (BerElement *)&berbuf;
824 Operation op = { 0 };
825 SlapReply rs = { 0 };
827 lc->lc_chaining_ctrlflag = 0;
829 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
830 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
831 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
832 if ( resolve == -1 ) {
833 fprintf( stderr, "%s line %d: "
834 "illegal <resolve> value %s "
835 "in \"chain-chaining>\"\n",
836 c->fname, c->lineno, argv[ 0 ] );
840 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
841 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
842 if ( continuation == -1 ) {
843 fprintf( stderr, "%s line %d: "
844 "illegal <continuation> value %s "
845 "in \"chain-chaining\"\n",
846 c->fname, c->lineno, argv[ 0 ] );
850 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
854 fprintf( stderr, "%s line %d: "
855 "unknown option in \"chain-chaining\"\n",
856 c->fname, c->lineno );
861 if ( resolve != -1 || continuation != -1 ) {
864 if ( resolve == -1 ) {
866 resolve = SLAP_CHAINING_DEFAULT;
869 ber_init2( ber, NULL, LBER_USE_DER );
871 err = ber_printf( ber, "{e" /* } */, resolve );
874 fprintf( stderr, "%s line %d: "
875 "chaining behavior control encoding error!\n",
876 c->fname, c->lineno );
880 if ( continuation > -1 ) {
881 err = ber_printf( ber, "e", continuation );
884 fprintf( stderr, "%s line %d: "
885 "chaining behavior control encoding error!\n",
886 c->fname, c->lineno );
891 err = ber_printf( ber, /* { */ "N}" );
894 fprintf( stderr, "%s line %d: "
895 "chaining behavior control encoding error!\n",
896 c->fname, c->lineno );
900 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
901 exit( EXIT_FAILURE );
905 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
908 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
909 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
911 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
913 fprintf( stderr, "%s line %d: "
914 "unable to parse chaining control%s%s\n",
916 rs.sr_text ? ": " : "",
917 rs.sr_text ? rs.sr_text : "" );
921 lc->lc_chaining_ctrlflag = op.o_chaining;
923 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
929 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
939 ldap_chain_db_config(
946 slap_overinst *on = (slap_overinst *) be->bd_info;
947 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
952 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
954 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
956 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
957 if ( strcasecmp( argv[ 0 ], "chaining" ) == 0 ) {
958 char **tmpargv = argv;
959 BerElementBuffer berbuf;
960 BerElement *ber = (BerElement *)&berbuf;
964 Operation op = { 0 };
965 SlapReply rs = { 0 };
967 lc->lc_chaining_ctrlflag = 0;
969 for ( argc--, tmpargv++; argc > 0; argc--, tmpargv++ ) {
970 if ( strncasecmp( tmpargv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
971 resolve = str2chain( tmpargv[ 0 ] + STRLENOF( "resolve=" ) );
972 if ( resolve == -1 ) {
973 fprintf( stderr, "%s line %d: "
974 "illegal <resolve> value %s "
975 "in \"chain-chaining>\"\n",
976 fname, lineno, tmpargv[ 0 ] );
980 } else if ( strncasecmp( tmpargv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
981 continuation = str2chain( tmpargv[ 0 ] + STRLENOF( "continuation=" ) );
982 if ( continuation == -1 ) {
983 fprintf( stderr, "%s line %d: "
984 "illegal <continuation> value %s "
985 "in \"chain-chaining\"\n",
986 fname, lineno, tmpargv[ 0 ] );
990 } else if ( strcasecmp( tmpargv[ 0 ], "critical" ) == 0 ) {
994 fprintf( stderr, "%s line %d: "
995 "unknown option in \"chain-chaining\"\n",
1001 if ( resolve != -1 || continuation != -1 ) {
1004 if ( resolve == -1 ) {
1006 resolve = SLAP_CHAINING_DEFAULT;
1009 ber_init2( ber, NULL, LBER_USE_DER );
1011 err = ber_printf( ber, "{e" /* } */, resolve );
1014 fprintf( stderr, "%s line %d: "
1015 "chaining behavior control encoding error!\n",
1020 if ( continuation > -1 ) {
1021 err = ber_printf( ber, "e", continuation );
1024 fprintf( stderr, "%s line %d: "
1025 "chaining behavior control encoding error!\n",
1031 err = ber_printf( ber, /* { */ "N}" );
1034 fprintf( stderr, "%s line %d: "
1035 "chaining behavior control encoding error!\n",
1040 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1041 exit( EXIT_FAILURE );
1045 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1048 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1049 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1051 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1053 fprintf( stderr, "%s line %d: "
1054 "unable to parse chaining control%s%s\n",
1056 rs.sr_text ? ": " : "",
1057 rs.sr_text ? rs.sr_text : "" );
1061 lc->lc_chaining_ctrlflag = op.o_chaining;
1063 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1068 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1071 db.be_cf_ocs = lback->bi_cf_ocs;
1072 db.be_private = lc->lc_li;
1073 rc = lback->bi_db_config( &db, fname, lineno, argc, argv );
1087 slap_overinst *on = (slap_overinst *)be->bd_info;
1088 ldap_chain_t *lc = NULL;
1092 if ( lback == NULL ) {
1093 lback = backend_info( "ldap" );
1095 if ( lback == NULL ) {
1100 lc = ch_malloc( sizeof( ldap_chain_t ) );
1101 memset( lc, 0, sizeof( ldap_chain_t ) );
1103 bd.be_private = NULL;
1104 rc = lback->bi_db_init( &bd );
1105 lc->lc_li = (struct ldapinfo *)bd.be_private;
1106 on->on_bi.bi_private = (void *)lc;
1118 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1119 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1120 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1126 ldap_chain_db_destroy(
1130 slap_overinst *on = (slap_overinst *) be->bd_info;
1131 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1132 void *private = be->be_private;
1135 be->be_private = (void *)lc->lc_li;
1136 rc = lback->bi_db_destroy( be );
1137 lc->lc_li = be->be_private;
1139 on->on_bi.bi_private = NULL;
1140 be->be_private = private;
1145 ldap_chain_connection_destroy(
1150 slap_overinst *on = (slap_overinst *) be->bd_info;
1151 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1152 void *private = be->be_private;
1155 be->be_private = (void *)lc->lc_li;
1156 rc = lback->bi_connection_destroy( be, conn );
1157 be->be_private = private;
1162 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1164 ldap_chain_parse_ctrl(
1174 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1175 rs->sr_text = "Chaining behavior control specified multiple times";
1176 return LDAP_PROTOCOL_ERROR;
1179 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1180 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1181 return LDAP_PROTOCOL_ERROR;
1184 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1185 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1190 /* Parse the control value
1191 * ChainingBehavior ::= SEQUENCE {
1192 * resolveBehavior Behavior OPTIONAL,
1193 * continuationBehavior Behavior OPTIONAL }
1195 * Behavior :: = ENUMERATED {
1196 * chainingPreferred (0),
1197 * chainingRequired (1),
1198 * referralsPreferred (2),
1199 * referralsRequired (3) }
1202 ber = ber_init( &ctrl->ldctl_value );
1204 rs->sr_text = "internal error";
1208 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1209 /* FIXME: since the whole SEQUENCE is optional,
1210 * should we accept no enumerations at all? */
1211 if ( tag != LBER_ENUMERATED ) {
1212 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1213 return LDAP_PROTOCOL_ERROR;
1216 switch ( behavior ) {
1217 case LDAP_CHAINING_PREFERRED:
1218 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1221 case LDAP_CHAINING_REQUIRED:
1222 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1225 case LDAP_REFERRALS_PREFERRED:
1226 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1229 case LDAP_REFERRALS_REQUIRED:
1230 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1234 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1235 return LDAP_PROTOCOL_ERROR;
1238 tag = ber_peek_tag( ber, &len );
1239 if ( tag == LBER_ENUMERATED ) {
1240 tag = ber_scanf( ber, "e", &behavior );
1241 if ( tag == LBER_ERROR ) {
1242 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1243 return LDAP_PROTOCOL_ERROR;
1247 if ( tag == LBER_DEFAULT ) {
1248 mode |= SLAP_CH_CONTINUATION_DEFAULT;
1251 switch ( behavior ) {
1252 case LDAP_CHAINING_PREFERRED:
1253 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1256 case LDAP_CHAINING_REQUIRED:
1257 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1260 case LDAP_REFERRALS_PREFERRED:
1261 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1264 case LDAP_REFERRALS_REQUIRED:
1265 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1269 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1270 return LDAP_PROTOCOL_ERROR;
1274 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1275 rs->sr_text = "Chaining behavior control: decoding error";
1276 return LDAP_PROTOCOL_ERROR;
1279 (void) ber_free( ber, 1 );
1282 op->o_chaining = mode | ( ctrl->ldctl_iscritical
1283 ? SLAP_CONTROL_CRITICAL
1284 : SLAP_CONTROL_NONCRITICAL );
1286 return LDAP_SUCCESS;
1288 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1290 static slap_overinst ldapchain;
1297 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1298 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1299 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1300 ldap_chain_parse_ctrl, &sc_chainingBehavior );
1301 if ( rc != LDAP_SUCCESS ) {
1302 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
1305 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1307 ldapchain.on_bi.bi_type = "chain";
1308 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1309 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1310 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1311 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1313 /* ... otherwise the underlying backend's function would be called,
1314 * likely passing an invalid entry; on the contrary, the requested
1315 * operational attributes should have been returned while chasing
1316 * the referrals. This all in all is a bit messy, because part
1317 * of the operational attributes are generated by the backend;
1318 * part by the frontend; back-ldap should receive all the available
1319 * ones from the remote server, but then, on its own, it strips those
1320 * it assumes will be (re)generated by the frontend (e.g.
1321 * subschemaSubentry.) */
1322 ldapchain.on_bi.bi_operational = ldap_chain_operational;
1324 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1326 ldapchain.on_response = ldap_chain_response;
1328 ldapchain.on_bi.bi_cf_ocs = chainocs;
1330 rc = config_register_schema( chaincfg, chainocs );
1335 return overlay_register( &ldapchain );