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;
144 if ( lip->url != NULL ) {
145 op->o_bd->be_private = on->on_bi.bi_private;
146 return ( *op_f )( op, rs );
150 op->o_bd->be_private = &li;
152 /* if we parse the URI then by no means
153 * we can cache stuff or reuse connections,
154 * because in back-ldap there's no caching
155 * based on the URI value, which is supposed
156 * to be set once for all (correct?) */
157 op->o_do_not_cache = 1;
159 for ( ; !BER_BVISNULL( ref ); ref++ ) {
163 /* We're setting the URI of the first referral;
164 * what if there are more?
166 Document: draft-ietf-ldapbis-protocol-27.txt
170 If the client wishes to progress the operation, it MUST follow the
171 referral by contacting one of the supported services. If multiple
172 URIs are present, the client assumes that any supported URI may be
173 used to progress the operation.
175 * so we actually need to follow exactly one,
176 * and we can assume any is fine.
179 /* parse reference and use
180 * proto://[host][:port]/ only */
181 rc = ldap_url_parse_ext( ref->bv_val, &srv );
182 if ( rc != LDAP_URL_SUCCESS ) {
188 /* remove DN essentially because later on
189 * ldap_initialize() will parse the URL
190 * as a comma-separated URL list */
191 save_dn = srv->lud_dn;
193 srv->lud_scope = LDAP_SCOPE_DEFAULT;
194 li.url = ldap_url_desc2str( srv );
195 srv->lud_dn = save_dn;
196 ldap_free_urldesc( srv );
198 if ( li.url == NULL ) {
204 rc = ( *op_f )( op, rs );
206 ldap_memfree( li.url );
209 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
218 ldap_chain_response( Operation *op, SlapReply *rs )
220 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
221 void *private = op->o_bd->be_private;
222 slap_callback *sc = op->o_callback,
225 int cache = op->o_do_not_cache;
227 struct berval ndn = op->o_ndn;
229 struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
231 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
232 int sr_err = rs->sr_err;
233 slap_reply_t sr_type = rs->sr_type;
234 slap_mask_t chain_mask = 0;
235 ber_len_t chain_shift = 0;
236 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
238 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
239 return SLAP_CB_CONTINUE;
242 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
243 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
244 switch ( get_resolveBehavior( op ) ) {
245 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
246 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
247 return SLAP_CB_CONTINUE;
250 chain_mask = SLAP_CH_RESOLVE_MASK;
251 chain_shift = SLAP_CH_RESOLVE_SHIFT;
255 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
256 switch ( get_continuationBehavior( op ) ) {
257 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
258 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
259 return SLAP_CB_CONTINUE;
262 chain_mask = SLAP_CH_CONTINUATION_MASK;
263 chain_shift = SLAP_CH_CONTINUATION_SHIFT;
267 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
270 * TODO: add checks on who/when chain operations; e.g.:
271 * a) what identities are authorized
272 * b) what request DN (e.g. only chain requests rooted at <DN>)
273 * c) what referral URIs
274 * d) what protocol scheme (e.g. only ldaps://)
281 /* we need this to know if back-ldap returned any result */
282 sc2.sc_response = ldap_chain_cb_response;
283 op->o_callback = &sc2;
285 /* Chaining can be performed by a privileged user on behalf
286 * of normal users, using the ProxyAuthz control, by exploiting
287 * the identity assertion feature of back-ldap; see idassert-*
288 * directives in slapd-ldap(5).
290 * FIXME: the idassert-authcDN is one, will it be fine regardless
291 * of the URI we obtain from the referral?
294 switch ( op->o_tag ) {
295 case LDAP_REQ_BIND: {
296 struct berval rndn = op->o_req_ndn;
297 Connection *conn = op->o_conn;
299 /* FIXME: can we really get a referral for binds? */
300 op->o_req_ndn = slap_empty_bv;
302 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
303 op->o_req_ndn = rndn;
309 int cleanup_attrs = 0;
311 if ( op->ora_e->e_attrs == NULL ) {
312 char textbuf[ SLAP_TEXT_BUFLEN ];
313 size_t textlen = sizeof( textbuf );
315 /* global overlay: create entry */
316 /* NOTE: this is a hack to use the chain overlay
317 * as global. I expect to be able to remove this
318 * soon by using slap_mods2entry() earlier in
319 * do_add(), adding the operational attrs later
321 rs->sr_err = slap_mods2entry( op->ora_modlist,
323 &rs->sr_text, textbuf, textlen );
324 if ( rs->sr_err != LDAP_SUCCESS ) {
325 send_ldap_result( op, rs );
330 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
331 if ( cleanup_attrs ) {
332 attrs_free( op->ora_e->e_attrs );
333 op->ora_e->e_attrs = NULL;
337 case LDAP_REQ_DELETE:
338 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
340 case LDAP_REQ_MODRDN:
341 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
343 case LDAP_REQ_MODIFY:
344 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
346 case LDAP_REQ_COMPARE:
347 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
349 case LDAP_REQ_SEARCH:
350 if ( rs->sr_type == REP_SEARCHREF ) {
351 struct berval *curr = ref,
353 ondn = op->o_req_ndn;
355 rs->sr_type = REP_SEARCH;
357 sc2.sc_response = ldap_chain_cb_search_response;
361 op->o_bd->be_private = &li;
363 /* if we parse the URI then by no means
364 * we can cache stuff or reuse connections,
365 * because in back-ldap there's no caching
366 * based on the URI value, which is supposed
367 * to be set once for all (correct?) */
368 op->o_do_not_cache = 1;
370 /* copy the private info because we need to modify it */
371 for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
375 /* parse reference and use
376 * proto://[host][:port]/ only */
377 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
378 if ( rc != LDAP_URL_SUCCESS ) {
380 rs->sr_err = LDAP_OTHER;
384 /* remove DN essentially because later on
385 * ldap_initialize() will parse the URL
386 * as a comma-separated URL list */
387 save_dn = srv->lud_dn;
389 srv->lud_scope = LDAP_SCOPE_DEFAULT;
390 li.url = ldap_url_desc2str( srv );
391 if ( li.url != NULL ) {
392 ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
394 ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
398 srv->lud_dn = save_dn;
399 ldap_free_urldesc( srv );
401 if ( li.url == NULL ) {
403 rs->sr_err = LDAP_OTHER;
408 /* FIXME: should we also copy filter and scope?
409 * according to RFC3296, no */
410 rc = lback->bi_op_search( op, rs );
412 ldap_memfree( li.url );
415 op->o_tmpfree( op->o_req_dn.bv_val,
417 op->o_tmpfree( op->o_req_ndn.bv_val,
420 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
426 op->o_req_ndn = ondn;
427 rs->sr_type = REP_SEARCHREF;
430 if ( rc != LDAP_SUCCESS ) {
431 /* couldn't chase any of the referrals */
432 rc = SLAP_CB_CONTINUE;
436 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
439 case LDAP_REQ_EXTENDED:
440 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
441 /* FIXME: ldap_back_extended() by design
442 * doesn't send result; frontend is expected
444 if ( rc != SLAPD_ABANDON ) {
445 send_ldap_extended( op, rs );
450 rc = SLAP_CB_CONTINUE;
454 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
455 if ( rc != LDAP_SUCCESS ) {
456 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
457 case LDAP_CHAINING_REQUIRED:
458 op->o_callback = NULL;
459 send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" );
463 rc = SLAP_CB_CONTINUE;
465 rs->sr_type = sr_type;
470 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
472 if ( sc2.sc_private == NULL ) {
473 op->o_callback = NULL;
474 rc = rs->sr_err = slap_map_api2result( rs );
475 send_ldap_result( op, rs );
478 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
480 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
481 op->o_do_not_cache = cache;
482 op->o_bd->be_private = private;
491 ldap_chain_db_config(
499 slap_overinst *on = (slap_overinst *) be->bd_info;
500 void *private = be->be_private;
504 be->be_private = on->on_bi.bi_private;
505 if ( strncasecmp( argv[ 0 ], "chain-", sizeof( "chain-" ) - 1 ) == 0 ) {
507 argv[ 0 ] = &argv[ 0 ][ sizeof( "chain-" ) - 1 ];
509 rc = lback->bi_db_config( be, fname, lineno, argc, argv );
514 be->be_private = private;
523 slap_overinst *on = (slap_overinst *)be->bd_info;
527 if ( lback == NULL ) {
528 lback = backend_info( "ldap" );
530 if ( lback == NULL ) {
535 bd.be_private = NULL;
536 rc = lback->bi_db_init( &bd );
537 on->on_bi.bi_private = bd.be_private;
543 ldap_chain_db_destroy(
547 slap_overinst *on = (slap_overinst *) be->bd_info;
548 void *private = be->be_private;
551 be->be_private = on->on_bi.bi_private;
552 rc = lback->bi_db_destroy( be );
553 on->on_bi.bi_private = be->be_private;
554 be->be_private = private;
558 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
560 ldap_chain_parse_ctrl(
570 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
571 rs->sr_text = "Chaining behavior control specified multiple times";
572 return LDAP_PROTOCOL_ERROR;
575 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
576 rs->sr_text = "Chaining behavior control specified with pagedResults control";
577 return LDAP_PROTOCOL_ERROR;
580 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
581 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
586 /* Parse the control value
587 * ChainingBehavior ::= SEQUENCE {
588 * resolveBehavior Behavior OPTIONAL,
589 * continuationBehavior Behavior OPTIONAL }
591 * Behavior :: = ENUMERATED {
592 * chainingPreferred (0),
593 * chainingRequired (1),
594 * referralsPreferred (2),
595 * referralsRequired (3) }
598 ber = ber_init( &ctrl->ldctl_value );
600 rs->sr_text = "internal error";
604 tag = ber_scanf( ber, "{e" /* } */, &behavior );
605 /* FIXME: since the whole SEQUENCE is optional,
606 * should we accept no enumerations at all? */
607 if ( tag != LBER_ENUMERATED ) {
608 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
609 return LDAP_PROTOCOL_ERROR;
612 switch ( behavior ) {
613 case LDAP_CHAINING_PREFERRED:
614 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
617 case LDAP_CHAINING_REQUIRED:
618 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
621 case LDAP_REFERRALS_PREFERRED:
622 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
625 case LDAP_REFERRALS_REQUIRED:
626 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
630 rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
631 return LDAP_PROTOCOL_ERROR;
634 tag = ber_peek_tag( ber, &len );
635 if ( tag == LBER_ENUMERATED ) {
636 tag = ber_scanf( ber, "e", &behavior );
637 if ( tag == LBER_ERROR ) {
638 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
639 return LDAP_PROTOCOL_ERROR;
643 if ( tag == LBER_DEFAULT ) {
644 mode |= SLAP_CH_CONTINUATION_DEFAULT;
647 switch ( behavior ) {
648 case LDAP_CHAINING_PREFERRED:
649 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
652 case LDAP_CHAINING_REQUIRED:
653 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
656 case LDAP_REFERRALS_PREFERRED:
657 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
660 case LDAP_REFERRALS_REQUIRED:
661 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
665 rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
666 return LDAP_PROTOCOL_ERROR;
670 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
671 rs->sr_text = "Chaining behavior control: decoding error";
672 return LDAP_PROTOCOL_ERROR;
675 (void) ber_free( ber, 1 );
678 op->o_chaining = mode | ( ctrl->ldctl_iscritical
679 ? SLAP_CONTROL_CRITICAL
680 : SLAP_CONTROL_NONCRITICAL );
684 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
686 static slap_overinst ldapchain;
691 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
694 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
695 SLAP_CTRL_ACCESS, NULL,
696 ldap_chain_parse_ctrl, &sc_chainingBehavior );
697 if ( rc != LDAP_SUCCESS ) {
698 fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
701 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
703 ldapchain.on_bi.bi_type = "chain";
704 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
705 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
706 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
708 /* ... otherwise the underlying backend's function would be called,
709 * likely passing an invalid entry; on the contrary, the requested
710 * operational attributes should have been returned while chasing
711 * the referrals. This all in all is a bit messy, because part
712 * of the operational attributes are generated by they backend;
713 * part by the frontend; back-ldap should receive all the available
714 * ones from the remote server, but then, on it own, it strips those
715 * it assumes will be (re)generated by the frontend (e.g.
716 * subschemaSubentry.) */
717 ldapchain.on_bi.bi_operational = ldap_chain_operational;
719 ldapchain.on_response = ldap_chain_response;
721 return overlay_register( &ldapchain );