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.
24 #if defined(SLAPD_LDAP)
26 #ifdef SLAPD_OVER_CHAIN
30 #include <ac/string.h>
31 #include <ac/socket.h>
34 #include "../back-ldap/back-ldap.h"
36 static BackendInfo *lback;
39 ldap_chain_chk_referrals( Operation *op, SlapReply *rs )
45 ldap_chain_operational( Operation *op, SlapReply *rs )
47 /* trap entries generated by back-ldap.
48 * FIXME: we need a better way to recognize them; a cleaner
49 * solution would be to be able to intercept the response
50 * of be_operational(), so that we can divert only those
51 * calls that fail because operational attributes were
52 * requested for entries that do not belong to the underlying
53 * database. This fix is likely to intercept also entries
54 * generated by back-perl and so. */
55 if ( rs->sr_entry->e_private == NULL ) {
59 return SLAP_CB_CONTINUE;
63 ldap_chain_cb_response( Operation *op, SlapReply *rs )
65 assert( op->o_tag == LDAP_REQ_SEARCH );
67 if ( rs->sr_type == REP_SEARCH ) {
68 Attribute **ap = &rs->sr_entry->e_attrs;
70 for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
71 /* will be generated later by frontend
72 * (a cleaner solution would be that
73 * the frontend checks if it already exists */
74 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
81 /* there SHOULD be one only! */
86 return SLAP_CB_CONTINUE;
93 ldap_chain_response( Operation *op, SlapReply *rs )
95 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
96 void *private = op->o_bd->be_private;
97 slap_callback *sc = op->o_callback;
98 LDAPControl **prev = op->o_ctrls;
99 LDAPControl **ctrls = NULL, authz;
100 int i, nctrls, rc = 0;
101 int cache = op->o_do_not_cache;
102 char *authzid = NULL;
104 struct berval ndn = op->o_ndn;
106 struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
108 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF )
109 return SLAP_CB_CONTINUE;
114 op->o_callback = NULL;
116 if ( lip->url == NULL ) {
117 /* if we parse the URI then by no means
118 * we can cache stuff or reuse connections,
119 * because in back-ldap there's no caching
120 * based on the URI value, which is supposed
121 * to be set once for all (correct?) */
122 op->o_do_not_cache = 1;
124 /* FIXME: we're setting the URI of the first referral;
125 * what if there are more? Is this something we should
128 op->o_bd->be_private = &li;
130 if ( rs->sr_type != REP_SEARCHREF ) {
134 /* parse reference and use
135 * proto://[host][:port]/ only */
136 rc = ldap_url_parse_ext( ref[0].bv_val, &srv );
137 if ( rc != LDAP_URL_SUCCESS) {
142 /* remove DN essentially because later on
143 * ldap_initialize() will parse the URL
144 * as a comma-separated URL list */
145 save_dn = srv->lud_dn;
147 srv->lud_scope = LDAP_SCOPE_DEFAULT;
148 li.url = ldap_url_desc2str( srv );
149 srv->lud_dn = save_dn;
150 ldap_free_urldesc( srv );
152 if ( li.url == NULL ) {
159 op->o_bd->be_private = on->on_bi.bi_private;
162 /* Chaining is performed by a privileged user on behalf
163 * of a normal user, using the ProxyAuthz control. However,
164 * Binds are done separately, on an anonymous session.
166 if ( op->o_tag != LDAP_REQ_BIND ) {
167 for ( i = 0; prev && prev[i]; i++ )
168 /* count and set prev to the last one */ ;
171 /* Add an extra NULL slot */
176 ctrls = op->o_tmpalloc((i + 1)*sizeof(LDAPControl *),
178 for ( i = 0; i < nctrls; i++ ) {
181 ctrls[nctrls] = &authz;
182 ctrls[nctrls + 1] = NULL;
183 authz.ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
184 authz.ldctl_iscritical = 1;
185 authz.ldctl_value = op->o_dn;
186 if ( !BER_BVISEMPTY( &op->o_dn ) ) {
187 authzid = op->o_tmpalloc( op->o_dn.bv_len + STRLENOF("dn:"),
189 strcpy(authzid, "dn:");
190 strcpy(authzid + STRLENOF("dn:"), op->o_dn.bv_val);
191 authz.ldctl_value.bv_len = op->o_dn.bv_len + STRLENOF("dn:");
192 authz.ldctl_value.bv_val = authzid;
195 op->o_ndn = op->o_bd->be_rootndn;
198 switch ( op->o_tag ) {
199 case LDAP_REQ_BIND: {
200 struct berval rndn = op->o_req_ndn;
201 Connection *conn = op->o_conn;
203 op->o_req_ndn = slap_empty_bv;
206 rc = lback->bi_op_bind( op, rs );
207 op->o_req_ndn = rndn;
213 int cleanup_attrs = 0;
215 if ( op->ora_e->e_attrs == NULL ) {
216 char textbuf[ SLAP_TEXT_BUFLEN ];
217 size_t textlen = sizeof( textbuf );
219 /* global overlay; create entry */
220 /* NOTE: this is a hack to use the chain overlay
221 * as global. I expect to be able to remove this
222 * soon by using slap_mods2entry() earlier in
223 * do_add(), adding the operational attrs later
225 rs->sr_err = slap_mods2entry( op->ora_modlist,
227 &rs->sr_text, textbuf, textlen );
228 if ( rs->sr_err != LDAP_SUCCESS ) {
229 send_ldap_result( op, rs );
234 rc = lback->bi_op_add( op, rs );
235 if ( cleanup_attrs ) {
236 attrs_free( op->ora_e->e_attrs );
237 op->ora_e->e_attrs = NULL;
241 case LDAP_REQ_DELETE:
242 rc = lback->bi_op_delete( op, rs );
244 case LDAP_REQ_MODRDN:
245 rc = lback->bi_op_modrdn( op, rs );
247 case LDAP_REQ_MODIFY:
248 rc = lback->bi_op_modify( op, rs );
250 case LDAP_REQ_COMPARE:
251 rc = lback->bi_op_compare( op, rs );
253 case LDAP_REQ_SEARCH:
254 if ( rs->sr_type == REP_SEARCHREF ) {
255 struct berval *curr = ref,
257 ondn = op->o_req_ndn;
258 slap_callback sc2 = { 0 };
260 ber_len_t refcnt = 0;
261 BerVarray newref = NULL;
263 sc2.sc_response = ldap_chain_cb_response;
264 op->o_callback = &sc2;
266 rs->sr_type = REP_SEARCH;
268 /* copy the private info because we need to modify it */
269 for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
273 /* parse reference and use
274 * proto://[host][:port]/ only */
275 tmprc = ldap_url_parse_ext( curr[0].bv_val, &srv );
276 if ( tmprc != LDAP_URL_SUCCESS ) {
279 goto end_of_searchref;
282 /* remove DN essentially because later on
283 * ldap_initialize() will parse the URL
284 * as a comma-separated URL list */
285 save_dn = srv->lud_dn;
287 srv->lud_scope = LDAP_SCOPE_DEFAULT;
288 li.url = ldap_url_desc2str( srv );
289 if ( li.url != NULL ) {
290 ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
292 ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
296 srv->lud_dn = save_dn;
297 ldap_free_urldesc( srv );
299 if ( li.url == NULL ) {
302 goto end_of_searchref;
306 /* FIXME: should we also copy filter and scope?
307 * according to RFC3296, no */
308 tmprc = lback->bi_op_search( op, rs );
310 ldap_memfree( li.url );
313 op->o_tmpfree( op->o_req_dn.bv_val,
315 op->o_tmpfree( op->o_req_ndn.bv_val,
321 goto end_of_searchref;
324 if ( rs->sr_err != LDAP_SUCCESS ) {
325 /* if search was not successful,
326 * at least return the referral! */
327 /* FIXME: assumes referrals
328 * are always created via
329 * referral_rewrite() and freed via
330 * ber_bvarray_free( rs->sr_ref ) */
331 newref = ch_realloc( newref, sizeof( struct berval ) * (refcnt + 2) );
332 ber_dupbv( &newref[ refcnt ], &curr[ 0 ] );
334 BER_BVZERO( &newref[ refcnt ] );
340 op->o_req_ndn = ondn;
341 rs->sr_type = REP_SEARCHREF;
344 /* if the error was bad, it was already returned
345 * by back-ldap; destroy the referrals left;
346 * otherwise, let the frontend return them. */
349 rc = SLAP_CB_CONTINUE;
350 if ( ref != default_referral ) {
351 ber_bvarray_free( ref );
356 ber_bvarray_free( newref );
361 rc = lback->bi_op_search( op, rs );
364 case LDAP_REQ_EXTENDED:
365 rc = lback->bi_extended( op, rs );
368 rc = SLAP_CB_CONTINUE;
371 op->o_do_not_cache = cache;
373 op->o_bd->be_private = private;
377 op->o_tmpfree( ctrls, op->o_tmpmemctx );
380 op->o_tmpfree( authzid, op->o_tmpmemctx );
383 if ( lip->url == NULL && li.url != NULL ) {
384 ldap_memfree( li.url );
399 slap_overinst *on = (slap_overinst *) be->bd_info;
400 void *private = be->be_private;
404 be->be_private = on->on_bi.bi_private;
405 if ( strncasecmp( argv[ 0 ], "chain-", sizeof( "chain-" ) - 1 ) == 0 ) {
407 argv[ 0 ] = &argv[ 0 ][ sizeof( "chain-" ) - 1 ];
409 rc = lback->bi_db_config( be, fname, lineno, argc, argv );
414 be->be_private = private;
423 slap_overinst *on = (slap_overinst *) be->bd_info;
424 void *private = be->be_private;
427 be->be_private = NULL;
428 rc = lback->bi_db_init( be );
429 on->on_bi.bi_private = be->be_private;
430 be->be_private = private;
440 slap_overinst *on = (slap_overinst *) be->bd_info;
441 void *private = be->be_private;
444 be->be_private = on->on_bi.bi_private;
445 rc = lback->bi_db_destroy( be );
446 on->on_bi.bi_private = be->be_private;
447 be->be_private = private;
451 static slap_overinst ldapchain;
456 lback = backend_info( "ldap" );
462 ldapchain.on_bi.bi_type = "chain";
463 ldapchain.on_bi.bi_db_init = ldap_chain_init;
464 ldapchain.on_bi.bi_db_config = ldap_chain_config;
465 ldapchain.on_bi.bi_db_destroy = ldap_chain_destroy;
467 /* ... otherwise the underlying backend's function would be called,
468 * likely passing an invalid entry; on the contrary, the requested
469 * operational attributes should have been returned while chasing
470 * the referrals. This all in all is a bit messy, because part
471 * of the operational attributes are generated by they backend;
472 * part by the frontend; back-ldap should receive all the available
473 * ones from the remote server, but then, on it own, it strips those
474 * it assumes will be (re)generated by the frontend (e.g.
475 * subschemaSubentry.) */
476 ldapchain.on_bi.bi_operational = ldap_chain_operational;
478 ldapchain.on_response = ldap_chain_response;
481 ldapchain.on_bi.bi_chk_referrals = ldap_chain_chk_referrals;
483 return overlay_register( &ldapchain );
486 #if SLAPD_OVER_CHAIN == SLAPD_MOD_DYNAMIC
487 int init_module(int argc, char *argv[]) {
490 #endif /* SLAPD_OVER_CHAIN == SLAPD_MOD_DYNAMIC */
492 #endif /* SLAPD_OVER_CHAIN */
494 #endif /* ! defined(SLAPD_LDAP) */