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"
32 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
33 #define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT
34 #define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT)
35 #define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
36 #define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
37 #define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
38 #define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
39 #define SLAP_CH_RESOLVE_DEFAULT SLAP_CH_RESOLVE_CHAINING_PREFERRED
40 #define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2)
41 #define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT)
42 #define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
43 #define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
44 #define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
45 #define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
46 #define SLAP_CH_CONTINUATION_DEFAULT SLAP_CH_CONTINUATION_CHAINING_PREFERRED
48 #define o_chaining o_ctrlflag[sc_chainingBehavior]
49 #define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK)
50 #define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
51 #define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
52 #define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
53 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
55 static int sc_chainingBehavior;
56 static BackendInfo *lback;
59 ldap_chain_operational( Operation *op, SlapReply *rs )
61 /* Trap entries generated by back-ldap.
63 * FIXME: we need a better way to recognize them; a cleaner
64 * solution would be to be able to intercept the response
65 * of be_operational(), so that we can divert only those
66 * calls that fail because operational attributes were
67 * requested for entries that do not belong to the underlying
68 * database. This fix is likely to intercept also entries
69 * generated by back-perl and so. */
70 if ( rs->sr_entry->e_private == NULL ) {
74 return SLAP_CB_CONTINUE;
78 * Search specific response that strips entryDN from entries
81 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
83 assert( op->o_tag == LDAP_REQ_SEARCH );
85 if ( rs->sr_type == REP_SEARCH ) {
86 Attribute **ap = &rs->sr_entry->e_attrs;
88 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
89 /* will be generated later by frontend
90 * (a cleaner solution would be that
91 * the frontend checks if it already exists */
92 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
99 /* there SHOULD be one only! */
104 return SLAP_CB_CONTINUE;
106 } else if ( rs->sr_type == REP_RESULT ) {
107 /* back-ldap tried to send result */
108 op->o_callback->sc_private = (void *)(1);
115 * Dummy response that simply traces if back-ldap tried to send
116 * anything to the client
119 ldap_chain_cb_response( Operation *op, SlapReply *rs )
121 if ( rs->sr_type == REP_RESULT ) {
122 op->o_callback->sc_private = (void *)(1);
124 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
126 /* strip the entryDN attribute, but keep returning results */
127 (void)ldap_chain_cb_search_response( op, rs );
130 return SLAP_CB_CONTINUE;
137 int ( *op_f )( Operation *op, SlapReply *rs ),
140 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
141 struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
143 /* NOTE: returned if ref is empty... */
146 if ( lip->url != NULL ) {
147 op->o_bd->be_private = on->on_bi.bi_private;
148 return ( *op_f )( op, rs );
152 op->o_bd->be_private = &li;
154 /* if we parse the URI then by no means
155 * we can cache stuff or reuse connections,
156 * because in back-ldap there's no caching
157 * based on the URI value, which is supposed
158 * to be set once for all (correct?) */
159 op->o_do_not_cache = 1;
161 for ( ; !BER_BVISNULL( ref ); ref++ ) {
165 /* We're setting the URI of the first referral;
166 * what if there are more?
168 Document: draft-ietf-ldapbis-protocol-27.txt
172 If the client wishes to progress the operation, it MUST follow the
173 referral by contacting one of the supported services. If multiple
174 URIs are present, the client assumes that any supported URI may be
175 used to progress the operation.
177 * so we actually need to follow exactly one,
178 * and we can assume any is fine.
181 /* parse reference and use
182 * proto://[host][:port]/ only */
183 rc = ldap_url_parse_ext( ref->bv_val, &srv );
184 if ( rc != LDAP_URL_SUCCESS ) {
190 /* remove DN essentially because later on
191 * ldap_initialize() will parse the URL
192 * as a comma-separated URL list */
193 save_dn = srv->lud_dn;
195 srv->lud_scope = LDAP_SCOPE_DEFAULT;
196 li.url = ldap_url_desc2str( srv );
197 srv->lud_dn = save_dn;
198 ldap_free_urldesc( srv );
200 if ( li.url == NULL ) {
206 rc = ( *op_f )( op, rs );
208 ldap_memfree( li.url );
211 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
220 ldap_chain_response( Operation *op, SlapReply *rs )
222 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
223 void *private = op->o_bd->be_private;
224 slap_callback *sc = op->o_callback,
227 int cache = op->o_do_not_cache;
229 struct berval ndn = op->o_ndn;
231 struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
233 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
234 int sr_err = rs->sr_err;
235 slap_reply_t sr_type = rs->sr_type;
236 slap_mask_t chain_mask = 0;
237 ber_len_t chain_shift = 0;
238 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
240 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
241 return SLAP_CB_CONTINUE;
244 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
245 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
246 switch ( get_resolveBehavior( op ) ) {
247 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
248 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
249 return SLAP_CB_CONTINUE;
252 chain_mask = SLAP_CH_RESOLVE_MASK;
253 chain_shift = SLAP_CH_RESOLVE_SHIFT;
257 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
258 switch ( get_continuationBehavior( op ) ) {
259 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
260 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
261 return SLAP_CB_CONTINUE;
264 chain_mask = SLAP_CH_CONTINUATION_MASK;
265 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
269 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
272 * TODO: add checks on who/when chain operations; e.g.:
273 * a) what identities are authorized
274 * b) what request DN (e.g. only chain requests rooted at <DN>)
275 * c) what referral URIs
276 * d) what protocol scheme (e.g. only ldaps://)
283 /* we need this to know if back-ldap returned any result */
284 sc2.sc_response = ldap_chain_cb_response;
285 op->o_callback = &sc2;
287 /* Chaining can be performed by a privileged user on behalf
288 * of normal users, using the ProxyAuthz control, by exploiting
289 * the identity assertion feature of back-ldap; see idassert-*
290 * directives in slapd-ldap(5).
292 * FIXME: the idassert-authcDN is one, will it be fine regardless
293 * of the URI we obtain from the referral?
296 switch ( op->o_tag ) {
297 case LDAP_REQ_BIND: {
298 struct berval rndn = op->o_req_ndn;
299 Connection *conn = op->o_conn;
301 /* FIXME: can we really get a referral for binds? */
302 op->o_req_ndn = slap_empty_bv;
304 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
305 op->o_req_ndn = rndn;
311 int cleanup_attrs = 0;
313 if ( op->ora_e->e_attrs == NULL ) {
314 char textbuf[ SLAP_TEXT_BUFLEN ];
315 size_t textlen = sizeof( textbuf );
317 /* global overlay: create entry */
318 /* NOTE: this is a hack to use the chain overlay
319 * as global. I expect to be able to remove this
320 * soon by using slap_mods2entry() earlier in
321 * do_add(), adding the operational attrs later
323 rs->sr_err = slap_mods2entry( op->ora_modlist,
325 &rs->sr_text, textbuf, textlen );
326 if ( rs->sr_err != LDAP_SUCCESS ) {
327 send_ldap_result( op, rs );
332 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
333 if ( cleanup_attrs ) {
334 attrs_free( op->ora_e->e_attrs );
335 op->ora_e->e_attrs = NULL;
339 case LDAP_REQ_DELETE:
340 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
342 case LDAP_REQ_MODRDN:
343 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
345 case LDAP_REQ_MODIFY:
346 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
348 case LDAP_REQ_COMPARE:
349 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
351 case LDAP_REQ_SEARCH:
352 if ( rs->sr_type == REP_SEARCHREF ) {
353 struct berval *curr = ref,
355 ondn = op->o_req_ndn;
357 rs->sr_type = REP_SEARCH;
359 sc2.sc_response = ldap_chain_cb_search_response;
363 op->o_bd->be_private = &li;
365 /* if we parse the URI then by no means
366 * we can cache stuff or reuse connections,
367 * because in back-ldap there's no caching
368 * based on the URI value, which is supposed
369 * to be set once for all (correct?) */
370 op->o_do_not_cache = 1;
372 /* copy the private info because we need to modify it */
373 for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
377 /* parse reference and use
378 * proto://[host][:port]/ only */
379 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
380 if ( rc != LDAP_URL_SUCCESS ) {
382 rs->sr_err = LDAP_OTHER;
386 /* remove DN essentially because later on
387 * ldap_initialize() will parse the URL
388 * as a comma-separated URL list */
389 save_dn = srv->lud_dn;
391 srv->lud_scope = LDAP_SCOPE_DEFAULT;
392 li.url = ldap_url_desc2str( srv );
393 if ( li.url != NULL ) {
394 ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
396 ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
400 srv->lud_dn = save_dn;
401 ldap_free_urldesc( srv );
403 if ( li.url == NULL ) {
405 rs->sr_err = LDAP_OTHER;
410 /* FIXME: should we also copy filter and scope?
411 * according to RFC3296, no */
412 rc = lback->bi_op_search( op, rs );
414 ldap_memfree( li.url );
417 op->o_tmpfree( op->o_req_dn.bv_val,
419 op->o_tmpfree( op->o_req_ndn.bv_val,
422 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
428 op->o_req_ndn = ondn;
429 rs->sr_type = REP_SEARCHREF;
432 if ( rc != LDAP_SUCCESS ) {
433 /* couldn't chase any of the referrals */
434 rc = SLAP_CB_CONTINUE;
438 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
441 case LDAP_REQ_EXTENDED:
442 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
443 /* FIXME: ldap_back_extended() by design
444 * doesn't send result; frontend is expected
446 if ( rc != SLAPD_ABANDON ) {
447 send_ldap_extended( op, rs );
452 rc = SLAP_CB_CONTINUE;
456 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
457 if ( rc != LDAP_SUCCESS ) {
458 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
459 case LDAP_CHAINING_REQUIRED:
460 op->o_callback = NULL;
461 send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" );
465 rc = SLAP_CB_CONTINUE;
467 rs->sr_type = sr_type;
472 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
474 if ( sc2.sc_private == NULL ) {
475 op->o_callback = NULL;
476 rc = rs->sr_err = slap_map_api2result( rs );
477 send_ldap_result( op, rs );
480 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
482 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
483 op->o_do_not_cache = cache;
484 op->o_bd->be_private = private;
493 ldap_chain_db_config(
501 slap_overinst *on = (slap_overinst *) be->bd_info;
502 void *private = be->be_private;
506 be->be_private = on->on_bi.bi_private;
507 if ( strncasecmp( argv[ 0 ], "chain-", sizeof( "chain-" ) - 1 ) == 0 ) {
509 argv[ 0 ] = &argv[ 0 ][ sizeof( "chain-" ) - 1 ];
511 rc = lback->bi_db_config( be, fname, lineno, argc, argv );
516 be->be_private = private;
525 slap_overinst *on = (slap_overinst *)be->bd_info;
529 if ( lback == NULL ) {
530 lback = backend_info( "ldap" );
532 if ( lback == NULL ) {
537 bd.be_private = NULL;
538 rc = lback->bi_db_init( &bd );
539 on->on_bi.bi_private = bd.be_private;
545 ldap_chain_db_destroy(
549 slap_overinst *on = (slap_overinst *) be->bd_info;
550 void *private = be->be_private;
553 be->be_private = on->on_bi.bi_private;
554 rc = lback->bi_db_destroy( be );
555 on->on_bi.bi_private = be->be_private;
556 be->be_private = private;
560 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
562 ldap_chain_parse_ctrl(
572 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
573 rs->sr_text = "Chaining behavior control specified multiple times";
574 return LDAP_PROTOCOL_ERROR;
577 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
578 rs->sr_text = "Chaining behavior control specified with pagedResults control";
579 return LDAP_PROTOCOL_ERROR;
582 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
583 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
588 /* Parse the control value
589 * ChainingBehavior ::= SEQUENCE {
590 * resolveBehavior Behavior OPTIONAL,
591 * continuationBehavior Behavior OPTIONAL }
593 * Behavior :: = ENUMERATED {
594 * chainingPreferred (0),
595 * chainingRequired (1),
596 * referralsPreferred (2),
597 * referralsRequired (3) }
600 ber = ber_init( &ctrl->ldctl_value );
602 rs->sr_text = "internal error";
606 tag = ber_scanf( ber, "{e" /* } */, &behavior );
607 /* FIXME: since the whole SEQUENCE is optional,
608 * should we accept no enumerations at all? */
609 if ( tag != LBER_ENUMERATED ) {
610 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
611 return LDAP_PROTOCOL_ERROR;
614 switch ( behavior ) {
615 case LDAP_CHAINING_PREFERRED:
616 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
619 case LDAP_CHAINING_REQUIRED:
620 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
623 case LDAP_REFERRALS_PREFERRED:
624 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
627 case LDAP_REFERRALS_REQUIRED:
628 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
632 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
633 return LDAP_PROTOCOL_ERROR;
636 tag = ber_peek_tag( ber, &len );
637 if ( tag == LBER_ENUMERATED ) {
638 tag = ber_scanf( ber, "e", &behavior );
639 if ( tag == LBER_ERROR ) {
640 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
641 return LDAP_PROTOCOL_ERROR;
645 if ( tag == LBER_DEFAULT ) {
646 mode |= SLAP_CH_CONTINUATION_DEFAULT;
649 switch ( behavior ) {
650 case LDAP_CHAINING_PREFERRED:
651 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
654 case LDAP_CHAINING_REQUIRED:
655 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
658 case LDAP_REFERRALS_PREFERRED:
659 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
662 case LDAP_REFERRALS_REQUIRED:
663 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
667 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
668 return LDAP_PROTOCOL_ERROR;
672 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
673 rs->sr_text = "Chaining behavior control: decoding error";
674 return LDAP_PROTOCOL_ERROR;
677 (void) ber_free( ber, 1 );
680 op->o_chaining = mode | ( ctrl->ldctl_iscritical
681 ? SLAP_CONTROL_CRITICAL
682 : SLAP_CONTROL_NONCRITICAL );
686 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
688 static slap_overinst ldapchain;
693 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
696 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
697 SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
698 ldap_chain_parse_ctrl, &sc_chainingBehavior );
699 if ( rc != LDAP_SUCCESS ) {
700 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
703 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
705 ldapchain.on_bi.bi_type = "chain";
706 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
707 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
708 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
710 /* ... otherwise the underlying backend's function would be called,
711 * likely passing an invalid entry; on the contrary, the requested
712 * operational attributes should have been returned while chasing
713 * the referrals. This all in all is a bit messy, because part
714 * of the operational attributes are generated by they backend;
715 * part by the frontend; back-ldap should receive all the available
716 * ones from the remote server, but then, on it own, it strips those
717 * it assumes will be (re)generated by the frontend (e.g.
718 * subschemaSubentry.) */
719 ldapchain.on_bi.bi_operational = ldap_chain_operational;
721 ldapchain.on_response = ldap_chain_response;
723 return overlay_register( &ldapchain );