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 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
683 static ConfigDriver chain_cf_gen;
684 static ConfigCfAdd chain_cfadd;
686 static ConfigLDAPadd chain_ldadd;
688 static ConfigTable chaincfg[] = {
689 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
690 { "chain-chaining", "args",
691 2, 4, 0, ARG_MAGIC|ARG_BERVAL|PC_CHAINING, chain_cf_gen,
692 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
693 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
694 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
695 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
696 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
699 static ConfigOCs chainocs[] = {
700 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
702 "NAME 'olcChainConfig' "
703 "DESC 'Chain configuration' "
704 "SUP olcOverlayConfig "
705 "MAY olcChainingBehavior )", Cft_Overlay, chaincfg, NULL, chain_cfadd },
708 "NAME 'olcChainDatabase' "
709 "DESC 'Chain remote server configuration' "
710 "AUXILIARY )", Cft_Misc, chaincfg, chain_ldadd },
715 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
717 if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
718 p->ce_bi->bi_cf_ocs != chainocs )
719 return LDAP_CONSTRAINT_VIOLATION;
724 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
727 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
729 CfEntryInfo *pe = p->e_private;
730 slap_overinst *on = (slap_overinst *)pe->ce_bi;
731 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
732 void *priv = (void *)ca->be->be_private;
735 /* FIXME: should not hardcode "olcDatabase" here */
736 bv.bv_len = sprintf( ca->msg, "olcDatabase=%s", lback->bi_type );
739 /* We can only create this entry if the database is table-driven */
740 if ( lback->bi_cf_ocs ) {
741 ca->be->be_private = (void *)lc->lc_li;
742 config_build_entry( op, rs, pe, ca, &bv, lback->bi_cf_ocs, &chainocs[1] );
743 ca->be->be_private = priv;
749 static slap_verbmasks chaining_mode[] = {
750 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
751 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
752 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
753 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
758 chain_cf_gen( ConfigArgs *c )
760 slap_overinst *on = (slap_overinst *)c->bi;
761 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
762 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
767 if ( c->op == SLAP_CONFIG_EMIT ) {
769 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
771 struct berval resolve = BER_BVNULL,
772 continuation = BER_BVNULL;
774 if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
778 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
779 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
781 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
783 + STRLENOF( "continuation=" ) + continuation.bv_len;
784 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
785 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
786 "resolve=%s continuation=%s",
787 resolve.bv_val, continuation.bv_val );
789 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
790 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
791 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
792 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
793 " critical", STRLENOF( " critical" ) + 1 );
794 c->value_bv.bv_len += STRLENOF( " critical" );
799 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
807 } else if ( c->op == LDAP_MOD_DELETE ) {
808 return 1; /* FIXME */
819 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
821 char **argv = c->argv;
823 BerElementBuffer berbuf;
824 BerElement *ber = (BerElement *)&berbuf;
828 Operation op = { 0 };
829 SlapReply rs = { 0 };
831 lc->lc_chaining_ctrlflag = 0;
833 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
834 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
835 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
836 if ( resolve == -1 ) {
837 fprintf( stderr, "%s line %d: "
838 "illegal <resolve> value %s "
839 "in \"chain-chaining>\"\n",
840 c->fname, c->lineno, argv[ 0 ] );
844 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
845 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
846 if ( continuation == -1 ) {
847 fprintf( stderr, "%s line %d: "
848 "illegal <continuation> value %s "
849 "in \"chain-chaining\"\n",
850 c->fname, c->lineno, argv[ 0 ] );
854 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
858 fprintf( stderr, "%s line %d: "
859 "unknown option in \"chain-chaining\"\n",
860 c->fname, c->lineno );
865 if ( resolve != -1 || continuation != -1 ) {
868 if ( resolve == -1 ) {
870 resolve = SLAP_CHAINING_DEFAULT;
873 ber_init2( ber, NULL, LBER_USE_DER );
875 err = ber_printf( ber, "{e" /* } */, resolve );
878 fprintf( stderr, "%s line %d: "
879 "chaining behavior control encoding error!\n",
880 c->fname, c->lineno );
884 if ( continuation > -1 ) {
885 err = ber_printf( ber, "e", continuation );
888 fprintf( stderr, "%s line %d: "
889 "chaining behavior control encoding error!\n",
890 c->fname, c->lineno );
895 err = ber_printf( ber, /* { */ "N}" );
898 fprintf( stderr, "%s line %d: "
899 "chaining behavior control encoding error!\n",
900 c->fname, c->lineno );
904 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
905 exit( EXIT_FAILURE );
909 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
912 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
913 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
915 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
917 fprintf( stderr, "%s line %d: "
918 "unable to parse chaining control%s%s\n",
920 rs.sr_text ? ": " : "",
921 rs.sr_text ? rs.sr_text : "" );
925 lc->lc_chaining_ctrlflag = op.o_chaining;
927 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
933 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
942 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
945 ldap_chain_db_config(
952 slap_overinst *on = (slap_overinst *) be->bd_info;
953 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
958 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
960 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
962 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
963 if ( strcasecmp( argv[ 0 ], "chaining" ) == 0 ) {
964 char **tmpargv = argv;
965 BerElementBuffer berbuf;
966 BerElement *ber = (BerElement *)&berbuf;
970 Operation op = { 0 };
971 SlapReply rs = { 0 };
973 lc->lc_chaining_ctrlflag = 0;
975 for ( argc--, tmpargv++; argc > 0; argc--, tmpargv++ ) {
976 if ( strncasecmp( tmpargv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
977 resolve = str2chain( tmpargv[ 0 ] + STRLENOF( "resolve=" ) );
978 if ( resolve == -1 ) {
979 fprintf( stderr, "%s line %d: "
980 "illegal <resolve> value %s "
981 "in \"chain-chaining>\"\n",
982 fname, lineno, tmpargv[ 0 ] );
986 } else if ( strncasecmp( tmpargv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
987 continuation = str2chain( tmpargv[ 0 ] + STRLENOF( "continuation=" ) );
988 if ( continuation == -1 ) {
989 fprintf( stderr, "%s line %d: "
990 "illegal <continuation> value %s "
991 "in \"chain-chaining\"\n",
992 fname, lineno, tmpargv[ 0 ] );
996 } else if ( strcasecmp( tmpargv[ 0 ], "critical" ) == 0 ) {
1000 fprintf( stderr, "%s line %d: "
1001 "unknown option in \"chain-chaining\"\n",
1007 if ( resolve != -1 || continuation != -1 ) {
1010 if ( resolve == -1 ) {
1012 resolve = SLAP_CHAINING_DEFAULT;
1015 ber_init2( ber, NULL, LBER_USE_DER );
1017 err = ber_printf( ber, "{e" /* } */, resolve );
1020 fprintf( stderr, "%s line %d: "
1021 "chaining behavior control encoding error!\n",
1026 if ( continuation > -1 ) {
1027 err = ber_printf( ber, "e", continuation );
1030 fprintf( stderr, "%s line %d: "
1031 "chaining behavior control encoding error!\n",
1037 err = ber_printf( ber, /* { */ "N}" );
1040 fprintf( stderr, "%s line %d: "
1041 "chaining behavior control encoding error!\n",
1046 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1047 exit( EXIT_FAILURE );
1051 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1054 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1055 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1057 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1059 fprintf( stderr, "%s line %d: "
1060 "unable to parse chaining control%s%s\n",
1062 rs.sr_text ? ": " : "",
1063 rs.sr_text ? rs.sr_text : "" );
1067 lc->lc_chaining_ctrlflag = op.o_chaining;
1069 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1074 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1077 db.be_cf_ocs = lback->bi_cf_ocs;
1078 db.be_private = lc->lc_li;
1079 rc = lback->bi_db_config( &db, fname, lineno, argc, argv );
1093 slap_overinst *on = (slap_overinst *)be->bd_info;
1094 ldap_chain_t *lc = NULL;
1098 if ( lback == NULL ) {
1099 lback = backend_info( "ldap" );
1101 if ( lback == NULL ) {
1106 lc = ch_malloc( sizeof( ldap_chain_t ) );
1107 memset( lc, 0, sizeof( ldap_chain_t ) );
1109 bd.be_private = NULL;
1110 rc = lback->bi_db_init( &bd );
1111 lc->lc_li = (struct ldapinfo *)bd.be_private;
1112 on->on_bi.bi_private = (void *)lc;
1124 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1125 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1126 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1132 ldap_chain_db_destroy(
1136 slap_overinst *on = (slap_overinst *) be->bd_info;
1137 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1138 void *private = be->be_private;
1141 be->be_private = (void *)lc->lc_li;
1142 rc = lback->bi_db_destroy( be );
1143 lc->lc_li = be->be_private;
1145 on->on_bi.bi_private = NULL;
1146 be->be_private = private;
1151 ldap_chain_connection_destroy(
1156 slap_overinst *on = (slap_overinst *) be->bd_info;
1157 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1158 void *private = be->be_private;
1161 be->be_private = (void *)lc->lc_li;
1162 rc = lback->bi_connection_destroy( be, conn );
1163 be->be_private = private;
1168 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1170 ldap_chain_parse_ctrl(
1180 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1181 rs->sr_text = "Chaining behavior control specified multiple times";
1182 return LDAP_PROTOCOL_ERROR;
1185 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1186 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1187 return LDAP_PROTOCOL_ERROR;
1190 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1191 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1196 /* Parse the control value
1197 * ChainingBehavior ::= SEQUENCE {
1198 * resolveBehavior Behavior OPTIONAL,
1199 * continuationBehavior Behavior OPTIONAL }
1201 * Behavior :: = ENUMERATED {
1202 * chainingPreferred (0),
1203 * chainingRequired (1),
1204 * referralsPreferred (2),
1205 * referralsRequired (3) }
1208 ber = ber_init( &ctrl->ldctl_value );
1210 rs->sr_text = "internal error";
1214 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1215 /* FIXME: since the whole SEQUENCE is optional,
1216 * should we accept no enumerations at all? */
1217 if ( tag != LBER_ENUMERATED ) {
1218 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1219 return LDAP_PROTOCOL_ERROR;
1222 switch ( behavior ) {
1223 case LDAP_CHAINING_PREFERRED:
1224 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1227 case LDAP_CHAINING_REQUIRED:
1228 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1231 case LDAP_REFERRALS_PREFERRED:
1232 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1235 case LDAP_REFERRALS_REQUIRED:
1236 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1240 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1241 return LDAP_PROTOCOL_ERROR;
1244 tag = ber_peek_tag( ber, &len );
1245 if ( tag == LBER_ENUMERATED ) {
1246 tag = ber_scanf( ber, "e", &behavior );
1247 if ( tag == LBER_ERROR ) {
1248 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1249 return LDAP_PROTOCOL_ERROR;
1253 if ( tag == LBER_DEFAULT ) {
1254 mode |= SLAP_CH_CONTINUATION_DEFAULT;
1257 switch ( behavior ) {
1258 case LDAP_CHAINING_PREFERRED:
1259 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1262 case LDAP_CHAINING_REQUIRED:
1263 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1266 case LDAP_REFERRALS_PREFERRED:
1267 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1270 case LDAP_REFERRALS_REQUIRED:
1271 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1275 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1276 return LDAP_PROTOCOL_ERROR;
1280 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1281 rs->sr_text = "Chaining behavior control: decoding error";
1282 return LDAP_PROTOCOL_ERROR;
1285 (void) ber_free( ber, 1 );
1288 op->o_chaining = mode | ( ctrl->ldctl_iscritical
1289 ? SLAP_CONTROL_CRITICAL
1290 : SLAP_CONTROL_NONCRITICAL );
1292 return LDAP_SUCCESS;
1294 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1296 static slap_overinst ldapchain;
1303 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1304 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1305 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1306 ldap_chain_parse_ctrl, &sc_chainingBehavior );
1307 if ( rc != LDAP_SUCCESS ) {
1308 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
1311 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1313 ldapchain.on_bi.bi_type = "chain";
1314 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1315 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1316 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1317 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1319 /* ... otherwise the underlying backend's function would be called,
1320 * likely passing an invalid entry; on the contrary, the requested
1321 * operational attributes should have been returned while chasing
1322 * the referrals. This all in all is a bit messy, because part
1323 * of the operational attributes are generated by the backend;
1324 * part by the frontend; back-ldap should receive all the available
1325 * ones from the remote server, but then, on its own, it strips those
1326 * it assumes will be (re)generated by the frontend (e.g.
1327 * subschemaSubentry.) */
1328 ldapchain.on_bi.bi_operational = ldap_chain_operational;
1330 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1332 ldapchain.on_response = ldap_chain_response;
1334 ldapchain.on_bi.bi_cf_ocs = chainocs;
1336 rc = config_register_schema( chaincfg, chainocs );
1341 return overlay_register( &ldapchain );