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 ldap_pvt_thread_mutex_t lc_mutex;
74 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
75 LDAPControl lc_chaining_ctrl;
76 char lc_chaining_ctrlflag;
77 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
80 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
85 LDAPControl ***oldctrlsp )
87 LDAPControl **ctrls = NULL;
90 *oldctrlsp = op->o_ctrls;
92 /* default chaining control not defined */
93 if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
98 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
102 /* FIXME: check other incompatibilities */
104 /* add to other controls */
106 for ( c = 0; op->o_ctrls[ c ]; c++ )
110 ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
111 ctrls[ 0 ] = &lc->lc_chaining_ctrl;
113 for ( c = 0; op->o_ctrls[ c ]; c++ ) {
114 ctrls[ c + 1 ] = op->o_ctrls[ c ];
117 ctrls[ c + 1 ] = NULL;
121 op->o_chaining = lc->lc_chaining_ctrlflag;
127 chaining_control_remove(
129 LDAPControl ***oldctrlsp )
131 LDAPControl **oldctrls = *oldctrlsp;
133 /* we assume that the first control is the chaining control
134 * added by the chain overlay, so it's the only one we explicitly
136 if ( op->o_ctrls != oldctrls ) {
137 assert( op->o_ctrls != NULL );
138 assert( op->o_ctrls[ 0 ] != NULL );
143 op->o_ctrls = oldctrls;
150 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
153 ldap_chain_operational( Operation *op, SlapReply *rs )
155 /* Trap entries generated by back-ldap.
157 * FIXME: we need a better way to recognize them; a cleaner
158 * solution would be to be able to intercept the response
159 * of be_operational(), so that we can divert only those
160 * calls that fail because operational attributes were
161 * requested for entries that do not belong to the underlying
162 * database. This fix is likely to intercept also entries
163 * generated by back-perl and so. */
164 if ( rs->sr_entry->e_private == NULL ) {
168 return SLAP_CB_CONTINUE;
172 * Search specific response that strips entryDN from entries
175 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
177 assert( op->o_tag == LDAP_REQ_SEARCH );
179 /* if in error, don't proceed any further */
180 if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
184 if ( rs->sr_type == REP_SEARCH ) {
185 Attribute **ap = &rs->sr_entry->e_attrs;
187 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
188 /* will be generated later by frontend
189 * (a cleaner solution would be that
190 * the frontend checks if it already exists */
191 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
198 /* there SHOULD be one only! */
203 return SLAP_CB_CONTINUE;
205 } else if ( rs->sr_type == REP_SEARCHREF ) {
206 /* if we get it here, it means the library was unable
207 * to chase the referral... */
209 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
210 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
211 switch ( get_continuationBehavior( op ) ) {
212 case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
213 op->o_callback->sc_private = LDAP_CH_ERR;
220 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
221 return SLAP_CB_CONTINUE;
223 } else if ( rs->sr_type == REP_RESULT ) {
224 /* back-ldap tried to send result */
225 op->o_callback->sc_private = LDAP_CH_RES;
232 * Dummy response that simply traces if back-ldap tried to send
233 * anything to the client
236 ldap_chain_cb_response( Operation *op, SlapReply *rs )
238 /* if in error, don't proceed any further */
239 if ( op->o_callback->sc_private == LDAP_CH_ERR ) {
243 if ( rs->sr_type == REP_RESULT ) {
244 op->o_callback->sc_private = LDAP_CH_RES;
246 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
248 /* strip the entryDN attribute, but keep returning results */
249 (void)ldap_chain_cb_search_response( op, rs );
252 return SLAP_CB_CONTINUE;
259 int ( *op_f )( Operation *op, SlapReply *rs ),
262 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
263 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
265 struct ldapinfo *lip = lc->lc_li;
266 char *save_url = NULL;
267 SlapReply rs2 = { 0 };
269 /* NOTE: returned if ref is empty... */
272 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
273 LDAPControl **ctrls = NULL;
275 (void)chaining_control_add( lc, op, &ctrls );
276 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
278 if ( lip->url != NULL ) {
279 op->o_bd->be_private = lip;
280 rc = ( *op_f )( op, &rs2 );
281 rs->sr_err = rs2.sr_err;
287 op->o_bd->be_private = lip;
289 /* if we parse the URI then by no means
290 * we can cache stuff or reuse connections,
291 * because in back-ldap there's no caching
292 * based on the URI value, which is supposed
293 * to be set once for all (correct?) */
294 op->o_do_not_cache = 1;
296 for ( ; !BER_BVISNULL( ref ); ref++ ) {
300 /* We're setting the URI of the first referral;
301 * what if there are more?
303 Document: draft-ietf-ldapbis-protocol-27.txt
307 If the client wishes to progress the operation, it MUST follow the
308 referral by contacting one of the supported services. If multiple
309 URIs are present, the client assumes that any supported URI may be
310 used to progress the operation.
312 * so we actually need to follow exactly one,
313 * and we can assume any is fine.
316 /* parse reference and use
317 * proto://[host][:port]/ only */
318 rc = ldap_url_parse_ext( ref->bv_val, &srv );
319 if ( rc != LDAP_URL_SUCCESS ) {
325 /* remove DN essentially because later on
326 * ldap_initialize() will parse the URL
327 * as a comma-separated URL list */
328 save_dn = srv->lud_dn;
330 srv->lud_scope = LDAP_SCOPE_DEFAULT;
331 lip->url = ldap_url_desc2str( srv );
332 srv->lud_dn = save_dn;
333 ldap_free_urldesc( srv );
335 if ( lip->url == NULL ) {
341 rc = ( *op_f )( op, &rs2 );
342 rs->sr_err = rs2.sr_err;
344 ldap_memfree( lip->url );
347 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
355 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
356 (void)chaining_control_remove( op, &ctrls );
357 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
363 ldap_chain_response( Operation *op, SlapReply *rs )
365 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
366 void *private = op->o_bd->be_private;
367 slap_callback *sc = op->o_callback,
370 int cache = op->o_do_not_cache;
372 struct berval ndn = op->o_ndn;
374 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
375 struct ldapinfo *lip = lc->lc_li;
377 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
378 int sr_err = rs->sr_err;
379 slap_reply_t sr_type = rs->sr_type;
380 slap_mask_t chain_mask = 0;
381 ber_len_t chain_shift = 0;
382 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
384 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
385 return SLAP_CB_CONTINUE;
388 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
389 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
390 switch ( get_resolveBehavior( op ) ) {
391 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
392 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
393 return SLAP_CB_CONTINUE;
396 chain_mask = SLAP_CH_RESOLVE_MASK;
397 chain_shift = SLAP_CH_RESOLVE_SHIFT;
401 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
402 switch ( get_continuationBehavior( op ) ) {
403 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
404 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
405 return SLAP_CB_CONTINUE;
408 chain_mask = SLAP_CH_CONTINUATION_MASK;
409 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
413 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
415 ldap_pvt_thread_mutex_lock( &lc->lc_mutex );
418 * TODO: add checks on who/when chain operations; e.g.:
419 * a) what identities are authorized
420 * b) what request DN (e.g. only chain requests rooted at <DN>)
421 * c) what referral URIs
422 * d) what protocol scheme (e.g. only ldaps://)
429 /* we need this to know if back-ldap returned any result */
430 sc2.sc_response = ldap_chain_cb_response;
431 op->o_callback = &sc2;
433 /* Chaining can be performed by a privileged user on behalf
434 * of normal users, using the ProxyAuthz control, by exploiting
435 * the identity assertion feature of back-ldap; see idassert-*
436 * directives in slapd-ldap(5).
438 * FIXME: the idassert-authcDN is one, will it be fine regardless
439 * of the URI we obtain from the referral?
442 switch ( op->o_tag ) {
443 case LDAP_REQ_BIND: {
444 struct berval rndn = op->o_req_ndn;
445 Connection *conn = op->o_conn;
447 /* FIXME: can we really get a referral for binds? */
448 op->o_req_ndn = slap_empty_bv;
450 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
451 op->o_req_ndn = rndn;
456 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
458 case LDAP_REQ_DELETE:
459 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
461 case LDAP_REQ_MODRDN:
462 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
464 case LDAP_REQ_MODIFY:
465 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
467 case LDAP_REQ_COMPARE:
468 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
470 case LDAP_REQ_SEARCH:
471 if ( rs->sr_type == REP_SEARCHREF ) {
472 struct berval *curr = ref,
474 ondn = op->o_req_ndn;
475 char *save_url = NULL;
476 SlapReply rs2 = { 0 };
478 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
479 LDAPControl **ctrls = NULL;
481 (void)chaining_control_add( lc, op, &ctrls );
482 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
484 rs->sr_type = REP_SEARCH;
486 sc2.sc_response = ldap_chain_cb_search_response;
490 op->o_bd->be_private = lip;
492 /* if we parse the URI then by no means
493 * we can cache stuff or reuse connections,
494 * because in back-ldap there's no caching
495 * based on the URI value, which is supposed
496 * to be set once for all (correct?) */
497 op->o_do_not_cache = 1;
499 /* copy the private info because we need to modify it */
500 for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
504 /* parse reference and use
505 * proto://[host][:port]/ only */
506 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
507 if ( rc != LDAP_URL_SUCCESS ) {
509 rs->sr_err = LDAP_OTHER;
513 /* remove DN essentially because later on
514 * ldap_initialize() will parse the URL
515 * as a comma-separated URL list */
516 save_dn = srv->lud_dn;
518 srv->lud_scope = LDAP_SCOPE_DEFAULT;
519 lip->url = ldap_url_desc2str( srv );
520 if ( lip->url != NULL ) {
521 ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
523 ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
527 srv->lud_dn = save_dn;
528 ldap_free_urldesc( srv );
530 if ( lip->url == NULL ) {
532 rs->sr_err = LDAP_OTHER;
537 /* FIXME: should we also copy filter and scope?
538 * according to RFC3296, no */
539 rc = lback->bi_op_search( op, &rs2 );
540 rs->sr_err = rs2.sr_err;
542 ldap_memfree( lip->url );
545 op->o_tmpfree( op->o_req_dn.bv_val,
547 op->o_tmpfree( op->o_req_ndn.bv_val,
550 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
557 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
558 (void)chaining_control_remove( op, &ctrls );
559 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
564 op->o_req_ndn = ondn;
565 rs->sr_type = REP_SEARCHREF;
568 if ( rc != LDAP_SUCCESS ) {
569 /* couldn't chase any of the referrals */
570 rc = SLAP_CB_CONTINUE;
574 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
577 case LDAP_REQ_EXTENDED:
578 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
579 /* FIXME: ldap_back_extended() by design
580 * doesn't send result; frontend is expected
582 if ( rc != SLAPD_ABANDON ) {
583 send_ldap_extended( op, rs );
588 rc = SLAP_CB_CONTINUE;
592 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
593 if ( rc != LDAP_SUCCESS || sc2.sc_private == LDAP_CH_ERR ) {
594 if ( rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
598 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
599 case LDAP_CHAINING_REQUIRED:
601 op->o_callback = NULL;
602 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
603 "operation cannot be completed without chaining" );
607 rc = SLAP_CB_CONTINUE;
609 rs->sr_type = sr_type;
614 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
616 if ( sc2.sc_private == LDAP_CH_NONE ) {
617 op->o_callback = NULL;
618 rc = rs->sr_err = slap_map_api2result( rs );
619 send_ldap_result( op, rs );
622 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
624 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
625 op->o_do_not_cache = cache;
626 op->o_bd->be_private = private;
631 ldap_pvt_thread_mutex_unlock( &lc->lc_mutex );
636 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
638 ldap_chain_parse_ctrl(
644 str2chain( const char *s )
646 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
647 return LDAP_CHAINING_PREFERRED;
649 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
650 return LDAP_CHAINING_REQUIRED;
652 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
653 return LDAP_REFERRALS_PREFERRED;
655 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
656 return LDAP_REFERRALS_REQUIRED;
661 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
667 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
668 static ConfigDriver chain_cf_gen;
669 static ConfigCfAdd chain_cfadd;
671 static ConfigLDAPadd chain_ldadd;
673 static ConfigTable chaincfg[] = {
674 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
675 { "chain-chaining", "args",
676 2, 4, 0, ARG_MAGIC|ARG_BERVAL|PC_CHAINING, chain_cf_gen,
677 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
678 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
679 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
680 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
681 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
684 static ConfigOCs chainocs[] = {
685 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
687 "NAME 'olcChainConfig' "
688 "DESC 'Chain configuration' "
689 "SUP olcOverlayConfig "
690 "MAY olcChainingBehavior )", Cft_Overlay, chaincfg, NULL, chain_cfadd },
693 "NAME 'olcChainDatabase' "
694 "DESC 'Chain remote server configuration' "
695 "AUXILIARY )", Cft_Misc, chaincfg, chain_ldadd },
700 chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
702 if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
703 p->ce_bi->bi_cf_ocs != chainocs )
704 return LDAP_CONSTRAINT_VIOLATION;
709 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
712 chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
714 CfEntryInfo *pe = p->e_private;
715 slap_overinst *on = (slap_overinst *)pe->ce_bi;
716 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
717 void *priv = (void *)ca->be->be_private;
720 /* FIXME: should not hardcode "olcDatabase" here */
721 bv.bv_len = sprintf( ca->msg, "olcDatabase=%s", lback->bi_type );
724 /* We can only create this entry if the database is table-driven */
725 if ( lback->bi_cf_ocs ) {
726 ca->be->be_private = (void *)lc->lc_li;
727 config_build_entry( op, rs, pe, ca, &bv, lback->bi_cf_ocs, &chainocs[1] );
728 ca->be->be_private = priv;
734 static slap_verbmasks chaining_mode[] = {
735 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
736 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
737 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
738 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
743 chain_cf_gen( ConfigArgs *c )
745 slap_overinst *on = (slap_overinst *)c->bi;
746 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
747 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
752 if ( c->op == SLAP_CONFIG_EMIT ) {
754 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
756 struct berval resolve = BER_BVNULL,
757 continuation = BER_BVNULL;
759 if ( !( lc->lc_flags & LDAP_CHAIN_F_CHAINING ) ) {
763 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
764 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
766 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
768 + STRLENOF( "continuation=" ) + continuation.bv_len;
769 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
770 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
771 "resolve=%s continuation=%s",
772 resolve.bv_val, continuation.bv_val );
774 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
775 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
776 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
777 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
778 " critical", STRLENOF( " critical" ) + 1 );
779 c->value_bv.bv_len += STRLENOF( " critical" );
784 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
792 } else if ( c->op == LDAP_MOD_DELETE ) {
793 return 1; /* FIXME */
804 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
806 char **argv = c->argv;
808 BerElementBuffer berbuf;
809 BerElement *ber = (BerElement *)&berbuf;
813 Operation op = { 0 };
814 SlapReply rs = { 0 };
816 lc->lc_chaining_ctrlflag = 0;
818 for ( argc--, argv++; argc > 0; argc--, argv++ ) {
819 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
820 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
821 if ( resolve == -1 ) {
822 fprintf( stderr, "%s line %d: "
823 "illegal <resolve> value %s "
824 "in \"chain-chaining>\"\n",
825 c->fname, c->lineno, argv[ 0 ] );
829 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
830 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
831 if ( continuation == -1 ) {
832 fprintf( stderr, "%s line %d: "
833 "illegal <continuation> value %s "
834 "in \"chain-chaining\"\n",
835 c->fname, c->lineno, argv[ 0 ] );
839 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
843 fprintf( stderr, "%s line %d: "
844 "unknown option in \"chain-chaining\"\n",
845 c->fname, c->lineno );
850 if ( resolve != -1 || continuation != -1 ) {
853 if ( resolve == -1 ) {
855 resolve = SLAP_CHAINING_DEFAULT;
858 ber_init2( ber, NULL, LBER_USE_DER );
860 err = ber_printf( ber, "{e" /* } */, resolve );
863 fprintf( stderr, "%s line %d: "
864 "chaining behavior control encoding error!\n",
865 c->fname, c->lineno );
869 if ( continuation > -1 ) {
870 err = ber_printf( ber, "e", continuation );
873 fprintf( stderr, "%s line %d: "
874 "chaining behavior control encoding error!\n",
875 c->fname, c->lineno );
880 err = ber_printf( ber, /* { */ "N}" );
883 fprintf( stderr, "%s line %d: "
884 "chaining behavior control encoding error!\n",
885 c->fname, c->lineno );
889 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
890 exit( EXIT_FAILURE );
894 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
897 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
898 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
900 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
902 fprintf( stderr, "%s line %d: "
903 "unable to parse chaining control%s%s\n",
905 rs.sr_text ? ": " : "",
906 rs.sr_text ? rs.sr_text : "" );
910 lc->lc_chaining_ctrlflag = op.o_chaining;
912 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
918 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
927 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
930 ldap_chain_db_config(
937 slap_overinst *on = (slap_overinst *) be->bd_info;
938 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
943 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
945 argv[ 0 ] = &argv[ 0 ][ STRLENOF( "chain-" ) ];
947 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
948 if ( strcasecmp( argv[ 0 ], "chaining" ) == 0 ) {
949 char **tmpargv = argv;
950 BerElementBuffer berbuf;
951 BerElement *ber = (BerElement *)&berbuf;
955 Operation op = { 0 };
956 SlapReply rs = { 0 };
958 lc->lc_chaining_ctrlflag = 0;
960 for ( argc--, tmpargv++; argc > 0; argc--, tmpargv++ ) {
961 if ( strncasecmp( tmpargv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
962 resolve = str2chain( tmpargv[ 0 ] + STRLENOF( "resolve=" ) );
963 if ( resolve == -1 ) {
964 fprintf( stderr, "%s line %d: "
965 "illegal <resolve> value %s "
966 "in \"chain-chaining>\"\n",
967 fname, lineno, tmpargv[ 0 ] );
971 } else if ( strncasecmp( tmpargv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
972 continuation = str2chain( tmpargv[ 0 ] + STRLENOF( "continuation=" ) );
973 if ( continuation == -1 ) {
974 fprintf( stderr, "%s line %d: "
975 "illegal <continuation> value %s "
976 "in \"chain-chaining\"\n",
977 fname, lineno, tmpargv[ 0 ] );
981 } else if ( strcasecmp( tmpargv[ 0 ], "critical" ) == 0 ) {
985 fprintf( stderr, "%s line %d: "
986 "unknown option in \"chain-chaining\"\n",
992 if ( resolve != -1 || continuation != -1 ) {
995 if ( resolve == -1 ) {
997 resolve = SLAP_CHAINING_DEFAULT;
1000 ber_init2( ber, NULL, LBER_USE_DER );
1002 err = ber_printf( ber, "{e" /* } */, resolve );
1005 fprintf( stderr, "%s line %d: "
1006 "chaining behavior control encoding error!\n",
1011 if ( continuation > -1 ) {
1012 err = ber_printf( ber, "e", continuation );
1015 fprintf( stderr, "%s line %d: "
1016 "chaining behavior control encoding error!\n",
1022 err = ber_printf( ber, /* { */ "N}" );
1025 fprintf( stderr, "%s line %d: "
1026 "chaining behavior control encoding error!\n",
1031 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1032 exit( EXIT_FAILURE );
1036 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1039 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1040 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1042 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1044 fprintf( stderr, "%s line %d: "
1045 "unable to parse chaining control%s%s\n",
1047 rs.sr_text ? ": " : "",
1048 rs.sr_text ? rs.sr_text : "" );
1052 lc->lc_chaining_ctrlflag = op.o_chaining;
1054 lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1059 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1062 db.be_cf_ocs = lback->bi_cf_ocs;
1063 db.be_private = lc->lc_li;
1064 rc = lback->bi_db_config( &db, fname, lineno, argc, argv );
1078 slap_overinst *on = (slap_overinst *)be->bd_info;
1079 ldap_chain_t *lc = NULL;
1083 if ( lback == NULL ) {
1084 lback = backend_info( "ldap" );
1086 if ( lback == NULL ) {
1091 lc = ch_malloc( sizeof( ldap_chain_t ) );
1092 memset( lc, 0, sizeof( ldap_chain_t ) );
1094 ldap_pvt_thread_mutex_init( &lc->lc_mutex );
1096 bd.be_private = NULL;
1097 rc = lback->bi_db_init( &bd );
1098 lc->lc_li = (struct ldapinfo *)bd.be_private;
1099 on->on_bi.bi_private = (void *)lc;
1111 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1112 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1113 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1119 ldap_chain_db_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_db_destroy( be );
1130 ldap_pvt_thread_mutex_destroy( &lc->lc_mutex );
1132 on->on_bi.bi_private = NULL;
1133 be->be_private = private;
1138 ldap_chain_connection_destroy(
1143 slap_overinst *on = (slap_overinst *) be->bd_info;
1144 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
1145 void *private = be->be_private;
1148 be->be_private = (void *)lc->lc_li;
1149 rc = lback->bi_connection_destroy( be, conn );
1150 be->be_private = private;
1155 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1157 ldap_chain_parse_ctrl(
1167 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
1168 rs->sr_text = "Chaining behavior control specified multiple times";
1169 return LDAP_PROTOCOL_ERROR;
1172 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
1173 rs->sr_text = "Chaining behavior control specified with pagedResults control";
1174 return LDAP_PROTOCOL_ERROR;
1177 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1178 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
1183 /* Parse the control value
1184 * ChainingBehavior ::= SEQUENCE {
1185 * resolveBehavior Behavior OPTIONAL,
1186 * continuationBehavior Behavior OPTIONAL }
1188 * Behavior :: = ENUMERATED {
1189 * chainingPreferred (0),
1190 * chainingRequired (1),
1191 * referralsPreferred (2),
1192 * referralsRequired (3) }
1195 ber = ber_init( &ctrl->ldctl_value );
1197 rs->sr_text = "internal error";
1201 tag = ber_scanf( ber, "{e" /* } */, &behavior );
1202 /* FIXME: since the whole SEQUENCE is optional,
1203 * should we accept no enumerations at all? */
1204 if ( tag != LBER_ENUMERATED ) {
1205 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
1206 return LDAP_PROTOCOL_ERROR;
1209 switch ( behavior ) {
1210 case LDAP_CHAINING_PREFERRED:
1211 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
1214 case LDAP_CHAINING_REQUIRED:
1215 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
1218 case LDAP_REFERRALS_PREFERRED:
1219 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
1222 case LDAP_REFERRALS_REQUIRED:
1223 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
1227 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
1228 return LDAP_PROTOCOL_ERROR;
1231 tag = ber_peek_tag( ber, &len );
1232 if ( tag == LBER_ENUMERATED ) {
1233 tag = ber_scanf( ber, "e", &behavior );
1234 if ( tag == LBER_ERROR ) {
1235 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
1236 return LDAP_PROTOCOL_ERROR;
1240 if ( tag == LBER_DEFAULT ) {
1241 mode |= SLAP_CH_CONTINUATION_DEFAULT;
1244 switch ( behavior ) {
1245 case LDAP_CHAINING_PREFERRED:
1246 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
1249 case LDAP_CHAINING_REQUIRED:
1250 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
1253 case LDAP_REFERRALS_PREFERRED:
1254 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
1257 case LDAP_REFERRALS_REQUIRED:
1258 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
1262 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
1263 return LDAP_PROTOCOL_ERROR;
1267 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
1268 rs->sr_text = "Chaining behavior control: decoding error";
1269 return LDAP_PROTOCOL_ERROR;
1272 (void) ber_free( ber, 1 );
1275 op->o_chaining = mode | ( ctrl->ldctl_iscritical
1276 ? SLAP_CONTROL_CRITICAL
1277 : SLAP_CONTROL_NONCRITICAL );
1279 return LDAP_SUCCESS;
1281 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1283 static slap_overinst ldapchain;
1290 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1291 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
1292 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
1293 ldap_chain_parse_ctrl, &sc_chainingBehavior );
1294 if ( rc != LDAP_SUCCESS ) {
1295 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
1298 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1300 ldapchain.on_bi.bi_type = "chain";
1301 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
1302 ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
1303 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
1304 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
1306 /* ... otherwise the underlying backend's function would be called,
1307 * likely passing an invalid entry; on the contrary, the requested
1308 * operational attributes should have been returned while chasing
1309 * the referrals. This all in all is a bit messy, because part
1310 * of the operational attributes are generated by the backend;
1311 * part by the frontend; back-ldap should receive all the available
1312 * ones from the remote server, but then, on its own, it strips those
1313 * it assumes will be (re)generated by the frontend (e.g.
1314 * subschemaSubentry.) */
1315 ldapchain.on_bi.bi_operational = ldap_chain_operational;
1317 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
1319 ldapchain.on_response = ldap_chain_response;
1321 ldapchain.on_bi.bi_cf_ocs = chainocs;
1323 rc = config_register_schema( chaincfg, chainocs );
1328 return overlay_register( &ldapchain );