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 static BackendInfo *lback;
35 ldap_chain_operational( Operation *op, SlapReply *rs )
37 /* Trap entries generated by back-ldap.
39 * FIXME: we need a better way to recognize them; a cleaner
40 * solution would be to be able to intercept the response
41 * of be_operational(), so that we can divert only those
42 * calls that fail because operational attributes were
43 * requested for entries that do not belong to the underlying
44 * database. This fix is likely to intercept also entries
45 * generated by back-perl and so. */
46 if ( rs->sr_entry->e_private == NULL ) {
50 return SLAP_CB_CONTINUE;
54 * Search specific response that strips entryDN from entries
57 ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
59 assert( op->o_tag == LDAP_REQ_SEARCH );
61 if ( rs->sr_type == REP_SEARCH ) {
62 Attribute **ap = &rs->sr_entry->e_attrs;
64 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
65 /* will be generated later by frontend
66 * (a cleaner solution would be that
67 * the frontend checks if it already exists */
68 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
75 /* there SHOULD be one only! */
80 return SLAP_CB_CONTINUE;
82 } else if ( rs->sr_type == REP_RESULT ) {
83 /* back-ldap tried to send result */
84 op->o_callback->sc_private = (void *)(1);
91 * Dummy response that simply traces if back-ldap tried to send
92 * anything to the client
95 ldap_chain_cb_response( Operation *op, SlapReply *rs )
97 if ( rs->sr_type == REP_RESULT ) {
98 op->o_callback->sc_private = (void *)(1);
100 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
102 /* strip the entryDN attribute, but keep returning results */
103 (void)ldap_chain_cb_search_response( op, rs );
106 return SLAP_CB_CONTINUE;
113 int ( *op_f )( Operation *op, SlapReply *rs ),
116 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
117 struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
120 if ( lip->url != NULL ) {
121 op->o_bd->be_private = on->on_bi.bi_private;
122 return ( *op_f )( op, rs );
126 op->o_bd->be_private = &li;
128 /* if we parse the URI then by no means
129 * we can cache stuff or reuse connections,
130 * because in back-ldap there's no caching
131 * based on the URI value, which is supposed
132 * to be set once for all (correct?) */
133 op->o_do_not_cache = 1;
135 for ( ; !BER_BVISNULL( ref ); ref++ ) {
139 /* We're setting the URI of the first referral;
140 * what if there are more?
142 Document: draft-ietf-ldapbis-protocol-27.txt
146 If the client wishes to progress the operation, it MUST follow the
147 referral by contacting one of the supported services. If multiple
148 URIs are present, the client assumes that any supported URI may be
149 used to progress the operation.
151 * so we actually need to follow exactly one,
152 * and we can assume any is fine.
155 /* parse reference and use
156 * proto://[host][:port]/ only */
157 rc = ldap_url_parse_ext( ref->bv_val, &srv );
158 if ( rc != LDAP_URL_SUCCESS ) {
164 /* remove DN essentially because later on
165 * ldap_initialize() will parse the URL
166 * as a comma-separated URL list */
167 save_dn = srv->lud_dn;
169 srv->lud_scope = LDAP_SCOPE_DEFAULT;
170 li.url = ldap_url_desc2str( srv );
171 srv->lud_dn = save_dn;
172 ldap_free_urldesc( srv );
174 if ( li.url == NULL ) {
180 rc = ( *op_f )( op, rs );
182 ldap_memfree( li.url );
185 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
194 ldap_chain_response( Operation *op, SlapReply *rs )
196 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
197 void *private = op->o_bd->be_private;
198 slap_callback *sc = op->o_callback,
201 int cache = op->o_do_not_cache;
203 struct berval ndn = op->o_ndn;
205 struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
207 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
208 return SLAP_CB_CONTINUE;
212 * TODO: add checks on who/when chain operations; e.g.:
213 * a) what identities are authorized
214 * b) what request DN (e.g. only chain requests rooted at <DN>)
215 * c) what referral URIs
216 * d) what protocol scheme (e.g. only ldaps://)
223 /* we need this to know if back-ldap returned any result */
224 sc2.sc_response = ldap_chain_cb_response;
225 op->o_callback = &sc2;
227 /* Chaining can be performed by a privileged user on behalf
228 * of normal users, using the ProxyAuthz control, by exploiting
229 * the identity assertion feature of back-ldap; see idassert-*
230 * directives in slapd-ldap(5).
232 * FIXME: the idassert-authcDN is one, will it be fine regardless
233 * of the URI we obtain from the referral?
236 switch ( op->o_tag ) {
237 case LDAP_REQ_BIND: {
238 struct berval rndn = op->o_req_ndn;
239 Connection *conn = op->o_conn;
241 /* FIXME: can we really get a referral for binds? */
242 op->o_req_ndn = slap_empty_bv;
244 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
245 op->o_req_ndn = rndn;
251 int cleanup_attrs = 0;
253 if ( op->ora_e->e_attrs == NULL ) {
254 char textbuf[ SLAP_TEXT_BUFLEN ];
255 size_t textlen = sizeof( textbuf );
257 /* global overlay: create entry */
258 /* NOTE: this is a hack to use the chain overlay
259 * as global. I expect to be able to remove this
260 * soon by using slap_mods2entry() earlier in
261 * do_add(), adding the operational attrs later
263 rs->sr_err = slap_mods2entry( op->ora_modlist,
265 &rs->sr_text, textbuf, textlen );
266 if ( rs->sr_err != LDAP_SUCCESS ) {
267 send_ldap_result( op, rs );
272 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
273 if ( cleanup_attrs ) {
274 attrs_free( op->ora_e->e_attrs );
275 op->ora_e->e_attrs = NULL;
279 case LDAP_REQ_DELETE:
280 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
282 case LDAP_REQ_MODRDN:
283 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
285 case LDAP_REQ_MODIFY:
286 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
288 case LDAP_REQ_COMPARE:
289 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
291 case LDAP_REQ_SEARCH:
292 if ( rs->sr_type == REP_SEARCHREF ) {
293 struct berval *curr = ref,
295 ondn = op->o_req_ndn;
297 rs->sr_type = REP_SEARCH;
299 sc2.sc_response = ldap_chain_cb_search_response;
303 op->o_bd->be_private = &li;
305 /* if we parse the URI then by no means
306 * we can cache stuff or reuse connections,
307 * because in back-ldap there's no caching
308 * based on the URI value, which is supposed
309 * to be set once for all (correct?) */
310 op->o_do_not_cache = 1;
312 /* copy the private info because we need to modify it */
313 for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
317 /* parse reference and use
318 * proto://[host][:port]/ only */
319 rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
320 if ( rc != LDAP_URL_SUCCESS ) {
322 rs->sr_err = LDAP_OTHER;
326 /* remove DN essentially because later on
327 * ldap_initialize() will parse the URL
328 * as a comma-separated URL list */
329 save_dn = srv->lud_dn;
331 srv->lud_scope = LDAP_SCOPE_DEFAULT;
332 li.url = ldap_url_desc2str( srv );
333 if ( li.url != NULL ) {
334 ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
336 ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
340 srv->lud_dn = save_dn;
341 ldap_free_urldesc( srv );
343 if ( li.url == NULL ) {
345 rs->sr_err = LDAP_OTHER;
350 /* FIXME: should we also copy filter and scope?
351 * according to RFC3296, no */
352 rc = lback->bi_op_search( op, rs );
354 ldap_memfree( li.url );
357 op->o_tmpfree( op->o_req_dn.bv_val,
359 op->o_tmpfree( op->o_req_ndn.bv_val,
362 if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
368 op->o_req_ndn = ondn;
369 rs->sr_type = REP_SEARCHREF;
372 if ( rc != LDAP_SUCCESS ) {
373 /* couldn't chase any of the referrals */
374 rc = SLAP_CB_CONTINUE;
378 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
381 case LDAP_REQ_EXTENDED:
382 rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
383 /* FIXME: ldap_back_extended() by design
384 * doesn't send result; frontend is expected
386 if ( rc != SLAPD_ABANDON ) {
387 send_ldap_extended( op, rs );
392 rc = SLAP_CB_CONTINUE;
396 if ( sc2.sc_private == NULL ) {
397 op->o_callback = NULL;
398 rc = rs->sr_err = slap_map_api2result( rs );
399 send_ldap_result( op, rs );
402 op->o_do_not_cache = cache;
403 op->o_bd->be_private = private;
412 ldap_chain_db_config(
420 slap_overinst *on = (slap_overinst *) be->bd_info;
421 void *private = be->be_private;
425 be->be_private = on->on_bi.bi_private;
426 if ( strncasecmp( argv[ 0 ], "chain-", sizeof( "chain-" ) - 1 ) == 0 ) {
428 argv[ 0 ] = &argv[ 0 ][ sizeof( "chain-" ) - 1 ];
430 rc = lback->bi_db_config( be, fname, lineno, argc, argv );
435 be->be_private = private;
444 slap_overinst *on = (slap_overinst *)be->bd_info;
448 if ( lback == NULL ) {
449 lback = backend_info( "ldap" );
451 if ( lback == NULL ) {
456 bd.be_private = NULL;
457 rc = lback->bi_db_init( &bd );
458 on->on_bi.bi_private = bd.be_private;
464 ldap_chain_db_destroy(
468 slap_overinst *on = (slap_overinst *) be->bd_info;
469 void *private = be->be_private;
472 be->be_private = on->on_bi.bi_private;
473 rc = lback->bi_db_destroy( be );
474 on->on_bi.bi_private = be->be_private;
475 be->be_private = private;
479 static slap_overinst ldapchain;
484 ldapchain.on_bi.bi_type = "chain";
485 ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
486 ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
487 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
489 /* ... otherwise the underlying backend's function would be called,
490 * likely passing an invalid entry; on the contrary, the requested
491 * operational attributes should have been returned while chasing
492 * the referrals. This all in all is a bit messy, because part
493 * of the operational attributes are generated by they backend;
494 * part by the frontend; back-ldap should receive all the available
495 * ones from the remote server, but then, on it own, it strips those
496 * it assumes will be (re)generated by the frontend (e.g.
497 * subschemaSubentry.) */
498 ldapchain.on_bi.bi_operational = ldap_chain_operational;
500 ldapchain.on_response = ldap_chain_response;
502 return overlay_register( &ldapchain );