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 static slap_verbmasks chaining_mode[] = {
744 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
745 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
746 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
747 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
752 chain_cf_gen( ConfigArgs *c )
754 slap_overinst *on = (slap_overinst *)c->bi;
755 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
759 if ( c->op == SLAP_CONFIG_EMIT ) {
761 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
763 struct berval resolve = BER_BVNULL,
764 continuation = BER_BVNULL;
766 if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
770 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
771 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
773 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
775 + STRLENOF( "continuation=" ) + continuation.bv_len;
776 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
777 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
778 "resolve=%s continuation=%s",
779 resolve.bv_val, continuation.bv_val );
781 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
782 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
783 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
784 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
785 " critical", STRLENOF( " critical" ) + 1 );
786 c->value_bv.bv_len += STRLENOF( " critical" );
791 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
799 } else if ( c->op == LDAP_MOD_DELETE ) {
800 return 1; /* FIXME */
811 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
813 char **argv = c->argv;
815 BerElementBuffer berbuf;
816 BerElement *ber = (BerElement *)&berbuf;
820 Operation op = { 0 };
821 SlapReply rs = { 0 };
823 lc->lc_chaining_ctrlflag = 0;
825 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
826 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
827 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
828 if ( resolve == -1 ) {
829 fprintf( stderr, "%s line %d: "
830 "illegal <resolve> value %s "
831 "in \"chain-chaining>\"\n",
832 c->fname, c->lineno, argv[ 0 ] );
836 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
837 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
838 if ( continuation == -1 ) {
839 fprintf( stderr, "%s line %d: "
840 "illegal <continuation> value %s "
841 "in \"chain-chaining\"\n",
842 c->fname, c->lineno, argv[ 0 ] );
846 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
850 fprintf( stderr, "%s line %d: "
851 "unknown option in \"chain-chaining\"\n",
852 c->fname, c->lineno );
857 if ( resolve != -1 || continuation != -1 ) {
860 if ( resolve == -1 ) {
862 resolve = SLAP_CHAINING_DEFAULT;
865 ber_init2( ber, NULL, LBER_USE_DER );
867 err = ber_printf( ber, "{e" /* } */, resolve );
870 fprintf( stderr, "%s line %d: "
871 "chaining behavior control encoding error!\n",
872 c->fname, c->lineno );
876 if ( continuation > -1 ) {
877 err = ber_printf( ber, "e", continuation );
880 fprintf( stderr, "%s line %d: "
881 "chaining behavior control encoding error!\n",
882 c->fname, c->lineno );
887 err = ber_printf( ber, /* { */ "N}" );
890 fprintf( stderr, "%s line %d: "
891 "chaining behavior control encoding error!\n",
892 c->fname, c->lineno );
896 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
897 exit( EXIT_FAILURE );
901 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
904 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
905 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
907 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
909 fprintf( stderr, "%s line %d: "
910 "unable to parse chaining control%s%s\n",
912 rs.sr_text ? ": " : "",
913 rs.sr_text ? rs.sr_text : "" );
917 lc->lc_chaining_ctrlflag = op.o_chaining;
919 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
925 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
935 ldap_chain_db_config(
942 slap_overinst *on = (slap_overinst *) be->bd_info;
943 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
948 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
950 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
952 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
953 if ( strcasecmp( argv[ 0 ], "chaining" ) == 0 ) {
954 char **tmpargv = argv;
955 BerElementBuffer berbuf;
956 BerElement *ber = (BerElement *)&berbuf;
960 Operation op = { 0 };
961 SlapReply rs = { 0 };
963 lc->lc_chaining_ctrlflag = 0;
965 for ( argc--, tmpargv++; argc > 0; argc--, tmpargv++ ) {
966 if ( strncasecmp( tmpargv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
967 resolve = str2chain( tmpargv[ 0 ] + STRLENOF( "resolve=" ) );
968 if ( resolve == -1 ) {
969 fprintf( stderr, "%s line %d: "
970 "illegal <resolve> value %s "
971 "in \"chain-chaining>\"\n",
972 fname, lineno, tmpargv[ 0 ] );
976 } else if ( strncasecmp( tmpargv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
977 continuation = str2chain( tmpargv[ 0 ] + STRLENOF( "continuation=" ) );
978 if ( continuation == -1 ) {
979 fprintf( stderr, "%s line %d: "
980 "illegal <continuation> value %s "
981 "in \"chain-chaining\"\n",
982 fname, lineno, tmpargv[ 0 ] );
986 } else if ( strcasecmp( tmpargv[ 0 ], "critical" ) == 0 ) {
990 fprintf( stderr, "%s line %d: "
991 "unknown option in \"chain-chaining\"\n",
997 if ( resolve != -1 || continuation != -1 ) {
1000 if ( resolve == -1 ) {
1002 resolve = SLAP_CHAINING_DEFAULT;
1005 ber_init2( ber, NULL, LBER_USE_DER );
1007 err = ber_printf( ber, "{e" /* } */, resolve );
1010 fprintf( stderr, "%s line %d: "
1011 "chaining behavior control encoding error!\n",
1016 if ( continuation > -1 ) {
1017 err = ber_printf( ber, "e", continuation );
1020 fprintf( stderr, "%s line %d: "
1021 "chaining behavior control encoding error!\n",
1027 err = ber_printf( ber, /* { */ "N}" );
1030 fprintf( stderr, "%s line %d: "
1031 "chaining behavior control encoding error!\n",
1036 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1037 exit( EXIT_FAILURE );
1041 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1044 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1045 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1047 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1049 fprintf( stderr, "%s line %d: "
1050 "unable to parse chaining control%s%s\n",
1052 rs.sr_text ? ": " : "",
1053 rs.sr_text ? rs.sr_text : "" );
1057 lc->lc_chaining_ctrlflag = op.o_chaining;
1059 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1064 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1067 db.be_cf_ocs = lback->bi_cf_ocs;
1068 db.be_private = lc->lc_li;
1069 rc = lback->bi_db_config( &db, fname, lineno, argc, argv );
1083 slap_overinst *on = (slap_overinst *)be->bd_info;
1084 ldap_chain_t *lc = NULL;
1088 if ( lback == NULL ) {
1089 lback = backend_info( "ldap" );
1091 if ( lback == NULL ) {
1096 lc = ch_malloc( sizeof( ldap_chain_t ) );
1097 memset( lc, 0, sizeof( ldap_chain_t ) );
1099 bd.be_private = NULL;
1100 rc = lback->bi_db_init( &bd );
1101 lc->lc_li = (struct ldapinfo *)bd.be_private;
1102 on->on_bi.bi_private = (void *)lc;
1114 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1115 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1116 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1122 ldap_chain_db_destroy(
1126 slap_overinst *on = (slap_overinst *) be->bd_info;
1127 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1128 void *private = be->be_private;
1131 be->be_private = (void *)lc->lc_li;
1132 rc = lback->bi_db_destroy( be );
1133 lc->lc_li = be->be_private;
1135 on->on_bi.bi_private = NULL;
1136 be->be_private = private;
1141 ldap_chain_connection_destroy(
1146 slap_overinst *on = (slap_overinst *) be->bd_info;
1147 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1148 void *private = be->be_private;
1151 be->be_private = (void *)lc->lc_li;
1152 rc = lback->bi_connection_destroy( be, conn );
1153 be->be_private = private;
1158 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1160 ldap_chain_parse_ctrl(
1170 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1171 rs->sr_text = "Chaining behavior control specified multiple times";
1172 return LDAP_PROTOCOL_ERROR;
1175 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1176 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1177 return LDAP_PROTOCOL_ERROR;
1180 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1181 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1186 /* Parse the control value
1187 * ChainingBehavior ::= SEQUENCE {
1188 * resolveBehavior Behavior OPTIONAL,
1189 * continuationBehavior Behavior OPTIONAL }
1191 * Behavior :: = ENUMERATED {
1192 * chainingPreferred (0),
1193 * chainingRequired (1),
1194 * referralsPreferred (2),
1195 * referralsRequired (3) }
1198 ber = ber_init( &ctrl->ldctl_value );
1200 rs->sr_text = "internal error";
1204 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1205 /* FIXME: since the whole SEQUENCE is optional,
1206 * should we accept no enumerations at all? */
1207 if ( tag != LBER_ENUMERATED ) {
1208 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1209 return LDAP_PROTOCOL_ERROR;
1212 switch ( behavior ) {
1213 case LDAP_CHAINING_PREFERRED:
1214 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1217 case LDAP_CHAINING_REQUIRED:
1218 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1221 case LDAP_REFERRALS_PREFERRED:
1222 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1225 case LDAP_REFERRALS_REQUIRED:
1226 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1230 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1231 return LDAP_PROTOCOL_ERROR;
1234 tag = ber_peek_tag( ber, &len );
1235 if ( tag == LBER_ENUMERATED ) {
1236 tag = ber_scanf( ber, "e", &behavior );
1237 if ( tag == LBER_ERROR ) {
1238 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1239 return LDAP_PROTOCOL_ERROR;
1243 if ( tag == LBER_DEFAULT ) {
1244 mode |= SLAP_CH_CONTINUATION_DEFAULT;
1247 switch ( behavior ) {
1248 case LDAP_CHAINING_PREFERRED:
1249 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1252 case LDAP_CHAINING_REQUIRED:
1253 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1256 case LDAP_REFERRALS_PREFERRED:
1257 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1260 case LDAP_REFERRALS_REQUIRED:
1261 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1265 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1266 return LDAP_PROTOCOL_ERROR;
1270 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1271 rs->sr_text = "Chaining behavior control: decoding error";
1272 return LDAP_PROTOCOL_ERROR;
1275 (void) ber_free( ber, 1 );
1278 op->o_chaining = mode | ( ctrl->ldctl_iscritical
1279 ? SLAP_CONTROL_CRITICAL
1280 : SLAP_CONTROL_NONCRITICAL );
1282 return LDAP_SUCCESS;
1284 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1286 static slap_overinst ldapchain;
1291 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1294 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1295 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1296 ldap_chain_parse_ctrl, &sc_chainingBehavior );
1297 if ( rc != LDAP_SUCCESS ) {
1298 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
1301 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1303 ldapchain.on_bi.bi_type = "chain";
1304 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1305 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1306 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1307 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1309 /* ... otherwise the underlying backend's function would be called,
1310 * likely passing an invalid entry; on the contrary, the requested
1311 * operational attributes should have been returned while chasing
1312 * the referrals. This all in all is a bit messy, because part
1313 * of the operational attributes are generated by the backend;
1314 * part by the frontend; back-ldap should receive all the available
1315 * ones from the remote server, but then, on its own, it strips those
1316 * it assumes will be (re)generated by the frontend (e.g.
1317 * subschemaSubentry.) */
1318 ldapchain.on_bi.bi_operational = ldap_chain_operational;
1320 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1322 ldapchain.on_response = ldap_chain_response;
1324 ldapchain.on_bi.bi_cf_ocs = chainocs;
1326 rc = config_register_schema( chaincfg, chainocs );
1331 return overlay_register( &ldapchain );