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;
444 /* slap_mods2entry () should be called in do_add() */
445 assert( op->ora_e->e_attrs != NULL );
446 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
448 case LDAP_REQ_DELETE:
449 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
451 case LDAP_REQ_MODRDN:
452 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
454 case LDAP_REQ_MODIFY:
455 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
457 case LDAP_REQ_COMPARE:
458 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
460 case LDAP_REQ_SEARCH:
461 if ( rs->sr_type == REP_SEARCHREF ) {
462 struct berval *curr = ref,
464 ondn = op->o_req_ndn;
466 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
467 LDAPControl **ctrls = NULL;
469 (void)chaining_control_add( lc, op, &ctrls );
470 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
472 rs->sr_type = REP_SEARCH;
474 sc2.sc_response = ldap_chain_cb_search_response;
478 op->o_bd->be_private = &li;
480 /* if we parse the URI then by no means
481 * we can cache stuff or reuse connections,
482 * because in back-ldap there's no caching
483 * based on the URI value, which is supposed
484 * to be set once for all (correct?) */
485 op->o_do_not_cache = 1;
487 /* copy the private info because we need to modify it */
488 for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
492 /* parse reference and use
493 * proto://[host][:port]/ only */
494 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
495 if ( rc != LDAP_URL_SUCCESS ) {
497 rs->sr_err = LDAP_OTHER;
501 /* remove DN essentially because later on
502 * ldap_initialize() will parse the URL
503 * as a comma-separated URL list */
504 save_dn = srv->lud_dn;
506 srv->lud_scope = LDAP_SCOPE_DEFAULT;
507 li.url = ldap_url_desc2str( srv );
508 if ( li.url != NULL ) {
509 ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
511 ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
515 srv->lud_dn = save_dn;
516 ldap_free_urldesc( srv );
518 if ( li.url == NULL ) {
520 rs->sr_err = LDAP_OTHER;
525 /* FIXME: should we also copy filter and scope?
526 * according to RFC3296, no */
527 rc = lback->bi_op_search( op, rs );
529 ldap_memfree( li.url );
532 op->o_tmpfree( op->o_req_dn.bv_val,
534 op->o_tmpfree( op->o_req_ndn.bv_val,
537 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
544 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
545 (void)chaining_control_remove( op, &ctrls );
546 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
549 op->o_req_ndn = ondn;
550 rs->sr_type = REP_SEARCHREF;
553 if ( rc != LDAP_SUCCESS ) {
554 /* couldn't chase any of the referrals */
555 rc = SLAP_CB_CONTINUE;
559 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
562 case LDAP_REQ_EXTENDED:
563 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
564 /* FIXME: ldap_back_extended() by design
565 * doesn't send result; frontend is expected
567 if ( rc != SLAPD_ABANDON ) {
568 send_ldap_extended( op, rs );
573 rc = SLAP_CB_CONTINUE;
577 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
578 if ( rc != LDAP_SUCCESS || sc2.sc_private == LDAP_CH_ERR ) {
579 if ( rs->sr_err == LDAP_CANNOT_CHAIN ) {
583 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
584 case LDAP_CHAINING_REQUIRED:
586 op->o_callback = NULL;
587 send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" );
591 rc = SLAP_CB_CONTINUE;
593 rs->sr_type = sr_type;
598 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
600 if ( sc2.sc_private == LDAP_CH_NONE ) {
601 op->o_callback = NULL;
602 rc = rs->sr_err = slap_map_api2result( rs );
603 send_ldap_result( op, rs );
606 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
608 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
609 op->o_do_not_cache = cache;
610 op->o_bd->be_private = private;
618 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
620 ldap_chain_parse_ctrl(
626 str2chain( const char *s )
628 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
629 return LDAP_CHAINING_PREFERRED;
631 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
632 return LDAP_CHAINING_REQUIRED;
634 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
635 return LDAP_REFERRALS_PREFERRED;
637 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
638 return LDAP_REFERRALS_REQUIRED;
643 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
649 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
650 static ConfigDriver chain_cf_gen;
651 static ConfigCfAdd chain_cfadd;
653 static ConfigLDAPadd chain_ldadd;
655 static ConfigTable chaincfg[] = {
656 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
657 { "chain-chaining", "args",
658 2, 4, 0, ARG_MAGIC|ARG_BERVAL|PC_CHAINING, chain_cf_gen,
659 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
660 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
661 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
662 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
663 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
666 static ConfigOCs chainocs[] = {
667 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
669 "NAME 'olcChainConfig' "
670 "DESC 'Chain configuration' "
671 "SUP olcOverlayConfig "
672 "MAY olcChainingBehavior )", Cft_Overlay, chaincfg, NULL, chain_cfadd },
675 "NAME 'olcChainDatabase' "
676 "DESC 'Chain remote server configuration' "
677 "AUXILIARY )", Cft_Misc, chaincfg, chain_ldadd },
682 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
684 if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
685 p->ce_bi->bi_cf_ocs != chainocs )
686 return LDAP_CONSTRAINT_VIOLATION;
691 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
694 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
696 CfEntryInfo *pe = p->e_private;
697 slap_overinst *on = (slap_overinst *)pe->ce_bi;
698 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
699 void *priv = (void *)ca->be->be_private;
702 /* FIXME: should not hardcode "olcDatabase" here */
703 bv.bv_len = sprintf( ca->msg, "olcDatabase=%s", lback->bi_type );
706 /* We can only create this entry if the database is table-driven */
707 if ( lback->bi_cf_ocs ) {
708 ca->be->be_private = (void *)lc->lc_li;
709 config_build_entry( op, rs, pe, ca, &bv, lback->bi_cf_ocs, &chainocs[1] );
710 ca->be->be_private = priv;
716 static slap_verbmasks chaining_mode[] = {
717 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
718 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
719 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
720 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
725 chain_cf_gen( ConfigArgs *c )
727 slap_overinst *on = (slap_overinst *)c->bi;
728 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
729 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
734 if ( c->op == SLAP_CONFIG_EMIT ) {
736 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
738 struct berval resolve = BER_BVNULL,
739 continuation = BER_BVNULL;
741 if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
745 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
746 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
748 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
750 + STRLENOF( "continuation=" ) + continuation.bv_len;
751 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
752 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
753 "resolve=%s continuation=%s",
754 resolve.bv_val, continuation.bv_val );
756 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
757 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
758 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
759 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
760 " critical", STRLENOF( " critical" ) + 1 );
761 c->value_bv.bv_len += STRLENOF( " critical" );
766 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
774 } else if ( c->op == LDAP_MOD_DELETE ) {
775 return 1; /* FIXME */
786 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
788 char **argv = c->argv;
790 BerElementBuffer berbuf;
791 BerElement *ber = (BerElement *)&berbuf;
795 Operation op = { 0 };
796 SlapReply rs = { 0 };
798 lc->lc_chaining_ctrlflag = 0;
800 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
801 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
802 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
803 if ( resolve == -1 ) {
804 fprintf( stderr, "%s line %d: "
805 "illegal <resolve> value %s "
806 "in \"chain-chaining>\"\n",
807 c->fname, c->lineno, argv[ 0 ] );
811 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
812 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
813 if ( continuation == -1 ) {
814 fprintf( stderr, "%s line %d: "
815 "illegal <continuation> value %s "
816 "in \"chain-chaining\"\n",
817 c->fname, c->lineno, argv[ 0 ] );
821 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
825 fprintf( stderr, "%s line %d: "
826 "unknown option in \"chain-chaining\"\n",
827 c->fname, c->lineno );
832 if ( resolve != -1 || continuation != -1 ) {
835 if ( resolve == -1 ) {
837 resolve = SLAP_CHAINING_DEFAULT;
840 ber_init2( ber, NULL, LBER_USE_DER );
842 err = ber_printf( ber, "{e" /* } */, resolve );
845 fprintf( stderr, "%s line %d: "
846 "chaining behavior control encoding error!\n",
847 c->fname, c->lineno );
851 if ( continuation > -1 ) {
852 err = ber_printf( ber, "e", continuation );
855 fprintf( stderr, "%s line %d: "
856 "chaining behavior control encoding error!\n",
857 c->fname, c->lineno );
862 err = ber_printf( ber, /* { */ "N}" );
865 fprintf( stderr, "%s line %d: "
866 "chaining behavior control encoding error!\n",
867 c->fname, c->lineno );
871 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
872 exit( EXIT_FAILURE );
876 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
879 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
880 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
882 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
884 fprintf( stderr, "%s line %d: "
885 "unable to parse chaining control%s%s\n",
887 rs.sr_text ? ": " : "",
888 rs.sr_text ? rs.sr_text : "" );
892 lc->lc_chaining_ctrlflag = op.o_chaining;
894 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
900 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
909 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
912 ldap_chain_db_config(
919 slap_overinst *on = (slap_overinst *) be->bd_info;
920 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
925 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
927 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
929 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
930 if ( strcasecmp( argv[ 0 ], "chaining" ) == 0 ) {
931 char **tmpargv = argv;
932 BerElementBuffer berbuf;
933 BerElement *ber = (BerElement *)&berbuf;
937 Operation op = { 0 };
938 SlapReply rs = { 0 };
940 lc->lc_chaining_ctrlflag = 0;
942 for ( argc--, tmpargv++; argc > 0; argc--, tmpargv++ ) {
943 if ( strncasecmp( tmpargv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
944 resolve = str2chain( tmpargv[ 0 ] + STRLENOF( "resolve=" ) );
945 if ( resolve == -1 ) {
946 fprintf( stderr, "%s line %d: "
947 "illegal <resolve> value %s "
948 "in \"chain-chaining>\"\n",
949 fname, lineno, tmpargv[ 0 ] );
953 } else if ( strncasecmp( tmpargv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
954 continuation = str2chain( tmpargv[ 0 ] + STRLENOF( "continuation=" ) );
955 if ( continuation == -1 ) {
956 fprintf( stderr, "%s line %d: "
957 "illegal <continuation> value %s "
958 "in \"chain-chaining\"\n",
959 fname, lineno, tmpargv[ 0 ] );
963 } else if ( strcasecmp( tmpargv[ 0 ], "critical" ) == 0 ) {
967 fprintf( stderr, "%s line %d: "
968 "unknown option in \"chain-chaining\"\n",
974 if ( resolve != -1 || continuation != -1 ) {
977 if ( resolve == -1 ) {
979 resolve = SLAP_CHAINING_DEFAULT;
982 ber_init2( ber, NULL, LBER_USE_DER );
984 err = ber_printf( ber, "{e" /* } */, resolve );
987 fprintf( stderr, "%s line %d: "
988 "chaining behavior control encoding error!\n",
993 if ( continuation > -1 ) {
994 err = ber_printf( ber, "e", continuation );
997 fprintf( stderr, "%s line %d: "
998 "chaining behavior control encoding error!\n",
1004 err = ber_printf( ber, /* { */ "N}" );
1007 fprintf( stderr, "%s line %d: "
1008 "chaining behavior control encoding error!\n",
1013 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1014 exit( EXIT_FAILURE );
1018 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1021 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1022 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1024 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1026 fprintf( stderr, "%s line %d: "
1027 "unable to parse chaining control%s%s\n",
1029 rs.sr_text ? ": " : "",
1030 rs.sr_text ? rs.sr_text : "" );
1034 lc->lc_chaining_ctrlflag = op.o_chaining;
1036 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1041 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1044 db.be_cf_ocs = lback->bi_cf_ocs;
1045 db.be_private = lc->lc_li;
1046 rc = lback->bi_db_config( &db, fname, lineno, argc, argv );
1060 slap_overinst *on = (slap_overinst *)be->bd_info;
1061 ldap_chain_t *lc = NULL;
1065 if ( lback == NULL ) {
1066 lback = backend_info( "ldap" );
1068 if ( lback == NULL ) {
1073 lc = ch_malloc( sizeof( ldap_chain_t ) );
1074 memset( lc, 0, sizeof( ldap_chain_t ) );
1076 bd.be_private = NULL;
1077 rc = lback->bi_db_init( &bd );
1078 lc->lc_li = (struct ldapinfo *)bd.be_private;
1079 on->on_bi.bi_private = (void *)lc;
1091 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1092 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1093 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1099 ldap_chain_db_destroy(
1103 slap_overinst *on = (slap_overinst *) be->bd_info;
1104 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1105 void *private = be->be_private;
1108 be->be_private = (void *)lc->lc_li;
1109 rc = lback->bi_db_destroy( be );
1110 lc->lc_li = be->be_private;
1112 on->on_bi.bi_private = NULL;
1113 be->be_private = private;
1118 ldap_chain_connection_destroy(
1123 slap_overinst *on = (slap_overinst *) be->bd_info;
1124 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1125 void *private = be->be_private;
1128 be->be_private = (void *)lc->lc_li;
1129 rc = lback->bi_connection_destroy( be, conn );
1130 be->be_private = private;
1135 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1137 ldap_chain_parse_ctrl(
1147 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1148 rs->sr_text = "Chaining behavior control specified multiple times";
1149 return LDAP_PROTOCOL_ERROR;
1152 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1153 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1154 return LDAP_PROTOCOL_ERROR;
1157 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1158 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1163 /* Parse the control value
1164 * ChainingBehavior ::= SEQUENCE {
1165 * resolveBehavior Behavior OPTIONAL,
1166 * continuationBehavior Behavior OPTIONAL }
1168 * Behavior :: = ENUMERATED {
1169 * chainingPreferred (0),
1170 * chainingRequired (1),
1171 * referralsPreferred (2),
1172 * referralsRequired (3) }
1175 ber = ber_init( &ctrl->ldctl_value );
1177 rs->sr_text = "internal error";
1181 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1182 /* FIXME: since the whole SEQUENCE is optional,
1183 * should we accept no enumerations at all? */
1184 if ( tag != LBER_ENUMERATED ) {
1185 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1186 return LDAP_PROTOCOL_ERROR;
1189 switch ( behavior ) {
1190 case LDAP_CHAINING_PREFERRED:
1191 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1194 case LDAP_CHAINING_REQUIRED:
1195 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1198 case LDAP_REFERRALS_PREFERRED:
1199 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1202 case LDAP_REFERRALS_REQUIRED:
1203 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1207 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1208 return LDAP_PROTOCOL_ERROR;
1211 tag = ber_peek_tag( ber, &len );
1212 if ( tag == LBER_ENUMERATED ) {
1213 tag = ber_scanf( ber, "e", &behavior );
1214 if ( tag == LBER_ERROR ) {
1215 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1216 return LDAP_PROTOCOL_ERROR;
1220 if ( tag == LBER_DEFAULT ) {
1221 mode |= SLAP_CH_CONTINUATION_DEFAULT;
1224 switch ( behavior ) {
1225 case LDAP_CHAINING_PREFERRED:
1226 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1229 case LDAP_CHAINING_REQUIRED:
1230 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1233 case LDAP_REFERRALS_PREFERRED:
1234 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1237 case LDAP_REFERRALS_REQUIRED:
1238 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1242 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1243 return LDAP_PROTOCOL_ERROR;
1247 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1248 rs->sr_text = "Chaining behavior control: decoding error";
1249 return LDAP_PROTOCOL_ERROR;
1252 (void) ber_free( ber, 1 );
1255 op->o_chaining = mode | ( ctrl->ldctl_iscritical
1256 ? SLAP_CONTROL_CRITICAL
1257 : SLAP_CONTROL_NONCRITICAL );
1259 return LDAP_SUCCESS;
1261 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1263 static slap_overinst ldapchain;
1270 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1271 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1272 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1273 ldap_chain_parse_ctrl, &sc_chainingBehavior );
1274 if ( rc != LDAP_SUCCESS ) {
1275 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
1278 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1280 ldapchain.on_bi.bi_type = "chain";
1281 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1282 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1283 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1284 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1286 /* ... otherwise the underlying backend's function would be called,
1287 * likely passing an invalid entry; on the contrary, the requested
1288 * operational attributes should have been returned while chasing
1289 * the referrals. This all in all is a bit messy, because part
1290 * of the operational attributes are generated by the backend;
1291 * part by the frontend; back-ldap should receive all the available
1292 * ones from the remote server, but then, on its own, it strips those
1293 * it assumes will be (re)generated by the frontend (e.g.
1294 * subschemaSubentry.) */
1295 ldapchain.on_bi.bi_operational = ldap_chain_operational;
1297 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1299 ldapchain.on_response = ldap_chain_response;
1301 ldapchain.on_bi.bi_cf_ocs = chainocs;
1303 rc = config_register_schema( chaincfg, chainocs );
1308 return overlay_register( &ldapchain );