openldap/servers/slapd/back-ldap/chain.c

505 lines
12 KiB
C
Raw Normal View History

/* chain.c - chain LDAP operations */
/* $OpenLDAP$ */
2003-11-27 01:35:14 -05:00
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
2005-01-01 15:49:32 -05:00
* Copyright 2003-2005 The OpenLDAP Foundation.
2003-12-08 12:41:40 -05:00
* Portions Copyright 2003 Howard Chu.
2003-11-27 01:35:14 -05:00
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/* ACKNOWLEDGEMENTS:
* This work was initially developed by the Howard Chu for inclusion
* in OpenLDAP Software.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include <ac/socket.h>
#include "slap.h"
2005-01-09 16:26:32 -05:00
#include "back-ldap.h"
static BackendInfo *lback;
static int
ldap_chain_operational( Operation *op, SlapReply *rs )
{
/* Trap entries generated by back-ldap.
*
* FIXME: we need a better way to recognize them; a cleaner
* solution would be to be able to intercept the response
* of be_operational(), so that we can divert only those
* calls that fail because operational attributes were
* requested for entries that do not belong to the underlying
* database. This fix is likely to intercept also entries
* generated by back-perl and so. */
if ( rs->sr_entry->e_private == NULL ) {
return 0;
}
return SLAP_CB_CONTINUE;
}
/*
* Search specific response that strips entryDN from entries
*/
static int
ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
{
assert( op->o_tag == LDAP_REQ_SEARCH );
if ( rs->sr_type == REP_SEARCH ) {
Attribute **ap = &rs->sr_entry->e_attrs;
for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
/* will be generated later by frontend
* (a cleaner solution would be that
* the frontend checks if it already exists */
if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
{
Attribute *a = *ap;
*ap = (*ap)->a_next;
attr_free( a );
/* there SHOULD be one only! */
break;
}
}
return SLAP_CB_CONTINUE;
} else if ( rs->sr_type == REP_RESULT ) {
/* back-ldap tried to send result */
op->o_callback->sc_private = (void *)(1);
}
return 0;
}
/*
* Dummy response that simply traces if back-ldap tried to send
* anything to the client
*/
static int
ldap_chain_cb_response( Operation *op, SlapReply *rs )
{
if ( rs->sr_type == REP_RESULT ) {
op->o_callback->sc_private = (void *)(1);
} else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
{
/* strip the entryDN attribute, but keep returning results */
(void)ldap_chain_cb_search_response( op, rs );
}
return SLAP_CB_CONTINUE;
}
static int
ldap_chain_op(
Operation *op,
SlapReply *rs,
int ( *op_f )( Operation *op, SlapReply *rs ),
BerVarray ref )
{
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
int rc;
if ( lip->url != NULL ) {
op->o_bd->be_private = on->on_bi.bi_private;
return ( *op_f )( op, rs );
}
li = *lip;
op->o_bd->be_private = &li;
/* if we parse the URI then by no means
* we can cache stuff or reuse connections,
* because in back-ldap there's no caching
* based on the URI value, which is supposed
* to be set once for all (correct?) */
op->o_do_not_cache = 1;
for ( ; !BER_BVISNULL( ref ); ref++ ) {
LDAPURLDesc *srv;
char *save_dn;
/* We're setting the URI of the first referral;
* what if there are more?
Document: draft-ietf-ldapbis-protocol-27.txt
4.1.10. Referral
...
If the client wishes to progress the operation, it MUST follow the
referral by contacting one of the supported services. If multiple
URIs are present, the client assumes that any supported URI may be
used to progress the operation.
* so we actually need to follow exactly one,
* and we can assume any is fine.
*/
/* parse reference and use
* proto://[host][:port]/ only */
rc = ldap_url_parse_ext( ref->bv_val, &srv );
if ( rc != LDAP_URL_SUCCESS ) {
/* try next */
rc = LDAP_OTHER;
continue;
}
/* remove DN essentially because later on
* ldap_initialize() will parse the URL
* as a comma-separated URL list */
save_dn = srv->lud_dn;
srv->lud_dn = "";
srv->lud_scope = LDAP_SCOPE_DEFAULT;
li.url = ldap_url_desc2str( srv );
srv->lud_dn = save_dn;
ldap_free_urldesc( srv );
if ( li.url == NULL ) {
/* try next */
rc = LDAP_OTHER;
continue;
}
rc = ( *op_f )( op, rs );
ldap_memfree( li.url );
li.url = NULL;
if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
break;
}
}
return rc;
}
static int
ldap_chain_response( Operation *op, SlapReply *rs )
{
2004-04-05 13:31:27 -04:00
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
void *private = op->o_bd->be_private;
slap_callback *sc = op->o_callback,
sc2 = { 0 };
int rc = 0;
2004-04-05 13:31:27 -04:00
int cache = op->o_do_not_cache;
BerVarray ref;
struct berval ndn = op->o_ndn;
struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
return SLAP_CB_CONTINUE;
}
/*
* TODO: add checks on who/when chain operations; e.g.:
* a) what identities are authorized
* b) what request DN (e.g. only chain requests rooted at <DN>)
* c) what referral URIs
* d) what protocol scheme (e.g. only ldaps://)
* e) what ssf
*/
2003-06-11 02:32:24 -04:00
ref = rs->sr_ref;
2004-04-05 13:31:27 -04:00
rs->sr_ref = NULL;
2003-06-11 02:32:24 -04:00
/* we need this to know if back-ldap returned any result */
sc2.sc_response = ldap_chain_cb_response;
op->o_callback = &sc2;
2004-04-05 13:31:27 -04:00
/* Chaining can be performed by a privileged user on behalf
* of normal users, using the ProxyAuthz control, by exploiting
* the identity assertion feature of back-ldap; see idassert-*
* directives in slapd-ldap(5).
*
* FIXME: the idassert-authcDN is one, will it be fine regardless
* of the URI we obtain from the referral?
2003-06-11 18:35:31 -04:00
*/
switch ( op->o_tag ) {
2003-06-11 18:35:31 -04:00
case LDAP_REQ_BIND: {
2004-04-05 13:31:27 -04:00
struct berval rndn = op->o_req_ndn;
Connection *conn = op->o_conn;
/* FIXME: can we really get a referral for binds? */
2003-06-11 18:35:31 -04:00
op->o_req_ndn = slap_empty_bv;
op->o_conn = NULL;
rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref );
2003-06-11 18:35:31 -04:00
op->o_req_ndn = rndn;
op->o_conn = conn;
}
break;
case LDAP_REQ_ADD:
{
int cleanup_attrs = 0;
if ( op->ora_e->e_attrs == NULL ) {
char textbuf[ SLAP_TEXT_BUFLEN ];
size_t textlen = sizeof( textbuf );
/* global overlay: create entry */
/* NOTE: this is a hack to use the chain overlay
* as global. I expect to be able to remove this
* soon by using slap_mods2entry() earlier in
* do_add(), adding the operational attrs later
* if required. */
rs->sr_err = slap_mods2entry( op->ora_modlist,
&op->ora_e, 0, 1,
&rs->sr_text, textbuf, textlen );
if ( rs->sr_err != LDAP_SUCCESS ) {
send_ldap_result( op, rs );
rc = 1;
break;
}
}
rc = ldap_chain_op( op, rs, lback->bi_op_add, ref );
if ( cleanup_attrs ) {
attrs_free( op->ora_e->e_attrs );
op->ora_e->e_attrs = NULL;
}
break;
}
case LDAP_REQ_DELETE:
rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref );
break;
case LDAP_REQ_MODRDN:
rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref );
break;
case LDAP_REQ_MODIFY:
rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref );
break;
case LDAP_REQ_COMPARE:
rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref );
break;
case LDAP_REQ_SEARCH:
2004-03-20 11:35:55 -05:00
if ( rs->sr_type == REP_SEARCHREF ) {
2004-04-05 13:31:27 -04:00
struct berval *curr = ref,
2004-03-20 11:35:55 -05:00
odn = op->o_req_dn,
ondn = op->o_req_ndn;
rs->sr_type = REP_SEARCH;
sc2.sc_response = ldap_chain_cb_search_response;
li = *lip;
li.url = NULL;
op->o_bd->be_private = &li;
/* if we parse the URI then by no means
* we can cache stuff or reuse connections,
* because in back-ldap there's no caching
* based on the URI value, which is supposed
* to be set once for all (correct?) */
op->o_do_not_cache = 1;
2004-03-20 11:35:55 -05:00
/* copy the private info because we need to modify it */
for ( ; !BER_BVISNULL( &curr[0] ); curr++ ) {
2004-03-20 11:35:55 -05:00
LDAPURLDesc *srv;
char *save_dn;
2004-03-20 11:35:55 -05:00
/* parse reference and use
* proto://[host][:port]/ only */
rc = ldap_url_parse_ext( curr[0].bv_val, &srv );
if ( rc != LDAP_URL_SUCCESS ) {
/* try next */
rs->sr_err = LDAP_OTHER;
continue;
2004-03-20 11:35:55 -05:00
}
/* remove DN essentially because later on
* ldap_initialize() will parse the URL
* as a comma-separated URL list */
save_dn = srv->lud_dn;
2004-03-20 11:35:55 -05:00
srv->lud_dn = "";
srv->lud_scope = LDAP_SCOPE_DEFAULT;
2004-03-20 11:35:55 -05:00
li.url = ldap_url_desc2str( srv );
if ( li.url != NULL ) {
ber_str2bv_x( save_dn, 0, 1, &op->o_req_dn,
op->o_tmpmemctx );
ber_dupbv_x( &op->o_req_ndn, &op->o_req_dn,
op->o_tmpmemctx );
}
srv->lud_dn = save_dn;
ldap_free_urldesc( srv );
2004-03-20 11:35:55 -05:00
if ( li.url == NULL ) {
/* try next */
rs->sr_err = LDAP_OTHER;
continue;
2004-03-20 11:35:55 -05:00
}
2004-03-20 11:35:55 -05:00
/* FIXME: should we also copy filter and scope?
* according to RFC3296, no */
rc = lback->bi_op_search( op, rs );
2004-03-20 11:35:55 -05:00
2004-04-05 13:31:27 -04:00
ldap_memfree( li.url );
li.url = NULL;
op->o_tmpfree( op->o_req_dn.bv_val,
op->o_tmpmemctx );
op->o_tmpfree( op->o_req_ndn.bv_val,
op->o_tmpmemctx );
2004-03-20 11:35:55 -05:00
if ( rc == LDAP_SUCCESS && rs->sr_err == LDAP_SUCCESS ) {
break;
}
2004-03-20 11:35:55 -05:00
}
op->o_req_dn = odn;
op->o_req_ndn = ondn;
rs->sr_type = REP_SEARCHREF;
rs->sr_entry = NULL;
if ( rc != LDAP_SUCCESS ) {
/* couldn't chase any of the referrals */
rc = SLAP_CB_CONTINUE;
}
2004-03-20 11:35:55 -05:00
} else {
rc = ldap_chain_op( op, rs, lback->bi_op_search, ref );
2004-03-20 11:35:55 -05:00
}
break;
case LDAP_REQ_EXTENDED:
rc = ldap_chain_op( op, rs, lback->bi_extended, ref );
2005-01-12 18:22:56 -05:00
/* FIXME: ldap_back_extended() by design
* doesn't send result; frontend is expected
* to send it... */
if ( rc != SLAPD_ABANDON ) {
send_ldap_extended( op, rs );
rc = LDAP_SUCCESS;
2005-01-12 18:22:56 -05:00
}
break;
default:
rc = SLAP_CB_CONTINUE;
break;
}
if ( sc2.sc_private == NULL ) {
op->o_callback = NULL;
rc = rs->sr_err = slap_map_api2result( rs );
send_ldap_result( op, rs );
}
op->o_do_not_cache = cache;
op->o_bd->be_private = private;
2003-06-11 02:32:24 -04:00
op->o_callback = sc;
2003-06-11 18:35:31 -04:00
op->o_ndn = ndn;
2003-06-11 02:32:24 -04:00
rs->sr_ref = ref;
return rc;
}
static int
2005-01-09 16:26:32 -05:00
ldap_chain_db_config(
BackendDB *be,
const char *fname,
int lineno,
int argc,
char **argv
)
{
2004-03-18 13:59:46 -05:00
slap_overinst *on = (slap_overinst *) be->bd_info;
void *private = be->be_private;
char *argv0 = NULL;
int rc;
be->be_private = on->on_bi.bi_private;
2004-03-18 13:59:46 -05:00
if ( strncasecmp( argv[ 0 ], "chain-", sizeof( "chain-" ) - 1 ) == 0 ) {
argv0 = argv[ 0 ];
argv[ 0 ] = &argv[ 0 ][ sizeof( "chain-" ) - 1 ];
}
rc = lback->bi_db_config( be, fname, lineno, argc, argv );
2004-03-18 13:59:46 -05:00
if ( argv0 ) {
argv[ 0 ] = argv0;
}
be->be_private = private;
return rc;
}
static int
2005-01-09 16:26:32 -05:00
ldap_chain_db_init(
BackendDB *be
)
{
slap_overinst *on = (slap_overinst *)be->bd_info;
int rc;
BackendDB bd = *be;
2005-01-09 16:26:32 -05:00
if ( lback == NULL ) {
lback = backend_info( "ldap" );
if ( lback == NULL ) {
return -1;
}
}
bd.be_private = NULL;
rc = lback->bi_db_init( &bd );
on->on_bi.bi_private = bd.be_private;
2004-04-05 13:31:27 -04:00
return rc;
}
static int
2005-01-09 16:26:32 -05:00
ldap_chain_db_destroy(
BackendDB *be
)
{
slap_overinst *on = (slap_overinst *) be->bd_info;
void *private = be->be_private;
int rc;
be->be_private = on->on_bi.bi_private;
rc = lback->bi_db_destroy( be );
on->on_bi.bi_private = be->be_private;
be->be_private = private;
return rc;
}
static slap_overinst ldapchain;
int
2005-01-09 16:26:32 -05:00
chain_init( void )
{
ldapchain.on_bi.bi_type = "chain";
2005-01-09 16:26:32 -05:00
ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
/* ... otherwise the underlying backend's function would be called,
* likely passing an invalid entry; on the contrary, the requested
* operational attributes should have been returned while chasing
* the referrals. This all in all is a bit messy, because part
* of the operational attributes are generated by they backend;
* part by the frontend; back-ldap should receive all the available
* ones from the remote server, but then, on it own, it strips those
* it assumes will be (re)generated by the frontend (e.g.
* subschemaSubentry.) */
ldapchain.on_bi.bi_operational = ldap_chain_operational;
ldapchain.on_response = ldap_chain_response;
return overlay_register( &ldapchain );
}
2004-03-18 13:59:46 -05:00